我是靠谱客的博主 迷路便当,这篇文章主要介绍k8s之Service,现在分享给大家,希望可以做个参考。

写在前面

本文接k8s之DaemonSet 。

通过Deployment我们可以实现一直有指定个数的POD在运行,而通过DaemonSet可以实现在每个Node上都有一个POD在运行,不管是这两种方式中的哪一种,都是仅仅实现了有若干个POD在运行的效果,但是还无法正常的对外提供访问,并且要支持负载均衡,服务发现(因为POD可能经常会有老的退出,新的创建的情况,所以必须具备自动的服务发现机制,即发现退出的POD失效,新创建的POD加入到负载均衡中),为了实现这个目的,k8s提供了Service API对象,其定义如下:

复制代码
1
2
3
4
5
ongyunqi@mongodaddy:~/k8s$ kubectl api-resources | egrep -w 'Service|KIND' NAME SHORTNAMES APIVERSION NAMESPACED KIND services svc v1 true Service

简称svc。这样,Deployment,DaemonSet负责保证有一定数量的POD在运行,Service负责让这些POD以负载均衡的方式对外提供访问,各司其职,以POD们为中心来运转。下面我们就详细看下Service API对象的使用。

1:定义yaml

我们知道API对象,如POD,Job,CronJob等都是通过kubectl create命令来创建yaml模板的,但是Service API对象就比较特殊,需要通过kubectl expose,这点要注意下(其实我认为这样搞特殊化挺不好的,徒增了学习和使用的复杂度,当然设计者可能想突出其expose的功能吧!),如下:

复制代码
1
2
3
export out="--dry-run=client -o yaml" kubectl expose deploy ngx-dep-name --port=80 --target-port=80 $out

简单说明:

复制代码
1
2
3
deploy ngx-dep-name:指定使用名称为ngx-dep-name的Deployment管理的POD --port=80 --target-port=80 暴露在Service端口号和POD内部服务端口号

以上说明可以参考下图:

在这里插入图片描述

注意图例,是以Deployment生成和管理POD为例,但其实POD到底是怎么来的Service是不关心的,它只是按照指定的app label来找到POD并用之对外提供服务,仅此而已,即Service API对象只和POD API对象有关系。

我们生成的yaml模板如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dongyunqi@mongodaddy:~/k8s$ kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE ngx-dep-name 3/3 3 3 9m37s dongyunqi@mongodaddy:~/k8s$ export out="--dry-run=client -o yaml" && kubectl expose deploy ngx-dep-name --port=80 --target-port=80 $out apiVersion: v1 kind: Service metadata: creationTimestamp: null labels: app: ngx-dep-app name: ngx-dep-name spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: ngx-dep-pod status: loadBalancer: {}

app: ngx-dep-pod可看到自动为我们生成了deployment管理的名称为ngx-dep-pod的POD。修改后如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1 kind: Service metadata: name: ngx-svc spec: selector: app: ngx-dep-pod ports: - port: 80 targetPort: 80 protocol: TCP

这里我们通过label选择器selector选择app为ngx-dep-pod的POD,也就是我们在前面文章已经定义好的POD,基于这些POD来提供负载均衡,服务发现的服务。因为nginx默认返回的信息无法看出负载均衡的效果,所以我们需要来修改其默认的配置文件,这里我们通过ConfigMap 来实现,首先定义configmap如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1 kind: ConfigMap metadata: name: ngx-conf data: default.conf: | server { listen 80; location / { default_type text/plain; return 200 'srv : $server_addr:$server_portnhost: $hostnamenuri : $request_method $host $request_urindate: $time_iso8601n'; } }

特别说明这里的data结构转换为json后如下:

复制代码
1
2
3
4
{ "default.conf": "server {n listen 80;n location / {n default_type text/plain;n return 200n 'srv : $server_addr:$server_port\nhost: $hostname\nuri : $request_method $host $request_uri\ndate: $time_iso8601\n';n }n}n" }

接下来我们应用configmap,如下:

复制代码
1
2
3
4
5
6
dongyunqi@mongodaddy:~/k8s$ kubectl apply -f configmap.yml configmap/ngx-conf created dongyunqi@mongodaddy:~/k8s$ kubectl get configmap | egrep 'ngx-conf|NAME' NAME DATA AGE ngx-conf 1 49s

然后我们修改Deloyment API对象,以Volumes存储卷的方式加载到nginx容器中去,修改后如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: apps/v1 kind: Deployment metadata: labels: app: ngx-dep-app name: ngx-dep-name spec: replicas: 3 selector: matchLabels: app: ngx-dep-pod template: metadata: labels: app: ngx-dep-pod spec: volumes: - name: ngx-conf-vol configMap: name: ngx-conf containers: - image: nginx:alpine name: nginx ports: - containerPort: 80 volumeMounts: - mountPath: /etc/nginx/conf.d name: ngx-conf-vol

然后我们apply让配置生效,如下:

复制代码
1
2
3
dongyunqi@mongodaddy:~/k8s$ kubectl apply -f deploy.yml ...

完成后可以进入POD的NGINX容器中查看文件是否挂载进去,如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
dongyunqi@mongodaddy:~/k8s$ kubectl get pod NAME READY STATUS RESTARTS AGE ngx-dep-name-dcc8b7bfd-7lbrq 1/1 Running 0 61m ngx-dep-name-dcc8b7bfd-9m2rs 1/1 Running 0 61m ngx-dep-name-dcc8b7bfd-wp4zt 1/1 Running 0 61m redis-ds-5jf5x 1/1 Running 1 (5h17m ago) 41h redis-ds-n8p45 1/1 Running 1 (5h17m ago) 41h dongyunqi@mongodaddy:~/k8s$ kubectl exec -it ngx-dep-name-dcc8b7bfd-7lbrq -- sh / # ls bin docker-entrypoint.sh lib opt run sys var dev etc media proc sbin tmp /etc/nginx/conf.d # cat default.conf server { listen 80; location / { default_type text/plain; return 200 'srv : $server_addr:$server_portnhost: $hostnamenuri : $request_method $host $request_urindate: $time_iso8601n'; } } /etc/nginx/conf.d # pwd /etc/nginx/conf.d /etc/nginx/conf.d # cat /etc/nginx/conf.d/default.conf server { listen 80; location / { default_type text/plain; return 200 'srv : $server_addr:$server_portnhost: $hostnamenuri : $request_method $host $request_urindate: $time_iso8601n'; } }

可以看到已经成功挂载进去了。接着我们就可以创建Service了。

2:apply

再贴下service的yaml:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1 kind: Service metadata: name: ngx-svc spec: selector: app: ngx-dep-pod # 查找app label为ngx-dep-pod的POD ports: - port: 80 targetPort: 80 protocol: TCP

apply:

复制代码
1
2
3
4
5
6
7
dongyunqi@mongodaddy:~/k8s$ kubectl apply -f service.yml service/ngx-svc created dongyunqi@mongodaddy:~/k8s$ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d18h ngx-svc ClusterIP 10.105.114.19 <none> 80/TCP 7s

可以看到service的IP地址是10.105.114.19,这是一个虚拟的静态IP。通过kubectl describe可以查看service所使用的POD信息:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
dongyunqi@mongodaddy:~/k8s$ kubectl describe svc ngx-svc Name: ngx-svc Namespace: default Labels: <none> Annotations: <none> Selector: app=ngx-dep-pod Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.105.114.19 IPs: 10.105.114.19 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.10.1.19:80,10.10.1.20:80,10.10.1.21:80 Session Affinity: None Events: <none>

Endpoints: 10.10.1.19:80,10.10.1.20:80,10.10.1.21:80就是所使用的POD IP,如下验证:

复制代码
1
2
3
4
5
6
dongyunqi@mongodaddy:~/k8s$ kubectl get pod -l 'app=ngx-dep-pod' -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ngx-dep-name-dcc8b7bfd-7lbrq 1/1 Running 0 80m 10.10.1.20 mongomummy <none> <none> ngx-dep-name-dcc8b7bfd-9m2rs 1/1 Running 0 80m 10.10.1.21 mongomummy <none> <none> ngx-dep-name-dcc8b7bfd-wp4zt 1/1 Running 0 80m 10.10.1.19 mongomummy <none> <none>

是对得上的。

3:负载均衡测试

现在我们已经知道Service API对象实例的IP地址是10.105.114.19,而且映射的端口号是80,那么我们就可以了来测试负载均衡效果了,如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dongyunqi@mongodaddy:~/k8s$ curl http://10.105.114.19:80 srv : 10.10.1.19:80 host: ngx-dep-name-dcc8b7bfd-wp4zt uri : GET 10.105.114.19 / date: 2023-01-12T08:29:23+00:00 dongyunqi@mongodaddy:~/k8s$ curl http://10.105.114.19:80 srv : 10.10.1.20:80 host: ngx-dep-name-dcc8b7bfd-7lbrq uri : GET 10.105.114.19 / date: 2023-01-12T08:29:24+00:00 dongyunqi@mongodaddy:~/k8s$ curl http://10.105.114.19:80 srv : 10.10.1.19:80 host: ngx-dep-name-dcc8b7bfd-wp4zt uri : GET 10.105.114.19 / date: 2023-01-12T08:29:25+00:00

service负载均衡使用的是简单的round robin轮询算法。英 [ˈrɒbɪn] 。

然后我们测试下服务发现,先来看下当前的POD:

复制代码
1
2
3
4
5
6
dongyunqi@mongodaddy:~/k8s$ kubectl get pod -l 'app=ngx-dep-pod' -o wide | awk '{print $1}' NAME ngx-dep-name-dcc8b7bfd-7lbrq ngx-dep-name-dcc8b7bfd-9m2rs ngx-dep-name-dcc8b7bfd-wp4zt

接着我们将PODngx-dep-name-dcc8b7bfd-7lbrq删除,如下:

复制代码
1
2
3
dongyunqi@mongodaddy:~/k8s$ kubectl delete pod ngx-dep-name-dcc8b7bfd-7lbrq pod "ngx-dep-name-dcc8b7bfd-7lbrq" deleted

然后查看是否新建POD:

复制代码
1
2
3
4
5
6
dongyunqi@mongodaddy:~/k8s$ kubectl get pod -l 'app=ngx-dep-pod' -o wide | awk '{print $1}' NAME ngx-dep-name-dcc8b7bfd-9m2rs ngx-dep-name-dcc8b7bfd-pzddf ngx-dep-name-dcc8b7bfd-wp4zt

可以看到新建了PODngx-dep-name-dcc8b7bfd-pzddf,再来看下负载均衡是否能使用到该POD:

复制代码
1
2
3
4
5
6
dongyunqi@mongodaddy:~/k8s$ curl 10.105.114.19 srv : 10.10.1.22:80 host: ngx-dep-name-dcc8b7bfd-pzddf uri : GET 10.105.114.19 / date: 2023-01-12T08:56:49+00:00

3.1:通过域名的方式访问

在看如何通过域名访问之前需要先看下什么是命名空间namespace,是一个逻辑上的概念,用来对API对象实例进行分组,每个API对象都会归属于某个命名空间,如果不显式指定则就是默认的default命名空间,如下查看所有的命名空间:

复制代码
1
2
3
4
5
6
7
8
dongyunqi@mongodaddy:~/k8s$ kubectl get namespace NAME STATUS AGE default Active 3d19h kube-flannel Active 3d5h kube-node-lease Active 3d19h kube-public Active 3d19h kube-system Active 3d19h

截止到现在我们定义的所有API对象都是没有显式指定命名空间的,所以都在default命名空间中。kube-system命名空间,像api-server,ectd等核心组件的POD都在该命名空间下。如下查看我们定义的Service API对象所在的命名空间:

复制代码
1
2
3
4
dongyunqi@mongodaddy:~/k8s$ kubectl get service -A | egrep 'NAMESP|ngx-svc'| awk '{print $1}' NAMESPACE default

看完了命名空间的概念后就可以来看域名了,service在启动后会生成默认的域名,规则是对象.名字空间.svc.cluster.local,但很多时候也可以省略后面的部分,直接写“对象.名字空间”甚至“对象名”就足够了,默认会使用对象所在的名字空间(比如这里就是 default),如下使用域名测试:

在这里插入图片描述

只有集群内的POD才能使用,k8s的node无法访问。

3.2:对外暴露服务

想要对外暴露服务的话需要配置Service API对象的type属性,该值默认是ClusterIP,通过静态IP方式负载均衡,只允许内部访问,当配置为NodePort时,service会在Node节点上映射一个访问端口号,因为是映射到Node上,所以外部就可以访问了,如下增加type:

复制代码
1
2
3
4
5
6
apiVersion: v1 ... spec: ... type: NodePort

完整如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1 kind: Service metadata: name: ngx-svc spec: selector: app: ngx-dep-pod type: NodePort ports: - port: 80 targetPort: 80 protocol: TCP

然后重新apply,如下:

复制代码
1
2
3
dongyunqi@mongodaddy:~/k8s$ kubectl apply -f service.yml service/ngx-svc configured

此时service就会在Node启用一个映射端口,如下查看:

复制代码
1
2
3
4
dongyunqi@mongodaddy:~/k8s$ kubectl get services ngx-svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ngx-svc NodePort 10.105.114.19 <none> 80:30825/TCP 3h56m

其中80:30825/TCP是将service的80端口映射到Node的30825,之后我们就可以直接通过访问Node的30825端口来访问POD了,NODE到Service,Service到POD的映射关系如下图:

在这里插入图片描述

访问测试如下:

在这里插入图片描述

在这里插入图片描述

可以看到能够正常访问,并且负载均衡也是生效的。

写在后面

小结

本文看了基于Service来实现服务的负载和自动发现,并看了如何以域名的方式访问,如何对外暴露服务。希望本文能够帮助到你。

多知道一点

Kubernetes也为每个Pod分配了域名,形式是“IP 地址. 名字空间.pod.cluster.local”,但需要把 IP 地址里的. 改成 - 。比如地址 10.10.1.87,它对应的域名就是 10-10-1-87.default.pod。

参考文章列表

最后

以上就是迷路便当最近收集整理的关于k8s之Service的全部内容,更多相关k8s之Service内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部