k8s 学习笔记之 k8s 存储管理

文章目录

  • 概述
    • 卷的常用类型
    • emptyDir
    • HostPath
    • nfs
    • PV/PVC
      • [静态供给 PV 和 PVC](#静态供给 PV 和 PVC)
        • [创建静态 PV](#创建静态 PV)
        • [创建 pvc](#创建 pvc)
        • [创建 pod 应用 pvc](#创建 pod 应用 pvc)
      • [动态供给 PV 和 PVC](#动态供给 PV 和 PVC)
        • [创建 StorageClass](#创建 StorageClass)
        • [创建 pvc](#创建 pvc)
        • [创建 pod 使用 pvc](#创建 pod 使用 pvc)
      • [PV 的生命周期](#PV 的生命周期)
    • 内置存储对象
  • 配置文件自动重新加载方案
      • [**1. 应用内动态检测文件变更**](#1. 应用内动态检测文件变更)
      • [**2. 通过信号触发重新加载**](#2. 通过信号触发重新加载)
      • [**3. 使用 Reloader 或类似工具**](#3. 使用 Reloader 或类似工具)
      • [**4. 手动重启 Pod**](#4. 手动重启 Pod)
  • 拉取镜像的脚本

概述

在虚拟机的环境中,应用程序的数据通常存储在本地磁盘上,即使重启虚拟机也不会数据丢失。但是在 pod 中。pod 的特点就是 "临时性" ,随着 pod 的重建,容器中的数据也会消失,这将导致一些应用程序读不到之前的数据。因此 pod 引入了持久化这个概念,也就是 "卷"

卷是 pod 中存储数据和共享数据的一个抽象概念。它提供了一种将存储设备挂载进容器的机制。

卷的常用类型

卷的分类 卷类型 说明
临时存储 emptyDir 用于 pod 中,容器之间的共享
本地存储 hostPath 将节点文件系统上的文件或者目录挂载到 pod 中
对象存储 ConfigMap,Secret k8s 内置的存储对象,用于存储应用程序配置和敏感数据
自建存储系统 NFS,Ceph,ISCSI 将自建的存储系统挂载到 pod 中
存储对象 persistentVolumesClaim(PVC) 与 PV 持久卷配合使用

emptyDir

empytDir 用于在 pod 中实现容器之间的数据共享,与 pod 的生命周期一致,当 pod 被删除时,对应的目录也会销毁

BASH 复制代码
[root@k8s-master ~]# cat emptyDir.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-emptydir
spec:
  containers:
  - image: docker.io/library/centos:latest
    imagePullPolicy: IfNotPresent
    name: app
    command: ["/bin/sh","-c","for i in {1..10000};do echo $i >> /opt/file.txt;sleep 1;done"]
    volumeMounts:
    - name: data
      mountPath: /opt
  - image: docker.io/library/centos:latest
    imagePullPolicy: IfNotPresent
    name: sidecar # 边车容器
    command: ["/bin/sh","-c","tail -f /opt/file.txt"]
    volumeMounts:
    - name: data
      mountPath: /opt
  volumes:
  - name: data
    emptyDir: {}
[root@k8s-master ~]# kubectl apply -f emptyDir.yaml 
pod/pod-emptydir created
[root@k8s-master ~]# kubectl get pod
NAME                                       READY   STATUS    RESTARTS       AGE
pod-emptydir                               2/2     Running   0              3s

在上面的例子中,我们定义了两个容器和一个 emptyDir 卷,该卷被挂载到两个容器中的同一个目录下了,因此该目录中的文件可以被彼此访问

BASH 复制代码
[root@k8s-master ~]# kubectl exec -it pod-emptydir --container app -- /bin/bash
Defaulted container "app" out of: app, sidecar
[root@pod-emptydir /]# cd opt/
[root@pod-emptydir opt]# ls
file.txt
BASH 复制代码
[root@k8s-master ~]# kubectl exec -it pod-emptydir --container sidecar -- /bin/bash
[root@pod-emptydir /]# cd opt/
[root@pod-emptydir opt]# ls
file.txt

边车容器

边车容器(Sidecar Container)是与主应用容器在同一个Pod中运行的辅助容器。它们通过提供额外的服务或功能(如日志记录、监控、安全性或数据同步)来增强或扩展主应用容器的功能,而无需直接修改主应用代码。边车容器与主容器共享网络和存储命名空间,使得它们能够紧密交互并共享资源。

除了边车容器,Kubernetes还支持其他类型的容器,包括:

  1. 标准容器(Application Containers):这是最常见的容器类型,用于运行主要的应用逻辑。

  2. Init 容器:这些容器在应用容器启动之前运行,用于执行一些初始化任务,比如设置配置文件或者等待外部服务就绪。Init 容器在Pod中的所有应用容器启动前完成执行并退出。

  3. Ephemeral 容器:这是一种临时性的容器,它们缺少对资源或执行的保证,并且永远不会自动重启。Ephemeral 容器主要用于调试目的,允许用户加入一个临时容器到正在运行的Pod中,用于调试。

HostPath

hostpath 卷用来将宿主机的目录挂载进容器,这使得容器可以访问宿主机的数据,由于挂载的是宿主机的目录,因此在容器被销毁后,数据并不会丢失。

但是在 k8s 卷的分类中,我们还有一个专门做持久卷的,名叫 PV/PVC 。那这两种挂载方式到底差距在哪呢:

HostPath:

  • 直接挂载宿主机本地路径到容器,适合单节点和开发环境。
  • 不支持跨节点存储,数据与特定节点绑定。
  • 无自动扩展功能,存储容量和管理完全依赖宿主机。
  • 没有 Kubernetes 对存储的生命周期管理,容器删除后数据可能丢失。

PV/PVC:

  • Kubernetes 管理的持久化存储,可以通过 PVC 请求动态存储资源。
  • 支持跨节点、云存储等多种后端,具备高可用性和扩展性。
  • 具备自动扩展、容量管理和存储生命周期管理功能。
  • 支持不同存储策略(如保留、删除等),适用于生产环境。

那我们在 yaml 文件中如何挂载它呢?

BASH 复制代码
[root@k8s-master ~]# cat hostPath.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
  - name: hostpath-container
    image: docker.io/library/nginx:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: hostpath-volume
      mountPath: /usr/share/nginx/html
  volumes:
  - name: hostpath-volume
    hostPath:
      path: /data  # 这会挂载宿主机的 /data 目录到容器的 /usr/share/nginx/html 目录
      type: DirectoryOrCreate
BASH 复制代码
[root@k8s-master ~]# kubectl get pod
NAME                                       READY   STATUS    RESTARTS       AGE
hostpath-pod                               1/1     Running   0               4s

在该 yaml 文件中,我们将 /usr/share/nginx/html 挂载到容器里面的 data 目录下了

hostPath 所支持的卷的类型 (这些参数在 yaml 文件中有对应)

取值 作用
"" 该字段为空或者未指定,默认是 DirectoryOrCreate
DirectoryOrCreate 如果指定的目录不存在,则会自动创建空目录并为其赋值 0755
Directory 指定的目录必须存在
FileOrCreate 和上面那个差不多,在空文件被创建出来之后默认赋权 0644
File 指定文件必须存在
Socket 指定套接字文件必须存在
CharDevice 指定的字符设备必须存在
BlockDevice 指定的块设备必须存在

k8s 在 pod 启动时会检查路径是否与期望类型所匹配,如果不匹配或者类型检查异常,pod 会呈现 ContainerCreateing 状态

hostpath 卷不支持存储容器限制,并且可使用的存储容量受主机文件系统限制

nfs

将 nfs 服务器挂载到 pod 中,实现 pod 之间的数据共享。我们在之前还提到过一个 emptyDir 和 nfs 挂载的实现的功能是一致的,那他们的区别在哪呢?

NFSemptyDir 的主要区别是:

  • 持久性
    • NFS:数据持久化,Pod 删除后数据保留。
    • emptyDir:数据临时存储,Pod 删除时数据丢失。
  • 共享范围
    • NFS:可以跨多个节点和 Pod 共享数据。
    • emptyDir:仅限单个 Pod 内的容器共享数据。
  • 适用场景
    • NFS:需要跨 Pod 和节点共享数据的场景。
    • emptyDir:适合 Pod 内部容器间共享临时数据。

pv对接nfs共享,使用静态创建的方式创建pvc

JSON 复制代码
[root@k8s-master ~]# cat nginx.json 
{
  "apiVersion": "apps/v1",
  "kind": "Deployment",
  "metadata": {
    "name": "nginx"
  },
  "spec": {
    "selector": {
      "matchLabels": {
        "app": "nginx"
      }
    },
    "template": {
      "metadata": {
        "labels": {
          "app": "nginx"
        }
      },
      "spec": {
        "containers": [
          {
            "image": "docker.io/library/nginx:latest",
            "imagePullPolicy": "IfNotPresent",
            "name": "nginx",
            "volumeMounts": [
              {
                "name": "data",
                "mountPath": "/data"
              }
            ]
          }
        ],
        "volumes": [
          {
            "name": "data",
            "nfs": {
              "server": "192.168.142.139",
              "path": "/data/nfs"
            }
          }
        ]
      }
    }
  }
}
BASH 复制代码
mkdir -p /data/nfs
chmod 777 /data/nfs/
vim /etc/exports
/data/nfs *(no_root_squash,rw,no_all_squash)
systemctl restart nfs-server.service
showmount -e 192.168.142.139

[root@k8s-slave1 ~]# df -h | grep 192.168.142.139:/data/nfs
df: /var/lib/kubelet/pods/6210f716-6da2-4a14-b320-33b169b684bc/volumes/kubernetes.io~nfs/mypv1: Stale file handle
192.168.142.139:/data/nfs   17G  6.3G   11G  38% /var/lib/kubelet/pods/fc2ccf93-693b-44b8-93f7-6d60f2f1b412/volumes/kubernetes.io~nfs/data

PV/PVC

在之前,我们已经简单的介绍了一下 PV/PVC 与 hostPath 之间的对比,也是知道了 PV/PVC 存在的意义,那么什么是 PV ? 什么是 PVC ?

持久卷(PV)就是 Kubernetes 用来管理集群中存储的工具,它让你不用关心底层存储的具体实现,只要把它用作存储就行。简单来说,就是让你的数据在 Pod 重启或销毁后还能保留下来。

持久卷声明(PVC)是用户申请存储的方式,简单来说,就是你向 Kubernetes 请求一个存储空间。PVC 就像是一个 "存储请求单",你告诉 Kubernetes 需要多大的存储空间,Kubernetes 会找一个合适的持久卷(PV)来满足这个请求。

你可以把 PVPVC 想象成 房子租房合同 的关系:

  • PV(持久卷) 就是一个已经建好的 房子,它有一定的空间和资源,可能是物理硬盘、NFS 共享或云存储等。

  • PVC(持久卷声明) 就是你去 租房 ,向 Kubernetes 提出需要多大面积的房子,Kubernetes 会找一个合适的 房子(PV) 给你。

所以,PVC 是你用来申请 PV 的工具,PVC 就像是租房合同,PV 是你租到的房子。

在 PV/PVC 的世界里还有两个非常重要的概念:PV 静态供给,PV 动态供给

  • 静态供给 :管理员手动创建好 PV(持久卷) ,然后用户通过 PVC(持久卷声明) 来请求匹配的 PV。这种方式下,管理员负责管理存储资源。

  • 动态供给 :当用户创建 PVC 时,Kubernetes 会根据 StorageClass 自动创建一个符合要求的 PV。这种方式下,管理员不需要事先创建 PV,Kubernetes 会自动提供存储。

下面是两个示例:

静态供给 PV 和 PVC

创建静态 PV
BASH 复制代码
[root@k8s-master ~]# cat pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: static-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
     - ReadWriteOnce  # 只能被一个节点挂载
  persistentVolumeReclaimPolicy: Retain  # PV 删除后数据保留
  storageClassName: standard
  hostPath:
    path: /data/static-pv  # 宿主机路径

persistentVolumeReclaimPolicy 参数有三个值可以选

  • Retain:保留数据。PV 被释放后,存储资源不会被回收,管理员需要手动处理该 PV。数据保留在原位置,可以进行手动清理或重新绑定到新的 PVC。

  • Recycle (已弃用,不再推荐使用):回收数据。在这种模式下,PV 被释放后,Kubernetes 会尝试清除存储中的数据(通常是执行 rm -rf /some-directory/* 操作),然后将 PV 设置为可以重新绑定的状态。这种方式已被 Kubernetes 弃用。

  • Delete:删除数据。PV 被释放后,Kubernetes 会自动删除与 PV 相关联的存储资源(如删除一个云存储卷)。数据将被永久删除。

创建 pvc
BASH 复制代码
[root@k8s-master ~]# cat pvc.yaml
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: static-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi  # 请求10GB的存储
  storageClassName: standard
创建 pod 应用 pvc

pull-image.sh 是我自己编写的脚本,专门用来拉取镜像的,如果小伙伴们也是 container 无法拉取镜像,不妨可以试一试我这个脚本,脚本放到最后

BASH 复制代码
[root@k8s-master ~]# ./pull-image.sh nginx:latest 192.168.142.139 192.168.142.140 192.168.142.141
镜像 nginx:latest 已存在于本地
Docker save nginx:latest
Docker save nginx:latest is successful
Sending nginx-latest.tar to 192.168.142.139
root@192.168.142.139's password: 
nginx-latest.tar                                                                         100%  187MB 947.0MB/s   00:00    
Successfully sent nginx-latest.tar to 192.168.142.139
root@192.168.142.139's password: 
镜像 nginx:latest 已存在于 192.168.142.139
Sending nginx-latest.tar to 192.168.142.140
root@192.168.142.140's password: 
nginx-latest.tar                                                                         100%  187MB 355.8MB/s   00:00    
Successfully sent nginx-latest.tar to 192.168.142.140
root@192.168.142.140's password: 
导入镜像到 192.168.142.140
root@192.168.142.140's password: 
unpacking docker.io/library/nginx:latest (sha256:466e72df2f0b10ecb0dc90dc99d523a1c6432b764ebcbcdab3f0a7ef5cd4e061)...done
ctr import nginx-latest.tar is successful on 192.168.142.140
Sending nginx-latest.tar to 192.168.142.141
root@192.168.142.141's password: 
nginx-latest.tar                                                                         100%  187MB 325.1MB/s   00:00    1
Successfully sent nginx-latest.tar to 192.168.142.141
root@192.168.142.141's password: 
导入镜像到 192.168.142.141
root@192.168.142.141's password: 
unpacking docker.io/library/nginx:latest (sha256:466e72df2f0b10ecb0dc90dc99d523a1c6432b764ebcbcdab3f0a7ef5cd4e061)...done
ctr import nginx-latest.tar is successful on 192.168.142.141
BASH 复制代码
[root@k8s-master ~]# cat pod1.yaml 
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-pod
spec:
  containers:
  - name: nginx
    image: docker.io/library/nginx:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: static-storage
  volumes:
  - name: static-storage
    persistentVolumeClaim:
      claimName: static-pvc
BASH 复制代码
[root@k8s-master ~]# kubectl apply -f pod1.yaml 
pod/static-pod created
[root@k8s-master ~]# kubectl get pod
static-pod                                 1/1     Bound   0                5s

以上就是静态供给了,但是相较于静态供给,还是动态供给香多了

动态供给 PV 和 PVC

在动态供给中,管理员配置了存储类(StorageClass),Kubernetes 会根据 PVC 的需求动态创建一个 PV。

创建 StorageClass
BASH 复制代码
# storage-class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: dynamic-storage
provisioner: kubernetes.io/aws-ebs  # 使用 AWS EBS 或者可以是其他存储提供商
parameters:
  type: gp2
创建 pvc
BASH 复制代码
[root@k8s-master ~]# cat dynamic-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dynamic-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: dynamic-storage  # 确保 PVC 引用正确的 StorageClass
创建 pod 使用 pvc
BASH 复制代码
[root@k8s-master ~]# cat pod-dynamic.yaml
# pod-dynamic.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dynamic-pod
spec:
  containers:
  - name: nginx
    image: docker.io/library/nginx:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: dynamic-storage
  volumes:
  - name: dynamic-storage
    persistentVolumeClaim:
      claimName: dynamic-pvc

最后的结果和上面静态供给是差不多的

演示到这里,pv / pvc 大概就是这么用的

PV 的生命周期

最后,我们再来聊一聊 pv 的生命周期:pv 的生命周期包含多个阶段:

  1. Provisioning PV 供给: 可以通过静态供给和动态供给创建 PV
  2. Binding 绑定: 在 PV 创建后,并且处于 available 状态时,PVC 与 PV 绑定,此时 PV 就会转换成 Bound
  3. Using 使用: 当 PVC 与 PV 成功绑定之后,Pod 获取到 PV 的存储资源,从而将容器中的数据存储到外部的存储系统中
  4. Releasing 释放: 当 PVC 被删除时 , PV 也会随之被删除,具体行为由回收策略决定。
  5. Reclaiming 回收: 释放后,根据回收策略执行相应的操作,具体的操作就是上面讲到的 Retain ,Recycle ,Delete

另外,动态供给创建的 pv ,回收策略默认使用的是 Delete,但是这个可以在 StorageClass 的 yaml 文件中通过 reclaimPolicy 参数进行修改

内置存储对象

ConfigMap

它是一种专门用来存储各种配置文件的 pod,它以键值对的形式存储保存数据。

例如 nginx 的配置文件等等

下面是一个简单的示例:

BASH 复制代码
[root@k8s-master ~]# cat nginx-proxy-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  a.conf: |
    server {
      listen 80;
      server_name a.example.com;
      location / {
        proxy_pass http://192.168.142.139:8080
      }
    }

当我们看到 data 的时候是不是似曾相识,这个就是 nginx 反向代理的配置文件

BASH 复制代码
[root@k8s-master ~]# kubectl apply -f nginx-proxy-configmap.yaml 
configmap/nginx-config created
[root@k8s-master ~]# kubectl get cm
NAME                             DATA   AGE
kube-root-ca.crt                 1      11d
nginx-config                     1      7s

那 ConfigMap 如何配合 PV 使用呢?

BASH 复制代码
# nginx-proxy-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-proxy
spec:
  containers:
  - name: web
    image: docker.io/library/nginx:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
      - name: config
        mountPath: /etc/nginx/conf.d # 将 nginx 的配置文件挂载进容器
  volumes:
    - name: config
      configMap:
        name: nginx-config
BASH 复制代码
[root@k8s-master ~]# kubectl get pod
nginx-proxy                                1/1     Running   0                46s

[root@k8s-master ~]# kubectl exec nginx-proxy -- ls /etc/nginx/conf.d a.conf
/etc/nginx/conf.d:
a.conf

每次挂载新的配置文件都有一个等待的过程,在这个过程中,存在一种自动更新的机制。kubelet 组件会定期检查 pod 挂载的 ConfigMap 对象中的数据是否发生变更,如果发生变更,就会将最新的配置文件再次加载到容器中去,以确保 pod 始终是最新的配置。

如果只想挂载指定的 键 或者 文件名,我们还可以这样:

YAML 复制代码
volumes:
- name: config
  configMap:
    name: nginx-config
    items:
    - key: "a.conf"
      path: "a.example.com.conf"

这样,只有 ConfigMap 中键为 a.conf 的数据会被挂载到容器中。

还有一件事,默认情况下 /etc/nginx/conf.d 目录下是会有一个 defalut.conf 文件的,但是当我们将 a.conf 挂载进去之后,它就不见了,这其实是因为我们的挂载操作是覆盖操作。然而,我们要如何避免这种事情呢?

YAML 复制代码
volumeMounts:
- name: config
  mountPath: /etc/nginx/conf.d/a/example.com.conf
  subPath: a.example.com.conf
volumes:
- name: config
  configMap:
    name: nginx-config
    items:
    - key: "a.conf"
      path: "a.example.com.conf"

但是要注意的是,当我们使用了 subPath 之后,kubelet 不会自动更新新的数据到 pod 中

Secret

它比较敏感,因为它主要负责存储一些特别重要的信息,比如找密码,密钥证书等。

secret 支持三种类型

  • Opaque(默认类型):

    • 描述 :最常用的 Secret 类型。用于存储任意的键值对数据,这些数据会被以 base64 编码的形式存储。
    • 示例用途:存储用户名和密码、API 密钥等。
    • 默认类型 :如果在 Secret 的定义中没有指定类型,Kubernetes 会默认使用 Opaque 类型。
BASH 复制代码
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: bXl1c2VybmFtZQ==  # base64 编码的用户名
  password: bXlwYXNzd29yZA==  # base64 编码的密码
  • kubernetes.io//service-account-token:

    • 描述 :此类型的 Secret 存储的是服务账户的令牌,它会自动由 Kubernetes 控制平面创建和管理。
    • 示例用途:为 pod 提供与 Kubernetes API 通信所需的身份验证令牌。
    • 创建方式 :当你创建服务账户时,Kubernetes 会自动创建与该账户关联的 Secret
BASH 复制代码
apiVersion: v1
kind: Secret
metadata:
  name: default-token-abcde
  annotations:
    kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
data:
  token: <base64 encoded token>
  ca.crt: <base64 encoded ca.crt>
  • kubernetes.io//dockercfgkubernetes.io//dockerconfigjson:

    • 描述 :这两种类型的 Secret 用于存储 Docker 配置文件,通常用于存储 Docker 仓库的认证信息。
    • 示例用途 :在 Secret 中存储 Docker 的认证信息,以便 Kubernetes 能够从私有 Docker 仓库拉取镜像。
    • 区别kubernetes.io/dockercfg 存储 .dockercfg 配置文件,而 kubernetes.io/dockerconfigjson 存储的是 Docker 配置 JSON 文件(更现代的格式)。

    示例 kubernetes.io/dockerconfigjson

BASH 复制代码
apiVersion: v1
kind: Secret
metadata:
  name: my-docker-secret
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <base64 encoded docker config json>

当然在 k8s 的一些老版本中是这第三个:docker-registry ,generic 以及 tls

  • Kubernetes 1.2 是 Secret 类型支持的重要版本:Opaque(默认类型)、kubernetes.io/service-account-tokenkubernetes.io/dockercfg 同时被引入。
  • Kubernetes 1.9 开始推荐使用 kubernetes.io/dockerconfigjson,这是更现代的私有镜像认证方式。
  • 当然这两种方式只是 不同表达方式,它们并没有严格意义上的更新替换,只是在文档和实践中有些变化。

配置文件自动重新加载方案

当 ConfigMap 和 Secret 以卷的方式被挂载到容器中时,如果它们发生改变,那么最新的数据也会被更新到 pod 中。为了使得最新的配置生效,应用程序还需要有自动检测和处理变更的能力,具体的实现方式有以下几种:

1. 应用内动态检测文件变更

  • 原理:应用程序自行监控挂载文件的变更,并动态加载最新内容。
  • 实现方式
    • 使用文件监听工具(如 inotify)。
    • 定期轮询挂载路径,检测文件更新。
  • 适用场景:轻量级服务,业务逻辑明确,适合直接修改应用代码。

2. 通过信号触发重新加载

  • 原理 :监听指定信号(如 SIGHUP),触发配置重新加载。
  • 实现方式
    • 在应用程序内设置信号处理器。
    • 配合文件挂载变更,使用工具发送信号:
BASH 复制代码
kill -SIGHUP <PID>
  • 适用场景:对应用进行小幅改动即可实现,适合中等规模服务。

3. 使用 Reloader 或类似工具

  • 原理:借助外部工具监控 ConfigMap 或 Secret 变更,触发 Pod 滚动更新。
  • 实现方式
    • 部署 Reloader(或类似工具)。
    • 在 Pod 上配置注解:
BASH 复制代码
metadata:
  annotations:
    reloader.stakater.com/match: "true"
  • 适用场景:需要在更新时完全重启服务,适合大型分布式服务。

4. 手动重启 Pod

  • 原理:手动触发 Pod 滚动更新,以应用最新配置。
  • 实现方式
    • 修改 Deployment 或 StatefulSet 的 annotation:
BASH 复制代码
kubectl patch deployment <name> -p '{"spec":{"template":{"metadata":{"annotations":{"date":"<current-timestamp>"}}}}}'
  • 适用场景:非频繁更新情况下的简易方案。

拉取镜像的脚本

仅支持检测 tar 包,具体的使用方法,开袋即食,跑完脚本就直接写 yaml 文件就可以了,前提是我们要下 yaml 文件的 image 下再加一条 imagePullPolicy: IfNotPresent / Never 。然后还有一点要注意的就是如果本地有镜像,并且已经 docker save 了,那么镜像务必使 tar 结尾,不然脚本会重新为你 docker save ,然后就是具体的使用方法,在上面,已经有示范了

#!/bin/bash

# 检查 Docker 是否安装
check_docker_installed() {
    if ! docker -v > /dev/null 2>&1; then
        echo "Docker is not installed"
        exit 1
    fi
}

# 检查是否提供了足够的参数
check_args() {
    if [ "$#" -lt 2 ]; then
        echo "Usage: $0 <image-name> <host1> [<host2> ...]"
        exit 1
    fi
}

# 检查本地是否存在指定的镜像
check_local_image() {
    local image_name=$1
    if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "$image_name"; then
        echo "镜像 $image_name 已存在于本地"
        return 0
    else
        echo "正在拉取 Docker 镜像 $image_name..."
        if docker pull "$image_name" > /dev/null 2> pull.log; then
            echo "Docker 镜像 $image_name 拉取成功"
            return 0
        else
            echo "Docker 镜像 $image_name 拉取失败。详情查看 pull.log。"
            exit 1
        fi
    fi
}

# 检查 tar 文件是否存在
check_tar_file() {
    local image_name=$1
    local tar_file=$2
    if [ -f "$tar_file" ]; then
        echo "镜像文件 $tar_file 已经存在"
        return 0
    else
        echo "Docker save $image_name"
        if docker save -o "$tar_file" "$image_name" 2> save.log; then
            echo "Docker save $image_name is successful"
            return 0
        else
            echo "Docker save $image_name failed. Check save.log for details."
            exit 1
        fi
    fi
}

# 发送和导入镜像到其他主机
send_and_import_image() {
    local image_name=$1
    local tar_file=$2
    shift 2 # 移动参数,使得 $@ 包含剩余的主机地址
    for host in "$@"; do
        echo "Sending $tar_file to $host"
        scp "$tar_file" "root@$host:/root/" && echo "Successfully sent $tar_file to $host" || echo "Failed to send $tar_file to $host"
        # 检查目标主机上是否存在镜像
        if ssh root@$host "docker images --format '{{.Repository}}:{{.Tag}}' | grep -q '$image_name'"; then
            echo "镜像 $image_name 已存在于 $host"
        else
            echo "导入镜像到 $host"
            if ssh root@$host "ctr -n k8s.io images import /root/$tar_file"; then
                echo "ctr import $tar_file is successful on $host"
            else
                echo "ctr import $tar_file failed on $host"
            fi
        fi
    done
}

# 主函数
main() {
    check_docker_installed
    check_args "$@"

    local image_name=$1
    local iso="${image_name//:/-}"
    local tar_file="$iso.tar"

    check_local_image "$image_name"
    check_tar_file "$image_name" "$tar_file"
    send_and_import_image "$image_name" "$tar_file" "${@:2}"
}

# 调用主函数
main "$@"
相关推荐
Shall#27 分钟前
Unix进程
linux·笔记·unix
m0_736180442 小时前
20221403郑骁恒 第十周预习报告
学习
wusong9992 小时前
InfluxDB时序数据库笔记(一)
笔记·时序数据库·influxdb
海盗猫鸥3 小时前
常见排序算法
学习·算法·排序算法
梨子串桃子_3 小时前
物联网通信技术及应用 | 第七章 NB-IoT与LoRa通信技术 | 自用笔记
笔记·物联网·学习
q_973 小时前
k8s搭建1.23版本
linux·云原生·容器·kubernetes
我是汉堡请多指教3 小时前
Unity学习---IL2CPP打包时可能遇到的问题
学习·unity·游戏引擎
致奋斗的我们3 小时前
RHCE的学习(21)
linux·学习·shell·redhat·rhce·rhcsa
虾球xz3 小时前
游戏引擎学习第16天
学习·microsoft·游戏引擎
虾球xz3 小时前
游戏引擎学习第13天
android·学习·游戏引擎