這節講一下 k8s 網絡相關的資源類型 service 和 endpoint,關於這一塊,這篇博客講的不錯:https://www.cnblogs.com/moonlight-lin/p/14553119.html
在前面創建的 POD 會發現一個問題,POD的 IP 地址不是固定不變的,對外提供服務時很不方面,使用 Service 資源可以解決這個問題。
Service
先創建一個deployment 資源配置文件,其中 tomcat 容器添加開放的端口聲明,別名為 app008-8080 :
[root@ylserver10686071 ~]# cat deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app008
namespace: prod
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: tomcat
image: tomcat:8.0
ports:
- name: app008-8080
containerPort: 8080
protocol: TCP
創建deployment資源,查看創建的POD IP 地址:
[root@ylserver10686071 ~]# kubectl apply -f deployment.yml
deployment.apps/app008 created
[root@ylserver10686071 ~]# kubectl get pods -n prod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
app008-c7b79f6c-l4xdx 1/1 Running 0 2m11s 10.233.67.33 ylserver10686072 <none> <none>
[root@ylserver10686071 ~]#
創建Service 資源配置文件:
[root@ylserver10686071 ~]# cat service.yml kind: Service apiVersion: v1 metadata: name: app008 namespace: prod spec: type: ClusterIP ports: - name: app008-svc protocol: TCP port: 38080 targetPort: 8080 selector: app: web
-
- type: ClusterIP service類型,不填的話默認為 ClusterIP,其他類型有 NodePort、LoadBalancer、ExternalName、Headless Services
- port: 38080 servcice 暴露的端口
- targetPort: 8080 與 POD 關聯的端口,不填的話默認和 port字段相同,也可以填 POD 端口的別名,這里為 app008-8080
- selector 標簽選擇器,這里關聯了定義 標簽為 app: web 的POD
創建 Service資源,查看 Service 時可以簡寫為 svc:
[root@ylserver10686071 ~]# kubectl apply -f service.yml
service/app008 created
[root@ylserver10686071 ~]# kubectl get svc -n prod -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
app008 ClusterIP 10.233.63.3 <none> 38080/TCP 6s app=web
[root@ylserver10686071 ~]#
請求 svc 暴露的端口檢驗是否訪問正常:
[root@ylserver10686071 ~]# curl -I http://10.233.63.3:38080 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: text/html;charset=UTF-8 Transfer-Encoding: chunked Date: Tue, 20 Jul 2021 09:11:56 GMT [root@ylserver10686071 ~]#
如果網絡插件使用的是ipvs,會生成一個虛擬網口和ClusterIP 對應:
[root@ylserver10686071 ~]# ip a|grep -i14 10.233.63.3 6: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default link/ether ca:3d:52:63:42:78 brd ff:ff:ff:ff:ff:ff inet 10.233.35.162/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.233.20.148/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.233.25.192/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.233.54.175/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.233.0.1/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.233.0.3/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.233.63.3/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever
使用ipvsadm 可以看到 ipvs 轉發規則:
[root@ylserver10686071 ~]# ipvsadm -Ln|grep -4 10.233.63.3 TCP 10.233.35.162:8000 rr -> 10.233.72.33:8000 Masq 1 0 0 TCP 10.233.54.175:443 rr -> 10.233.75.35:8443 Masq 1 2 0 TCP 10.233.63.3:38080 rr -> 10.233.67.33:8080 Masq 1 0 0 TCP 10.233.75.0:38443 rr -> 10.233.67.18:8443 Masq 1 0 0 TCP 127.0.0.1:34654 rr [root@ylserver10686071 ~]# kubectl get pods -n prod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES app008-c7b79f6c-l4xdx 1/1 Running 0 42m 10.233.67.33 ylserver10686072 <none> <none> [root@ylserver10686071 ~]#
ClusterIP只能在 k8s 集群內部才可以訪問到,如果是集群外部的話,可以使用 NodePort 類型映射到宿主機端口上,修改下配置文件,nodePort 為宿主機暴露端口:
[root@ylserver10686071 ~]# cat service.yml kind: Service apiVersion: v1 metadata: name: app008 namespace: prod spec: type: NodePort ports: - name: app008-svc protocol: TCP nodePort: 38090 port: 38080 targetPort: 8080 selector: app: web
更新 service資源配置文件,查看資源信息:
[root@ylserver10686071 ~]# kubectl apply -f service.yml service/app008 configured [root@ylserver10686071 ~]# kubectl get svc -n prod -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR app008 NodePort 10.233.63.3 <none> 38080:38090/TCP 3h1m app=web [root@ylserver10686071 ~]#
當Service 為NodePort 類型時,集群內所有的宿主機都會監聽nodePort端口,並轉發到對應的POD,驗證一下:
[root@ylserver10686071 ~]# ss -anputl|grep 38090 tcp LISTEN 0 128 *:38090 *:* users:(("kube-proxy",pid=11474,fd=16)) [root@ylserver10686072 ~]# ss -anputl|grep 38090 tcp LISTEN 0 128 *:38090 *:* users:(("kube-proxy",pid=11321,fd=16)) [root@ylserver10686073 ~]# ss -anputl|grep 38090 tcp LISTEN 0 128 *:38090 *:* users:(("kube-proxy",pid=11315,fd=16))
其中NodePort 轉發到 POD 主要也是使用ipvs 實現的,如果網絡插件為flannel,則由 iptables 實現,這里就不做展開。
Endpoints
當創建service資源配置了selector時,endpoints 控制器會自動創建 endpoints 資源對象,該資源對象記錄了 svc 和 pod 的一一對應關系,存儲在數據庫etcd中,查看創建的endpoints詳細信息:
[root@ylserver10686071 ~]# kubectl get endpoints -n prod -o wide
NAME ENDPOINTS AGE
app008 10.233.67.33:8080 5m40s
[root@ylserver10686071 ~]# kubectl describe endpoints app008 -n prod
Name: app008
Namespace: prod
Labels: <none>
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2021-07-20T12:56:33Z
Subsets:
Addresses: 10.233.67.33
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
app008-svc 8080 TCP
Events: <none>
[root@ylserver10686071 ~]#
service還有一個用途,可以關聯集群外的主機的端口,假如 集群外 有一台 mysql 主機,IP地址為 10.68.60.57,端口為 3306 ,編寫 service 和 endpoints 資源配置文件,此處的service 不再使用 selector 做關聯 :
[root@ylserver10686071 ~]# cat svc-endpoints.yml apiVersion: v1 kind: Service metadata: name: mysql-svc namespace: prod spec: ports: - protocol: TCP port: 3306 targetPort: 3306 --- apiVersion: v1 kind: Endpoints metadata: name: mysql-svc namespace: prod subsets: - addresses: - ip: 10.68.60.57 ports: - port: 3306
創建 svc 和 endpoints 資源,並查看相關信息:
[root@ylserver10686071 ~]# kubectl apply -f svc-endpoints.yml service/mysql-svc created endpoints/mysql-svc created [root@ylserver10686071 ~]# kubectl get svc -n prod NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE mysql-svc ClusterIP 10.233.56.68 <none> 3306/TCP 7s [root@ylserver10686071 ~]# kubectl get endpoints -n prod NAME ENDPOINTS AGE mysql-svc 10.68.60.57:3306 14s [root@ylserver10686071 ~]#
使用ClusterIP 測試 mysql 是否能夠連接成功:
[root@ylserver10686071 ~]# mysql -uroot -p -h10.233.56.68 Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 1071787 Server version: 5.7.31-log MySQL Community Server (GPL) Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]>
到這里就會有一個疑問,既然用的是ClusterIP ,為何不直接用 mysql 主機的IP 地址呢,其實對於 k8s 集群內的 POD 可以直接使用k8s提供的域名請求集群外的mysql主機,針對的是環境中沒有DNS服務器的情況,有DNS服務器還是建議直接用域名做關聯。
創建一個 鏡像為 mysql:5.7 的 deployment 資源配置文件,使用command可以覆蓋原來images的啟動命令,平常可用來調試:
apiVersion: apps/v1 kind: Deployment metadata: name: mysql-client namespace: prod spec: replicas: 1 selector: matchLabels: mysql: client template: metadata: labels: mysql: client spec: containers: - name: mysql-client image: mysql:5.7 command: ["tail","-f","/dev/null"] - name: busybox image: busybox:latest command: ["tail"] args: ["-f","/dev/null"] - name: tool image: slongstreet/bind-utils:latest command: ["tail","-f","/dev/null"]
創建deployment資源后,進入容器使用 svc 的名稱 mysql-svc 連接 mysql主機:
[root@ylserver10686071 ~]# kubectl exec -it mysql-client-858f464d57-64vbk -n prod -- bash
root@mysql-client-6c566f95cd-7h4x2:/# mysql -uroot -p -hmysql-svc
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1071942
Server version: 5.7.31-log MySQL Community Server (GPL)
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
這里會有一個疑惑,為何可以通過 svc的名稱直接連接到mysql主機呢,查看下POD的 resolv.conf 就知道:
[root@ylserver10686071 ~]# kubectl exec -it mysql-client-858f464d57-64vbk -c mysql-client -n prod -- cat /etc/resolv.conf
nameserver 169.254.25.10
search prod.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
[root@ylserver10686071 ~]#
配置文件resolv.conf 中 options ndots:5 參數表示查詢的域名 . 的個數少於5的時候,會根據 search 中配置的列表依次在對應的域名中先進行搜索,如果沒有返回,則最后再直接查詢域名本身,驗證一下:
[root@ylserver10686071 ~]# kubectl exec -it mysql-client-858f464d57-64vbk -c tool -n prod -- sh / # host -v www.baidu.com Trying "www.baidu.com.prod.svc.cluster.local" Trying "www.baidu.com.svc.cluster.local" Trying "www.baidu.com.cluster.local" Trying "www.baidu.com" ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57120 ;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;www.baidu.com. IN A ;; ANSWER SECTION: www.baidu.com. 28 IN CNAME www.a.shifen.com. www.a.shifen.com. 28 IN A 163.177.151.110 www.a.shifen.com. 28 IN A 163.177.151.109
同理,驗證一下 svc的名稱是如何解析的:
/ # host -v mysql-svc Trying "mysql-svc.prod.svc.cluster.local" ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43733 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;mysql-svc.prod.svc.cluster.local. IN A ;; ANSWER SECTION: mysql-svc.prod.svc.cluster.local. 5 IN A 10.233.56.68 / # nslookup mysql-svc.prod.svc.cluster.local Server: 169.254.25.10 Address: 169.254.25.10:53 Name: mysql-svc.prod.svc.cluster.local Address: 10.233.56.68
實驗結果顯示域名 mysql-svc 的完整域名為 mysql-svc.prod.svc.cluster.local ,解析的IP 地址為 ClusterIP 地址,域名格式是 [servicename].[namespace].svc.cluster.local ,同個namespace 域名可以直接簡寫為 servicename,不同namespace 則需要寫完整的域名,想想這是為什么。
實驗驗證一下,創建一個在 kube-public namespace 下的 deployment資源配置文件:
[root@ylserver10686071 ~]# cat busybox.yml apiVersion: apps/v1 kind: Deployment metadata: name: busybox namespace: kube-public spec: replicas: 1 selector: matchLabels: k8s-app: busybox template: metadata: labels: k8s-app: busybox spec: containers: - name: busybox image: busybox:latest command: - tail - -f - /dev/null
創建deployment資源,並驗證:
[root@ylserver10686071 ~]# kubectl apply -f busybox.yml deployment.apps/busybox created [root@ylserver10686071 ~]# kubectl exec -it busybox-bbf7c9c98-fw97g -n kube-public -- sh / # ping -c4 mysql-svc ping: bad address 'mysql-svc' / # ping -c4 mysql-svc.prod.svc.cluster.local PING mysql-svc.prod.svc.cluster.local (10.233.56.68): 56 data bytes 64 bytes from 10.233.56.68: seq=0 ttl=64 time=0.296 ms 64 bytes from 10.233.56.68: seq=1 ttl=64 time=0.229 ms 64 bytes from 10.233.56.68: seq=2 ttl=64 time=0.340 ms 64 bytes from 10.233.56.68: seq=3 ttl=64 time=0.263 ms --- mysql-svc.prod.svc.cluster.local ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.229/0.282/0.340 ms / # cat /etc/resolv.conf nameserver 169.254.25.10 search kube-public.svc.cluster.local svc.cluster.local cluster.local options ndots:5 / #
ExternalName
上面的實驗中,跨namespace 訪問 svc 需要 填寫完整的域名,如果想跟在同個namespace下 使用 簡寫,借助 externalname 也可以實現該功能。
編寫externalname 類型的 service 資源配置文件:
[root@ylserver10686071 ~]# cat service-extname.yml
kind: Service
apiVersion: v1
metadata:
name: mysql-svc
namespace: kube-public
spec:
type: ExternalName
externalName: mysql-svc.prod.svc.cluster.local
創建資源后,進POD里面驗證一下:
[root@ylserver10686071 ~]# kubectl apply -f service-extname.yml service/mysql-svc created [root@ylserver10686071 ~]# kubectl exec -it busybox-bbf7c9c98-fw97g -n kube-public -- sh / # ping -c4 mysql-svc PING mysql-svc (10.233.56.68): 56 data bytes 64 bytes from 10.233.56.68: seq=0 ttl=64 time=0.212 ms 64 bytes from 10.233.56.68: seq=1 ttl=64 time=0.320 ms 64 bytes from 10.233.56.68: seq=2 ttl=64 time=0.273 ms 64 bytes from 10.233.56.68: seq=3 ttl=64 time=0.208 ms --- mysql-svc ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.208/0.253/0.320 ms / # ping -c4 mysql-svc.prod.svc.cluster.local PING mysql-svc.prod.svc.cluster.local (10.233.56.68): 56 data bytes 64 bytes from 10.233.56.68: seq=0 ttl=64 time=0.205 ms 64 bytes from 10.233.56.68: seq=1 ttl=64 time=0.398 ms 64 bytes from 10.233.56.68: seq=2 ttl=64 time=0.278 ms 64 bytes from 10.233.56.68: seq=3 ttl=64 time=0.214 ms --- mysql-svc.prod.svc.cluster.local ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.205/0.273/0.398 ms / #
總結一下:
- Service資源有四種類型:ClusterIP、NodePort、LoadBalancer、ExternalName、Headless Services;
- Service資源類型為ClusterIP、NodePort 是,Endpoints 控制器會創建 endpoints,記錄對應的POD 地址 或者 自定義的子網地址;
- ExternalName 類型的 Service 可以軟連接到其他 namespace 的 svc ,可以想在同個namespace一樣使用其他namespace的svc地址。