五、k8s入门系列----service、endpoint

  这节讲一下 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
/ # 

 

  总结一下:

  1.   Service资源有四种类型:ClusterIP、NodePort、LoadBalancer、ExternalName、Headless Services;
  2.   Service资源类型为ClusterIP、NodePort 是,Endpoints 控制器会创建 endpoints,记录对应的POD 地址  或者 自定义的子网地址;
  3.   ExternalName 类型的 Service 可以软连接到其他 namespace 的 svc ,可以想在同个namespace一样使用其他namespace的svc地址。

 

 

  

posted @ 2021-07-20 23:04  梦君子  阅读(5107)  评论(0编辑  收藏  举报