认识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