k8s Pod 创建
文件名:deploy-echoserver.yml (这里以 Deployment 的方式来创建与管理 Pod)
apiVersion: apps/v1beta1
kind: Deployment
metadata:
# Deployment 实例名称
name: echoserver
spec:
# 设置 Pod 个数
replicas: 2
template:
metadata:
# 设置 Pod 标签
labels:
app: echoserver
spec:
# 运行 docker 镜像
containers:
- name: echoserver
image: jasonn/php-echoserver
启动命令:
kubectl create -f deploy-echoserver.yml
查看 deployment 启动情况:
对 Pod 的访问情况如下(通过kubectl describe pods获取 Pod 的 IP 地址):
问题
场景1:现在 K8S 上运行着2个 Pod。我们希望通过上述所说的 Service 来整合这两个 Pod 的访问,完成对它们的统一访问,而不用向具体的 Pod 发出请求。
Service 创建脚本内容如下(service-echoserver.yml):
apiVersion: v1
kind: Service
metadata:
# Service 实例名称
name: svc-echoserver
spec:
ports:
- protocol: TCP
# Service 端口地址
port: 8080
# Pod 端口地址
targetPort: 80
selector:
# 匹配符合标签条件的 Pod
app: echoserver
创建 Service 命令:
kubectl create -f service-echoserver.yml
由此,我们创建好了一个 Service,同时也生成了一个对应的 VIP。
查看 Serivce 创建情况:
下面,我们来验证下是否如之前所说,对 VIP 的访问能访问到 Pod 的内容。
场景2:了解了 Service 是通过 label & selecor 来进行整合 Pod 的。那如果 Pod 不存在标签,又或者是在不同 Namespace 下,也可能是 K8S 集群外的一个服务。现实情况往往更加复杂,这样的情况下,Service 又该如何整合。
-
Service 与 Pod 的地址映射关系由谁管理?
这里引出了另一个概念 Endpoints。我们先来看看它的一个具体情况。
发现在 Service 创建的同时,还生成了一个 Endpoints。 该 Endpoints 与 Service 同名,它所暴露的地址信息正是对应 Pod 的地址。由此猜测是 Endpoints 维护了 Service 与 Pod 的映射关系。
为了验证我们的猜测,我们手动删除 Endpoints,发现之前能成功访问到 Pod 的 VIP,现在已经已经访问不到了。
我们在手动把 Endpoints 创建回来,创建脚本如下(endpoint-echoserver.yml):
apiVersion: v1
kind: Endpoints
metadata:
# Endpoints 实例的名称
name: svc-echoserver
subsets:
- addresses:
- ip: 172.17.0.5
- ip: 172.17.0.6
ports:
- port: 80
创建命令:
kubectl create -f endpoint-echoserver.yml
注意:Endpoints 与 Service 的绑定关系通过名称来关联的,所以这两者的名称(name)一定要一致。
如果创建失败,出现的错误信息是“...endpoints "svc-echoserver" already exists”,说明 Service 已经更新了 Endpoints。这里就说到了 Service 会定期去检查 Pod 的状态,并且将结果更新到 Endpoints 上。
VIP 再次访问时又能成功访问到,如图:
现在我们已经能轻松的解决场景2的问题了,在创建 Service 时,只要不设置 Selector 属性,那么将不会自动创建 Endpoints,这是我们可以根据需求手动的创建指定地址(address)的 Endpoints,来解决标签无法实现的整合。
场景3:知道了 Service、Endpoints、Pod 的三者关系后,我们来具体看看所说的代理到底是如何实现的。从之前 K8S 的架构中,我们知道 Service 的代理是由 kube-proxy 实现的。而它的代理模式(Proxy mode)主要有两种:userspace 与 iptables。自 K8S v1.2 开始,默认的代理模式就是 iptables,并且它的性能也是要高于 userspace 的,所以在这儿只讨论 iptables 的实现。
- :kube-proxy 是如何使用 iptables 做到服务代理的?
我们现在要做的呢,是将 VIP 请求给转发到对应的 Pod 上。而实现此的正是 iptables。
了解 iptables 的同学都知道四表五链的概念,而做端口地址转发的呢,主要是在 nat 表中实现。我们下面看一下一个 VIP 请求在 nat 表中是如何一步步被转发到 Pod 上的。
根据 iptables 的机制,请求是先到 nat 表的 PREROUTING 链(chain)上的,它的规则如下:
图中发现,请求首先经过 KUBE-SERVICE 链,其次再到 DOCKER 链上的。
我们看一下 KUBE-SERVICE 的情况:
我们发现 KUBE-SERVICE 中包含了一系列 Service 的规则。根据我们请求的 VIP 的目的地址,对应到了下一个名叫 KUBE-SVC-PRQ3AXYQLQGIVVIU 的 Service 链上。
1.KUBE-SVC-PRQ3AXYQLQGIVVIU 规则如下:
从规则的名字上可以看出,该条 Service 链上记录的是2个 Endpoints 链,具体的选择是通过 50% 的随机性的进行决定(这也是它的一个负载规则)。
-
我们来看第一个名叫 KUBE-SEP-JSFY3ZFM2EVD64VQ 的 Endpoints 链的情况:
从图中,我们已经很清晰的看到了它转发到 Pod 的具体规则。
-
下面以一张简单的流程图,看一下请求的转发情况:
关于 DOCKER 链的跟踪,方法是差不多的,请求 从 nat 表结束后,在到 filter 表中,这里就不加以说明了。
而这些规则的实现正是由 Service、Endpoints 来完成的。我们在创建、更新、以及其自身的检测机制,都会对这些规则进行更新。
场景4:Service 的创建、内部结构以及映射关系,我们都了解了。下面我们就要关心如何优雅的使用它,上面我们都是通过 Service 的 VIP 进行访问的。这存在的问题是,如果有服务与服务之间的调用,难道我还要知道所调用服务的 VIP 不成,对于 VIP 的访问能否更通用一些。
- Service 的服务发现机制是怎样的?
对于服务与服务间的调用,实际上就是 Pod 对 Servie 的调用。而 Pod 是如何发现 Service 的,这里可选择的方式有2种。
我们通过启动一个名为 busybox 的 Pod 来观察这两种方式:
kubectl run -i --tty busybox --image=busybox --restart=Never -- sh
环境变量
在 Pod 中,集群中的 Service 会以环境变量的方式赋值在容器中,我们可以通过 {SERVICE_NAME}_SERVICE_HOST 和 {SERVICE_NAME}_SERVICE_PORT 进行获取(对于有多个 Port 的,可以通过带指定 PORT 名称的变量获得。)
busybox 中 环境变量如下:
dns 解析
第二种方式是通过 kube-dns 对 Service 进行域名解析,同样能达到服务发现的目的。查看 DNS 域名解析配
通过 nslookup 查询 dns 记录
网友评论