11、Kubernetes学习总结-Kubernetes Pod 到底是什么?

前言

【译】What are Kubernetes Pods Anyway?最近看到了一条关于Kubernetes Pods的推特,来自了不起的Amy Codes(我真的希望这是她的真名):

 

虽然不是100%准确(容器并不是一个真正的东西。我们将在稍后讨论这个东东)不过它确实指出了一个令人惊奇的事实。看来确实有必要探讨一下pod和容器到底是什么。关于Pods,Kubernetes文档提供了对Pods最好、最完整的解释,但它是用非常一般的意义编写的,使用了很多术语。但我还是建议你好好读读它,因为它比我能写的解释更好、更正确。为官方文档打call!!

是容器,也不是容器

许多人已经知道Linux“容器”并不是真实存在的。Linux中没有所谓的“容器”。众所周知,容器使用了Linux内核中的两个特性(命名空间和cgroups)来执行的普通进程。命名空间允许我们为进程提供一个“视图”,该视图将所有内容隐藏在这些命名空间之外,从而为进程提供自己的运行环境。这使得进程无法看到或干扰其他进程。这里所说的命名空间包括一下几部分:

  • 主机名
  • 进程id
  • 文件系统
  • 网络接口
  • 进程间通信

虽然我在上面说过,在命名空间中运行的进程不能干扰其他进程,但事实并非如此。一个进程可以使用它所运行的物理机器上的所有资源,从而使其他进程无法获得资源。为了限制这一点,Linux有一个称为cgroups的特性。进程可以像在命名空间中一样在cgroup中运行,但是cgroup限制了进程可以使用的资源。这些资源包括CPU、RAM、块I/O、网络I/O等。CPU可以被限制到毫核(一个核的千分之一)或RAM字节级别。进程本身可以正常运行,但是它只能使用cgroup允许的CPU数量,如果超过cgroup上设置的内存限制,就会出现out-of-memory错误。有很多关于命名空间和cgroups的资料可以帮助我们了解更多关于命名空间和cgroups的信息,这里有几个很好的例子:

What even is a container: namespaces and cgroups
[https://jvns.ca/blog/2016/10/10/what-even-is-a-container/][https_jvns.ca_blog_2016_10_10_what-even-is-a-container]
Cgroups, namespaces, and beyond: what are containers made from?
[https://www.youtube.com/watch?v=sK5i-N34im8][https_www.youtube.com_watch_v_sK5i-N34im8](这哥们口音有点难懂

这里我想指出的是,cgroups和每种命名空间类型都是拥有一些独立的特性。上面列出的一些命名空间可以被使用,也可以完全不使用。我们只能使用cgroups或任意两种其他命名空间的组合(当然仍可以使用命名空间,cgroups只是根命名空间,但无所谓)。命名空间和cgroups也可以作用于一组进程上。您使用一个命名空间运行多个进程。这样他们就能看到彼此并相互影响。或者可以在一个单独的cgroup中运行它们,这样,进程将一起在特定数量的CPU和RAM中运行。

组合中的组合

当您正常使用Docker运行容器时,Docker为每个容器创建命名空间和cgroups,容器和命名空间会一一映射。开发人员通常是这样看待容器的。

 

容器

这些容器本质上是独立的仓库,只是它们可能有一个映射到主机的卷或端口映射到主机上,以便它们互相可以进行通信。此外,使用一些额外的命令行参数,我们也可以使用一个命名空间来组合Docker容器。首先,先创建一个nginx容器

 $ cat <<EOF >> nginx.conf
> error_log stderr;
> events { worker_connections 1024; }
> http {
> access_log /dev/stdout combined;
> server {
> listen 80 default_server;
> server_name example.com www.example.com;
> location / {
> proxy_pass http://127.0.0.1:2368;
> }
> }
> }
> EOF
$ docker run -d --name nginx -v pwd/nginx.conf:/etc/nginx/nginx.conf -p 8080:80 nginx

接下来再运行一个 ghost 容器,但这次我们加一些额外的参数,让 ghost容器 共享 nginx 容器的命名空间。

docker run -d --name ghost --net=container:nginx --ipc=container:nginx --pid=container:nginx ghost

现在nginx 容器可以将本地的访问请求转发到ghost容器。比如我们访问 http://localhost:8080/,就会访问到 ghost 主页。这些创建 docker 容器的选项创建了一组运行在同一个命名空间的容器。这个命名空间允许 docker 容器发现并与其他容器互相通信。

&nbsp;

Pod也是容器

现在我们已经可以将命名空间和cgroups与多个进程结合起来,这也正是Kubernetes Pods的功能。Pods允许我们指定要运行的容器,Kubernetes自动以正确的方式设置命名空间和cgroups。当然kubernetes pod会比这稍微复杂一些,因为Kubernetes不使用Docker网络(它使用CNI)。

&nbsp;

一旦我们以这种方式设置容器,每个进程都像是在同一台机器上运行。它们可以在localhost上相互通信,可以使用共享卷。它们甚至可以使用IPC或互相发送HUP或TERM之类的信号(在Kubernetes 1.7中共享PID命名空间,Docker >=1.13)。假设这时现在我们想要运行nginx, 并使用cond更新nginx配置,并在您添加/删除应用程序服务器时重启nginx。假设我们还有一个etcd服务器,它保存后端应用程序服务器的IP地址。当列表发生变化时,cond可以得到一个通知并写出一个新的nginx配置,并向nginx发送一个HUP信号,让nginx重新加载它的配置。

&nbsp;

如果使用Docker这种方式,我们将nginx和cond都放在一个容器中。但因为Docker只能设置一个ENTRYPOINT,所以我们需要使用监控进程来保持两个进程都运行。这种方式并不理想,因为我们需要为运行的每个nginx副本运行监管。但关键是Docker只能看到监控进程。它对每个进程并没有可见性,这意味着您和其他工具无法通过Docker API获得该信息。Nginx可能会崩溃,但Docker并不知道。

&nbsp;

Kubernetes通过pods资源可以管理每个进程,从而洞察其状态。通过这种方式,它可以通过API向用户提供关于该pod状态的信息,还可以提供服务,比如在它崩溃时重新启动它或自动记录日志。

&nbsp;

Pod容器作为API

通过将容器组合到Pods中的方式,我们可以在Pod中创建一些容器,并作为“API”添加到Pods中供其他人使用。这不是一般意义上类似于Web API的API,而是其他pod可以使用的抽象入口。例如,以nginx + confd为例。在这个例子中,condfd对nginx过程一无所知。它只知道需要监视etcd中的值并向进程发送HUP信号或运行命令。这个应用程序不一定是nginx。它可以是任何一种应用。通过这种方式,您可以使用condfd容器镜像和配置,并将其与任意数量的不同类型的pod进行搭配。我们还可以考虑使用其他类型的抽象。像istio这样的服务网格可以作为sidecars上,提供服务路由、遥测和策略执行,而不需要更改主应用程序。我们也可以使用多个sidecar。例如,同时使用condfd sidecar和istio sidecar。应用程序可以通过这种组合方式来构建更加复杂和可靠的系统,同时让每个应用程序相对简单。