Juju-62q's blog

参加記録やメモ書き、思考のまとめをしています

kiamをEKS/Fargateでいい感じに動かす

f:id:Juju_62q:20190714224653p:plain

この記事はAmazon EKS Advent Calendar 2019 8日目の記事です。 また、OpenSaaS Studio Advent Calendar 2019 9日目の記事としてクロスポストをしています。

TL;DR

  • kiam-serverはFargateを使うといい感じに動く
  • 古いaws-sdkに依存したツールを使うとIAM Role for ServiceAccountは使えない
  • kiam-agentを動かすとiptables書き換えにより、ノード側でできなくなることがあるから気をつけて

はじめに

AWS Fargate for AWS EKSがRe:InventのKeynoteで発表されましたね! Twitterの反応を見る限り非常に評価が高いようです。 が、個人的にはアプリケーションとObservability系のツールは引き離したいため1にDaemonSetの使えないFargateを常駐アプリの展開に使いたいとは基本的には思いません。 詰まるところ現状EKSをフルFargateの構成にできるとは考えておらず、ノード管理をしなくていい世界線が来るのはまだ先のようです。

一方でバッチジョブなどではノード管理したくないですし、常駐アプリのリソースをバッチに掻き回されるのも嫌なので非常に向いているように思えます。 要するに適材適所、使い所を考える必要があるわけです。今日はFargateに非常にむいていると自分が思っているユースケースを1つ紹介しようかなと思います。

EKSでの権限管理

2019年の9月くらいにIAM Role for ServiceAccountという機能が発表されました。 これにより技術書典で書いていた本の権限管理の章を大幅に書き直すことになったことが記憶に新しいです。 このブログでも

juju-62q.hatenablog.com

で触ってみた記事を上げています。

記事内でも触れていますがIAM Role for ServiceAccountはそこそこ新しいaws-sdkでないと動作しません。 これはServiceAccountによってPodにアタッチされたファイルをAWSのcredentialとして認識する機能が元々は存在しなかったというのが原因なわけですが、みなさんが開発しているアプリやその依存ライブラリは対応しているでしょうか? 自分の開発しているアプリの依存ライブラリは対応していませんでした。悲しいですね。

そういった人がEKSでPod単位の権限管理を行う場合、kiamやkube2iamを利用することが多いでしょう。 kiamとkube2iamの違いとしてkiamはkube2iamにインスパイアされて作られたものでkube2iamの"レースコンディションが発生し、間違ったRoleがPodに割り当てられる場合がある"という不具合を解消しています。 一方、権限をアタッチするエージェントとSTSとやり取りするサーバを別のノードで動かす必要があり、EKSで動かすためにはtaintをつけたり、Node Affinityをつけるなど工夫やアーキテクチャ的な汚さを許容する必要がありました。 しかしFargateをkiam-serverで動かすことでこれを解決できます。嬉しいですね。

kiamとは

github.com

kind: Pod
  annotations:
    iam.amazonaws.com/role: hogehoge

みたいな感じでPodのannotationにIAM Roleの名称を渡すことでPodとIAM Roleを対応させるツールです。

やり方

K8sのワーカーノードのメタデータサーバ(169.254.169.254)とのアクセスをiptablesの書き換えによりジャックすることで、kiam-agentの動作するノードで動いているPodにIAM Roleを錯覚させる2というような権限管理ツールです。 この方法はAWS Traditional IAM Managementであるために古いaws-sdkでも動作します。嬉しいですね。 namespaceごとにアタッチされるIAM Roleの名称を制限したりすることもでき、この辺りは地味に嬉しいです。

そもそもIAM Roleをアタッチするってどういうこと?

メタデータサーバにアクセスして、IAM Roleの名称とcredentialを取得できる状態を指すようです(間違っていたら教えてください)。

hogehogeというRoleがノードにアタッチされている場合下記のようなデータをメタデータサーバへのアクセスで取得可能です。

$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
hogehoge
$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/hogehoge
{
  "Code" : "Success",
  "LastUpdated" : "2019-12-08T07:20:38Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "*****",
  "SecretAccessKey" : "******",
  "Token" : "*****",
  "Expiration" : "2019-12-08T13:49:31Z"
}

kiamでは 169.254.169.254 へのアクセスをkiamに向け3STSと疎通して取得したIAMの情報をいい感じに返却することで、PodにIAM Roleを錯覚させています。

kiamのアーキテクチャ

kiamのアーキテクチャは以下のようになっています.

f:id:Juju_62q:20190715021137p:plain IAM Access in Kubernetes: kube2iam vs kiamから引用

kiam-agentはWorker Nodeのiptablesを書き換え、169.254.169.254 へのアクセスをkiam-agentへと誘導するようにしています。 また、kiam-agentはkiam-serverとやり取りすることで自身のノードで動いているPodとIAMの対応をいい感じに管理し、PodからIAM周辺のアクセスが来た時にPodのannotationで指定されたIAM Roleの情報を返却しています。

kiam-serverはK8sのイベントを監視し、必要なcredentialをSTSから取得します。 なおkiam-serverはメタデータサーバとやり取りし、STSと通信する権限を得る必要があるため、kiam-agentと同一のノードに同居することはできません。

ここまで来ると気づくわけです。kiam-serverを動かすためのnamespaceを作ってFargateで動かしてやればkiam-server専用ノード作らなくてもいい感じに管理できるんじゃね? さらにいえばkiam-serverは結構センシティブな存在で、クラスタアップグレードのときはデプロイする順番に注意しないといい感じにIAM Roleをアタッチできません。 この辺りをノードレスに管理することができればtaintやNodeAffinityなんかの設定をUserDataやmanifestから一掃することができ、最高です。ではやっていきましょう

Fargateでkiam-serverを動かしてみる

Gistに必要なファイルが列挙しています。必要なファイルはここから拾ってください。 必要に応じて確認してください。なお、パッと動かすためにTLS秘密鍵なんかも上げています。真似しないようにしてください。 kiamはリリースされていないものの最新のものであればIAM Role for ServiceAccountに対応しているため、kiam-serverの権限管理に利用していきましょう。

files to use kiam on EKS/Fargate · GitHub

下準備

kiam-serverが利用するIAM Policyを作成する

aws iam create-policy --policy-name kiam-server --policy-document file://kiam-server.json

クラスタとServiceAccountと紐づくIAMの作成

eksctl create cluster -f cluster.yaml
eksctl utils associate-iam-oidc-provider --config-file=cluster.yaml --approve
eksctl create iamserviceaccount --config-file=cluster.yaml --approve

テスト用のIAMの作成

aws iam create-role \
    --role-name kiam-test \
    --assume-role-policy-document file://kiam-test.json
aws iam attach-role-policy \
    --role-name kiam-test \
    --policy-arn "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"

Fargate用のnamespace作成

kubectl apply -f namespace.yaml

実際にデプロイする

kiam-serverのデプロイ

kubectl apply -f kiam-server.yaml

kiam-agentのデプロイ

kubectl apply -f kiam-agent.yaml

test用コンテナのデプロイ

kubectl apply -f kiam-test.yaml

IAM Roleの確認をしてみる

kubectl exec で適当にコンテナに入ってawscliをインストールして aws ec2 describe-instances を叩いてみる。

root@kiam-test:/# aws configure
AWS Access Key ID [None]: 
AWS Secret Access Key [None]: 
Default region name [None]: ap-northeast-1
Default output format [None]: json
root@kiam-test:/# aws ec2 describe-instances
{
    "Reservations": [
        {
            "Instances": [
                {
                    "Monitoring": {
                        "State": "disabled"
                    }, 
                    "PublicDnsName": "", 
                    "State": {
                        "Code": 16, 
                        "Name": "running"
                    }, 
                    "EbsOptimized": false, 
                    "LaunchTime": "2019-12-09T13:45:59.000Z", 

いい感じにみれましたね。

補足: kiamがTLSするためのオレオレ証明書作成コマンド

cfssl gencert -initca ca.json | cfssljson -bare ca
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem server.json | cfssljson -bare server
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem agent.json | cfssljson -bare agent

解説

kiam-server

IAM Role for ServiceAccountに対応したイメージがリリースされていないのでイメージタグはコミットハッシュを直で指定しています。趣があっていいですね。

      containers:
        - name: kiam
          image: quay.io/uswitch/kiam:b4a4891b136469511b3b011d38147cc1640b152e

kaimは元々ホストマシンの証明書を利用してHTTPS通信を行っているため証明書を適当に用意してあげる必要があります。 本来はDockerfileで対応するのがいいですが、コンテナレジストリを作るのが面倒くさいため、適当に当て込みます。 伴って、kiamのリポジトリのサンプルにはホストマシンへのマウントパスがありますが削除しています。

          lifecycle:
            postStart:
              exec:
                command:
                  - "/bin/sh"
                  - "-c"
                  - "apk update && apk add --no-cache ca-certificates && update-ca-certificates"
          env:
            - name: SSL_CERT_FILE
              value: /etc/ssl/certs/ca-certificates.crt
            - name: SSL_CERT_DIR
              value: /etc/ssl/certs

role-base-arnのauto-detectにはホストマシンのENIを利用しているためbase-arnのauto-detectはできません。 自身で使っているAWS IAM Roleのbase-arnを指定してあげましょう。

          command:
            - /kiam
          args:
            - server
            - --json-log
            - --level=warn
            - --bind=0.0.0.0:443
            - --cert=/etc/kiam/tls/server.pem
            - --key=/etc/kiam/tls/server-key.pem
            - --ca=/etc/kiam/tls/ca.pem
            - --role-base-arn=arn:aws:iam::${ACCOUNT_ID}:role/
            - --sync=1m

参考URL https://github.com/uswitch/kiam/issues/194

kiam-agent

kiam-serverは同一namespaceにありませんので適当に向き先を変えましょう。

          args:
            - agent
            - --iptables
            - --host-interface=eni+
            - --json-log
            - --port=8181
            - --cert=/etc/kiam/tls/agent.pem
            - --key=/etc/kiam/tls/agent-key.pem
            - --ca=/etc/kiam/tls/ca.pem
            - --server-address=kiam-server.kiam:443
            - --gateway-timeout-creation=1s

server証明書の設定でもnamespaceまたぎの通信を許可するようにしています。

  "hosts": [
    "kiam-server.kiam",
    "kiam-server.kiam.svc",
    "kiam-server.kiam.svc.cluster",
    "kiam-server.kiam.svc.cluster.local",
    "127.0.0.1",
    "localhost"
  ],

まとめ

AWS Fargate for AWS EKSにより、kiam利用時のアーキテクチャの気持ち悪さを一層し、管理コストをかなり下げることができました! EKS/Fargateは現状はwebアプリケーションを動かすための最適解とは自分は考えていません。 一方で、バッチ処理や必ず異なるノードで動かす必要があるアプリケーションとは一定の親和性があると考えています。今回はその一例を紹介してみました。 このようにEKS/EC2とEKS/Fargateを組み合わせることで管理コスト下げたり、アプリケーションを安定稼働させることができると思っています! kubeconでのManaged Worker Nodeの発表など2019年はEKSの進化が目覚ましい1年でしたね。来年も楽しみにしています。 みなさんはEKS/Fargate、何に使っていきますか?

参考

medium.com

www.bluematador.com


  1. Observability系のツールに引っ張られてアプリが落ちるのが嫌なので。K8s 1.18で追加されるサイドカーコンテナには期待しています。

  2. 本来はWorker NodeのIAMを参照する。

  3. 初期設定では 169.254.169.254 への全てのアクセスをkiam-agentに向けてしまいますが、"whitelist-route-regexp"というオプションを利用することでアクセスを許可するパスを正規表現で指定可能です。