Kubernetes Secretをミニマムな構成で試してみる

# TL;DR

Opaque SecretをMinikube環境で試してみました。
この記事では、ミニマムな構成でのOpaque Secretの適用方法を説明します。

公式ドキュメント: https://kubernetes.io/ja/docs/concepts/configuration/secret/
この記事で登場するマニフェスト等: https://github.com/cacapouh/k8s-secret-example

# Secretの適用

まず最初に、Secretを作成します。以下はサンプルの秘匿情報です。

```
{
  "user": "example-user",
  "password": "example-password"
}
```

Secretとして扱う値はbase64でエンコードされている必要があります:

```shell:エンコード
$ cat << EOF | base64
{
  "user": "example-user",
  "password": "example-password"
}
EOF
ewogICJ1c2VyIjogImV4YW1wbGUtdXNlciIsCiAgInBhc3N3b3JkIjogImV4YW1wbGUtcGFzc3dvcmQiCn0K
```

次に、適用するマニフェストファイルを作成します。`data.credentials.json`の文字列には、先ほどエンコードした値を設定します。

```yml:secret.yml
apiVersion: v1
kind: Secret
metadata:
  name: my-config-secret
data:
  credentials.json: ewogICJ1c2VyIjogImV4YW1wbGUtdXNlciIsCiAgInBhc3N3b3JkIjogImV4YW1wbGUtcGFzc3dvcmQiCn0K
```

このマニフェストをkubectlで適用します。

```
$ kubectl apply -f secret.yml
secret/my-config-secret created
```

Secretの内容を確認するには、以下のように対象のマニフェストファイルを標準出力し、`credentials.json`をbase64でデコードします。

```
$ kubectl get secrets
NAME               TYPE     DATA   AGE
my-config-secret   Opaque   1      106s

$ kubectl get secret my-config-secret -o yaml
apiVersion: v1
data:
  credentials.json: ewogICJ1c2VyIjogImV4YW1wbGUtdXNlciIsCiAgInBhc3N3b3JkIjogImV4YW1wbGUtcGFzc3dvcmQiCn0K
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"credentials.json":"ewogICJ1c2VyIjogImV4YW1wbGUtdXNlciIsCiAgInBhc3N3b3JkIjogImV4YW1wbGUtcGFzc3dvcmQiCn0K"},"kind":"Secret","metadata":{"annotations":{},"name":"my-config-secret","namespace":"default"}}
  creationTimestamp: "2024-01-14T12:11:48Z"
  name: my-config-secret
  namespace: default
  resourceVersion: "1345"
  uid: 7245a991-70b2-4712-82fe-9ffa752817b1
type: Opaque

$ echo 'ewogICJ1c2VyIjogImV4YW1wbGUtdXNlciIsCiAgInBhc3N3b3JkIjogImV4YW1wbGUtcGFzc3dvcmQiCn0K' | base64 --decode
{
  "user": "example-user",
  "password": "example-password"
}
```

# SecretをPythonアプリから確認してみる

上記で作成したアプリが他のPodから利用可能か確認するために、`credentials.json`の中身を標準出力するPythonアプリを作成します。
ただし、秘匿情報がPodのログに流れることに注意が必要です。

以下は、簡単なPythonアプリのスクリプト`main.py`、`Dockerfile`、およびビルド方法です。

```python:main.py
import json

# NOTICE: /etc/config/credentials.jsonはSecret経由で配置する
with open("/etc/config/credentials.json") as f:
    credentials = json.loads(f.read())

if __name__ == '__main__':
    user = credentials['user']
    password = credentials['password']
    print("{}:{}".format(user, password))
```

```Dockerfile:Dockerfile
FROM python:3.9.18-slim

RUN mkdir /work
WORKDIR /work

COPY main.py /work
ENTRYPOINT python main.py
```

```shell:ビルド方法
docker build . -t simple-app
minikube image load simple-app:latest  # Minikube環境の場合
```

最後に、Pythonアプリをデプロイするためのマニフェスト設定です。
Secret関連の設定は`spec.template.spec.volumes`で定義しています。

```yaml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-app-deployment
  labels:
    app: simple-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-app
  template:
    metadata:
      labels:
        app: simple-app
    spec:
      containers:
      - name: simple-app
        image: "simple-app:latest"
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
      - name: config-volume
        secret:
          secretName: my-config-secret
```

以下は`deployment.yml`の適用結果です。
PodのログにSecret情報である`example-user:example-password`が出ています :thumbsup: 

```
$ kubectl apply -f deployment.yml
deployment.apps/simple-app-deployment created

$ kubectl get pods
NAME                                     READY   STATUS      RESTARTS   AGE
simple-app-deployment-85995766c4-82rk5   0/1     Completed   0          1s

$ kubectl logs simple-app-deployment-85995766c4-82rk5
example-user:example-password
```