1.容器及其三要素
1.1.容器是什么
容器的本质是一种特殊的进程。
在linux容器中有三个重要的概念:Namespace、Cgroups、rootfs。
Namespace做隔离,让进程只能看到Namespace中的世界;
Cgroups 做限制,让这个“世界”围着一个看不见的墙。
rootfs 做文件系统,rootfs 只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。
这样就实现了进程在我们所看到的一个与世隔绝的房间,这个房间就是Pass项目赖以生存的"沙盒"。
1.2.Namespace
进入容器后,ps命令看到的容器的应用进程都是1号进程,这个其实是pid namespace导致,他其实就是个障眼法,
让你看到的是类似于一个新的虚拟机新环境,其实是不一样的,容器就是一个运行的进程,而容器中的其他进程则是pid为1的子进程。
除了刚刚pid namespace,还有其它的namespace如下:
容器是怎么新建namespace的?
docker创建容器,其实就是linux系统的一次fork的调用,
在进行fork调用时,会传入一些flag参数,这个参数可以控制对linux内核调用新的namespace。
1.3.rootfs
挂载在容器根目录上、用来为容器进程提供隔离后执行环境的文件系统,就是所谓的“容器镜像”。它还有一个更为专业的名字,叫作:rootfs(根文件系统)。
容器的rootfs由三部分组成,1:只读层、2:可读写层、3:init层
1.只读层:都以增量的方式分别包含了 操作系统的一部分。
2.可读写:就是专门用来存放你修改 rootfs 后产生的增量,无论是增、删、改,都发生在这里。而当我们使用完了这个被修改过的容器之后,还可以使用 docker commit 和 push 指令,保存这个被修改过的可读写层,并上传到 Docker Hub 上,供其他人使用;而与此同时,原先的只读层里的内容则不会有任何变化。这,就是增量 rootfs 的好处。
3.Init 层:是 Docker 项目单独生成的一个内部层,专门用来存放 /etc/hosts、/etc/resolv.conf 等信息。
5.kubernetes Service:让客户端发现pod并与之通信
5.1.Service介绍
5.1.1.Serice简介
5.1.1.1什么是Service
service是k8s中的一个重要概念,主要是提供负载均衡和服务自动发现。
Service 是由 kube-proxy 组件,加上 iptables 来共同实现的。
5.1.1.2.Service的创建
创建Service的方法有两种:
1.通过kubectl expose创建
1 2 3 4 5 6 7 |
|
2.通过yaml文件创建
创建一个名为hostnames-yaohong的服务,将在端口80接收请求并将链接路由到具有标签选择器是app=hostnames的pod的9376端口上。
使用kubectl creat来创建serivice
1 2 3 4 5 6 7 8 9 10 11 12 |
|
5.1.1.3.检测服务
使用如下命令来检查服务:
1 2 3 |
|
5.1.1.4.在运行的容器中远程执行命令
使用kubectl exec 命令来远程执行容器中命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
5.2.连接集群外部的服务
5.2.1.介绍服务endpoint
服务并不是和pod直接相连的,介于他们之间的就是Endpoint资源。
Endpoint资源就是暴露一个服务的IP地址和端口列表。
通过service查看endpoint方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
直接查看endpoint信息方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
5.2.2.手动配置服务的endpoint
如果创建pod时不包含选择器,则k8s将不会创建endpoint资源。这样就需要创建endpoint来指的服务的对应的endpoint列表。
service中创建endpoint资源,其中一个作用就是用于service知道包含哪些pod。
5.2.3.为外部服务创建别名
除了手动配置来访问外部服务外,还可以使用完全限定域名(FQDN)访问外部服务。
1 2 3 4 5 6 |
|
1 |
|
服务创建完成后,pod可以通过external-service.default.svc.cluster.local域名(甚至是external-service)连接外部服务。
5.3.将服务暴露给外部客户端
有3种方式在外部访问服务:
1.将服务的类型设置成NodePort;
2.将服务的类型设置成LoadBalance;
3.创建一个Ingress资源。
5.3.1.使用nodeport类型的服务
NodePort 服务是引导外部流量到你的服务的最原始方式。NodePort,正如这个名字所示,在所有节点(虚拟机)上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。
YAML 文件类似如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
这种方法有许多缺点:
1.每个端口只能是一种服务
2.端口范围只能是 30000-32767
如果节点/VM 的 IP 地址发生变化,你需要能处理这种情况
基于以上原因,我不建议在生产环境上用这种方式暴露服务。如果你运行的服务不要求一直可用,或者对成本比较敏感,你可以使用这种方法。这样的应用的最佳例子是 demo 应用,或者某些临时应用。
5.3.2.通过Loadbalance将服务暴露出来
LoadBalancer 服务是暴露服务到 internet 的标准方式。在 GKE 上,这种方式会启动一个 Network Load Balancer[2],它将给你一个单独的 IP 地址,转发所有流量到你的服务。
通过如下方法来定义服务使用负载均衡
1 2 3 4 5 6 7 8 9 10 11 |
|
何时使用这种方式?
如果你想要直接暴露服务,这就是默认方式。所有通往你指定的端口的流量都会被转发到对应的服务。它没有过滤条件,没有路由等。这意味着你几乎可以发送任何种类的流量到该服务,像 HTTP,TCP,UDP,Websocket,gRPC 或其它任意种类。
这个方式的最大缺点是每一个用 LoadBalancer 暴露的服务都会有它自己的 IP 地址,每个用到的 LoadBalancer 都需要付费,这将是非常昂贵的。
5.4.通过Ingress暴露服务
为什么使用Ingress,一个重要的原因是LoadBalancer服务都需要创建自己的负载均衡器,以及独有的公有Ip地址,而Ingress只需要一个公网Ip就能为许多服务提供访问。
5.4.1.创建Ingress资源
Ingress 事实上不是一种服务类型。相反,它处于多个服务的前端,扮演着“智能路由”或者集群入口的角色。
你可以用 Ingress 来做许多不同的事情,各种不同类型的 Ingress 控制器也有不同的能力。
编写如下ingress.yml文件
1 2 3 4 5 6 7 8 9 10 11 12 |
|
通过如下命令进行查看ingress
1 |
|
5.4.2.通过Ingress访问服务
通过kubectl get ing命令进行查看ingress
1 2 3 |
|
了解Ingress的工作原理
何时使用这种方式?
Ingress 可能是暴露服务的最强大方式,但同时也是最复杂的。Ingress 控制器有各种类型,包括 Google Cloud Load Balancer, Nginx,Contour,Istio,等等。它还有各种插件,比如 cert-manager[5],它可以为你的服务自动提供 SSL 证书。
如果你想要使用同一个 IP 暴露多个服务,这些服务都是使用相同的七层协议(典型如 HTTP),那么Ingress 就是最有用的。如果你使用本地的 GCP 集成,你只需要为一个负载均衡器付费,且由于 Ingress是“智能”的,你还可以获取各种开箱即用的特性(比如 SSL、认证、路由等等)。
5.4.3.通过相同的Ingress暴露多少服务
1.将不同的服务映射到相同的主机不同的路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
2.将不同的服务映射到不同的主机上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
5.4.4.配置Ingress处理TLS传输
客户端和控制器之间的通信是加密的,而控制器和后端pod之间的通信则不是。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
5.5.pod就绪后发出信号
5.5.1.介绍就绪探针
就绪探针有三种类型:
1.Exec探针,执行进程的地方。容器的状态由进程的退出状态代码确定。
2.HTTP GET探针,向容器发送HTTP GET请求,通过响应http状态码判断容器是否准备好。
3.TCP socket探针,它打开一个TCP连接到容器的指定端口,如果连接建立,则认为容器已经准备就绪。
启动容器时,k8s设置了一个等待时间,等待时间后才会执行一次准备就绪检查。之后就会周期性的进行调用探针,并根据就绪探针的结果采取行动。