k8s之service服务
service服务介绍
在k8s中,pod是应用程序的载体,我们可以通过pod的ip来访问应用程序,但是pod的ip地址不是固定的,这也就意味着不方便直接采用pod的ip对服务进行访问
为了解决这个问题,k8s提供了service资源,service会对提供同一个服务的多个pod进行聚合,并且提供一个统一的入口地址。通过访问service的入口地址就能访问到后面的pod服务
Service服务也是Kubernetes里的核心资源对象之一,Kubernetes里的每个Service其实就是我们经常提起的微服务架构中的一个微服务,受kube-proxy管理,运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,负责把对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡与会话保持机制。
可以看到上面的架构图,service服务通过标签选择器定位后端pod,前提是service的selector必须和后端Pod标签对应上才能找到相对应的Pod,而前段frontend通过service就可以访问到后端提供服务的pod了,而service默认IP类型为主要分为:
- ClusterIP:主要是为集群内部提供访问服务的 (默认类型)
- NodePort:可以被集群外部所访问,访问方式为 宿主机:端口号
- LoadBalancer:在NodePort的基础上,借助cloud provider(云提供商)创建一个外部负载均衡器并将请求转发到NodePort
- ExternalName: 把集群外部的访问引入到集群内部来,在集群内部直接使用,没有任何代理被创建
当Service一旦被创建,Kubernetes就会自动为它分配一个可用的Cluster IP,而且在Service的整个生命周期内,它的Cluster IP不会发生改变,service会通过标签选择器与后端的pod进行连接并被kubo-poxry监控,当后端pod被重建时会通过标签自动加入到对应的service服务中,从而避免失联。
service三种代理模式
-
userspace
这种模式,kube-proxy 会监视 Kubernetes 控制平面对 Service 对象和 Endpoints 对象的添加和移除操作。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的后端 Pods
中的某个上面(如 Endpoints
所报告的一样)。 使用哪个后端 Pod,是 kube-proxy 基于 SessionAffinity
来确定的。
-
iptables
这种模式,kube-proxy
会监视 Kubernetes 控制节点对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会配置 iptables 规则,从而捕获到达该 Service 的 clusterIP
和端口的请求,进而将请求重定向到 Service 的一组后端中的某个 Pod 上面。 对于每个 Endpoints 对象,它也会配置 iptables 规则,这个规则会选择一个后端组合。
-
ipvs
在 ipvs
模式下,kube-proxy 监视 Kubernetes 服务和端点,调用 netlink
接口相应地创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes 服务和端点同步。该控制循环可确保 IPVS 状态与所需状态匹配。访问服务时,IPVS 将流量定向到后端 Pod 之一。
IPVS 代理模式基于类似于 iptables 模式的 netfilter 挂钩函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。 这意味着,与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。
IPVS 提供了更多选项来平衡后端 Pod 的流量。这些是:
rr
:轮替(Round-Robin)lc
:最少链接(Least Connection),即打开链接数量最少者优先dh
:目标地址哈希(Destination Hashing)sh
:源地址哈希(Source Hashing)sed
:最短预期延迟(Shortest Expected Delay)nq
:从不排队(Never Queue)
要在 IPVS 模式下运行 kube-proxy,必须在启动 kube-proxy 之前使 IPVS 在节点上可用。
当 kube-proxy 以 IPVS 代理模式启动时,它将验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 将退回到以 iptables 代理模式运行。
ipvs模式设置
先下载ipvsadm
然后修改kube-poxy的configmap
[root@master ~]# kubectl edit cm kube-proxy -n kube-system # Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # apiVersion: v1 data: config.conf: |- apiVersion: kubeproxy.config.k8s.io/v1alpha1 bindAddress: 0.0.0.0 bindAddressHardFail: false clientConnection: acceptContentTypes: "" burst: 0 contentType: "" kubeconfig: /var/lib/kube-proxy/kubeconfig.conf qps: 0 clusterCIDR: 10.244.0.0/16 configSyncPeriod: 0s conntrack: maxPerCore: null min: null tcpCloseWaitTimeout: null tcpEstablishedTimeout: null detectLocalMode: "" enableProfiling: false healthzBindAddress: "" hostnameOverride: "" iptables: masqueradeAll: false masqueradeBit: null minSyncPeriod: 0s syncPeriod: 0s ipvs: excludeCIDRs: null minSyncPeriod: 0s scheduler: "" strictARP: false syncPeriod: 0s tcpFinTimeout: 0s tcpTimeout: 0s udpTimeout: 0s kind: KubeProxyConfiguration metricsBindAddress: "" mode: "ipvs" #将模式改为ipvs nodePortAddresses: null oomScoreAdj: null portRange: "" showHiddenMetricsForVersion: "" udpIdleTimeout: 0s winkernel: enableDSR: false networkName: "" sourceVip: "" kubeconfig.conf: |- apiVersion: v1 kind: Config clusters: - cluster: certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt server: https://192.168.248.129:6443 name: default contexts: - context: cluster: default namespace: default user: default name: default current-context: default users: - name: default user: tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
删除当前的kube-poxy。
[root@master ~]# kubectl get pod -n kube-system|grep kube-proxy|awk '{system("kubectl delete pod "$1" -n kube-system")}'
ClusterIP(内部集群访问)
下面演示ClusterIP的service
先创建一个yaml文件
apiVersion: v1 kind: Service metadata: labels: app: web name: nginx-svc namespace: default spec: ports: - name: http port: 3001 # service暴露的端口 protocol: TCP targetPort: 80 #后端容器的端口 selector: #标签选择器与deployment一致 app: web type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: web name: deployment-nginx spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: nginx:1.21.4 name: nginx ports: - name: http containerPort: 80 #容器端口
然后创建service和deployment
[root@master ~]# kubectl apply -f deploymen_nginx_serivce.yaml service/nginx-svc created deployment.apps/web created
查看service和pod状态
[root@master ~]# kubectl get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 40m <none> nginx-svc ClusterIP 10.103.130.138 <none> 3001/TCP 12m app=web [root@master ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES deployment-nginx-64fc98d58f-6llbw 1/1 Running 0 10m 10.101.11.32 node2 <none> <none> deployment-nginx-64fc98d58f-9s6mt 1/1 Running 0 10m 10.101.11.31 node2 <none> <none> deployment-nginx-64fc98d58f-fkcmf 1/1 Running 0 10m 10.101.149.15 node1 <none> <none>
此时我们发现nginx-svc有了一个CLUSTER-IP:10.103.130.138, 用curl检查一下是否能访问10.103.130.138:3001
[root@master ~]# curl 10.103.130.138:3001 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
NodePod(外部访问)
下面创建一个NodePort的service演示,只需要将上面yaml文件中的ClusterIP改为NodePort
apiVersion: v1 kind: Service metadata: labels: app: web name: nginx-svc namespace: default spec: ports: - name: http port: 3001 protocol: TCP targetPort: 80 selector: app: web type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: web name: deployment-nginx spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: nginx:1.21.4 name: nginx ports: - name: http containerPort: 80 ~
执行yaml文件后查看pod和svc的信息
[root@master ~]# kubectl get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 59m <none> nginx-svc NodePort 10.99.62.52 <none> 3001:31966/TCP 3s app=web [root@master ~]# kubectl get node -owide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME master Ready control-plane,master 79d v1.21.0 192.168.248.129 <none> CentOS Linux 7 (Core) 3.10.0-1062.el7.x86_64 docker://20.10.14 node1 Ready <none> 79d v1.21.0 192.168.248.128 <none> CentOS Linux 7 (Core) 3.10.0-1062.el7.x86_64 docker://20.10.14 node2 Ready <none> 79d v1.21.0 192.168.248.130 <none> CentOS Linux 7 (Core) 3.10.0-1062.el7.x86_64 docker://20.10.14 [root@master ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES deployment-nginx-64fc98d58f-b5s2t 1/1 Running 0 112s 10.101.11.36 node2 <none> <none> deployment-nginx-64fc98d58f-gd5fw 1/1 Running 0 112s 10.101.11.35 node2 <none> <none> deployment-nginx-64fc98d58f-p6tw5 1/1 Running 0 112s 10.101.149.17 node1 <none> <none>
此时发现svc的PORT变成3001:31966/TCP ,其中3001为svc的端口,31966为随机生成的node端口,我们可以通过nodeIP加端口访问
headless service(无头服务)
headless service就是无头service(也就是没有ip的service),这种无头服务对于有状态应用来说很重要,我们可以利用service的labels关联后端pod,我们访问的流量就可以直接到达pod而不再需要service负载均衡至后端pod
定义无头服务如下:
apiVersion: v1 kind: Service metadata: name: redis labels: app: redis spec: ports: - port: 6379 name: redis-port clusterIP: None selector: app: redis --- apiVersion: apps/v1 kind: StatefulSet metadata: name: redis spec: serviceName: "redis" replicas: 3 # 默认值是 1 selector: matchLabels: app: redis template: metadata: labels: app: redis # 必须匹配 .spec.selector.matchLabels spec: containers: - name: redis image: redis ports: - containerPort: 6379 name: redis
查看pod以及service
[root@master ~]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES redis-0 1/1 Running 0 29s 10.101.11.37 node2 <none> <none> redis-1 1/1 Running 0 23s 10.101.149.18 node1 <none> <none> redis-2 1/1 Running 0 21s 10.101.11.38 node2 <none> <none> [root@master ~]# kubectl get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 89m <none> redis ClusterIP None <none> 6379/TCP 40s app=redis
查看coredns地址
[root@master ~]# kubectl get pod -n kube-system -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES calico-kube-controllers-65898446b5-qtcgv 1/1 Running 1 79d 10.101.149.1 node1 <none> <none> calico-node-7wcsl 1/1 Running 0 79d 192.168.248.128 node1 <none> <none> calico-node-8sjcz 1/1 Running 0 79d 192.168.248.129 master <none> <none> calico-node-f84fw 1/1 Running 0 79d 192.168.248.130 node2 <none> <none> coredns-545d6fc579-w5rj2 1/1 Running 0 79d 10.101.11.1 node2 <none> <none> coredns-545d6fc579-xr62w 1/1 Running 0 79d 10.101.11.2 node2 <none> <none> etcd-master 1/1 Running 0 79d 192.168.248.129 master <none> <none> kube-apiserver-master 1/1 Running 0 79d 192.168.248.129 master <none> <none> kube-controller-manager-master 1/1 Running 0 79d 192.168.248.129 master <none> <none> kube-proxy-2qx8x 1/1 Running 0 79d 192.168.248.129 master <none> <none> kube-proxy-6khnn 1/1 Running 0 79d 192.168.248.128 node1 <none> <none> kube-proxy-znv97 1/1 Running 0 79d 192.168.248.130 node2 <none> <none> kube-scheduler-master 1/1 Running 0 79d 192.168.248.129 master <none> <none>
解析service服务
[root@master ~]# dig redis.default.svc.cluster.local @10.101.11.1 ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.9 <<>> nginx.default.svc.cluster.local @10.101.11.1 ;; global options: +cmd ;; Got answer: ;; WARNING: .local is reserved for Multicast DNS ;; You are currently testing what happens when an mDNS query is leaked to DNS ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49448 ;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;nginx.default.svc.cluster.local. IN A ;; ANSWER SECTION: nginx.default.svc.cluster.local. 30 IN A 10.101.11.38 # 这个地址对应的是statefulset集群的的redis-2的地址 nginx.default.svc.cluster.local. 30 IN A 10.101.149.18 nginx.default.svc.cluster.local. 30 IN A 10.101.11.37 ;; Query time: 7 msec ;; SERVER: 10.101.11.1#53(10.101.11.1) ;; WHEN: 三 7月 27 23:10:03 CST 2022 ;; MSG SIZE rcvd: 201
ExternalName 类型
类型为 ExternalName 的服务将服务映射到 DNS 名称,而不是典型的选择算符,例如 my-service
或者 cassandra
。 你可以使用 spec.externalName
参数指定这些服务。
例如,以下 Service 定义将 prod
名称空间中的 my-service
服务映射到 my.database.example.com
:
apiVersion: v1 kind: Service metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.com
当查找主机 my-service.prod.svc.cluster.local
时,集群 DNS 服务返回 CNAME
记录, 其值为 my.database.example.com
。 访问 my-service
的方式与其他服务的方式相同,但主要区别在于重定向发生在 DNS 级别,而不是通过代理或转发。 如果以后你决定将数据库移到集群中,则可以启动其 Pod,添加适当的选择算符或端点以及更改服务的 type
。
外部 IP
如果外部的 IP 路由到集群中一个或多个 Node 上,Kubernetes Service 会被暴露给这些 externalIPs
。 通过外部 IP(作为目的 IP 地址)进入到集群,打到 Service 的端口上的流量, 将会被路由到 Service 的 Endpoint 上。 externalIPs
不会被 Kubernetes 管理,它属于集群管理员的职责范畴。
根据 Service 的规定,externalIPs
可以同任意的 ServiceType
来一起指定。 在上面的例子中,my-service
可以在 "80.11.12.10:80
"(externalIP:port
) 上被客户端访问。
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app.kubernetes.io/name: MyApp ports: - name: http protocol: TCP port: 80 targetPort: 9376 externalIPs: - 80.11.12.10
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性