コンテナはどんな環境でも同じように動くよ!ってのがウリの1つではあるのですが、そのためには環境ごとに異なる指定値はコンテナ外でKey/Valueを設定し、Pod の環境変数として扱えるようにしてあげる必要があります。
ここでは、色んな環境変数の扱い方があるということを整理するところまでやって、取り扱い方の考察については次回に続きたいと思います。
マニフェストによる設定
ここでは Deployment を使った、環境変数の定義方法についてまとめていきます。env
ここで書いた env の name, value がそのまま環境変数になる火の玉ストレートです。下記では ENV=production のようなヤツが設定されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
resource "kubernetes_deployment" "main" { ... spec { ... template { ... spec { ... container { ... env { name = "ENV" value = local.env } } } } } } |
複数に対応するために、こう dynamic で書いたほうがよさげですね。
(実際にはもう少し複雑な、Workspaceごとの設定になってるんだけど、簡略化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
locals { eks_config = { env_basic = { ENV = local.env, SERVICE = local.service_name, } } } resource "kubernetes_deployment" "main" { ... container { ... dynamic "env" { for_each = local.eks_config["env_basic"] content { name = env.key value = env.value } } ... } |
ConfigMap
こちらは、ハッシュとして設定した値を、いったん ConfigMap に登録して、それを Deployment で読み込む形になっています。KVS ではないですが、値を保存しておく場所があるということは、他のリソースでも共有して扱えるので、直env とは使い分けってとこでしょうか。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
locals { eks_configmap_ref_prefix = "env-" eks_config = { env_config_map = { TEST_CONFIGMAP_STRING = "string_for_env" } } } resource "kubernetes_config_map" "env" { count = local.on_eks ? 1 : 0 metadata { name = "${local.eks_configmap_ref_prefix}${local.env}" } data = local.eks_config["env_config_map"] depends_on = [aws_eks_cluster.main] } resource "kubernetes_deployment" "main" { ... container { ... env_from { config_map_ref { name = "${local.eks_configmap_ref_prefix}${local.env}" } } } ... } |
Secret
形だけみたら ConfigMap とたいして変わらないのですが、Secret に登録される値は base64 エンコーディングされます。Terraform だと自分でエンコーディング書かないでよいだけで、マニフェストYAML書いたりすると、encode/decode が必要になります。Secret って名前のくせに、なんも暗号化されてないやんけって思うのは当然なので、このあたりを参考に頭の体操の時間になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
locals { eks_configmap_ref_prefix = "env-" eks_config = { env_secret = { TEST_SECRET_SECURE = "secure_for_env" } } } resource "kubernetes_secret" "env" { count = local.on_eks ? 1 : 0 metadata { name = "${local.eks_configmap_ref_prefix}${local.env}" } data = local.eks_config["env_secret"] depends_on = [aws_eks_cluster.main] } resource "kubernetes_deployment" "main" { ... container { ... env_from { secret_ref { name = "${local.eks_configmap_ref_prefix}${local.env}" } } } ... } |
マニフェストで設定することの是非についてはいったん置いといて、次の方法へ参ります。
AWS Systems Managerによる設定
ちょいマイナーな AWS Systems Manager というサービスがありまして、見てみると色んなシステムをマネージしてくれそうなサービスなのですが、サイドメニューの一番下に、Parameter Store というのがあります。ここは、Key/Value の設定値などを普通に String として保存したり、Valueの部分を暗号化できる SecureString を保存できたりします。それを他のサービスで取り出して使います。今回は取り出すのは EC2 Node の中の Pod さんということになります。Parameter Store と Secrets Manager
と、その前にこの記事は読んでおいたほうがよいです。Secrets Manager っていうまた別のサービスがあって、似た用途に使えるのですが、現在の事情とか一般的要件を踏まえて華麗にスルーし、Parameter Store について追っていきたいと思います。
IAM Role
まず、Parameter Store の情報を Pod が引き出すために権限を確認します。下準備の記事で記載しましたが、その抜粋でこんな部分がありました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
locals { eks_ssm_prefix = "/app" } resource "aws_iam_role_policy" "eks_node_custom" { ... policy = <<JSON { "Version": "2012-10-17", "Statement": [ ... { "Effect": "Allow", "Action": [ "ssm:GetParameter*", "secretsmanager:GetSecretValue", "kms:Decrypt" ], "Resource": [ "arn:aws:ssm:${local.region}:${local.service_account_id}:parameter${local.eks_ssm_prefix}/*", "arn:aws:kms:${local.region}:${local.service_account_id}:alias/aws/ssm" ] } ] } |
この記述で、SSMからの特定パス以下の、通常パラメータとSecret値の読み込みができるようになります。
今回は、パスの規則を /app/${local.env}/key_name として設計しました。/app によって、アプリケーション用途であること、env によって環境ごとにそれ以下を全て利用するよう区切るためです。ここの設計は要件によってだいぶ変わりそうなところですが、いったんパラメータのパスと、ポリシーでの指定にて範囲を管理する形でいくことにします。
Parameter Store の登録
管理画面にて、「文字列 = String」として通常のKey/Value として登録したり、「安全な文字列 = SecureString」としてKMS alias/aws/ssm を指定して暗号化したりと、用途によってタイプを変えて登録します。例えば、/app/production/TEST_PARAMETER_STORE_SECURE = SecureValue とかですね。SecureString にしても、権限があれば作成後も値を確認することができます。
この作業を Terraform で行わなかった理由は、次回の考察で書いていくとします。
PodでParameter取得
PodにSSMを読む権限があり、ParameterStore に値があるので、あとは読みこんで使えるようにするだけです。その処理のタイミングはいくつかあるかもですが、Docker の entrypoint でやってみたのがこちら。これは何をしているかというと、特定パス以下のパラメータを全部読み込み、いらない部分を削ぎ落として、全てのパラメータを export で環境変数として登録しています。もちろん、aws, jq コマンドは事前にイメージに埋め込んであります。処理内容がよくわからない場合は、Podじゃなく手元の環境ででも、パイプで渡しているところを順に1つずつ実行して見ていけば、どんな文字列をどう処理しているかわかります。
この処理だけパッと見たら、なんか複雑怪奇なことしてるなって感じるかもですが、よく見たら、わりと綺麗にまとまっている黒魔術一歩手前のテクニックだと思います:-)
環境変数の確認
Podで直接確認
コンテナイメージには基本的にSSHは入れていないでしょうから、Podへ直にコマンドを送ることで環境変数を確認します。
1 |
kubectl exec -it ${pod_name} /bin/sh -c env |
WEBサーバー経由で確認
Podで起動したデーモンは、環境変数が使えるようになっています。テスト用の表示確認用コードでは、env | sort を出しているので、アクセスしたらズラッと環境変数が並び、その中にあることを確認できますので、ALB に curl で https したり、EC2 NodePort に直接 curl や telnet で http を送信して、テスト用のレスポンスを確認してみましょう。とりあえず、それっぽい手法群についてまとめました。外っ面ばかり触ってると、ふわふわした夢心地なインフラ感がありますが、内っ側を整えだすと少しずつ本番運用へのイメージというか実感が湧いてきてよいですね。
次回はこれらをどう取り扱っていくべきなのか、についての考察をしていきたいと思います。