ConfigMapとかSecretとか
Kubernetes完全ガイドの続き.
ConfigMapとかSecretとか, Podから利用できるリソースの話.
読んだもの
- Kubernetes完全ガイド 7章(Config & Storageリソース)
重要そうなところとかよく使いそうなところだけまとめる.
読んだことのまとめ
ConfigMap
Pod
から利用できる情報をKey-Value形式で保持するリソース.
平文ファイル, 直接入力, マニフェストファイル(YAML)の3種類の方法で作成できる.
# 平文ファイルからConfigMapを作成
$ cat config.txt
abc
def
ghi
1234
$ kubectl create configmap --save-config sample-configmap-01 --from-file=./config.txt
configmap/sample-configmap-01 created
# kubectlで値を直接入力してConfigMapを作成
$ kubectl create configmap --save-config web-config \
--from-literal=connection.max=100 \
--from-literal=connection.min=10
configmap/web-config created
# マニフェストファイルから作成する
$ kubectl apply -f sample-configmap.yaml
configmap/sample-configmap created
# 値がKey-Value形式で保存されている
$ kubectl get configmap sample-configmap-01 -o json | jq '.data'
{
"config.txt": "abc\ndef\nghi\n1234\n"
}
$ kubectl get configmap web-config -o json | jq '.data'
{
"connection.max": "100",
"connection.min": "10"
}
$ kubectl get configmap sample-configmap -o json | jq '.data'
{
"connection.max": "100",
"connection.min": "10",
"nginx.conf": "user nginx;\nworker_processes auto;\nerror_log /var/log/nginx/error.log;\npid /run/nginx.pid;\n",
"sample.properties": "property.1=value-1\nproperty.2=value-2\nproperty.3=value-3\n",
"thread": "16"
}
作成したConfigMap
はPod
から環境変数またはマウントしたファイルとして扱うことができる.
# ConfigMap(sample-configmap)のValue(Key=connection.max)が環境変数CONNECTION_MAXとして利用されている
$ kubectl get pod sample-configmap-single-env -o json | jq '.spec.containers[].env[]'
{
"name": "CONNECTION_MAX",
"valueFrom": {
"configMapKeyRef": {
"key": "connection.max",
"name": "sample-configmap"
}
}
}
$ kubectl exec -it sample-configmap-single-env env | grep CONNECTION_MAX
CONNECTION_MAX=100
# ConfigMap(sample-configmap)のKey-ValueがVolume(config-volume)として/configにマウントされている
$ kubectl get pod sample-configmap-multi-volume -o json | jq '.spec.volumes[0]'
{
"configMap": {
"defaultMode": 420,
"name": "sample-configmap"
},
"name": "config-volume"
}
$ kubectl get pod sample-configmap-multi-volume -o json | jq '.spec.containers[0].volumeMounts[0]'
{
"mountPath": "/config",
"name": "config-volume"
}
$ kubectl exec -it sample-configmap-multi-volume ls /config
connection.max connection.min nginx.conf sample.properties thread
$ kubectl exec -it sample-configmap-multi-volume cat /config/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
sample-configmap-single-env.yaml
sample-configmap-multi-volume.yaml
Secret
ConfigMap
とは異なり, 秘密情報を扱うためのリソース.
対応するSecret
を利用するPod
がある場合のみetcd
からNode
の一時的な領域(tmpfs
)にKey-Valueのデータが送られるようになっているので, ConfigMap
に比べて機密性が高い.
また, 安全のためにKey-ValueのValueがbase64エンコードされていて少し見えにくくなっている.
Opaque
タイプのSecret
は基本的にConfigMap
と同じような使い方ができる.
# 平文ファイルからSecretを作成
$ echo -n "root" > ./username
$ echo -n "rootpassword" > ./password
$ kubectl create secret generic --save-config sample-db-auth-from-file \
--from-file=./username --from-file=./password
secret/sample-db-auth-from-file created
# envfileからSecretを作成
$ cat << 'EOF' > env-secret.txt
username=root
password=rootpassword
EOF
$ kubectl create secret generic --save-config sample-db-auth-from-env-file \
--from-env-file ./env-secret.txt
secret/sample-db-auth-from-env-file created
# kubectlで値を直接入力してSecretを作成
$ kubectl create secret generic --save-config sample-db-auth-from-literal \
--from-literal=username=root --from-literal=password=rootpassword
secret/sample-db-auth-from-literal created
# マニフェストファイルからSecretを作成
$ kubectl apply -f sample-db-auth.yaml
secret/sample-db-auth created
# describeしても見えない
$ kubectl describe secret sample-db-auth-from-literal
Name: sample-db-auth-from-literal
Namespace: default
Labels: <none>
Annotations:
Type: Opaque
Data
====
password: 12 bytes
username: 4 bytes
# 中身はbase64エンコードされている
$ kubectl get secret sample-db-auth-from-literal -o json | jq '.data'
{
"password": "cm9vdHBhc3N3b3Jk",
"username": "cm9vdA=="
}
$ kubectl get secret sample-db-auth-from-literal -o json | jq -r '.data.username' | base64 -D
root
$ kubectl get secret sample-db-auth-from-literal -o json | jq -r '.data.password' | base64 -D
rootpassword
Ingress
などからTLSに必要な証明書と秘密鍵を扱うためのkubernetes.io/tls
タイプのSecret
もある.
# 秘密鍵(tls.key)とオレオレ証明書(tls.crt)を同時に作成
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ./tls.key -out ./tls.crt -subj "/CN=sample1.example.com"
Generating a 2048 bit RSA private key
......+++
.........................................................................................................................................................................................................+++
writing new private key to './tls.key'
-----
$ ls
tls.crt tls.key
# 秘密鍵と証明書からSecretを作成
$ kubectl create secret tls --save-config tls-sample --key ./tls.key --cert ./tls.crt
secret/tls-sample created
# 秘密鍵と証明書のpemをbase64エンコードしたものが登録されている
$ kubectl get secret tls-sample -o json | jq '.data'
{
"tls.crt": "LS0tLS...tLS0K",
"tls.key": "LS0tLS...tLS0K"
}
$ kubectl get secret tls-sample -o json | jq -r .data'["tls.crt"]' | base64 -D | openssl x509 -noout -text
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 12508610311645361173 (0xad9782ce1368f415)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=sample1.example.com
Validity
Not Before: Jul 8 14:54:37 2020 GMT
Not After : Jul 8 14:54:37 2021 GMT
Subject: CN=sample1.example.com
...
DockerHub
のプライベートリポジトリなど, 認証のかかったDocker
レジストリからimage
を取得するための認証情報を扱う場合はkubernetes.io/dockerconfigjson
タイプのSecret
を使用する.Pod
から認証がかかったリポジトリのimage
をpullするにはマニフェストファイルの.spec.imagePullSecrets
にSecret
名を指定する(例 : sample-pull-secret.yaml).
# 認証情報からSecretを作成
$ kubectl create secret docker-registry --save-config sample-registry-auth \
--docker-server=SERVER \
--docker-username=USER \
--docker-password=PASSWORD \
--docker-email=EMAIL
secret/sample-registry-auth created
# dockercfg形式の認証情報が保存されている
$ kubectl get secret sample-registry-auth -o json | jq -r .data'[".dockerconfigjson"]' | base64 -D
{"auths":{"SERVER":{"username":"USER","password":"PASSWORD","email":"EMAIL","auth":"VVNFUjpQQVNTV09SRA=="}}}
作成したSecret
はPod
から環境変数またはマウントしたファイルとして扱うことができる.
# Secret(sample-db-auth)のValue(Key=username)が環境変数DB_USERNAMEとして利用されている
$ kubectl get pod sample-secret-single-env -o json | jq '.spec.containers[].env[]'
{
"name": "DB_USERNAME",
"valueFrom": {
"secretKeyRef": {
"key": "username",
"name": "sample-db-auth"
}
}
}
$ kubectl exec -it sample-secret-single-env env | grep DB_USERNAME
DB_USERNAME=root
# Secret(sample-db-auth)のKey-ValueがVolume(config-volume)として/configにマウントされている
$ kubectl get pod sample-secret-multi-volume -o json | jq '.spec.volumes[0]'
{
"name": "config-volume",
"secret": {
"defaultMode": 420,
"secretName": "sample-db-auth"
}
}
$ kubectl get pod sample-secret-multi-volume -o json | jq '.spec.containers[].volumeMounts[0]'
{
"mountPath": "/config",
"name": "config-volume"
}
$ kubectl exec -it sample-secret-multi-volume ls /config
password username
$ kubectl exec -it sample-secret-multi-volume cat /config/username
root
Volume
Pod
からディスクを扱うためのリソース.
いくつか種類があるが, どれもPod
上に静的に領域を指定して使用する.
emptyDir
はPod
に一時的なディスク領域を作成し, Pod
が終了すると同時に削除される.Pod
内の複数のコンテナ間でファイルを共有したりするのに使える.
# emptyDirが /cache にマウントされている
$ kubectl get pod sample-emptydir -o json | jq '.spec.volumes[0]'
{
"emptyDir": {},
"name": "cache-volume"
}
$ kubectl get pod sample-emptydir -o json | jq '.spec.containers[0].volumeMounts[0]'
{
"mountPath": "/cache",
"name": "cache-volume"
}
# 中身は空
$ kubectl exec -it sample-emptydir -- ls -la /cache
total 8
drwxrwxrwx 2 root root 4096 Jul 9 14:26 .
drwxr-xr-x 1 root root 4096 Jul 9 14:26 ..
hostPath
はNode
上の領域を指定してPod
のコンテナにマウントする.Node
に直接影響を与えるので扱いには注意が必要.
# Node(gke-k8s-01-pool-2-641104a4-7r06)の /etc が /srv としてPodにマウントされている
$ kubectl get pod sample-hostpath -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
sample-hostpath 1/1 Running 0 3m22s 10.0.1.5 gke-k8s-01-pool-2-641104a4-7r06 <none> <none>
$ kubectl get pod sample-hostpath -o json | jq '.spec.volumes[0]'
{
"hostPath": {
"path": "/etc",
"type": "DirectoryOrCreate"
},
"name": "hostpath-sample"
}
$ kubectl get pod sample-hostpath -o json | jq '.spec.containers[0].volumeMounts[0]'
{
"mountPath": "/srv",
"name": "hostpath-sample"
}
# マウントしたNodeの領域にアクセスする
$ kubectl exec -it sample-hostpath cat /srv/os-release | grep PRETTY_NAME
PRETTY_NAME="Container-Optimized OS from Google" # NodeのOS
# コンテナ自体の領域にアクセスする
$ kubectl exec -it sample-hostpath cat /etc/os-release | grep PRETTY_NAME
PRETTY_NAME="Debian GNU/Linux 9 (stretch)" # コンテナのOS
# 実際にNodeの同じ領域のファイルを確認する
$ gcloud compute ssh gke-k8s-01-pool-2-641104a4-7r06
gke-k8s-01-pool-2-641104a4-7r06 ~ $ cat /etc/os-release | grep PRETTY_NAME
PRETTY_NAME="Container-Optimized OS from Google"
downwardAPI
はPod
の情報をPod
上の領域にファイルとして配置する.
# /srv/podnameと/srv/cpu-requestにmetadata.nameとrequests.cpuの値がファイルとして配置されている
$ kubectl get pod sample-downward-api -o json | jq '.spec.volumes[0]'
{
"downwardAPI": {
"defaultMode": 420,
"items": [
{
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.name"
},
"path": "podname"
},
{
"path": "cpu-request",
"resourceFieldRef": {
"containerName": "nginx-container",
"divisor": "0",
"resource": "requests.cpu"
}
}
]
},
"name": "downward-api-volume"
}
$ kubectl get pod sample-downward-api -o json | jq '.spec.containers[0].volumeMounts[0]'
{
"mountPath": "/srv",
"name": "downward-api-volume"
}
# ファイルを確認する
$ kubectl exec -it sample-downward-api cat /srv/podname
sample-downward-api
$ kubectl exec -it sample-downward-api cat /srv/cpu-request
1
projected
は複数のVolume
, Secret
, ConfigMap
などを1つのvolumeMounts
にまとめる.
# Secret(sample-db-auth), ConfigMap(sample-configmap), downwardAPIが /srv にまとめてマウントされている
$ kubectl get secret sample-db-auth
NAME TYPE DATA AGE
sample-db-auth Opaque 2 24h
$ kubectl get configmap sample-configmap
NAME DATA AGE
sample-configmap 5 12d
$ kubectl get pod sample-projected -o yaml | yq read - 'spec.volumes[0]'
name: projected-volume
projected:
defaultMode: 420
sources:
- secret:
items:
- key: username
path: secret/username.txt
name: sample-db-auth
- configMap:
items:
- key: nginx.conf
path: configmap/nginx.conf
name: sample-configmap
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.name
path: podname
$ kubectl get pod sample-projected -o yaml | yq read - 'spec.containers[0].volumeMounts[0]'
mountPath: /srv
name: projected-volume
# 中身を確認
$ kubectl exec -it sample-projected ls /srv
configmap podname secret
$ kubectl exec -it sample-projected cat /srv/secret/username.txt
root
$ kubectl exec -it sample-projected cat /srv/configmap/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
$ kubectl exec -it sample-projected cat /srv/podname
sample-projected
PersistentVolume
永続化領域を扱うためのVolume
で, 個別のリソースとして扱う.
ネットワーク経由でPod
等から利用するため, アクセス可能な場所にディスクを用意する必要がある.
(Ingress
みたいな感じ?)
# GCPでディスクを作成
$ gcloud compute disks create --size=10GB sample-gce-pv --zone us-central1-a
NAME ZONE SIZE_GB TYPE STATUS
sample-gce-pv us-central1-a 10 pd-standard READY
# PersistentVolume(sample-pv)を作成
$ kubectl apply -f sample-pv.yaml
persistentvolume/sample-pv created
$ kubectl get pv sample-pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
sample-pv 10Gi RWO Retain Available manual 56s
PersistentVolumeClaim
Pod
等から利用できるPersistentVolume
を払い出すためのリソース.
クラスタが認識しているPersistentVolume
の中でPersistentVolumeClaim
の条件に合ったものがPod
に接続される.
# PersistentVolume(sample-pv)が存在する状態でPersistentVolumeClaim(sample-pvc)を作成
$ kubectl apply -f sample-pvc.yaml
persistentvolumeclaim/sample-pvc created
$ kubectl get pvc sample-pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
sample-pvc Bound sample-pv 10Gi RWO manual 26s
# PersistentVolume(sample-pv)がsample-pvcに確保(Bound)されている
$ kubectl get pv sample-pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
sample-pv 10Gi RWO Retain Bound default/sample-pvc manual 111s
# Podの/usr/share/nginx/htmlにsample-pvcで払い出されたPersistentVolumeがマウントされている
$ kubectl get pod sample-pvc-pod -o yaml | yq read - 'spec.volumes[0]'
name: nginx-pvc
persistentVolumeClaim:
claimName: sample-pvc
$ kubectl get pod sample-pvc-pod -o yaml | yq read - 'spec.containers[0].volumeMounts[0]'
mountPath: /usr/share/nginx/html
name: nginx-pvc
sample-pvc.yaml
sample-pvc-pod.yaml
また, StorageClass
の設定によっては,
事前にPersistentVolume
を用意しなくてもPersistentVolumeClaim
に応じたPersistentVolume
を自動で払い出すことができる(Dynamic Provisioning
).
# GKEのデフォルトStorageClass
$ kubectl describe sc standard
Name: standard
IsDefaultClass: Yes
Annotations: storageclass.kubernetes.io/is-default-class=true
Provisioner: kubernetes.io/gce-pd
Parameters: type=pd-standard
AllowVolumeExpansion: True
MountOptions: <none>
ReclaimPolicy: Delete
VolumeBindingMode: Immediate
Events: <none>
# PersistentVolumeがない状態でStorageClassを指定しないPersistentVolumeClaimを作成する
$ kubectl get pv
No resources found.
$ kubectl apply -f sample-pvc-default-storageclass.yaml
persistentvolumeclaim/sample-pvc created
# GCPのディスクとPersistentVolumeが自動で払い出されている
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-332c4de9-ce56-11ea-a3f9-42010a80005d 4Gi RWO Delete Bound default/sample-pvc standard 69s
$ gcloud compute disks list
NAME LOCATION LOCATION_SCOPE SIZE_GB TYPE STATUS
gke-k8s-01-08fea67e-dy-pvc-332c4de9-ce56-11ea-a3f9-42010a80005d us-central1-a zone 4 pd-standard READY
# 使用したPersistentVolumeClaimはsample-pvc.yamlを改変したもの
$ cat sample-pvc-default-storageclass.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: sample-pvc
spec:
resources:
requests:
storage: 4Gi
accessModes:
- ReadWriteOnce