深入掌握 Service
spec.type
Service 的类型, 指定 Service 的访问方式, 默认值为 ClusterIP
:
ClusterIP
: 虚拟的服务 IP 地址. 该地址用于 Kubernetes 集群内部的 Pod 访问, 在 Node 上 kube-proxy 通过设置的 iptables 规则进行转发NodePort
: 使用宿主机的端口. 使得能够访问各 Node 的外部客户端通过 Node 的 IP 地址和端口号就能访问服务LoadBalancer
: 使用外部负载均衡器完成到服务的负载分发, 需要在spec.status.loadBalancer
字段指定外部负载均衡器的 IP 地址, 并同时定义nodePort
和clusterIP
, 用于公有云环境
可以通过 kubectl expose
命令来创建 Service: $ kubectl expose <pod/svc/rc/deploy/rs> <对应控制器的name>
可以使用 yaml 文件来声明 Service:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8081
targetPort: 8080
selector:
app: webapp
port
定义了 Service 自己的端口号
targetPort
定义了Pod提供服务的端口号
目前 Kubernetes 提供了两种负载分发策略:
RoundRobin
: 轮询模式SessionAffinity
: 基于客户端 IP 地址进行会话保持的模式, 之后从相同的客户端发起的请求都将被转发到相同的 Pod 上
默认使用 RoundRobin
多端口服务
Service 支持设置将多个端口对应到多个应用服务
外部服务 Service
在某些环境中, 应用系统需要将一个外部数据库作为后端服务进行连接, 或将另一个集群或 namespace 中的服务作为服务的后端
这时可以通过创建一个无 Label Selector 的 Service 来实现
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
由于没有 Label Selector, 所以无法选择后端的 Pod, 所以系统不会自动创建 Endpoint
因此需要手动创建一个和该 Service 同名的 Endpoint, 用于指向实际的后端访问地址:
apiVersion: v1
kind: Endpoints
metadata:
name: external-service
subsets:
- addresss:
- ip: 1.2.3.4
ports:
- port: 80
Headless Service
如果希望自己控制负载均衡的策略, 或者应用程序希望知道属于同组服务的其他实例
Kubernetes 提供了 Headless Service 来实现这种功能, 即不为 Service 设置 ClusterIP
(入口 IP 地址
), 仅通过 Label Selector 将后端的 Pod 列表返回给调用的客户端:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
clusterIP: None # 重点!
selector:
app: nginx
这样, Service 就不具有一个特定的 ClusterIP 地址, 对其进行访问将获得包含 Label app: nginx
的全部 Pod 列表, 然后客户端程序自行决定如何处理这个 Pod 列表
从集群外部访问 Pod 或 Service
将容器应用的端口号映射到物理机
(1) 通过设置容器级别的 hostPort
, 将容器应用的端口号映射到物理机上:
apiVersion: v1
kind: Pod
metadata:
name: webapp
labels:
app: webapp
spec:
containers:
- name: webapp
image: tomcat
ports:
- containerPort: 8080
hostPort: 8081 # 重点!
将容器的 8080
端口映射到物理宿主机的 8081
端口
通过物理宿主机的 IP + 8081
即可访问到这个容器的 8080
端口
(2) 通过设置Pod级别的 spec.hostNetwork=true
, 该 Pod 中所有容器的端口号都将被直接映射到物理宿主机上; hostPort
默认等于 containerPort
将 Service 的端口号映射到物理机
(1) 设置 nodePort
映射到物理机, 同时设置 Service 的类型为 NodePort
:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
type: NodePort
ports:
- port: 8080
targetPort: 8080
nodePort: 8081 # 重点!
selector:
app: webapp
(2) 通过设置 LoadBalancer 映射到云服务提供商提供的 LoadBalancer 地址
这种用法仅用于在公有云服务提供商的云平台上设置 Service 的场景
DNS 服务搭建和配置指南
在集群内需要能够通过服务名对服务进行访问, 通过一个集群范围内的 DNS 服务来完成从服务名 (Service name)到ClusterIP的解析
Ingress (HTTP 7 层 路由机制)
Service 的表现形式为 IP:Port, 即工作在 TCP/IP 层
对于基于 HTTP 的服务来说, 不同的 URL 地址经常对应到不同的后端服务 或者虚拟服务器
这些应用层的转发机制仅通过 Service 机制是无法实现的
使用 Ingress 资源对象, 可以将不同的 URL 的访问请求转发到后端不同的Service, 以实现 HTTP 层的业务路由机制
Kubernetes 使用了一个 Ingress 策略定义和一个具体的 Ingress Controller, 两者结合并实现了一个完整的 Ingress 负载均衡器
Ingress Controller 基于Ingress 规则将客户端请求直接转发到 Service 对应的后端 Endpoint (Pod) 上
这样会跳过 kube-proxy 的转发功能, kube-proxy 不再起作用
如果 Ingress Controller 提供的是对外服务, 则实际上实现的是边缘路由器的功能
要使用 Ingress, 需要创建 Ingress Controller (带一个默认 backend 服务) 和 Ingress 策略配置
创建 Ingress Controller 和默认的 backend 服务
在 Kubernetes 中, Ingress Controller 将以 Pod 的形式运行, 监控 API Server 的 /ingress
接口后端的 backend services, 如果 Service 发生变化, 则 Ingress Controller 应自动更新其转发规则
使用第三方提供的 nginx-ingress-controller image来创建 Ingress Controller. 该 Ingress Controller 以 daemonSet
的形式进行创建, 在每个 Node 上都将启动一个 Nginx 服务
为这个 Nginx 容器设置 hostPort
, 将容器监听的 80 和 443 端口映射到物理机上
使得客户端可以通过 URL 地址 ://物理机IP:80
或者 ://物理机IP:443
来访问该 Ingress Controller
这使得 Nginx 类似于通过 NodePort 映射到物理机的 Service, 成为代替 kube-proxy 的 HTTP 层的 Load Balancer
Ingress 的策略配置技巧
- 转发到单个后端服务上
- 同一域名下, 不同的 URL 路径被转发到不同的服务上
- 不同的域名 (虚拟主机名) 被转发到不同的服务上
- 不使用域名的转发规则
Ingress 的 TLS 安全设置
可以为 Ingress 中的域名进行 TLS 安全证书的设置:
- 创建自签名的密钥和 SSL 证书文件
- 将证书保存到 Kubernetes 中的一个 Secret 资源对象中
- 将该 Secret 对象设置到 Ingress 中
使用 OpenSSL 工具生成密钥和证书文件