Cloud Native 12 min read

Exploring Consul’s Service Mesh: Setup, Code Samples, and Deep Dive

This article walks through upgrading Consul to 1.2, configuring service‑mesh support, building two Go micro‑services, registering them with Consul, reloading the configuration, testing the mesh with curl, and provides a detailed analysis of Consul’s service‑mesh architecture, advantages, and limitations.

Programmer DD
Programmer DD
Programmer DD
Exploring Consul’s Service Mesh: Setup, Code Samples, and Deep Dive

Upgrade Consul to 1.2

On macOS use Homebrew to upgrade: brew update consul Add the -config-dir flag to the launch plist at /usr/local/opt/consul/homebrew.mxcl.consul.plist. Example plist snippet:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>KeepAlive</key><true/>
  <key>Label</key><string>homebrew.mxcl.consul</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/opt/consul/bin/consul</string>
    <string>agent</string>
    <string>-dev</string>
    <string>-advertise</string>
    <string>127.0.0.1</string>
    <string>-config-dir</string>
    <string>/usr/local/etc/consul.d</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>WorkingDirectory</key><string>/usr/local/var</string>
  <key>StandardErrorPath</key><string>/usr/local/var/log/consul.log</string>
  <key>StandardOutPath</key><string>/usr/local/var/log/consul.log</string>
</dict>
</plist>

Create two mock micro‑services

service1.go

package main

import (
    "net/http"
    "log"
    "io"
)

func TestServer(w http.ResponseWriter, req *http.Request) {
    resp, err := http.Get("http://127.0.0.1:38082/test2")
    if resp != nil && resp.Body != nil {
        defer resp.Body.Close()
    }
    if err != nil {
        w.Write([]byte("make request failed
"))
        return
    }
    io.Copy(w, resp.Body)
}

func main() {
    http.HandleFunc("/test1", TestServer)
    if err := http.ListenAndServe(":8081", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

service2.go

package main

import (
    "net/http"
    "log"
    "io"
)

func TestServer(w http.ResponseWriter, req *http.Request) {
    io.WriteString(w, "hello, world!
")
}

func main() {
    http.HandleFunc("/test2", TestServer)
    if err := http.ListenAndServe(":8082", nil); err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

Register the services in Consul

Place the following JSON files under /usr/local/etc/consul.d:

01_service1.json

{
  "service": {
    "name": "service1",
    "port": 8081,
    "connect": {
      "proxy": {
        "config": {
          "upstreams": [{
            "destination_name": "service2",
            "local_bind_port": 38082
          }]
        }
      }
    }
  }
}

01_service2.json

{
  "service": {
    "name": "service2",
    "port": 8082,
    "connect": {
      "proxy": {}
    }
  }
}

Reload Consul to apply the new configuration:

consul reload

Run and test

Start both services in the background and issue a curl request to service1:

go run service1.go &> /dev/null
go run service2.go &> /dev/null
curl http://127.0.0.1:8081/test1
# Expected output: hello, world!

Consul Service‑Mesh architecture

When a service definition contains a "connect":{"proxy":{}} block, Consul creates a dedicated tunnel proxy for each instance. The proxy establishes a TLS‑encrypted TCP tunnel to the original service; higher‑level protocols (HTTP/1.1, HTTP/2, gRPC) flow through this tunnel. The proxy implementation lives in connect/proxy/listener.go (function NewPublicListener).

Service‑to‑service calls use an UpstreamListener declared in the consumer’s configuration. The upstream listener acts as a reverse proxy that connects to the provider’s ConnectProxy (function NewUpstreamListener). Together they form the complete data path, providing TLS encryption and enabling Consul Intentions for fine‑grained access control. The tunnel also supports HTTP/2 when using the Connect‑Native integration.

Pros and cons

Supports any TCP‑based protocol (HTTP/1.1, HTTP/2, gRPC) via the TLS tunnel.

Implementation is compact (≈20 source files), making the logic easy to follow.

Deep integration with Consul’s service registration and discovery.

Load‑balancing is currently a simple random selection.

Advanced features such as timeouts, retries, circuit breaking, and traffic shaping are not built‑in; they must be added by extending connect/proxy/listener.go.

Manual configuration is required: service definitions must be edited and callers must use the upstream address instead of the original service address.

Conclusion

Consul’s service‑mesh implementation is lightweight and functional, offering a solid foundation for further extension and a good study case for service‑mesh internals.

References

https://www.hashicorp.com/blog/consul-1-2-service-mesh

https://www.consul.io/intro/getting-started/connect.html

https://www.consul.io/docs/agent/options.html

https://www.consul.io/docs/connect/intentions.html

https://www.consul.io/docs/connect/native.html

https://www.consul.io/docs/connect/native/go.html

https://www.consul.io/docs/connect/configuration.html

https://www.consul.io/docs/connect/proxies.html

https://www.consul.io/docs/connect/dev.html

https://www.consul.io/docs/connect/ca/consul.html

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

MicroservicesGoService MeshConsulTLSconnect
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.