k8s 学习笔记之 k8s 存储管理

embedded/2024/11/21 12:13:39/

文章目录

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

概述

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

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

卷的常用类型

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

emptyDir

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

[root@k8s-master ~]# cat emptyDir.yaml 
apiVersion: v1
kind: Pod
metadata:name: pod-emptydir
spec:containers:- image: docker.io/library/centos:latestimagePullPolicy: IfNotPresentname: appcommand: ["/bin/sh","-c","for i in {1..10000};do echo $i >> /opt/file.txt;sleep 1;done"]volumeMounts:- name: datamountPath: /opt- image: docker.io/library/centos:latestimagePullPolicy: IfNotPresentname: sidecar # 边车容器command: ["/bin/sh","-c","tail -f /opt/file.txt"]volumeMounts:- name: datamountPath: /optvolumes:- name: dataemptyDir: {}
[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 卷,该卷被挂载到两个容器中的同一个目录下了,因此该目录中的文件可以被彼此访问

[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
[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 文件中如何挂载它呢?

[root@k8s-master ~]# cat hostPath.yaml 
apiVersion: v1
kind: Pod
metadata:name: hostpath-pod
spec:containers:- name: hostpath-containerimage: docker.io/library/nginx:latestimagePullPolicy: IfNotPresentvolumeMounts:- name: hostpath-volumemountPath: /usr/share/nginx/htmlvolumes:- name: hostpath-volumehostPath:path: /data  # 这会挂载宿主机的 /data 目录到容器的 /usr/share/nginx/html 目录type: DirectoryOrCreate
[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

[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"}}]}}}
}
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
[root@k8s-master ~]# cat pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:name: static-pv
spec:capacity:storage: 10GiaccessModes:- ReadWriteOnce  # 只能被一个节点挂载persistentVolumeReclaimPolicy: Retain  # PV 删除后数据保留storageClassName: standardhostPath:path: /data/static-pv  # 宿主机路径

persistentVolumeReclaimPolicy 参数有三个值可以选

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

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

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

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

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

[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
[root@k8s-master ~]# cat pod1.yaml 
# pod.yaml
apiVersion: v1
kind: Pod
metadata:name: static-pod
spec:containers:- name: nginximage: docker.io/library/nginx:latestimagePullPolicy: IfNotPresentvolumeMounts:- mountPath: /usr/share/nginx/htmlname: static-storagevolumes:- name: static-storagepersistentVolumeClaim:claimName: static-pvc
[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
# 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
[root@k8s-master ~]# cat dynamic-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: dynamic-pvc
spec:accessModes:- ReadWriteOnceresources:requests:storage: 1GistorageClassName: dynamic-storage  # 确保 PVC 引用正确的 StorageClass
创建 pod 使用 pvc
[root@k8s-master ~]# cat pod-dynamic.yaml
# pod-dynamic.yaml
apiVersion: v1
kind: Pod
metadata:name: dynamic-pod
spec:containers:- name: nginximage: docker.io/library/nginx:latestimagePullPolicy: IfNotPresentvolumeMounts:- mountPath: /usr/share/nginx/htmlname: dynamic-storagevolumes:- name: dynamic-storagepersistentVolumeClaim: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 的配置文件等等

下面是一个简单的示例:

[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 反向代理的配置文件

[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 使用呢?

# nginx-proxy-pod.yaml
apiVersion: v1
kind: Pod
metadata:name: nginx-proxy
spec:containers:- name: webimage: docker.io/library/nginx:latestimagePullPolicy: IfNotPresentvolumeMounts:- name: configmountPath: /etc/nginx/conf.d # 将 nginx 的配置文件挂载进容器volumes:- name: configconfigMap:name: nginx-config
[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 始终是最新的配置。

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

volumes:
- name: configconfigMap:name: nginx-configitems:- key: "a.conf"path: "a.example.com.conf"

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

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

volumeMounts:
- name: configmountPath: /etc/nginx/conf.d/a/example.com.confsubPath: a.example.com.conf
volumes:
- name: configconfigMap:name: nginx-configitems:- key: "a.conf"path: "a.example.com.conf"

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

Secret

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

secret 支持三种类型

  • Opaque(默认类型):

    • 描述:最常用的 Secret 类型。用于存储任意的键值对数据,这些数据会被以 base64 编码的形式存储。
    • 示例用途:存储用户名和密码、API 密钥等。
    • 默认类型:如果在 Secret 的定义中没有指定类型,Kubernetes 会默认使用 Opaque 类型。
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
apiVersion: v1
kind: Secret
metadata:name: default-token-abcdeannotations: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//dockercfg 和 kubernetes.io//dockerconfigjson:

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

    示例 kubernetes.io/dockerconfigjson

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),触发配置重新加载。
  • 实现方式
    • 在应用程序内设置信号处理器。
    • 配合文件挂载变更,使用工具发送信号:
kill -SIGHUP <PID>
  • 适用场景:对应用进行小幅改动即可实现,适合中等规模服务。

3. 使用 Reloader 或类似工具

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

4. 手动重启 Pod

  • 原理:手动触发 Pod 滚动更新,以应用最新配置。
  • 实现方式
    • 修改 Deployment 或 StatefulSet 的 annotation:
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; thenecho "Docker is not installed"exit 1fi
}# 检查是否提供了足够的参数
check_args() {if [ "$#" -lt 2 ]; thenecho "Usage: $0 <image-name> <host1> [<host2> ...]"exit 1fi
}# 检查本地是否存在指定的镜像
check_local_image() {local image_name=$1if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "$image_name"; thenecho "镜像 $image_name 已存在于本地"return 0elseecho "正在拉取 Docker 镜像 $image_name..."if docker pull "$image_name" > /dev/null 2> pull.log; thenecho "Docker 镜像 $image_name 拉取成功"return 0elseecho "Docker 镜像 $image_name 拉取失败。详情查看 pull.log。"exit 1fifi
}# 检查 tar 文件是否存在
check_tar_file() {local image_name=$1local tar_file=$2if [ -f "$tar_file" ]; thenecho "镜像文件 $tar_file 已经存在"return 0elseecho "Docker save $image_name"if docker save -o "$tar_file" "$image_name" 2> save.log; thenecho "Docker save $image_name is successful"return 0elseecho "Docker save $image_name failed. Check save.log for details."exit 1fifi
}# 发送和导入镜像到其他主机
send_and_import_image() {local image_name=$1local tar_file=$2shift 2 # 移动参数,使得 $@ 包含剩余的主机地址for host in "$@"; doecho "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'"; thenecho "镜像 $image_name 已存在于 $host"elseecho "导入镜像到 $host"if ssh root@$host "ctr -n k8s.io images import /root/$tar_file"; thenecho "ctr import $tar_file is successful on $host"elseecho "ctr import $tar_file failed on $host"fifidone
}# 主函数
main() {check_docker_installedcheck_args "$@"local image_name=$1local 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 "$@"

http://www.ppmy.cn/embedded/139329.html

相关文章

C语言内存:我家大门常打开

C语言本着自由开放的理念&#xff0c;并不禁止程序访问非法内存。 什么是非法内存&#xff1f;就是那本不是你家的地&#xff0c;你却硬跑过去种庄稼。 或者&#xff0c;你在澡堂子里拿着自己的钥匙去捅别人的柜。 这种行为当然后果难料。 可能你捅了半天&#xff0c;火花冒…

【Maven】nexus 配置私有仓库配置【转】

介绍&#xff1a;【Maven】Nexus几个仓库的介绍-CSDN博客 一、仓库类型 proxy 远程仓库的代理&#xff0c;比如说nexus配置了一个central repository的proxy,当用户向这个proxy请求一个artifact的时候&#xff0c;会现在本地查找&#xff0c;如果找不到&#xff0c;则会从远程…

持续集成与持续部署:CI/CD实现教程

以下是一个基于常见工具实现 CI/CD 的基本教程示例&#xff0c;这里以 Git、Jenkins、Maven&#xff08;用于 Java 项目构建和管理依赖&#xff0c;其他语言项目可替换为对应构建工具&#xff09;以及 Docker&#xff08;用于容器化部署&#xff0c;非必需但很常用&#xff09;…

Apache Paimon】-- 6 -- 清理过期数据

目录 1、简要介绍 2、操作方式和步骤 2.1、调整快照文件过期时间 2.2、设置分区过期时间 2.2.1、举例1 2.2.2、举例2 2.3、清理废弃文件 3、参考 1、简要介绍 清理 paimon &#xff08;表&#xff09;过期数据可以释放存储空间&#xff0c;优化资源利用并提升系统运行效…

八、无刷电机电压电流温度采集

1、三相电流采样 上图为 U 相电流采集电路&#xff08;U、V、W 三相同理&#xff0c;这里只以 U 相为例&#xff09;&#xff0c;其中R17 为采样电阻&#xff08;20mR&#xff09;&#xff0c;当有电流 I 流过采样电阻时&#xff0c;采样电阻上就会产生一个电压&#xff0c;电压…

人工智能与SEO优化中的关键词策略解析

内容概要 在当今数字化快速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;与搜索引擎优化&#xff08;SEO&#xff09;的结合正变得愈发重要。关键词策略是SEO优化的一项基础工作&#xff0c;它直接影响到网站的可见性和流量。通过运用智能算法&#xff0c;企业能…

关联度分析、灰色预测GM(1,1)、GM(1,1)残差模型——基于Python实现

关联度分析 import numpy as np import pandas as pd #关联度分析 #参考序列 Y_0[170,174,197,216.4,235.8] #被比较序列 Y_1[195.4,189.9,187.2,205,222.7] Y_2[308,310,295,346,367]#初始化序列 X_0np.array(Y_0)/Y_0[0] X_1np.array(Y_1)/Y_1[0] X_2np.array(Y_2)/Y_2[0]#计…

基于Java Springboot旅游民宿信息管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…