Kubernetes 存储与配置管理 ——Volume、ConfigMap 与 Secret 实战详解

复习和预习

昨天课堂内容

  1. Namespace and Contexts
  2. Kubernetes Pod

今天课前练习

实验题 1:Namespace and Contexts 综合实验

  1. 使用 yaml 方式创建命名空间dev-ns;
  2. 在dev-ns命名空间创建 nginx pod,镜像使用nginx:alpine;
  3. 使用原生kubectl config命令,将当前上下文默认命名空间设置为dev-ns,不使用-n参数查看该 pod;
  4. 安装 kubens 工具,使用 kubens 切换回default命名空间,查看 dev‑ns 下 pod(必须带‑n 参数);
  5. 编写一个简易 shell 脚本实现命名空间切换功能(参考笔记自定义脚本);
  6. 删除dev‑ns命名空间。

验证点

  1. kubectl get ns可以看到dev‑ns;
  2. 不指定‑n,kubectl get pod 能看到 nginx pod;
  3. kubens 切换 default 后,直接 get pod 看不到 dev‑ns 的 pod;
  4. 删除 ns 后,该 ns 下 pod 自动被清除。

实验题 2:Kubernetes Pod 基础管理

章节:Kubernetes Pod(pod 基本管理、多容器 pod)

  1. 创建命名空间pod‑lab,设置上下文默认 ns 为pod‑lab;

  2. 编写 yaml 创建多容器 Pod,pod 名称

    复制代码
    two‑container‑pod
    • 容器 1:nginx,name:web,镜像nginx:alpine;
    • 容器 2:busybox,name:client,镜像busybox:1.36,command 执行 sleep 3600 保持运行;
  3. 使用kubectl get pod -o wide查看 pod 状态与 podIP;确认状态2/2 Running;

  4. 在 client 容器内部,使用wget 127.0.0.1访问同 pod 内 nginx 服务;

  5. 将 master 节点/etc/hosts文件复制到 pod 的 web 容器内/tmp/hosts路径;

  6. 查看 nginx 容器日志;进入 client 容器执行 hostname;

  7. 删除 pod,观察 terminating 状态。

今天课堂内容

  1. 静态 pod
  2. 编写 yaml 文件
  3. Kubernetes Volume
  4. Kubernetes Configure

今天课后作业

  1. 完成课堂笔记中实验。
  2. 梳理今天的课堂笔记,并写到博客中。
  3. 预习:

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: false
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-with-env-from-configmap.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-with-env-from-configmap.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-with-volume-from-configmap.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-with-volume-from-configmap.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 cm -n kube-system kubeadm-config -o yaml
apiVersion: v1
data:
  ClusterConfiguration: |
    apiServer:
      timeoutForControlPlane: 4m0s
    apiVersion: kubeadm.k8s.io/v1beta3
    certificatesDir: /etc/kubernetes/pki
    clusterName: kubernetes
    controllerManager: {}
    dns: {}
    etcd:
      local:
        dataDir: /var/lib/etcd
    imageRepository: registry.k8s.io
    kind: ClusterConfiguration
    kubernetesVersion: v1.30.2
    networking:
      dnsDomain: cluster.local
      podSubnet: 10.224.0.0/16
      serviceSubnet: 10.96.0.0/12
    scheduler: {}
kind: ConfigMap
metadata:
  creationTimestamp: "2026-06-23T08:50:46Z"
  name: kubeadm-config
  namespace: kube-system
  resourceVersion: "202"
  uid: 7c5cc481-8de7-4751-9c62-48529e2f7baa

综合案例:haproxy+web

需求:

  1. 创建一个名称为 haproxy 的pod,使用镜像 haproxy ,pod haproxy 的配置文件保存在 configmap 中,通过 volume 方式挂载到 /usr/local/etc/haproxy/haproxy.cfg 。将流量转发到pod webapp-1 和 webapp-2。

haproxy 的配置文件模板内容如下:

ini 复制代码
global
    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
  1. 创建第一个pod,名称为webapp-1 ,使用镜像nginx 。为该pod创建一个cm,名称为webapp-1,该cm保存两个值:
  • index.html="hello webapp-1"
  • error.html="sorry, error."

这个两个值通过volume方式挂载到**/usr/share/nginx/html**目录。

  1. 创建第二个pod,名称为webapp-2 ,使用镜像nginx 。为该pod创建一个cm,名称为webapp-2,该cm保存两个值:
  • index.html="hello webapp-2"
  • error.html="sorry, error."

这个两个值通过volume方式挂载到**/usr/share/nginx/html**目录。

解答:

  1. 创建第一个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.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: webapp-1
  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-1
bash 复制代码
root@master30:~/web# kubectl apply -f pod-webapp-1.yaml
  1. 创建第二个cm和pod
bash 复制代码
root@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.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: webapp-2
  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-2
bash 复制代码
root@master30:~/web# kubectl apply -f pod-webapp-2.yaml
  1. 创建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.cfg
ini 复制代码
global
    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
bash 复制代码
# 创建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.yaml
yaml 复制代码
---
apiVersion: v1
kind: Pod
metadata:
  name: haproxy
spec:
  containers:
  - name: haproxy
    image: hub.laoma.cloud/library/haproxy
    imagePullPolicy: IfNotPresent
    securityContext:
      allowPrivilegeEscalation: true
    volumeMounts:
    - name: config
      mountPath: "/usr/local/etc/haproxy"
      readOnly: true
  volumes:
  - name: config
    configMap:
      name: haproxy.cfg
bash 复制代码
root@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=admin@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=admin@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-with-env-from-secret.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-with-env-from-secret.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-with-volume-from-secret.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-with-volume-from-secret.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应用。要求如下:

  1. 部署数据库应用:mysql 相关用户和密码使用 secret 存储。

  2. blog 站点:

环境清理

bash 复制代码
root@master30:~# kubectl delete ns configure
相关推荐
code_slave(码畜)3 小时前
微服务架构落地:读写分离架构 —— 主从延迟引发的“刚开单查不到”灾难与破局
spring boot·分布式·spring cloud·微服务·云原生·架构·读写分离
你看我名字4 小时前
LAMP 架构演练:从云主机准备到 Apache、PHP、MySQL 上线验证
运维·ci/cd·架构·kubernetes·jenkins
丨晨 丨6 小时前
K8s存储卷与配置管理实战
云原生·容器·kubernetes
暖核6 小时前
K8s 存储与配置:Volume PV‑PVC ConfigMap Secret 实战解析
云原生·容器·kubernetes
每天一道题6 小时前
从单体到微服务:Service、Instance 与服务拆分到底是什么关系?
后端·微服务·云原生·架构
木雷坞7 小时前
Docker Compose端口映射排查:用port和inspect定位网页访问失败
运维·docker·容器
jonyleek20 小时前
JVS数字底座私有化部署实践:Spring Cloud + K8S 实现文档平台开箱即用
elasticsearch·kubernetes·私有化部署·springcloud·minio·jvs企业文档·文档治理
阿里云云原生21 小时前
欢迎报名丨Alibaba Cloud AgentCore 八城巡回沙龙上海首站开启
云原生
阿里云云原生1 天前
AgentTeams Dashboard:统一管理团队、项目与资产
云原生·agent