作業メモ。
Kubernetes完全ガイド impress top gearシリーズを読みながら手元で確認した時のメモ。
環境
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.3", GitCommit:"2bba0127d85d5a46ab4b778548be28623b32d0b0", GitTreeState:"clean", BuildDate:"2018-05-28T20:03:09Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"11+", GitVersion:"v1.11.5-eks-6bad6d", GitCommit:"6bad6d9c768dc0864dab48a11653aa53b5a47043", GitTreeState:"clean", BuildDate:"2018-12-06T23:13:14Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
Kubernetes 環境としては EKS を利用。
Volume/PersistentVolume(PV)/PersistentVolumeClaim(PVC)の違い
Volume
Volume は予め用意されたボリュームをマニュフェストに直接指定することで利用可能となる。そのため、Kubernetes 上で新規にボリュームを作成したり、既存のボリュームを削除することは出来ない。例えば Node 上の任意のパスをマウントすること出来たりする。
PersistentVolume(PV)
PersistentVolume(以降 PV) は永続化領域として確保される Volume。
PV は個別にリソースを作成して利用する必要があり、後述する PersistentVolumeClaim(以降 PVC)経由で利用する。
PersistentVolumeClaim(PVC)
Claim(要求する)という意味の通り、作成された PV リソースからアサインするためのリソース。PV はクラスタにボリュームを登録するだけなので Pod から利用する場合には PVC を定義する必要がある。
また、Dynamic プロビジョニングを行うことで事前に PV を作成せず、PVC が作成された際に動的に PV を作成する事ができる。
以降ではそれぞれを試してみる。
Volume
Kubernetes では Volume を抽象化して Pod と疎結合なりソースとして定義している。様々な種類が提供されており、クラウドサービスのボリュームもサポートされている。
今回は Pod 用の一時的なディスクとして利用可能な emptyDir とホスト(ノード)の任意のパスをマウントできる hostPath を使ってみる。
emptyDir
emptyDir は Pod 用の一時的なディスク領域として利用可能であり、Pod が terminate されると削除される。
以下のマニュフェストで試す。
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
$kubectl apply -f emptydir.yaml
pod "test-pd" created
# エラー
$kubectl exec -it test-pd -- /bin/sh
rpc error: code = 2 desc = oci runtime error: exec failed: container_linux.go:262: starting container process caused "exec: \"/bin/sh\": stat /bin/sh: no such file or directory"
command terminated with exit code 126
Scratch から作っているので /bin/sh もないみたい。
kubernetes/test/images/test-webserver/Dockerfile
image を nginx にしてから再度 apply.
# /cache がマウントされている
$kubectl exec -it test-pd -- df -h
Filesystem Size Used Avail Use% Mounted on
overlay 20G 3.4G 17G 17% /
tmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/nvme0n1p1 20G 3.4G 17G 17% /cache
shm 64M 0 64M 0% /dev/shm
tmpfs 1.9G 12K 1.9G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1.9G 0 1.9G 0% /sys/firmware
# テストファイルを作成
$ kubectl exec -it test-pd -- touch /cache/test.txt
# ファイルが存在する
$kubectl exec -it test-pd -- ls /cache/
test.txt
# Pod を削除
$kubectl delete -f emptydir.yaml
pod "test-pd" deleted
# 再作成
$kubectl apply -f emptydir.yaml
pod "test-pd" created
# ファイルはない
$kubectl exec -it test-pd -- ls /cache/
hostPath
hostPath を利用することで Kubernetest ノード(ホスト)の任意の領域をマウントできる。
以下では /tmp
をマウントしてみる
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /tmp
# this field is optional
type: Directory
$ kubectl apply -f hostpath.yaml
pod "test-pd" created
# /test-pd にマウント
$kubectl exec -it test-pd -- df -h
Filesystem Size Used Avail Use% Mounted on
overlay 20G 3.4G 17G 17% /
tmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/nvme0n1p1 20G 3.4G 17G 17% /test-pd
shm 64M 0 64M 0% /dev/shm
tmpfs 1.9G 12K 1.9G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1.9G 0 1.9G 0% /sys/firmware
# ファイルを作成
$kubectl exec -it test-pd -- touch /test-pd/test.txt
# ファイルが存在
$kubectl exec -it test-pd -- ls /test-pd/
systemd-private-857b4e69b79c4039a8d0e50db7fa36bd-chronyd.service-zdQfQx
test.txt
# Pod が動作している Node を確認
$kubectl describe pod test-pd |grep Node
Node: ip-172-31-0-56.ap-northeast-1.compute.internal/172.31.0.56
Node-Selectors: <none>
# Pod を削除
$ kubectl delete pod test-pd
pod "test-pd" deleted
# Node にSSH ログインして /tmp を確認するとファイルが確認できる。Pod が削除されてもファイルは消えない
$ls /tmp/
systemd-private-857b4e69b79c4039a8d0e50db7fa36bd-chronyd.service-zdQfQx
test.txt
上記のように Pod が削除されても Node のファイルは削除されない。
ただし、Node が複数存在する場合、再度 Pod を起動した場合に同じ Node 上で動作するかは分からない。
PersistentVolume(PV)
PersistentVolume(PV) は永続化領域として確保される Volume。
PV は個別にリソースを作成して利用する必要があり、後述する PersistentVolumeClaim(PVC)経由で利用する。
今回は PV を個別に作成する。
自分の実行環境が AWS なので以下を見て試す。
Persistent Storage Using AWS Elastic Block Store
# 予め EBS を作成
$aws ec2 create-volume --size=10 --volume-type=gp2 --availability-zone ap-northeast-1c
{
"AvailabilityZone": "ap-northeast-1c",
"CreateTime": "2019-01-01T21:59:55.000Z",
"Encrypted": false,
"Size": 5,
"SnapshotId": "",
"State": "creating",
"VolumeId": "vol-0af3eacd9228e23a0",
"Iops": 100,
"Tags": [],
"VolumeType": "gp2"
}
上記で作成した volume-id を利用してマニュフェストファイルを作成する。
apiVersion: "v1"
kind: "PersistentVolume"
metadata:
name: "pv0001"
spec:
capacity:
storage: "5Gi"
accessModes:
- "ReadWriteOnce"
awsElasticBlockStore:
fsType: "ext4"
volumeID: "vol-0af3eacd9228e23a0"
上記を apply して作成する。
# pv を作成
$ kubectl apply -f pv.yaml
persistentvolume "pv0001" created
# pv リソースが存在する
$kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv0001 10Gi RWO Retain Available 6s 14s
なお、上記場合、pv リソースを削除しても EBS は削除されないので別途削除が必要。
# pv を削除
$kubectl delete pv pv0001
persistentvolume "pv0001" deleted
# pv は削除済み
$ kubectl get pv
No resources found.
# EBS は削除されない
$aws ec2 describe-volumes --volume-ids vol-0af3eacd9228e23a0
{
"Volumes": [
{
"Attachments": [],
"AvailabilityZone": "ap-northeast-1c",
"CreateTime": "2019-01-01T21:59:55.943Z",
"Encrypted": false,
"Size": 10,
"SnapshotId": "",
"State": "available",
"VolumeId": "vol-0af3eacd9228e23a0",
"Iops": 100,
"VolumeType": "gp2"
}
]
}
# 必要に応じて削除する
$aws ec2 delete-volume --volume-id vol-0af3eacd9228e23a0
# 削除確認
$ aws ec2 describe-volumes --volume-ids vol-0af3eacd9228e23a0
An error occurred (InvalidVolume.NotFound) when calling the DescribeVolumes operation: The volume 'vol-0af3eacd9228e23a0' does not exist.
PersistentVolumeClaim(PVC)
PVC は PV の要求を行うリソース。
前途の PV は PVC 経由でマニュフェストから要求され、利用する。
PVC は要求された内容(容量、ラベル)から適切な PV を探し、Volume を割り当てる。
プロビジョニングの方法として2種類あり、前途のように予め PV を作成する方法を Static、PVC が作成(つまり Volume の要求があった)際に動的に PV を作成する Dynamic の2種類がある。
Dynamic を利用することで事前に PV や必要なストレージ(EBSなど)を作る手間を省くことが出来る。
今回は上記 Dynamic のプロビジョニングを試す。
StorageClass を作成する
PVC をマニュフェストに記載する際に StorageClass を指定する。
StorageClass ではどの Provider(GCEPersistentDisk なのか AWSElasticBlockStore なのかなど)であるかなど利用の際の設定を記載する。
EKS では gp2 という StorageClass が予め作成されている。
$kubectl describe storageclass gp2
Name: gp2
IsDefaultClass: Yes
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"name":"gp2","namespace":""},"parameters":{"fsType":"ext4","type":"gp2"},"provisioner":"kubernetes.io/aws-ebs"}
,storageclass.kubernetes.io/is-default-class=true
Provisioner: kubernetes.io/aws-ebs
Parameters: fsType=ext4,type=gp2
AllowVolumeExpansion: <unset>
MountOptions: <none>
ReclaimPolicy: Delete
VolumeBindingMode: Immediate
Events: <none>
別途 StorageClass の作成なども可能だが今回は上記を利用する。
PVC を作成する
PVC を作成してみる。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
storageClassName: gp2
# pvc を作成
$kubectl apply -f pvc.yaml
persistentvolumeclaim "myclaim" created
# pvc を確認
$kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
myclaim Bound pvc-9a73a51a-0ed3-11e9-b090-0ae6cc179478 8Gi RWO gp2 1m
# 少し時間が経つと pv も作成される
$kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-9a73a51a-0ed3-11e9-b090-0ae6cc179478 8Gi RWO Delete Bound default/myclaim gp2 1m
# describe すると詳細が分かる
$kubectl describe pv pvc-9a73a51a-0ed3-11e9-b090-0ae6cc179478
Name: pvc-9a73a51a-0ed3-11e9-b090-0ae6cc179478
Labels: failure-domain.beta.kubernetes.io/region=ap-northeast-1
failure-domain.beta.kubernetes.io/zone=ap-northeast-1c
Annotations: kubernetes.io/createdby=aws-ebs-dynamic-provisioner
pv.kubernetes.io/bound-by-controller=yes
pv.kubernetes.io/provisioned-by=kubernetes.io/aws-ebs
Finalizers: [kubernetes.io/pv-protection]
StorageClass: gp2
Status: Bound
Claim: default/myclaim
Reclaim Policy: Delete
Access Modes: RWO
Capacity: 8Gi
Node Affinity: <none>
Message:
Source:
Type: AWSElasticBlockStore (a Persistent Disk resource in AWS)
VolumeID: aws://ap-northeast-1c/vol-05129125c1e7e8696
FSType: ext4
Partition: 0
ReadOnly: false
Events: <none>
# volume id から EBS を確認.Tag に Kubernetes の情報が記載されている
$ aws ec2 describe-volumes --volume-ids vol-05129125c1e7e8696
{
"Volumes": [
{
"Attachments": [],
"AvailabilityZone": "ap-northeast-1c",
"CreateTime": "2019-01-02T21:16:15.189Z",
"Encrypted": false,
"Size": 8,
"SnapshotId": "",
"State": "available",
"VolumeId": "vol-05129125c1e7e8696",
"Iops": 100,
"Tags": [
{
"Key": "Name",
"Value": "kubernetes-dynamic-pvc-9a73a51a-0ed3-11e9-b090-0ae6cc179478"
},
{
"Key": "kubernetes.io/created-for/pvc/name",
"Value": "myclaim"
},
{
"Key": "kubernetes.io/created-for/pv/name",
"Value": "pvc-9a73a51a-0ed3-11e9-b090-0ae6cc179478"
},
{
"Key": "kubernetes.io/created-for/pvc/namespace",
"Value": "default"
},
{
"Key": "kubernetes.io/cluster/eks-first-cluster",
"Value": "owned"
}
],
"VolumeType": "gp2"
}
]
}
Pod から利用する
Pod から PVC つまりボリュームを利用する場合、claimName を指定する。
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
# pod を作成
$ kubectl apply -f pod-pvc.yaml
pod "mypod" created
# Pod では /var/www/html がマウントされており、pvc で要求した 8G
$kubectl exec -it mypod -- df -h
Filesystem Size Used Avail Use% Mounted on
overlay 20G 3.4G 17G 17% /
tmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/nvme0n1p1 20G 3.4G 17G 17% /etc/hosts
shm 64M 0 64M 0% /dev/shm
/dev/nvme1n1 7.8G 36M 7.7G 1% /var/www/html
tmpfs 1.9G 12K 1.9G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1.9G 0 1.9G 0% /sys/firmware
# pod が動いている node を確認
$ kubectl describe pod mypod |grep Node
Node: ip-172-31-0-56.ap-northeast-1.compute.internal/172.31.0.56
Node-Selectors: <none>
# 該当する EC2 の BlockDeviceMappings を確認.pvc より作成された volume vol-05129125c1e7e8696 が EC2 にアタッチされている
$aws ec2 describe-instances --filters "Name=network-interface.addresses.private-ip-address,Values=172.31.0.56"
(略)
"Architecture": "x86_64",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"AttachTime": "2018-12-20T02:33:55.000Z",
"DeleteOnTermination": true,
"Status": "attached",
"VolumeId": "vol-0347b33bce60e7776"
}
},
{
"DeviceName": "/dev/xvdbb",
"Ebs": {
"AttachTime": "2019-01-02T21:27:04.000Z",
"DeleteOnTermination": false,
"Status": "attached",
"VolumeId": "vol-05129125c1e7e8696"
}
}
],
リソースを削除する
検証が終わったので削除しつつ、挙動を確認する。
# Pod を削除
$kubectl delete pod mypod
pod "mypod" deleted
# EBS が EC2 からデタッチされている
$ aws ec2 describe-instances --filters "Name=network-interface.addresses.private-ip-address,Values=172.31.0.56"
(略)
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"AttachTime": "2018-12-20T02:33:55.000Z",
"DeleteOnTermination": true,
"Status": "attached",
"VolumeId": "vol-0347b33bce60e7776"
}
}
],
# EC2 からでタッチされているが EBS は存在する
$aws ec2 describe-volumes --volume-ids vol-05129125c1e7e8696 |grep State
"State": "available",
# pvc を削除
$kubectl delete pvc myclaim
persistentvolumeclaim "myclaim" deleted
# pv も削除される
$ kubectl get pv
No resources found.
# EBS も削除される
$aws ec2 describe-volumes --volume-ids vol-05129125c1e7e8696
An error occurred (InvalidVolume.NotFound) when calling the DescribeVolumes operation: The volume 'vol-05129125c1e7e8696' does not exist.
上記のように PVC が削除された際に EBS も削除される挙動は StorageClass の ReclaimPolicy が Delete であるため。
ReclaimPoliyc を Retain にすれば EBS は削除されない。