サービスメッシュのLinkerdで取得したデータをZipkinで分散トレーシングする

#前書き

首題の通り、マイクロサービスを実現する上で欠かせないサービスメッシュをLinkerdで実現し、各マイクロサービスの分散トレースをZipkinで実現します。
説明もそこそこに、とにかく早く構築してみたい方は、「構築手順」の章から進めてください。


#Linkerdについて
Linkerdとは、buoyant社が開発したサービスメッシュOSSツールで、「本番環境として世界で最も使われているサービスメッシュ」と言われています。([ソース](https://blog.buoyant.io/2017/12/05/introducing-conduit/))

buoyant社はLinkerdとは別に、kubernetes専用のサービスメッシュConduitを発表しました。
ConduitはLinkerdの後継ではなく、Linkerdの開発とサポートは継続されるものの、いずれはConduitがメインとなると言われています。(ソース同上)


サービスメッシュの構成方法は主に、per nodeとper pod(サイドカー)方式があります。
Linkerdは両方対応可能ですが、今回はper nodeで実装します。
per nodeの場合、多数のpodを配置してもコンポーネントが増加することはないため、一般的にはper podよりもリソースを抑えることが出来ると言われています(が、per nodeのLinkerd自体のリソース使用量は小さくなく(ソース同上)、ultra-low-resourceな環境では別の選択肢を考える必要があります。)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/223065/417e2f25-1b0a-c015-1803-e2f3bfb35eac.png)


上図では、pod4にあるサービスからpod8にあるサービスへリクエストを送る様子を描いています。
pod4はnode 1のLinkerdにリクエストを送ります。node1のLinkerdはdtabルールに従って、node2のLinkerdにリクエストを送りnode2のLinkerdはpod8のサービスに送ります。

**※node1のLinkerdとnode2のLinkerdを繋ぐnamerdについて**
manerdはdtabs(Delegation tables)によるルーティング情報を中央管理し、Linkerdからルーティング情報が参照されます。
上図のようにnodeまたぎでLinkerdのサービスメッシュを実現するためには、namerdが必要です。



#Zipkinについて
zipkinは分散トレーシングツールで、マイクロサービス間のレイテンシーを収集/可視化することができます。([Zipkin公式ホームページ](https://zipkin.io/))
Zipkinは以下のアーキテクチャーで動作します。
Zipkinをkubernetesのpodとして動作させるために、serviceとしてcollectorとUIの2つが必要であることがわかります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/223065/8b83dc89-c430-8836-714b-9043b7b5e145.png)



#構築手順
##GCP(GKE)の設定

GCP(GKE)のクラスターが作成されている前提で、以下コマンドを実行し、認証/kubectlとdockerの向き先を設定します。

```
##ログイン
$ gcloud auth login

##プロジェクト一覧
$ gcloud projects list

##プロジェクト変更
$ gcloud config set project soty-naky-0113

## クラスター一覧
$ gcloud container clusters list

## クラスター設定
$ gcloud container clusters get-credentials NAME --zone ZONE

## kubectlの構成の確認
$ kubectl config current-context

## dockerの構成
$ gcloud auth configure-docker
```

##Zipkinの導入
以下のファイルを用意します。

**・zipkin-replicationcontroller.yaml**  ：podのyaml
**・zipkin-collecter-service.yaml**  ：Zipkinのcollecterのservice.yaml
**・zipkin-ui-service.yaml**  ：UIのservice.yaml
**・zipkin-ingress.yaml**  ：ZipkinのUIへ(httpsで)アクセスするためのingress.yaml
**・各種tlsファイル**  ：ingressへhttpsプロトコルで接続するために作成する自己証明のファイル群
**・zipkin-secret.yaml**  ：Ingressの自己証明書のsecret.yaml

それぞれのyamlファイルの中身は以下の通りです。

```zipkin-replicationcontroller.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: zipkin
spec:
  replicas: 1
  selector:
    app: zipkin
  template:
    metadata:
      name: zipkin
      labels:
        app: zipkin
    spec:
      containers:
      - name: zipkin
        image: openzipkin/zipkin:1.20
        env:
        - name: SCRIBE_ENABLED
          value: "true"
        ports:
        - name: scribe
          containerPort: 9410
        - name: http
          containerPort: 9411
```


```zipkin-collecter-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    name: zipkin-collector
  name: zipkin-collector
spec:
  type: ClusterIP
  selector:
    app: zipkin
  ports:
  - name: scribe
    port: 9410
    targetPort: 9410
```

```zipkin-ui-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: zipkin-ui
  labels:
    name: zipkin
spec:
  type: NodePort
  selector:
    app: zipkin
  ports:
  - name: https
    port: 443
    targetPort: 9411
```

```zipkin-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: zipkin-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.allow-http: "false"
spec:
  tls:
  - secretName: zipkin-tls
  backend:
    serviceName: zipkin-ui
    servicePort: 443
```

```zipkin-secret.yaml
apiVersion: v1
data:
  tls.crt: LS0tLS1...... #生成した.crtをbase64でエンコードした値を入力
  tls.key: LS0tLS1...... #生成した.keyをbase64でエンコードした値を入力
kind: Secret
metadata:
  name: zipkin-tls
  namespace: default
type: Opaque
```

上記`tls.crt:`と`tls.key:`については、以下の手順で作成します。
・自己証明ファイル群を作成します。どの方法でも構いませんが、私は[こちらのwebサイト](http://d.hatena.ne.jp/ozuma/20130511/1368284304)を参考に、自己証明ファイル群を作成しました。
・作成した`.crt`ファイルと`.key`ファイル(以下例ではそれぞれserver.crtとserver.key)をbase64でエンコードします。以下コマンドを実行して、その標準出力をそれぞれ`tls.crt:`と`tls.key:`の値として貼り付けます。

```
## server.crtとserver.keyファイルを、それぞれbase64エンコードするコマンド
$ cat server.crt | base64
$ cat server.key | base64
```

ここまで作成したファイル群をGKEに適用します。
まず以下コマンドを実行して、先にSecretを作成します。

```
$ kubectl create -f zipkin-secret.yaml
secret "zipkin-tls" created
```

pod(replicationcontroller)を作成します。

```
$ kubectl create -f zipkin-replicationcontroller.yaml
```

serviceを作成します。

```
$ kubectl create -f zipkin-ui-service.yaml zipkin-collecter-service.yaml
```

ingressを作成します。

```
$ kubectl create -f zipkin-ingress.yaml
```

アクセスポイントを確認します。以下コマンドでingressに割り振られているIPアドレスを確認します。(IPアドレスの割り振りには時間がかかる場合があります)

```
$ kubectl get ingress zipkin-ingress
NAME             HOSTS     ADDRESS          PORTS     AGE
zipkin-ingress   *         35.xxx.xxx.xxx   80, 443   10m
```

割り振られたアドレス(35.xxx.xxx.xxx)にhttpsでアクセスします。
(Ingressの`annotations:`の設定で`kubernetes.io/ingress.allow-http: "false"`としているため、httpプロトコルではアクセスできないことも併せて確認します)

httpsプロトコルでのアクセス
![image.png](https://qiita-image-store.s3.amazonaws.com/0/223065/9bfee177-e569-5b68-5799-2fa6e7b04a2c.png)


httpプロトコルでのアクセス
![image.png](https://qiita-image-store.s3.amazonaws.com/0/223065/2226848f-faf5-5fec-bd9d-7a780d38de62.png)


以上でZipkinの導入は完了です。

##Linkerdの導入

Linkerdを導入するにあたり、以下のファイルを用意します。
**・linkerd-daemonset.yaml** ：linkerdをdaemonsetとしてデプロイするyaml
**・linkerd-configmap.yaml** ：linkerdの設定ファイルとなるconfig.yaml


```linkerd-daemonset.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  labels:
    app: l5d
  name: l5d
spec:
  template:
    metadata:
      labels:
        app: l5d
    spec:
      volumes:
      - name: l5d-config
        configMap:
          name: "l5d-config"
      containers:
      - name: l5d
        image: mycluster.icp:8500/default/linkerd:1.3.6
        env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        args:
        - /io.buoyant/linkerd/config/config.yaml
        ports:
        - name: outgoing
          containerPort: 4140
          hostPort: 4140
        - name: incoming
          containerPort: 4141
        - name: admin
          containerPort: 9990
        volumeMounts:
        - name: "l5d-config"
          mountPath: "/io.buoyant/linkerd/config"
          readOnly: true

      - name: kubectl
        image: mycluster.icp:8500/default/kubectl:v1.8.5
        args:
        - "proxy"
        - "-p"
        - "8001"
```


```linkerd-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: l5d-config
data:
  config.yaml: |-
    admin:
      ip: 0.0.0.0
      port: 9990

    namers:
    - kind: io.l5d.k8s
      host: localhost
      port: 8001

    telemetry:
    - kind: io.l5d.prometheus
    - kind: io.l5d.zipkin
      host: zipkin-collector.default.svc.cluster.local
      port: 9410
      sampleRate: 1.0
    - kind: io.l5d.recentRequests
      sampleRate: 0.25

    usage:
      orgId: linkerd-examples-daemonset-zipkin

    routers:
    - protocol: http
      label: outgoing
      dtab: |
        /srv        => /#/io.l5d.k8s/default/http;
        /host       => /srv;
        /svc        => /host;
        /host/world => /srv/world-v1;
      interpreter:
        kind: default
        transformers:
        - kind: io.l5d.k8s.daemonset
          namespace: default
          port: incoming
          service: l5d
      servers:
      - port: 4140
        ip: 0.0.0.0
      service:
        responseClassifier:
          kind: io.l5d.http.retryableRead5XX

    - protocol: http
      label: incoming
      dtab: |
        /srv        => /#/io.l5d.k8s/default/http;
        /host       => /srv;
        /svc        => /host;
        /host/world => /srv/world-v1;
      interpreter:
        kind: default
        transformers:
        - kind: io.l5d.k8s.localnode
      servers:
      - port: 4141
        ip: 0.0.0.0
```

Linkerdが取得したサービスメッシュのデータは、config.yaml内で定義した以下の箇所でzipkinに渡しています。

```
    telemetry:
    - kind: io.l5d.prometheus
    - kind: io.l5d.zipkin
      host: zipkin-collector.default.svc.cluster.local
      port: 9410
      sampleRate: 1.0
```


configmapを作成します。

```
$ kubectl create -f linkerd-configmap.yaml
```


Linkerdをdaemonsetとしてデプロイします。

```
$ kubectl create -f linkerd-daemonset.yaml
```

デプロイしたpodが正常に稼働していることを確認します。

```
$ kubectl get po -o wide -l app=l5d
NAME        READY     STATUS    RESTARTS   AGE       IP         NODE
l5d-22rkl   2/2       Running   0          5m        10.8.2.8   gke-cluster-1-default-pool-c4146799-hc7z
l5d-tcfhc   2/2       Running   0          5m        10.8.1.8   gke-cluster-1-default-pool-c4146799-69xf
l5d-xmv7z   2/2       Running   0          5m        10.8.0.9   gke-cluster-1-default-pool-c4146799-tps1
```

以上でLinkerdの導入は完了です。

##サンプルアプリケーションの導入

あとはアプリケーションをデプロイし、動作させることでZipkinのインターフェースから分散トレースを可視化することができます。
**※6/8までに詳しい手順をアップデート予定です**