写在前面
本文接k8s之DaemonSet 。
通过Deployment我们可以实现一直有指定个数的POD在运行,而通过DaemonSet可以实现在每个Node上都有一个POD在运行,不管是这两种方式中的哪一种,都是仅仅实现了有若干个POD在运行
的效果,但是还无法正常的对外提供访问,并且要支持负载均衡,服务发现(因为POD可能经常会有老的退出,新的创建的情况,所以必须具备自动的服务发现机制,即发现退出的POD失效,新创建的POD加入到负载均衡中)
,为了实现这个目的,k8s提供了Service API对象,其定义如下:
1
2
3
4
5ongyunqi@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
3export out="--dry-run=client -o yaml" kubectl expose deploy ngx-dep-name --port=80 --target-port=80 $out
简单说明:
1
2
3deploy 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
21dongyunqi@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
14apiVersion: 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
16apiVersion: 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
6dongyunqi@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
33apiVersion: 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
3dongyunqi@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
32dongyunqi@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
14apiVersion: 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
7dongyunqi@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
17dongyunqi@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
6dongyunqi@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
16dongyunqi@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
6dongyunqi@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
3dongyunqi@mongodaddy:~/k8s$ kubectl delete pod ngx-dep-name-dcc8b7bfd-7lbrq pod "ngx-dep-name-dcc8b7bfd-7lbrq" deleted
然后查看是否新建POD:
1
2
3
4
5
6dongyunqi@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
6dongyunqi@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
8dongyunqi@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
4dongyunqi@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
6apiVersion: v1 ... spec: ... type: NodePort
完整如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15apiVersion: 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
3dongyunqi@mongodaddy:~/k8s$ kubectl apply -f service.yml service/ngx-svc configured
此时service就会在Node启用一个映射端口,如下查看:
1
2
3
4dongyunqi@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内容请搜索靠谱客的其他文章。
发表评论 取消回复