kube-awsで作ったクラスタのetcdをprometheusのservice-discoveryで検出してメトリクスを取る

タイトルを削れなかった

##ターゲット

- kube-aws使っててちょっとわかる（なのでawsとkubernetesも当然）
- prometheus使っててちょっとわかる（kubernetesで運用してるとなお好都合）
- etcdのメトリクス取りたい！（いなさそう）

##目次
1. etcdのインスタンスを検出する
2. etcdのメトリクスを取得する
3. その他つらつら

## 1. etcdのインスタンスを検出する

### 理屈

kube-awsにより作られたkubernetesクラスタは、etcdのinstanceがcontrollerから独立しており、kubernetesクラスタの外にある状態になっています（見た感じそうなってるからそのはず）。

通常、prometheusをkubernetesで使う場合は、`service-discovery`の`kubernetes_sd_config`を使って監視対象のリソースを探します。
が、上記の通りetcdのinstanceはkubernetesクラスタの外にあるためこれでは検出できません。
かと言ってクラスタを作った後にetcdの__ipを調べてprometheusのconfigのtargetsに書いていくなんてことはまっぴら御免__です。
ですので、etcdのinstanceは`ec2_sd_config`を使って検出します。

何もしないとprometheusに設定したuser/roleから見える全てのインスタンスを検出してしまい、とても散らかるので必要なものだけに絞っていきます。
今回はtagに`kube-aws:role: etcd`と、ちょうど良さそうなものがついていたのでこれを使います。

![aaa.png](https://qiita-image-store.s3.amazonaws.com/0/122084/98fc86ca-74b4-cc6c-803a-860c5ce6b8c6.png)

kube-awsの変更でこのtagがつかなくなる、なんてことがあるかもしれないので、自分でオリジナルのタグを付けるのもいいかもしれません。（cluster.yamlの記述でそういうのできたりするんですかね、よくわかってない）

ステージングのインスタンスも検出してしまう！なんて時は他にも自分の環境に応じて条件を加えます。
私の場合はvpcでの絞り込みも追加しました。

### 方法

prometheusのconfigにこんな感じのセクションを追加します

```yaml:configmap.yaml
#scrape kube-aws etcd
- job_name: 'kube-aws-etcd'
  ec2_sd_configs:
    - region: ap-northeast-1
      port: 2379 #etcdのポート
  relabel_configs:
    - source_labels: [__meta_ec2_tag_kube_aws_role]
      regex: 'etcd'
      action: keep
    - source_labels: [__meta_ec2_vpc_id]
      regex: 'vpc-1234abcd'
      action: keep
```

## 2. etcdのメトリクスを取得する

### 理屈

etcdはデフォルトで`/metircs`にprometheus用のメトリクスを出力しています。
なので通常であれば、検出したインスタンスの`/metrics`にアクセスするだけです。
しかし今回はひとつ壁があって、__アクセスするのにkeyやらcertやらが必要__になっています。
（ここはドキュメントやコードを読まずにひたすら総当たりで調べたので何か間違ってたらすいません。とにかく取得はできますので…）

これはkube-awsでクラスタを作る際の、`kube-aws render credentials`によって生み出される（だったような）

- credentials/etcd-client-key.pem
- credentials/etcd-client.pem

を使います。

これらはnodeを作る際、`/etc/kubernetes/ssl/`に配置されるようで、それならprometheusコンテナでvolumeマウントして使えばいい
…と思ったのですが、rootにしかread権限がついてなかったので諦めて、普通にsecretを作りました。
prometheusをrootで起動してる人はマウントで使えるかもしれません。

### 方法

まずはsecretですが、上記のpemを例によってbase64エンコードして作ります

```yaml:secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: etcd-secret
  namespace: default
type: Opaque
data:
  etcd-client.pem: "kekkounagaidehogehogehogehoge"
  etcd-client-key.pem: "koitsumonagaidehogehogehogehoge"
```

私の場合はこんな感じのスクリプトでsecretを作ってます

```bash:make_secret.sh
FILE="secret.yaml"
cat << EOF >>${FILE}
apiVersion: v1
kind: Secret
metadata:
  name: etcd-secret
  namespace: default
type: Opaque
data:
EOF
echo '  etcd-client.pem: "'$(/bin/cat credentials/etcd-client.pem | base64)\" >>${FILE}
echo '  etcd-client-key.pem: "'$(/bin/cat credentials/etcd-client-key.pem | base64)\" >>${FILE}
```

configは1.のものにちょっと手を加えて

```yaml:configmap.yaml
#scrape kube-aws etcd
- job_name: 'kube-aws-etcd'
  scheme: https
  tls_config:
    key_file: /mnt/ssl/etcd-client-key.pem #deploymentでmountするpathとsecretのdataのname
    cert_file: /mnt/ssl/etcd-client.pem #上に同じ
    insecure_skip_verify: true
  ec2_sd_configs:
    - region: ap-northeast-1
      port: 2379 #etcdのポート
  relabel_configs:
    - source_labels: [__meta_ec2_tag_kube_aws_role]
      regex: 'etcd'
      action: keep
    - source_labels: [__meta_ec2_vpc_id]
      regex: 'vpc-1234abcd'
      action: keep
    - action: labelmap
      regex: __meta_ec2_(.+)
```

こんな感じのものを追加します。

deploymentは今回に関係ある部分を抜粋するとこんな感じ

```yaml:deployment.yaml
spec:
  template:
    spec:
      containers:
        - name: prometheus
          image: prom/prometheus:latest
          volumeMounts:
            - name: etcd-ssl-volume
              mountPath: /mnt/ssl/ #configと揃っていればなんでも
            - name: secrets-volume
      volumes:
        - name: etcd-ssl-volume
          secret:
            secretName: etcd-secret
            defaultMode: 420
```

Pod派の人やprometheusをkubernetesで運用してない人はよしなにやりましょう。

要は、__`ec2_sd_config`でインスタンスを探して、`etcd-client.pem`と`etcd-client-key.pem`を使って`:2379/metrics`にアクセスすればいい__のです。

問題なければこんな感じに通るはずです。（label整理してない）
![bbb.png](https://qiita-image-store.s3.amazonaws.com/0/122084/99a98e6c-bd59-90c3-75ef-827bf5d04131.png)

grafanaでdashboardにすればそれっぽい
![ccc.jpg](https://qiita-image-store.s3.amazonaws.com/0/122084/d33f97c3-7d28-45fc-baf6-cd12a46d46f1.jpeg)



## 3.その他つらつら
これで取れるのはあくまでetcdのメトリクスであって、etcdインスタンスのメトリクスは取れません。
ちょっとしたcpuやmemoryのメトリクスなら`process_cpu_seconds_total`や`go_memstats_*`がありますが、足りないならnode-exporter等を配置する必要があります。
しかしこれは現状etcdインスンタンスが立った後に直接ログインしてインストールして公開して必要ならアクセス制限して…などの作業が必要になるため現実的ではありません（し、実際に可能かどうかも私にはわかりません）。
最初に述べたとおりkubernetesクラスタの外なので当然daemonsetも届きません。

なので<a href="https://github.com/prometheus/cloudwatch_exporter">cloudwatch_exporter</a>でcloudwatchのmetricsをprometheusに取り込むか、grafanaを使っているならcloudwatchを直接データソースにしてしまうなどが現実的かと思います。
（私は先述の「ちょっとしたcpuやmemoryのメトリクス」で間に合わせてます。）
kube-awsでクラスタを作る時にetcdインスタンスにnode-exporterが入ったりしてくれれば…

kube-awsはあまりしっかり触ってないので何か間違っていたらすみません。