Kubernetes之十九: Service服务
认识Service
1)为什么要使用service
kubernetes pod是有生命周期的,可以被创建,也可以被销毁;一旦被销毁,通过控制器能够动态创建新的pod,每次pod都会获取自己的IP地址会变动
2)service介绍
Kubernetes Service定义了这样一种抽象:一个Pod的逻辑分组,一种可以访问它们不同的策略—通常称为微服务。这一组Pod能够被Service访问到,通常是通过Label Sekector实现的;
对Kubernetes集群中的应用,Kubernetes提供了简单的Endpoints API,只要service中的一组Pod发生变更,应用程序就会被更新。
对非Kubernetes集群中的应用,Kubernetes提供了基本VIP的网桥的方式访问Service,再由Service重定向到backend Pod
3)三种代理模式
userspace 代理模式
iptables 代理模式
ipvs 代理模式
(下图为iptables代理模式)
(下图为ipvs的代理模式)
iptables:
客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。
另外,如果集群中存在上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折扣。
ipvs:
客户端IP请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Kubernetes Service
对象和Endpoints
,调用netlink
接口以相应地创建ipvs规则并定期与Kubernetes Service
对象和Endpoints
对象同步ipvs规则,
以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。
Service类型
ClusterIP
:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的ServiceType
。NodePort
:通过每个 Node 上的 IP 和静态端口(NodePort
)暴露服务。NodePort
服务会路由到ClusterIP
服务,这个ClusterIP
服务会自动创建。通过请求<NodeIP>:<NodePort>
,可以从集群的外部访问一个NodePort
服务。LoadBalancer
:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort
服务和ClusterIP
服务。ExternalName
:通过返回CNAME
和它的值,可以将服务映射到externalName
字段的内容(例如,foo.bar.example.com
)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的kube-dns
才支持- 无 selector 的Services (需要手工配置Endpoints)
- 无 ClusterIP的Service (headless service)
第二部分: 案例
1)创建clusterIP类型的service
#定义deployment和svc apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: default spec: replicas: 1 selector: matchLabels: app: redis role: logstor template: metadata: labels: app: redis role: logstor spec: containers: - name: redis image: redis:4.0-alpine ports: - name: redis containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: redis namespace: default spec: selector: app: redis role: logstor type: ClusterIP ports: - port: 6380 targetPort: 6379 #kubectl apply -f redis-svc.yaml 验证 [root@master01 mainfest]# kubectl get pods NAME READY STATUS RESTARTS AGE redis-85b846ff9c-x2f48 1/1 Running 0 35s [root@master01 mainfest]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE redis ClusterIP 10.20.227.191 <none> 6380/TCP 38s [root@master01 mainfest]# redis-cli -h 10.20.227.191 -p 6380 10.20.227.191:6380> keys * (empty list or set) 10.20.227.191:6380> set name aaa OK
2)创建NodePort类型的Service
apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: myapp release: canary template: metadata: labels: app: myapp release: canary spec: containers: - name: myapp image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: selector: app: myapp release: canary type: NodePort ports: - port: 80 targetPort: 80 nodePort: 31180 #kubectl apply -f myapp-svc.yaml 验证 [root@master01 mainfest]# kubectl get pods NAME READY STATUS RESTARTS AGE myapp-deploy-65df64765c-27gj6 1/1 Running 0 23s myapp-deploy-65df64765c-2wv2t 1/1 Running 0 23s myapp-deploy-65df64765c-9nmwb 1/1 Running 0 23s [root@master01 mainfest]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.20.0.1 <none> 443/TCP 4d14h myapp NodePort 10.20.44.7 <none> 80:31180/TCP 31s #打补丁修改sessionAffinity为clientip;实现会话连接 [root@master01 mainfest]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClientIP"}}' service/myapp patched [root@master01 mainfest]# kubectl describe svc myapp Name: myapp Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"ports":[{"nodePort":31180,... Selector: app=myapp,release=canary Type: NodePort IP: 10.20.44.7 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 31180/TCP Endpoints: 172.31.219.219:80,172.31.67.144:80,172.31.67.145:80 Session Affinity: ClientIP External Traffic Policy: Cluster Events: <none> [root@master01 mainfest]# while true;do curl 192.168.10.211:31180/hostname.html;sleep 1;done myapp-deploy-65df64765c-2wv2t myapp-deploy-65df64765c-2wv2t myapp-deploy-65df64765c-2wv2t myapp-deploy-65df64765c-2wv2t myapp-deploy-65df64765c-2wv2t [root@master01 mainfest]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"None"}}' service/myapp patched [root@master01 mainfest]# while true;do curl 192.168.10.211:31180/hostname.html;sleep 1;done myapp-deploy-65df64765c-2wv2t myapp-deploy-65df64765c-9nmwb myapp-deploy-65df64765c-27gj6 myapp-deploy-65df64765c-9nmwb myapp-deploy-65df64765c-2wv2t myapp-deploy-65df64765c-9nmwb
3)创建无头service(headless)
apiVersion: v1 kind: Service metadata: name: myapp-svc namespace: default spec: selector: app: myapp release: canary clusterIP: None ports: - port: 80 targetPort: 80 返回后端所有pod的地址 [root@master01 mainfest]# dig -t A myapp-svc.default.svc.ziioffice.com. @10.20.254.254 ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> -t A myapp-svc.default.svc.ziioffice.com. @10.20.254.254 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6273 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;myapp-svc.default.svc.ziioffice.com. IN A ;; ANSWER SECTION: myapp-svc.default.svc.ziioffice.com. 5 IN A 172.31.67.146 myapp-svc.default.svc.ziioffice.com. 5 IN A 172.31.219.220 myapp-svc.default.svc.ziioffice.com. 5 IN A 172.31.67.147
4)没有selector的service(指定外部)
Service抽象了该如何访问Kubernetes Pod,但也能抽象其他类型的backend,例如:
希望在生产环境中使用外部的数据库集群,但测试环境使用自己的数据库。
希望服务指向另一个Namespace中或其他集群中的服务。
apiVersion: v1 kind: Endpoints metadata: name: mysql-service namespace: default subsets: - addresses: - ip: 192.168.10.204 ports: - port: 3306 --- kind: Service apiVersion: v1 metadata: name: mysql-service namespace: default spec: ports: - protocol: TCP port: 3336 targetPort: 3306 #测试pod内部,通过service,访问外部的数据库 [root@web-55fff97c7-gsrl5 /]# mysql -h mysql-service.default.svc.ziioffice.com -u aa -p -P 3336 Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 249 Server version: 5.6.40 MySQL Community Server (GPL) Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
apiVersion: v1 kind: Endpoints metadata: name: redis-service namespace: default subsets: - addresses: - ip: 192.168.10.204 ports: - port: 6379 --- apiVersion: v1 kind: Service metadata: name: redis-service spec: ports: - port: 6380 targetPort: 6379 #kubectl apply -f redis-svc.yaml [root@web-55fff97c7-7mdf9 /]# redis-cli -h redis-service.default.svc.ziioffice.com -p 6380 redis-service.default.svc.ziioffice.com:6380>
5) externalname
引入rds数据库到集群内部
apiVersion: v1 kind: Service metadata: name: prod-mysql-service namespace: default spec: type: ExternalName externalName: rm-uf665jsp7111111.mysql.rds.aliyuncs.com 测试: [root@master03-171 mainfest]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE prod-mysql-service ExternalName <none> rm-uf665jsp7vwk4lz83.mysql.rds.aliyuncs.com <none> 20m [root@master03-171 mainfest]# kubectl exec -it pod test-centos-6979f6bc4c-clbz7 -- /bin/bash [root@test-centos-6979f6bc4c-clbz7 /]# yum -y install mysql [root@test-centos-6979f6bc4c-clbz7 /]# mysql -h prod-mysql-service.default.svc.ziioffice.com -u xionghh -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 60 Server version: 5.7.26-log Source distribution Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
连接阿里云redis
apiVersion: v1 kind: Service metadata: name: prod-redis-service namespace: default spec: type: ExternalName externalName: r-uf6d6wb8111111.redis.rds.aliyuncs.com 验证: [root@master03-171 mainfest]# kubectl describe svc prod-redis-service Name: prod-redis-service Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"prod-redis-service","namespace":"default"},"spec":{"externalName"... Selector: <none> Type: ExternalName IP: External Name: r-uf6d6wb8wj63ru7vtm.redis.rds.aliyuncs.com Session Affinity: None Events: <none>
[root@test-centos-6979f6bc4c-clbz7 /]# redis-cli -h prod-redis-service.default.svc.ziioffice.com -p 6379 prod-redis-service.default.svc.ziioffice.com:6379> keys * (error) NOAUTH Authentication required.
连接mongodb服务
apiVersion: v1 kind: Service metadata: name: prod-mongodb-service namespace: defalut spec: type: ExternalName externalName: dds-uf618b111111.mongodb.rds.aliyuncs.com