Terraformでcloud-configを記述する
AWSのEC2インスタンスでは、User Dataにシェルスクリプトを渡すことで起動時にスクリプトを実行できます。ファイルを配置したり、パッケージをインストールしたり、といった複雑なことがやりたい場合はcloud-initが便利です。詳しくは公式ドキュメントの例を参照してください。
例えば、下記のようなYAMLをUser Dataに指定すると、OSの起動時にsystemdで独自のサービスを開始できます。
#cloud-config write_files: - path: /etc/systemd/system/helloworld.service" content: | [Unit] Description = Hello World [Service] ExecStart = /usr/local/bin/helloworld.sh Restart = always Type = simple [Install] WantedBy = multi-user.target - path: /usr/local/bin/helloworld.sh" permissions: 0755 content: | #!/bin/sh while true; do date; sleep 1; done runcmd: - ["systemctl", "start", "helloworld"] - ["systemctl", "enable", "helloworld"]
Terraformでcloud-configを指定する場合、下記のように外部ファイルを読み込むと簡単に書けます。
resource "aws_instance" "this" { user_data_base64 = filebase64('./cloud-config.yaml') }
ただ、YAMLの中にインラインでシェルスクリプトなどをベタ書きしているため、可読性がよくないというデメリットがあります。エディタで開いてもsyntax highlightingしてくれません。
そこで、下記のように動的にYAMLを組み立てることで、シェルスクリプトなどのファイルを外部化できます。
resource "aws_instance" "this" { user_data_base64 = base64encode(join("\n", [ "#cloud-config", yamlencode({ write_files : [ { path : "/etc/systemd/system/helloworld.service", content : file("./helloworld.service"), }, { path : "/usr/local/bin/helloworld", content : file("./helloworld.sh"), permissions : "0755", }, ], runcmd : [ ["systemctl", "enable", "helloworld"], ["systemctl", "start", "helloworld"], ], }) ])) }
サンプルが下記にあるので参考にどうぞ。
Homebrew FormulaをCircleCIでテストする
Homebrew Formulaは一度書くと変更する機会があまりないですが、外部からPRを受け付ける場合はCIでテストしておくと便利です。linuxbrew/linuxbrew イメージを利用すると、LinuxのCI runnerでもbrewコマンドを利用できます。
CircleCIの場合は下記の設定でFormulaをテストできます。
# .circleci/config.yml version: 2 jobs: build: docker: - image: linuxbrew/linuxbrew:1.9.3 environment: HOMEBREW_NO_AUTO_UPDATE: 1 steps: - checkout - run: brew install foobar.rb - run: brew test foobar
この例ではリポジトリにある foobar.rb
というFormulaをテストしています。デフォルトでは Updating Homebrew... でめっちゃ待たされるので HOMEBREW_NO_AUTO_UPDATE
を設定しています。
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モジュールを書きました。面倒な構築作業を簡略化できるので、役に立ったら幸いです。