2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【本気で学ぶKubernetes】SecretとVolumeで機密情報とデータを扱う

Posted at

はじめに

こんにちは!

本記事は「本気で学ぶ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を作成します。

pod-single-secret-env-variable.yaml
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を作成します。

dotfile-secret.yaml
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内の一時ストレージとして利用することになると思います。

two-containers.yaml
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のようなローカル環境で開発やデバッグをする際に便利ですね。

ただしノードのファイルシステムに直接アクセスすることになるので、セキュリティリスクがありますし本番環境では推奨されないかなと思います。

hostpath-pod.yaml
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サービスと組み合わせて使うケースが多いため、今回は概要だけ触れることとします。

簡単な例を見てみます。

pv-pvc-example.yaml
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の機能について確認していきます。

それでは、また明日!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?