如何在 Kubernetes 中模仿“--volumes-from”

Posted

技术标签:

【中文标题】如何在 Kubernetes 中模仿“--volumes-from”【英文标题】:How to mimic '--volumes-from' in Kubernetes 【发布时间】:2015-08-12 19:54:39 【问题描述】:

我正在寻找一种模式,允许在 Kubernetes 中运行在同一个 pod 上的两个容器之间共享卷。

我的用例是: 我有一个在 docker 容器内运行的 Ruby on Rails 应用程序。 docker 镜像包含 /app/<app-name>/public 目录中的静态资产,我需要从在同一个 pod 中运行的 nginx 容器中访问这些资产。

在 'vanilla' docker 中,我会使用 --volumes-from 标志来共享此目录:

docker run --name app -v /app/<app-dir>/public <app-image>
docker run --volumes-from app nginx

阅读此文档后:https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/volumes.md 我试过这个(只显示相关条目):

spec:
  containers:
    - image: <app-image>
      name: <app-name>
      volumeMounts:
        - mountPath: /app/<app-name>/public
          name: assets
    - image: nginx
      name: nginx
      volumeMounts:
        - mountPath: /var/www/html
          name: assets
          readOnly: true
    volumes:
      - name: assets
        hostPath:
          path: /tmp/assets

但是:

即使节点上的/tmp/assets存在,它也是空的 应用容器内的/app/&lt;app-name&gt;/public 也是空的

作为一种解决方法,我将尝试在应用程序容器启动时填充共享目录(只需 cp /app/&lt;app-name&gt;/public/* 到共享目录),但我真的不喜欢这个想法。

问题:如何在 Kubernetes 中模仿 --volumes-from,或者如果没有直接对应物,我如何将文件从一个容器共享到在同一个 pod 中运行的另一个容器?

apiVersion: v1beta3

Client Version: version.InfoMajor:"0", Minor:"17", GitVersion:"v0.17.0", GitCommit:"82f8bdac06ddfacf493a9ed0fedc85f5ea62ebd5", GitTreeState:"clean"
Server Version: version.InfoMajor:"0", Minor:"17", GitVersion:"v0.17.0", GitCommit:"82f8bdac06ddfacf493a9ed0fedc85f5ea62ebd5", GitTreeState:"clean"

【问题讨论】:

【参考方案1】:

[update-2016-8] 在最新的 Kubernetes 版本中,您可以使用名为 init-container 的非常好的功能来替换我下面答案中的 postStart 部分,这将确保集装箱订单。

apiVersion: v1
kind: Pod
metadata:
  name: javaweb-2
spec:
  initContainers:
  - name: war
    image: resouer/sample:v2
    command: ["cp", "/sample.war", "/app"]
    volumeMounts:
    - mountPath: /app
      name: app-volume
  containers:
  - name: tomcat
    image: resouer/mytomcat:7.0
    command: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"]
    volumeMounts:
    - mountPath: /root/apache-tomcat-7.0.42-v2/webapps
      name: app-volume
    ports:
    - containerPort: 8080
      hostPort: 8001
  volumes:
  - name: app-volume
    emptyDir: 

注意:initContainer 仍然是一个beta 功能,所以这个 yaml 的工作版本实际上是这样的:http://kubernetes.io/docs/user-guide/production-pods/#handling-initialization,请注意pod.beta.kubernetes.io/init-containers 部分。

---原始答案开始---

其实可以的。您需要使用容器生命周期处理程序来控制要与其他容器共享的文件/目录。喜欢:

---
apiVersion: v1
kind: Pod
metadata:
    name: server
spec:
    restartPolicy: OnFailure
    containers:
    - image: resouer/sample:v2
      name: war
      lifecycle:
        postStart:
          exec:
            command:
              - "cp"
              - "/sample.war"
              - "/app"
      volumeMounts:
      - mountPath: /app
        name: hostv1 
    - name: peer
      image: busybox
      command: ["tail", "-f", "/dev/null"]
      volumeMounts:
      - name: hostv2
        mountPath: /app/sample.war
    volumes:
    - name: hostv1
      hostPath:
          path: /tmp
    - name: hostv2
      hostPath:
          path: /tmp/sample.war

请查看我的要点了解更多详情:

https://gist.github.com/resouer/378bcdaef1d9601ed6aa

当然你也可以使用emptyDir。因此,war 容器可以将其 /sample.war 共享给对等容器,而不会弄乱对等容器的 /app 目录。

如果我们可以容忍 /app 被覆盖,它会简单得多:

---
apiVersion: v1
kind: Pod
metadata:
  name: javaweb-2
spec:
  restartPolicy: OnFailure
  containers:
  - image: resouer/sample:v2
    name: war
    lifecycle:
      postStart:
        exec:
          command:
            - "cp"
            - "/sample.war"
            - "/app"
    volumeMounts:
    - mountPath: /app
      name: app-volume
  - image: resouer/mytomcat:7.0
    name: tomcat
    command: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"]
    volumeMounts:
    - mountPath: /root/apache-tomcat-7.0.42-v2/webapps
      name: app-volume
    ports:
    - containerPort: 8080
      hostPort: 8001 
  volumes:
  - name: app-volume
    emptyDir: 

【讨论】:

@aronchick 的回答很有价值,但你的回答最适合我的用例,所以我接受它,谢谢。 我在这里看到了一些问题: 1. 当两个 Pod 在同一个节点上运行时会发生什么? 2.滚动更新会发生什么?当两个不同的 pod 版本在同一个节点上运行时,它们会覆盖彼此的源。 @Alex 我只是用hostDir为例,你可以用emptyDIr,就不会出现覆盖问题了。请参阅我发布的要点。 感谢示例配置;我能够让容器将应用程序包移动到持久卷。但是如何让 Openshift/Kubernetes 不认为容器因为完成复制并退出而崩溃了呢?看来我没有 100% 遵循解决方案。 提前感谢您的帮助! @harryz【参考方案2】:

答案是 - 现在 - 你不能。以下是 Kubernetes 问题的几个讨论主题:

https://github.com/GoogleCloudPlatform/kubernetes/issues/6120 https://github.com/GoogleCloudPlatform/kubernetes/issues/831

但是,我可以建议您有一个可能更好的替代设计吗?

    如果您的资产在容器上线时被锁定, 你可以使用类似gitRepo 卷,它将在上线时将其复制到emptyDir,这意味着您不必在 全部,直接下载到共享目录即可。 如果您的资产被锁定在容器的位置 正在构建中,最好在那时复制它们,使用 Docker COPY 命令。 如果您真的想坚持自己的工作方式,则必须将内容复制到 emptyDir 卷中,该卷专为您正在寻找的内容而设计(减去不必复制进去)。

NFS[1] 卷也可以解决您的问题,但可能过于复杂。

此外,我建议这两个服务存在于不同的 pod 中,这样您就可以分别扩展它们。如果需要,您可以创建一个服务端点在它们之间进行通信。

[1]https://github.com/GoogleCloudPlatform/kubernetes/blob/master/examples/nfs/nfs-web-pod.yaml

【讨论】:

【参考方案3】:

未来的进一步更新:

现在有一个用于 Docker 卷的 FlexVol 插件:https://github.com/dims/docker-flexvol

在撰写本文时,FlexVol 仍是一个 alpha 功能,因此请谨慎购买。

【讨论】:

【参考方案4】:

Kubernetes 有自己的卷类型,这些是最常用的卷类型:

    空目录 秘密 gitRepo hostPath(类似于 --volumes-from) 配置地图 持久化存储(云平台提供的存储磁盘)

您可以在此处找到有关 kubernets 卷的更多信息 -https://kubernetes.io/docs/concepts/storage/volumes/

主机路径卷示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

hostpath会将host/node目录挂载到容器目录。一个pod内的多个容器可以使用不同或相同的卷。你需要在每个容器中提及它。 hostPath 卷独立于 pod 生命周期,但它在 node 和 pod 之间创建了紧密耦合,您应该避免使用 hostPath。

【讨论】:

如果您将在给定主机上运行多个 pod 实例,这似乎可能会出现问题。您当然可以这样做,但要注意这是一个重要的限制。我还没有证明这种担忧是有效的,但我想我会分享以防其他人正在评估选项。如果我错了,请告诉我。【参考方案5】:

如果您使用的是 Docker v17.0.5 或更高版本,则可以在构建期间使用多阶段构建将文件从一个容器复制到另一个容器。这是https://medium.com/@tonistiigi/advanced-multi-stage-build-patterns-6f741b852fae的高级功能的精彩入门@

我使用它从后端容器复制静态资产到 Nginx 代理的方式是

ARG API_BACKEND_CONTAINER="api:backend"
FROM $API_BACKEND_CONTAINER as source

FROM nginx:mainline-alpine

ARG NGINX_ROOT=/usr/share/nginx/html/
COPY --from=source  /var/share/api/static/ $NGINX_ROOT

很棒的是,因为 API_BACKEND_CONTAINER 是一个构建参数,我能够传入最新 API 构建的标签。

【讨论】:

这带来了一个很大的缺点,即您无法为容器独立部署新映像,并且您无法构建通用映像以供重用并改变其行为。例如,我构建了一个 nginx 映像来为各种 Rails 应用程序提供静态资产。我必须为每个 Rails 应用程序构建不同版本的 nginx 版本并将它们部署在一起。这是一个非常不幸的限制,然而,就目前而言,这看起来确实是最好的解决方案(而且它非常聪明:-))

以上是关于如何在 Kubernetes 中模仿“--volumes-from”的主要内容,如果未能解决你的问题,请参考以下文章

Kubernetes入门至精通 | Kubernetes集群安全 - 认证

kubernetes(K8S) 容器管理“扫盲“ 学习笔记

如何在 WebSocket 中模仿 Facebook 的“点赞”功能

如何在我自己的方法中模仿 string.Format()?

我如何模仿新行的拖动功能,例如在 Excel 中,但在 R 中?

kubernetes(K8S) 容器管理“扫盲“ 学习笔记