LoginSignup
13

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-30

この記事は 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:

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13