KubernetesのConfig&Storageリソース(その1)
Secret
Kubernetes上でアプリケーションを実行する場合、MySQLデータベースなどに対して接続する際にユーザ名やパスワードが必要となるケースがあるかと思います。どのような手段が考えられるでしょうか?
Dockerビルド時にコンテナイメージに埋め込んでおく
イメージビルド時、アプリケーション側やコンテナの環境変数及び実行引数などにパスワードなどを埋め込んでおくことができます。しかし、イメージ自体に機密情報が入ってしまうと、Docker Registryなどにアップロードすることが困難になります。また、認証情報が変わった場合に再度イメージをビルドしなければならず、利便性もよくありません。
PodやDeploymentのYAML Manifestに記載して渡す
これもYAMLファイル自体に機密情報が入ってしまうため、GitHub上にこのYAML Manifestをアップロードすることが困難になります。また複数のアプリケーションから同じパスワードを参照する場合に、パスワードが散らばってしまうといった問題もあります。
このような場合に、ユーザ名やパスワードを別リソースとして定義しておき、Podから読み出すためのリソースがSecretです。
また、Secretが定義されたYAML Manifestを暗号化するkubesecといったOSSも存在します。kubesecはgpg、Google Cloud KMS、AWS KMSなどを利用することで簡単にdata.*の部分のみを暗号化することができるため、暗号化したYAMLファイルごとGitHub上にアップロードすることも可能です。
Secretの分類
一口にSecretといっても、いくつかのタイプに分けられています。通常のパスワードなどはGeneric用の「type: Opeque」を利用します。他にも、Ingressなどで参照可能なTLS用のSecretや、Docker Registryへの認証情報用のSecretなどもあります。また、手動で作ることはありませんが、PodにService AccountのTokenをマウントするための、「type: kubernetes.io/service-account-token」のSecretも存在します。
- Generic(type: Opaque)
- TLS(type: kubernetes.io/tls)
- Docker Registry(type: kubernetes.io/dockerconfigjson)
- Service Account(type: kubernetes.io/service-account-token)
GenericタイプのSecret
一般的なフリースキーマのSecretを作成する場合は、genericを指定します。作成方法は下記の4パターンがあります。
- ファイルから作成する(--from-file)
- yamlファイルから作成する
- kubectlから直接作成する(--from-literal)
- envfileから作成する
Secretでは、Secretの名前の中に、複数のKey-Value値が保存されます。例えば、Databaseの認証情報を作成する場合、Secret名はdb-auth、Keyはusername、passwordの2種類となります。もちろん、Kubernetesクラスタ内で複数のDBを作成する場合もあると思うので、その場合はSecret名をユニークとなるように定義するか、システムごとにNamespaceを分割する必要があります。
ファイルから作成する
ファイルから作成する場合には、--from-fileを指定します。通常はファイル名がそのままKeyとなるため、username.txtなど拡張子は外しておいた方がいいでしょう。外せない場合は--from-file=username=username.txtなどのように指定して下さい。また、改行コードが入らないようecho -nなどを使って出力して下さい。
$ echo -n "root" > ./username $ echo -n "rootpassword" > ./password
作成したファイルを元にSecretを作成します。
$ kubectl create secret generic sample-db-auth \ --from-file=./username --from-file=./password
作成したSecretを確認するには、kubectl getでjsonかyamlフォーマットで出力し、dataの部分を確認します。
$ kubectl get secret sample-db-auth -o json | jq .data { "password": "cm9vdHBhc3N3b3Jk", "username": "cm9vdA==" }
base64でエンコードされているため、平文で確認するには下記のようにデコードします。
$ kubectl get secret sample-db-auth -o json | jq -r .data.username cm9vdA== $ kubectl get secret sample-db-auth -o json | jq -r .data.username | base64 -d root
yamlファイルから作成する
yamlファイルから作成する場合、base64でエンコードした値をyamlファイルに埋め込みます。上記と同様のSecretを作成する場合は、下記のようにします。
apiVersion: v1 kind: Secret metadata: name: sample-db-auth type: Opaque data: username: cm9vdA== password: cm9vdHBhc3N3b3Jk $ kubectl create -f ./secret_sample.yml
kubectlから直接作成する (--from-literal)
kubectlから直接作成するには、--from-literalオプションを使って作成します。
$ kubectl create secret generic sample-db-auth \ --from-literal=username=root --from-literal=password=rootpassword
envfileから作成する
一括で作成する場合には、envfileから生成する方法もあります。Dockerで --env-fileオプションを使ってコンテナを起動していた場合は、この方法を利用することでそのままSecretに移行することが可能です。
username=root password=rootpassword $ kubectl create secret generic sample-db-auth --from-env-file ./env_secret
TLSタイプのSecret
証明書として利用するSecretを作成する場合は、tlsを指定します。TLSタイプのSecretはIngressリソースなどから利用することが一般的です。TLSタイプの場合には、基本的にファイルから作成することが望ましいです。
ファイルから作成する
ファイルから作成する場合には、--keyと--certに秘密鍵と証明書を指定します。
# 証明書の作成 $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=sample1.example.com" # TLS Secret の作成 $ kubectl create secret tls tls-sample --key /tmp/tls.key --cert /tmp/tls.crt
Docker RegistryタイプのSecret
Docker RegistryタイプのSecretを作成する場合は、docker-registryを指定します。
kubectl から直接作成する
kubectlから作成する際には、Registryサーバと認証情報を引数で指定します。
$ kubectl create secret docker-registry sample-registry-auth \ --docker-server=REGISTRY_SERVER \ --docker-username=REGISTRY_USER \ --docker-password=REGISTRY_USER_PASSWORD \ --docker-email=REGISTRY_USER_EMAIL
中身は、dockercfg形式のJSONがbase64エンコードされた形式になっています。そのため、YAMLを書いて作成するよりもkubectlから作成するほうがいいでしょう。
$ kubectl get secret -o json sample-registry-auth | jq .data { ".dockercfg": "eyJSRUdJU1RSWV9TRVJWRVIiOnsidXNlcm5hbWUiOiJSRUdJU1RSWV9VU0VSIiw icGFzc3dvcmQiOiJSRUdJU1RSWV9VU0VSX1BBU1NXT1JEIiwiZW1haWwiOiJSRUdJU1RSWV9VU0VSX0V NQUlMIiwiYXV0aCI6IlVrVkhTVk5VVWxsZlZWTkZVanBTUlVkSlUxUlNXVjlWVTBWU1gxQkJVMU5YVDFKRSJ9fQ==" } $ kubectl get secret sample-registry-auth -o yaml | grep "\.dockercfg" | awk -F' ' '{print $2}' | base64 -d {"REGISTRY_SERVER":{"username":"REGISTRY_USER", "password":"REGISTRY_USER_PASSWORD","email":"REGISTRY_USER_EMAIL", "auth":"UkVHSVNUUllfVVNFUjpSRUdJU1RSWV9VU0VSX1BBU1NXT1JE"}}
Secretを利用したイメージの取得
認証がかかったDocker RegistryやDocker HubのPrivateリポジトリに置かれたイメージを取得する場合は、Secretを事前に作成した後、Podのspec.imagePullSecretsにdocker-registryタイプのSecretを指定する必要があります。
apiVersion: v1 kind: Pod metadata: name: sample-pod spec: containers: - name: secret-image-container image: REGISTRY_NAME/secret-image:latest imagePullSecrets: - name: sample-registry-auth
Secretの利用
Secretをコンテナから利用する場合、大きく分けて下記の2つのパターンがあります。
- 環境変数として渡す
- Secretの特定のKeyのみ
- Secretの全てのKey
- Volumeとしてマウントする
- Secretの特定のKeyのみ
- Secretの全てのKey
環境変数として渡す
環境変数として渡す場合、特定のKeyのみを渡すか、Secret全体を渡すかの2通りの方法が用意されています。Secretの特定のKeyを渡す場合には、spec.containers[].envのvalueFrom.secretKeyRefを使って指定します。
apiVersion: v1 kind: Pod metadata: name: sample-secret-single-env spec: containers: - name: secret-container image: nginx:1.12 env: - name: DB_USERNAME valueFrom: secretKeyRef: name: sample-db-auth key: username kubectl apply -f secret_single_env_sample.yml
envとして1つずつ定義しているため、環境変数名が指定できる特徴があります。
$ kubectl exec -it sample-secret-single-env env | grep DB_USERNAME DB_USERNAME=root
一方でSecretの全体を変数として展開することも可能です。Keyごとにそれぞれenvを設定する必要がないため、YAML configが冗長ではなくなりますが、Secretにどのような値が保存されているかをPod Templateからは判断しづらくなるといったデメリットもあります。
apiVersion: v1 kind: Pod metadata: name: sample-secret-multi-env spec: containers: - name: secret-container image: nginx:1.12 envFrom: - secretRef: name: sample-db-auth kubectl apply -f secret_multi_env_sample.yml
以下のように、Secretに登録されているすべてのKeyが環境変数として登録されていることが確認できます。
$ kubectl exec -it sample-secret-multi-env env … username=root password=rootpassword …
Volumeとしてマウントする
Volumeとしてマウントする場合も、特定のKeyのみを渡すか、Secret全体を渡すかの2通りの方法が用意されています。Secretの特定のKeyを渡す場合には、spec.volumes[]のsecret.items[]を使って指定します。
apiVersion: v1 kind: Pod metadata: name: sample-secret-single-volume spec: containers: - name: secret-container image: nginx:1.12 volumeMounts: - name: config-volume mountPath: /config volumes: - name: config-volume secret: secretName: sample-db-auth items: - key: username path: username.txt $kubectl apply -f secret_single_volume_sample.yml
マウントするファイルを1つずつ定義しているため、ファイル名を指定できる特徴があります。
$ kubectl exec -it sample-secret-single-volume cat /config/username.txt root
Secretの全体を変数として展開することも可能です。こちらもYAML configが冗長ではなくなりますが、Secretにどのような値が保存されているかを、Pod Templateからは判断しづらくなるといったデメリットもあります。
apiVersion: v1 kind: Pod metadata: name: sample-secret-multi-volume spec: containers: - name: secret-container image: nginx:1.12 volumeMounts: - name: config-volume mountPath: /config volumes: - name: config-volume secret: secretName: sample-db-auth $ kubectl apply -f secret_multi_volume_sample.yml
$ kubectl exec -it sample-secret-multi-volume ls /config password username
動的なSecretの更新
Volumeマウントを利用したSecretに限り、一定期間ごと(kubeletのSync Loopのタイミング)にkube-apiserverに変更を確認し、変更がある場合は入れ替えを行います。
デフォルトではSyncLoopの間隔は60秒に設定されています。この頻度を上げる場合には、kubeletのオプションとして--sync-frequencyを設定して下さい。また、環境変数を利用したSecretの場合、動的な更新はできません。
例えば、先ほどの例のPodではthreadの値は16で設定しています。
$ kubectl exec -it sample-secret-multi-volume cat /config/username root $ kubectl get pod sample-secret-multi-volume NAME READY STATUS RESTARTS AGE sample-secret-multi-volume 1/1 Running 0 11m
その後、Secretの値を書き換えます。
$ cat << _EOF_ | kubectl apply -f - apiVersion: v1 kind: Secret metadata: name: sample-db-auth type: Opaque data: # root > admin に変更 username: YWRtaW4= _EOF_
一定期間が経過すると、Volumeにマウントされているファイルの値が書き換わっていることが確認できます。また、Podが再作成されることもないため、瞬断も起きません。
$ kubectl exec -it sample-secret-multi-volume cat /config/username admin $ kubectl get pod sample-secret-multi-volume NAME READY STATUS RESTARTS AGE sample-secret-multi-volume 1/1 Running 0 11m
また、今回の例ではSecretの中身をusernameだけにしてapplyしているため、その他のファイルが削除されている点にも注意して下さい。
$ kubectl exec -it sample-secret-multi-volume ls /config username
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Kubernetesの基礎
- kustomizeで復数環境のマニフェストファイルを簡単整理
- kustomizeやSecretを利用してJavaアプリケーションをデプロイする
- KubernetesのDiscovery&LBリソース(その2)
- KubernetesのWorkloadsリソース(その1)
- 3scaleをインストールしてみよう!
- Oracle Cloud Hangout Cafe Season7 #1「Kubnernetes 超入門」(2023年6月7日開催)
- KubernetesのDiscovery&LBリソース(その1)
- OpenShift:アプリケーションの構成と運用
- Oracle Cloud Hangout Cafe Season5 #3「Kubernetes のセキュリティ」(2022年3月9日開催)