この連載が書籍になりました!『Kubernetes完全ガイド

KubernetesのConfig&Storageリソース(その1)

2018年5月31日(木)
青山 真也
今回と次回の2回に渡って、5種類に大別されるKubernetesのリソースのうち、Config&Storageリソースを解説する。

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などを使って出力して下さい。

リスト10:Secretを作成する元のファイル

$ echo -n "root" > ./username
$ echo -n "rootpassword" > ./password

作成したファイルを元にSecretを作成します。

リスト11:ファイルからSecretを作成

$ kubectl create secret generic sample-db-auth \
--from-file=./username --from-file=./password

作成したSecretを確認するには、kubectl getでjsonかyamlフォーマットで出力し、dataの部分を確認します。

リスト12:作成したSecretを確認

$ kubectl get secret sample-db-auth -o json | jq .data
{
  "password": "cm9vdHBhc3N3b3Jk",
  "username": "cm9vdA=="
}

base64でエンコードされているため、平文で確認するには下記のようにデコードします。

リスト13:平文に戻してSecretを確認

$ 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を作成する場合は、下記のようにします。

リスト14:GenericタイプのSecretを作成するsecret_sample.yml

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オプションを使って作成します。

リスト15:kubectlから直接Secretを作成する

$ kubectl create secret generic sample-db-auth \
--from-literal=username=root --from-literal=password=rootpassword

envfileから作成する

一括で作成する場合には、envfileから生成する方法もあります。Dockerで --env-fileオプションを使ってコンテナを起動していた場合は、この方法を利用することでそのままSecretに移行することが可能です。

リスト16:envfileからSecretを作成するenv_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に秘密鍵と証明書を指定します。

リスト17:TLSタイプのSecretを作成する

# 証明書の作成
$ 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サーバと認証情報を引数で指定します。

リスト18:Docker RegistryタイプのSecretを作成する

$ 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から作成するほうがいいでしょう。

リスト19:Docker RegistryタイプのSecretの中身を見る

$ 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.imagePullSecretsdocker-registryタイプのSecretを指定する必要があります。

リスト20:Secretを用いてイメージを取得するsecret_pull_sample.yml

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[].envvalueFrom.secretKeyRefを使って指定します。

リスト21:Secretを環境変数として渡すsecret_singl_env_sample.yml

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つずつ定義しているため、環境変数名が指定できる特徴があります。

リスト22:Secretの内容を確認

$ kubectl exec -it sample-secret-single-env env | grep DB_USERNAME
DB_USERNAME=root

一方でSecretの全体を変数として展開することも可能です。Keyごとにそれぞれenvを設定する必要がないため、YAML configが冗長ではなくなりますが、Secretにどのような値が保存されているかをPod Templateからは判断しづらくなるといったデメリットもあります。

リスト23:Secret全体を環境変数として展開するsecret_multi_env_sample.yml

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が環境変数として登録されていることが確認できます。

リスト24:すべてのKeyを確認

$ kubectl exec -it sample-secret-multi-env env
…
username=root
password=rootpassword
…

Volumeとしてマウントする

Volumeとしてマウントする場合も、特定のKeyのみを渡すか、Secret全体を渡すかの2通りの方法が用意されています。Secretの特定のKeyを渡す場合には、spec.volumes[]secret.items[]を使って指定します。

リスト25:Volumeとしてマウントするsecret_single_volume_sample.yml

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つずつ定義しているため、ファイル名を指定できる特徴があります。

リスト26:Secretの内容を確認

$ kubectl exec -it sample-secret-single-volume cat /config/username.txt
root

Secretの全体を変数として展開することも可能です。こちらもYAML configが冗長ではなくなりますが、Secretにどのような値が保存されているかを、Pod Templateからは判断しづらくなるといったデメリットもあります。

リスト27:Secret全体をVolumeとしてマウントするsecret_multi_volume_sample.yml

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

リスト28:Secret全体の値を確認

$ 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で設定しています。

リスト29:更新前のSecretの値を確認

$ 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の値を書き換えます。

リスト30: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が再作成されることもないため、瞬断も起きません。

リスト31:更新後のSecretの値を確認

$ 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しているため、その他のファイルが削除されている点にも注意して下さい。

リスト32:Secretの中身はusernameのみ

$ kubectl exec -it sample-secret-multi-volume ls /config
username
株式会社サイバーエージェント アドテク本部 Strategic Infrastructure Agency

2016年入社。OpenStackを使ったプライベートクラウドやGKE互換なコンテナプラットフォームをゼロから構築し、国内カンファレンスでのKeynoteに登壇。
その後、世界で2番目にCertified Kubernetes Application Developer、138番目にCertified Kubernetes Administratorの認定資格を取得。
現在はKubernetesやOpenStackなどOSSへのコントリビュート活動をはじめ、CNCF公式のCloud Native Meetup TokyoのOrganizerやJapan Container Daysの運営などコミュニティ活動にも従事している。

連載バックナンバー

仮想化/コンテナ技術解説
第9回

KubernetesのConfig&Storageリソース(その2)

2018/6/6
連載9回目となる今回は、Config&Storageリソースのうち、PersistentVolumeClaimについて解説する。
仮想化/コンテナ技術解説
第8回

KubernetesのConfig&Storageリソース(その1)

2018/5/31
今回と次回の2回に渡って、5種類に大別されるKubernetesのリソースのうち、Config&Storageリソースを解説する。
仮想化/コンテナ技術解説
第7回

KubernetesのDiscovery&LBリソース(その2)

2018/5/16
前回に引き続き、Discovery&LBリソースのServiceとIngressを紹介する。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています