IAM Roles for Service Accountsに必要なリソースを作成するTerraformモジュールを書いた
kopsなどで自前構築しているKubernetesクラスタでIAM Roles for Service Accounts(IRSA)を利用する時に必要なAWSリソースを作成するTerraformモジュールを書きました。
kopsとTerraformの組み合わせでIRSAを利用する方法は下記の記事を参考にさせていただきました。ありがとうございます。
Terraformモジュールの使い方
TerraformモジュールのREADME.mdに使い方を書いています。本記事に日本語で使い方を書いておきます。
鍵ペアの生成
まず、鍵ペアを生成します。
mkdir -p irsa cd irsa ssh-keygen -t rsa -b 2048 -f sa-signer.key -m pem ssh-keygen -e -m PKCS8 -f sa-signer.key.pub > sa-signer-pkcs8.pub go run /amazon-eks-pod-identity-webhook/hack/self-hosted/main.go -key sa-signer-pkcs8.pub | jq '.keys += [.keys[0]] | .keys[1].kid = ""' > jwks.json
Terraformの実行
Terraformで kubernetes-irsa
モジュールを適用します。
resource "random_uuid" "irsa_s3_bucket_name" { } module "irsa" { source = "int128/kubernetes-irsa/aws" oidc_s3_bucket_name = "oidc-${random_uuid.irsa_s3_bucket_name.result}" oidc_jwks_filename = "./irsa/jwks.json" } output "irsa_oidc_issuer" { description = "Issuer of OIDC provider for IRSA" value = module.irsa.oidc_issuer } output "irsa_pod_identity_webhook_ecr_repository_url" { description = "URL to the ECR repository for eks/pod-identity-webhook" value = module.irsa.pod_identity_webhook_ecr_repository_url } resource "local_file" "irsa_kops_cluster_yaml" { filename = "./irsa/kops.yaml" content = module.irsa.kops_cluster_yaml }
このTerraformモジュールを実行すると、下記のAWSリソースが作成されます。
このTerraformモジュールで作成されるCodeBuildプロジェクトを実行すると、CodeBuild上で amazon-eks-pod-identity-webhook のDockerイメージをビルドしてECRリポジトリにpushできます。ローカルでビルドする手間が省けます。
kopsの設定
続いて、kopsのクラスタ設定を変更します。Terraformを実行すると以下の内容で kops.yaml
が生成されるので、{base64}
の部分を実際の鍵に置き換えます。
spec: fileAssets: - content: {base64 sa-signer.key} isBase64: true name: service-account-signing-key-file path: /srv/kubernetes/assets/service-account-signing-key - content: {base64 sa-signer-pkcs8.pub} isBase64: true name: service-account-key-file path: /srv/kubernetes/assets/service-account-key kubeAPIServer: apiAudiences: - sts.amazonaws.com serviceAccountIssuer: https://oidc-RANDOM.s3.amazonaws.com serviceAccountKeyFile: - /srv/kubernetes/server.key - /srv/kubernetes/assets/service-account-key serviceAccountSigningKeyFile: /srv/kubernetes/assets/service-account-signing-key
base64 irsa/sa-signer-pkcs8.pub | sed -e 's/=//g' base64 irsa/sa-signer.key | sed -e 's/=//g'
kops.yaml
ファイルの内容をkopsに適用します。
kops edit cluster
pod-identity-webhookのデプロイ
amazon-eks-pod-identity-webhook リポジトリのMakefileを実行して、Kubernetesクラスタにpod-identity-webhookをデプロイします。
cd /amazon-eks-pod-identity-webhook make cluster-up IMAGE=REGISTRY_ID.dkr.ecr.REGION.amazonaws.com/eks/pod-identity-webhook
動作確認
最終的に、Service Accountを割り当てたPodがS3バケットにアクセスできるか確認します。
まず、S3バケットにアクセスするためのIAMロールを作成します。
resource "aws_iam_role" "s3-echoer" { name = "s3-echoer" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "${module.irsa.oidc_provider_arn}" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "${module.irsa.oidc_issuer}:sub": "system:serviceaccount:default:s3-echoer" } } } ] } EOF } resource "aws_iam_role_policy_attachment" "s3-echoer" { role = "${aws_iam_role.s3-echoer.name}" policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" }
Service Accountを作成します。先ほど作成したIAMロールのARNをアノテーションに含めます。
kubectl create sa s3-echoer
kubectl annotate sa s3-echoer eks.amazonaws.com/role-arn=arn:aws:iam::ACCOUNT_ID:role/s3-echoer
aws-cliを実行してS3バケットのリストが表示されることを確認します。
kubectl run aws -i --rm --image amazon/aws-cli --restart=Never --serviceaccount=s3-echoer -- s3 ls
まとめ
kopsなどで自前構築しているKubernetesクラスタでIAM Roles for Service Accounts(IRSA)を利用する時に必要なAWSリソースを作成するTerraformモジュールを書きました。面倒な構築作業を簡略化できるので、役に立ったら幸いです。
Prometheus Alertmanagerの通知テンプレートを改善する
AlertmanagerのSlack通知テンプレートで四苦八苦したのでメモを残します。
Prometheus OperatorのHelm chartには便利なデフォルトルールが組み込まれています。例えば、Podが頻繁に再起動している場合に通知するルール(KubePodCrashLooping)が組み込まれています。しかし、Slackやメールなどのアラート通知は自分で設定する必要があります。
アラート通知の書き方は公式の Notification template examples | Prometheus に例がありますが、そのままではうまくメッセージが表示されない場合があります。ドキュメントでは以下の例が挙げられていますが、 summary
や description
というアノテーションが付いているルールでないとメッセージが空白になってしまいます。また、CommonAnnotations
を使っているため、複数のアラートがグループ化された場合は共通のアノテーションしか表示されません。
- name: 'team-x' slack_configs: - channel: '#alerts' # Alertmanager templates apply here. text: "<!channel> \nsummary: {{ .CommonAnnotations.summary }}\ndescription: {{ .CommonAnnotations.description }}"
通知テンプレートのよい例はないか探したところ、下記の記事が参考になりました。
Prometheus Operatorのデフォルトルールには message
というアノテーションが付いているため、テンプレートでは message
を使うとよさそうです。また、グループ化された場合にうまく表示されない問題は Alerts
フィールドに入っているアラートを列挙すると解決できそうです。
というわけで試行錯誤の結果、以下のテンプレートを利用しています。
alertmanager: config: receivers: - name: "null" - name: slack slack_configs: - api_url: https://slack.example.com/webhook channel: "#alerts" send_resolved: true username: dev/Alertmanager icon_url: https://prometheus.io/assets/favicons/favicon.ico title: | {{ .Status | toUpper }} text: | {{- range .Alerts }} - **{{ .Labels.alertname }}**: {{ .Annotations.message }} {{- end }} route: receiver: "null" routes: - receiver: "null" match: alertname: Watchdog - receiver: slack
Fluxによるアプリケーションの継続的デプロイ
FluxのAutomated deployment of new container imagesを利用して、Kubernetes上でアプリケーションの継続的デプロイを構成する機会があったのでまとめます。
GitOpsの基本形
GitOpsを採用する場合は下図のデプロイフローが基本形になります。
具体的には以下の流れになります。
- 開発者がアプリケーションリポジトリを更新する。
- CIがアプリケーションをビルドし、新しいDockerイメージをプッシュする。
- 開発者がマニフェストリポジトリを更新する。
- GitOpsが新しいマニフェストをデプロイする。
これはWeaveworksのGuide To GitOpsで説明されている開発フローです。チームの大きさやアプリケーションの特性によっては、以下のような違いが出てくると思います。
- アプリケーションリポジトリのブランチ戦略
- アプリケーションリポジトリの更新を契機にビルドとデプロイを行うか、ビルドのみ行うか
- マニフェストリポジトリとクラスタを自動的に同期させるか、手動で同期するか
- 実行環境(dev/stg/prod)によって自動と手動を使い分ける
継続的デプロイに必要なもの
チームが小さい、あるいは、チームが自己完結している場合は、アプリケーションを更新した契機で開発環境にデプロイされる方が効率的に開発できることがあります。ビジネス上の受入テストまで自動化している場合は本番環境まで自動デプロイすることもあります。これを継続的デプロイ(Continuous Deployment)といいます。下記の記事が参考になります。
継続的デプロイを実現するにはアプリケーションのビルドとデプロイを連続して行う必要があります。Cloud FoundryのようなPaaSを利用するとこのような開発フローを簡単に実現できますが、KubernetesのようなCaaSでは仕組みを作る必要があります。具体的には、以下が自動的に流れる仕組みが必要になります。
- 開発者がアプリケーションリポジトリを更新する。
- CIがアプリケーションをビルドし、新しいDockerイメージをプッシュする。
- (何らかの仕組みで)マニフェストリポジトリにあるマニフェストのイメージタグを書き換える。
- GitOpsが新しいマニフェストをクラスタに適用する。
今のところ、GitOpsで継続的デプロイを実現するにはFluxを利用するか、上記3を自作する必要があります。Argo CDでは実現できません。
Flux automated deployment of new container images
FluxにはAutomated deployment of new container imagesという機能があります。この機能を有効にすると、Dockerレジストリに新しいイメージが存在したらマニフェストのイメージタグを新しいものに書き換えてくれます。ちょうど下図のようなデプロイフローになります。
具体的には以下の流れになります。
- Gitリポジトリからマニフェストを取得する。
- Dockerレジストリにあるイメージをスキャンする。
- 新しいタイムスタンプのイメージが存在する場合は以下を行う。
- 新しいマニフェストをクラスタに適用する。
公式ドキュメントには詳細が書かれていないので、Fluxのログを元に動作を推測しています。間違っていたら教えてください。
Automated deployment of new container imagesを有効にするには、Deploymentに以下のアノテーションを付加します。
metadata: annotations: fluxcd.io/automated: "true"
スキャン対象のイメージタグを正規表現やglobで絞り込むことも可能です。以下に例を示します。
metadata: annotations: fluxcd.io/automated: "true" # appコンテナについて、master-で始まるイメージタグのみ自動更新とする fluxcd.io/tag.app: glob:master-* spec: template: spec: containers: - name: app image: ...
スキャン対象のイメージ名やDockerレジストリを限定することも可能です。詳しくはfluxdの引数リストを参照してください。
下記のリポジトリにデモを置いているので、よかったらご覧ください。
FlaggerによるCanary Release
参考までに、FluxとFlaggerを併用するとCanary Releaseが可能です。アプリケーションリポジトリを更新した契機でデプロイを開始し、一定の基準(例:10%のトラフィックに新しいバージョンを適用して99%が成功ステータスだった場合)を満たしたら全トラフィックを新しいバージョンに切り替える、といったことも可能です。
Flaggerを使うにはIstioなどのサービスメッシュが必要です。また、Kialiなどの可視化ツールがあるとトラフィックの切り替えがよく分かります。詳しくは下記の記事が参考になります。
Fluxによる継続的デプロイの課題
Fluxには以下の制約があるため、実際に運用してみると辛いところがあります。
- デプロイが成功した場合やエラーが発生した場合の通知がない。
- WeaveworksとしてはWeave Cloudを使ってほしいみたいです。
- fluxdのログをelastalertなどで監視してSlackに通知する仕組みを作れば可能です。
- デプロイが成功すると
flux-sync
タグが更新されるので、GitHubでタグの更新をSlackに通知するという方法もあります。 - FlaggerにはSlack通知の機能があります。
- GUIがないので、クラスタで何が起きているのか分かりにくい。
- 同期状態を管理するためにマニフェストリポジトリに
flux-sync
というタグが作成される。flux-sync
タグ契機でCIが実行されないように設定するとよいです。
参考までに、fluxctlコマンドを利用すると以下のようなステータスを表示できます。
% fluxctl --k8s-fwd-ns flux -n develop list-images WORKLOAD CONTAINER IMAGE CREATED develop:deployment/echoserver echoserver gcr.io/google_containers/echoserver '-> 1.10 22 Mar 18 17:30 UTC 1.9 14 Feb 18 22:35 UTC 1.8 27 Jul 17 18:50 UTC 1.7 18 Jul 17 19:23 UTC 1.6 16 Jun 17 22:41 UTC 1.5 06 Jun 17 23:28 UTC 1.4 29 May 16 05:03 UTC 1.3 08 Mar 16 19:10 UTC 1.2 23 Feb 16 02:18 UTC 1.1 26 Jan 16 18:13 UTC
デプロイの前後で自動テストなどの処理を入れたい場合は今のところ方法がありません。Tekton CDでは柔軟なデプロイメントパイプラインを構成できるので、今後Tekton CDのエコシステムが発達してきたら有力候補になるかもしれませんね。