Telepresence で EKS 上の k8s クラスタをデバッグする

この記事は Kubernetes2 Advent Calendar 2018 一日目の記事です。


概要

ローカルにある docker image を特定の k8s Deployment と差し替えてくれる便利な OSS、Telepresence を紹介します。コードの変更箇所をリモートにある k8s クラスタにデプロイしなくても、ローカルで動作確認ができるようになるライブラリです。

※ 今回 k8s は AWS EKS 上に構築していますが、Telepresence の情報量は GCP の方が圧倒的に多いくらい、ほぼ GCP 向けに作られていると思います。


環境など


リモート

すべて AWS リソースを使用しています。

- EKS (us-west-2) 上に k8s クラスタを構築

- 他に Dynamo DB, Fluentd, SQS などを作っています


ローカル


  • macOS High Sierra (v10.13.2)

  • Telepresence (v0.94)

  • Docker for Mac (v18.09.0)


    • k8s 入りですが今回は使いません



  • kubectl (v1.10.3)




  • AWS IAM Authenticator (v0.4.0-alpha.1)

  • eksctl (v0.1.11)

  • helm (v2.11.0)


使用したソースコード

今年の re:Invent のワークショップで使用された EKS を Cloudwatch と X-Ray に連携するためのサンプルプロジェクト を使用します。


  • サービス数がそこそこある


    • サービスが少ないとスペック :money_with_wings: で殴れば minikube や Docker for Mac with Kubernetes が使えてしまうため



  • Dynamo DB や SQS など AWS EKS 以外のリソースを使っている

  • MIT License :raised_hands: :raised_hands: :raised_hands: (念のため…)

:zap: このサンプルは EC2 の m5.large インスタンスを4台も使用しているため、無料枠では収まりません。ご注意ください。 :zap:

※ 請求書によると、今回だけで EC2 (61h) + EKS (75h) で合計 $10.54 かかりましたが、それ以外は無料枠に収まっていました。

使い終わったら cleanup 方法 の通りにリソースを削除していってください。VPC を削除できなくなります… (私です)。


k8s クラスタの構築

基本的には上記の サンプルプロジェクトdocs/ 以下を順番にやっていけば問題ないです。ただし、


  • ローカルから Telepresence を使うので PrerequisitesManual Setup - Expert で各ライブラリをインストールしてください



  • X-Ray 連携まではやらないので Prerequisites のあとは lab1 + lab2: Collecting logs using Fluentd までで大丈夫です

  • region は us-west-2 (オレゴン) にするとすべてコピペで完成します

frontend service から URL を取得し、ブラウザでこんなページが出てくれば OK です

screencapture-a0f89d6ddf3c211e8b3020abf6600db1-522586172-us-west-2-elb-amazonaws-2018-11-29-19_37_36.png


Cloud9 を使わなかった理由

主旨と離れるので細かくは書きませんが、EKS は aws-auth という configmap でそのクラスタにアクセスできるロールやユーザを制御できるようになっており、eksctl でクラスタを作成すると、最初はクラスタを作成した端末の default credential だけが許可されています。そのため Cloud9 以外 (今回はローカル) からクラスタに (kubectl で) アクセスすると Access denied が返ってきてしまいます。

また aws-auth 自体を変更するのにはなんらかの credentials が必要なようなのですが、このサンプルプロジェクトでは Cloud9 から AWS credentials を全く使わず EKS にクラスタを構築しているため、Cloud9 から編集しようとしても Please login to the server というエラーになってしまいます。

eksctlaws eksaws-auth にロールやユーザを追加するコマンドは現時点では存在せず、結局 Cloud9 からしかクラスタにアクセスできませんでした。今回はローカル端末から Telepresence を使って EKS にアクセスしたかったので、Cloud9 を使わない方法を選びました。

※ もし解決法をご存知の方がいらっしゃったらぜひご教示ください :bow:


Telepresence を使う

Telepresence は k8s にある特定の deployment を proxy に置き換え、そのサービスに来た通信をローカルのコンテナに向けてくれるライブラリです (Document の Why Telepresence? にある図が分かりやすいです)。How it works にあるように、Telepresence は内部的には kubectl port-forwardos port-forward を使って proxy を実現しています。

Proxying methods を見ると3種類の差し替え方法がありますが、今回は docker :whale2: を使った方法のみ使用します。

$ telepresence --swap-deployment <Deployment name on your k8s cluster> --docker-run \

$ --rm -it <Docker image name>:<tag>

これだけです。--docker-run 以降は任意の docker run option を使えます。

ちなみに、今回は考慮しなくてよかったのですが Troubleshooting & Workarounds を読むと気付きが得られることがあります (特に localhost の扱い)。


Frontend Service

一番わかりやすいので最初は view をいじってみます。

src/frontend/views/index.hbsAnyCompany ShopYuzuco's Shop に置換してみます :rabbit:


src/frontend/views/index.hbs

...

<div class="container">
...
<h2>Yuzuco's Shop</h2>
<p>The Yuzuco's Shop is an online store for luxury products.</p>
<p><a class="btn btn-secondary" href="/static" role="button">View details &raquo;</a></p>
...

ローカルに docker image を作成し、Telepresence で EKS 上にある frontend deployment と swap します。

$ cd reinvent2018-dev303-code/src/frontend

$ docker build -t dev303-frontend .
$ telepresence --swap-deployment frontend --docker-run \
$ --rm -it dev303-frontend:latest

ここで大量に出てくるログは ./telepresence.log にも書き込まれるので、issue を投げるときにたくさん活用しましょう :bookmark:

先ほどと同じ URL で、きちんとローカルの image が反映されているのを確認できます :rabbit: :tada: :tada:

screencapture-a0f89d6ddf3c211e8b3020abf6600db1-522586172-us-west-2-elb-amazonaws-2018-11-29-21_08_51.png


反映までの流れ

反映までの Deployment と Pod の状態を見てみます。Telepresence の仕組みをよく知っている方は読み飛ばしてください :pray:

# オリジナルの frontend deployment がなくなり、変な deployment が作成されています

$ kgd
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
...
frontend 0 0 0 0 1h
frontend-89842e0a82b144c48181ff262f0575de 1 1 1 1 12s
...
# Pod の方も変な pod が起動しています
$ kgp
NAME READY STATUS RESTARTS AGE
...
frontend-5769984958-46f9w 0/1 Terminating 0 1h
frontend-5769984958-nwkfh 0/1 Terminating 0 1h
frontend-89842e0a82b144c48181ff262f0575de-85955c7555-qbtrp 1/1 Running 0 39s
...
# オリジナルの frontend deployment は desired replicasets が 0 になっています
$ kdd frontend
Name: frontend
...
Replicas: 0 desired | 0 updated | 0 total | 0 available | 0 unavailable
...
# 代わりに telepresence によって作成された deployment は desired replicasets が 1 になっています
$ kdd frontend-89842e0a82b144c48181ff262f0575de
Name: frontend-89842e0a82b144c48181ff262f0575de
...
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
...
# 変な pod の image は telepresence proxy になっています
$ kdp frontend-89842e0a82b144c48181ff262f0575de-85955c7555-qbtrp
Name: frontend-89842e0a82b144c48181ff262f0575de-85955c7555-qbtrp
...
Containers:
frontend:
Container ID: docker://632c14c9118f0e67dfe5264606cf664c4438dedfb7610c1800543ec640d1f31e
Image: datawire/telepresence-k8s:0.94
Image ID: docker-pullable://datawire/telepresence-
k8s@sha256:2a8485d8a27b4e84751f714ba2f84753a2684aced4abdcc0894e63ece8013a7d
...

前述した「 Why Telepresence? にある図」の状態を垣間見ることができました :wink:

ちなみに今回のサンプルでは Cloudwatch logs でコンテナイメージに telepresence proxy が使われていることを確認できます。

Screen Shot 2018-11-30 at 5.56.51.png

ログが流れている状態で Ctrl-C を押すと telepresence proxy deployment が削除され、元の image を使った deployment と pods が再び起動します。


Catalog Service

AWS EKS (EC2) 以外のリソースにアクセスしている場合を試してみます。実は先ほどと同じように単に swap するだけでは Products ページは 500 エラーが返ってきてしまいます。

Catalog Service は Python の boto3 という AWS SDK を使って DynamoDB にアクセスしていますが、DynamoDB は CatalogserviceDDB-Policy がアタッチされたリソースからのアクセスのみを許可しています。そのため EKS (EC2) の外にある (ローカルの) コンテナからは DynamoDB にアクセスできません。

500 が返ってくるのは Catalog Service がレスポンスをそのようにまとめてしまっているからです。telepresence.log を見るときちんと CredentialsError が返ってきています。

File "/usr/local/lib/python3.7/site-packages/botocore/auth.py", line 357, in add_auth

raise NoCredentialsError
botocore.exceptions.NoCredentialsError: Unable to locate credentials


Credentials 情報を与える

一番簡単な解決法は boto3 が DynamoDB にアクセスするところ で DynamoDB の read 権限以上のアクセスキーを与えてあげることです。


catalogservice/catalog/app.py

# DO NOT DO THIS!!!

ddb = boto3.resource('dynamodb',
region_name=app.config['AWS_REGION'],
aws_access_key_id='YourAccessKeyId'
aws_secret_access_key='YourSecretAccessKey'
)

しかしこれはとても危険な方法ですので 絶対に行わないでください! :no_good: :no_good: :no_good:

(何かあっても責任持てませんよ〜)

せっかく k8s を使っているので Secrets を使う方法や、今回は AWS を使っているので KMS を使う方法など、ぜひ安全なやり方でアクセスキーを秘匿した上で boto3 に渡してあげてください :secret: :thumbsup: :thumbsup:

KMS を使う方法 も見つけましたが、今回は Secrets を使って環境変数としてアクセスキーを渡すことにしました。

※ Deployment.yml などに元々入れている環境変数は、telepresence で proxy に swap されたあとも使えます。


deploy/eks/prep.yml

apiVersion: v1

kind: Secret
...
data:
# Random secret data, replace with your own.
dynamouserkey: <base64 encoded aws access key>
dynamouservalue: <base64 encoded aws secret key>


deploy/services/catalogservice.yml

apiVersion: apps/v1beta1

kind: Deployment
...
containers:
- name: catalogservice
...
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: microservices-aws
key: dynamouserkey
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: microservices-aws
key: dynamouservalue
...


catalogservice/catalog/app.py

import os

ddb = boto3.resource('dynamodb',
region_name=app.config['AWS_REGION'],
aws_access_key_id=os.environ.get('SECRET_USERNAME'),
aws_secret_access_key=os.environ.get('SECRET_PASSWORD')
)


あとは新しい Secret と Deployment を $ kubectl apply -f path/to/file で反映させれば準備は完了です :muscle:


DynamoDB にアクセスする箇所を変更

もっと面白いことができればよかったのですが、私が Python に全く慣れていないため至極単純な変更です… :disappointed:

DynamoDB から product の情報を取得している部分を固定値にします。


catalogservice/catalog/routes.py

@api.route('/product/<string:product_id>')

def get_product(product_id):
try:
res = current_app.config['db'].get_item(
Key={
# 'id': product_id
'id': '1051094507639'
}
)

あとはいつもの swap コマンドで telepresence proxy が起動するのを待ちます。

$ cd reinvent2018-dev303-code/src/catalogservice

$ docker build -t dev303-catalog:dynamo .
$ telepresence --swap-deployment catalogservice --docker-run \
$ --rm -it dev303-catalog:dynamo

今回はうまく DynamoDB にアクセスすることができました! :clap: :clap:

サイトの方は…何を選択しても一番高い Rolex を買わせるサイトになってしまった… :metal: :metal:

rolex_g.gif


まとめ

Telepresence を使って EKS 上にある k8s クラスタをデバッグ (?) してみました。

複数の deployments を同時に telepresence で swap することはできませんでしたが、今回のようなちょっとした変更であればいちいちリモートにデプロイしたり、ローカルに k8s クラスタを構築しなくても動作確認を行うことができそうです。

今回使ったソースコード を載せるので自由に試してみてください :angel:

(Secrets の base64 エンコードされたアクセスキーの箇所だけダミーになっています)

それでは、Happy Kubernetes Life :christmas_tree: :santa:

次回の担当は @tanaka_733 さんです :rabbit: :rabbit: