1、理论基础

1)问题

Pod生成后会自动获取一个集群内部IP,Pod重新调度后,会重新分配IP,IP网段由安装时指定的参数—pod-network-cidr决定。

Kubernetes Pod 是有生命周期的。 它们可以被创建,而且销毁之后不会再启动。 如果你使用 Deployment 来运行你的应用程序,则它可以动态创建和销毁 Pod。

每个 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一时刻运行的 Pod 集合可能与稍后运行该应用程序的 Pod 集合不同。

这导致了一个问题: 如果一组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用工作量的后端部分?

2)Service资源

Service是一种为一组功能相同的pod提供单一不变的接入点的资源。service存在时,它的IP和端口不会改变。通常,service通过标签和指定pod进行匹配。
image.png

3)网络实现

service IP为集群中的虚拟IP,不是实际存在的,所以是无法PING。service网络由每个节点上运行的kube-proxy实现,kube-proxy常见实现模式有两种:
1) iptables (默认)
2)IPVS
查看你的集群中的实现模式:

  1. [root@clientvm ~]# kubectl get configmaps kube-proxy -n kube-system -o yaml | grep -i mode
  2. detectLocalMode: ""
  3. mode: ""

如果没有设置,默认为iptables。
更改kube-proxy的实现模式需要在安装集群时,修改集群配置文件,增加kube-proxy相关配置。
https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/README.md

  1. ...
  2. ---
  3. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  4. kind: KubeProxyConfiguration
  5. clusterCIDR: "10.244.0.0/16"
  6. mode: "ipvs"
  7. ipvs:
  8. minSyncPeriod: 5s
  9. syncPeriod: 5s
  10. scheduler: "wrr"

查看IPVS网络

  1. [root@master ~]# yum -y install ipvsadm
  2. [root@master ~]# ipvsadm -ln
  3. IP Virtual Server version 1.2.1 (size=4096)
  4. Prot LocalAddress:Port Scheduler Flags
  5. -> RemoteAddress:Port Forward Weight ActiveConn InActConn
  6. TCP 10.96.0.1:443 wrr
  7. -> 192.168.198.129:6443 Masq 1 4 0
  8. TCP 10.96.0.10:53 wrr
  9. -> 10.244.204.65:53 Masq 1 0 0
  10. -> 10.244.204.66:53 Masq 1 0 0
  11. TCP 10.96.0.10:9153 wrr
  12. -> 10.244.204.65:9153 Masq 1 0 0
  13. -> 10.244.204.66:9153 Masq 1 0 0
  14. UDP 10.96.0.10:53 wrr
  15. -> 10.244.204.65:53 Masq 1 0 0
  16. -> 10.244.204.66:53 Masq 1 0 0

4)查看service网络

image.png
image.png

5)定义service

假定有一组 Pod,它们对外暴露了 9376 端口,同时还被打上 app=MyApp 标签。

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-service
  5. spec:
  6. selector:
  7. app: MyApp
  8. ports:
  9. - protocol: TCP
  10. port: 80
  11. targetPort: 9376

上述配置创建一个名称为 “my-service” 的 Service 对象,它会将请求代理到使用 TCP 端口 9376,并且具有标签 "app=MyApp" 的 Pod 上。 Kubernetes 为该服务分配一个 IP 地址(有时称为 “集群IP”),该 IP 地址由服务代理使用。 服务selector 的控制器不断扫描与其选择器匹配的 Pod,然后将所有更新发布到也称为 “my-service” 的 Endpoint 对象。

2、Service 操作范例

1) 创建Deployment

  1. [root@clientvm ~]# cat Dep-myweb.yaml
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. labels:
  6. app: myweb
  7. name: myweb
  8. namespace: mytest
  9. spec:
  10. replicas: 4
  11. selector:
  12. matchLabels:
  13. app: myweb
  14. template:
  15. metadata:
  16. creationTimestamp: null
  17. labels:
  18. app: myweb
  19. spec:
  20. containers:
  21. - image: nginx
  22. name: nginx
  23. imagePullPolicy: IfNotPresent
  1. [root@clientvm ~]# kubectl apply -f Dep-myweb.yaml
  2. deployment.apps/myweb created
  3. [root@clientvm ~]# kubectl get pod -n mytest -o wide
  4. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  5. myweb-84d4bd5ff4-2tp72 1/1 Running 0 5m6s 10.244.2.50 worker2.example.com <none> <none>
  6. myweb-84d4bd5ff4-8qwc6 1/1 Running 0 5m6s 10.244.1.37 worker1.example.com <none> <none>
  7. myweb-84d4bd5ff4-m48bp 1/1 Running 0 5m6s 10.244.1.38 worker1.example.com <none> <none>
  8. myweb-84d4bd5ff4-sqwz7 1/1 Running 0 5m6s 10.244.2.51 worker2.example.com <none> <none>

2) 创建Service

  1. [root@clientvm ~]# kubectl create service clusterip -h
  2. Create a ClusterIP service with the specified name.
  3. Usage:
  4. kubectl create service clusterip NAME [--tcp=<port>:<targetPort>] [--dry-run=server|client|none] [options]
  5. Examples:
  6. # Create a new ClusterIP service named my-cs
  7. kubectl create service clusterip my-cs --tcp=5678:8080
  8. # Create a new ClusterIP service named my-cs (in headless mode)
  9. kubectl create service clusterip my-cs --clusterip="None"
  1. [root@clientvm ~]# kubectl create service clusterip myweb --tcp=80:80 -n mytest
  2. service/myweb created
  3. [root@clientvm ~]# kubectl get service -n mytest
  4. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  5. myweb ClusterIP 10.97.70.147 <none> 80/TCP 86s
  6. [root@clientvm ~]# kubectl describe service -n mytest myweb
  7. Name: myweb
  8. Namespace: mytest
  9. Labels: app=myweb
  10. Annotations: <none>
  11. Selector: app=myweb
  12. Type: ClusterIP
  13. IP: 10.97.70.147
  14. Port: 80-80 80/TCP
  15. TargetPort: 80/TCP
  16. Endpoints: 10.244.1.37:80,10.244.1.38:80,10.244.2.50:80 + 1 more...

注意比较Endpoint IP与deployment中的Pod IP。

3)验证Service

  1. [root@master ~]# kubectl run busybox --image=nginx -n mytest -- sleep 10000
  2. pod/busybox created
  3. [root@master ~]# kubectl get pod -n mytest
  4. NAME READY STATUS RESTARTS AGE
  5. busybox 1/1 Running 0 8s
  6. [root@master ~]# kubectl exec busybox -n mytest -it -- bash
  7. root@busybox:/#
  8. root@busybox:/# curl myweb
  9. <!DOCTYPE html>
  10. <html>
  11. <head>
  12. <title>Welcome to nginx!</title>
  13. ......
  14. root@busybox:/# curl 10.244.1.37
  15. <!DOCTYPE html>
  16. <html>
  17. <head>
  18. <title>Welcome to nginx!</title>
  19. ......

4)范例理解

image.png

3、服务发现

service的工作范围为集群内部。集群内部,同一nameSpace中所有的Pod都能通过Service访问后端的Pod。
Kubernetes为Pod发现服务提供以下几种方式:

1)环境变量

  1. [root@clientvm ~]# kubectl get svc -n mytest
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. myweb ClusterIP 10.97.70.147 <none> 80/TCP 141m
  4. [root@clientvm ~]# kubectl exec busybox -n mytest -- env | grep -i service
  5. MYWEB_SERVICE_PORT=80
  6. KUBERNETES_SERVICE_PORT=443
  7. KUBERNETES_SERVICE_PORT_HTTPS=443
  8. MYWEB_SERVICE_HOST=10.97.70.147
  9. MYWEB_SERVICE_PORT_80_80=80

2) DNS

  1. [root@clientvm ~]# kubectl run dns --image=busybox -n mytest -- sleep 10000
  2. pod/dns created
  3. [root@clientvm ~]# kubectl exec dns -n mytest -- nslookup myweb
  4. Server: 10.96.0.10
  5. Address: 10.96.0.10:53
  6. Name: myweb.mytest.svc.example.com
  7. Address: 10.97.70.147

4、几种不同的Service类型

4.1 简介

  1. [root@clientvm ~]# kubectl create service -h
  2. Create a service using specified subcommand.
  3. Aliases:
  4. service, svc
  5. Available Commands:
  6. clusterip Create a ClusterIP service.
  7. externalname Create an ExternalName service.
  8. loadbalancer Create a LoadBalancer service.
  9. nodeport Create a NodePort service.
  10. Usage:
  11. kubectl create service [flags] [options]

ClusterIP: 如以上范例所示,ClusterIP主要用于集群内部访问Pod。
ExternalName:用于连接外部服务,其本质是在集群DNS上创建一个指向外部域名的CNAME记录,因此连接到这类service的客户端会直接访问到外部资源。
NodePort: 在每个集群节点上都打开一个端口,并在该端口上接收流量并转发到内部的Pod,通过NodePort可以暴露服务到集群外部。
LoadBalancer: NodePort类型的扩展,由K8S外部设施提供负载均衡服务,将流量转发到所以节点的某个端口。需要集群外部基础架构支持。

4.2 ClusterIP

4.3 NodePort范例

创建NodePort Service:

  1. [root@clientvm ~]# kubectl create service nodeport -h
  2. Create a NodePort service with the specified name.
  3. Examples:
  4. # Create a new NodePort service named my-ns
  5. kubectl create service nodeport my-ns --tcp=5678:8080
  6. Usage:
  7. kubectl create service nodeport NAME [--tcp=port:targetPort] [--dry-run=server|client|none] [options]
  1. [root@clientvm ~]# kubectl create service nodeport myweb-nodeport -n mytest --tcp=80:80
  2. service/myweb-nodeport created
  3. [root@clientvm ~]# kubectl edit service myweb-nodeport -n mytest
  4. ......
  5. selector:
  6. app: myweb
  7. . ......
  8. [root@clientvm ~]# kubectl get service -n mytest
  9. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  10. myweb ClusterIP 10.97.70.147 <none> 80/TCP 3h16m
  11. myweb-nodeport NodePort 10.110.40.185 <none> 80:30568/TCP 88s
  12. [root@clientvm ~]# kubectl describe service -n mytest myweb-nodeport
  13. Name: myweb-nodeport
  14. Namespace: mytest
  15. Labels: app=myweb-nodeport
  16. Annotations: <none>
  17. Selector: app=myweb
  18. Type: NodePort
  19. IP: 10.110.40.185
  20. Port: 80-80 80/TCP
  21. TargetPort: 80/TCP
  22. NodePort: 80-80 30568/TCP
  23. Endpoints: 10.244.1.37:80,10.244.1.38:80,10.244.2.50:80 + 1 more...
  24. Session Affinity: None
  25. External Traffic Policy: Cluster
  26. Events: <none>
  27. [root@clientvm ~]# curl worker1:30568
  28. <!DOCTYPE html>
  29. <html>
  30. <head>
  31. <title>Welcome to nginx!</title>
  32. ......
  33. ##到节点上验证,有端口在侦听
  34. [root@worker2 ~]# netstat -ntlp | grep 30568
  35. tcp 0 0 0.0.0.0:30568 0.0.0.0:* LISTEN 48001/kube-proxy

4.4 ExternalName范例

Externalname的本质就是在DNS中创建一条CNAME记录,通常可用于跨NameSpace服务访问的链接,访问外部url资源等场景。

  1. [root@clientvm ~]# kubectl create service externalname -h
  2. Create an ExternalName service with the specified name.
  3. ExternalName service references to an external DNS address instead of only pods, which will allow application authors
  4. to reference services that exist off platform, on other clusters, or locally.
  5. Examples:
  6. # Create a new ExternalName service named my-ns
  7. kubectl create service externalname my-ns --external-name bar.com
  8. Usage:
  9. kubectl create service externalname NAME --external-name external.name [--dry-run=server|client|none] [options]
  1. ## 创建ExternalName
  2. [root@clientvm ~]# kubectl create service externalname my-ns --external-name www.qq.com
  3. service/my-ns created
  4. ## 创建测试工具Pod
  5. [root@clientvm ~]# kubectl run tools --image=tutum/dnsutils --image-pull-policy=IfNotPresent -- sleep 1000
  6. pod/tools created
  7. root@tools:/# ping my-ns
  8. PING ins-r23tsuuf.ias.tencent-cloud.net (121.14.77.201) 56(84) bytes of data.
  9. 64 bytes from 121.14.77.201: icmp_seq=1 ttl=127 time=6.66 ms
  10. 64 bytes from 121.14.77.201: icmp_seq=2 ttl=127 time=6.63 ms
  11. root@tools:/# dig -t cname my-ns.default.svc.example.com
  12. ;; QUESTION SECTION:
  13. ;my-ns.default.svc.example.com. IN CNAME
  14. ;; ANSWER SECTION:
  15. my-ns.default.svc.example.com. 30 IN CNAME www.qq.com.

4.5 LoadBalancer类型

1)介绍

在云提供商的Kubernetes集群中,您请求负载均衡器,云平台为您分配IP地址。在自建的集群中,可以使用MetallB负责该这一类型的IP分配。

Metallb无法从空气中创建IP地址,因此您必须提供它可以使用的IP地址池。 Metallb将根据服务需求分配IP,并提供服务。

如何获取MetallB的IP地址池取决于您的环境。如果您在托管设施中运行裸机集群,则托管提供商可能提供IP地址以供租赁。在这种情况下,您将租赁,例如IP空间(64个地址),并为MetallB提供群集服务的范围。

或者,您的群集可能是纯粹的私人,为附近的LAN提供服务,但不会暴露在互联网上。在这种情况下,您可以从其中一个私有地址空间中选择一系列IP,并将那些分配给MetallB。此类地址是免费的,只要您只为您的LAN提供集群服务,就可以正常工作。

官网https://metallb.universe.tf/
架构如下图:
70b6cf937df2ae8c2e1c271724af24a.png

2)安装前准备

如果你的kube-proxy使用的IPVS模式,那么必须手动启用strict ARP模式。如果是iptable模式,可忽略这一步骤。
要验证使用模式,可在任一节点上执行:

  1. [root@prod-master-01 ~]# curl http://localhost:10249/proxyMode
  2. iptables

为IPVS启用strict ARP:

  1. kubectl edit configmap -n kube-system kube-proxy
  1. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  2. kind: KubeProxyConfiguration
  3. mode: "ipvs"
  4. ipvs:
  5. strictARP: true

3)安装MetalLB

  1. [root@clientvm k8s]# kubectl apply -f /resources/yaml/matelLB/namespace.yaml
  2. namespace/metallb-system created
  3. [root@clientvm k8s]# kubectl apply -f /resources/yaml/matelLB/metallb.yaml
  4. Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
  5. podsecuritypolicy.policy/controller created
  6. podsecuritypolicy.policy/speaker created
  7. serviceaccount/controller created
  8. serviceaccount/speaker created
  9. clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
  10. clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
  11. role.rbac.authorization.k8s.io/config-watcher created
  12. role.rbac.authorization.k8s.io/pod-lister created
  13. role.rbac.authorization.k8s.io/controller created
  14. clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
  15. clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
  16. rolebinding.rbac.authorization.k8s.io/config-watcher created
  17. rolebinding.rbac.authorization.k8s.io/pod-lister created
  18. rolebinding.rbac.authorization.k8s.io/controller created
  19. daemonset.apps/speaker created
  20. deployment.apps/controller created

这将在MetallB-System命名空间下将MetallB部署到群集。清单中的组件是:
MetallB-System / Controller部署。这是处理IP地址分配的群集控制器。
Metallb-system /speaker守护程序。这是讲述您选择的协议的组件,以使服务可以到达。
为controller和speaker创建SA账户,以及组件需要运行的RBAC权限。
安装清单不包括配置文件。MetallB的组件仍将启动,但在您定义和部署ConfigMap之前将保持闲置。

4)配置MetalLB

创建configMap,为matelLB分配IP池:
请根据你的环境,修改IP网段。

  1. [root@clientvm ~]# cat /resources/yaml/matelLB/metalLB-configmap.yaml
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. namespace: metallb-system
  6. name: config
  7. data:
  8. config: |
  9. address-pools:
  10. - name: default
  11. protocol: layer2
  12. addresses:
  13. - 192.168.126.40-192.168.126.45
  1. kubectl apply -f /resources/yaml/matelLB/metalLB-configmap.yaml
  1. [root@clientvm ~]# kubectl get all -n metallb-system
  2. NAME READY STATUS RESTARTS AGE
  3. pod/controller-7dcc8764f4-rhh8k 1/1 Running 0 2m37s
  4. pod/speaker-4p2g8 1/1 Running 0 2m37s
  5. pod/speaker-78v9b 1/1 Running 0 2m37s
  6. pod/speaker-xqh8k 1/1 Running 0 2m37s
  7. NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
  8. daemonset.apps/speaker 3 3 3 3 3 kubernetes.io/os=linux 2m37s
  9. NAME READY UP-TO-DATE AVAILABLE AGE
  10. deployment.apps/controller 1/1 1 1 2m37s
  11. NAME DESIRED CURRENT READY AGE
  12. replicaset.apps/controller-7dcc8764f4 1 1 1 2m37s

5)创建LB类型service

  1. [root@clientvm ~]# kubectl create deployment nginx --image=nginx --replicas=3 --image-pull-policy='IfNotPresent'
  2. deployment.apps/nginx created
  3. [root@clientvm ~]#
  4. [root@clientvm ~]# kubectl expose deployment nginx --name myweb --port=80 --type LoadBalancer
  5. service/myweb exposed
  6. [root@clientvm ~]#
  7. [root@clientvm ~]# kubectl get svc
  8. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  9. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h
  10. myweb LoadBalancer 10.99.138.22 192.168.126.40 80:31581/TCP 7s
  11. [root@clientvm ~]# kubectl get pod
  12. NAME READY STATUS RESTARTS AGE
  13. nginx-6799fc88d8-7lt6q 1/1 Running 0 18s
  14. nginx-6799fc88d8-lcbp7 1/1 Running 0 18s
  15. nginx-6799fc88d8-tkg57 1/1 Running 0 18s

验证:

  1. [root@clientvm ~]# curl 192.168.126.40
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <title>Welcome to nginx!</title>
  6. <style>
  7. html { color-scheme: light dark; }
  8. body { width: 35em; margin: 0 auto;
  9. font-family: Tahoma, Verdana, Arial, sans-serif; }
  10. </style>
  11. </head>
  12. <body>
  13. <h1>Welcome to nginx!</h1>
  14. <p>If you see this page, the nginx web server is successfully installed and
  15. working. Further configuration is required.</p>
  16. <p>For online documentation and support please refer to
  17. <a href="http://nginx.org/">nginx.org</a>.<br/>
  18. Commercial support is available at
  19. <a href="http://nginx.com/">nginx.com</a>.</p>
  20. <p><em>Thank you for using nginx.</em></p>
  21. </body>
  22. </html>

4.6 其他

1)为Service指定ExternallP

image.png
curl 1.2.3.4 —->httpd pod
curl 1.2.3.5 ——> nginx pod
Why not Ingress? Ingress是7层LB,不支持HTTP/HTTPS之外的其他类型协议。

  1. [root@clientvm ~]# cat externalIP.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: myweb-externalip
  6. namespace: mytest
  7. spec:
  8. selector:
  9. app: myaweb
  10. ports:
  11. - name: http
  12. protocol: TCP
  13. port: 80
  14. targetPort: 80
  15. externalIPs:
  16. - 192.168.241.130

192.168.241.130为节点IP。

  1. [root@clientvm ~]# kubectl apply -f externalIP.yaml
  2. service/myweb-externalip configured
  3. [root@clientvm ~]#
  4. [root@clientvm ~]# kubectl get service -n mytest
  5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  6. myweb ClusterIP 10.97.70.147 <none> 80/TCP 4h12m
  7. myweb-externalip ClusterIP 10.99.149.3 192.168.241.130 80/TCP 3m
  8. myweb-externalname ExternalName <none> myweb.mytest.svc.example.com <none> 44m
  9. myweb-nodeport NodePort 10.110.40.185 <none> 80:30568/TCP 57m
  10. [root@clientvm ~]# curl 192.168.241.130
  11. <!DOCTYPE html>
  12. <html>
  13. <head>
  14. <title>Welcome to nginx!</title>
  15. [root@worker1 ~]# netstat -ntlp | grep 80
  16. tcp 0 0 192.168.241.130:80 0.0.0.0:* LISTEN 25256/kube-proxy

2)headless服务

headless服务是指ClusterIP定义为None的service。使用无头服务,因为service不再获取到集群IP,因此客户端在访问service时,DNS解析会直接将后端POD的IP返回给客户端,headless服务依然可以提供跨pod的负载均衡,但是通过DNS轮询机制,而不是通过service来代理。

  1. [root@clientvm ~]# cat myweb-headless.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: myweb-headless
  6. namespace: mytest
  7. spec:
  8. ports:
  9. - name: 80-80
  10. port: 80
  11. protocol: TCP
  12. targetPort: 80
  13. selector:
  14. app: myweb
  15. clusterIP: None
  1. [root@clientvm ~]# kubectl apply -f myweb-headless.yaml
  2. service/myweb-headless created
  3. [root@clientvm ~]# kubectl get service -n mytest
  4. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  5. myweb ClusterIP 10.97.70.147 <none> 80/TCP 4h23m
  6. myweb-externalip ClusterIP 10.99.149.3 192.168.241.130 80/TCP 13m
  7. myweb-externalname ExternalName <none> myweb.mytest.svc.example.com <none> 55m
  8. myweb-headless ClusterIP None <none> 80/TCP 9s
  9. myweb-nodeport NodePort 10.110.40.185 <none> 80:30568/TCP 67m
  1. [root@clientvm ~]# kubectl exec busybox -n mytest -it -- bash
  2. root@busybox:/# curl myweb-headless
  3. <!DOCTYPE html>
  4. <html>
  5. <head>
  6. <title>Welcome to nginx!</title>
  1. [root@clientvm ~]# kubectl exec dns -n mytest -it -- sh
  2. / # nslookup 10.244.1.37
  3. Server: 10.96.0.10
  4. Address: 10.96.0.10:53
  5. 37.1.244.10.in-addr.arpa name = 10-244-1-37.myweb-headless.mytest.svc.example.com