一、服务概述 1. 服务简介
Service:为一组功能相同的 Pod 提供单一不变的接入点 的资源
服务创建后,其 IP 和端口不会改变。访问服务的 IP 和端口会被路由到提供该服务的任意一个 Pod 上
服务通过标签选择器 判断哪些 Pod 属于该服务
服务的必要性:
Pod 需要对集群内部其他 Pod 或集群外部客户端的 HTTP 请求作出响应
Pod 生命周期短,随时启动或关闭。K8s 在 Pod 启动前为其分配 IP 地址,因此客户端不能提前知道 Pod 的 IP 地址
多个 Pod 可能提供相同的服务,因此需要单一的 IP 地址访问
2. 创建服务 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 $ kubectl expose rc nginx --port=80 --target-port=8000 $ cat svc.yaml apiVersion: v1 kind: Service metadata: name: kubia spec: selector: app: kubia ports: - port: 80 targetPort: 8080 $ kubectl apply -f svc.yaml $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubia ClusterIP 10.111.249.153 <none> 80/TCP 2d16h $ curl 10.111.249.153 You've hit kubia-5fje3 $ kubectl exec kubia-7nog1 -- curl -s http://10.111.249.153 You' ve hit kubia-gzwli
3. 服务的会话亲和性
将来自同一个 client IP 的请求转发到同一个 Pod
1 2 spec: sessionAffinity: ClientIP
Kubernetes 仅仅支持两种形式的会话亲和性服务:None 和 ClientIP
不支持 Cookie:服务不是在 HTTP 层面上工作。服务处理 TCP 和 UDP 包,并不关心其中的荷载内容。而 Cookie 是 HTTP 协议的一部分
会话亲和性和 Web 浏览器 :浏览器使用 keep-alive 连接,通过单个连接发送所有请求。服务在连接级别工作,因此当首次与服务连接时会随机,然后将属于该连接的所有网络数据包全部发送到单个 Pod(即使服务会话亲和性设置为 None),直到连接关闭。而 curl 每次打开一个新连接
4. 服务暴露多个端口
多个端口的服务必须指定端口名字
标签选择器作用于整个服务,不能对每个端口做单独的配置
1 2 3 4 5 6 7 8 9 10 spec: ports: - name: http port: 80 targetPort: 8080 - name: https port: 443 targetPort: 8443 selector: app: kubia
5. 使用命名的端口(推荐)
1 2 3 4 5 6 7 8 9 kind: Pod spec: containers: - name: kubia ports: - name: http containerPort: 8080 - name: https containerPort: 8443
1 2 3 4 5 6 7 8 9 kind: Service spec: ports: - name: http port: 80 targetPort: http - name: https port: 443 targetPort: https
6. 服务发现
(1) 通过环境变量发现服务
Pod 启动时,K8s 会初始化一系列环境变量指向现存的服务 。若服务早于 Pod 创建,Pod 进程便可根据环境变量获取服务信息
1 2 3 4 5 6 $ kubectl exec kubia-3inly -- env KUBIA_SERVICE_HOST=10.111.249.153 KUBIA_SERVICE_PORT=80 BACKEND_DATABASE_SERVICE_HOST=10.111.249.155 BACKEND_DATABASE_SERVICE_PORT=80 ...
(2) 通过 DNS 发现服务
kube-system 命名空间下的 kube-dns
Pod 运行 DNS 服务,集群中的其他 Pod 都被配置成使用其作为 DNS(K8s 通过修改每个容器的 /etc/resolve.conf
实现)
运行在 Pod 上的进程查询 DNS 时都会被 K8s 自身的 DNS 服务器响应,该服务器知道系统中运行的所有服务
Pod 是否使用内部的 DNS 服务器是根据 Pod 中 spec.dnsPolicy
决定
1 2 3 4 5 6 7 $ kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 3d20h $ kubectl exec kubia-3inly -- cat /etc/resolv.conf nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local ...
每个服务从内部 DNS 服务器中获得一个 DNS 条目,客户端 Pod 在知道服务名称的情况下可通过 FQDN(全限定域名)来访问,格式为:<服务名称>.<服务命名空间>.svc.cluster.local
。其中 svc.cluster.local
是在所有集群本地服务名称中使用的可配置集群域后缀
客户端仍需知道服务的端口号。服务可直接使用标准端口号(如 HTTP 的 80 端口或 Postgres 的 5432 端口)或从环境变量中获取端口号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ kubectl exec -it kubia-3inly -- bash root@kubia-3inly:/ You've hit kubia-3inly # 若两个Pod在同一个命名空间,可直接使用服务名称 root@kubia-3inly:/# curl kubia You' ve hit kubia-5asi2root@kubia-3inly:/ search default.svc.cluster.local svc.cluster.local cluster.local ... root@kubia-3inly:/ 6 packets transmitted, 0 packets received, 100% packet loss
二、连接集群外部的服务
1. Endpoint
服务并不是和 Pod 直接相连,而是通过 Endpoint 资源:暴露一个服务的 IP 和端口的列表
服务的 Pod 选择器仅用来构建 IP 和端口列表,然后存储在 Endpoint 资源中 。当客户端连接到服务时,服务代理会从列表中选择一个进行重定向
1 2 3 $ kubectl get endpoints kubia NAME ENDPOINTS AGE kubia 10.108.1.4:8080,10.108.2.5:8080,10.108.2.6:8080 2h
2. 手动配置 Endpoint
创建不包含 Pod 选择器的服务将不会创建 Endpoint 资源 ,此时需要手动创建 Endpoint
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: v1 kind: Service metadata: name: external-service spec: ports: - port: 80 --- apiVersion: v1 kind: Endpoints metadata: name: external-service subsets: - addresses: - ip: 11.11 .11 .11 - ip: 22.22 .22 .22 ports: - port: 80
3. 为外部服务创建别名
除了手动配置 Endpoint,还可以通过 FQDN(完全限定域名)访问外部服务
1 2 3 4 5 6 7 8 9 apiVersion: v1 kind: Service metadata: name: external-service spec: type: ExternalName externalName: someapi.somecompany.com ports: - port: 80
Pod 通过 external-service.default.svc.cluster.local
就能访问外部服务
ExternalName 服务仅在 DNS 级别实施,即为服务创建了简单的 CNAME DNS 记录。因此连接到服务的客户端将直接连接到外部服务,完全绕过服务代理。因此该类型服务不会获得集群 IP
CNAME 记录指向完全限定域名而不是数字 IP 地址
三、headless 服务
如果客户端需要连接到所有的 Pod,就需要找到每个 Pod 的 IP。一种选择是通过调用 API 服务器获取 Pod 及其 IP 地址列表,不太好
K8s 允许客户通过 DNS 查找发现 Pod IP 。通常,当执行服务的 DNS 查找时,DNS 服务器会返回单个 IP,即服务的集群 IP。但是,如果将服务的 clusterIp 字段设置为 None,表明不需要为服务提供集群 IP,此时 DNS 服务器将不再返回单个 DNS A 记录,而是返回多个 A 记录,每个记录指向支持该服务的 Pod 的 IP
1. 创建 headless 服务 1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: Service metadata: name: kubia-headless spec: clusterIP: None selector: app: kubia ports: - port: 80 targetPort: 8080
2. 通过 DNS 发现 Pod 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ kubectl run dnsutils --image=tutum/dnsutils --generator=run-pod/v1 --command -- sleep infinity pod/dnsutils created $ kubectl exec dnsutils -- nslookup kubia-headless ... Name: kubia-headless.default.svc.cluster.local Address: 10.42.0.20 Name: kubia-headless.default.svc.cluster.local Address: 10.42.0.19 $ kubectl exec dnsutils -- nslookup kubia ... Name: kubia.default.svc.cluster.local Address: 10.43.99.228
headless 服务通过 DNS 轮询机制提供 Pod 的负载均衡,而非通过服务代理