Kubernetes Volume
学习参考:卷
环境准备
bash
[root@master30 ~]# kubectl create ns storage
[root@master30 ~]# kubectl config set-context --current --namespace storage
Volume 类型
Kubernetes支持Volume类型有:
- emptyDir
- hostPath
- gcePersistentDisk
- awsElasticBlockStore
- nfs
- iscsi
- fc (fibre channel)
- flocker
- glusterfs
- rbd
- cephfs
- gitRepo
- secret
- persistentVolumeClaim
- downwardAPI
- projected
- azureFileVolume
- azureDisk
- vsphereVolume
- Quobyte
- PortworxVolume
- ScaleIO
- StorageOS
- local
emptyDir
默认情况下,当Pod分配到Node上时,将会创建emptyDir,只要Node上的Pod一直运行,Volume就会一直存。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。
**实验:**准备一个包含2个容器的pod,使用emptyDir。
bash
[root@master30 ~]# vim pod-with-emptyDir.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
app: busybox
spec:
volumes:
- name: datavolume
emptyDir: {}
containers:
- name: busybox1
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
- name: busybox2
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
配置说明:
-
spec.volumes:定义卷,是默认卷类型。
-
spec.containers.volumeMounts:引用卷
bash
# 创建pod
[root@master30 ~]# kubectl apply -f pod-with-emptyDir.yaml
pod/busybox created
# 获取容器ID
[root@master30 ~]# kubectl describe pod busybox |egrep -o 'Container ID.*//.{12}'
Container ID: containerd://07d189383321
Container ID: containerd://bb02c680ca8a
# 获取容器所在节点
[root@master30 ~]# kubectl describe pod busybox |grep Node:
Node: worker32.laoma.cloud/10.1.8.32
# 登录到node查看容器挂载情况
root@worker32:~# crictl inspect 07d189383321|grep datavolume
"hostPath": "/var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume",
"host_path": "/var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume"
"source": "/var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume",
# 创建数据
[root@master30 ~]# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
[root@master30 ~]# kubectl exec busybox -c busybox2 -- ls /data
b1-f1
# node上查看数据
root@worker32:~# ls /var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume
b1-f1
# 删除pod,验证emptyDir
[root@master30 ~]# kubectl delete pod busybox --force
root@worker32:~# ls /var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume
ls: cannot access '/var/lib/kubelet/pods/8417656f-4b6c-4b2a-a2f3-f4a248c4c043/volumes/kubernetes.io~empty-dir/datavolume': No such file or directory
# 容器删除后,需要等待一些时间,临时卷数据等待才会删除。
hostPath
hostPath允许Pod将Node的文件系统中某个目录挂载到Pod内部。pod删除后,hostPath卷数据保留。
**实验:**准备一个pod,使用hostPath。
bash
[root@master30 ~]# vim pod-with-hostPath.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
labels:
app: busybox
spec:
volumes:
- name: datavolume
hostPath:
path: /busyboxdir
containers:
- name: busybox1
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
- name: busybox2
image: hub.laoma.cloud/library/busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
volumeMounts:
- mountPath: /data
name: datavolume
# 设置readOnly,控制读写,默认值是false,也就是读写访问。
readOnly: true
bash
# 创建pod
[root@master30 ~]# kubectl apply -f pod-with-hostPath.yaml
pod/busybox created
# 获取容器ID
[root@master30 ~]# kubectl describe pod busybox |egrep -o 'Container ID.*//.{12}'
Container ID: containerd://318922d0c666
Container ID: containerd://0688146babe5
# 获取容器所在节点
[root@master30 ~]# kubectl describe pod busybox |grep Node:
Node: worker32.laoma.cloud/10.1.8.32
# 登录到worker31查看容器挂载情况
root@worker32:~# crictl inspect 318922d0c666|grep busyboxdir
"hostPath": "/busyboxdir",
"host_path": "/busyboxdir"
"source": "/busyboxdir",
# 创建数据
[root@master30 ~]# kubectl exec busybox -c busybox1 -- touch /data/b1-f1
[root@master30 ~]# kubectl exec busybox -c busybox2 -- ls /data
b1-f1
[root@master30 ~]# kubectl exec busybox -c busybox2 -- touch /data/b2-f2
touch: /data/b2-f2: Read-only file system
# node上查看数据
root@worker32:~# ls /busyboxdir/
b1-f1
# 删除pod,验证hostPath
[root@master30 ~]# kubectl delete pod busybox --force
root@worker32:~# ls /busyboxdir/
b1-f1
# pod删除后,hostPath卷数据保留。
NFS 存储
NFS卷,将数据存储在NFS共享中。
准备NFS共享
bash
# 安装 NFS server
[root@master30 ~]# apt install -y nfs-kernel-server
# 安创建NFS目录 修改创建文件夹的权限
[root@master30 ~]# mkdir -m 777 /nfsshares
[root@master30 ~]# echo hello laoma > /nfsshares/index.html
# 配置共享,允许所有客户端访问
[root@master30 ~]# cat << EOF > /etc/exports
/nfsshares *(rw)
EOF
# 重启 nfs server
[root@master30 ~]# systemctl restart nfs-server.service
# 客户端安装
root@worker31:~# apt install -y nfs-common
root@worker32:~# apt install -y nfs-common
准备 pod
bash
[root@master30 ~]# vim pod-with-nfs.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: nginx
name: nginx
spec:
volumes:
- name: nfs
nfs:
server: 10.1.8.30
path: "/nfsshares"
containers:
- image: hub.laoma.cloud/library/nginx
name: nginx
volumeMounts:
- name: nfs
mountPath: "/usr/share/nginx/html"
bash
# 创建pod
[root@master30 ~]# kubectl apply -f pod-with-nfs.yaml
pod/nginx created
# 获取容器ID
[root@master30 ~]# kubectl describe pod nginx |egrep -o 'Container ID.*//.{12}'
Container ID: containerd://c5e734dab9b5
# 获取容器所在节点
[root@master30 ~]# kubectl describe pod nginx |grep Node:
Node: worker32.laoma.cloud/10.1.8.32
# 登录到worker31查看容器挂载情况
root@worker32:~# crictl inspect c5e734dab9b5|grep |grep -e nfs -e html
"containerPath": "/usr/share/nginx/html",
"hostPath": "/var/lib/kubelet/pods/b5bd3f1a-3ef4-4327-95ff-61352268ee41/volumes/kubernetes.io~nfs/nfs",
"container_path": "/usr/share/nginx/html",
"host_path": "/var/lib/kubelet/pods/b5bd3f1a-3ef4-4327-95ff-61352268ee41/volumes/kubernetes.io~nfs/nfs"
"destination": "/usr/share/nginx/html",
"source": "/var/lib/kubelet/pods/b5bd3f1a-3ef4-4327-95ff-61352268ee41/volumes/kubernetes.io~nfs/nfs",
root@worker32:~# df|grep nfsshare
10.1.8.30:/nfsshares 101590016 6659072 89723904 7% /var/lib/kubelet/pods/b5bd3f1a-3ef4-4327-95ff-61352268ee41/volumes/kubernetes.io~nfs/nfs
# 访问容器
[root@master30 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 5m19s 10.224.51.171 worker32.laoma.cloud <none> <none>
[root@master30 ~]# curl 10.224.51.171
hello laoma
# 创建数据
[root@master30 ~]# kubectl exec nginx -- ls /usr/share/nginx/html
index.html
[root@master30 ~]# kubectl exec nginx -- touch /usr/share/nginx/html/test.html
[root@master30 ~]# ls /nfsshares
index.html test.html
# 删除pod,验证nfs卷数据
[root@master30 ~]# kubectl delete pod nginx --force
pod "nginx" deleted
[root@master30 ~]# ls /nfsshares
index.html test.html
# pod删除后,nfs卷数据保留。
持久性存储
学习参考:持久卷
Kubernetes 使用 persistent volume(PV)架构为集群提供永久存储。
PV 和 PVC 架构
开发人员不知道特定云环境的细节的情况下,只需要使用persistentVolumeClaim(PVC)请求PV资源,实现持久化存储。
-
Persistent Volume ,由PersistentVolume API对象定义,代表集群中现有存储。PV的生命周期与使用其的pod无关。Persistent Volume 是集群级别资源。
-
Persistent Volume Claim ,由PersistentVolumeClaim API对象定义,代表开发人员请求PV。Persistent Volume Claim 是 namespace 级别资源。
创建 PV 和 PVC
创建 PV
集群管理员可以创建任意数量PV,取决于后端存储。
为了方便演示,我们这里使用NFS后端存储。
pv文件示例:
bash
[root@master30 ~]# vim pv.yaml
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
nfs:
path: /nfsshares
server: 10.1.8.30
bash
[root@master30 ~]# kubectl apply -f pv.yaml
[root@master30 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Recycle Available 3s
创建 PVC
用户创建PVC,pod使用PVC申请特定容量、特定modes和特定存储类别的存储。master监控PVCs,查找匹配的PV或者等待后端存储创建相应PV,然后绑定PV和PVC。
pvc示例:
bash
[root@master30 ~]# vim pvc.yaml
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: webclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
bash
[root@master30 ~]# kubectl apply -f pvc.yaml
[root@master30 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
webclaim Bound web 5Gi RWO 4s
创建Pod使用PV
bash
[root@master30 ~]# vim pod-with-pvc.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx
name: web
ports:
- containerPort: 80
name: web-port
volumeMounts:
- name: web-persistent-storage
mountPath: /usr/share/nginx/html
volumes:
- name: web-persistent-storage
persistentVolumeClaim:
claimName: webclaim
bash
[root@master30 ~]# kubectl apply -f pod-with-pvc.yaml
pod/web created
[root@master30 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 35s 10.224.193.69 worker32.laoma.cloud <none> <none>
# 测试:访问pod web页面
[root@master30 ~]# curl http://10.224.193.69
hello laoma
[root@master30 ~]# echo test > /nfsshares/test.html
[root@master30 ~]# curl http://10.224.193.69/test.html
test
# 删除pod
[root@master30 ~]# kubectl delete pod web --force
PV accessModes
PersistentVolume 卷访问模式有:
-
ReadWriteOnce ,卷可以被一个节点以读写方式挂载,也允许同一节点上的多个 Pod 读写访问卷。
-
ReadOnlyMany ,卷可以被多个节点以只读方式挂载。
-
ReadWriteMany ,卷可以被多个节点以读写方式挂载。
-
ReadWriteOncePod ,卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。
这篇博客文章 Introducing Single Pod Access Mode for PersistentVolumes 描述了更详细的内容。
在命令行接口(CLI)中,访问模式也使用以下缩写形式:
- RWO - ReadWriteOnce
- ROX - ReadOnlyMany
- RWX - ReadWriteMany
- RWOP - ReadWriteOncePod
不同存储后端支持不同的模式:
| 卷插件 | ReadWriteOnce | ReadOnlyMany | ReadWriteMany | ReadWriteOncePod |
|---|---|---|---|---|
| AzureFile | ✓ | ✓ | ✓ | - |
| CephFS | ✓ | ✓ | ✓ | - |
| CSI | 取决于驱动 | 取决于驱动 | 取决于驱动 | 取决于驱动 |
| FC | ✓ | ✓ | - | - |
| FlexVolume | ✓ | ✓ | 取决于驱动 | - |
| GCEPersistentDisk | ✓ | ✓ | - | - |
| Glusterfs | ✓ | ✓ | ✓ | - |
| HostPath | ✓ | - | - | - |
| iSCSI | ✓ | ✓ | - | - |
| NFS | ✓ | ✓ | ✓ | - |
| RBD | ✓ | ✓ | - | - |
| VsphereVolume | ✓ | - | -(Pod 运行于同一节点上时可行) | - |
| PortworxVolume | ✓ | - | ✓ | - |
重要: 每个卷同一时刻只能以一种访问模式挂载,即使该卷能够支持多种访问模式。 例如,一个 GCEPersistentDisk 卷可以被某节点以 ReadWriteOnce 模式挂载,或者被多个节点以 ReadOnlyMany 模式挂载,但不可以同时以两种模式挂载。
常见的NAS存储都支持三种存储模式:ReadWriteOnce、ReadOnlyMany、ReadWriteMany。
PV volumeModes
特性状态: Kubernetes v1.18 [stable]
针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):Filesystem(文件系统) 和 Block(块)。 volumeMode 是一个可选的 API 参数。 如果该参数被省略,默认的卷模式是 Filesystem。
-
Filesystem 卷 ,会被 Pod 挂载(Mount) 到某个目录。 如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前在设备上创建文件系统。
-
Block 卷 ,会被作为原始块设备来使用。 这类卷以块设备的方式交给 Pod 使用,其上没有任何文件系统。 这种模式对于为 Pod 提供一种使用最快可能方式来访问卷而言很有帮助, Pod 和卷之间不存在文件系统层。另外,Pod 中运行的应用必须知道如何处理原始块设备。 关于如何在 Pod 中使用
volumeMode: Block的卷, 可参阅原始块卷支持。
PVC与PV匹配规则
-
PV的mode必须高于PVC申请的最低要求:mode 优先级,可简单理解为ROX<RWO<RWX。例如,用户请求RWO模式PV,但是目前只有NFS PV(RWO+ROX+RWX),PVC将匹配NFS。
-
容量满足最低要求:具有相同modes卷会被分组,然后根据size分类(由小到大)。
-
pv storage classes:用于对pv进行分类,pvc可以根据storageClassName参数申请特定类型pv。如果pv设置了storageClassName,那么 pvc 申请资源的时候也要指定storageClassName。例如,storageClassName指定为 ns1-storage,ns1中pvc申请也指定storageClassName为ns1-storage。
PV 回收策略
当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。
PersistentVolume 回收策略支持:Retain(保留)、Recycle(回收)、Delete(删除)。
Retain(保留)
回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然保留上一次关联的pvc信息,清理掉上一次关联的pvc信息才可分配给其他pvc。Retain(保留)策略是默认策略。
示例:
bash
[root@master30 ~]# vim pv-pvc-Retain.yaml
yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
capacity:
storage: 5Gi
#persistentVolumeReclaimPolicy: Retain
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
nfs:
path: /nfsshares
server: 10.1.8.30
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: webclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
bash
[root@master30 ~]# kubectl apply -f pv-pvc-Retain.yaml
[root@master30 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Retain Bound default/webclaim 22s
[root@master30 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
webclaim Bound web 5Gi RWO 23s
删除 pvc 验证
bash
[root@master30 ~]# kubectl delete pvc webclaim
[root@master30 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Retain Released default/webclaim 3m4s
# 创建新pvc,无法绑定
[root@master30 ~]# vim pvc-db.yaml
yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dbclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
bash
[root@master30 ~]# kubectl apply -f pvc-db.yaml
[root@master30 ~]# kubectl get pvc dbclaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
dbclaim Pending 11s
# 手动清理卷上claimRef信息,删除claimRef部分
[root@master30 ~]# kubectl edit pv web
yaml
......
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
# 删除claimRef部分
# claimRef:
# apiVersion: v1
# kind: PersistentVolumeClaim
# name: webclaim
# namespace: test
# resourceVersion: "112157"
# uid: 0839cdc1-81bb-11e9-9ca8-52540000fa0a
# 删除claimRef部分
nfs:
path: /web
server: 10.1.8.30
......
bash
# dbclaim 绑定成功
[root@master30 ~]# kubectl get pvc dbclaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
dbclaim Bound web 5Gi RWO 3m49s
清理环境
bash
[root@master30 ~]# kubectl delete pvc dbclaim
[root@master30 ~]# kubectl delete pv web
[root@master30 ~]# ls /nfsshares/
index.html test.html
**注意:**删除pv,并不会删除后端存储中数据。
Recycle(回收)
**警告:**回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。
Recycle(回收)策略的效果与 Retaine(保留)一致。
Delete(删除)
对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS 或 GCE PD 卷)中移除所关联的存储资产。
示例:
bash
[root@master30 ~]# vim pv-pvc-Delete.yaml
yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
capacity:
storage: 5Gi
persistentVolumeReclaimPolicy: Delete
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
nfs:
path: /nfsshares
server: 10.1.8.30
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: webclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
bash
[root@master30 ~]# kubectl apply -f pv-pvc-Delete.yaml
[root@master30 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Retain Bound default/webclaim 22s
[root@master30 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
webclaim Bound web 5Gi RWO 23s
删除 pvc 验证
bash
[root@master30 ~]# kubectl delete pvc webclaim
[root@master30 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Retain Released default/webclaim 3m4s
# pv
[root@master30 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
web 5Gi RWO Delete Failed default/webclaim 33s
[root@master30 ~]# kubectl describe pv web
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning VolumeFailedDelete 50s persistentvolume-controller error getting deleter volume plugin for volume "web": no deletable volume plugin matched
kubernetes提示信息为Error getting deleter volume plugin for volume "web": no deletable volume plugin matched,也就是为找到删除插件,所以无法删除。
此时我们可以手动删除pv,手动删除pv,并不会删除后端存储数据。
思考
问题:如何创建一个只能绑定给特定pvc的pv?
答案:在定义pv的时候,指定claimRef属性。
yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: web
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
nfs:
path: /nfsshares
server: 10.1.8.30
claimRef:
name: webclaim
namespace: test
PV 卷的类型
PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件:
csi- 容器存储接口 (CSI)fc- Fibre Channel (FC) 存储hostPath- HostPath 卷 (仅供单节点测试使用;不适用于多节点集群;请尝试使用local卷作为替代)iscsi- iSCSI (SCSI over IP) 存储local- 节点上挂载的本地存储设备nfs- 网络文件系统 (NFS) 存储
以下的持久卷已被弃用。这意味着当前仍是支持的,但是 Kubernetes 将来的发行版会将其移除。
azureFile- Azure File (于 v1.21 弃用)flexVolume- FlexVolume (于 v1.23 弃用)gcePersistentDisk- GCE Persistent Disk (于 v1.17 弃用)portworxVolume- Portworx 卷 (于 v1.25 弃用)vsphereVolume- vSphere VMDK 卷 (于 v1.19 弃用)cephfs- CephFS 卷 (于 v1.28 弃用)rbd- Rados Block Device (RBD) 卷 (于 v1.28 弃用)
旧版本的 Kubernetes 仍支持这些"树内(In-Tree)"持久卷类型:
awsElasticBlockStore- AWS Elastic Block Store (EBS) (v1.27 开始不可用)azureDisk- Azure Disk (v1.27 开始不可用)cinder- Cinder (OpenStack block storage) (v1.27 开始不可用)photonPersistentDisk- Photon 控制器持久化盘。(从 v1.15 版本开始将不可用)scaleIO- ScaleIO 卷(v1.21 之后不可用)flocker- Flocker 存储 (v1.25 之后不可用)quobyte- Quobyte 卷 (v1.25 之后不可用)storageos- StorageOS 卷 (v1.25 之后不可用)
原始块卷支持
特性状态: Kubernetes v1.18 [stable]
以下卷插件支持原始块卷,包括其动态制备(如果支持的话)的卷:
- CSI
- FC(光纤通道)
- GCEPersistentDisk(已弃用)
- iSCSI
- Local 卷
- OpenStack Cinder
- RBD(已弃用)
- RBD(Ceph 块设备,已弃用)
- VsphereVolume
pv示例
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: block-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
volumeMode: Block
persistentVolumeReclaimPolicy: Retain
fc:
targetWWNs: ["50060e801049cfd1"]
lun: 0
readOnly: false
pvc示例
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: block-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Block
resources:
requests:
storage: 10Gi
pod示例
yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-block-volume
spec:
containers:
- name: fc-container
image: fedora:26
command: ["/bin/sh", "-c"]
args: [ "tail -f /dev/null" ]
volumeDevices:
- name: data
devicePath: /dev/xvda
volumes:
- name: data
persistentVolumeClaim:
claimName: block-pvc
**说明:**向 Pod 中添加原始块设备时,你要在容器内设置设备路径而不是挂载路径。
环境清理
bash
[root@master30 ~]# kubectl delete ns storage
Kubernetes Configure
环境准备
bash
[root@master30 ~]# kubectl create ns configure
[root@master30 ~]# kubectl config set-context --current --namespace configure
ConfigMap
学习参考:ConfigMap
ConfigMap 介绍
ConfigMap 是一个存储其他对象所需要使用的配置的 API 对象。 ConfigMap 使用 data 和 binaryData 字段,保存键值对数据。
data字段,用来保存 UTF-8 字符串。binaryData字段,用来保存二进制数据(base64 编码的字串) 。
ConfigMap 的名字必须是一个合法的 DNS 子域名 。data 或 binaryData 字段下面的每个键的名称都必须由字母、数字或者 -、_ 或 . 组成。在 data 下保存的键名不可以与在 binaryData 下出现的键名有重叠。
从 v1.19 开始,你可以添加一个 immutable 字段到 ConfigMap 定义中, 创建不可变更的 ConfigMap。
ConfigMap 使用建议
-
使用 ConfigMap 存储配置数据,不存数据 / 文件,上限 = 1 MiB。
-
**ConfigMap 在设计上不是用来保存大量数据的。**如果你需要保存大量数据,考虑使用挂载存储卷或者使用独立的数据库或者文件服务。
ConfigMap 创建
使用帮助
bash
kubectl create configmap NAME [--from-file=[key=]source]
[--from-literal=key1=value1] [--dry-run=server|client|none] [options]
键值对类型
bash
[root@master30 ~]# kubectl create configmap mysql --from-literal=password=redhat
[root@master30 ~]# kubectl get configmaps mysql -o yaml |grep ^data -A1
data:
password: redhat
文件类型
bash
[root@master30 ~]# echo Hello World > index.html
[root@master30 ~]# kubectl create configmap web1 --from-file=./index.html
[root@master30 ~]# kubectl get configmaps web1 -o yaml |grep ^data -A2
data:
index.html: |
Hello World
目录类型
bash
[root@master30 ~]# echo error > error.html
[root@master30 ~]# mkdir web2
[root@master30 ~]# mv index.html error.html web2
[root@master30 ~]# kubectl create configmap web2 --from-file=./web2
[root@master30 ~]# kubectl get configmaps web2 -o yaml |grep ^data -A4
data:
error.html: |
error
index.html: |
Hello World
ConfigMap 引用
环境变量方式引用
**注意:**环境变量属于特定容器级别。
bash
[root@master30 ~]# vim pod-cm-env.yaml
yaml
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
name: mysql
spec:
containers:
- image: hub.laoma.cloud/library/mysql:latest
imagePullPolicy: IfNotPresent
name: mysql
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
# 原先通过value设置环境变量值
valueFrom:
configMapKeyRef:
name: mysql
key: password
验证
bash
[root@master30 ~]# kubectl apply -f pod-cm-env.yaml
[root@master30 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 1/1 Running 0 4m33s 10.224.193.70 worker32.laoma.cloud <none>
[root@master30 ~]# kubectl exec -it mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
redhat
[root@master30 ~]# apt install -y mysql-client
[root@master30 ~]# mysql -u root -predhat -h 10.224.193.70
mysql: [Warning] Using a password on the command line interface can be insecure.
......
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> quit
Bye
# 删除 pod
[root@master30 ~]# kubectl delete pod mysql --force
以 volume 方式引用
**注意:**volumes属于pod级别,通过volumeMounts挂载。
引用整体
bash
[root@master30 ~]# vim pod-cm-volume-all.yaml
yaml
---
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
# mountPath值是一个挂载点,将configuremap中所有键值对挂载过来
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
configMap:
name: web2
验证
bash
[root@master30 ~]# kubectl apply -f pod-cm-volume-all.yaml
[root@master30 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 66s 10.224.193.69 worker32.laoma.cloud <none> <none>
[root@master30 ~]# kubectl exec web -- ls /usr/share/nginx/html
error.html
index.html
[root@master30 ~]# curl http://10.224.193.69
Hello World
[root@master30 ~]# curl http://10.224.193.69/error.html
error
引用特定key
bash
[root@master30 ~]# vim pod-cm-volume-single.yaml
yaml
---
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
configMap:
name: web2
items:
- key: index.html
path: index.html
等同于
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
mountPath: "/usr/share/nginx/html/index.html"
subPath: index.html
volumes:
- name: webcontent
configMap:
name: web2
**被挂载的 ConfigMap 内容会被自动更新。**当卷中使用的 ConfigMap 被更新时,所投射的键最终也会被更新。 kubelet 组件会在每次周期性同步时检查所挂载的 ConfigMap 是否为最新。
k8s 中 ConfigMap
以 kube-proxy 为例
bash
[root@master30 ~]# kubectl get configmaps -n kube-system
NAME DATA AGE
calico-config 4 37h
coredns 1 37h
extension-apiserver-authentication 6 37h
kube-apiserver-legacy-service-account-token-tracking 1 37h
kube-proxy 2 37h
kube-root-ca.crt 1 37h
kubeadm-config 1 37h
kubelet-config 1 37h
[root@master30 ~]# kubectl get configmaps kube-proxy -n kube-system -o yaml
apiVersion: v1
data:
config.conf: |-
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
bindAddressHardFail: false
clientConnection:
acceptContentTypes: ""
burst: 0
contentType: ""
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 0
clusterCIDR: 10.224.0.0/16
configSyncPeriod: 0s
conntrack:
maxPerCore: null
min: null
tcpCloseWaitTimeout: null
tcpEstablishedTimeout: null
detectLocalMode: ""
enableProfiling: false
healthzBindAddress: ""
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: null
minSyncPeriod: 0s
syncPeriod: 0s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
strictARP: false
syncPeriod: 0s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: ""
mode: ""
nodePortAddresses: null
oomScoreAdj: null
portRange: ""
showHiddenMetricsForVersion: ""
udpIdleTimeout: 0s
winkernel:
enableDSR: false
networkName: ""
sourceVip: ""
kubeconfig.conf: |-
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
server: https://10.1.8.30:6443
name: default
contexts:
- context:
cluster: default
namespace: default
user: default
name: default
current-context: default
users:
- name: default
user:
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
kind: ConfigMap
metadata:
annotations:
kubeadm.kubernetes.io/component-config.hash: sha256:77333136e4f6883a76e786187738706eb9a60efd1f8641f2489dbd1e085726d9
creationTimestamp: "2021-07-03T03:07:55Z"
labels:
app: kube-proxy
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:config.conf: {}
f:kubeconfig.conf: {}
f:metadata:
f:annotations:
.: {}
f:kubeadm.kubernetes.io/component-config.hash: {}
f:labels:
.: {}
f:app: {}
manager: kubeadm
operation: Update
time: "2021-07-03T03:07:55Z"
name: kube-proxy
namespace: kube-system
resourceVersion: "193"
selfLink: /api/v1/namespaces/kube-system/configmaps/kube-proxy
uid: 2b6168fd-29c8-49c4-a465-08720e9f48db
# kube-proxy有两种工作模式ipvs和iptables
# 这里 mode 值为空,查看日志发现默认使用iptables。
[root@master30 ~]# kubectl logs -n kube-system kube-proxy-8kp8w
I1019 02:26:00.975333 1 server_others.go:69] "Using iptables proxy"
I1019 02:26:01.037589 1 node.go:141] Successfully retrieved node IP: 10.1.8.31
I1019 02:26:01.040909 1 conntrack.go:100] "Set sysctl" entry="net/netfilter/nf_conntrack_max" value=131072
I1019 02:26:01.041412 1 conntrack.go:52] "Setting nf_conntrack_max" nfConntrackMax=131072
I1019 02:26:01.041902 1 conntrack.go:100] "Set sysctl" entry="net/netfilter/nf_conntrack_tcp_timeout_close_wait" value=3600
......
# 更改模式为ipvs
[root@master30 ~]# kubectl edit cm -n kube-system kube-proxy
......
mode: "ipvs"
......
# 删除pod,等待控制器重新创建
[root@master30 ~]# kubectl get pod -n kube-system |grep kube-proxy
kube-proxy-8kp8w 1/1 Running 1 37d
kube-proxy-ghgxw 1/1 Running 1 37d
kube-proxy-p6qnk 1/1 Running 1 37d
[root@master30 ~]# kubectl delete pod -n kube-system kube-proxy-{8kp8w,ghgxw,p6qnk}
pod "kube-proxy-8kp8w" deleted
pod "kube-proxy-ghgxw" deleted
pod "kube-proxy-p6qnk" deleted
# 再次查看日志
[root@master30 ~]# kubectl get pod -n kube-system |grep kube-proxy
kube-proxy-8swmx 1/1 Running 0 18s
kube-proxy-948cw 1/1 Running 0 29s
kube-proxy-xlh7b 1/1 Running 0 24s
[root@master30 ~]# kubectl logs -n kube-system kube-proxy-8swmx
I1019 03:41:12.288952 1 node.go:141] Successfully retrieved node IP: 10.1.8.31
I1019 03:41:12.290902 1 conntrack.go:52] "Setting nf_conntrack_max" nfConntrackMax=131072
I1019 03:41:12.325988 1 server.go:632] "kube-proxy running in dual-stack mode" primary ipFamily="IPv4"
I1019 03:41:12.364416 1 server_others.go:218] "Using ipvs Proxier"
......
# 实验完成后,模式更改回来
综合案例:haproxy+web
需求:
-
创建一个名称为 haproxy 的pod,使用镜像 haproxy ,pod haproxy 的配置文件保存在 configmap 中,通过 volume 方式挂载到 /usr/local/etc/haproxy/haproxy.cfg 。将流量转发到pod webapp-1 和 webapp-2。
haproxy 的配置文件模板内容如下:
iniglobal daemon maxconn 256 defaults mode http timeout connect 5000ms timeout client 50000ms timeout server 50000ms frontend http-in bind *:8080 default_backend servers backend servers server app1 10.224.84.80:80 check server app2 10.224.149.25:80 check -
创建第一个pod,名称为webapp-1 ,使用镜像nginx 。为该pod创建一个cm,名称为webapp-1,该cm保存两个值:
- index.html="hello webapp-1"
- error.html="sorry, error."
这个两个值通过volume方式挂载到**/usr/share/nginx/html**目录。
-
创建第二个pod,名称为webapp-2 ,使用镜像nginx 。为该pod创建一个cm,名称为webapp-2,该cm保存两个值:
- index.html="hello webapp-2"
- error.html="sorry, error."
这个两个值通过volume方式挂载到**/usr/share/nginx/html**目录。
解答:
-
创建第一个cm和pod
bash[root@master30 ~]# mkdir web && cd web root@master30:~/web# kubectl create cm webapp-1 --from-literal=index.html="hello webapp-1" --from-literal=error.html="sorry, error." root@master30:~/web# vim pod-webapp-1.yamlyamlapiVersion: v1 kind: Pod metadata: name: webapp-1 spec: containers: - name: nginx image: hub.laoma.cloud/library/nginx:latest imagePullPolicy: IfNotPresent volumeMounts: - name: config mountPath: "/usr/share/nginx/html" readOnly: true volumes: - name: config configMap: name: webapp-1bashroot@master30:~/web# kubectl apply -f pod-webapp-1.yaml -
创建第二个cm和pod
bashroot@master30:~/web# kubectl create cm webapp-2 --from-literal=index.html="hello webapp-2" --from-literal=error.html="sorry, error." root@master30:~/web# vim pod-webapp-2.yamlyamlapiVersion: v1 kind: Pod metadata: name: webapp-2 spec: containers: - name: nginx image: hub.laoma.cloud/library/nginx:latest imagePullPolicy: IfNotPresent volumeMounts: - name: config mountPath: "/usr/share/nginx/html" readOnly: true volumes: - name: config configMap: name: webapp-2bashroot@master30:~/web# kubectl apply -f pod-webapp-2.yaml -
创建haproxy配置文件。
bash# 获取pod ip地址 root@master30:~/web# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES webapp-1 1/1 Running 0 63m 10.224.84.80 worker1.laoma.fun <none> <none> webapp-2 1/1 Running 0 63m 10.224.149.25 worker2.laoma.fun <none> <none> # haproxy内容如下: root@master30:~/web# vim haproxy.cfginiglobal daemon maxconn 256 defaults mode http timeout connect 5000ms timeout client 50000ms timeout server 50000ms frontend http-in bind *:8080 default_backend servers backend servers server app1 10.224.84.80:80 check server app2 10.224.149.25:80 checkbash# 创建haproxy.cfg configmap root@master30:~/web# kubectl create cm haproxy.cfg --from-file=haproxy.cfg=./haproxy.cfg # 创建pod haproxy root@master30:~/web# vim pod-haproxy.yamlyaml--- apiVersion: v1 kind: Pod metadata: name: haproxy spec: containers: - name: haproxy image: docker.io/library/haproxy imagePullPolicy: IfNotPresent securityContext: allowPrivilegeEscalation: true volumeMounts: - name: config mountPath: "/usr/local/etc/haproxy" readOnly: true volumes: - name: config configMap: name: haproxy.cfgbashroot@master30:~/web# kubectl apply -f pod-haproxy.yaml # 验证 root@master30:~/web# kubectl get pods haproxy -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES haproxy 1/1 Running 0 33s 10.224.149.35 worker32.laoma.cloud <none> <none> [root@master30 ~]# curl http://10.224.149.35:8080 hello webapp-1 [root@master30 ~]# curl http://10.224.149.35:8080 hello webapp-2
Secret
学习参考: Secret
Secret 介绍
Secret 是一种包含少量敏感信息的对象,例如密码、令牌或密钥。 这样的信息可能会被放在 Pod 规约中或者镜像中。 使用 Secret 意味着你不需要在应用程序代码中包含机密数据。
创建 Secret 可以独立于使用它们的 Pod, 减少在创建、查看和编辑 Pod 的工作流程中暴露 Secret(及其数据)的风险。 Kubernetes 和在集群中运行的应用程序也可以对 Secret 采取额外的预防措施, 例如避免将敏感数据写入非易失性存储。
Secret 类似于 ConfigMap 但专门用于保存机密数据。
Secret 与ConfigMap的区别在于:Secret对数据编码,ConfigMap不对数据编码。
Secret 类型
-
generic:定义键值对,对变量值加密,类型为Opaque。
-
docker-registry:用于访问registry仓库的凭据,类型kubernetes.io/dockerconfigjson。
-
tls:保存TLS公钥和私钥,类型为Opaque。
Secret 创建
generic
bash
# 帮助信息
[root@master30 ~]# kubectl create secret generic --help
Create a secret based on a file, directory, or specified literal value.
A single secret may package one or more key/value pairs
......
基于键值对
一般用于传递变量值。
bash
[root@master30 ~]# kubectl create secret generic mysecret1 --from-literal=user=tom --from-literal=password1=redhat --from-literal=password2=redhat
[root@master30 ~]# kubectl get secrets
NAME TYPE DATA AGE
mysecret1 Opaque 3 21s
[root@master30 ~]# kubectl get secrets mysecret1 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
......
name: mysecret1
namespace: laoma
resourceVersion: "24180"
selfLink: /api/v1/namespaces/laoma/secrets/mysecret1
uid: d510d58f-8737-4d2f-bd00-dd9ce6b50a9b
type: Opaque
[root@master30 ~]# kubectl get secrets mysecret1 -o jsonpath={.data} | json_reformat
{
"password1": "cmVkaGF0",
"password2": "cmVkaGF0",
"user": "amFjaw=="
}
# json_reformat 工具由软件包 yajl-tools 提供
[root@master30 ~]# kubectl get secrets mysecret1 -o jsonpath={.data.user}
amFjaw==
[root@master30 ~]# kubectl describe secrets mysecret1
Name: mysecret1
Namespace: laoma
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password1: 6 bytes
password2: 6 bytes
user: 3 bytes
# secret 通过 base64 编码
[root@master30 ~]# echo -n tom | base64
dG9t
[root@master30 ~]# echo -n redhat|base64
cmVkaGF0
[root@master30 ~]# echo -n cmVkaGF0|base64 -d
redhat
基于普通文件
一般用于传递配置文件。
bash
[root@master30 ~]# echo -n tom > user
[root@master30 ~]# echo -n redhat > password1
[root@master30 ~]# echo -n redhat > password2
[root@master30 ~]# kubectl create secret generic mysecret2 --from-file=./user --from-file=./password1 --from-file=./password2
# 文件名用作key名,文件内容用作value,也可以自定义key名
[root@master30 ~]# kubectl create secret generic mysecret3 --from-file=username=./user --from-file=./password1 --from-file=./password2
[root@master30 ~]# kubectl get secrets mysecret2 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
......
name: mysecret2
namespace: laoma
resourceVersion: "25329"
selfLink: /api/v1/namespaces/laoma/secrets/mysecret2
uid: 047366b6-e8d2-4cf0-b68c-d1f5f9999458
type: Opaque
[root@master30 ~]# kubectl get secrets mysecret3 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
username: dG9t
kind: Secret
metadata:
......
name: mysecret3
namespace: laoma
resourceVersion: "25532"
selfLink: /api/v1/namespaces/laoma/secrets/mysecret3
uid: e19df9a7-7ce3-4539-b6f5-92f83417ac1f
type: Opaque
基于键值对内容的文件
bash
[root@master30 ~]# echo 'user=tom
password1=redhat
password2=redhat' > env.txt
[root@master30 ~]# kubectl create secret generic mysecret4 --from-env-file=./env.txt
secret/mysecret4 created
[root@master30 ~]# kubectl get secrets mysecret4 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
......
name: mysecret5
namespace: laoma
resourceVersion: "27240"
selfLink: /api/v1/namespaces/laoma/secrets/mysecret3
uid: 935c0392-8e54-492d-b093-99021e0dc5bb
type: Opaque
基于目录
文件名用作key名,文件内容用作value。
bash
[root@master30 ~]# kubectl create secret generic mysecret5 --from-file=./config
secret/my-secret5 created
[root@master30 ~]# kubectl get secrets mysecret5 -o yaml
apiVersion: v1
data:
password1: cmVkaGF0
password2: cmVkaGF0
user: dG9t
kind: Secret
metadata:
......
name: mysecret5
namespace: laoma
resourceVersion: "26687"
selfLink: /api/v1/namespaces/laoma/secrets/mysecret4
uid: 04eccd8b-8686-4856-82c3-0ac697d75746
type: Opaque
基于 yaml 文件
yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret6
type: Opaque
data:
user: dG9t
password1: cmVkaGF0
password2: cmVkaGF0
yaml格式中变量值使用转换后的值。
docker-registry
回顾docker login命令:
docker login DOCKER_REGISTRY_SERVER -u=DOCKER_USER -p=DOCKER_PASSWORD
docker 登录的信息默认保存在~/.dockercfg,后续docker pull和push使用该凭据文件访问registry。
bash
Usage:
kubectl create secret docker-registry NAME --docker-username=user --docker-password=password --docker-email=email [--docker-server=string] [--from-literal=key1=value1] [--dry-run=server|client|none] [options]
示例:
bash
[root@master30 ~]# kubectl create secret docker-registry registry --docker-username=laoma --docker-password=redhat --docker-email=laoma@laoma.cloud --docker-server=registry.laoma.cloud
secret/docker-registry-secret created
[root@master30 ~]# kubectl get secrets docker-registry-secret -o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5yZWRoYXQuZnVuIjp7InVzZXJuYW1lIjoibGFvbWEiLCJwYXNzd29yZCI6InJlZGhhdCIsImVtYWlsIjoibGFvbWFAcmVkaGF0LmZ1biIsImF1dGgiOiJiR0Z2YldFNmNtVmthR0YwIn19fQ==
kind: Secret
metadata:
......
name: docker-registry-secret
namespace: laoma
resourceVersion: "29629"
selfLink: /api/v1/namespaces/laoma/secrets/docker-registry-secret
uid: 017f736a-11f5-42d8-a9ed-47dad254f2c2
type: kubernetes.io/dockerconfigjson
TLS
创建 TLS secret 保存公钥/私钥对。 公钥证书必须是 .PEM 编码。
bash
Usage:
kubectl create secret tls NAME --cert=path/to/cert/file --key=path/to/key/file [--dry-run=server|client|none] [options]
创建私钥和证书
bash
#--1--生成私钥
[root@master30 ~]# mkdir certs && cd certs
[root@master30 certs]# openssl genrsa -out www.key 2048
#--2--生成请求文件csr
[root@master30 certs]# openssl req -new -key www.key -out www.csr -subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=www.laoma.cloud/emailAddress=webadmin@laoma.cloud"
# CN的值必须是网站域名
#--3--使用自己的私钥对请求文件签名,以生成证书
[root@master30 certs]# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt
创建 tls 类型 secret
bash
[root@master30 certs]# kubectl create secret tls www-tls --cert=./www.crt --key=./www.key
Secret 引用
Secrets可以以卷方式挂载给pods,也可以通过环境变量方式传递给pods。
以环境变量方式引用
示例:
bash
[root@master30 ~]# vim pod-secret-env.yaml
yaml
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
name: mysql
spec:
containers:
- image: hub.laoma.cloud/library/mysql:latest
imagePullPolicy: IfNotPresent
name: mysql
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
# 原先通过value设置环境变量值
valueFrom:
secretKeyRef:
name: mysecret1
key: password1
验证
bash
[root@master30 ~]# kubectl apply -f pod-secret-env.yaml
[root@master30 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql 1/1 Running 0 90s 10.224.51.131 worker31.laoma.cloud <none> <none>
[root@master30 ~]# kubectl exec -it mysql -- bash -c 'echo $MYSQL_ROOT_PASSWORD'
redhat
[root@master30 ~]# mysql -u root -predhat -h 10.224.51.131 --ssl-mode=DISABLED
# 清理pod
[root@master30 ~]# kubectl delete pod mysql --force
以 volume 方式引用
bash
[root@master30 ~]# echo Hello world > index.html
[root@master30 ~]# echo error > error.html
[root@master30 ~]# kubectl create secret generic web --from-file=./index.html --from-file=./error.html
引用整体
示例:
bash
[root@master30 ~]# vim pod-secret-volume-all.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
# mountPath值是一个挂载点,将secrete中所有键值对挂载过来
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
secret:
secretName: web
验证
bash
[root@master30 ~]# kubectl apply -f pod-secret-volume-all.yaml
[root@master30 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 15s 10.224.51.136 worker31.laoma.cloud <none> <none>
[root@master30 ~]# curl 10.224.51.136
Hello World
[root@master30 ~]# curl 10.224.51.136/error.html
error
# 清理pod
[root@master30 ~]# kubectl delete pod web --force
引用特定 key
示例:nginx.yaml
bash
[root@master30 ~]# vim pod-secret-volume-single.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
mountPath: "/usr/share/nginx/html"
volumes:
- name: webcontent
secret:
secretName: web
items:
- key: index.html
# 通过path设置挂载的文件名,这里我们path值与key一致。
path: index.html
或者
yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: web
name: web
spec:
containers:
- image: hub.laoma.cloud/library/nginx:latest
imagePullPolicy: IfNotPresent
name: web
volumeMounts:
- name: webcontent
# 将webcontent中index.html挂载到/usr/share/nginx/html/index.html
mountPath: "/usr/share/nginx/html/index.html"
# 使用subPath参数,则表明mountPath的值是一个文件
subPath: index.html
volumes:
- name: webcontent
secret:
secretName: web
验证
bash
[root@master30 ~]# kubectl create -f pod-secret-volume-single.yaml
[root@master30 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 21s 10.224.51.137 worker31.laoma.cloud <none> <none>
[root@master30 ~]# curl 10.224.51.137/index.html
Hello World
# /usr/share/nginx/html目录中没有index.html和error.html
[root@master30 ~]# kubectl exec web -- ls /usr/share/nginx/html
index.html
以卷方式引用,支持动态更新secret内容:
bash
[root@master30 ~]# echo 'Hello Nginx' |base64
SGVsbG8gbmdpbng=
# 更改data.index内容为SGVsbG8gbmdpbng=
[root@master30 ~]# kubectl edit secrets web
......
apiVersion: v1
data:
index: SGVsbG8gbmdpbng=
......
# 等30秒左右查看
[root@master30 ~]# curl 10.224.51.137/index.html
Hello Nginx
文件权限
映射过去文件默认权限是644(8进制),对应10进制是420.。
设置映射的所有文件权限,defaultMode。
yaml
volumes:
- name: foo
secret:
secretName: mysecret1
defaultMode: 256
10进制是256转换成8进制是400。因为json格式不支持8进制,所以使用10进制。
bash
[root@master30 ~]# kubectl exec mypod1 -- ls -l /etc/foo/..data/user
-r-------- 1 root root 4 Aug 9 08:58 /etc/foo/..data/user
设置映射的单独文件权限
yaml
volumes:
- name: foo
secret:
secretName: mysecret1
items:
- key: user
path: user
mode: 511
10进制是511转换成8进制是777。
bash
[root@master30 ~]# kubectl exec mypod1 -- ls -l /etc/foo/..data/user
-rwxrwxrwx 1 root root 4 Aug 9 08:58 /etc/foo/..data/user
综合实验
使用mysql和wordpress镜像部署blog应用。要求如下:
- mysql密码和wordpress站点证书用secret存储。
- 为blog站点配置vhost,配置文件使用configmap提供。
环境清理
bash
[root@master30 ~]# kubectl delete ns configure