跳到主要内容

第8章 Service服务

1.Service服务介绍

通过前面的实验我们已经掌握了使用Pod控制器来管理Pod,我们也会发现,Pod的生命周期非常短暂,每次镜像升级都会销毁以及创建,而我们知道每个Pod都拥有自己的IP地址,并且随着Pod删除和创建,这个IP是会变化的。

当我们的Pod数量非常多的时候前端的入口服务该怎么知道后面都有哪些Pod呢?


为了解决这个问题k8s提供了一个对象Service和三种IP,创建的Service资源通过标签可以动态的知道后端的Pod的IP地址,在PodIP之上设计一个固定的IP,也就是ClusterIP,然后使用NodePort来对外暴露端口提供访问。


接下来我们先认识一下K8s里的三种IP及其作用。

2.k8s的三种IP

NodeIP
映射宿主机的端口,用于节点对外提供访问的IP

ClusterIP
用来动态发现和负载均衡POD的IP,仅限于k8s内部访问

PodIP
提供POD使用的IP,仅限于k8s内部访问

1725236849644-b679889d-202b-4708-bdfc-bd8108cb6ec2.png

3.ClusterIP资源配置

资源配置清单:

apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
type: ClusterIP

配置解释:

ports:
- name: http
port: 80 #clusterIP的端口号
protocol: TCP #协议类型
targetPort: 80 #POD暴露的端口
type: ClusterIP #service类型

查看ClusterIP

kubectl get svc

4.NodePort资源配置

资源配置清单:

apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 80
nodePort: 30000
type: NodePort

配置解释:

ports:
- name: http
port: 8080 #clusterIP的端口号
protocol: TCP #协议类型
targetPort: 80 #POD暴露的端口
nodePort: 30000 #NodeIP的端口号,也就是对外用户访问的端口号
type: NodePort #service类型

查看创建的资源

kubectl get svc

5.Service 服务发现

在k8s中,一个service对应的"后端"由Pod的IP和容器端口号组成,即一个完整的"IP:Port"访问地址,这在k8s里叫做Endpoint。通过查看Service的详细信息可以看到后端Endpoint的列表。

[root@node1 ~]# kubectl describe svc my-nginx
...................
Endpoints: 10.2.1.24:80,10.2.1.26:80,10.2.1.27:80 + 2 more...

我们也可以使用DNS域名的形式访问Service,如果在同一个命名空间里甚至可以直接使用service名来访问服务。

servicename.namespace.svc.cluster.local
servicename.namespace
servicename

6.kube-proxy负载均衡模式

通过前面的学习我们知道ClusrerIP可以知道后端关联的Pod的IP,并且还可以将流量负载均衡的分摊到关联的Pod中,而这个功能就是由每个Node节点都运行的kube-proxy组件实现的。目前kube-proxy支持2种负载模式,分别为默认的iptables和性能更高的ipvs模式。

iptables模式

iptables模式下,kube-proxy通过射者Linux内核的iptables规则,实现从Service到后端Endpoint列表的负载转发规则,但是如果某个后端Endpoint在转发时不可用,则请求就会失败。所以应该通过为Pod设置readinessprobe来保证只有达到ready状态的Endpoint才会被设置为Service的后端Endpoint。

1725236849777-07a219d3-d871-42c5-84a8-de69c07acd13.png

ipvs模式

除了iptables模式,k8s还支持ipvs模式,ipvs在k8s 1.11版本达到Stable阶段,kube-proxy通过设置Linux内核的netlink接口设置IPVS规则,转发效率和支持的吞吐率都是最高的。ipvs模块要求Linux内核开启IPVS模块,如果操作系统未启用IPVS内核模块,kube-proxy会自动切换到iptables模式,同时,ipvs模式支持更多的负载均衡策略:

修改默认策略需要在kube-proxy中配置–ipvs-scheduler参数来实现,但是这个参数是全局的,对所有Service类型都生效。

rr 轮询
lc 最小连接数
dh 目的地址哈希
sh 源地址哈希
sed 最短期望延时
nq 永不排队

1725236849885-0cfca4c1-c8b7-4e02-b0a8-872e37b144dc.png

7.服务发现模式

k8s支持2种查找服务的主要模式:环境变量和DNS,前者开箱即用,而后者则需要CoreDNS组件。

环境变量模式

在一个Pod运行起来的时候,系统会自动为其容器运行环境注入所有集群中有效的Service的信息。

Service的相关信息包括服务IP,服务端口号,相关协议等。

[root@node1 ~]# kubectl exec my-nginx-7c4ff94949-6jjq2 -- printenv|grep SERVICE
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
MYSQL_SERVICE_PORT=3306
MYWEB_SERVICE_PORT=8080
MY_NGINX_SERVICE_PORT_HTTP=80
KUBERNETES_SERVICE_HOST=10.1.0.1
MY_NGINX_SERVICE_PORT=80
MY_NGINX_SERVICE_HOST=10.1.182.68
MYSQL_SERVICE_HOST=10.1.81.63
MYWEB_SERVICE_HOST=10.1.149.26

这样客户端就可以使用相关的环境变来访问服务了

[root@node1 ~]# kubectl exec -it my-nginx-7c4ff94949-6jjq2 -- /bin/bash
root@my-nginx-7c4ff94949-6jjq2:/# curl -I ${MY_NGINX_SERVICE_HOST}
HTTP/1.1 200 OK

DNS模式

Kubernetes 提供了一个 DNS 插件 Service,当Service以DNS域名形式进行访问时,需要在k8s集群里存在一个DNS服务来完成域名到CusterIP地址的解析工作。经过多年发展,目前由CoreDNS作为k8s集群的默认DNS服务来提供域名解析服务。

1725236849836-e41a9d5c-65be-4af7-afe9-b7493402c62e.png

CoreDNS为其他的Pod提供Service解析服务,查看Pod的DNS解析文件可以发现nameserver是一个IP地址,这个IP地址其实是CoreDNS自己的ClusterIP。

[root@node1 ~]# kubectl -n kube-system get svc|grep kube-dns
kube-dns ClusterIP 10.1.0.10 <none> 53/UDP,53/TCP,9153/TCP

安装了CoreDNS的集群,当我们创建Pod后,kubelet会使用配置文件里配置的--cluster-dns=和--cluster-domain参数来传递给容器内部。kubelet配置如下;

[root@node1 ~]# cat /var/lib/kubelet/config.yaml
............................
clusterDNS:
- 10.1.0.10
clusterDomain: cluster.local
............................

查看容器的/etc/resolv.conf内容可以发现域名的相关配置

[root@node1 ~]# kubectl exec -it my-nginx-7c4ff94949-6jjq2 -- cat /etc/resolv.conf
nameserver 10.1.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

那么我们在容器里该怎么去访问Service服务呢?一共有以下几种形式。

servicename.default.svc.cluster.local
servicename.namespace
servicename

实验一下:

[root@node1 ~]# kubectl exec -it my-nginx-7c4ff94949-6jjq2 -- curl -sI my-nginx.default.svc.cluster.local|head -1
HTTP/1.1 200 OK

[root@node1 ~]# kubectl exec -it my-nginx-7c4ff94949-6jjq2 -- curl -sI my-nginx.default|head -1
HTTP/1.1 200 OK

[root@node1 ~]# kubectl exec -it my-nginx-7c4ff94949-6jjq2 -- curl -sI my-nginx|head -1
HTTP/1.1 200 OK

[root@node1 ~]# kubectl run -it --image busybox:1.28.3 test-dns --restart=Never -- nslookup my-nginx
Server: 10.1.0.10
Address 1: 10.1.0.10 kube-dns.kube-system.svc.cluster.local

Name: my-nginx
Address 1: 10.1.182.68 my-nginx.default.svc.cluster.local

8.Service和Deployment关系示意图image-20210803102504255.png

更新: 2024-09-02 08:53:24