본문으로 건너뛰기

Jaeger를 활용한 Consul Service Mesh Tracing

유형욱약 17 분ConsulJaegerTracingOpenTelemetryIstioIngressGatewayKubetenetesK8s

Jaeger를 활용한 Consul Service Mesh Tracing

0. 사전 요구사항

1) Consul Install

Jaeger 연동을 위해 Consul on K8s 환경을 구성합니다. 해당 가이드의 경우에는 여기를 참고하세요.

(1) 시크릿 생성 - 라이센스

  • 라이센스 파일 생성 및 시크릿 생성
# license파일 생성
vi consul.lic

# 생성한 license파일로 secret 생성
kubectl create secret generic license --from-file='key=./consul.lic'

(2) 시크릿 생성 - Gossip Key

  • consul-gossip-encryption-key 시크릿 생성
GOSSIP_KEY="VeQ8CHV3sDY/bHCseXC7PGXNTSXtWWvOzQKAaFFo9oE="
kubectl patch secret consul-gossip-encryption-key -n consul --patch='{"stringData":{"key": "$GOSSIP_KEY"}}'

(3) Consul Helm 배포

values.yaml 파일 수정 및 배포합니다.

  • values.yaml 파일 예시
global:
  name: consul
  datacenter: dc1
  logLevel: "debug"
  logJSON: false
  image: hashicorp/consul-enterprise:1.12.3-ent
  gossipEncryption:
    autoGenerate: true
  tls:
    enabled: false
    enableAutoEncrypt: false
    verify: false
    httpsOnly: false
  imageEnvoy: envoyproxy/envoy:v1.22-latest
  enterpriseLicense:
    secretName: license
    secretKey: key
server:
  replicas: 3
client:
  enabled: true
  exposeGossipPorts: true
  extraConfig: |
    {
      "log_level": "debug"
    }
  grpc: true
ui:
  enabled: true
  service:
    type: LoadBalancer
connectInject:
  enabled: true
controller:
  enabled: true
  #terminatingGateways:
  #enabled: true
  #apiGateway:
  #enabled: true
  #image: "hashicorp/consul-api-gateway:latest"
ingressGateways:
  enabled: true
  gateways:
  - name: ingress-gateway
    service:
      type: LoadBalancer
      ports:
      - port: 5000

1. Cert-Manager 설치open in new window

Jaeger를 설치할 때 cert-manager 설치가 필수적으로 요구됩니다.

Since version 1.31 the Jaeger Operator uses webhooks to validate Jaeger custom resources (CRs). This requires an installed version of the cert-manager.

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml
  • cert-manager 파드 배포확인
kubectl get pods -n cert-manager
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-6544c44c6b-z76nd              1/1     Running   0          25s
cert-manager-cainjector-5687864d5f-pdzbn   1/1     Running   0          25s
cert-manager-webhook-785bb86798-v6phx      1/1     Running   0          25s

2. Jaeger 설치open in new window

Tracing을 위해 Jaeger 공식 문서를 참고하여 K8s 환경에 Jaeger Operator를 설치합니다.

(1) Role Binding 배포(선택)

💡참고 : 동일 네임스페이스 배포할 경우 해당 과정은 생략!

  • YAML 생성 : consul-jaeger RoleBinding 생성
# role-binding.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: jaeger-operator-in-myproject
  namespace: consul-jaeger
subjects:
- kind: ServiceAccount
  name: jaeger-operator
  namespace: observability
roleRef:
  kind: Role
  name: jaeger-operator
  apiGroup: rbac.authorization.k8s.io
  • YAML 배포
kubectl apply -f role-binding.yaml

(2) Jaeger Operator 배포(CRD 설치)

Jaeger Operator를 배포하기 위한 observbility 네임스페이스를 생성합니다. 이때, 별도의 네임스페이스에 배포할 경우에는 다운받은 .yaml에 설저된 네임스페이스명을 변경하셔야 합니다. 참고open in new window

  • 네임스페이스 생성 및 각종 리소스 배포
kubectl create namespace observability
kubectl create -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.37.0/jaeger-operator.yaml -n observability
  • 배포된 jaeger-operator 확인
kubectl get deployment jaeger-operator -n observability 
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
jaeger-operator   1/1     1            1           2m30s

(3) Jaeger CR 배포 - AllInOne

실제 K8s 환경에서 Jaeger리소스 생성을 위해 다음 .yaml 파일을 배포합니다. 본 문서에서는 편의상 AllInOne 이미지를 사용하여 배포합니다.

AllInOne 이미지는 프로덕션 환경에서 사용하기에는 적합하지 않으며, Dev 또는 Test 목적으로 사용해야 합니다. (배포전략 참고)open in new window

The simplest possible way to create a Jaeger instance is by creating a YAML file like the following example. This will install the default AllInOne strategy, which deploys the “all-in-one” image (agent, collector, query, ingester, Jaeger UI) in a single pod, using in-memory storage by default.

  • YAML 생성
# simplest.yaml
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: simplest
  namespace: observability
# simplest-debug.yaml
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: simplest
  namespace: observability
spec:
  strategy: allInOne
  allInOne:
    image: jaegertracing/all-in-one:latest
    options:
      log-level: debug
  • YAML 배포
kubectl apply -f simplest.yaml
  • 배포 로그 확인
    • {"level":"info","ts":1661997111.1498919,"caller":"healthcheck/handler.go:129","msg":"Health Check state change","status":"ready"} 로그를 통해서 정상적인 상태 확인됨
kubectl logs -l app.kubernetes.io/instance=simplest -n consul-jaeger
{"level":"info","ts":1661997111.149404,"caller":"channelz/funcs.go:340","msg":"[core][Channel #10] Channel Connectivity change to TRANSIENT_FAILURE","system":"grpc","grpc_log":true}
{"level":"info","ts":1661997111.1495373,"caller":"app/static_handler.go:181","msg":"UI config path not provided, config file will not be watched"}
{"level":"info","ts":1661997111.149864,"caller":"app/server.go:217","msg":"Query server started","http_addr":"[::]:16686","grpc_addr":"[::]:16685"}
{"level":"info","ts":1661997111.1498919,"caller":"healthcheck/handler.go:129","msg":"Health Check state change","status":"ready"}
{"level":"info","ts":1661997111.149912,"caller":"app/server.go:300","msg":"Starting GRPC server","port":16685,"addr":":16685"}
{"level":"info","ts":1661997111.1499252,"caller":"channelz/funcs.go:340","msg":"[core][Server #9 ListenSocket #12] ListenSocket created","system":"grpc","grpc_log":true}
{"level":"info","ts":1661997111.1499453,"caller":"app/server.go:281","msg":"Starting HTTP server","port":16686,"addr":":16686"}
{"level":"info","ts":1661997112.150468,"caller":"channelz/funcs.go:340","msg":"[core][Channel #10 SubChannel #11] Subchannel Connectivity change to IDLE","system":"grpc","grpc_log":true}
{"level":"info","ts":1661997112.1505697,"caller":"grpclog/component.go:71","msg":"[core]pickfirstBalancer: UpdateSubConnState: 0xc00082a700, {IDLE connection error: desc = \"transport: Error while dialing dial tcp :16685: connect: connection refused\"}","system":"grpc","grpc_log":true}
{"level":"info","ts":1661997112.1505857,"caller":"channelz/funcs.go:340","msg":"[core][Channel #10] Channel Connectivity change to IDLE","system":"grpc","grpc_log":true}

(4) (선택) Jaeger Sidecar 배포 방식

Jaeger Auto Injection 및 Manaul Injection 활용방안을 가이드합니다. (3)에서 Jaeger리소스를 직접 배포했다면 생략하셔도 됩니다.

방안1. CRD 배포

해당 방안은 3)-(3)에서 작성한 방식으로, 관리되는 네임스페이스에 있는 애플리케이션의 tracing을 수행합니다.

방안2. Auto Injection - annotation 활용

해당 방안은 annotation 절에 "sidecar.jaegertracing.io/inject": "true" 를 기입하여 tracing 하고자 하는 애플리케이션에 sidecar auto-injection을 수행합니다.

  • tracing 할 애플리케이션 deployment yaml의 annotation 위치
metadata:
  name: web-deployment
  labels:
    app: web
  namespace: observability
  annotations:
    "sidecar.jaegertracing.io/inject": 'true'

방안3. Manual Injectio

tracing 하고자 하는 애플리케이션에 직접 sidecar를 붙혀 tracing 합니다.

      - name: jaeger-agent
        image: jaegertracing/jaeger-agent:latest
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 5775
            name: zk-compact-trft
            protocol: UDP
          - containerPort: 5778
            name: config-rest
            protocol: TCP
          - containerPort: 6831
            name: jg-compact-trft
            protocol: UDP
          - containerPort: 6832
            name: jg-binary-trft
            protocol: UDP
          - containerPort: 14271
            name: admin-http
            protocol: TCP
        args:
          - --reporter.grpc.host-port=dns:///simplest-collector-headless.observability:14250
          - --reporter.type=grpc

(5) Jaeger UI 접속

기본적을 Jaeger UI는 ClusterIP로 배포됩니다. 외부에서 접속하기 위해 다음 몇 가지 방안을 제시합니다.

참고 : 본 문서에서는 편의상 LoadBalancer 타입으로 변경하는 샘플을 제공합니다.

  • Ingress
  • LoadBalancer
  • Port-Forwarding

기본적으로 Jaeager UI는 16686 Port를 사용합니다. 필자는 편읜상 simplest-query 서비스를 LoadBalancer타입으로 변경하여 조회합니다.

spec:
(중략)
  ports:
  - name: http-query
    nodePort: 32731
    port: 16686
    protocol: TCP
    targetPort: 16686
  - name: grpc-query
    nodePort: 31322
    port: 16685
    protocol: TCP
    targetPort: 16685
  selector:
    app: jaeger
    app.kubernetes.io/component: all-in-one
    app.kubernetes.io/instance: simplest
    app.kubernetes.io/managed-by: jaeger-operator
    app.kubernetes.io/name: simplest
    app.kubernetes.io/part-of: jaeger
  sessionAffinity: None
  type: LoadBalancer
  • Jaeger UI 예제
image-20221110190509279
image-20221110190509279

(참고) Jaeger와 다른 네임스페이스에 애플리케이션 사용할 경우

3. Consul Config 설정open in new window

(1) Ingress Gateway

apiVersion: consul.hashicorp.com/v1alpha1
kind: IngressGateway
metadata:
  name: ingress-gateway
  namespace: consul
spec:
  listeners:
    - port: 5000
      protocol: http
      services:
        - name: web
          hosts: ['*']

(2) Service Defaults

apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceDefaults
metadata:
  name: web
spec:
  protocol: "http"

(3) Proxy Defaults

ProxyDefaults 설정을 통해 Collector 서버에 대한 주소 및 Clustesr Name에 대해 선언합니다.

apiVersion: consul.hashicorp.com/v1alpha1
kind: ProxyDefaults
metadata:
  name: global
  namespace: consul
spec:
  config:
    protocol: http
    envoy_tracing_json: |
      {
        "http":{
          "name":"envoy.tracers.zipkin",
          "typedConfig":{
            "@type":"type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
            "collector_cluster":"simplest-collector",
            "collector_endpoint_version":"HTTP_JSON",
            "collector_endpoint":"/api/v2/spans",
            "shared_span_context":false
          }
        }
      }
    envoy_extra_static_clusters_json: |
      {
        "connect_timeout":"3.000s",
        "dns_lookup_family":"V4_ONLY",
        "lb_policy":"ROUND_ROBIN",
        "load_assignment":{
          "cluster_name":"simplest-collector",
          "endpoints":[
            {
              "lb_endpoints":[
                {
                  "endpoint":{
                    "address":{
                      "socket_address":{
                        "address":"simplest-collector",
                        "port_value":9411,
                        "protocol":"TCP"
                      }
                    }
                  }
                }
              ]
            }
          ]
        },
        "name":"simplest-collector",
        "type":"STRICT_DNS"
      }

99) 참고문서