この記事は 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)
-
Amazon EKS が発行した kubectl バイナリ も選択肢にはありますが、今回は
brew
で入れたものを使っています
-
Amazon EKS が発行した kubectl バイナリ も選択肢にはありますが、今回は
- AWS IAM Authenticator (v0.4.0-alpha.1)
- eksctl (v0.1.11)
- helm (v2.11.0)
使用したソースコード
今年の re:Invent のワークショップで使用された EKS を Cloudwatch と X-Ray に連携するためのサンプルプロジェクト を使用します。
- サービス数がそこそこある
- サービスが少ないとスペック で殴れば minikube や Docker for Mac with Kubernetes が使えてしまうため
- Dynamo DB や SQS など AWS EKS 以外のリソースを使っている
- MIT License (念のため…)
このサンプルは EC2 の m5.large インスタンスを4台も使用しているため、無料枠では収まりません。ご注意ください。
※ 請求書によると、今回だけで EC2 (61h) + EKS (75h) で合計 $10.54 かかりましたが、それ以外は無料枠に収まっていました。
使い終わったら cleanup 方法 の通りにリソースを削除していってください。VPC を削除できなくなります… (私です)。
k8s クラスタの構築
基本的には上記の サンプルプロジェクト の docs/
以下を順番にやっていけば問題ないです。ただし、
- ローカルから Telepresence を使うので Prerequisites の Manual Setup - Expert で各ライブラリをインストールしてください
- Cloud9 Setup ではなく Manual setup を使います
- X-Ray 連携まではやらないので Prerequisites のあとは lab1 + lab2: Collecting logs using Fluentd までで大丈夫です
- region は
us-west-2
(オレゴン) にするとすべてコピペで完成します
frontend
service から URL を取得し、ブラウザでこんなページが出てくれば OK です
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
というエラーになってしまいます。
eksctl
や aws eks
も aws-auth
にロールやユーザを追加するコマンドは現時点では存在せず、結局 Cloud9 からしかクラスタにアクセスできませんでした。今回はローカル端末から Telepresence を使って EKS にアクセスしたかったので、Cloud9 を使わない方法を選びました。
※ もし解決法をご存知の方がいらっしゃったらぜひご教示ください
Telepresence を使う
Telepresence は k8s にある特定の deployment を proxy に置き換え、そのサービスに来た通信をローカルのコンテナに向けてくれるライブラリです (Document の Why Telepresence? にある図が分かりやすいです)。How it works にあるように、Telepresence は内部的には kubectl port-forward
と os port-forward
を使って proxy を実現しています。
Proxying methods を見ると3種類の差し替え方法がありますが、今回は docker を使った方法のみ使用します。
$ 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.hbs の AnyCompany Shop
を Yuzuco's Shop
に置換してみます
...
<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 »</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 を投げるときにたくさん活用しましょう
先ほどと同じ URL で、きちんとローカルの image が反映されているのを確認できます
反映までの流れ
反映までの Deployment と Pod の状態を見てみます。Telepresence の仕組みをよく知っている方は読み飛ばしてください
# オリジナルの 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? にある図」の状態を垣間見ることができました
ちなみに今回のサンプルでは Cloudwatch logs でコンテナイメージに telepresence proxy が使われていることを確認できます。
ログが流れている状態で 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 権限以上のアクセスキーを与えてあげることです。
# DO NOT DO THIS!!!
ddb = boto3.resource('dynamodb',
region_name=app.config['AWS_REGION'],
aws_access_key_id='YourAccessKeyId'
aws_secret_access_key='YourSecretAccessKey'
)
しかしこれはとても危険な方法ですので 絶対に行わないでください!
(何かあっても責任持てませんよ〜)
せっかく k8s を使っているので Secrets を使う方法や、今回は AWS を使っているので KMS を使う方法など、ぜひ安全なやり方でアクセスキーを秘匿した上で boto3
に渡してあげてください
KMS を使う方法 も見つけましたが、今回は Secrets を使って環境変数としてアクセスキーを渡すことにしました。
※ Deployment.yml などに元々入れている環境変数は、telepresence で proxy に swap されたあとも使えます。
apiVersion: v1
kind: Secret
...
data:
# Random secret data, replace with your own.
dynamouserkey: <base64 encoded aws access key>
dynamouservalue: <base64 encoded aws secret key>
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
...
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
で反映させれば準備は完了です
DynamoDB にアクセスする箇所を変更
もっと面白いことができればよかったのですが、私が Python に全く慣れていないため至極単純な変更です…
DynamoDB から product
の情報を取得している部分を固定値にします。
@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 にアクセスすることができました!
サイトの方は…何を選択しても一番高い Rolex を買わせるサイトになってしまった…
まとめ
Telepresence を使って EKS 上にある k8s クラスタをデバッグ (?) してみました。
複数の deployments を同時に telepresence で swap することはできませんでしたが、今回のようなちょっとした変更であればいちいちリモートにデプロイしたり、ローカルに k8s クラスタを構築しなくても動作確認を行うことができそうです。
今回使ったソースコード を載せるので自由に試してみてください
(Secrets の base64 エンコードされたアクセスキーの箇所だけダミーになっています)
それでは、Happy Kubernetes Life
次回の担当は @tanaka_733 さんです