乐趣区

关于后端:硬核干货丨借助多容器Pod轻松扩展K8S中的应用

Kubernetes 提供了微小的灵活性和运行各种利用的能力。如果你的利用是云原生微服务或 12 因素(12-factor)利用,那么在 Kubernetes 中运行它们有可能会绝对简略。

然而,运行那些没有明确设计为在容器化环境中运行的应用程序呢?Kubernetes 也能够解决这些问题,然而设置起来可能会比拟麻烦。

Kubernetes 提供的最弱小的工具之一是多容器 pod(只管多容器 pod 在各种状况下对云原生利用也很有用)。为什么要在一个 pod 中运行多个容器?因为多容器 pod 能够让你在不扭转其代码的状况下更改应用程序的行为。

这在各种状况下都很有用,特地是对于那些最后没有被设计成在容器中运行的应用程序来说,这很不便。咱们来看看一个例子。

确保 HTTP 服务的平安

Elasticsearch 是在容器风行之前诞生的(当然当初在 Kubernetes 中运行也非常简略),它能够看成在虚拟机中运行的传统 Java 利用的代替。

咱们将 Elasticsearch 作为示例应用程序,而后应用多容器 pods 来加强它。

以下是非常根本的(非生产环境就绪)Elasticsearch Deployment 和服务:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: elasticsearch
  template:
    metadata:
      labels:
        app.kubernetes.io/name: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: elasticsearch:7.9.3
          env:
            - name: discovery.type
              value: single-node
          ports:
            - name: http
              containerPort: 9200
---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
spec:
  selector:
    app.kubernetes.io/name: elasticsearch
  ports:
    - port: 9200
      targetPort: 9200

discovery.type环境变量是让它以单个正本运行的必要条件。

Elasticsearch 默认通过 HTTP 端口 9200 进行监听。你能够通过在集群中运行另一个 Pod 并 curlelasticsearch服务来确认 pod 工作。

kubectl run -it --rm --image=curlimages/curl curl \
  -- curl http://elasticsearch:9200
{
  "name" : "elasticsearch-77d857c8cf-mk2dv",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "z98oL-w-SLKJBhh5KVG4kg",
  "version" : {
    "number" : "7.9.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "c4138e51121ef06a6404866cddc601906fe5c868",
    "build_date" : "2020-10-16T10:36:16.141335Z",
    "build_snapshot" : false,
    "lucene_version" : "8.6.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

当初,假如你正在向零信赖平安模式倒退,你须要对网络上的所有流量进行加密。如果应用程序没有原生的 TLS 反对,你会如何去做?

近期版本的 Elasticsearch 反对 TLS,但它在之前很长一段时间内是一个付费性能。

咱们首先想到的可能是用 nginx ingress 做 TLS 终止,因为 ingress 是集群中路由内部流量的组件。但这并不能满足要求,因为 ingress pod 和 Elasticsearch pod 之间的流量可能会在未加密的状况下通过网络。


内部流量被路由到 Ingress,而后路由到 Pod

如果你在 Ingress 终止 TLS,剩下的流量将不会加密。

一个能满足要求的解决方案是在 pod 上加一个 nginx 代理容器,通过 TLS 进行监听。从用户到 Pod 的一路流量都是加密的。

如果在 pod 中蕴含一个代理容器,你能够在 Nginx pod 中终止 TLS。

当你比拟以后的设置时,你能够留神到,在 Elasticsearch 容器之前,流量始终是加密的。

以下是部署的状况:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: elasticsearch
  template:
    metadata:
      labels:
        app.kubernetes.io/name: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: elasticsearch:7.9.3
          env:
            - name: discovery.type
              value: single-node
            - name: network.host
              value: 127.0.0.1
            - name: http.port
              value: '9201'
        - name: nginx-proxy
          image: nginx:1.19.5
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d
              readOnly: true
            - name: certs
              mountPath: /certs
              readOnly: true
          ports:
            - name: https
              containerPort: 9200
      volumes:
        - name: nginx-config
          configMap:
            name: elasticsearch-nginx
        - name: certs
          secret:
            secretName: elasticsearch-tls
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: elasticsearch-nginx
data:
  elasticsearch.conf: |
    server {
        listen 9200 ssl;
        server_name elasticsearch;
        ssl_certificate /certs/tls.crt;
        ssl_certificate_key /certs/tls.key;

        location / {proxy_pass http://localhost:9201;}
    }

让咱们来解读一下:

  • Elasticsearch 在端口 9201 上监听 localhost,而不是默认的 0.0.0.0:9200(那是 network.host 和 http.port 环境变量的作用)。
  • 新的 nginx-proxy 容器通过 HTTPS 在 9200 端口监听,并在 9201 端口代理申请到 Elasticsearch。(elasticsearch-tls
    secret 蕴含 TLS 证书和密钥,例如能够用 cert-manager 生成)。

所以来自 pod 内部的申请会通过 HTTPS 进入 9200 端口的 Nginx,而后转发到 9201 端口的 Elasticsearch。

你能够通过在集群内收回 HTTPS 申请来确认它是否能够失常工作。

kubectl run -it --rm --image=curlimages/curl curl \
  -- curl -k https://elasticsearch:9200
{
  "name" : "elasticsearch-5469857795-nddbn",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "XPW9Z8XGTxa7snoUYzeqgg",
  "version" : {
    "number" : "7.9.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "c4138e51121ef06a6404866cddc601906fe5c868",
    "build_date" : "2020-10-16T10:36:16.141335Z",
    "build_snapshot" : false,
    "lucene_version" : "8.6.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

对于自签名的 TLS 证书,- k 版本是必要的。在生产环境中,你须要应用可信的证书。

疾速查看日志,显示该申请通过了 Nginx 代理:

kubectl logs elasticsearch-5469857795-nddbn nginx-proxy | grep curl
10.88.4.127 – – [26/Nov/2020:02:37:07 +0000] “GET / HTTP/1.1” 200 559 “-” “curl/7.73.0-DEV” “-“

你也能够查看你是否无奈通过未加密的连贯连贯到 Elasticsearch:

kubectl run -it --rm --image=curlimages/curl curl \
  -- curl http://elasticsearch:9200
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.19.5</center>
</body>
</html>

你曾经强制执行了 TLS,而无需接触 Elasticsearch 代码或容器镜像。

代理容器是一种常见的模式

在 pod 中增加代理容器的做法很常见,以至于它有一个名字:Ambassador 模式。

这篇文章中的所有模式在谷歌的一篇优秀论文中都有详细描述。公众号后盾回复【论文】,获取论文下载地址。

增加根本的 TLS 反对只是一个开始。这里有一些其余的事件你能够用 Ambassador 模式来做:

  • 如果你想让集群中的所有流量都用 TLS 证书加密,你可能会在集群中的每个 pod 中装置一个 nginx(或其余)代理。你甚至能够更进一步,应用互相 TLS 来确保所有的申请都是通过认证以及加密的。(这是 Istio 和 Linkerd 等服务网格应用的次要办法)。
  • 你能够应用代理来确保集中的 OAuth 受权通过验证 jwts 来认证所有申请。例如,gcp-iap-auth,它能够验证申请是否被 GCP
    Identity-Aware Proxy 认证。
  • 你能够通过平安隧道连贯到内部数据库。这对于那些没有内置 TLS 反对的数据库来说尤其不便(比方旧版本的 Redis)。

多容器 pod 的工作原理

咱们先来理解 Kubernetes 上 pod 和容器之间的区别,以便更好地理解其底层是如何工作的。

一个传统的容器(例如由 docker run 启动的容器)提供了几种模式的隔离:

  • 资源隔离(如,内存限度)
  • 过程隔离
  • Filesystem 和挂载隔离
  • 网络隔离

Docker 还有其余一些设置,但这些是最次要的。

底层应用的工具是 Linux 命名空间和控制组(cgroups)。

控制组是一种用来限度资源的便捷办法,比方一个特定过程能够应用的 CPU 或内存。例如,你能够说你的过程应该只应用 2GB 的内存和 4 个 CPU 外围中的一个。

命名空间则负责隔离过程以及限度该过程能看到的货色。例如,过程只能看到与它间接相干的网络数据包,它无奈看到流经网络适配器的所有网络数据包。或者你能够隔离 filesystem,让过程置信它能够拜访所有的 filesystem。

从内核 5.6 版本开始,有八种命名空间,挂载命名空间是其中之一

有了挂载命名空间,你能够让过程认为它能够拜访主机上的所有目录,而事实上它并没有

挂载命名空间被设计为隔离资源——在本例中是 filesystem。

每个过程都能够看到同一个 filesystem,同时还能够与其余过程隔离

如果你须要温习一下 cgroups 和 namespaces,这里有一篇很好的博客文章,深入探讨了一些技术细节:

https://jvns.ca/blog/2016/10/…

在 Kubernetes 上,容器提供了所有模式的隔离,除了网络隔离。网络隔离产生在 pod 层面。换句话说,一个 pod 中的每个容器都会有本人的 filesystem、过程表等,但它们都会共享同一个网络命名空间。

让咱们来看看一个简略 pod 容器,以更好地理解它是如何工作的。

apiVersion: v1
kind: Pod
metadata:
  name: podtest
spec:
  containers:
    - name: c1
      image: busybox
      command: ['sleep', '5000']
      volumeMounts:
        - name: shared
          mountPath: /shared
    - name: c2
      image: busybox
      command: ['sleep', '5000']
      volumeMounts:
        - name: shared
          mountPath: /shared
  volumes:
    - name: shared
      emptyDir: {}

咱们将下面的代码段拆解一下:

  • 有两个容器,这两个容器都会沉睡一段时间。
  • 有一个 emptyDir 卷,它实质上是一个长期的本地卷,在 pod 的生命周期内继续存在。
  • emptyDir 卷装置在每个 pod 中的 /shared 目录下。

你能够应用 kubectl exec 看到卷被挂载在第一个容器上:

kubectl exec -it podtest --container c1 -- sh

该命令将终端会话连贯到 podtest pod 中的容器 c1。

kubectl exec 的 –container 选项通常缩写为 -c。

mount | grep shared
/dev/vda1 on /shared type ext4 (rw,relatime)

如你所见,一个卷挂载在 /shared 上——这就是咱们之前创立的 shared 卷。当初咱们来创立一些文件:

echo "foo" > /tmp/foo
echo "bar" > /shared/bar

咱们从第二个容器中查看雷同的文件。首先连贯到它:

kubectl exec -it podtest --container c2 -- sh
cat /shared/bar
bar
cat /tmp/foo
cat: can't open'/tmp/foo': No such file or directory

如你所见,在 shared 目录中创立的文件在两个容器上都是可用的,但 /tmp 中的文件却不可用。这是因为除了卷之外,容器的 filesysytem 之间是齐全隔离的。

当初咱们来看看网络和过程隔离。一个很好的办法是应用命令 ip link 来查看网络是如何设置的,它能够显示 Linux 零碎的网络设备。让咱们在第一个容器中执行这个命令:

kubectl exec -it podtest -c c1 -- ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
178: eth0@if179: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
    link/ether 46:4c:58:6c:da:37 brd ff:ff:ff:ff:ff:ff

在另一个容器中执行同样的命令:

kubectl exec -it podtest -c c2 -- ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
178: eth0@if179: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
    link/ether 46:4c:58:6c:da:37 brd ff:ff:ff:ff:ff:ff

你能够看到两个容器都有:

  • 雷同的设施eth0
  • 雷同 MAC 地址:46:4c:58:6c:da:37

因为 MAC 地址应该是全局惟一的,因而雷同的地址分明地表明,这些 Pod 共享同一个设施。

当初让咱们来看看网络共享的操作吧!咱们先连贯到第一个容器:

ubectl exec -it podtest -c c1 -- sh

借助 nc 启动一个简略的网络监听器:

nc -lk -p 5000 127.0.0.1 -e 'date'

该命令在端口 5000 的 localhost 上启动一个监听器,并向任何连贯的 TCP 客户端输出 date 命令。

那么第二个容器能够连贯到它吗?

应用以下命令在第二个容器中关上终端:

kubectl exec -it podtest -c c2 -- sh

当初你能够验证第二个容器能够连贯到该网络监听器,但不能看到 nc 过程:

telnet localhost 5000
Connected to localhost
Sun Nov 29 00:57:37 UTC 2020
Connection closed by foreign host

ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 sleep 5000
   73 root      0:00 sh
   81 root      0:00 ps aux

通过 telnet 连贯,能够看到 date 的输入,证实 nc 监听器在工作,然而 ps aux(显示容器上的所有过程)基本没有显示 nc。这是因为 pod 内的容器有过程隔离,但没有网络隔离。这就解释了 Ambassador 模式的工作原理:

  • 因为所有的容器都共享同一个网络命名空间,所以一个容器能够监听所有的连贯——甚至是内部的连贯。
  • 其余的容器只承受来自 localhost 的连贯——回绝任何内部连贯。

接管内部流量的容器就是 Ambassador,因而该模式也被称为 Ambassador 模式。

不过有一点很要害,要记住:因为网络命名空间是共享的,所以一个 pod 中的多个容器不能在同一个端口监听。

让咱们来看看多容器 pod 的一些其余用例。

应用标准接口裸露指标

假如你曾经标准化地应用 Prometheus 来监控 Kubernetes 集群中的所有服务,但你应用的一些应用程序并没有原生导出 Prometheus 指标(如,Elasticsearch)。

你能在不扭转你的利用程序代码的状况下,将 Prometheus 指标增加到你的 pod 中吗?事实上,你能够,应用 Adapter 模式。

对于 Elasticsearch 的例子,让咱们在 pod 中增加一个 “exporter” 容器,以 Prometheus 格局裸露各种 Elasticsearch 指标。

这并不艰难,因为有一个 Elasticsearch 的开源 exporter(你还须要将相干端口增加到服务中):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: elasticsearch
  template:
    metadata:
      labels:
        app.kubernetes.io/name: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: elasticsearch:7.9.3
          env:
            - name: discovery.type
              value: single-node
          ports:
            - name: http
              containerPort: 9200
        - name: prometheus-exporter
          image: justwatch/elasticsearch_exporter:1.1.0
          args:
            - '--es.uri=http://localhost:9200'
          ports:
            - name: http-prometheus
              containerPort: 9114
---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
spec:
  selector:
    app.kubernetes.io/name: elasticsearch
  ports:
    - name: http
      port: 9200
      targetPort: http
    - name: http-prometheus
      port: 9114
      targetPort: http-prometheus

一旦利用了这个性能,你就能够在 9114 端口找到裸露的指标:

kubectl run -it --rm --image=curlimages/curl curl \
  -- curl -s elasticsearch:9114/metrics | head
# HELP elasticsearch_breakers_estimated_size_bytes Estimated size in bytes of breaker
# TYPE elasticsearch_breakers_estimated_size_bytes gauge
elasticsearch_breakers_estimated_size_bytes{breaker="accounting",name="elasticsearch-ss86j"} 0
elasticsearch_breakers_estimated_size_bytes{breaker="fielddata",name="elasticsearch-ss86j"} 0
elasticsearch_breakers_estimated_size_bytes{breaker="in_flight_requests",name="elasticsearch-ss86j"} 0
elasticsearch_breakers_estimated_size_bytes{breaker="model_inference",name="elasticsearch-ss86j"} 0
elasticsearch_breakers_estimated_size_bytes{breaker="parent",name="elasticsearch-ss86j"} 1.61106136e+08
elasticsearch_breakers_estimated_size_bytes{breaker="request",name="elasticsearch-ss86j"} 16440
# HELP elasticsearch_breakers_limit_size_bytes Limit size in bytes for breaker
# TYPE elasticsearch_breakers_limit_size_bytes gauge

再次,你曾经可能扭转你的应用程序的行为,而无需理论扭转你的代码或容器镜像。你曾经裸露了标准化的 Prometheus 指标,这些指标能够被集群范畴内的工具(如 Prometheus Operator 应用),从而实现了应用程序和底层基础设施之间的良好拆散。

Tailing logs

接下来,咱们来看看 Sidecar 模式,在这一模式下你能够将容器增加到 Pod,该 pod 能够以某些形式加强应用程序。

Sidecar 模式非常通用,能够利用到不同类型的用例中。咱们接下来摸索以下 sidecar 的经典用例:log tailing sidecar。

在容器化环境中,最佳实际是始终将日志记录到规范输入,这样能够集中收集和汇总日志。但许多旧的应用程序被设计成日志输入到文件,而扭转这一形式并非易事。而增加一个 log tailing sidecar 意味着你不须要更改原有的形式也能够实现日志的集中收集和汇总。

咱们持续以 Elasticsearch 为例,这可能会有点顺当,因为 Elasticsearch 容器默认是将日志记录到规范输入的(而且让它记录到文件也不是件容易的事)。

以下是部署状况:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
  labels:
    app.kubernetes.io/name: elasticsearch
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: elasticsearch
  template:
    metadata:
      labels:
        app.kubernetes.io/name: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: elasticsearch:7.9.3
          env:
            - name: discovery.type
              value: single-node
            - name: path.logs
              value: /var/log/elasticsearch
          volumeMounts:
            - name: logs
              mountPath: /var/log/elasticsearch
            - name: logging-config
              mountPath: /usr/share/elasticsearch/config/log4j2.properties
              subPath: log4j2.properties
              readOnly: true
          ports:
            - name: http
              containerPort: 9200
        - name: logs
          image: alpine:3.12
          command:
            - tail
            - -f
            - /logs/docker-cluster_server.json
          volumeMounts:
            - name: logs
              mountPath: /logs
              readOnly: true
      volumes:
        - name: logging-config
          configMap:
            name: elasticsearch-logging
        - name: logs
          emptyDir: {}

日志配置文件是一个独自的 ConfigMap,因为它太长了所以这里没有包含它。

两个容器共享雷同的 volume,名为 logs。Elasticsearch 容器将日志写入该卷,而日志容器只是从相应的文件中读取并输入到规范输入。你能够用 kubectl logs 指定相应的容器来检索日志流:

kubectl logs elasticsearch-6f88d74475-jxdhl logs | head
{
  "type": "server",
  "timestamp": "2020-11-29T23:01:42,849Z",
  "level": "INFO",
  "component": "o.e.n.Node",
  "cluster.name": "docker-cluster",
  "node.name": "elasticsearch-6f88d74475-jxdhl",
  "message": "version[7.9.3], pid[7], OS[Linux/5.4.0-52-generic/amd64], JVM"
}
{
  "type": "server",
  "timestamp": "2020-11-29T23:01:42,855Z",
  "level": "INFO",
  "component": "o.e.n.Node",
  "cluster.name": "docker-cluster",
  "node.name": "elasticsearch-6f88d74475-jxdhl",
  "message": "JVM home [/usr/share/elasticsearch/jdk]"
}
{
  "type": "server",
  "timestamp": "2020-11-29T23:01:42,856Z",
  "level": "INFO",
  "component": "o.e.n.Node",
  "cluster.name": "docker-cluster",
  "node.name": "elasticsearch-6f88d74475-jxdhl",
  "message": "JVM arguments […]"
}

应用 sidecar 的益处是,流式传输到规范输入并不是惟一的抉择。

如果你须要切换到一个自定义的日志聚合服务,你能够只扭转 sidecar 容器,而无需扭转你的应用程序中任何其余货色。

其余 sidecar 用例

Sidecar 有许多用例,日志容器只是其中一个比较简单的用例。

以下是你在其余方面可能用到的一些其余用例:

  • 实时从新加载 ConfigMaps,而不须要重新启动 pod
  • 将 Hashicorp Vault 中的 secret 注入到应用程序中
  • 将本地 Redis 实例增加到你的应用程序中,以实现低提早的内存缓存

筹备运行 pod

到目前为止,本篇文章所介绍的所有多容器 pod 的例子都波及到多个容器同时运行。Kubernetes 还提供了运行 Init Containers 的能力,Init Containers 是在 “ 惯例 “ 容器启动之前运行实现的容器。

这容许你在你的 pod 正式启动之前运行一个初始化脚本。为什么你心愿你的筹备工作在一个独自的容器中运行,而不是在你的容器的 entrypoint 脚本中增加一些初始化?

让咱们来看看 Elasticsearch 的一个理论例子。Elasticsearch 文档举荐在生产就绪部署中设置 vm.max_map_count 的 sysctl 设置。这在容器化环境中是有问题的,因为没有容器级的 sysctl 隔离,任何更改都必须产生在节点级。

在不能自定义 Kubernetes 节点的状况下,如何解决这个问题?

一种办法是在特权容器中运行 Elasticsearch,这将使 Elasticsearch 可能扭转其主机节点上的零碎设置,并扭转 entrypoint 脚本以增加 sysctls。但从平安角度来看,这将是十分危险的!如果 Elasticsearch 服务被入侵,攻击者将领有对其主机节点的 root 权限。你能够应用 init container 来肯定水平上升高这个危险:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: elasticsearch
  template:
    metadata:
      labels:
        app.kubernetes.io/name: elasticsearch
    spec:
      initContainers:
        - name: update-sysctl
          image: alpine:3.12
          command: ['/bin/sh']
          args:
            - -c
            - |
              sysctl -w vm.max_map_count=262144
          securityContext:
            privileged: true
      containers:
        - name: elasticsearch
          image: elasticsearch:7.9.3
          env:
            - name: discovery.type
              value: single-node
          ports:
            - name: http
              containerPort: 9200

pod 在特权 init container 中设置了 sysctl,之后 Elasticsearch 容器按预期启动。

你依然在应用一个特权容器,这并不是现实状态,但至多它持续时间很短,所以攻击面要低得多。

这是 Elastic Cloud Operator 举荐的办法:
https://www.elastic.co/guide/…

应用特权 init container 为运行 pod 的节点做筹备是一种相当常见的模式。例如,Istio 应用 init container 来设置每次 pod 运行时的 iptables 规定。

应用 init container 的另一个起因是以某种形式筹备 pod 的 filesystem。一个常见的用例是 secrets 治理。

其余的 init container 用例

如果你应用相似 HashicCorp Vault 这样的工具来治理 secrets,而不是 Kubernetes secrets,你能够在一个 init container 中检索 secrets,并将它们长久化到一个共享的 emptyDir 卷。

如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app.kubernetes.io/name: myapp
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: myapp
  template:
    metadata:
      labels:
        app.kubernetes.io/name: myapp
    spec:
      initContainers:
        - name: get-secret
          image: vault
          volumeMounts:
            - name: secrets
              mountPath: /secrets
          command: ['/bin/sh']
          args:
            - -c
            - |
              vault read secret/my-secret > /secrets/my-secret
      containers:
        - name: myapp
          image: myapp
          volumeMounts:
            - name: secrets
              mountPath: /secrets
      volumes:
        - name: secrets
          emptyDir: {}

当初secret/my-secret secret 将在 myapp 容器的 filesystem 中可用。

这就是 Vault Agent Sidecar Injector 等零碎工作的基本思路。然而,它们在实践中相当简单(联合 mutating webhooks、init container 和 sidecars 来暗藏大部分的复杂性)。

此外,还有一些其余你可能想要应用 init container 的起因:

  • 你心愿数据库迁徙脚本在你的应用程序之前运行(这通常能够在一个 entrypoint 脚本中实现,但有时应用专用容器更容易做到这一点)。
  • 你想从 S3 或 GCS 中检索一个你的利用所依赖的大文件(为此应用一个 init container 有助于防止利用容器的臃肿)。

总 结

这篇文章涵盖了相当多的内容,所以这里有一个表格,列出了一些多容器模式,以及你什么时候可能要应用它们:

图片

如果你想深入研究这个问题,请务必浏览官网文档和原始容器设计模式文件:

https://kubernetes.io/docs/co…

https://static.googleusercont…


原文链接:
https://learnk8s.io/sidecar-c…

退出移动版