创建/访问/Service,保护 Service 安全,暴露 Service;
Service连接应用程序
Kubernetes 的网络模型
通常,Docker 使用一种 host-private 的联网方式,在此情况下,只有两个容器都在同一个节点(主机)上时,一个容器才可以通过网络连接另一个容器。为了使 Docker 容器可以跨节点通信,必须在宿主节点(主机)的 IP 地址上分配端口,并将该端口接收到的网络请求转发(或代理)到容器中。这意味着,用户必须非常小心地为容器分配宿主节点(主机)的端口号,或者端口号可以自动分配。
在一个集群中,多个开发者之间协调分配端口号是非常困难的。Kubernetes 认为集群中的两个 Pod 应该能够互相通信,无论他们各自在哪个节点上。每一个 Pod 都被分配自己的 “cluster-private-IP”,因此,无需在 Pod 间建立连接,或者将容器的端口映射到宿主机的端口。因此:
- Pod 中的任意容器可以使用 localhost 直连同 Pod 中另一个容器的端口
- 集群中的任意 Pod 可以使用另一的 Pod 的 cluster-private-IP 直连对方的端口,(无需 NAT 映射)
在集群中部署 Pod
创建文件 run-my-nginx.yaml,文件内容如下
1 | apiVersion: apps/v1 |
执行以下命令,部署 Pod 并检查运行情况:
1 | [root@k8s-master 0408]# kubectl apply -f run-my-nginx.yaml |
执行命令 kubectl get pods -l run=my-nginx -o yaml | grep podIP, 检查 Pod 的 IP 地址,输出结果如下:
1 | [root@k8s-master 0408]# kubectl get pods -l run=my-nginx -o yaml | grep podIP |
在集群中的任意节点上,您可以执行 curl 10.244.235.206 或curl 10.244.44.235` 获得 nginx 的响应。此时:
- 容器并没有使用节点上的 80 端口
- 没有使用 NAT 规则对容器端口进行映射
这意味着,您可以
- 在同一节点上使用 80 端口运行多个 nginx Pod
- 在集群的任意节点/Pod 上使用 nginx Pod 的 clusterIP 访问 nginx 的 80 端口
同 Docker 一样,Kubernets 中,仍然可以将 Pod 的端口映射到宿主节点的网络地址上(使用 nodePort),但是使用 Kubernetes 的网络模型时,这类需求已经大大减少了。
创建 Service
Pod 因为故障或其他原因终止后,Deployment Controller 将创建一个新的 Pod 以替代该 Pod,但是 IP 地址将发生变化。Kubernetes Service 解决了这样的问题。
Kubernetes Service:
- 定义了集群中一组 Pod 的逻辑集合,该集合中的 Pod 提供了相同的功能
- 被创建后,获得一个唯一的 IP 地址(ClusterIP)。直到该 Service 被删除,此地址不会发生改变
- Pod 可以直接连接 Service IP 地址上的端口,且发送到该 IP 地址的网络请求被自动负载均衡分发到 Service 所选取的 Pod 集合中
执行命令 kubectl expose deployment/my-nginx 可以为上面的两个 nginx Pod 创建 Service,输出结果如下所示:
1 | [root@k8s-master 0408]# kubectl expose deployment/my-nginx |
该命令等价于 kubectl apply -f nginx-svc.yaml,其中 nginx-svc.yaml 文件的内容如下所示:
1 | apiVersion: v1 |
该 yaml 文件将创建一个 Service:
- 该 Service 通过 label selector 选取包含
run: my-nginx标签的 Pod 作为后端 Pod - 该 Service 暴露一个端口 80(
spec.ports[*].port) - 该 Service 将 80 端口上接收到的网络请求转发到后端 Pod 的 80 (
spec.ports[*].targetPort)端口上,支持负载均衡
1 | [root@k8s-master 0408]# kubectl get svc my-nginx |
Service 的后端 Pod 实际上通过 Endpoints 来暴露。Kubernetes 会持续检查 Service 的 label selector spec.selector,并将符合条件的 Pod 更新到与 Service 同名(my-nginx)的 Endpoints 对象。如果 Pod 终止了,该 Pod 将被自动从 Endpoints 中移除,新建的 Pod 将自动被添加到该 Endpoint。
执行命令 kubectl describe svc my-nginx,输出结果如下,请注意 Endpoints 中的 IP 地址与上面获得的 Pod 地址相同:
1 | [root@k8s-master 0408]# kubectl describe svc my-nginx |
执行命令 kubectl get ep my-nginx,输出结果如下:
1 | [root@k8s-master 0408]# kubectl get ep my-nginx |
此时,可以在集群的任意节点上执行 curl 10.0.162.149:80,通过 Service 的 ClusterIP:Port 访问 nginx。
Service 的 IP 地址是虚拟地址。
访问 Service
Kubernetes 支持两种方式发现服务:
- 环境变量
- DNS
环境变量
针对每一个有效的 Service,kubelet 在创建 Pod 时,向 Pod 添加一组环境变量。这种做法引发了一个 Pod 和 Service 的顺序问题。例如:
1 | [root@k8s-master 0408]# kubectl exec my-nginx-75897978cd-4t7zb -- printenv | grep SERVICE |
DNS
Kubernetes 提供了一个 DNS cluster addon,可自动为 Service 分配 DNS name。该 addon 已经默认安装。
执行命令 kubectl get services kube-dns --namespace=kube-system 查看该 addon 在集群上是否可用,输出结果如下所示:
1 | [root@k8s-master 0408]# kubectl get services kube-dns --namespace=kube-system |
此时,可以从集群中任何 Pod 中按 Service 的名称访问该 Service。
- 执行命令
kubectl run curl --image=radial/busyboxplus:curl -i --tty获得 busyboxplus 容器的命令行终端,该命令输出结果如下所示:
1 | [root@k8s-master 0408]# kubectl run curl --image=radial/busyboxplus:curl -i --tty |
- 执行命令
nslookup my-nginx,输出结果如下所示:
1 | [ root@curl-69c656fd45-848f6:/ ]$ nslookup my-nginx |
执行命令
curl my-nginx:80,可获得 Nginx 的响应。执行命令
kubectl delete deployment curl可删除刚才创建的curl测试容器
保护 Service 的安全
在将该 Service 公布到互联网时,可能需要确保该通信渠道是安全的。为此:
- 准备 https 证书(购买,或者自签名)
- 将该 nginx 服务配置好,并使用该 https 证书
- 配置 Secret,以使得其他 Pod 可以使用该证书
配置 nginx 使用自签名证书:
- 创建密钥对
1 | [root@k8s-master 0408]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/nginx.key -out /tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx" |
- 将密钥对转换为 base64 编码
1 | cat /tmp/nginx.crt | base64 |
- 创建一个如下格式的 nginxsecrets.yaml 文件,使用前面命令输出的 base64 编码替换其中的内容(base64编码内容不能换行)
1 | apiVersion: "v1" |
- 使用该文件创建 Secrets
1 | #创建 Secrets |
- 修改 nginx 部署,使 nginx 使用 Secrets 中的 https 证书,修改 Service,使其暴露 80 端口和 443端口。nginx-secure-app.yaml 文件如下所示:
1 | apiVersion: v1 |
关于 nginx-secure-app.yaml
- 该文件同时包含了 Deployment 和 Service 的定义
- nginx server 监听 HTTP 80 端口和 HTTPS 443 端口的请求, nginx Service 同时暴露了这两个端口
- nginx 容器可以通过
/etc/nginx/ssl访问到 https 证书,https 证书存放在 Secrets 中,且必须在 Pod 创建之前配置好。
执行命令使该文件生效:
1 | kubectl delete deployments,svc my-nginx |
此时,可以从任何节点访问该 nginx server
1 | kubectl get pods -o yaml | grep -i podip |
创建 curlpod.yaml 文件,内容如下:
1 | apiVersion: apps/v1 |
- 执行命令,完成 curlpod 的部署
1 | [root@k8s-master 0408]# kubectl apply -f curlpod.yaml |
暴露 Service
在应用程序中,可能有一部分功能需要通过 Service 发布到一个外部的 IP 地址上。Kubernetes 支持如下两种方式:
- NodePort
- LoadBalancer
- 需要云环境支持
执行命令查看service暴露的外部端口
[root@k8s-master 0408]# kubectl get svc my-nginx -o yaml | grep nodePort -C 5 spec: clusterIP: 10.107.66.92 externalTrafficPolicy: Cluster ports: - name: http nodePort: 30407 ### port: 80 protocol: TCP targetPort: 80 - name: https nodePort: 30534 ### port: 443 protocol: TCP targetPort: 443 selector: run: my-nginx1
2
3
4
5
6
假设某一节点的公网 IP 地址为 23.251.152.56,可以在任意一台可上网的机器执行命令 `curl https://23.251.152.56:30407 -k`。输出结果为:
```text
...
<h1>Welcome to nginx!</h1>