持久存储卷
K8S提供了三种基于存储的抽象对象,分别是PersistentVloume(PV),StorageClass,PersistentVloumeClaim(PVC),通过这三种类型来支持基础设施和应用之间的分离。
PV:持久存储卷,定义了K8S集群中的可用的存储资源,其中包含了存储资源的实现细节,比如包含NFS等资源的具体设置。
PVC:表示持久存储卷的申请,是通过用户发起对存储资源的请求。申请中只包含请求资源的大小和读写访问模式,不需要关心具体的资源实现细节,K8S会自动为其绑定符合条件的PV。
1.创建PV
[root@master ~]# vi testnfspv.yaml
apiVersion: v1 #创建的资源对象的版本
kind: PersistentVolume #所要创建的资源对象,在这里是创建的PV
metadata:
name: testnfspv
spec: #具体设置
capacity: #PV的容量
storage: 1Gi #指定占用的具体存储资源(NFS)的大小
accessModes: #定义PV对具体存储资源的访问模式。它下设的属性一共包含三种类型访问模式,分别为
ReadWriteOnce:这个卷可以被单个节点以读写模式挂载;ReadWriteMany:这个卷可以被多个节点以读写模式挂载;ReadOnlyMany:这个卷可以被多个节点以只读的模式挂载
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle #删除PVC时对于PV资源的回收策略,它下设了三种类型的策略,分别
为:Retain(保留);Recycle(自动回收);Delete(自动删除)
storageClassName: testnfs #PV资源的描述性分类,在后续创建PVC时可以引用这个名称来绑定PV
nfs: #表示PV使用NFS服务器作为具体的存储资源
path: /data/k8snfs/
server: 192.168.200.11
[root@master ~]# kubectl apply -f testnfspv.yaml
persistentvolume/testnfspv created
[root@master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
testnfspv 1Gi RWX Recycle Available(资源空闲) testnfs 36s
[root@master ~]# kubectl describe pv testnfspv
Name: testnfspv
Labels: <none>
Annotations: <none>
Finalizers: [kubernetes.io/pv-protection]
StorageClass: testnfs
Status: Available
Claim:
Reclaim Policy: Recycle
Access Modes: RWX
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
Source:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.200.11
Path: /data/k8snfs/
ReadOnly: false
Events: <none>
2.创建PVC
[root@master ~]# vi testnfspvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: testnfspvc
spec:
accessModes:
- ReadWriteMany
storageClassName: "testnfs" #表示要引用的PV的资源名称
resources: #定义PVC资源的参数
requests: #设置具体的资源需求
storage: 500Mi #表示申请500MiB的资源
[root@master ~]# kubectl apply -f testnfspvc.yaml
persistentvolumeclaim/testnfspvc created
[root@master ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
testnfspvc Bound testnfspv 1Gi RWX testnfs 26s
//Bound代表已经绑定到了符合条件的PV上
[root@master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
testnfspv 1Gi RWX Recycle Bound default/testnfspvc testnfs 12m
[root@master ~]# kubectl describe pvc testnfspvc
Name: testnfspvc
Namespace: default
StorageClass: testnfs
Status: Bound
Volume: testnfspv
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Gi
Access Modes: RWX
VolumeMode: Filesystem
Used By: <none>
Events: <none>
3.定义Pod并使用PVC引用的资源
[root@master ~]# cat testnfspvcdeployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: testnfspvcdeployment
spec:
replicas: 2
selector:
matchLabels:
example: testnfspvc
template:
metadata:
labels:
example: testnfspvc
spec:
containers:
- name: testnfspvc
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "The host is $(hostname)" >> /dir/dataforpvc; sleep 3600']
volumeMounts:
- name: pvcdata
mountPath: /dir
volumes:
- name: pvcdata
persistentVolumeClaim:
claimName: testnfspvc
[root@master ~]# kubectl apply -f testnfspvcdeployment.yaml
deployment.apps/testnfspvcdeployment created
//本案例中所创建存储卷名称为pvcdata,这个名称会被容器中设置的volumeMounts所引用。存储卷的类型时PVC,claimName属性表示引用的PVC名称,这里为 testnfspvc
[root@master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
testnfspvcdeployment 2/2 2 2 21s
[root@master ~]# kubectl get pod -o wide |grep testnfspvcdeployment
testnfspvcdeployment-58756f77c5-gpg72 1/1 Running 0 74s 10.244.0.187 master <none> <none>
testnfspvcdeployment-58756f77c5-r5vsw 1/1 Running 0 74s 10.244.0.186 master <none> <none>
//在上面的案例中,我们观察到pod已经被分布在了master节点上,pvc所绑定的pv引用中nfs服务器的共享目录为 /data/k8snfs/,这时我们可以执行以下命令查看共享目录。
[root@master k8snfs]# cat dataforpvc
The host is testnfspvcdeployment-58756f77c5-r5vsw
The host is testnfspvcdeployment-58756f77c5-gpg72
//从以上内容可知,目前在nfs服务器端已经同步了pod主机名信息,我们可以通过pod中的命令行执行情况来做一个对比,随机抽取一个pod进入后查看。
[root@master ~]# kubectl exec -it testnfspvcdeployment-58756f77c5-gpg72 -- /bin/sh
/ # cat /dir/data
data dataforpvc
/ # cat /dir/dataforpvc
The host is testnfspvcdeployment-58756f77c5-r5vsw
The host is testnfspvcdeployment-58756f77c5-gpg72
//从上面内容可见两条输出信息之技安时相同的,证明我们的pvc创建无误
PV的解绑与回收
在创建pvc时我们已经将资源绑定在了唯一的pv上,如果此时我们在创建一个pvc这时候会发生什么问题?
[root@master ~]# vi testnfspvc2.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: testnfspvc2
spec:
accessModes:
- ReadWriteMany
storageClassName: "testnfs" #表示要引用的PV的资源名称
resources: #定义PVC资源的参数
requests: #设置具体的资源需求
storage: 500Mi #表示申请500MiB的资源
[root@master ~]# kubectl apply -f testnfspvc2.yaml
persistentvolumeclaim/testnfspvc2 created
[root@master ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
testnfspvc Bound testnfspv 1Gi RWX testnfs 3d21h
testnfspvc2 Pending testnfs 16s
//由上述结果可知,新创建的pvc2状态已经时pending,这表示了我们新创建的pvc一致处于挂起的状态,虽然我们之前定义的pv大小为1GiB,后面定义的两个pvc都之申请了500MiB的资源,但是对于PV和PVC来说只能一对一的绑定,不能一对多,所以新的pvc无法找到合适的资源,如果向使用只能在重新创建一个pv或者将之前的pvc解绑。
[root@master ~]# kubectl delete -f testnfspvcdeployment.yaml
deployment.apps "testnfspvcdeployment" deleted
[root@master ~]# kubectl delete pvc testnfspvc --force
warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
persistentvolumeclaim "testnfspvc" force deleted
//在执行删除时,尽可能先删除pod,然后删除pvc,最后删除pv,因为我们之前定义过回收策略,所以在删除时,自动回收资源,清理PVC在PV上写入的内容,此时查看宿主机下的文件,会发现已经被删除了。
[root@master ~]# ll /data/k8snfs/
total 0
//如果自动回收失败,则PVC的STATUS属性将变为Failed,,此时PV暂时无法使用。如果之前设置的PV的回收策略时Retain(保留),那么资源将不会被回收,宿主机的文件依然存在,但是PV的属性变为了Released,并且依然不能重新绑定到其他的PVC。
Storage Class(存储类)
针对于前面的PV和PVC的创建是静态的创建,我们需要指定规定的PV大小,而且PV的创建又是手动创建整个过程会很麻烦。
Kubernetes又提供了一种Storage Class的抽象,来动态的创建PV,Storage Class简化了PV的创建过程,当申请PVC资源的时候,如果匹配到满足条件的Storage Class,就会自动的为PVC创建对应大小的PV并进行绑定
Storage Class是通过存储分配器来动态分配PV,需要注意的是Kubernetes官方内置的存储分配器并不支持NFS,所以我们需要先安装NFS存储分配器。
1.安装NFS存储器
[root@master ~]# git clone https://github.com/kubernetes-incubator/external-storage.git
//利用这种方式需要先进入到external-storage目录下找到nfs-client/deploy目录,然后创建rbac.yaml文件
或
wget https://github.com/kubernetes-retired/external-storage/blob/master/nfs-client/deploy/rbac.yaml
[root@master deploy]# kubectl create -f rbac.yaml
[root@master ~]# wget https://raw.githubusercontent.com/Kubernetes-incubator/external-storage/master/nfs-client/deploy/deployment.yaml
[root@master ~]# vi deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 192.168.200.11
- name: NFS_PATH
value: /data/k8snfs
volumes:
- name: nfs-client-root
nfs:
server: 192.168.200.11
path: /data/k8snfs
//在env的属性当中PROVISIONER_NAME参数代表了存储器的名称,NFS_SERVER参数代表了之前配置的NFS服务端地址, NFS_PATH代表了NFS共享目录的地址,在volume的属性中,也需要将 server与path改为之前的NFS服务器的地址和共享目录。
[root@master ~]# kubectl apply -f deployment.yaml
deployment.apps/nfs-client-provisioner created
[root@master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nfs-client-provisioner 1/1 1 1 5m25s
[root@master ~]# kubectl get pod |grep nfs
nfs-client-provisioner-849c76dd46-drh2d 1/1 Running 0 5m45s
//从以上结果可知我们的pod和控制器都是正常运行的状态,如果在集群中已经启用了RBAC,则必须要为NFS存储器去授权,可以按照以下步骤进行操作。
[root@master ~]# wget https://github.com/kubernetes-retired/external-storage/blob/master/nfs-client/deploy/class.yaml
[root@master deploy]# vi class.yaml
apiVersion: storage.k8s.io/v1 #API的版本,此处使用的是API的稳定版
kind: StorageClass #资源对象
metadata: #元数据
name: managed-nfs-storage
provisioner: fuseim.pri/ifs #存储器的名称,这边采用的是Deployment控制器中创建的- name: PROVISIONER_NAME参数中指定的
parameters: #资源对象的参数,如果archiveOnDelete为false时,与其关联的PVC在删除时,它所绑定的PV不会被存储分配器保留;如果为true则相反
archiveOnDelete: "false"
[root@master deploy]# kubectl apply -f class.yaml
storageclass.storage.k8s.io/managed-nfs-storage created
[root@master deploy]# kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage fuseim.pri/ifs Delete Immediate false 26s
[root@master deploy]# kubectl describe storageclass managed-nfs-storage
Name: managed-nfs-storage
IsDefaultClass: No
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"managed-nfs-storage"},"parameters":{"archiveOnDelete":"false"},"provisioner":"fuseim.pri/ifs"}
Provisioner: fuseim.pri/ifs
Parameters: archiveOnDelete=false
AllowVolumeExpansion: <unset>
MountOptions: <none>
ReclaimPolicy: Delete
VolumeBindingMode: Immediate
Events: <none>
2.创建PVC
在K8S集群中默认仅用了SelfLink项,所以我们首先要修改api-server的配置,按照下面的方法进行修改
[root@master ~]# cd /etc/kubernetes/manifests/
[root@master manifests]# vi kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.200.11:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
......添加下面红色处
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
- --feature-gates=TTLAfterFinished=true
- --feature-gates=RemoveSelfLink=false
[root@master manifests]# kubectl apply -f kube-apiserver.yaml
//等待api-pod运行正常,在创建pvc
[root@master ~]# vi teststoragepvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: teststorageclass
spec:
accessModes:
- ReadWriteMany
storageClassName: "managed-nfs-storage"
resources:
requests:
storage: 500Mi
[root@master ~]# kubectl apply -f teststoragepvc.yaml
persistentvolumeclaim/teststorageclass created
[root@master ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
teststorageclass Bound pvc-f5ca4c79-b080-4f83-a800-653d0a7ab891 500Mi RWX managed-nfs-storage 6s
[root@master ~]# ^C
[root@master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-f5ca4c79-b080-4f83-a800-653d0a7ab891 500Mi RWX Delete Bound default/teststorageclass managed-nfs-storage 29s
[root@master ~]# kubectl describe pv
Name: pvc-f5ca4c79-b080-4f83-a800-653d0a7ab891
Labels: <none>
Annotations: pv.kubernetes.io/provisioned-by: fuseim.pri/ifs
Finalizers: [kubernetes.io/pv-protection]
StorageClass: managed-nfs-storage
Status: Bound
Claim: default/teststorageclass
Reclaim Policy: Delete
Access Modes: RWX
VolumeMode: Filesystem
Capacity: 500Mi
Node Affinity: <none>
Message:
Source:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.200.11
Path: /data/k8snfs/default-teststorageclass-pvc-f5ca4c79-b080-4f83-a800-653d0a7ab891
ReadOnly: false
Events: <none>
//从标红的位置我们可以看到动态的PV在NFS服务器的目录中创建了一个他的专属子目录
[root@master ~]# vi teststoragedeployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: teststoragedeployment
spec:
replicas: 2
selector:
matchLabels:
example: teststoragedeployment
template:
metadata:
labels:
example: teststoragedeployment
spec:
containers:
- name: teststoragedeployment
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "The host is $(hostname)" >> /dir/dataforpvc; sleep 3600']
volumeMounts:
- name: pvcdata
mountPath: /dir
volumes:
- name: pvcdata
persistentVolumeClaim:
claimName: teststorageclass
[root@master ~]# kubectl apply -f teststoragedeployment.yaml
deployment.apps/teststoragedeployment created
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
teststoragedeployment-b7db57c78-65zzc 1/1 Running 0 15s
teststoragedeployment-b7db57c78-cqkhv 1/1 Running 0 15s
[root@master ~]# cat /data/k8snfs/d
data
default-teststorageclass-pvc-f5ca4c79-b080-4f83-a800-653d0a7ab891/
[root@master ~]# cat /data/k8snfs/default-teststorageclass-pvc-f5ca4c79-b080-4f83-a800-653d0a7ab891/dataforpvc
The host is teststoragedeployment-b7db57c78-65zzc
The host is teststoragedeployment-b7db57c78-cqkhv
注意:在删除时也要遵循先删控制器/pod,然后删pvc,在这里我们删除完pvc后会直接将pv动态进行删除,以及PV的专属子目录在主机上也会删除
StatefulSet控制器
提供排序和唯一性保证的特殊Pod控制器,当有部署顺序、持久化数据以及固定网络等相关特殊需求时,可以使用该控制器进行细粒度的控制。
该控制器对于有状态服务(Deployment对应于无状态服务),其功能如下:
- 实现稳定的持久化存储:Pod重新调度之后还能访问相同的持久化数据,可基于PVC来实现
- 实现稳定的网络标识:Pod重新调度之后其PodName和HostName保持不变,基于无头Service(没有ClusterIPde Service实现)
- 实现有序部署、有序伸缩:Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次执行(也就是说从第一个到最后一个一次部署,在下一个Pod运行之前所有的Pod都必须处于running或ready状态)
- 实现有序收缩、有序删除:从最后一个开始,一次删除至第一个
- 无头Service:用于为Pod资源标识符生成可以解析的DNS记录
- VolumeClaimTempalte:基于静态或动态的PV供给方式为Pod资源提供专属的固定存储
- StatefulSet:管理Pod资源
StatefulSet控制器下的Pod,虽然各个Pod定义是一样的,但是因为数据的不同,所提供的服务是有差异的,分布式存储系统就非常适合StatefulSet控制器,由PodA存储一部分数据并提供相关服务,由Pod B存储另外一部分的数据提供相关服务。在这些场景中每一个有状态的Pod提供的服务是不一样的,所以每一个Pod是不能被取代的,必须是有序的分配而且必须为其分配唯一的标识。Pod名称就是它们唯一的标识符,和Deployment控制器下的Pod不同,即使有状态的Pod发生故障并被重建,Pod名称也会和原来的一模一样。因为各个有状态的Pod也必须要拥有一个唯一的网络标识符一访问具体的某个Pod,,所以采用无头Service,它会给每一个Pod分配唯一的一个DNS名称。
有状态的Pod都会使用到持久存储,众所周知,有状态的Pod最大的特点是每一个Pod中的数据是不一样的,所以各个Pod没有办法使用同一个存储卷。因为每个Pod要拥有自己的专用存储卷,所以这个存储卷的定义并不是在Pod模板中定义的(如果实在Pod模板中定义,那么每个Pod都会用到同一个存储卷),StatefuSet控制器的存储卷配置,而是在StatefuSet控制器模板的VolumeClaimTemplate属性中定义存储卷的申请模板,为每一个Pod生成不同的PVC并且绑定各自的PV,从而使各个Pod拥有各自专用的存储卷。
因为每个Pod都会产生各自专用的PVC以及PV,所以StatefuSet控制器的存储最好通过StorageClass来进行动态创建。Sure,也可通过手动创建各个预设的PV,But会很麻烦。
1.创建控制器&&service
[root@master ~]# cat teststatefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: teststatefulsetservice
spec:
selector:
example: teststateful
clusterIP: None
ports:
- protocol: TCP
port: 8080
targetPort: 80
type: ClusterIP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: teststatefulset
spec:
replicas: 3
serviceName: "teststatefulsetservice"
selector:
matchLabels:
example: teststateful
template:
metadata:
labels:
example: teststateful
spec:
containers:
- name: pythonservice
image: python:3.7
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "The host is $(hostname)" >> /dir/data; echo "<p>The host is $(hostname)</p>" >index.html; python -m http.server 80']
volumeMounts:
- name: statefuldata
mountPath: /dir
ports:
- name: http
containerPort: 80
volumeClaimTemplates:
- metadata:
name: statefuldata
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "managed-nfs-storage"
resources:
requests:
storage: 200Mi
//此模板分为三个部分的内容,先创建了一个无头Service,名称为“teststatefulsetservice”,它会通过标签选择器关联到各个标签为teststateful的pod上。
然后创建了一个statefulset的模板,定义了三个Pod副本,容器为“Python3.7”的镜像,目的是为了构建服务。在启动容器时会以追加的方式向/data/dir文件写入The host is $(hostname),获取当前Pod名称,/dir目录通过volumeMounts的属性映射到statefuldata的存储卷,在写入文本时会直接写入存储卷。
接下来,执行echo "<p>The host is $(hostname)</p>语句将代码插入到html文件,这样在访问时就知道访问的是哪个Pod,另外通过 python -m http.server 80构建一个简单的Web服务,并且声明服务的端口为80。
后半部分为存储卷的申请模板,内容基本和PVC类似,但需要注意我们这里批量定义了PVC,然后storageclass属性为上一节创建的,requests为storage:200Mi,表示为每一个Pod搜申请200Mi的存储空间。
[root@master ~]# kubectl apply -f teststatefulset.yaml
service/teststatefulsetservice unchanged
statefulset.apps/teststatefulset created
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
teststatefulset-0 1/1 Running 0 8s
teststatefulset-1 1/1 Running 0 6s
teststatefulset-2 1/1 Running 0 5s
//从上述结果可知,在创建的过程中,并且在不同时段去查看pod创建状态,会发现Pod创建顺序是依次创建的,Pod名称和Deployment控制器不一样,名称末尾并没有随机生成字符串,二十按照数字从0依次叠加。
[root@master ~]# kubectl get statefulset
NAME READY AGE
teststatefulset 3/3 6m7s
[root@master ~]# kubectl describe statefulset teststatefulset
Name: teststatefulset
Namespace: default
CreationTimestamp: Mon, 29 May 2023 23:00:15 -0400
Selector: example=teststateful
Labels: <none>
Annotations: <none>
Replicas: 3 desired | 3 total
Update Strategy: RollingUpdate
Partition: 0
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: example=teststateful
Containers:
pythonservice:
Image: python:3.7
Port: 80/TCP
Host Port: 0/TCP
Command:
sh
-c
Args:
echo "The host is $(hostname)" >> /dir/data; echo "<p>The host is $(hostname)</p>" >index.html; python -m http.server 80
Environment: <none>
Mounts:
/dir from statefuldata (rw)
Volumes: <none>
Volume Claims:
Name: statefuldata
StorageClass: managed-nfs-storage
Labels: <none>
Annotations: <none>
Capacity: 200Mi
Access Modes: [ReadWriteOnce]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 6m33s statefulset-controller create Pod teststatefulset-0 in StatefulSet teststatefulset successful
Normal SuccessfulCreate 6m31s statefulset-controller create Pod teststatefulset-1 in StatefulSet teststatefulset successful
Normal SuccessfulCreate 6m30s statefulset-controller create Pod teststatefulset-2 in StatefulSet teststatefulset successful
2.PV以及PVC的使用
[root@master ~]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-bbf264cc-be7d-4b53-9a7e-ee5c3bb19bb5 200Mi RWO Delete Bound default/statefuldata-teststatefulset-1 managed-nfs-storage 19m
persistentvolume/pvc-c94269db-3267-4fc3-8727-b8b51afed71a 200Mi RWO Delete Bound default/statefuldata-teststatefulset-2 managed-nfs-storage 19m
persistentvolume/pvc-fb168ccf-f6e8-43d0-9be7-873396518907 200Mi RWO Delete Bound default/statefuldata-teststatefulset-0 managed-nfs-storage 19m
persistentvolume/testnfspv 1Gi RWX Recycle Available testnfs 4d18h
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/statefuldata-teststatefulset-0 Bound pvc-fb168ccf-f6e8-43d0-9be7-873396518907 200Mi RWO managed-nfs-storage 19m
persistentvolumeclaim/statefuldata-teststatefulset-1 Bound pvc-bbf264cc-be7d-4b53-9a7e-ee5c3bb19bb5 200Mi RWO managed-nfs-storage 19m
persistentvolumeclaim/statefuldata-teststatefulset-2 Bound pvc-c94269db-3267-4fc3-8727-b8b51afed71a 200Mi RWO managed-nfs-storage 19m
//接下来随意挑选出一个PV,查询状态
[root@master ~]# kubectl describe pv pvc-fb168ccf-f6e8-43d0-9be7-873396518907
Name: pvc-fb168ccf-f6e8-43d0-9be7-873396518907
Labels: <none>
Annotations: pv.kubernetes.io/provisioned-by: fuseim.pri/ifs
Finalizers: [kubernetes.io/pv-protection]
StorageClass: managed-nfs-storage
Status: Bound
Claim: default/statefuldata-teststatefulset-0
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 200Mi
Node Affinity: <none>
Message:
Source:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.200.11
Path: /data/k8snfs/default-statefuldata-teststatefulset-0-pvc-fb168ccf-f6e8-43d0-9be7-873396518907
ReadOnly: false
Events: <none>
[root@master ~]# cat /data/k8snfs/default-statefuldata-teststatefulset-0-pvc-fb168ccf-f6e8-43d0-9be7-873396518907/data
The host is teststatefulset-0
The host is teststatefulset-0
3.无头Service的访问
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
teststatefulsetservice ClusterIP None <none> 8080/TCP 156m
//由于这个Service无法通过集群内外的机器直接访问,因此只有Pod才可以访问,而且需要DNS的形式来进行访问,其具体的形式为{ServiceName}.{Namespace}.svc.{ClusterDomain}
[root@master ~]# vi testpodheadlessservice.yaml
apiVersion: v1
kind: Pod
metadata:
name: testpodheadlessservice
spec:
containers:
- name: testcontainer
image: docker.io/appropriate/curl
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "The test pod for headless service!"; sleep 3600']
[root@master ~]# kubectl apply -f testpodheadlessservice.yaml
pod/testpodheadlessservice created
[root@master ~]# kubectl exec -it testpodheadlessservice -- /bin/sh
/ # nslookup teststatefulsetservice.default.svc.cluster.local
nslookup: can't resolve '(null)': Name does not resolve
Name: teststatefulsetservice.default.svc.cluster.local
Address 1: 10.244.1.108 teststatefulset-0.teststatefulsetservice.default.svc.cluster.local
Address 2: 10.244.1.110 teststatefulset-2.teststatefulsetservice.default.svc.cluster.local
Address 3: 10.244.1.109 teststatefulset-1.teststatefulsetservice.default.svc.cluster.local
//总共返回了三个地址,然后K8S又为每一个Pod地址创建了对应的专属域名,访问这些域名就可以访问指定的Pod,当然也可通过无头Service的总域名来访问服务
/ # curl teststatefulsetservice.default.svc.cluster.local
<p>The host is teststatefulset-0</p>
/ # curl teststatefulsetservice.default.svc.cluster.local
<p>The host is teststatefulset-0</p>
/ # curl teststatefulsetservice.default.svc.cluster.local
<p>The host is teststatefulset-1</p>
/ # curl teststatefulsetservice.default.svc.cluster.local
<p>The host is teststatefulset-2</p>
/ # curl teststatefulsetservice.default.svc.cluster.local
//以上这种访问方式对于Deployment控制器是没有问题的,但是对于StatefulSet控制器是不行的,因为每个Pod提供的服务是不一样的,所以在调用时需要指定哪一个Pod提供服务。
在无头Service中,每一个Pod会生成一个专属的域名,其访问格式为{PodName}.{ServiceName}.{Namespace}.svc.{ClusterDomain},每个域名通过DNS查询都可以解析出Pod的IP地址,如下:
Name: teststatefulset-0.teststatefulsetservice.default.svc.cluster.local
Address 1: 10.244.1.108 teststatefulset-0.teststatefulsetservice.default.svc.cluster.local
//要访问有不同有状态Pod提供的服务,只需要访问专属域名,如下:
/ # curl teststatefulset-0.teststatefulsetservice.default.svc.cluster.local
<p>The host is teststatefulset-0</p>
/ # curl teststatefulset-1.teststatefulsetservice.default.svc.cluster.local
<p>The host is teststatefulset-1</p>
/ # curl teststatefulset-2.teststatefulsetservice.default.svc.cluster.local
<p>The host is teststatefulset-2</p>
4.pod重建
接下来模拟Pod重建的场景
[root@master ~]# kubectl delete pod teststatefulset-1
pod "teststatefulset-1" deleted
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
teststatefulset-0 1/1 Running 0 178m
teststatefulset-1 1/1 Terminating 0 178m
teststatefulset-2 1/1 Running 0 178m
//因为在之前的副本数量中定义Pod为3,表示会保留三个稳定的副本,所以Pod会重建,下面的内容我们可以看到Pod重建之后的状态和名称是一模一样的,IP地址会有变化,但不会影响服务。
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
teststatefulset-0 1/1 Running 0 3h2m 10.244.1.108 worker2 <none> <none>
teststatefulset-1 1/1 Running 0 3m11s 10.244.1.111 worker2 <none> <none>
teststatefulset-2 1/1 Running 0 3h2m 10.244.1.110 worker2 <none> <none>
//执行下述命令输出Pod专属的存储卷中文件的内容,查看是否仍然调用同一个存储
[root@master ~]# cat /data/k8snfs/default-statefuldata-teststatefulset-1-pvc-bbf264cc-be7d-4b53-9a7e-ee5c3bb19bb5/data
The host is teststatefulset-1
The host is teststatefulset-1
The host is teststatefulset-1
//因为在之前的Pod定义这个Pod启动时会一追加的方式向文件中写入数据,所以Pod重建之后会在写一条数据。因为重建后的P;od使用的还是同一个PVChePV,所以仍然在同一个文件上进行编辑。查询出来会有三条文本内容,其中两条是第一次创建时写入的,另外一条时重建时写入的。
5.控制器的伸缩与更新
和Depolyment控制器一样,它也可以实现动态伸缩,我们只需要更改配置中的replicas属性然后执行应用即可。但是与Depolyment控制器不一样的地方在于Pod更新也是有序的,跟创建一样。在扩容时,后续新增的Pod会从前往后依次创建,创建完成后才开始进行下一个Pod创建,在缩容的时候,在缩容时会先从编号大的Pod开始从后往前依次删除,完全删除完成后才开始进行下一个Pod的删除。
该控制器也有两种更新策略,可以模板中通过spec.updateStrategy属性进行设置。
第一种是OnDelete更新策略,这是默认向后兼容的更新策略,使用这种更新策略更新StatefulSet模板后,只有在手动删除旧的Pod之后才会新建新的Pod。
第二种更新策略RollingUpdate,在更新该控制器的模板后,旧的Pod将被终止哦,并且以受控的方式自动新建Pod,但是和Deployment控制之间有一些席位的差异,如下:
- StatefulSet控制器是有序的,更新的顺序会从编号最大的Pod到最小的Pod依次进行更新,在更新之前不会立即删除旧的Pod,会等到新的Pod创建完毕并且处于Running状态,才会替换并删除旧的Pod
- 该控制器拥有独有的更新属性,spec.updateStrategy.rollingUpdate.partition。这种方式类似于金丝雀部署,如果将.partition设置为4,只有编号大于或者等于4的Pod才会更新,编号小于.partition的Pod将不会更新,如果已经更新的Pod通过验证,则在将.partition的值改为0,更新其余的Pod即可。
配置存储卷
K8S还包含一些存储卷,他们之间不是用来进行容器间的交互或者Pod之间的数据共享,而是用于向各个Pod的容器中注入配置信息的,在使用方式上大同小异,Pod可以通过环境变量或者存储卷访问这些配置信息。
目前的这类的存储卷主要分为三种:
- ConfigMap:传递普通的配置信息
- Secret:传递敏感的、加密的配置信息
- DownwardAPI:可以传递Pod和容器自身的运行信息
一.ConfigMap
在企业的运营中,一般会有多个部署环境,如开发环境、测试环境、预发布环境、生产环境等等,每一种环境的配置不同。如果在Pod模板信息中直接配置,会比较难管理,而且每个环境都需要准备不同的模板。
利用ConfigMap可以解耦部署与配置之间的关系,只需要在各个环境中的机器上预先完成不同的配置即可,也就是配置ConfigMap。对于同一个应用部署,Pod模板无需变化,只需要将明文编写的配置设置为对ConfigMap的引用,就可以降低环境管理和管理的复杂度。
ConfigMap是以键值对的方式来存储配置信息的。
[root@master ~]# vi testconfigmap1.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: testconfigmap
data:
exampleHostName: www.testk8s.com
exampleBusinessMode: testMode
[root@master ~]# kubectl apply -f testconfigmap1.yaml
configmap/testconfigmap created
[root@master ~]# kubectl get configmap
NAME DATA AGE
testconfigmap 2 30s
[root@master ~]# kubectl describe configmap testconfigmap
]Name: testconfigmap
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
exampleBusinessMode:
----
testMode
exampleHostName:
----
www.testk8s.com
BinaryData
====
Events: <none>
//以上内容是ConfigMap的详细信息,可以查看到我们的键值对信息,后续在Pod中即可引用,下面是ConfigMap的两种引用方式。
1.环境变量的引用方式
ConfigMap可以通过设置环境变量或者命令行参数的形式进行引用,首先定义模板文件:
[root@master ~]# vi testconfigmap2.yaml
apiVersion: v1
kind: Pod
metadata:
name: testconfigmap2
spec:
containers:
- name: containerenv
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "EnvParaHostName: ${EnvParaHostName} EnvParaBusinessMode: ${EnvParaBusinessMode}" prientenv grep EnvPara ; sleep 3600']
env:
- name: EnvParaHostName
valueFrom:
configMapKeyRef:
name: testconfigmap
key: exampleHostName
- name: EnvParaBusinessMode
valueFrom:
configMapKeyRef:
name: testconfigmap
key: exampleBusinessMode
//上述的模板通过valueFrom、configMapKeyRef、name、key等属性,我们可以指定具体要引用的那些环境变量。重点是在env的属性中,我们先定义了环境变量的名称分别为EnvParaHostName、EnvParaBusinessMode。和之前的环境变量不同的地方在于,这里是通过valueFrom属性来定义,表示环境变量的值来自外部引用,关键字是onfigMapKeyRef表示从ConfigMap中引用;configMapKeyRef.name属性表示要引用的ConfigMap的名称,而configMapKeyRef.key表示要引用的键值对的键名,它的值会映射到环境变量上。
在容器的参数中,首先会通过命令行直接输出定义的参数,然后通过$ print env |grep EnvPara输出Pod中包含的关键字。
[root@master ~]# kubectl apply -f testconfigmap2.yaml
pod/testconfigmap2 created
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
testconfigmap2 1/1 Running 0 8s
//pod运行正常,接下来我们需要查看pod输出信息
[root@master ~]# kubectl logs testconfigmap2
EnvParaHostName: www.testk8s.com EnvParaBusinessMode: testMode prientenv grep EnvPara
//从上述信息中可以看到我们的环境变量已经被成功引用,但是在有些时候ConfigMap的键值会非常多,如果一个个的配置到Pod中会非常麻烦,所以K8S还提供了一种简易方式,将ConfigMap中的键值对直接配置到Pod中。
[root@master ~]# cat testconfigmap3.yaml
apiVersion: v1
kind: Pod
metadata:
name: testconfigmap3
spec:
containers:
- name: containerenv3
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['printenv |grep test ; sleep 3600']
envFrom:
- configMapRef:
name: testconfigmap
//我们在这个模板中,是直接使用envFrom属性,表示整个环境都是从外部环境引用,引用方式是configMapRef。
[root@master ~]# kubectl apply -f testconfigmap3.yaml
pod/testconfigmap3 created
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
testconfigmap3 1/1 Running 0 18s
[root@master ~]# kubectl logs testconfigmap3
HOSTNAME=testconfigmap3
exampleHostName=www.testk8s.com
exampleBusinessMode=testMode
//从上述内容可以查看,对于testconfigmap中的内容全部引用完毕。
2.存储卷引用方式
因为ConfigMap本身就是一种特殊的存储卷,so 也可以利用存储卷的方式配置到Pod中,不同于环境变量的方式,这种引用方式会将每个键值对都转换成对应的实体文件。
[root@master ~]# vi testconfigmap4.yaml
apiVersion: v1
kind: Pod
metadata:
name: testconfigmap4
spec:
containers:
- name: containervolume
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "files:"; ls /config/allvalues; sleep 3600']
volumeMounts:
- name: volumeconfig
mountPath: /config/allvalues
volumes:
- name: volumeconfig
configMap:
name: testconfigmap
//本文件创建的存储卷名称为volumeconfig,这个名称会被容器设置的存储卷引用。存储卷的类型是configmap,name的属性是testconfigmap。创建的容器名为containervolume会引用volumeconfig存储卷,并将其映射到容器的/config/allvalues目录下,然后通过ls命令输出/config/allvalues下的所有文件
[root@master ~]# kubectl apply -f testconfigmap4.yaml
pod/testconfigmap4 created
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
testconfigmap2 1/1 Running 0 25m
testconfigmap3 1/1 Running 0 12m
testconfigmap4 1/1 Running 0 13s
[root@master ~]# kubectl logs testconfigmap4
files:
exampleBusinessMode
exampleHostName
//从以上内容中可以看出,Pod成功引用了ConfigMap中的键值,并将其对应为实体文件,接下来进入pod中查看,如下:
[root@master ~]# kubectl exec -it testconfigmap4 -- /bin/sh
/ # cat /config/allvalues/exampleHostName
www.testk8s.com
/ # cat /config/allvalues/exampleBusinessMode
testMode
Secret
Secret对应的三种类型:
(1)OpaqueSecret:使用base64的格式进行比那吗,用来存储密码
(2)ImagePullSecret:用来存储私有的Dcoker Registry的认证信息
(3)ServiceAccountSecret:主要用来访问K8S API。会被Service Account进行引用。在ServiceAccount创建的时候,K8S会默认创建对应的Secret。如果Pod使用了ServiceAccount,则对应的Secret会自动挂载到pod的/run/secrets/Kubernetes.io/serviceaccount目录下
一.OpaqueSecret
此类型与ConfigMap的定义方式和使用方式类似,都是使用的键值对形式,但是区别在于,这种类型中的各个键对应的值必须要经过base64格式进行编码才能配置。
接下来举一个简单的例子,使用该类型存储自定的用户名和密码,用户名为superuser,密码为abc12345,接下来我们要先对用户名和密码进行编码,命令如下:
[root@master ~]# echo -n "superuser" | base64
c3VwZXJ1c2Vy
[root@master ~]# echo -n "abc12345" | base64
YWJjMTIzNDU=
//上述结果中产生的字符串信息均为用户名和密码的编码结果
[root@master ~]# vi testsecret.yaml
apiVersion: v1
kind: Secret
metadata:
name: testsecret
type: Opaque
data:
username: c3VwZXJ1c2Vy
password: YWJjMTIzNDU=
//在这个模板中资源对象类型为Secret,名字为testsecret,并且包含了两个键值对,分别为刚才编码之后的值
[root@master ~]# kubectl apply -f testsecret.yaml
secret/testsecret created
[root@master ~]# kubectl get secret
NAME TYPE DATA AGE
testsecret Opaque 2 15s
[root@master ~]# kubectl describe secret testsecret
Name: testsecret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 8 bytes //两个键值对并未使用明文展示
username: 9 bytes
[root@master ~]# kubectl get secret testsecret -o yaml
apiVersion: v1
data:
password: YWJjMTIzNDU=
username: c3VwZXJ1c2Vy
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"password":"YWJjMTIzNDU=","username":"c3VwZXJ1c2Vy"},"kind":"Secret","metadata":{"annotations":{},"name":"testsecret","namespace":"default"},"type":"Opaque"}
creationTimestamp: "2023-05-31T01:00:17Z"
name: testsecret
namespace: default
resourceVersion: "833622"
selfLink: /api/v1/namespaces/default/secrets/testsecret
uid: 2769805a-82ab-42b7-9980-a450bea8470b
type: Opaque
//从上述的信息可知,使用-o yaml的方式一下就可以查出我们配置的键值,所以这个方式一点也不安全,只要稍微解码即可获取原始值,接下来我们进行解码,如下:
[root@master ~]# echo "c3VwZXJ1c2Vy" | base64 --decode
superuser
[root@master ~]# echo "YWJjMTIzNDU=" | base64 --decode
abc12345
//Secret创建完成之后就可以在Pod中进行引用了,其引用方式和ConfigMap的方式大同小异,接下来分别说一下两种引用方式。
【环境变量的方式】
[root@master ~]# vi testsecretpod1.yaml
apiVersion: v1
kind: Pod
metadata:
name: testsecretpod
spec:
containers:
- name: containerforenv
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "EnvParaUserName: ${EnvParaUserName} EnvParaPassword: ${EnvParaPassword}" printenv grep EnvPara; sleep 3600']
env:
- name: EnvParaUserName
valueFrom:
secretKeyRef:
name: testsecret
key: username
- name: EnvParaPassword
valueFrom:
secretKeyRef:
name: testsecret
key: password
//上述的模板通过valueFrom、secretKeyRef、name、key等属性,我们可以指定具体要引用的那些环境变量。重点是在env的属性中,我们先定义了环境变量的名称分别为EnvParaUserName、EnvParaPassword。通过valueFrom属性来定义,表示环境变量的值来自外部引用,关键字是secretKeyRef表示从secret中引用;secretKeyRef.name属性表示要引用的secret的名称,而secretKeyRef.key表示要引用的键值对的键名,它的值会映射到环境变量上。
[root@master ~]# kubectl apply -f testsecretpod1.yaml
pod/testsecretpod created
[root@master ~]# kubectl get pod |grep testsecret
testsecretpod 1/1 Running 0 36s
[root@master ~]# kubectl logs testsecretpod
EnvParaUserName: superuser EnvParaPassword: abc12345 printenv grep EnvPara
//从上述信息中,我们可以看到secret中设置的键值被成功引用,在secret中设置的键值也是非常多,单个形式配置会很麻烦,K8S也为它提供了简易的方式,来将多个键值配置到Pod,命令如下:
[root@master ~]# cat testsecretpod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: testsecretpod2
spec:
containers:
- name: containerforenv
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['printenv ; sleep 3600']
envFrom:
- secretRef:
name: testsecret
//此模板直接使用envFrom的属性,代表整个环境从外部文件引用;引用方式为secretRef,代表从secret中引用,- secretRef.name代表secret的名字
[root@master ~]# kubectl apply -f testsecretpod2.yaml
pod/testsecretpod2 created
[root@master ~]# kubectl get pod |grep testsecret
testsecretpod 1/1 Running 0 9m49s
testsecretpod2 1/1 Running 0 22s
[root@master ~]# kubectl logs testsecretpod2
username=superuser
password=abc12345
【存储卷引用方式】
Secret本身也是一种特殊的存储卷,所以也可以通过存储卷的方式进行配置到Pod中,不同于环境变量的引用方式,它也会把每个键值对都转换成对应的实体文件。
[root@master ~]# vi testsecretvolume.yaml
apiVersion: v1
kind: Pod
metadata:
name: testsecretvolume
spec:
containers:
- name: containervolume
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "files:"; ls /secret/allvalues; sleep 3600']
volumeMounts:
- name: volumesecret
mountPath: /secret/allvalues
volumes:
- name: volumesecret
secret:
secretName: testsecret
//本文件创建的存储卷名称为volumesecret,这个名称会被容器设置的存储卷引用。存储卷的类型是secret,secretName的属性是testsecret。创建的容器名为containervolume会引用volumesecret存储卷,并将其映射到容器的/secret/allvalues目录下,然后通过ls命令输出/secret/allvalues下的所有文件
[root@master ~]# kubectl apply -f testsecretvolume.yaml
pod/testsecretvolume created
[root@master ~]# kubectl get pod |grep testsecretvolume
testsecretvolume 1/1 Running 0 19s
[root@master ~]# kubectl logs testsecretvolume
files:
password
Username
//从以上结果可知,在容器的映射目录下分别存放了两个文件,我们可以进入这个Pod中查看这两个文件的信息
[root@master ~]# kubectl exec -it testsecretvolume -- /bin/sh
/ # cat /secret/allvalues/
..2023_05_31_02_05_54.985248978/ password
..data/ username
/ # cat /secret/allvalues/username
superuser/ #
/ # cat /secret/allvalues/password
abc12345/
//从以上信息可知,两个文件的内容是secret中各个键对应的值,而且这些值已经被解码成了明文
ImagePullSecret(仅作参考)
此类型主要用来存储私有Dcoker Registry的认证信息,在设置Pod模板时,如果需要从私有仓库中拉取镜像,可以设置imagePullSecret属性为此类型的Secret,以作为仓库的登陆密钥。
[root@master ~]# kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_PASSWORD
secret/myregistrykey created
//在这个案例中我们以命令行的方式创建了一个名为myregistrykey的Secret,在创建时需要将自己的DOCKER_REGISTRY_SERVER、DOCKER_USER、DOCKER_PASSWORD、DOCKER_PASSWORD设置为对应环境中的值,也可以使用读取文件的方式来进行创建,比如:
# kubectl create secret docker-registry myregistrykey --from-file="./dockercfg"
//上述命令执行完毕后可以使用下面命令输出该secret的yaml内容,在改内容中data字段下包含了一串字符串,这串内容就是编码之后的值,可以对该字符串进行解码,使其采用明文的形式展示出来,可以使用下面的命令
[root@master ~]# kubectl get secret
NAME TYPE DATA AGE
default-token-r9kpb kubernetes.io/service-account-token 3 20d
myregistrykey kubernetes.io/dockerconfigjson 1 35s
nfs-client-provisioner-token-j2csg kubernetes.io/service-account-token 3 44h
testsecret Opaque 2 145m
[root@master ~]# kubectl get secret myregistrykey -o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJET0NLRVJfUkVHSVNUUllfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImVtYWlsIjoiRE9DS0VSX1BBU1NXT1JEIiwiYXV0aCI6IlJFOURTMFZTWDFWVFJWSTZSRTlEUzBWU1gxQkJVMU5YVDFKRSJ9fX0=
kind: Secret
metadata:
creationTimestamp: "2023-05-31T03:25:26Z"
name: myregistrykey
namespace: default
resourceVersion: "850444"
selfLink: /api/v1/namespaces/default/secrets/myregistrykey
uid: d8763d6a-aae2-4568-9a9f-74c50fdbf923
type: kubernetes.io/dockerconfigjson
[root@master ~]# echo "eyJhdXRocyI6eyJET0NLRVJfUkVHSVNUUllfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImVtYWlsIjoiRE9DS0VSX1BBU1NXT1JEIiwiYXV0aCI6IlJFOURTMFZTWDFWVFJWSTZSRTlEUzBWU1gxQkJVMU5YVDFKRSJ9fX0=" | base64 --decode
{"auths":{"DOCKER_REGISTRY_SERVER":{"username":"DOCKER_USER","password":"DOCKER_PASSWORD","email":"DOCKER_PASSWORD","auth":"RE9DS0VSX1VTRVI6RE9DS0VSX1BBU1NXT1JE"}}}
//Secret创建完成后,就可以在Pod中引用,将Secret名称配置到spec.imagePullSecrets属性中即可,模板如下:
[root@master ~]# vi testsecretimagePullSecret.yaml
apiVersion: v1
kind: Pod
metadata:
name: testsecretimagePullSecret
spec:
containers:
- name: testcontainer
image: mydocker/myapp:v1
imagePullSecrets:
- name: myregistrykey
DownWard API
主要作用是向Pod中运行的容器暴露Pod自身的信息,DownWardapi允许容器在不使用K8S客户端或者API Server的情况下获取有关自身或集群的信息。
在目前的版本中DownWardAPI可以获取大量的信息,下面列出可同时通过环境变量或存储卷获取的信息:
【使用FieldRef属性可获取的信息】
● metadata.name:Pod名称
● metadata.namespace:Pod的命名空间
● metadata.uid:Pod的UID
● metadata.labels[‘{KEY}’]:Pod的标签{KEY}值
● metadata.annottations[‘{KEY}’]:Pod注解{KEY}的值
【使用resourceFieldRef属性可获取的信息】
如果你没有给容器指定CPU或者内存限制,则DownWardAPI获取节点上CPU和内存默认可分配的值
● limits.cpu:容器的CPU限制
● requests.cpu:容器的CPU请求
● limits.memory:容器的内存限制
● requests.memory:容器的内存请求
● limits.ephemeral-storage:容器的临时存储限制
● requests.ephemeral-storage:容器的临时存储请求
【使用FieldRef属性批量可获取的信息】
● metadata.labels:所有的Pod标签,格式为label-key=”escaped-label-value”,每行一个标签
●metadata.annottations:所有Pod的注解,格式为annottation-key=”escaped-annottation-value”每行一个注解
【只能通过环境变量获得的信息】
● status.podIP:Pod的IP地址
● spec.serviceAccountName:Pod的ServiceAccount名称
● spec.nodeName:节点名称
● status.hostIP:节点的IP
1.【环境变量引用方式】
DownWardAPI可以采用设置环境变量和命令行参数的形式进行引用
[root@master ~]# cat testpodfordownward.yaml
apiVersion: v1
kind: Pod
metadata:
name: testpodfordownward
spec:
containers:
- name: containerenv
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "EnvParaPodName: ${EnvParaPodName} EnvParaPodIP: ${EnvParaPodIP} EnvParaNodeName: ${EnvParaNodeName}"; printenv |grep EnvPara; sleep 3600']
env:
- name: EnvParaPodName
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: EnvParaPodIP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: EnvParaNodeName
valueFrom:
fieldRef:
fieldPath: spec.nodeName
[root@master ~]# kubectl apply -f testpodfordownward.yaml
pod/testpodfordownward created
[root@master ~]# kubectl get pod |grep testpodfordownward
testpodfordownward 1/1 Running 0 21s
[root@master ~]# kubectl logs testpodfordownward
EnvParaPodName: testpodfordownward EnvParaPodIP: 10.244.2.10 EnvParaNodeName: worker1
EnvParaPodName=testpodfordownward
EnvParaPodIP=10.244.2.10
EnvParaNodeName=worker1
2.【通过存储卷的方式引用】
因为DownWard API本身就是一种特殊的存储卷,so 也可以利用存储卷的方式配置到Pod中,这种引用方式会将每个键值对都转换成对应的实体文件。
[root@master ~]# cat testpodforvolume.yaml
apiVersion: v1
kind: Pod
metadata:
name: testpodforvolumedownward
spec:
containers:
- name: containerforvolume
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "files:"; ls /config/alldownward; sleep 3600']
volumeMounts:
- name: volumedownward
mountPath: /config/alldownward
volumes:
- name: volumedownward
downwardAPI:
items:
- path: "PodName"
fieldRef:
fieldPath: metadata.name
- path: "PodUID"
fieldRef:
fieldPath: metadata.uid
- path: "PodNameSpace"
fieldRef:
fieldPath: metadata.namespace
//本文件创建的存储卷名称为volumedownward,这个名称会被容器设置的存储卷引用。存储卷的类型是downwardAPI,分别引用了metadata.name、metadata.uid、metadata.namespace,分别将其命名为到指定路径的PodName、PodNameSpace、PodUID。创建的容器名为containervolume会引用volumeconfig存储卷,并将其映射到容器的/config/alldownward目录下,然后通过ls命令输出/config/alldownward下的所有文件
[root@master ~]# kubectl apply -f testpodforvolume.yaml
pod/testpodforvolumedownward created
[root@master ~]# kubectl get pod |grep testpodforvolume
testpodforvolumedownward 1/1 Running 0 32s
[root@master ~]# kubectl logs testpodforvolumedownward
files:
PodName
PodNameSpace
PodUID
[root@master ~]# kubectl exec -it testpodforvolumedownward -- /bin/sh
/ # cat /config/alldownward/PodName
testpodforvolumedownward/ #
/ # cat /config/alldownward/PodNameSpace
/ # cat /config/alldownward/PodUID
d5fa0630-8c4c-4f61-8ab4-563484c7d85a/ #