在部署更新时重新附加卷声明

Re-attach volume claim on deployment update

我正在使用持久卷声明在容器中存储数据:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-pvc
  labels:
    type: amazonEBS
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

规范声明:

spec:
  volumes:
  - name: test-data-vol
    persistentVolumeClaim:
      claimName: test-pvc
  containers:
  - name: test
    image: my.docker.registry/test:1.0
    volumeMounts:
    - mountPath: /var/data
      name: test-data-vol

当我第一次启动时,此卷已正确安装。但是当我尝试更新容器镜像时:

-    image: my.docker.registry/test:1.0
+    image: my.docker.registry/test:1.1

此卷无法安装到新的 pod:

# kubectl get pods
test-7655b79cb6-cgn5r       0/1     ContainerCreating   0          3m
test-bf6498559-42vvb        1/1     Running             0          11m

# kubectl describe test-7655b79cb6-cgn5r
Events:
  Type     Reason                 Age   From                                                  Message
  ----     ------                 ----  ----                                                  -------
  Normal   Scheduled              3m5s  default-scheduler                                     Successfully assigned test-7655b79cb6-cgn5r to ip-*-*-*-*.us-west-2.compute.internal
  Warning  FailedAttachVolume     3m5s  attachdetach-controller                               Multi-Attach error for volume "pvc-2312eb4c-c270-11e8-8d4e-065333a7774e" Volume is already exclusively attached to one node and can't be attached to another
  Normal   SuccessfulMountVolume  3m4s  kubelet, ip-*-*-*-*.us-west-2.compute.internal  MountVolume.SetUp succeeded for volume "default-token-x82km"
  Warning  FailedMount            62s   kubelet, ip-*-*-*-*.us-west-2.compute.internal  Unable to mount volumes for pod "test-7655b79cb6-cgn5r(fab0862c-d1cf-11e8-8d4e-065333a7774e)": timeout expired waiting for volumes to attach/mount for pod "test-7655b79cb6-cgn5r". list of unattached/unmounted volumes=[test-data-vol]

Kubernetes 似乎无法将此卷从一个容器重新附加到另一个容器。如何正确处理?当旧版本停止时,我需要卷上的这些数据供新版本的部署使用。

不确定,RollingUpdate可能会解决问题。根据 docs,"Rolling Update" 是更新容器镜像的安全方式。我假设,K8s 也可以处理 PV/PVC。

这里的问题是 EBS 卷是 ReadWriteOnce 并且只能安装到单个 pod,因此当您进行滚动更新时,旧 pod 会保留该卷。为此,您要么必须使用 StatefulSet or you can use any of the ReadWriteMany PV 类型。

A Kubernetes Deployment 有时更好地用于无状态 pods。

您始终可以采用蛮力方法,强制删除持有该卷的 pod。确保 Reclaim Policy 设置为 Retain

根据您在问题中提供的上下文,我无法判断您的意图是运行一个单实例有状态应用程序,还是集群 有状态应用程序。

我最近 运行 遇到了这个问题,从 this section in the docs 开始,这里是解决这个问题的方法...

如果您运行正在使用单个实例有状态应用程序:

  • 您应该缩放应用程序,也就是说,如果您使用 Deployment[=,请将 spec.replicas 的默认值保留为 1 54=]
  • 您应该指示 Kubernetes 使用滚动更新,也就是说,您应该在 Deployment[ 中将 spec.strategy.type 设置为 Recreate

样本Deployment(来自docs):

# application/mysql/mysql-deployment.yaml

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          # Use secret in real usage
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim

和示例 PersistentVolume & PersistentVolumeClaim(来自 docs):

# application/mysql/mysql-pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

这里明显的潜在问题是滚动更新不起作用,因为任何时候都不能超过一个 pod 运行ning。将 spec.strategy.type 设置为 Recreate 告诉 Kubernetes 在部署新 pod 之前停止 运行ning pod,因此可能会有一些停机时间,即使是最小的。

如果您需要集群有状态应用程序,那么使用已经提到的 StatefulSet 作为控制器类型或 ReadWriteMany 作为存储类型可能是要走的路。