Kubernetes の 環境変数設定方法を調べる

作業メモ。

[Kubernetes完全ガイド impress top gearシリーズ](https://www.amazon.co.jp/dp/B07HFS7TDT/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)を読みながら手元で確認した時のメモ。

# 環境

```bash
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.3", GitCommit:"2bba0127d85d5a46ab4b778548be28623b32d0b0", GitTreeState:"clean", BuildDate:"2018-05-28T20:03:09Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.5-eks-6bad6d", GitCommit:"6bad6d9c768dc0864dab48a11653aa53b5a47043", GitTreeState:"clean", BuildDate:"2018-12-06T23:13:14Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
```

Kubernetes 環境としては EKS を利用。

# 概要

環境変数を利用することでコンテナに任意の情報を与えることが出来る。
例えば DB への接続先 URL,ユーザー名やパスワード情報など。

1. 静的設定
2. Pod の情報
3. コンテナの情報
4. Secret リソースの機密情報
5. ConfigMap リソースの設定値

# 1. 静的設定

[Define an environment variable for a container](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#define-an-environment-variable-for-a-container)

上記にあるように **env** もしくは **envFrom** フィールドでマニュフェストファイルに定義できる。

```evnvars.yaml
apiVersion: v1
kind: Pod
metadata:
  name: envar-demo
  labels:
    purpose: demonstrate-envars
spec:
  containers:
  - name: envar-demo-container
    image: gcr.io/google-samples/node-hello:1.0
    env:
    - name: DEMO_GREETING
      value: "Hello from the environment"
    - name: DEMO_FAREWELL
      value: "Such a sweet sorrow"
```

やってみる。

```bash
# Pod 作成
$kubectl apply -f https://k8s.io/examples/pods/inject/envars.yaml
pod "envar-demo" created

# describe すると設定がわかる
$kubectl describe pod envar-demo

・・・
    Environment:
      DEMO_GREETING:  Hello from the environment
      DEMO_FAREWELL:  Such a sweet sorrow

# exec を使って env コマンドを実行すると設定されていることが確認出来る
$ kubectl exec -it envar-demo -- env|grep DEMO
DEMO_GREETING=Hello from the environment
DEMO_FAREWELL=Such a sweet sorrow

```

## マニュフェストファイルで環境変数を利用する場合の注意点

[Using environment variables inside of your config](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/#using-environment-variables-inside-of-your-config)

上記のようにマニュフェストファイルに環境変数を定義し、マニュフェストファイルで参照(command や args など)する場合には注意が必要。
通常であれば **${GREETING}** という形式で参照できるがマニュフェストファイル内での読み込む場合には **$(GREETING)** というように「{}」ではなく、「()」を利用する。

```evnvars2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: print-greeting
spec:
  containers:
  - name: env-print-demo
    image: bash
    env:
    - name: GREETING
      value: "Warm greetings to"
    - name: HONORIFIC
      value: "The Most Honorable"
    - name: NAME
      value: "Kubernetes"
    command: ["echo"]
    args: ["$(GREETING) $(HONORIFIC) $(NAME)"]
```

```bash
# Pod 作成
$kubectl apply -f envvars2.yaml
pod "print-greeting" created

# マニュフェストファイルに定義した環境変数が args で読み込めている
$kubectl logs print-greeting
Warm greetings to The Most Honorable Kubernetes
```

# 2. Pod の情報、3. コンテナの情報　

[Expose Pod Information to Containers Through Environment Variables](https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/)

Pod の情報(どのノードで起動しているか、自分の IP アドレスなど）について **fieldRef** を使って参照可能。
参照可能な値については `kubectl get pods -o yaml` などで確認出来る。

コンテナの情報(CPU リソース設定など)は **resourceFieldRef** を使って参照可能。
参照可能な値については `kubectl get pods -o yaml` などで確認出来る。

同じような方法で取得が可能なので今回は Pod の情報取得を試す。

```dapi-envars-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-fieldref
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
          printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
          sleep 10;
        done;
      env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: MY_POD_SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
  restartPolicy: Never
```

```bash
# Pod 作成
$ kubectl apply -f dapi-envars-pod.yaml
pod "dapi-envars-fieldref" created

# Pod の情報が確認できる。while tru; doしているので継続的に表示される
$kubectl logs dapi-envars-fieldref

ip-172-31-23-75.ap-northeast-1.compute.internal
dapi-envars-fieldref
default
172.31.24.230
default

ip-172-31-23-75.ap-northeast-1.compute.internal
dapi-envars-fieldref
default
172.31.24.230
default

# exec しても環境変数が設定されている事が分かる
$kubectl exec -it dapi-envars-fieldref -- env |grep MY
MY_POD_NAMESPACE=default
MY_POD_IP=172.31.24.230
MY_POD_SERVICE_ACCOUNT=default
MY_NODE_NAME=ip-172-31-23-75.ap-northeast-1.compute.internal
MY_POD_NAME=dapi-envars-fieldref

```

# 4. Secret リソースの機密情報

[Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)

[Distribute Credentials Securely Using Secrets](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/)

[ シークレット ](https://cloud.google.com/kubernetes-engine/docs/concepts/secret?hl=ja)

ユーザー名・パスワードなどの機密情報を設定する場合には Secret を利用する。
なお、Secret は Base64 エンコードされているだけで暗号化されていない。
その為、Git リポジトリにマニュフェストファイルを配置するような場合、[kubesec](https://github.com/shyiko/kubesec)を使って暗号化するなど対応が必要。

以下より試す。

## Base64 エンコード化する

[Convert your secret data to a base-64 representation](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#convert-your-secret-data-to-a-base-64-representation)

Secret を使うためにはまずは設定したい値を Base64 エンコード化する。

```bash
$echo -n 'my-app' | base64
bXktYXBw

$ echo -n '39528$vdg7Jb' | base64
Mzk1MjgkdmRnN0pi
```

それぞれの値をメモしておく。

## Secret 作成

以下のうようなマニュフェストファイルを作成する。

```secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: test-secret
data:
  username: bXktYXBw
  password: Mzk1MjgkdmRnN0pi
```

作成。

```bash
$kubectl create -f https://k8s.io/docs/tasks/inject-data-application/secret-pod.yaml
secret "test-secret" created

# Secret の存在及び 2つのデータがあることを確認
$ kubectl get secret test-secret
NAME          TYPE      DATA      AGE
test-secret   Opaque    2         35s

# describe するとより詳細をな情報が確認出来る
$kubectl describe secret test-secret
Name:         test-secret
Namespace:    default
Labels:       <none>
Annotations:
Type:         Opaque

Data
====
password:  12 bytes
username:  6 bytes
```

## Secret を環境変数経由で Pod からアクセスする

環境変数で Secret から値を参照する場合、 **secretKeyRef** を設定する。
なお、環境変数で参照した際には Base64 デコードされて設定されている。

```secret-envars-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-envars-test-pod
spec:
  containers:
  - name: envars-test-container
    image: nginx
    env:
    - name: SECRET_USERNAME
      valueFrom:
        secretKeyRef:
          name: test-secret
          key: username
    - name: SECRET_PASSWORD
      valueFrom:
        secretKeyRef:
          name: test-secret
          key: password
```

```bash
$ kubectl create -f https://k8s.io/docs/tasks/inject-data-application/secret-envars-pod.yaml
pod "secret-envars-test-pod" created

# Secret の内容が Base64 デコードされて環境変数に設定されている。
$ kubectl exec -it secret-envars-test-pod -- env |grep SECRET
SECRET_USERNAME=my-app
SECRET_PASSWORD=39528$vdg7Jb
```

なお、Secret は環境変数だけでなく、 Volume を通して値を取得する事もできる。

[Create a Pod that has access to the secret data through a Volume](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#create-a-pod-that-has-access-to-the-secret-data-through-a-volume)

volumes で Secret 名を定義し、対象の Volume をマウントする。

```secret-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
          # name must match the volume name below
          - name: secret-volume
            mountPath: /etc/secret-volume
  # The secret data is exposed to Containers in the Pod through a Volume.
  volumes:
    - name: secret-volume
      secret:
        secretName: test-secret

```

試す。

```bash
$kubectl create -f https://k8s.io/docs/tasks/inject-data-application/secret-pod.yaml
pod "secret-test-pod" created

# マウントした /etc/secret-volume 配下に2つのファイルが存在する
$kubectl exec -it secret-test-pod -- ls /etc/secret-volume
password  username

# 環境変数と同様に Base64　デコードされて設定されている
$kubectl exec -it secret-test-pod -- cat /etc/secret-volume/username
my-app%
$kubectl exec -it secret-test-pod -- cat /etc/secret-volume/password
39528$vdg7Jb%

```

# 5. ConfigMap リソースの設定値

[Configure a Pod to Use a ConfigMap](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/)

[ConfigMap](https://cloud.google.com/kubernetes-engine/docs/concepts/configmap?hl=ja)

機密性がないような情報の場合、ConfigMap を使って Key=Value 形式で値を予め保存し、環境変数で参照する事が出来る。
また、Secret と同じように ConfigMap においても Volume をマウントして値を参照する事もできる(基本的に方法は同じ)

ConfigMap を予め作成し、値を環境変数から参照する。

## ConfigMap を作成する

ディレクトリを指定する、ファイルを指定する、 literal valuesから作成するなどいくつかの方法がある。

今回はマニュフェストファイル2つ作成して環境変数に設定し、参照する。

[Define container environment variables with data from multiple ConfigMaps](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#define-container-environment-variables-with-data-from-multiple-configmaps)

```configmap1.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
```

```configmap2.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO
```

```bash
# 作成
$kubectl apply -f configmap1.yaml
configmap "special-config" created
$kubectl apply -f configmap2.yaml
configmap "env-config" created

# 2つの ConfigMap が存在し、データはそれぞれ一つ(Key=value のペアが一つという意味)
$ kubectl get configmap
NAME             DATA      AGE
env-config       1         53s
special-config   1         1m

# Secret と違い、describe すると Value が確認出来る
$kubectl describe configmap env-config
Name:         env-config
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","data":{"log_level":"INFO"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"env-config","namespace":"default"}}


Data
====
log_level:
----
INFO
Events:  <none>

```

## Pod から環境変数経由で参照する

**configMapKeyRef** を指定して環境変数で参照可能。


```configmap-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: env-config
              key: log_level
  restartPolicy: Never
```

```bash
# Pod 作成
$kubectl apply -f configmap-pod.yaml
pod "dapi-test-pod" created

# configmap1 の内容が参照可能
$ kubectl logs dapi-test-pod |grep SPECIAL
SPECIAL_LEVEL_KEY=very

# configmap2 の内容が参照可能
$kubectl logs dapi-test-pod |grep LOG
LOG_LEVEL=INFO
```

なお、**envFrom** フィールドを使うと指定し ConfigMap のすべての環境変数を参照するようにすることも出来る。

```all-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh", "-c", "env" ]
      envFrom:
      - configMapRef:
          name: special-config
  restartPolicy: Never
```

上記を使う場合、マニュフェストファイルを見ただけではどのような内容が設定されているか分からない。