点击上方蓝色“Go语言中文网”关注我们,设个星标,每天学习 Go 语言

本文作者:倚天码农,原创授权发布

原文链接:https://segmentfault.com/a/1190000020595811

容器技术是微服务技术的核心技术之一,并随着微服务的流行而迅速成为主流。Docker 是容器技术的先驱和奠基者,它出现之后迅速占领市场,几乎成了容器的代名词。但它在开始的时候并没有很好地解决容器的集群问题。Kubernetes[1]抓住了这个机遇,以容器编排者(Container Orchestration)的身份出现,对容器集群进行管理和调度,现在已经打败了 Docker 成为了容器技术事实上的标准。当然 K8s 内部还是需要 Docker 的,但它的功能范围被大大压缩了,只是负责底层的容器引擎和镜像(Docker Image)管理,成为了容器体系中不可缺少, 但没有存在感的一部分。而绝大部分的对外接口都是由 k8s 来负责。

K8s 核心对象:

相对于简单易学的 Docker 来说,k8s 系统庞杂而且概念众多,同一个功能有很多不同方法来完成,让你无所适从,学习起来要困难的多。对于普通码农来讲不需要建立完整的生产环境,只需要搭建一个本地开发环境,这时你只需要了解 k8s 的核心概念就够了,这样可以大大缩短学习时间。k8s 的一切都是对象(Object),它的核心概念一共只有 4 个,Pod,部署(Deployment),服务(Service)和节点(Node)。另外再加上一个容器镜像(Docker Image),这个是 Docker 引擎的核心。掌握了这五个核心概念,就对容器技术有了基本了解,打下了扎实的基础。

Windows 安装环境:

Windows10 的 Windows 10 企业版, 专业版, 和教育版是可以支持直接安装 K8s 的,但电脑是要支持 Hyper-V 的, 详见这里[2]。由于我的 Windows 是家庭版,只能先安装虚拟机(VirtualBox),再在虚拟机上安装 k8s。我用的 k8s 是Minikube[3],是 k8s 的简化版。另外还安装了 Vagrant(它是管理虚拟机的一个软件)作为界面来管理 VirtualBox。

容器镜像(Docker Image):

任何程序都在容器中运行,k8s 支持多种容器,其中Docker[4]是最流行的。容器镜像(Docker Image)是一个以文件形式存在的运行环境,它的里面是分层的,每一层都在上一层的基础上不断叠加新的功能。容器镜像是由 Dockerfile 创建的。

Dockerfile 是一个文件,里面包含一组已经定义好的 Docker 命令(与 Linux 命令比较相似)。当运行 Dockerfile 时,里面的命令被依次执行,最后生成需要的容器镜像。你再调用 Docker 命令(Docker run)运行容器镜像来生成 Docker 容器,完成之后,应用程序就已经在容器里部署好了。这种方式能够保证每次得到的环境都是一样的。容器比虚拟机强的地方在于,它占用系统资源更少,生成时间更短。创建一个容器的耗时一般是秒级的,而虚拟机是分钟级的。容器镜像的创建效率取决于它的大小,一般来讲容器镜像越小,它的生成时间越短。

下面就是一个“nginx”的 Dockerfile 示例。

FROM alpine:3.2
EXPOSE 80 443
RUN apk add --update nginx && \
rm -rf /var/cache/apk/* && \
mkdir -p /tmp/nginx/client-body
COPY ./nginx.conf /etc/nginx/nginx.conf
VOLUME ["/etc/nginx/sites-enabled", "/etc/nginx/certs", "/etc/nginx/conf.d"]
CMD ["nginx", "-g", "daemon off;"]

任何 Dockerfile 的第一句总是“FROM 。。。”,就是要创建一个 Linux 的运行环境。一般有以下几种:

  • FROM ubuntu:18.04:按照 Linux 的具体版本来创建镜像,这样的 Linux 运行环境是比较完整的,镜像的大小是百兆级别的。

  • FROM alpine:latest : alpine 是一个 精简了的 Linux 运行环境,它的大小是十兆级别的。

  • FROM scratch : scratch 是最小 Linux 运行环境,创建非常快,但它的问题是你不能通过 shell 登录到容器内部,因此我一般不用它。

当用 Vagrant 管理虚拟机时,可以先用 Vagrant 命令启动虚拟机,然后敲入 vagrant ssh,进入虚拟机,系统显示:

PS E:\app2\kub> vagrant ssh
Last login: Sat Sep 28 06:56:11 2019 from 10.0.2.2

然后键入“docker run --name docker-nginx -p 8001:80 nginx”运行 Nginx 镜像。"docker-nginx"是容器的名字,“--name”是名字的参数选项。“-p”表示端口映射,把虚拟机的“8001”端口映射到容器的“80”端口(Nginx 的缺省端口)。“nginx”是镜像的名字,如果本地没有找到“Nginx”镜像,系统会自动从 Docker 镜像库里下载 Nginx 镜像到本地,然后再运行,这个镜像有比较完整的 Linux 系统,因此文件比较大(100M),但也可以凑活着用。命令运行之后,显示:

vagrant@ubuntu-xenial:~$ docker run --name docker-nginx -p 8001:80 nginx

这时 Nginx 已经运行,但还没有任何请求,控制台没有输出。如果名字为“docker-nginx”的容器以前已经被运行,那么你需要删除原来的,再运行上面命令。可以先敲入“docker ps -a”找到所有运行过的容器,再敲入“docker rm 1ec2e3d63537”进行删除,其中“ 1ec2e3d63537”是容器 ID.

切换到另一个虚拟机窗口,敲入 curl localhost:8001,显示:

vagrant@ubuntu-xenial:/usr/bin$ curl localhost:8001
...

Welcome to nginx!


...

这时就出现了 Nginx 的首页,表示 Docker 容器中的 Nginx 已经正常运行。

换回原容器显示窗口,这时有了请求,控制台输出 Nginx 日志。

vagrant@ubuntu-xenial:~$ docker run --name docker-nginx -p 8001:80 nginx
172.17.0.1 - - [28/Sep/2019:07:02:25 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

这时已验证 Docker 镜像是好的,敲入“CTRL-C”退出。

Pod:

Pod 是 k8 是的最基本概念,你可以把它看成是对容器(container)的一个封装,用来管理容器。一个 Pod 里可以管理有一个或多个容器,但一般是一个。Pod 里的所有容器都共享 Pod 的资源和网络。当一个 Pod 不能满足用户需求时,你可以把 Pod 作为复制的最小单元来复制出一个同样的 Pod 来处理用户请求。Pod 支持多种容器,不过一般是用 Docker。你可以用单独的 Pod 配置文件创建 Pod, 也可以把 Pod 的配置信息放在其他的对象(例如 Deployment)的配置文件里面,并与其他对象一起创建,后者更为常见。每一个 Pod 都有一个唯一的 IP 地址,Pod 一旦生成就可以通过 IP 地址进行访问。但一般不这么做,而是通过服务(Service)去间接地去访问。下面就是 Pod 的配置文件。它的解释放在后面的 Deployment 里面。

kind: Pod
apiVersion: v1
metadata:
labels:
app: nginx-app
spec:
containers:
- name: nginx-container
image: nginx:latest
restartPolicy: Never

Pod 模板(Pod Templates)

Pod 模板是嵌入在其他 K8s 对象(Object)中的 Pod 的配置说明,例如 Replication Controllers, Jobs, 和 DaemonSets. 这时,Pod 不是单独创建的,而是由其它对象来创建,其中最常用的是 Deployment。

部署(Deployment):

Deployment 是比 Pod 更高一层的对象,它的主要作用是管理 Pod 集群,它里面可以有一个或多个 Pod, 每一个 Pod 在功能上都是等同的。一般在 Deployment 里配置多个 Pod 以实现负载均衡和容错。在配置 Deployment 时,你需要指定 Pod 拷贝的个数,Deployment 会自动管理它里面的 Pod。当某个 Pod 宕机时,Deployment 能自动复制一个新的 Pod 并替换宕机的 Pod,

下面就是 Deployment 的配置文件(nginx-deployment.yaml)。在正方形灰框内(从 template 开始)的是嵌入在 Deployment 里的 Pod 的设置,灰框上面的是部署(Deployment)的设置。当你运行这个配置文件时,它会创建一个 Deployment,同时也会创建嵌入在里面的 Pod。这就是为什么我们一般不需要单独的 Pod 的配置文件,因为已经把它嵌入在了 Deployment 里了。

446e0b0a59501a55e1b2db104c1bf57a.png
file

键入“kubectl create -f nginx-deployment.yaml”来运行这个部署,显示:

vagrant@ubuntu-xenial:~/dockerimages/kubernetes/nginx$ kubectl create -f nginx-deployment.yaml
deployment.apps/nginx-deployment created

这时部署已经成功,现在就可以访问它了。每个 Pod 都有自己的 k8s 集群内部 IP 地址,我们这时只能在 K8s 内部用 IP 地址进行访问。键入下面命令查看 Pod 地址,里面有两个“Nginx”Pod,因为 Deployment 里面是两个 Pod 的集群。

vagrant@ubuntu-xenial:~/nginx$ kubectl get pods -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
NAME IP
hello-minikube-856979d68c-74c65 172.17.0.3
nginx-deployment-77fff558d7-bhbbt 172.17.0.10
nginx-deployment-77fff558d7-v6zqw 172.17.0.9

打开另一个窗口,连入虚拟机,然后键入以下命令“kubectl exec -ti hello-minikube-856979d68c-74c65 -- /bin/sh”登录到 k8s 集群内部,就能访问 Nginx 了。这里“hello-minikube-856979d68c-74c65”是"Minikube"Pod 的名字。“172.17.0.10”是其中一个 Pod 的内部 IP 地址。

vagrant@ubuntu-xenial:~$ kubectl exec -ti hello-minikube-856979d68c-74c65 -- /bin/sh
# curl 172.17.0.10

服务(Service):

Service 是最上层的 k8s 的对象,可以看成我们平常说的微服务。对服务来讲最重要的就是服务注册和发现。在 k8s 中,Service 就是用来实现这个功能的。下面就是 Service 的配置文件(nginx-service.yaml)。一般来说调用服务需要知道三个东西,IP 地址,协议和端口,例如"http://10.0.2.1:80". 但我们不想用 IP,而是用名字来寻址,这就需要 DNS。DNS 在 k8s 集群内部实现了基于服务名的寻址。下面是 Service 的配置文件。Service 通过“selector”来与 Pod 进行绑定,这里它把请求转发给标签“app”是“nginx-app”的 Pod。“nodePort”给服务创建了一个外部可以访问的端口,这样在虚拟机上就可以直接访问服务,而不必登录到 k8s 集群里。

1055afd929455ba2789a980dee8723d3.png
file

运行以下命令“kubectl create -f nginx-service.yaml”创建服务。


服务创建完成之后,调用以下命令显示当前的所有服务, 现在就有了“nginx-service”服务。“80”是服务的内部端口,“30163”是服务的外部端口。

vagrant@ubuntu-xenial:~/$ kubectl create -f nginx-service.yaml
service/nginx-service created

因为已经通过“NodePort”对外开放了端口,现在不必登录到 k8s 内部,在虚拟机上就可以访问服务了. 你可以键入“localhost”

vagrant@ubuntu-xenial:~/$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 35d
nginx-service NodePort 10.109.7.249 80:30163/TCP 18s

Service 和 Deployment 的区别:

Deployment 是用来管理集群的,与 Pod 绑定,但你不能直接访问 Deployment。服务(Service)是为了方便用名字(而不是 IP 地址)访问。但这也只是在集群内部。你可以登录 k8s 集群内部,然后键入下面命令访问服务,但在虚拟机上(k8s 外面)就不行, 这时只能用 IP 地址。因为 DNS 只是在 k8s 内部才起作用。

# curl nginx-service

节点(Node):

Node 刚开始接触时容易和 Pod 搞混,但它相当于虚拟机或物理机,而 Pod 相当于容器。所有的的 Pod 或容器都是部署在 Node 上面。Minikube 只支持单 Node,但你可以在它里面部署多个 Pod。下面是 Node 示意图。

5e02762ce529bdc7a571dab6999b2245.png
file

k8s 核心概念之间的关系:

对象间关系:

下面是 k8s 的一个简单结构图:

412fd5f5736ae434f747f7b6df75bd55.png
file

图中每个菱形是一个 Node,中间的 Node 是 Master Node,其余三个是 Worker Node。Master Node 负责管理 Worker Node。Worker Node 是真正干活的 Node。Master Node 里有各种控制器,Deployment 就是由控制器来管理的,它在中间的管理 Node 里,他里面有两个部署,A 和 B,分别对应服务 A 和服务 B。“服务 A”部署在最下面的 Node 里,它里面只有一个 Pod。“服务 B”部署在上面的两个 Node 里,左边的 Node 只有一个 Pod,右边的 Node 有两个 Pod,这是一个有三个 Pod 的集群。当一个部署里有多个 Pod 时,一般是把它们部署在不同的 Node 上,这样即使一个 Node 宕机,Deployment 仍然可用。

对象绑定:

下图是介绍对象之间如何绑定的关系图。

e46fd2ae0717df2130b6edf7120f2c1f.png
file

每个对象都有多个标签(Label),“app”就是一个用来标识 Pod 对象的标签。图里有两个 Pod,它们的“app”标签的值分别为“A”和“B”,其中 Pod “B”是集群,而 Pod “A”不是。服务(Service)和部署(Deployment)都通过标签选择器(Label Selector)来绑定与之匹配的 Pod。

附录:

你最好是已经安装了 k8s 和 Docker 环境,那就可以依次运行本文中的示例。如果你没有环境,新建一个也不难。如果你不愿意创建环境, k8s 的官网有一个练习环境,可以直接用来运行命令,详见这里[5]

推荐阅读

  • 『GCTT 出品』Go 语言中的包装一个微服务样板

317f7b6c40c2b024b1aacf8ddf425fa6.png

参考资料

[1]

Kubernetes: https://kubernetes.io/

[2]

详见这里: https://minikube.sigs.k8s.io/docs/start/windows/

[3]

Minikube: https://github.com/kubernetes/minikube

[4]

Docker: https://www.docker.com/

[5]

详见这里: https://kubernetes.io/docs/tutorials/kubernetes-basics/

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐