はじめに
こんにちは!
本記事は「本気で学ぶKubernetes」シリーズの第6回です。
今回は、Kubernetesで機密情報を扱うSecretと、データを永続化・共有するVolumeについて触れます。
実際のアプリケーションでは、データベースのパスワードやAPIキーといった秘密情報を扱う必要がありますし、ログファイルや画像といったデータをPodが再起動しても保持したいケースがあります。
Kubernetesではこれらをどう扱うのかをみていきます。
この記事は人間がKubernetesの公式ドキュメントを読み漁りながら、人間の手で書いていますのでご安心ください!
前提知識
- Pod、Deployment、Serviceの基本
- 環境変数の概念
- Linuxのファイルシステムとマウントの基本
なぜSecretとVolumeが必要なのか
マニフェストに直接パスワードを書くと、Gitにコミットしたときに秘密情報が外部に漏れてしまう可能性がありますし、かといって環境変数をコンテナイメージにハードコードするのもセキュリティ的に推奨されません。
また、コンテナのファイルシステムは基本的には揮発性のため、Podが再起動されるとコンテナ内で書き込んだファイルは消えてしまいます。
そこでログを永続化したい、複数のPodでファイルを共有したいといったニーズに対応するため、これらをSecretとVolumeで管理します。
Secretとは
Secretは、パスワード、トークン、SSHキーといった機密情報を保存するために用意されているKubernetesリソースです。
基本的にはマニフェストに直接書くのではなく、Secretオブジェクトとして管理しPodやDeploymentから参照する形で使います。
出典: Kubernetes公式ドキュメント - Secrets
環境変数として使う
以下のコマンドでSecretを作成します。
kubectl create secret generic backend-user \
--from-literal=backend-username='backend-admin'
# secret/backend-user createdz
次に今作成したSecretを環境変数として使うPodを作成します。
apiVersion: v1
kind: Pod
metadata:
name: env-single-secret
spec:
containers:
- name: envars-test-container
image: nginx
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: backend-user
key: backend-username
envセクションで、Secretの値を環境変数SECRET_USERNAMEに設定しています。secretKeyRefで、どのSecretのどのキーを参照するかを指定します。
デプロイして確認してみます。
kubectl apply -f pod-single-secret-env-variable.yaml
# pod/env-single-secret created
kubectl exec env-single-secret -- env | grep SECRET_USERNAME
# SECRET_USERNAME=backend-admin
Volumeとしてマウントする
Secretをファイルとしてコンテナ内にマウントする方法もあります。
Volumeというのは、コンテナ内にディレクトリやファイルをマウントする仕組みでこの記事の後半で詳しく扱います。ここではまず、SecretをVolumeとしてマウントする例を見てみます。
今回はドットファイル用のSecretとそれを使うPodを作成します。
apiVersion: v1
kind: Secret
metadata:
name: dotfile-secret
data:
.secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
name: secret-dotfiles-pod
spec:
volumes:
- name: secret-volume
secret:
secretName: dotfile-secret
containers:
- name: dotfile-test-container
image: busybox
command:
- ls
- "-la"
- "/etc/secret-volume"
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
volumesセクションで、dotfile-secretというSecretをボリュームとして定義し、volumeMountsでコンテナ内の/etc/secret-volumeにマウントしています。
上記の例では.secret-fileというドットファイルが/etc/secret-volume/.secret-fileとして作られます。
こちらもデプロイしてログを確認してみます。
kubectl apply -f dotfile-secret.yaml
kubectl logs secret-dotfiles-pod
# total 0
# lrwxrwxrwx 1 root root 15 ... .secret-file -> ..data/.secret-file
ドットファイルがシンボリックリンクとして存在していることが確認できました。
アプケーションでは.envなどで機密情報をファイル管理しているケースが多いと思いますので、外部のシークレットサービスを使用しない場合はこちらの利用方法が推奨されるかなと思います。
Volumeとは
コンテナのファイルシステムは基本的に揮発性で、Podが削除されると中のデータは消えてしまいます。
また、同じPod内の複数コンテナ間でファイルを共有したいケースもあります。
Kubernetesにはいくつかのボリュームタイプがあり、用途によって使い分けます。
- emptyDir: Pod内コンテナ間のファイル共有、キャッシュ
- hostPath: ノードのファイルシステムとしてデバッグ、開発用のデータ確認
- PV/PVC: 永続的にデータベース、ユーザーアップロードファイルの管理
出典: Kubernetes公式ドキュメント - Volumes
emptyDir
emptyDirは、Pod作成時に空のディレクトリとして作られ、Pod削除時に消えるVolumeです。
同じPod内の複数コンテナでファイルを共有したいときに使います。
ユースケースとしてはキャッシュや一時ファイルの置き場などPod内の一時ストレージとして利用することになると思います。
apiVersion: v1
kind: Pod
metadata:
name: two-containers
spec:
restartPolicy: Never
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: nginx-container
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
- name: debian-container
image: debian
volumeMounts:
- name: shared-data
mountPath: /pod-data
command: ["/bin/sh"]
args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]
この例では、shared-dataというemptyDirボリュームを2つのコンテナで共有しています。
具体的にはdebianコンテナが/pod-data/index.htmlにHTMLファイルを書き込み、nginxコンテナがそれを/usr/share/nginx/html(nginxのドキュメントルート)から配信しています。
デプロイして確認してみます。
kubectl apply -f two-containers.yaml
# Podが起動するまで待つ
kubectl get pod two-containers
# nginxコンテナにアクセスしてHTMLを確認
kubectl exec two-containers -c nginx-container -- cat /usr/share/nginx/html/index.html
# Hello from the debian container
debianコンテナが書き込んだファイルを、nginxコンテナが読み取れていることが確認できました。
hostPath
hostPathは、Kubernetesノード(ホストマシン)のディレクトリをPod内にマウントする方法です。
minikubeのようなローカル環境で開発やデバッグをする際に便利ですね。
ただしノードのファイルシステムに直接アクセスすることになるので、セキュリティリスクがありますし本番環境では推奨されないかなと思います。
apiVersion: v1
kind: Pod
metadata:
name: hostpath-example
spec:
containers:
- name: test-container
image: busybox
command: ['sh', '-c', 'echo "Hello from Pod" > /test-pd/hello.txt && cat /test-pd/hello.txt && sleep 3600']
volumeMounts:
- name: test-volume
mountPath: /test-pd
volumes:
- name: test-volume
hostPath:
path: /data
type: DirectoryOrCreate
このマニフェストは、ホストの/dataディレクトリをPod内の/test-pdにマウントします。
type: DirectoryOrCreateは、ディレクトリが存在しなければ作成するためのパラメータです。
minikubeにSSHしてホストのディレクトリを確認してみます。
kubectl apply -f hostpath-pod.yaml
kubectl logs hostpath-example
# Hello from Pod
minikube ssh
cat /data/hello.txt
# Hello from Pod
上記のコマンドでPod内で書き込んだファイルが、minikubeノードのファイルシステムに実際に存在していることを確認しました。
PersistentVolumeとPersistentVolumeClaim
本番環境でデータを永続化する場合は、ここで紹介するPersistentVolume(PV)とPersistentVolumeClaim(PVC)を使っていきます。
PersistentVolumeはクラスタ全体で管理されるストレージリソースです。
GCEならPersistent DiskやAWS EBS、Azure Diskといったクラウドストレージや、NFSなどのネットワークストレージをKubernetesから扱えるようにします。
PersistentVolumeClaimは、PodがPersistentVolumeを要求するためのリソースで、PodはPVCを通じてストレージを使うことになります。
実際はGKEなどのマネージドKubernetesサービスと組み合わせて使うケースが多いため、今回は概要だけ触れることとします。
簡単な例を見てみます。
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /tmp/pv-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
spec:
containers:
- name: busybox
image: busybox
command: ['sh', '-c', 'echo "Data from PVC" > /data/pvc-data.txt && cat /data/pvc-data.txt && sleep 3600']
volumeMounts:
- name: pvc-volume
mountPath: /data
volumes:
- name: pvc-volume
persistentVolumeClaim:
claimName: my-pvc
PVで1GBのストレージを定義し、PVCでそのストレージを要求、PodでPVCをマウントしています。
kubectl apply -f pv-pvc-example.yaml
kubectl get pv,pvc
# NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
# persistentvolume/my-pv 1Gi RWO Retain Bound default/my-pvc 10s
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
# persistentvolumeclaim/my-pvc Bound my-pv 1Gi RWO 10s
PVとPVCがBound(結びついた)状態になっています。
kubectl logs pvc-pod
# Data from PVC
Podが再起動してもデータは消えません、またPVCを削除しない限りはデータは永続化されます。
PodからはPVCを参照することで、実際のストレージの詳細を意識せずにデータを永続化できます。
出典: Kubernetes公式ドキュメント - Persistent Volumes
まとめと次回予告
今回は、KubernetesでSecretとVolumeを使って機密情報とデータを扱う方法を確認してきました。
実際のアプリケーションでは、データベースのパスワードをSecretで管理し、ログやアップロードされた画像をVolumeで永続化する、といった使い方になります。
今回の内容を踏まえて、実践的なアプリケーション構築に活かしていければと思います。
次回は ロールベースでリソースへのアクセス制御を行えるRBACの機能について確認していきます。
それでは、また明日!