原文:
https://www.mirantis.com/blog/multi-container-pods-and-container-communication-in-kubernetes/
Pavel Chekin - August 28, 2017

容器(Container)常被用来解决比如微服务的单个问题,但在实际场景中,问题的解决往往需要多容器方案。本文会讨论将多个容器整合进单个Kubernetes Pod 中,以及Pod中的容器之间是如何通信的。

1. 关于Kubernetes Pod

1.1 Kubernetes Pod 是什么?

首先我们来探讨下什么是Pod。Pod是Kubernetes中最小的可部署和管理单元。换句话讲,如果需要在Kubernetes中运行单个容器,那么你就得为这个容器创建一个Pod。同时,一个Pod可以包含多个容器,这些容器往往是紧耦合的。怎么样个紧耦合法呢?试着想象这么一个场景,一个Pod中的多个容器代表需要运行在同一个服务器上的多个进程。这种类比是合理的,因为在许多方面,Pod就类似于一台服务器。比如,通过localhost每个容器可以访问它所在Pod中的其它容器。

1.2 为什么Kubernetes将Pod而不是单个容器作为最小可部署单元呢?

尽管直接部署单个容器也许会更容易,但增加Pod这个新的抽象层会带来新的好处。容器是一个真实存在的实体,它代表一个具体的东西。这个“东西”可以是一个Docker容器,也可以是一个rkt容器。每种“东西”都有不同的用途。为了管理容器,Kubernetes需要更多的信息,比如重启策略(restart policy),它定义了当容器终止了时怎样重启容器;还有活性检测(liveness probe),它定义了如何从应用视角去检测容器中的进程是否活着,比如Web服务器进程是否能响应HTTP请求。

为了避免在容器这个已有的实体上增加这些新的属性,Kubernetes架构师们决定使用一个新的实体,那就是Pod。它逻辑地包含一个或多个容器。

1.3 为什么Kubernetes允许Pod中存在一个或多个容器?

Pod中的容器们运行在一个逻辑“主机”上。他们使用同一个网络命名空间(network namespace,换句话讲,就是同样的IP地址和端口空间),以及同样的IPC(inter-process communication,进程间通信)命名空间,他们还使用共享卷(shared volume)。这些特征使得Pod内的容器能互相高效地通信。同时,Pod使得你可以将多个紧耦合的应用容器当做一个实体来管理。

那么,如果一个应用需要在同一台服务器上运行多个容器,为什么不把所有东西放在一个容器里面呢?好吧,首先,这会违反“一个容器一个进程”规范。这个规范很重要,因为当一个容器中有多个进程时,调试会变得非常困难,因为不同进程的日志会混在一起,而且很难去管理这些进程的生命周期。其次,为一个应用使用多个容器会更简单、更直接、能解耦软件依赖。而且,更细粒度的容器可以在团队间复用。

1.4 多容器Pod的用例

多容器Pod的主要目的是为了支持同时存在的(co-located)及同时被管理的(co-managed)帮助进程(helper process)。帮助进程有几种通用场景:

  • 边车容器(sidecarcontainer):比如日志或数据变化监视器等。一个团队创建日志监视器(log watcher)后,它可以被各种应用使用。另一个边车容器的例子是文件或数据加载器,它负责为主容器产生数据。

  • 代理(Proxy)、桥(bridge)和适配器(adapter):它们将主容器连接到外部世界。比如,Apache HTTP 服务器或nginx 会读取静态文件。它们还能被用作主容器中的web应用的反向代理(reverseproxy)。

当你在Pod中运行多层应用(比如WordPress)时,推荐的方式是为每层使用单独的Pod。最简单的理由是这样你就可以独立地扩展每层,并将他们分布在不同节点上。

2. Pod 中容器间的通信

在Pod中运行多个容器,使得它们之间的通信非常直接。他们自己的通信有几种方法。

2.1 通过共享卷通信

在Kubernetes中,Pod中的容器可以将共享卷当做一种简单和高效的共享数据方式。在大多数场景中,使用主机上的一个目录,并在多个容器间共享,是一种高效的方式。

Kubernetes volume(卷)使得在容器重启后数据能被保存下来。卷具有和Pod一样的生命周期。这意味着,只要Pod存在,卷就存在。如果Pod被删除了,即使一模一样的Pod被创建出来,原来Pod的共享卷也会被销毁,一个新的共享卷会被创建出来。

Pod中的多个容器使用共享卷的一个标准用例是,当一个容器向共享目录写入日志或其它文件时,其它容器从共享目录中读取数据。比如我们创建一个下面的Pod:

apiVersion: v1
kind: Pod
metadata:
name: mc1
spec:
volumes:
- name: html
emptyDir: {}
containers:
- name: 1st
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
- name: 2nd
image: debian
volumeMounts:
- name: html
mountPath: /html
command: ["/bin/sh", "-c"]
args:
- while true; do
date >> /html/index.html;
sleep 1;
done

本例中,定义了一个名为html的卷。它的类型是 emptyDir,这意味着当Pod被分配到一个节点上时,卷会被基于一个空目录创建出来,只要该Pod一直运行着,这个卷就会一直存在。1st容器运行nginx服务器,它将共享卷挂载到/usr/share/nginx/html 目录。2nd容器使用Debian镜像,它将共享卷挂载到 /html目录。每秒钟,2nd容器会将当前日期和时间写入到共享卷之中的index.html文件。当用户向Pod发送HTTP请求时,Nginx读取这个文件的内容并返回给用户。

你可以通过暴露nginx端口或使用浏览器访问它来检查该Pod,或者直接查看容器额共享目录:

$ kubectl exec mc1 -c 1st -- /bin/cat /usr/share/nginx/html/index.html
...
Fri Aug 25 18:36:06 UTC 2017 $ kubectl exec mc1 -c 2nd -- /bin/cat /html/index.html
...
Fri Aug 25 18:36:06 UTC 2017
Fri Aug 25 18:36:07 UTC 2017

2.2 进程间通信(Inter-processCommunication,IPC)

Pod中的容器共享同一个IPC命名空间,这意味着它们可以使用标准的进程间通信方式来互相通信,比如SystemV信号量和POSIX共享内存。

在下面的例子中,我们会定义一个包含两个容器的Pod。它们使用同样的镜像。第一个容器是生产者(producer),它会创建一个标准的Linux消息队列,并向该队列中写入一些随机字符串,最后写入一个特定的退出字符。第二个容器是消费者(consumer),它打开同一个队列,读取字符,直到读到特殊的退出字符为止。我们将Pod的重启策略设置为“Never”,因此在两个容器都终止后Pod会停止。

apiVersion: v1
kind: Pod
metadata:
name: mc2
spec:
containers:
- name: producer
image: allingeek/ch6_ipc
command: ["./ipc", "-producer"]
- name: consumer
image: allingeek/ch6_ipc
command: ["./ipc", "-consumer"]
restartPolicy: Never

Pod 运行后,查看每个容器的日志,确认2nd容器收到了1st容器的全部消息,包括特定的退出消息:

$ kubectl get pods --show-all -w
NAME READY STATUS RESTARTS AGE
mc2 0/2 Pending 0 0s
mc2 0/2 ContainerCreating 0 0s
mc2 0/2 Completed 0 29

默认情况下,Pod中的所有容器都是并行启动的,因为没有办法去指定一个容器在另一个容器启动后才启动。比如,在IPC例子中,有可能第二个容器在第一个容器启动完成并创建消息队列前就启动完毕了。此时,第二个容器会失败,因此它需要消息队列在其启动时就已经存在了。

有一些方法去控制容器的启动顺序,比如 Kubernetes Init Containers(初始化容器),初始化容器会被首先启动。但是在云原生环境中,最好能为所有不可控的失败都做好准备。比如,要修复上述问题,最好的办法是修改应用去等待,直到消息队列被创建出来为止。

2.3 容器间的网络通信

Pod中的容器可以通过“localhost”来互相通信,因为他们使用同一个网络命名空间。而且,对容器来说,hostname就是Pod的名称。因为Pod中的所有容器共享同一个IP地址和端口空间,你需要为每个需要接收连接的容器分配不同的端口。也就是说,Pod中的应用需要自己协调端口的使用。

在下面的例子中,我们会创建一个多容器Pod,其中一个容器中运行Nginx,它作为另一个容器中运行的web应用的反向代理。

(1)步骤1,为nginx配置文件创建一个ConfigMap。从80端口进来的HTTP请求会被转发到localhost上的5000端口。

apiVersion: v1
kind: ConfigMap
metadata:
name: mc3-nginx-conf
data:
nginx.conf: |-
user nginx;
worker_processes 1; error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid; events {
worker_connections 1024;
} http {
include /etc/nginx/mime.types;
default_type application/octet-stream; sendfile on;
keepalive_timeout 65; upstream webapp {
server 127.0.0.1:5000;
} server {
listen 80; location / {
proxy_pass http://webapp;
proxy_redirect off;
}
}
}

(2)步骤2:创建一个两容器Pod,一个容器运行nginx,另一个容器运行简单的web应用。注意我们只为Pod定义了80端口。端口5000不能被从Pod外部访问到。

apiVersion: v1
kind: Pod
metadata:
name: mc3
labels:
app: mc3
spec:
containers:
- name: webapp
image: training/webapp
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: nginx-proxy-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: nginx-proxy-config
configMap:
name: mc3-nginx-conf

查看pod中的端口空间,能看到有80 和 5000端口。

(3)步骤3:将Pod暴露为一个 NodePort服务

$ kubectl expose pod mc3 --type=NodePort --port=80
service "mc3" exposed

(4)步骤4:确认服务

现在,就可以使用浏览器或者curl工具来访问这个web应用了。

nginx容器的80端口上收到的HTTP请求会被转发到web应用容器的5000端口。

上面的例子只展示了在Pod中一个容器去访问其它容器,实际上,更常见的是Pod中的多个容器会在不同的端口上监听,所有这些端口都会被暴露出去。要实现这种形式,要么你创建一个暴露多个端口的服务,要么为每个要被暴露的端口创建一个服务。

3. 小结

通过创建Pod概念,Kubernetes为编排容器的行为以及容器间互相通信都提供了极大的便利。容器们可以共享存储卷,以及可以通过网络甚至IPC互相通信。

感谢您的阅读,欢迎关注我的微信公众号:

(译)Kubernetes中的多容器Pod和Pod内容器间通信的更多相关文章

  1. kubernetes中的Pause容器如何理解?

    前几篇文章都是讲的Kubernetes集群和相关组件的部署,但是部署只是入门的第一步,得理解其中的一些知识才行.今天给大家分享下Kubernets的pause容器的作用. Pause容器 全称infr ...

  2. K8s中的多容器Pod和Pod内容器间通信

    容器(Container)常被用来解决比如微服务的单个问题,但在实际场景中,问题的解决往往需要多容器方案.本文会讨论将多个容器整合进单个Kubernetes Pod 中,以及Pod中的容器之间是如何通 ...

  3. 【K8S学习笔记】Part3:同一Pod中多个容器间使用共享卷进行通信

    本文将展示如何使用共享卷(Volume)来实现相同Pod中的两个容器间通信. 注意:本文针对K8S的版本号为v1.9,其他版本可能会有少许不同. 0x00 准备工作 需要有一个K8S集群,并且配置好了 ...

  4. kubernetes集群中的pause容器

    昨天晚上搭建好了k8s多主集群,启动了一个nginx的pod,然而每启动一个pod就伴随这一个pause容器,考虑到之前在做kubelet的systemd unit文件时有见到: 1 2 3 4 5 ...

  5. 傲视Kubernetes(三):Kubernetes中的Pod

    从本文开始,将正式开始Kubernetes的核心内容学习.首先要了解的是Pod,总共大约分为六篇左右,本篇是第一篇,相信学完之后,我们会对Pod有一个整体的理解. 本文内容: 1.什么是Pod 2.P ...

  6. Kubernetes 实战 —— 03. pod: 运行于 Kubernetes 中的容器

    介绍 pod P53 pod 是 Kubernetes 中最为重要的核心概念,而其他对象仅仅用于 pod 管理. pod 暴露或被 pod 使用. pod 是一组并置的容器,代表了 Kubernete ...

  7. Kubernetes中pod创建流程

    转自:https://blog.csdn.net/yan234280533/article/details/72567261 Pod是Kubernetes中最基本的部署调度单元,可以包含contain ...

  8. Kubernetes中Pod的健康检查

    本文介绍 Pod 中容器健康检查相关的内容.配置方法以及实验测试,实验环境为 Kubernetes 1.11,搭建方法参考kubeadm安装kubernetes V1.11.1 集群 0. 什么是 C ...

  9. Kubernetes中 Pod 是怎样被驱逐的?

    前言 在 Kubernetes 中,Pod 使用的资源最重要的是 CPU.内存和磁盘 IO,这些资源可以被分为可压缩资源(CPU)和不可压缩资源(内存,磁盘 IO).可压缩资源不可能导致 Pod 被驱 ...

随机推荐

  1. 利用zabbix监控ogg进程(Linux平台下)

    前段时间生产的一个数据库的ogg进程挂了快半个月才被发现,已经起不来了,只有重新初始化再同步.因此很有必要监控下ogg的进程,这里给大家介绍如何使用zabbix监控oracle的ogg的进程.思路就是 ...

  2. Python中model转dict

    问题 在query出来的行信息object中有一个dict变量,这个变量存储了字典信息 for u in session.query(User).all(): print u.__dict__ 但是这 ...

  3. nodejs通过钉钉群机器人推送消息

    nodejs 通过钉钉群机器人推送消息 Intro 最近在用 nodejs 写爬虫,之前的 nodejs 爬虫代码用 js 写的,感觉可维护性太差,也没有智能提示,于是把js改用ts(typescri ...

  4. Core源码(二) Linq的Distinct扩展

    先贴源码地址 https://github.com/dotnet/corefx/tree/master/src/System.Linq/src .NET CORE很大一个好处就是代码的开源,你可以详细 ...

  5. Docker是什么、为什么是一种趋势

    Docker的思想来自于集装箱,集装箱解决了什么问题?在一艘大船上,可以把货物规整的摆放起来.并且各种各样的货物被集装箱标准化了,集装箱和集装箱之间不会互相影响.那么我就不需要专门运送水果的船和专门运 ...

  6. Python3在使用requests提示警告InsecureRequestWarning

    解决方法:

  7. 版本管理·玩转git(推到远程仓库)

    经过前面的练习,你在本地的仓库里管理代码已经比较熟练了,但如果是团队开发呢,如何配合起来呢? 我们可以把版本仓库放在互联网上,开发者把自己最新的版本推到线上仓库,同时,把线上仓库的最新代码拉到自己本地 ...

  8. Linux—主机扫描工具(Nmap)

    Nmap包含五项基本功能: 主机探测 (Host Discovery) 端口扫描 (Port Scanning) 版本检测 (Version Detection) 操作系统侦测 (Operating ...

  9. .net core使用百度webupload上传图片

    后端代码: /// <summary> /// 图片上传,上传路径: "/Uploads/Images/" + folder + "/" + sav ...

  10. TensorFlow Federated:基于分散式数据的机器学习

    https://www.tensorflow.org/federated/ TensorFlow Federated (TFF) 是一个开源框架,用于对分散式数据进行机器学习和其他计算.我们开发 TF ...