Kubernetes の Pod 内で使う環境変数を Secret から設定する

Kubernetes 時代の環境変数設定。

目次

Kubernetes Secret について

Kubernetes クラスタ内で稼動する Pod が、API キーやトークンなどのいわゆる Credentials (クレデンシャル) 情報を扱う時、それをどこで管理するのが良いだろうか。

アプリ内へのハードコーディングは勿論避けたい。後からの差し替えが難しくなる、「設定ファイルに切り出して Docker コンテナに同梱」といった手法もイマイチ。

じゃあ環境変数として持たせよう、となると、Dockerfile の ENV 命令が思い付くが、コレも Docker イメージ丸ごとの差し替えが必要になってしまうので、環境変数以外の差分がウッカリ混じってしまう、といった事故 (デグレ) に繋がりかねない。

Docker は起動時に --env--env-file といったオプションを指定することで、環境変数を設定できる。しかし Kubernetes クラスタ内では別に docker run コマンドを自分で叩くワケではないので、別の方法を使うことになる。…というのが今回紹介するお話。

Kubernetes には Secret と呼ばれる秘密情報を保持するための領域があるので、コレを利用して、Pod に環境変数をブチ込んでみよう。

なお、似たようなモノに ConfigSet というモノもあるのだが、コチラは今回紹介しない。Secret との違いは Base64 で難読化されていないぐらいなので、主に外部の API サービスのエンドポイント URL など、直接覗かれてもあまり影響がない定数情報を保持するのに使える。設定方法や参照方法には大きな違いはない。

deployment.yamlenv プロパティ

まずは、Secret を使わない簡単なやり方から。deployment.yaml の中に環境変数を記述できる。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-app-deployment
spec:
  selector:
    matchLabels:
      app: my-app
  replicas: 1
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-app
        ports:
        - containerPort: 8080
        # 以下のように設定する
        env:
        - name: API_KEY
          value: "xxxxx"

仕組み的には、docker run --env オプションを使った時に似ているだろうか。

コレの難点は、秘密情報がベタ書きである点。この設定ファイルを漏らしてしまったらアウト。また、開発環境と本番環境を切り替えたりする時に、同じ設定ファイルを使い回せないこともやりづらい。

そこで、環境変数情報を Kubernetes Secret という別領域に逃してしまおう、というワケ。

--from-literal オプションで一つずつ登録

まずは Secret の登録方法の中でも簡単なモノから。先程と同様の環境変数を設定してみる。

$ kubectl create secret generic 【シークレット名】 --from-literal=【Key】=【Value】

# 先程の例だとこんな感じ
$ kubectl create secret generic my-api-key-secret --from-literal=API_KEY=xxxxx

# 一つの Secret に複数の Key・Value を保存させたりもできる
$ kubectl create secret generic my-secrets --from-literal=ANOTHER_API_KEY=yyyyy --from-literal=ANOTHER_TOKEN=zzzzz

このように登録できる。「シークレット名」は適当に決めて良い。登録したモノを参照するには、以下のように。

# 「シークレット名」を指定し、YAML 形式や JSON 形式で出力する
$ kubectl get secret my-secrets -o yaml

apiVersion: v1
data:
  ANOTHER_API_KEY: Zzzzzzzzzzz=
  ANOTHER_TOKEN: Llllllll=
kind: Secret
# 以下略

登録した情報は Base64 エンコードされているので、以下のようにデコードすれば元の値が分かる。

# ANOTHER_API_KEY 環境変数の値をデコードする
$ echo 'Zzzzzzzzzzz=' | base64 --decode

yyyyy
# デコードできた

envvalueFrom で1つずつ参照紐付け

さて、ココまでだと、Kubernetes Secret に秘密情報が登録されただけで、その情報を Pod で環境変数として参照できるような紐付けがされていない。deployment.yaml を以下のように変更し、この Secret を環境変数として読み込ませるようにしてみる。

spec:
  # 中略
  template:
    spec:
      containers:
      - name: my-app
        # 以下のように設定する
        env:
        - name: API_KEY  # Pod 内でこの環境変数名で参照できるようにする
          valueFrom:
            secretKeyRef:
              name: my-api-key-secret  # シークレット名
              key: API_KEY             # その Secret 内の Key 名
        - name: NEW_API_KEY  # Secret に登録した Key 名とは違う名前で環境変数を設定したりもできる
          valueFrom:
            secretKeyRef:
              name: my-secrets      # シークレット名
              key: ANOTHER_API_KEY  # シークレットに登録した Key 名

こんな感じ。シークレットに登録した Key と違う環境変数名を定義できるのは面白い。混乱するので揃えた方が良いけど。

このように設定し、Pod を再生成させれば変更が反映される (既存の Pod は環境変数をキャッシュしているので、deployment.yaml を Apply しても反映されない)。再生成した Pod のシェルにログインして env コマンドを叩けば、設定した環境変数が見えるはずだ。勿論、値は難読化されていない生の値になっている。

--from-env-file オプションで一括登録

このようなやり方でも環境変数を設定できなくはないが、登録する変数の個数が増えてくると、Secret の登録と deployment.yaml への記述を揃えるのが大変になってくる。

そこで、Docker における --env-file オプションのように、Key=Value を羅列した環境変数ファイルを一括登録し、それを一括読み込みしてみよう。

いわゆる .env ファイルの要領で、以下のように環境変数を用意する。

API_KEY=xxxxx

# 空行や、シャープ「#」で始まる行は無視されるので、コメントも書ける
ANOTHER_API_KEY=yyyyy
ANOTHER_TOKEN=zzzzz

Docker の --env-file もそうだが、コレって結局、シェルに source しているのとほとんど同じで、空行は無視されるし、シャープ # で始まる行はコメント行として無視される。

Lines beginning with # (i.e. comments) are ignored. Blank lines are ignored.

Secret への登録は次のように行う。

$ kubectl create secret generic my-env-file-secret --from-env-file=./.env

envFrom で一括読み込み

ファイルごと登録した Secret を環境変数として読み込むには、deployment.yaml を以下のように記述する。

spec:
  template:
    spec:
      containers:
      - name: my-app
        # 以下のように指定する
        envFrom:
        - secretRef:
          name: my-env-file-secret

このような deployment.yaml を設定し、Pod を再生成すれば OK。.env ファイルに定義していた複数の環境変数が一括で読み込まれる。

以上

--from-env-file で Secret を登録し、envFrom.secretRef で読み込む。コレがシンプルで良いかも。