概述
写在前面
本文接k8s之DaemonSet 。
通过Deployment我们可以实现一直有指定个数的POD在运行,而通过DaemonSet可以实现在每个Node上都有一个POD在运行,不管是这两种方式中的哪一种,都是仅仅实现了有若干个POD在运行
的效果,但是还无法正常的对外提供访问,并且要支持负载均衡,服务发现(因为POD可能经常会有老的退出,新的创建的情况,所以必须具备自动的服务发现机制,即发现退出的POD失效,新创建的POD加入到负载均衡中)
,为了实现这个目的,k8s提供了Service API对象,其定义如下:
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的功能吧!)
,如下:
export out="--dry-run=client -o yaml"
kubectl expose deploy ngx-dep-name --port=80 --target-port=80 $out
简单说明:
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模板如下:
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。修改后如下:
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如下:
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后如下:
{
"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,如下:
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容器中去,修改后如下:
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让配置生效,如下:
dongyunqi@mongodaddy:~/k8s$ kubectl apply -f deploy.yml
...
完成后可以进入POD的NGINX容器中查看文件是否挂载进去,如下:
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:
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:
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信息:
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,如下验证:
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
,那么我们就可以了来测试负载均衡效果了,如下:
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:
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
删除,如下:
dongyunqi@mongodaddy:~/k8s$ kubectl delete pod ngx-dep-name-dcc8b7bfd-7lbrq
pod "ngx-dep-name-dcc8b7bfd-7lbrq" deleted
然后查看是否新建POD:
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:
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
命名空间,如下查看所有的命名空间:
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对象所在的命名空间:
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:
apiVersion: v1
...
spec:
...
type: NodePort
完整如下:
apiVersion: v1
kind: Service
metadata:
name: ngx-svc
spec:
selector:
app: ngx-dep-pod
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
然后重新apply,如下:
dongyunqi@mongodaddy:~/k8s$ kubectl apply -f service.yml
service/ngx-svc configured
此时service就会在Node启用一个映射端口,如下查看:
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所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复