服务:一种为一组功能相同的 Pod 提供单一不变的接入点的资源。服务 IP 和端口不会改变
对服务的连接会被路由到提供该服务的任意一个 Pod 上(负载均衡)
服务通过标签选择器判断哪些 Pod 属于服务
WHY Service
Pod 需要对集群内部其他 Pod 或集群外部客户端 HTTP 请求作出响应
Pod 生命周期短,随时启动或关闭。K8s 在 Pod 启动前为其分配 IP 地址,因此客户端不能提前知道 Pod 的 IP 地址
多个 Pod 可能提供相同的服务,因此需要单一的 IP 地址访问
1. 创建服务 kubectl expose ... apiVersion: v1 kind: Service metadata: name: kubia spec: selector: # 该服务管理 app=kubia 的 Pod app: kubia ports: - port: 80 # 服务暴露端口 targetPort: 8080 # 服务将连接转发到的容器端口测试
# 该服务集群 IP 为 10.111.249.153,只能在集群内部访问 $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubia ClusterIP 10.111.249.153 <none> 80/TCP 2d16h # 1. 在 K8s 节点 curl $ curl 10.111.249.153 You've hit kubia-5fje3 # 2. 在运行的 Pod 容器中发送 curl 命令(`--`表示 kubectl 命令的结束,后跟 Pod 内部执行的指令) $ kubectl exec kubia-7nog1 -- curl -s You've hit kubia-gzwli (1) 服务会话亲和性让同一个 client IP 的请求转发到同一个 Pod
spec: sessionAffinity: ClientIP # 默认 None(仅支持这两种)服务会话亲和性不能基于 Cookie
K8s 服务不是在 HTTP 层面上工作。服务处理 TCP 和 UDP 包,并不关心其中的荷载内容。而 cookie 是 HTTP 协议的一部分,服务并不知道它们
会话亲和性和 Web 浏览器
浏览器使用 keep-alive 连接,通过单个连接发送所有请求,而 curl 每次打开一个新连接。服务在连接级别工作,因此当首次与服务连接时会随机,但属于该连接的所有网络数据包全部发送到单个 Pod(即使服务会话亲和性设置为 None),直到连接关闭
(2) 服务暴露多个端口 spec: ports: - name: http # 多个端口的服务必须指定端口名字 port: 80 targetPort: 8080 - name: https port: 443 targetPort: 8443 (3) 使用命名的端口(推荐) kind: Pod spec: containers: - name: kubia ports: - name: http # 端口 8080 被命名为 http containerPort: 8080 - name: https containerPort: 8443 kind: Service spec: ports: - name: http port: 80 targetPort: http # 映射到容器中被称为 http 的端口 - name: https port: 443 targetPort: https 2. 服务发现Pod 获取服务 IP 和端口
(1) 通过环境变量发现服务Pod 启动时,K8s 会初始化一系列环境变量指向现存的服务。若服务早于 Pod 创建,Pod 进程便可根据环境变量获取服务信息
规则:如名为backend-database的服务会生成BACKEND_DATABASE_SERVICE_HOST和BACKEND_DATABASE_SERVICE_PORT两个环境变量
$ kubectl exec kubia-3inly env KUBIA_SERVICE_HOST=10.111.249.153 KUBIA_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 决定
$ 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 端口)或从环境变量中获取端口号
$ kubectl exec -it kubia-3inly bash root@kubia-3inly:/# curl kubia.default.svc.cluster.local You've hit kubia-3inly # 若两个 Pod 在同一个命名空间,可直接使用服务名称 root@kubia-3inly:/# curl kubia You've hit kubia-5asi2 # 服务的集群 IP 为虚拟 IP,且只有与服务端口结合时才有意义 root@kubia-3inly:/# ping kubia 6 packets transmitted, 0 packets received, 100% packet loss 二、连接集群外部的服务服务将请求重定向到外部 IP 和端口
1. 服务 Endpoint服务并不是和 Pod 直接相连,而是通过 Endpoint 资源:暴露一个服务的 IP 地址和端口的列表