⑲１日１０分で理解するコンテナ技術入門 - Secret -

[前回](https://qiita.com/goroneko/items/4791d3ac97e68c949b5c)は、Podに環境変数や設定ファイルを渡す方法としてConfigMapをメインに紹介しました。ConfigMapさえあれば基本的にどんな情報でも渡すことができるのですが、APIキーなどの機密情報はConfigMapを使って渡すことは推奨されません。
今回はそういった機密情報を渡すためのオブジェクトであるSecretについて紹介します。

# Secretとは
Secretは、パスワードやトークン、鍵などをPodに渡すためのオブジェクトです。


Secretには、以下のようなタイプ（用途）があります（ほかにもあります）。
- generic
- docker-registry
- tls
- bootstrap-token

これらの用途別に、Builtin Typeとして以下のようなものが用意されています。

| Type | Builtin Type |
| --- | --- |
| generic | Opaque |
| docker-registry | kubernetes.io/dockerconfigjson |
| tls | kubernetes.io/tls |
| bootstrap-token | bootstrap.kubernetes.io/token |

Secretを作成する際に、その用途（Type）に応じたBuiltin Typeをマニフェストファイル内で指定します。
マニフェストファイルから作成せず、コマンドから作成する場合は、Typeの方を指定します。
# Secretを使ってみる
ここではまず`Opaque secrets`を作成してみます。まずはコマンドラインから作成する方法を見てみます。
## コマンドラインから作成
Secretに入れる値は、base64でエンコードする必要があるので、入れたい値をあらかじめbase64にエンコードしておきます。コマンドから作成する場合、以下のコマンドで作成できます。
```bash
$ kubectl create secret generic my-secret # 空のシークレットを作成
$ echo -n 'admin' | base64  # Secretに入れたい値をbase64でエンコードする。出力: YWRtaW4=
$ echo -n 's3cr3t' | base64  # 同上。出力: czNjcjN0
$ kubectl patch secret my-secret -p '{"data":{"username":"YWRtaW4=","password":"czNjcjN0"}}' # base64でエンコードした値を入れる
```
作成したSecretを確認してみます。
```bash
$ kubectl get secret
NAME        TYPE     DATA   AGE
my-secret   Opaque   2      22s
```

:::note info
初めから値を入れた状態でSecretを作成する方法
上記の例では空のSecretを作成してから値を追加しましたが、Secret作成時に値を入れておくこともできます。`kubectl create secret generic <secret-name> --from-literal=<key1>=<value1> --from-literal=<key2>=<value2>`のような形で記述します。

```bash
$ kubectl create secret generic my-secret --from-literal=username=admin --from-literal=password=s3cr3t
```
`--from-literal`オプションを使って値を入れる場合、Kubernetes側でbase64エンコードしてくれるので、通常のテキストをそのまま入れて問題ありません。
:::

## マニフェストファイルから作成
まずは、ひな型を作成します。
```bash
$ kubectl create secret generic my-secret2 --from-literal=username=admin --from-literal=password=s3cr3t --dry-run=client -o yaml

```
作成されたひな形は以下のようなものになります。
```yaml:create_secret.yaml
apiVersion: v1
data:
  password: czNjcjN0
  username: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: null
  name: my-secret2
```
`type`に`Opaque`がないので変に思うかもしれませんが、デフォルトのタイプが`Opaque`のため省略されています。

このマニフェストファイルをクラスターに適用します。
```bash
$ kubectl apply -f create_secret.yaml
```

作成されたものを確認します。
```bash
$ kubectl get secret
NAME         TYPE     DATA   AGE
my-secret    Opaque   2      14m
my-secret2   Opaque   2      17s
```
正しく作成されていることが分かります。

## 作成したSecretをPodから利用する
作成したSecretをPodから利用する場合、ConfigMapと同じで以下の方法で利用できます。
- ファイルとしてPodから利用する
- 環境変数として利用する

### ファイルとしてPodから利用する場合
以下のマニフェストファイルのように、ボリュームとしてSecretをマウントします。
```yaml:mount_pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: mypod
      image: redis
      volumeMounts:
        - name: foo
          mountPath: "/etc/foo"
          readOnly: true
  volumes:
    - name: foo
      secret:
        secretName: my-secret
```
Podを作成して中に入ってみます（見にくかったので少しだけ出力を整形しています）。
```bash
$ kubectl apply -f mount_pod.yaml
$ kubectl exec -it mypod -- /bin/bash
root@mypod:/data# ls /etc/foo
password  username
root@mypod:/data# cat /etc/foo/password
s3cr3t
root@mypod:/data# cat /etc/foo/username 
admin
```
上記の様に、各キーの名前のファイルが作成され、そのファイルの中に値が入っていることが分かります。

## 環境変数として利用する場合
環境変数として利用する場合も、ConfigMapと同様の書き方で記述します。以下のマニフェストファイルを作成します。
```yaml:env_pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: my-secret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: my-secret
            key: password
  restartPolicy: Never
```
Podを作成して確認してみます。
```bash
$ kubectl apply -f env_pod.yaml
$ kubectl exec -it secret-env-pod -- /bin/bash
root@secret-env-pod:/data# echo $SECRET_USERNAME
admin
root@secret-env-pod:/data# echo $SECRET_PASSWORD
s3cr3t
```
上記の様に、マニフェストファイルで指定した（`spec.containers.env.name`）名前で環境変数として利用できることが分かります。
# まとめ
今回は機密情報を扱うためのオブジェクトであるSecretについて紹介しました。次回はConfigMapとSecretを利用して、以前クラスターにデプロイしたアプリケーションを書き換えてみたいとおもいます。