1. 数据存储
-
容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。
-
Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时,Volume中的数据也不会丢失。
-
kubernetes的Volume支持多种类型,比较常见的有下面几个:
-
简单存储:EmptyDir、HostPath、NFS
-
高级存储:PV、PVC
-
配置存储:ConfigMap、Secret
-
1.1 基本存储
1.1.1 EmptyDir
EmptyDir 是一种轻量级的存储选项,用于 Pod 内部容器之间的数据共享或临时存储。下面是一些关于 EmptyDir 的补充信息:
-
生命周期:EmptyDir 的生命周期与 Pod 绑定。当 Pod 被创建时,EmptyDir 被创建并挂载到 Pod 中所有需要它的容器上。当 Pod 被删除时,EmptyDir 及其内容也会被删除。
-
使用场景:
-
临时存储:对于需要临时写入和读取数据,但不要求数据持久化的场景,如缓存或会话数据。
-
容器间数据共享:当 Pod 中运行多个容器需要共享文件时,EmptyDir 可以作为一个共享存储空间。
-
数据处理:在数据被处理并存储到更持久的存储解决方案之前,可以临时存储在 EmptyDir 中。
-
-
性能:EmptyDir 存储的性能通常与宿主机的磁盘性能相当,因为它直接存储在宿主机上。
-
限制:由于 EmptyDir 与 Pod 的生命周期绑定,因此它不适合存储需要跨 Pod 持久化的数据。此外,EmptyDir 不保证数据的备份或复制,所以在 Pod 重启或重建时数据可能会丢失。
-
配置:EmptyDir 可以通过 Pod 定义中的
spec.volumes
字段来配置,无需指定宿主机上的路径。 -
安全性:EmptyDir 默认只对 Pod 内部的容器可见,提供了一定程度的隔离。
在一个Pod中准备两个容器nginx和busybox,然后声明一个Volume分别挂在到两个容器的目录中,然后nginx容器负责向Volume中写日志,busybox中通过命令将日志内容读到控制台。
-
示例
[root@k8s-master-01 ~]# vim volume-emptydir.yaml --- apiVersion: v1 kind: Pod metadata: name: volume-emptydir namespace: test spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 volumeMounts: # 将logs-volume挂在到nginx容器中,对应的目录为 /var/log/nginx - name: logs-volume mountPath: /var/log/nginx - name: busybox image: busybox:1.30 command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件中内容 volumeMounts: # 将logs-volume 挂在到busybox容器中,对应的目录为 /logs - name: logs-volume mountPath: /logs volumes: # 声明volume, name为logs-volume,类型为emptyDir - name: logs-volume emptyDir: {} [root@k8s-master-01 ~]# kubectl apply -f volume-emptydir.yaml pod/volume-emptydir created [root@k8s-master-01 ~]# kubectl get pod -n test -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES volume-emptydir 2/2 Running 0 62s 1.244.44.201 k8s-node-02 <none> <none>
-
访问
[root@k8s-master-01 ~]# kubectl exec -it volume-emptydir -n test /bin/bash root@volume-emptydir:/# echo 'This is EmptyDir test' > /usr/share/nginx/html/index.html root@volume-emptydir:/# exit [root@k8s-master-01 ~]# curl 1.244.44.201 This is EmptyDir test
-
通过kubectl logs命令查看指定容器的标准输出
[root@k8s-master-01 ~]# kubectl logs -f volume-emptydir -n test -c busybox # 可以看到访问日志 1.244.151.128 - - [22/May/2024:07:15:58 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-" 1.244.151.128 - - [22/May/2024:07:17:04 +0000] "GET / HTTP/1.1" 200 22 "-" "curl/7.29.0" "-"
1.1.2 HostPath
-
EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单的将数据持久化到主机中,可以选择HostPath。
-
HostPath就是将Node主机中一个实际目录挂在到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依据可以存在于Node主机上。
-
数据持久性:与
EmptyDir
不同,HostPath
卷将 Node 主机上的一个目录挂载到 Pod 中,使得 Pod 内的容器可以访问和修改该目录上的文件。由于这些文件存储在 Node 主机上,因此即使 Pod 被删除,数据仍然可以在 Node 主机上保持。 -
用途:
HostPath
适用于需要持久化数据的场景,例如数据库文件、配置文件等。然而,由于HostPath
直接使用了 Node 主机的文件系统,可能会带来一些安全和管理上的挑战,例如权限管理、数据一致性等问题。
-
-
示例
[root@k8s-master-01 ~]# vim volume-hostpath.yaml --- apiVersion: v1 kind: Pod metadata: name: volume-hostpath namespace: test spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 volumeMounts: - name: logs-volume mountPath: /var/log/nginx - name: busybox image: busybox:1.30 command: ["/bin/sh","-c","tail -f /logs/access.log"] volumeMounts: - name: logs-volume mountPath: /logs volumes: - name: logs-volume hostPath: path: /root/logs type: DirectoryOrCreate [root@k8s-master-01 ~]# kubectl apply -f volume-hostpath.yaml pod/volume-hostpath created [root@k8s-master-01 ~]# kubectl get pod -n test -o wide # 调度到了node2 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES volume-hostpath 2/2 Running 0 23s 1.244.44.202 k8s-node-02 <none> <none>
各个 type
值的详细说明:
type 值 | 描述 |
---|---|
DirectoryOrCreate | 如果指定的目录存在,则使用该目录;如果不存在,则先创建目录后使用。 |
Directory | 目录必须存在,否则挂载操作会失败。 |
FileOrCreate | 如果指定的文件存在,则使用该文件;如果不存在,则先创建文件后使用。 |
File | 文件必须存在,否则挂载操作会失败。 |
Socket | Unix 套接字必须存在,否则挂载操作会失败。 |
CharDevice | 字符设备必须存在,否则挂载操作会失败。 |
BlockDevice | 块设备必须存在,否则挂载操作会失败。 |
-
测试
[root@k8s-master-01 ~]# curl 1.244.44.202 # 下来就可以去host的/root/logs目录下查看存储的文件了 注意: 下面的操作需要到Pod所在的节点运行,这里是调度到了node2 [root@k8s-node-02 ~]# ll /root/logs/ total 4 -rw-r--r-- 1 root root 95 May 22 15:44 access.log -rw-r--r-- 1 root root 0 May 22 15:39 error.log [root@k8s-node-02 ~]# cat /root/logs/access.log 1.244.151.128 - - [22/May/2024:07:44:59 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-" # 同样的道理,如果在此目录下创建一个文件,到容器中也是可以看到的
1.1.3 NFS
-
虽然
HostPath
可以用于解决数据持久化的问题,但它并不适用于跨节点的数据共享和高可用性场景。一旦 Pod 从一个节点迁移到另一个节点,HostPath
卷中的数据就无法访问。这是因为HostPath
卷依赖于特定节点上的文件系统,而不是一个独立的网络存储系统。 -
为了解决这个问题,确保数据在节点迁移时仍然可用,通常推荐使用网络文件存储系统,如 NFS (Network File System) 或 CIFS (Common Internet File System)。这两种协议允许 Pod 访问远程存储系统上的数据,就好像它们是本地文件一样。
-
NFS 是一种网络文件系统协议,它允许客户端计算机访问服务器上的文件和目录,就像它们是本地文件一样。通过设置 NFS 服务器,您可以将 Pod 中的存储直接连接到 NFS 系统上。这样,即使 Pod 从一个节点迁移到另一个节点,只要网络连接正常,Pod 就可以继续访问数据。
-
首先要准备NFS的服务器,这里为了简单,直接是master节点做NFS服务器
# 在master上安装nfs服务 [root@k8s-master-01 ~]# yum install nfs-utils -y # 准备一个共享目录 [root@k8s-master-01 ~]# mkdir /nfstest # 将共享目录以读写权限暴露给192.168.110.0/24网段中的所有主机 [root@k8s-master-01 ~]# vim /etc/exports /nfstest 192.168.110.0/24(rw,no_root_squash) [root@k8s-master-01 ~]# systemctl restart rpcbind.service nfs-server.service
-
在的每个node节点上都安装下NFS,这样的目的是为了node节点可以驱动NFS设备
[root@k8s-node-01 ~]# yum install nfs-utils -y [root@k8s-node-01 ~]# showmount -e 192.168.110.21 Export list for 192.168.110.21: /nfstest 192.168.110.0/24 [root@k8s-node-02 ~]# yum install nfs-utils -y [root@k8s-node-02 ~]# showmount -e 192.168.110.21 Export list for 192.168.110.21: /nfstest 192.168.110.0/24
-
编写Pod的配置文件
[root@k8s-master-01 ~]# vim volume-nfs.yaml --- apiVersion: v1 kind: Pod metadata: name: volume-nfs namespace: test spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 volumeMounts: - name: logs-volume mountPath: /var/log/nginx - name: busybox image: busybox:1.30 command: ["/bin/sh","-c","tail -f /logs/access.log"] volumeMounts: - name: logs-volume mountPath: /logs volumes: - name: logs-volume nfs: server: k8s-master-01 path: /nfstest [root@k8s-master-01 ~]# kubectl apply -f volume-nfs.yaml pod/volume-nfs created [root@k8s-master-01 ~]# kubectl get pod -n test -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES volume-nfs 2/2 Running 0 118s 1.244.44.204 k8s-node-02 <none>
-
查看
[root@k8s-master-01 ~]# ll /nfstest/ total 0 -rw-r--r-- 1 root root 0 May 22 17:23 access.log -rw-r--r-- 1 root root 0 May 22 17:23 error.log [root@k8s-master-01 ~]# curl 1.244.44.204 [root@k8s-master-01 ~]# cat /nfstest/access.log 1.244.151.128 - - [22/May/2024:09:26:39 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
1.2 高级存储
1.2.1 PV和PVC
由于kubernetes支持的存储系统有很多,要求客户全都掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用, kubernetes引入PV和PVC两种资源对象。
-
PV(Persistent Volume)
-
PV是Kubernetes中的一个API对象,它代表集群中的一块存储,这块存储已经预先按照某种方式设置好了,并且可以被多个用户使用。PV是集群资源,由Kubernetes管理员预先配置,它们不会因Pod的终止而消失,因此被称为“持久化”。PV可以采用多种形式,例如本地存储、网络附加存储(NAS)、云存储服务等。
-
-
PVC(Persistent Volume Claim)
-
PVC是用户对存储的请求,它允许用户以声明式的方式请求存储资源,而无需关心存储的具体实现细节。用户定义所需的存储容量、访问模式和存储类别(如果需要),然后提交PVC请求。
-
在Kubernetes中使用PV(Persistent Volume)和PVC(Persistent Volume Claim)确实可以带来工作流的细分和专业化,这样的分工可以提高效率并减少错误。以下是各个角色的主要职责:
-
存储工程师,存储工程师负责维护和操作底层的存储系统。他们的工作包括:
-
选择合适的存储解决方案,包括本地存储、网络附加存储(NAS)、存储区域网络(SAN)或云存储服务。
-
配置和管理存储硬件或服务,确保其性能、可靠性和安全性。
-
监控存储系统的性能,确保它满足Kubernetes集群的需求。
-
管理数据备份、恢复和灾难恢复计划。
-
-
Kubernetes管理员,Kubernetes管理员负责维护Kubernetes集群和PV资源。他们的工作职责包括:
-
创建和管理PV资源,将底层存储系统抽象化,使其可以在Kubernetes集群中使用。
-
配置PV的访问模式和存储容量,确保它们符合集群的使用需求。
-
监控PV的使用情况,确保存储资源得到合理分配和优化。
-
管理PV的生命周期,包括创建、修改和删除操作。
-
-
Kubernetes用户,Kubernetes用户负责维护PVC资源,他们通常是开发人员或应用运维团队。他们的工作职责包括:
-
创建和管理PVC,根据应用的需求声明所需的存储资源。
-
定义PVC的存储容量、访问模式和选择器,以便与合适的PV进行绑定。
-
将PVC挂载到Pod中,以便应用可以持久化存储数据。
-
监控PVC的状态和使用情况,确保应用的存储需求得到满足。
-
1.2.2 PV(Persistent Volume)
-
PV(Persistent Volume)在Kubernetes中指的是持久化卷,它是一种存储抽象的概念,代表了集群中的一块存储,这可以是本地磁盘、网络附加存储(NAS)、或者云存储等。PV与底层存储的具体实现细节相解耦,使得Kubernetes管理员可以灵活地管理存储资源。
1.2.2.1 PV的主要特点包括
-
独立性:PV独立于Pod存在,即使使用PV的Pod被删除,PV中的数据也不会丢失。
-
持久性:PV在设计上用于数据的持久化存储,它们在Kubernetes集群中是长寿命的。
-
可配置性:Kubernetes管理员可以配置PV的大小、访问模式(如ReadWriteOnce、ReadOnlyMany、ReadWriteMany)以及存储类别(StorageClass)。
-
可重用性:PV一旦被创建,可以被多个Pod重用,只要它们的访问模式和存储需求相匹配。
-
动态供应:通过StorageClass资源,Kubernetes可以自动创建PV以满足PVC的请求,这个过程称为动态存储供应。
1.2.2.2 PV的生命周期
-
创建:Kubernetes管理员根据集群的存储需求预先创建PV资源。
-
绑定:当用户提交PVC(Persistent Volume Claim)请求时,Kubernetes会尝试找到一个匹配的PV进行绑定。
-
使用:绑定成功后,PV可以被Pod通过PVC访问和使用。
-
回收:当PVC被删除时,PV可以被释放并根据回收策略进行清理,以便再次使用或保持其数据。
1.2.2.3 资源清单文件
apiVersion: v1 kind: PersistentVolume metadata: name: pv2 spec: nfs: # 存储类型,与底层真正存储对应 path: /path/to/nfs/share # NFS共享路径 server: nfs-server.example.com # NFS服务器地址 capacity: # 存储能力,目前只支持存储空间的设置 storage: 2Gi accessModes: # 访问模式 - ReadWriteOnce # 可以被一个节点挂载为读写模式 persistentVolumeReclaimPolicy: Retain # 回收策略 # Retain:删除PersistentVolumeClaim后,不会删除PersistentVolume,只是标记为不可用 # Delete:删除PersistentVolumeClaim后,会删除PersistentVolume # Recycle:删除PersistentVolumeClaim后,将PersistentVolume清理并重新可用,但数据将丢失
1.2.2.4 PV关键配置参数
-
存储类型
-
定义:底层实际存储的类型,Kubernetes 支持多种存储类型,如 NFS、iSCSI、glusterFS 等,每种类型的配置都会有所不同。
-
影响因素:不同存储类型可能支持的访问模式和回收策略不同。
-
-
存储能力(capacity)
-
定义:当前主要支持存储空间的设置(如
storage=1Gi
),但未来可能会增加 IOPS、吞吐量等其他性能指标的配置。 -
注意事项:存储能力的设置直接影响到 PV 能够提供给 Pod 的存储空间大小。
-
访问模式(accessModes)
-
定义:描述用户应用对存储资源的访问权限,包括:
-
ReadWriteOnce(RWO):读写权限,但只能被单个节点挂载。
-
ReadOnlyMany(ROX):只读权限,可被多个节点挂载。
-
ReadWriteMany(RWX):读写权限,可被多个节点挂载。
-
-
注意事项:底层不同的存储类型可能支持的访问模式不同。
-
-
回收策略(persistentVolumeReclaimPolicy)
-
定义:当 PV 不再被使用时,对其的处理方式,包括:
-
Retain(保留):保留数据,需管理员手动清理。
-
Recycle(回收):清除 PV 中的数据。
-
Delete(删除):与 PV 相关联的后端存储完成 volume 删除操作。
-
-
注意事项:底层不同的存储类型可能支持的回收策略不同。
-
-
存储类别(storageClassName)
-
定义:通过
storageClassName
参数指定一个存储类别,有助于管理和组织不同类型的存储资源。 -
绑定规则:具有特定类别的 PV 只能与请求了该类别的 Persistent Volume Claim (PVC) 进行绑定;未设定类别的 PV 则只能与不请求任何类别的 PVC 进行绑定。
-
-
状态(status)
-
定义:PV 在生命周期中的状态,包括:
-
Available(可用):表示可用状态,尚未被任何 PVC 绑定。
-
Bound(已绑定):表示 PV 已经被 PVC 绑定。
-
Released(已释放):表示 PVC 被删除,但资源还未被集群重新声明。
-
Failed(失败):表示该 PV 的自动回收失败。
-
-
1.2.2.5 示例
-
使用NFS作为存储,来演示PV的使用,创建3个PV,对应NFS中的3个暴露的路径
[root@k8s-master-01 ~]# mkdir /data/pv{1..3} -p [root@k8s-master-01 ~]# vim /etc/exports /data/pv1 192.168.110.0/24(rw,no_root_squash) /data/pv2 192.168.110.0/24(rw,no_root_squash) /data/pv3 192.168.110.0/24(rw,no_root_squash) [root@k8s-master-01 ~]# exportfs -arv exporting 192.168.110.0/24:/data/pv3 exporting 192.168.110.0/24:/data/pv2 exporting 192.168.110.0/24:/data/pv1
-
创建pv配置文件
[root@k8s-master-01 ~]# vim pv.yaml --- apiVersion: v1 kind: PersistentVolume metadata: name: pv1 spec: capacity: storage: 1Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: path: /data/pv1 server: k8s-master-01 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv2 spec: capacity: storage: 2Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: path: /data/pv2 server: k8s-master-01 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv3 spec: capacity: storage: 3Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: path: /data/pv3 server: k8s-master-01 [root@k8s-master-01 ~]# kubectl apply -f pv.yaml persistentvolume/pv1 created persistentvolume/pv2 created persistentvolume/pv3 created [root@k8s-master-01 ~]# kubectl get pv -o wide NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE pv1 1Gi RWX Retain Available 119s Filesystem pv2 2Gi RWX Retain Available 119s Filesystem pv3 3Gi RWX Retain Available 119s Filesystem
1.2.3 PVC(Persistent Volume Claim)
1.2.3.1 资源清单
-
PVC是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc namespace: dev spec: accessModes: # 访问模式 - ReadWriteOnce # 或者 ReadOnlyMany、ReadWriteMany,取决于你的需求 selector: # 采用标签对PV选择 matchLabels: app: myapp # 这里的值应该匹配你想要绑定的PV的标签 storageClassName: # 存储类别 - standard # 如果没有指定存储类别,则默认使用default resources: # 请求空间 requests: storage: 5Gi # 请求的存储空间大小
1.2.3.2 PVC 的关键配置参数
-
访问模式(accessModes)
-
定义:描述用户应用对存储资源的访问权限。
-
可选值
-
ReadWriteOnce
:表示该卷可以被集群中的一个节点挂载为读写模式。 -
ReadOnlyMany
:表示该卷可以被集群中的多个节点挂载为只读模式。 -
ReadWriteMany
:表示该卷可以被集群中的多个节点同时挂载为读写模式。
-
-
注意事项:不同的存储类型可能支持的访问模式不同。
-
-
选择条件(selector)
-
定义:通过 Label Selector 的设置,使 PVC 对系统中已存在的 PV 进行筛选。
-
作用:允许 PVC 根据标签选择特定的 PV,从而实现更灵活的存储资源管理。
-
示例:
matchLabels: { app: myapp }
表示 PVC 会尝试绑定标签为{ app: myapp }
的 PV。
-
-
存储类别(storageClassName)
-
定义:PVC 在定义时可以设定需要的后端存储的类别。
-
作用:只有设置了相同
storageClassName
的 PV 才能被系统选出作为 PVC 的目标。 -
示例:
storageClassName: standard
表示 PVC 要求使用名为standard
的存储类别。
-
-
资源请求(Resources)
-
定义:PVC 中的资源请求字段用于指定 PVC 需要的最小存储空间大小。
-
示例:
resources.requests.storage: 5Gi
表示 PVC 请求至少需要 5GB 的存储空间。 -
注意事项:PVC 的资源请求必须小于或等于 PV 提供的最大存储空间,否则 PVC 无法成功绑定到 PV 上。
-
1.2.3.3 配置示例
[root@k8s-master-01 ~]# vim pvc.yaml --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc1 namespace: test spec: accessModes: - ReadWriteMany resources: requests: storage: 10Mi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc2 namespace: test spec: accessModes: - ReadWriteMany resources: requests: storage: 10Mi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc3 namespace: test spec: accessModes: - ReadWriteMany resources: requests: storage: 10Mi [root@k8s-master-01 ~]# kubectl apply -f pvc.yaml persistentvolumeclaim/pvc1 created persistentvolumeclaim/pvc2 created persistentvolumeclaim/pvc3 created [root@k8s-master-01 ~]# kubectl get pvc -n test -o wide NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE pvc1 Bound pv1 1Gi RWX 6s Filesystem pvc2 Bound pv2 2Gi RWX 6s Filesystem pvc3 Bound pv3 3Gi RWX 6s Filesystem [root@k8s-master-01 ~]# kubectl get pv -o wide # PV状态变为绑定 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE pv1 1Gi RWX Retain Bound test/pvc1 2m26s Filesystem pv2 2Gi RWX Retain Bound test/pvc2 2m26s Filesystem pv3 3Gi RWX Retain Bound test/pvc3 2m26s Filesystem
-
创建pods.yaml, 使用pv
[root@k8s-master-01 ~]# vim pods.yaml --- apiVersion: v1 kind: Pod metadata: name: pod1 namespace: test spec: containers: - name: busybox image: busybox:1.30 command: ["/bin/sh", "-c", "while true; do echo pod1 >> /root/out.txt; sleep 10; done;"] volumeMounts: - name: volume mountPath: /root/ volumes: - name: volume persistentVolumeClaim: claimName: pvc1 readOnly: false --- apiVersion: v1 kind: Pod metadata: name: pod2 namespace: test spec: containers: - name: busybox image: busybox:1.30 command: ["/bin/sh", "-c", "while true; do echo pod2 >> /root/out.txt; sleep 10; done;"] volumeMounts: - name: volume mountPath: /root/ volumes: - name: volume persistentVolumeClaim: claimName: pvc2 readOnly: false [root@k8s-master-01 ~]# kubectl apply -f pods.yaml pod/pod1 created pod/pod2 created [root@k8s-master-01 ~]# kubectl get pod -n test -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod1 1/1 Running 0 17s 1.244.154.197 k8s-node-01 <none> <none> pod2 1/1 Running 0 17s 1.244.44.194 k8s-node-02 <none> <none> # 查看nfs中的文件存储 [root@k8s-master-01 ~]# cat /data/pv1/out.txt pod1 pod1 pod1 [root@k8s-master-01 ~]# cat /data/pv2/out.txt pod2 pod2 pod2
1.2.4 PVC和PV在Kubernetes中的生命周期
1.2.4.1 资源供应
-
管理员首先需要创建底层存储(如NFS、AWS EBS等),然后创建一个PV来代表这个存储。PV定义了存储的类型、大小、访问模式等信息。
1.2.4.2 资源绑定
-
用户创建PVC来请求存储资源。PVC定义了存储需求,比如大小和访问模式。
-
Kubernetes 集群会根据PVC的声明去寻找一个或多个PV来满足这些需求。如果找到匹配的PV,Kubernetes 会自动将PVC和PV进行绑定。
-
如果没有找到匹配的PV,PVC将进入Pending状态,直到管理员创建了一个符合要求的PV。
1.2.4.3 资源使用
-
一旦PVC和PV绑定,用户可以在Pod中使用PVC,就像使用普通的Volume一样,将PVC挂载到容器内部的路径上。
-
Pod使用Volume的方式将PVC挂载到容器内的某个路径进行使用。
1.2.4.4 资源释放
-
当用户不再需要PVC时,可以删除PVC来释放与之绑定的PV。删除PVC后,与该PVC绑定的PV会被标记为“已释放”,但PV上的数据可能仍然存在,直到管理员清理这些数据。
-
PV的存储空间在被释放后,根据PV的回收策略,Kubernetes 会决定如何处理剩余的数据。例如,如果设置为
Retain
,数据将保持原地;如果设置为Delete
,数据将被删除。
1.2.4.5 资源回收
-
对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用
-
Kubernetes 根据PV的设置的回收策略(reclaim policy)来决定如何回收PV上的数据。常见的回收策略有Retain和Delete。
-
Retain
: 数据将保留在PV上,直到管理员手动清理。 -
Delete
: 数据将被删除,PV可以被重新绑定。
-
-
示例
[root@k8s-master-01 ~]# kubectl delete -f pvc.yaml # 删除PV persistentvolumeclaim "pvc1" deleted persistentvolumeclaim "pvc2" deleted persistentvolumeclaim "pvc3" deleted [root@k8s-master-01 ~]# kubectl get pv -o wide # 状态变为Retain NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE pv1 1Gi RWX Retain Released test/pvc1 10m Filesystem pv2 2Gi RWX Retain Released test/pvc2 10m Filesystem pv3 3Gi RWX Retain Released test/pvc3
1.3 配置存储
-
在Kubernetes中,
ConfigMap
和Secret
是两种核心资源,用于存储和管理应用程序的配置数据和敏感信息。理解它们的功能和最佳实践对于提高Kubernetes应用程序的安全性和配置管理的效率至关重要。
1.3.1 ConfigMap
-
ConfigMap
是一种API对象,允许你存储非敏感配置数据,如环境变量、数据库URL等。它以键值对的形式存储数据,便于应用程序访问必要的配置。ConfigMap
可以直接挂载到容器中或作为环境变量注入到容器中,从而使得应用程序能够访问存储的配置数据,而无需修改应用程序代码。
1.3.1.1 基本配置
-
配置
[root@k8s-master-01 ~]# vim configmap.yaml --- apiVersion: v1 kind: ConfigMap metadata: name: configmap namespace: test data: msg: | username: kxy age: 22 address: beijing [root@k8s-master-01 ~]# kubectl apply -f configmap.yaml configmap/configmap created [root@k8s-master-01 ~]# kubectl get configmaps configmap -n test NAME DATA AGE configmap 1 65s
-
创建一个pod-configmap.yaml,将上面创建的configmap挂载进去
[root@k8s-master-01 ~]# vim pod-configmap.yaml --- apiVersion: v1 kind: Pod metadata: name: pod-configmap namespace: test spec: containers: - name: nginx image: nginx:1.17.1 volumeMounts: - name: config mountPath: /configmap/config volumes: - name: config configMap: name: configmap [root@k8s-master-01 ~]# kubectl apply -f pod-configmap.yaml pod/pod-configmap created [root@k8s-master-01 ~]# kubectl get pod -n test -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-configmap 1/1 Running 0 2m49s 1.244.154.198 k8s-node-01 <none> <none> [root@k8s-master-01 ~]# kubectl exec -it pod-configmap -n test /bin/bash root@pod-configmap:/# cd /configmap/config/ root@pod-configmap:/configmap/config# ls msg root@pod-configmap:/configmap/config# cat msg username: kxy age: 22 address: beijing # 可以看到映射已经成功,每个configmap都映射成了一个目录 # key--->文件 value---->文件中的内容 # 此时如果更新configmap的内容, 容器中的值也会动态更新 # 动态验证 [root@k8s-master-01 ~]# kubectl edit cm configmap -n test 将22改为100000000 [root@k8s-master-01 ~]# kubectl exec -it pod-configmap -n test /bin/bash root@pod-configmap:/# cd /configmap/config/ root@pod-configmap:/configmap/config# cat msg username: kxy age: 100000000 address: beijing
-
使用字面值创建
# 使用文字值创建,利用 --from-literal 参数传递配置信息,该参数可以使用多次 # 创建名称为special-config的ConfigMap配置 [root@k8s-master-01 ~]# kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm [root@k8s-master-01 ~]# kubectl get cm special-config NAME DATA AGE special-config 2 50s [root@k8s-master-01 ~]# kubectl describe cm special-config Name: special-config Namespace: default Labels: <none> Annotations: <none> Data ==== special.how: ---- very special.type: ---- charm BinaryData ==== Events: <none> [root@k8s-master-01 ~]# kubectl delete cm special-config
-
使用文件创建
# 只要指定为一个文件就可以从单个文件中创建 ConfigMap 。 --from-file 这个参数可以使用多次,你可以使用两次分别指定上个实 例中的那两个配置文件,效果就跟指定整个目录是一样的。 [root@k8s-master-01 ~]# kubectl create configmap game-config-2 --from-file=/etc/hosts [root@k8s-master-01 ~]# kubectl describe cm game-config-2 Name: game-config-2 Namespace: default Labels: <none> Annotations: <none> Data ==== hosts: ---- 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.110.21 k8s-master-01 192.168.110.24 K8s-node-01 192.168.110.25 K8s-node-02 BinaryData ==== Events: <none> [root@k8s-master-01 ~]# kubectl delete cm game-config-2
-
使用目录创建
# --from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容。 [root@k8s-master-01 ~]# mkdir test [root@k8s-master-01 ~]# cp /etc/hosts /root/test/ [root@k8s-master-01 ~]# cp /etc/resolv.conf /root/test/ [root@k8s-master-01 ~]# kubectl create configmap my-config --from-file=/root/test/ configmap/my-config created [root@k8s-master-01 ~]# kubectl describe cm my-config Name: my-config Namespace: default Labels: <none> Annotations: <none> Data ==== hosts: ---- 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.110.21 k8s-master-01 192.168.110.24 K8s-node-01 192.168.110.25 K8s-node-02 resolv.conf: ---- # Generated by NetworkManager nameserver 192.168.110.2 BinaryData ==== Events: <none>
1.3.1.2 ConfigMap的使用
-
通过环境变量方式传递给Pod
[root@k8s-master-01 ~]# vim pod-configmap.yaml apiVersion: v1 kind: Pod metadata: name: pod1 spec: containers: - name: pod1 image: busybox:1.30 command: ["/bin/sh", "-c", "env"] env: - name: key1 valueFrom: configMapKeyRef: name: cm1-config key: db_host restartPolicy: Never [root@k8s-master-01 ~]# kubectl apply -f pod-configmap.yaml pod/pod1 created [root@k8s-master-01 ~]# kubectl get pod NAME READY STATUS RESTARTS AGE pod1 0/1 Comp 0 74s
1.3.1.3 ConfigMap热更新
-
正常情况下,我们可以通过如下配置,在启动的 Pod 容器里面获取到 ConfigMap 中配置的信息。
[root@k8s-master-01 ~]# vim hotipdate.yaml --- apiVersion: v1 kind: ConfigMap metadata: name: log-config namespace: default data: log_level: INFO --- apiVersion: apps/v1 kind: Deployment metadata: name: my-nginx spec: replicas: 1 selector: matchLabels: run: my-nginx # 确保selector与template的labels匹配 template: metadata: labels: run: my-nginx spec: containers: - name: my-nginx image: nginx:1.17.1 ports: - containerPort: 80 volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: log-config [root@k8s-master-01 ~]# kubectl apply -f hotipdate.yaml configmap/log-config unchanged deployment.apps/my-nginx created [root@k8s-master-01 ~]# kubectl get cm,pod -o wide NAME DATA AGE configmap/cm1-config 0 152m configmap/kube-root-ca.crt 1 3d3h configmap/log-config 1 10m NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod/my-nginx-77cdddc657-6kbsz 1/1 Running 0 7m26s 1.244.154.200 k8s-node-01 <none> <none> # 查找对应信息 [root@k8s-master-01 ~]# kubectl exec -it my-nginx-77cdddc657-6kbsz /bin/bash -- cat /etc/config/log_level INFO
-
修改 ConfigMap 配置,修改 log_level 的值为 DEBUG 等待大概 30 秒钟时间,再次查看环境变量的值。
# 修改ConfigMap配置 [root@k8s-master-01 ~]# kubectl edit configmap log-config :%s/INFO/DEBUG/g [root@k8s-master-01 ~]# kubectl exec -it my-nginx-77cdddc657-6kbsz /bin/bash -- cat /etc/config/log_level DEBUG
1.3.1.4 ConfigMap 更新后滚动更新 Pod
更新ConfigMap
目前并不会触发相关 Pod 的滚动更新,可以通过修改 pod annotations 的方式强制触发滚动更新。这个例子里我们在 .spec.template.metadata.annotations
中添加 version/config,每次通过修改 version/config 来触发滚动更新。
[root@k8s-master-01 ~]# kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "20240525" }}}}}'
1.3.1.5 解决deployment 扩容引发服务配置不一致
-
运维层面
-
重启 Deployment: 使用
kubectl rollout restart deployment
命令可以触发 Deployment 控制器去滚动更新 Pods。这将导致旧的 Pods 被逐渐替换为新的 Pods,新的 Pods 将挂载最新的 ConfigMap。kubectl rollout restart deployment <deployment-name>
-
重建 Pods: 通过删除现有的 Pods,可以触发 Deployment 控制器根据最新的配置重新创建 Pods。这可以通过
kubectl delete pod
命令实现。kubectl delete pod <pod-name>
或者,您可以使用标签选择器来删除所有匹配的 Pods:
kubectl delete pods -l app=my-nginx
-
应用程序层面
-
配置热加载: 应用程序需要能够检测到配置文件的变化,并在不重启服务的情况下重新加载配置。这通常涉及到在应用程序中实现一个监听器,当检测到 ConfigMap 中的文件发生变化时,触发配置的重新加载。
-
使用环境变量: 另一种方法是将 ConfigMap 中的值作为环境变量挂载到 Pod 中。这样,应用程序可以在启动时读取这些环境变量,而不需要关心文件系统的变化。
-
使用 Init 容器: 在 Pod 中使用 Init 容器来处理配置文件。Init 容器在应用程序容器启动之前运行,可以用来下载或生成配置文件,然后将它们存储在 Pod 的共享卷中。
-
使用 ConfigMap 触发器: 某些应用程序或平台可能支持 ConfigMap 触发器,当 ConfigMap 更新时,这些触发器可以通知应用程序进行相应的操作。
-
使用自定义的 Operator: 开发一个自定义的 Operator 来管理应用程序的配置。Operator 可以监控 ConfigMap 的变化,并根据这些变化自动更新应用程序的配置。
-
1.3.1.6 ConfigMap 错误排查和故障处理方法
-
检查 ConfigMap 是否存在: 使用
kubectl get configmap <configmap-name> -n <namespace>
命令来确认 ConfigMap 是否已经创建。 -
检查 Pod 是否正确引用 ConfigMap:
-
检查 Pod 的 YAML 文件,确保
spec.volumes
中包含了对 ConfigMap 的引用。 -
使用
kubectl describe pod <pod-name> -n <namespace>
命令查看 Pod 的详细信息,以确定是否正确引用了 ConfigMap。
-
-
检查 ConfigMap 的数据是否正确:
-
确保 ConfigMap 中的数据是正确的。
-
使用
kubectl get configmap <configmap-name> -n <namespace> -o yaml
命令查看 ConfigMap 的详细信息。
-
-
检查容器中的环境变量和配置文件:
-
使用
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh
命令进入容器。 -
在容器内部,使用
env
命令查看环境变量,或者直接查看配置文件的内容。
-
-
检查 ConfigMap 的权限和安全性:
-
如果 ConfigMap 包含敏感信息,确保使用 Kubernetes 的 RBAC 功能来限制访问权限。
-
使用
kubectl get rolebindings -n <namespace>
和kubectl get roles -n <namespace>
命令来检查权限设置。
-
-
使用 Kubernetes 的日志记录和监控功能:
-
使用
kubectl logs <pod-name> -n <namespace>
命令查看容器的日志信息。 -
使用 Kubernetes 的监控工具来监控 ConfigMap 和相关资源的状态。
-
1.3.2 Secret
-
在 Kubernetes 中,
Secret
对象确实是用来存储敏感信息的一种资源例如密码、秘钥、证书等等。它与ConfigMap
类似,但设计目的不同。
1.3.2.1 Secret有三种类型
-
Opaque:
-
这种类型的
Secret
用于存储少量的敏感数据,如密码、令牌或密钥。 -
数据以 Base64 编码格式存储,这意味着虽然数据在存储时被编码,但仍然可以通过 Base64 解码来查看原始数据,因此安全性相对较低。
-
-
Service Account:
-
这种类型的
Secret
通常用于服务账户(ServiceAccount),它们由 Kubernetes 自动创建和管理。 -
服务账户的
Secret
包含访问 Kubernetes API 所需的认证信息,如 API 令牌。 -
这些
Secret
会自动挂载到 Pod 的/run/secrets/kubernetes.io/serviceaccount
目录中,供 Pod 内的应用程序使用。
-
-
kubernetes.io/dockerconfigjson:
-
这种类型的
Secret
用于存储私有 Docker 注册表(registry)的认证信息。 -
当需要从私有 registry 拉取镜像时,可以使用这种
Secret
来提供认证信息。 -
可以通过在 Pod 的
spec
中指定imagePullSecrets
来引用这些Secret
,以便 Kubernetes 能够访问私有 registry。
-
1.3.2.2 Service Account类型
-
Service Account 是 Kubernetes 中的一个对象,它为运行在集群中的 Pod 提供了一种安全的方式来访问 Kubernetes API。Service Account 与普通用户账户类似,但它是自动管理的,并且通常与特定的服务或应用程序相关联,而不是与个人用户相关联。
# serviceaccout创建时Kubernetes会双认创建对M的secret,对应的secret会自动挂载到Pod [root@k8s-master-01 ~]# kubectl exec -it my-nginx-55b7dc67f-6tzzd -- ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token # 每个namespace下有一个名为defau1t的默认的ServiceAccount对象 [root@k8s-master-01 ~]# kubectl get sa NAME SECRETS AGE default 0 3d3h [root@k8s-master-01 ~]# kubectl get sa -n test NAME SECRETS AGE default 0 3h21m # serviceAccount里有一个名为Tokens的可以作为Volume一样被Mount到Pod里的secret,当Pod启动时这个secret会被自动Mount到Pod的指定目录下,用来协助完成Pod中的进程访问API server时的身份鉴权过程 [root@k8s-master-01 ~]# kubectl get pod my-nginx-55b7dc67f-6tzzd -o yaml spec: containers: - image: nginx:1.17.1 imagePullPolicy: IfNotPresent name: my-nginx ports: - containerPort: 80 protocol: TCP resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /etc/config name: config-volume - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: kube-api-access-mvpss readOnly: true
1.3.2.3 Opaque类型
-
Opaque 类型的数据是一个 map 类型,要求value是base64编码。
-
手动创建base64加密
[root@k8s-master-01 ~]# echo -n 'admin' | base64 YWRtaW4= [root@k8s-master-01 ~]# echo -n 'kxy20021003' | base64 a3h5MjAwMjEwMDM=
-
解密
[root@k8s-master-01 ~]# echo 'a3h5MjAwMjEwMDM=' | base64 --decode kxy20021003
这里需要注意的是,像这样创建的 Secret 对象,它里面的内容仅仅是经过了转码,而并没有被加密。在真正的生产环境中,你需要在 Kubernetes 中开启 Secret 的加密插件,增强数据的安全性。
1.3.2.4 kubernetes.io/dockerconfigjson类型
-
用来创建用户docker registry认证的Secret,直接使用kubectl create命令创建即可
[root@k8s-master-01 ~]# kubectl create secret docker-registry myregistry \ --docker-server=DOCKER_SERVER \ --docker-username=DOCKER_USER \ --docker-password=DOCKER_PASSWORD \ --docker-email=DOCKER_EMAIL [root@k8s-master-01 ~]# kubectl get secret NAME TYPE DATA AGE myregistry kubernetes.io/dockerconfigjson 1 37s
-
kubectl create secret
是创建新 Secret 的命令。 -
docker-registry
是 Secret 的类型,指定了这是一个用于 Docker registry 的认证信息。 -
myregistry
是要创建的 Secret 的名称。 -
--docker-server
后面应跟 Docker registry 的服务器地址,例如https://index.docker.io/v1/
。 -
--docker-username
后面应跟 Docker registry 的用户名。 -
--docker-password
后面应跟 Docker registry 的密码。 -
--docker-email
后面应跟 Docker registry 的用户邮箱。
1.3.2.5 Secret创建及使用方法
-
方式一: kubectl create secret 命令
[root@k8s-master-01 ~]# kubectl create secret --help Create a secret with specified type. A docker-registry type secret is for accessing a container registry. A generic type secret indicate an Opaque secret type. A tls type secret holds TLS certificate and its associated key. Available Commands: docker-registry Create a secret for use with a Docker registry generic Create a secret from a local file, directory, or literal value tls Create a TLS secret Usage: kubectl create secret (docker-registry | generic | tls) [options] Use "kubectl create secret <command> --help" for more information about a given command. Use "kubectl options" for a list of global command-line options (applies to all commands).
-
kubectl create secret
:这是创建 Secret 的基本命令。 -
(docker-registry | generic | tls)
:这表示创建 Secret 时需要指定一个类型。Kubernetes 支持三种类型的 Secret:-
docker-registry
:用于访问 Docker 容器注册表的认证信息。 -
generic
:通用类型的 Secret,用于存储少量的敏感数据,如密码或密钥。 -
tls
:用于存储 TLS 证书及其关联的私钥。
-
-
[options]
:创建不同类型的 Secret 时,可能需要提供一些选项或参数,比如认证信息、文件路径、证书详情等。 -
Available Commands
:列出了与kubectl create secret
相关的可用子命令。 -
Usage
:显示了如何使用kubectl create secret
命令的示例格式。 -
Use "kubectl create secret <command> --help"
:提示用户如果想要获取某个特定子命令的更多信息,可以使用<command> --help
来查看帮助信息。 -
Use "kubectl options"
:提示用户如果想要查看所有kubectl
命令的全局选项,可以使用kubectl options
命令。
# generic子命令可以通过本地文件、目录或者literal(键值对) [root@k8s-master-01 ~]# echo admin > username.txt [root@k8s-master-01 ~]# echo 1f2d1e2e67df > passwd.txt [root@k8s-master-01 ~]# kubectl create secret generic user --from-file=./username.txt secret/user created [root@k8s-master-01 ~]# kubectl create secret generic pass --from-file=./passwd.txt secret/pass created [root@k8s-master-01 ~]# kubectl get secrets | grep Opaque pass Opaque 1 16s user Opaque 1 30s # 默认情况下key为文件名或者直接通过键值对创建 [root@k8s-master-01 ~]# kubectl delete secrets pass secret "pass" deleted [root@k8s-master-01 ~]# kubectl delete secrets user secret "user" deleted [root@k8s-master-01 ~]# kubectl create secret generic user --from-literal=username=admin secret/user created [root@k8s-master-01 ~]# kubectl create secret generic pass --from-literal=password=1f2d1e2e67df secret/pass created [root@k8s-master-01 ~]# kubectl get secrets | grep Opaque pass Opaque 1 3s user Opaque 1 7s
-
通过yaml文件创建
[root@k8s-master-01 ~]# vim secret.yaml apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: user: YWRtaW4= # base64编码的"user" pass: MWYyZDFlMmU2N2Rm # base64编码的"pass123" [root@k8s-master-01 ~]# kubectl apply -f secret.yaml secret/mysecret created [root@k8s-master-01 ~]# kubectl get secret | grep Opaque mysecret Opaque 2 41s
1.3.2.6 Secret的使用
-
方式一:通过Volume挂载的方式
[root@k8s-master-01 ~]# vim test-projected-volume.yaml apiVersion: v1 kind: Pod metadata: name: test-projected-volume spec: containers: - name: test-secret-volume image: busybox:1.30 args: - sleep - "86400" volumeMounts: - name: mysql-cred mountPath: "/projected-volume" readOnly: true volumes: - name: mysql-cred projected: sources: - secret: name: mysql-user items: - key: user path: user - secret: name: mysql-pass items: - key: pass path: pass [root@k8s-master-01 ~]# kubectl apply -f test-projected-volume.yaml # 当 Pod 变成 Running 状态之后,我们再验证一下这些 Secret 对象是不是已经在容器里了 [root@k8s-master-01 ~]# kubectl exec -it test-projected-volume -- /bin/sh # ls /projected-volume/ user pass # cat /projected-volume/user admin # cat /projected-volume/pass 1f2d1e2e67df
-
方法二:通过环境变量
[root@k8s-master-01 ~]# kubectl create secret generic mysecret --from-file=user=./username.txt --from-file=pass=passwd.txt [root@k8s-master-01 ~]# vim pod-secret-env.yaml apiVersion: v1 kind: Pod metadata: name: pod-secret-env spec: containers: - name: myapp image: busybox args: - sleep - "86400" env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: mysecret key: user - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: mysecret key: pass restartPolicy: Never [root@k8s-master-01 ~]# kubectl get pod NAME READY STATUS RESTARTS AGE pod-secret-env 1/1 Running 0 40s
1.3.2.7 docker config json
-
使用 Kuberctl 创建 docker registry 认证的 secret
# 创建格式 $ kubectl create secret docker-registry myregistrykey \ --docker-server=DOCKER_REGISTRY_SERVER \ --docker-username=DOCKER_USER \ --docker-password=DOCKER_PASSWORD \ --docker-email=DOCKER_EMAIL
-
在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey
[root@k8s-master-01 ~]# kubectl create secret docker-registry myregistrykey \ --docker-server=hub.escape.com \ --docker-username=admin \ --docker-password=harbor123456 \ --docker-email=harbor@escape.com [root@k8s-master-01 ~]# kubectl get secret NAME TYPE DATA AGE myregistrykey kubernetes.io/dockerconfigjson 1 3m25s