赞
踩
按照图示部署好了K8s集群,一个Master,两个worker nodes。
所以Kubernetes Service定义了这样一种抽象:一个Pod的逻辑分组,一种可以访问它们的策略 ——通常称为微服务。这一组Pod能够被Service访问到,通常是通过Label Selector。用户仅需要访问 Service,Service能够提供负载均衡的能力,能够将用户流量分配到背后的pod上去。
Service能够提供负载均衡的能力,但是在使用上有以下限制:
只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的。
创建deployment,部署3个pods:
生成并修改deployment的yaml文件:将端口映射到宿主的33300
kubectl create deployment web1 --image=nginx --dry-run=client -o yaml >web1.yaml [root@vms201 svc]# cat web1.yaml apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: web1 name: web1 spec: replicas: 2 selector: matchLabels: app: web1 strategy: {} template: metadata: creationTimestamp: null labels: app: web1 spec: terminationGracePeriodSeconds: 0 containers: - image: nginx name: nginx imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 hostPort: 33300 resources: {} status: {}
创建并查看deployment:
[root@vms201 svc]# kubectl create -f web1.yaml
deployment.apps/web1 created
[root@vms201 svc]# kubectl create -f web1.yaml
deployment.apps/web1 created
[root@vms201 svc]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web1-7579ff4ffd-rmhmn 1/1 Running 0 5s 10.244.185.124 vms203.rhce.cc <none> <none>
web1-7579ff4ffd-xrzkn 1/1 Running 0 5s 10.244.58.249 vms202.rhce.cc <none> <none>
[root@vms201 svc]# kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
web1 2/2 2 2 10s
这种情况下,在外部设备访问两个worker nodes上的2个pod:
vms202:
vms203:
这种情况虽然能够满足外部设备访问集群中的pod,但是需要修改对应的node的IP地址进行访问,不满足实际的需求。同时,如果一个node上有多个容器映射端口到宿主机的同一个端口号上,会出现端口冲突的故障。
部署SVC关联的deployment:
修改deploy的yaml文件并部署:
[root@vms201 svc]# cat web1.yaml apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: web1 name: web1 spec: replicas: 2 selector: matchLabels: app1: web1 strategy: {} template: metadata: creationTimestamp: null labels: app1: web1 app2: web2 app3: web3 spec: terminationGracePeriodSeconds: 0 containers: - image: nginx name: nginx imagePullPolicy: IfNotPresent ports: resources: {} status: {} [root@vms201 svc]# kubectl apply -f web1.yaml deployment.apps/web1 created [root@vms201 svc]# kubectl get pods --show-labels NAME READY STATUS RESTARTS AGE LABELS web1-54cbfdf7b5-c5r9n 1/1 Running 0 5m49s app1=web1,app2=web2,app3=web3,pod -template-hash=54cbfdf7b5 web1-54cbfdf7b5-wg2vr 1/1 Running 0 5m49s app1=web1,app2=web2,app3=web3,pod -template-hash=54cbfdf7b5
yaml文件中定义了2个副本,并且每个pod有3个标签。
创建SVC的方式:
举例: 通过命令行的方式创建SVC(建议)
语法:
kubectl expose --name=名字 资源类型/名字 --port=xxx --target-port=yyy --selector=aaa=bbb
其中target-port是pod运行的端口,不能随便写。而—port为SVC的端口,可以随意修改。对deployment设置服务:
[root@vms201 svc]# kubectl expose --name=svc1 deployment web1 --port=80 --target-port=80
service/svc1 exposed
[root@vms201 svc]# kubectl get svc svc1 -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc1 ClusterIP 10.105.219.0 <none> 80/TCP 63s app1=web1
可以看到SELECTOR为web1,说明了如果在创建SVC时没有指定哪个标签定位pod时,使用的是和deploy一样的标签。查看SVC关联的后端容器等信息:
[root@vms201 svc]# kubectl describe svc svc1 Name: svc1 Namespace: svc Labels: app=web1 Annotations: <none> Selector: app1=web1 Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.105.219.0 IPs: 10.105.219.0 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.185.125:80,10.244.58.252:80 Session Affinity: None Events: <none>
Endpoints指明了SVC关联的后端容器的地址。
注意:如果我们再deployment之外重新创建了一个pod,且标签和设置的SVC匹配的标签相同,也会被被SVC关联。
修改deployment中部署pod的主页,查看负载分担的情况:
kubectl exec -it web1-54cbfdf7b5-c5r9n -- sh -c "echo 111 > /usr/share/nginx/html/index.html"
kubectl exec -it web1-54cbfdf7b5-wg2vr -- sh -c "echo 222 > /usr/share/nginx/html/index.html"
svc的地址为 10.105.219.0,在cluster中都可以访问:
[root@vms201 svc]# while true ; do
> curl 10.105.219.0
> sleep 1
> done
111
111
222
111
111
222
111
111
222
111
222
可以看到流量被分配到了SVC关联的两个pod中去。
注意:kube-proxy的模式是iptales(默认),则无法ping通svc的地址,如果为ipvs,则可以ping通svc的地址。
[root@vms201 svc]# ping 10.105.219
PING 10.105.219 (10.105.0.219) 56(84) bytes of data.
我们现在创建的应用比较单一,有时候需要创建多级应用,例如WordPress+mysql。流量访问Wordpress的SVC,被负载到身后的pods中;Wordpress的pod再连接到mysql的pod的SVC上,访问mysqldb。而wordpress如何访问mysql pods的SVC,则就是集群的发现。
服务发现的三种方式:
方式1: clusterIP
修改mysql的vim文件:
kubectl run dbpod --image=mysql --image-pull-policy=IfNotPresent --dry-run=client -o yaml > dbpod.yaml [root@vms201 svc]# cat dbpod.yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: dbpod name: dbpod spec: terminationGracePeriodSeconds: 0 containers: - image: mysql imagePullPolicy: IfNotPresent name: dbpod resources: {} env: - name: MYSQL_ROOT_PASSWORD value: mysql1 - name: MYSQL_USER value: tom - name: MYSQL_PASWORD value: mysql1 - name: MYSQL_DATABASE value: wordpress dnsPolicy: ClusterFirst restartPolicy: Always status: {}
创建并查看mysql的pod:
[root@vms201 svc]# kubectl create -f dbpod.yaml
pod/dbpod created
[root@vms201 svc]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dbpod 1/1 Running 0 6s
创建mysql pod对应的SVC:
[root@vms201 svc]# kubectl expose --name=dbsvc pod dbpod --port=3306 --target-port=3306
service/dbsvc exposed
[root@vms201 svc]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dbsvc ClusterIP 10.111.190.70 <none> 3306/TCP 27s
创建wordpress的pod,yaml文件如下:
[root@vms201 svc]# cat blogpod.yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: blogpod name: blogpod spec: terminationGracePeriodSeconds: 0 containers: - image: wordpress imagePullPolicy: IfNotPresent name: blogpod resources: {} env: - name: WORDPRESS_DB_HOST value: 10.111.190.70 - name: WORDPRESS_DB_USER value: root - name: WORDPRESS_DB_PASSWORD value: mysql1 - name: WORDPRESS_DB_NAME value: wordpress dnsPolicy: ClusterFirst restartPolicy: Always status: {}
其中WORDPRESS_DB_HOST为mysql SVC的cluster IP,WORDPRESS_DB_USER、WORDPRESS_DB_PASSWORD、WORDPRESS_DB_NAME分别是mysqldb的账号密码和数据库。
[root@vms201 svc]# kubectl apply -f blogpod.yaml
pod/blogpod created
[root@vms201 svc]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
blogpod 1/1 Running 0 2m23s 10.244.185.66 vms203.rhce.cc <none> <none>
dbpod 1/1 Running 0 8m10s 10.244.185.126 vms203.rhce.cc <none> <none>
可以看到,blogpod运行在vms203.rhce.cc宿主机上。接着创建wordpress的SVC:
[root@vms201 svc]# kubectl expose --name=blogsvc pod blogpod --port=80 --target-port=80 --type=NodePort
service/blogsvc exposed
[root@vms201 svc]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
blogsvc NodePort 10.105.201.27 <none> 80:32726/TCP 5s
dbsvc ClusterIP 10.111.190.70 <none> 3306/TCP 8m21s
type=NodePort会在后续补充。现在可以使用宿主机vms203.rhce.cc的32726端口访问到wordpress,发现不用创建数据库,说明连接成功。
方式2: 变量方式
修改blogpod的变量:blogpod会以变量的方式去记录服务的信息。进入pod,输入命令env查看环境变量:
[root@vms201 svc]# kubectl exec -it blogpod -- bash
root@blogpod:/var/www/html# env | grep SERVICE_HOST
DBSVC_SERVICE_HOST=10.111.190.70
KUBERNETES_SERVICE_HOST=10.96.0.1
root@blogpod:/var/www/html# env | grep SERVICE_PORT
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
DBSVC_SERVICE_PORT=3306
其中的几个参数需要注意:
相关SVC信息如果哎此pod之前创建,会自动被记录,在之后则不会。
重新创建blogpod:
[root@vms201 svc]# kubectl delete -f blogpod.yaml
pod "blogpod" deleted
修改blogpod的yaml文件:引用为变量的方式。
[root@vms201 svc]# cat blogpod.yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: blogpod name: blogpod spec: terminationGracePeriodSeconds: 0 containers: - image: wordpress imagePullPolicy: IfNotPresent name: blogpod resources: {} env: - name: WORDPRESS_DB_HOST value: $(DBSVC_SERVICE_HOST) - name: WORDPRESS_DB_USER value: root - name: WORDPRESS_DB_PASSWORD value: mysql1 - name: WORDPRESS_DB_NAME value: wordpress dnsPolicy: ClusterFirst restartPolicy: Always status: {}
同理可以访问到WordPress上,且不用创建数据库。
此方式存在的问题:
方式3: DNS方式
直接通过SVC名称的方式访问服务。可以看到集群中已经存在了一个DNS的SVC:kube-dns 。
[root@vms201 svc]# kubectl get svc -n kube-system -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 7d17h k8s-app=kube-dns
metrics-server ClusterIP 10.96.163.225 <none> 443/TCP 7d15h k8s-app=metrics-server
当我们每创建一个SVC,都会想kube-dns 进行注册,kubedns都会知道这个这个SVC的IP地址。所以在同一个命名空间里面可以直接通过SVC名访问SVC(通过这个SVC名向kube-dns进行查询),如果pod和SVC不是同一个命名空间,需要加上指定SVC命名空间的参数。
重新创建pod,修改WORDPRESS_DB_HOST参数为dbsvc即可:
[root@vms201 svc]# kubectl delete -f blogpod.yaml pod "blogpod" deleted [root@vms201 svc]# vim blogpod.yaml [root@vms201 svc]# cat blogpod.yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: blogpod name: blogpod spec: terminationGracePeriodSeconds: 0 containers: - image: wordpress imagePullPolicy: IfNotPresent name: blogpod resources: {} env: - name: WORDPRESS_DB_HOST value: dbsvc - name: WORDPRESS_DB_USER value: root - name: WORDPRESS_DB_PASSWORD value: mysql1 - name: WORDPRESS_DB_NAME value: wordpress dnsPolicy: ClusterFirst restartPolicy: Always status: {} [root@vms201 svc]# kubectl apply -f blogpod.yaml pod/blogpod created
同理可以访问到WordPress上,且不用创建数据库。
所谓发布指的是,如何让集群之外的主机能访问服务。Service发布方式在 K8s 中有以下四种类型:
1.ClusterIp:
默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP(service创建一个仅集群内部可访问的ip,集群内部其他的pod可以通过该服务访问到其监控下的pod)
2.NodePort:
在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过NodePort 来访问该服务(在service及各个node节点上开启端口,外部的应用程序或客户端访问node的端口将会转发到service的端口,而service将会依据负载均衡随机将请求转发到某一个pod的端口上。一般暴露服务常用的端口)。NodePort是宿主机的端口,port是Service的端口,target-port为pod的端口。
创建deploy和服务,type内心为NodePort:
[root@vms201 svc]# kubectl create -f web1.yaml
deployment.apps/web1 created
[root@vms201 svc]# kubectl expose --name=websvc deployment web1 --port=80 --target-port=80 --type=NodePort
service/blogsvc exposed
[root@vms201 svc]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
websvc NodePort 10.103.97.15 <none> 80:32717/TCP 2m12s
可以看到,type为NodePort,映射的端口为32717,我们就可以在外部设备访问集群内的任何一个机器(无论是master还是worker nodes),以访问到内部的pod:
此方式存在的问题,例如在一个集群发布了多个SVC,那么其宿主机上也会映射相应的端,这样会导致2个问题:
3.LoadBalancer:
在 NodePort 的基础上,借助cloud provider创建一个外部负载均衡器,并将请求转发到: NodePort(在NodePort基础之上,即各个节点前加入了负载均衡器实现了真正的高可用,一般云供应商提供的k8s集群就是这种)
每个SVC都有一个私有的地址,只能在集群内部访问,如果我们将一个SVC设置为Loadbalancer,则这个SVC会获取一个外部IP地址(需要一个地址池,由metallb提供)
安装metallb,官网:https://metallb.universe.tf/installation/,根据其中的步骤完成metalib安装。创建命名空间:
[root@vms201 svc]# kubectl create ns metallb-system
namespace/metallb-system created
下载的yaml文件,地址为:https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/metallb.yaml,下载完成后保存为metallb.yaml文件。修改其文件,在image下设置imagePullPolicy为IfNotPresent。
应用对于的yaml文件创建metallb的pod:
[root@vms201 svc]# kubectl apply -f metallb.yaml Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+ podsecuritypolicy.policy/controller created podsecuritypolicy.policy/speaker created serviceaccount/controller created serviceaccount/speaker created clusterrole.rbac.authorization.k8s.io/metallb-system:controller created clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created role.rbac.authorization.k8s.io/config-watcher created role.rbac.authorization.k8s.io/pod-lister created role.rbac.authorization.k8s.io/controller created clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created rolebinding.rbac.authorization.k8s.io/config-watcher created rolebinding.rbac.authorization.k8s.io/pod-lister created rolebinding.rbac.authorization.k8s.io/controller created daemonset.apps/speaker created deployment.apps/controller created [root@vms201 svc]# kubectl get pods -n metallb-system NAME READY STATUS RESTARTS AGE controller-6b78bff7d9-shv95 1/1 Running 0 42m speaker-gcbkh 1/1 Running 0 42m speaker-tg2tm 1/1 Running 0 42m speaker-v5f7h 1/1 Running 0 42m
创建地组池,yaml文件如下:分配的地址为192.168.0.240-192.168.0.250,与node同网段。
[root@vms201 svc]# cat pool.yaml
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.0.240-192.168.0.250
应用yaml文件:
[root@vms201 svc]# kubectl apply -f pool.yaml
configmap/config created
删除之前的SVC,创建新的SVC:
[root@vms201 svc]# kubectl delete svc websvc
service "websvc" deleted
[root@vms201 svc]# kubectl expose --name=websvc deployment web1 --port=80 --target-port=80 --type=LoadBalancer
service/websvc exposed
[root@vms201 svc]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
websvc LoadBalancer 10.110.232.180 192.168.0.240 80:32010/TCP 45s
可以看到,EXTERNAL-IP为地组池分配的地址:192.168.0.240。如果没有安装metallib,服务的状态为pending。外部设备现在可以以192.168.0.240直接访问pod:
LB方式最大的缺点则是每个service一个LB又有点浪费和麻烦,,并且需要k8s之外的支持。
4.Ingress:(推荐)
ingress(简称ing)则只需要一个NodePort或者一个LB就可以满足所有service对外服务的需求。工作机制大致可以用下图表示:
实际上,ingress相当于一个7层的负载均衡器,是k8s对反向代理的一个抽象。大概的工作原理也确实类似于Nginx,可以理解成在 Ingress 里建立一个个映射规则 , ingress Controller 通过监听 Ingress这个api对象里的配置规则并转化成 Nginx 的配置(kubernetes声明式API和控制循环) , 然后对外部提供服务。
搭建ingress–nginx控制器过程:
1.首先在nginx上下载对应镜像和yaml文件。
2.在三台设备上加载镜像。
3.应用其yaml文件生成ingress控制器:
[root@vms201 svc]# kubectl apply -f nginx-ingress-controller.yaml namespace/ingress-nginx created serviceaccount/ingress-nginx created configmap/ingress-nginx-controller created clusterrole.rbac.authorization.k8s.io/ingress-nginx created clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created role.rbac.authorization.k8s.io/ingress-nginx created rolebinding.rbac.authorization.k8s.io/ingress-nginx created service/ingress-nginx-controller-admission created service/ingress-nginx-controller created deployment.apps/ingress-nginx-controller created validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created serviceaccount/ingress-nginx-admission created clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created role.rbac.authorization.k8s.io/ingress-nginx-admission created rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created job.batch/ingress-nginx-admission-create created job.batch/ingress-nginx-admission-patch created
4.查看运行结果: 可以看到,控制器在vms202.rhce.cc node上运行。
[root@vms201 svc]# kubectl get pods -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NOD E READINESS GATES
ingress-nginx-admission-create-pc4pd 0/1 Completed 0 2m30s 10.244.185.75 vms203.rhce.cc <none> <none>
ingress-nginx-admission-patch-phkjj 0/1 Completed 1 2m30s 10.244.185.76 vms203.rhce.cc <none> <none>
ingress-nginx-controller-59b8bf5fdc-4x4x4 1/1 Running 0 2m30s 192.168.0.202 vms202.rhce.cc <none> <none>
5.修改外部设备的host文件,当访问web应用(2个pod)的域名时,都被解析为node2的ip 192.168.0.202。添加内容如下:
192.168.0.202 www1.rhce.cc
192.168.0.202 www2.rhce.cc
6.创建2个nginx的pod:
yaml文件为:
[root@vms201 svc]# cat pod1.yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: web1 name: web1 spec: terminationGracePeriodSeconds: 0 containers: - image: nginx imagePullPolicy: IfNotPresent name: c1 resources: {} dnsPolicy: ClusterFirst restartPolicy: Always status: {}
创建两个nginx的pod:
[root@vms201 svc]# kubectl apply -f pod1.yaml
pod/web1 created
[root@vms201 svc]# sed 's/web1/web2/' pod1.yaml | kubectl apply -f -
pod/web2 created
[root@vms201 svc]# kubectl get pods
[root@vms201 svc]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web1 1/1 Running 0 16s 10.244.185.69 vms203.rhce.cc <none> <none>
web2 1/1 Running 0 15s 10.244.185.70 vms203.rhce.cc <none> <none>
分别将其中主页修改为111和222,方便后续测试观察。
[root@vms201 svc]# kubectl exec -it web1 -- sh -c "echo 111 > /usr/share/nginx/html/index.html"
[root@vms201 svc]# kubectl exec -it web2 -- sh -c "echo 222 > /usr/share/nginx/html/index.html"
7.分别对于两个pod创建service
[root@vms201 svc]# kubectl expose --name=svc1 pod web1 --port=80
service/svc1 exposed
[root@vms201 svc]# kubectl expose --name=svc2 pod web2 --port=80
service/svc2 exposed
[root@vms201 svc]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc1 ClusterIP 10.104.248.179 <none> 80/TCP 25s
svc2 ClusterIP 10.100.210.160 <none> 80/TCP 14s
8.设置ingress规则:
我们需要设置当访问www1.rhce.cc时,流量被发送到web1;当访问www2.rhce.cc时,流量被发送到web2 。注意:配置ingress规则不需要切换到ingress的命名空间。
编辑ingress规则的yaml文件(在官网查找格式:https://kubernetes.io/docs/concepts/services-networking/ingress/):
[root@vms201 svc]# cat ingressrule.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myingress #annotations: # nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: www1.rhce.cc http: paths: - path: / pathType: Prefix backend: service: name: svc1 port: number: 80 - host: www2.rhce.cc http: paths: - path: / pathType: Prefix backend: service: name: svc2 port: number: 80
在测试环境中需要注释2句话。
9.创建ingress规则,应用到ingress上,然后测试:
[root@vms201 svc]# kubectl apply -f ingressrule.yaml
ingress.networking.k8s.io/myingress created
[root@vms201 svc]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
myingress <none> www1.rhce.cc,www2.rhce.cc 10.108.154.34 80 29s
然后在修改了host文件的设备上访问www1.rhce.cc和www2.rhce.cc:测试成功。
注意点:
如果在别的命名空间里也创建了和当前命名空间一样的ingress规则,会报错,告知www1.rhce.cc这个域名已经被占用了。
参考资料:
6. 《老段CKA课程》
7. 链接:https://www.jianshu.com/p/fd597312751a
8. 链接:https://blog.csdn.net/yrx420909/article/details/105724292
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。