首页 > 解决方案 > 为什么我无法从 Kubernetes 容器之间的共享 PersistentVolumeClaim 中读取文件?

问题描述

我有一个felipeogutierrez/tpch-dbgen使用构建的dockerdocker-compose映像,并使用.travis-CI

version: "3.7"
services:
  other-images: ....
  tpch-dbgen:
    build: ../docker/tpch-dbgen
    image: felipeogutierrez/tpch-dbgen
    volumes:
      - tpch-dbgen-data:/opt/tpch-dbgen/data/
      - datarate:/tmp/
    stdin_open: true

这是Dockerfile构建此图像的方法:

FROM gcc AS builder
RUN mkdir -p /opt
COPY ./generate-tpch-dbgen.sh /opt/generate-tpch-dbgen.sh
WORKDIR /opt
RUN chmod +x generate-tpch-dbgen.sh && ./generate-tpch-dbgen.sh

最后,这个脚本创建了一个目录/opt/tpch-dbgen/data/,其中包含一些我想从我在 Kubernetes 上运行的另一个 docker 镜像中读取的文件。然后,我创建了一个 Flink 映像,可以在 Kubernetes 中运行。该镜像启动了 3 个 Flink 任务管理器和一个从镜像中读取文件的流应用程序tpch-dbgen-data。我认为正确的方法是创建一个PersistentVolumeClaim这样我就可以在 Kubernetes 中将目录/opt/tpch-dbgen/data/从镜像共享felipeogutierrez/tpch-dbgen到我的 flink 镜像。所以,首先我有这个文件来创建PersistentVolumeClaim

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: tpch-dbgen-data-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Mi

然后,我正在创建一个initContainers启动图像felipeogutierrez/tpch-dbgen,然后启动我的图像felipeogutierrez/explore-flink:1.11.1-scala_2.12

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flink-taskmanager
spec:
  replicas: 3
  selector:
    matchLabels:
      app: flink
      component: taskmanager
  template:
    metadata:
      labels:
        app: flink
        component: taskmanager
    spec:
      initContainers:
      - name: tpch-dbgen
        image: felipeogutierrez/tpch-dbgen
        #imagePullPolicy: Always
        env:
        command: ["ls"]
        # command: ['sh', '-c', 'for i in 1 2 3; do echo "job-1 `date`" && sleep 5s; done;', 'ls']
        volumeMounts:
        - name: tpch-dbgen-data
          mountPath: /opt/tpch-dbgen/data
      containers:
      - name: taskmanager
        image: felipeogutierrez/explore-flink:1.11.1-scala_2.12
        #imagePullPolicy: Always
        env:
        args: ["taskmanager"]
        ports:
        - containerPort: 6122
          name: rpc
        - containerPort: 6125
          name: query-state
        livenessProbe:
          tcpSocket:
            port: 6122
          initialDelaySeconds: 30
          periodSeconds: 60
        volumeMounts:
        - name: flink-config-volume
          mountPath: /opt/flink/conf/
        - name: tpch-dbgen-data
          mountPath: /opt/tpch-dbgen/data
        securityContext:
          runAsUser: 9999  # refers to user _flink_ from official flink image, change if necessary
      volumes:
      - name: flink-config-volume
        configMap:
          name: flink-config
          items:
          - key: flink-conf.yaml
            path: flink-conf.yaml
          - key: log4j-console.properties
            path: log4j-console.properties
      - name: tpch-dbgen-data
        persistentVolumeClaim:
          claimName: tpch-dbgen-data-pvc

Flink 流应用程序正在启动,但它无法读取/opt/tpch-dbgen/data图像目录中的文件felipeogutierrez/tpch-dbgen。我收到错误:java.io.FileNotFoundException: /opt/tpch-dbgen/data/orders.tbl (No such file or directory)。这很奇怪,因为当我尝试进入容器时,felipeogutierrez/tpch-dbgen我可以列出文件。所以我想我的 Kubernetes 配置有问题。有谁知道指出我在 Kubernetes 配置文件中缺少什么?

$ docker run -i -t felipeogutierrez/tpch-dbgen /bin/bash
root@10c0944a95f8:/opt# pwd
/opt
root@10c0944a95f8:/opt# ls tpch-dbgen/data/
customer.tbl  dbgen  dists.dss  lineitem.tbl  nation.tbl  orders.tbl  part.tbl  partsupp.tbl  region.tbl  supplier.tbl

此外,当我列出容器的日志时,tpch-dbgen我可以看到tpch-dbgen我想要读取的目录。虽然我无法command: ["ls tpch-dbgen"]在 Kubernetes 配置文件中执行命令。

$ kubectl get pods
NAME                                 READY   STATUS    RESTARTS   AGE
flink-jobmanager-n9nws               1/1     Running   2          17m
flink-taskmanager-777cb5bf77-ncdl4   1/1     Running   0          4m54s
flink-taskmanager-777cb5bf77-npmrx   1/1     Running   0          4m54s
flink-taskmanager-777cb5bf77-zc2nw   1/1     Running   0          4m54s
$ kubectl logs flink-taskmanager-777cb5bf77-ncdl4 tpch-dbgen
generate-tpch-dbgen.sh
tpch-dbgen

标签: dockerkubernetes

解决方案


Docker 有一个不寻常的功能,在某些特定情况下,它会从映像中填充新创建的卷。您不应依赖此功能,因为它完全忽略了底层映像中的更新,并且不适用于 Kubernetes。

在您的 Kubernetes 设置中,您创建一个新的空 PersistentVolumeClaim,然后将其挂载到 init 和主容器中的实际数据上。与所有 Unix 挂载一样,这会隐藏该目录中先前的数据。没有什么会导致数据被复制到该卷中。这与其他所有类型的挂载方式相同,除了 Docker 命名卷挂载:如果您将 Compose 设置更改为进行主机绑定挂载,或者使用本地开发系统进行操作,您将看到相同的行为使用 USB 驱动器作为“卷”。

您需要使您的 init 容器(或其他东西)显式地将数据复制到目录中。例如:

initContainers:
- name: tpch-dbgen
  image: felipeogutierrez/tpch-dbgen
  command:
    - /bin/cp
    - -a
    - /opt/tpch-dbgen/data
    - /data
  volumeMounts:
    - name: tpch-dbgen-data
      mountPath: /data # NOT the same path as in the image

如果主进程修改了这些文件,您可以使命令更智能,或者将脚本写入您的映像中,仅在单个文件不存在时复制它们。

让您的映像在启动时而不是在映像构建时生成数据文件可能更有意义。这可能看起来像:

FROM gcc
COPY ./generate-tpch-dbgen.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/generate-tpch-dbgen.sh
CMD ["generate-tpch-dbgen.sh"]

然后在您的 init 容器中,您可以运行默认命令(生成脚本),并将工作目录设置为卷目录

initContainers:
- name: tpch-dbgen
  image: felipeogutierrez/tpch-dbgen
  volumeMounts:
    - name: tpch-dbgen-data
      mountPath: /opt/tpch-dbgen/data # or anywhere really
  workingDir: /opt/tpch-dbgen/data    # matching mountPath

推荐阅读