Service 类型
对于Kubernetes 可以实现内部服务的自由通信,也可以将平台内部的服务发布到外部环境 Service主要有四种类型,实现不同的网络通信功能,参考文档
https://kubernetes.io/docs/concepts/services-networking/service/#publishingservices-service-types

| 类型 | 解析 |
|---|---|
| ClusterIP | 此为Service的默认类型,为集群内部的客户端访问,包括节点和Pod等,外部网络无法访问,In client --> clusterIP: ServicePort (Service) --> PodIP: PodPort |
| NodePort | 本质上在ClusterIP模式基础上,再多加了一层端口映射的封装,相当于增强版的 ClusterIP 通过NodeIP:NodePort对外部网络提供服务,默认随机端口范围 30000~32767, 可指定为固定端口,NodePort是一个随机的端口,以防止端口冲突,在所有安装kube-proxy的节点上都会打开此相同的端口,可通过访问ClusterIP实现集群内部访问,也可以通过NodeIP:NortPort的方式实 现从集群外部至内部的访问 Ex Client --> NodeIP:NodePort (Service) --> PodIP:PodPort |
| LoadBalancer | 基于NodePort基础之上,使用集群外部的运营商负载均衡器方式实现对外提供 服务,增强版的NodePort 基于云运营商IaaS云创建一个Kubernetes云,云平台也支持LBaaS(Load Balance as a Service)产品服务 Master 借助cloud-manager向LBaaS的API请求动态创建软件LB,即支持和Kubernetes API Server 进行交互 如果没有云服务,将无法获取EXTERNAL-IP,显示Pending状态,则降级为 NodePort类型 Ex Client --> LB_IP:LB_PORT --> NodeIP:NodePort(Service)--> PodIP:PodPort |
| ExternalName | 当Kubernetes集群需要访问集群外部服务时,需要通过externalName将外部 主机引入到集群内部 外部主机名以 DNS方式解析为一个 CNAME记录给Kubernetes集群的其他主机 来使用 这种Service既没有ClusterIP,也没有NodePort.而且依赖于内部的CoreDNS功能In client -->Cluster ServiceName --> CName --> External Service Name 本方式的service没有selector,所以也不会创建同名的Endpoints资源对象 示例: Service名称:MySQL服务内部域名 mysql.default.svc.cluster.local MySQL服务外部域名:mysql.example.org In Client --> mysql.default.svc.cluster.local --> mysql.example.org --> 集群外的 MYSQL IP |
1.ClusterIP Service 实现
1.1.查看集群内默认 Service
#在集群内kubeapi对应的SVC默认占用Service 网络的第一个IP
root@master01:~# kubectl get svc kubernetes
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d16h
#DNS对应的SVc,默认占用Service网络的第10个IP
root@master01:~# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 3d17h
1.2.创建Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 3
selector:
matchLabels:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: nginx
image: harbor.apotos.com/nginx/nginx-podip:latest
ports:
- containerPort: 80
root@master01:~/kubernetes-cni# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master01 Ready control-plane 3d18h v1.32.12
node01 Ready <none> 3d18h v1.32.12
node02 Ready <none> 3d18h v1.32.12
root@master01:~/kubernetes-cni# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 3d18h
root@master01:~/kubernetes-cni# kubectl apply -f myweb-deploy.yaml
deployment.apps/myweb created
root@master01:~/kubernetes-cni# kubectl get pod -l app=myweb
NAME READY STATUS RESTARTS AGE
myweb-7d756bcd87-chbsv 1/1 Running 0 29s
myweb-7d756bcd87-kcbz7 1/1 Running 0 29s
myweb-7d756bcd87-pn27p 1/1 Running 0 29s
1.3.创建service-ClusterIP
kind: Service
apiVersion: v1
metadata:
name: myweb
spec:
# type: ClusterIP # 默认即为ClusterIP,此行可省略
# clusterIP: 192.168.64.100 # 可以手动指定IP,但一般都是系统自动指定而无需添加此行
selector:
app: myweb # 引用上面deployment的名称,同时也是Pod的Label中app的值,实现service代理指定Pod的功能
# version: v1.0 # 可以添加多个label与关系,即只过滤出有此处label匹配的pod,而非deployment的名称
ports:
- name: http
protocol: TCP
port: 80 # Service的端口
targetPort: 80 # 后端Pod对应的端口
root@master01:~/kubernetes-cni# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d18h
myweb ClusterIP 10.111.115.235 <none> 80/TCP 3m38s
service会自动创建同名的endpoint
root@master01:~/kubernetes-cni# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.48.100:6443 3d18h
myweb 172.16.140.85:80,172.16.196.149:80,172.16.196.150:80 6m1s
ENDPOINTS对应了deployment的三个pod。
root@master01:~/kubernetes-cni# kubectl get pod -l app=myweb -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myweb-7d756bcd87-chbsv 1/1 Running 0 25m 172.16.196.149 node01 <none> <none>
myweb-7d756bcd87-kcbz7 1/1 Running 0 25m 172.16.140.85 node02 <none> <none>
myweb-7d756bcd87-pn27p 1/1 Running 0 25m 172.16.196.150 node01 <none> <none>
1.4.访问service
宿主机上使用ClusterIP访问
root@master01:~/kubernetes-cni# curl 10.111.115.235
pod ip: 172.16.196.149
root@master01:~/kubernetes-cni# curl 10.111.115.235
pod ip: 172.16.140.85
root@master01:~/kubernetes-cni# curl 10.111.115.235
pod ip: 172.16.196.150
宿主机上使用PodIP访问
root@master01:~/kubernetes-cni# curl 172.16.196.149
pod ip: 172.16.196.149
root@master01:~/kubernetes-cni# curl 172.16.140.85
pod ip: 172.16.140.85
root@master01:~/kubernetes-cni# curl 172.16.196.150
pod ip: 172.16.196.150
宿主机上使用svc的域名访问
root@master01:~/kubernetes-cni# curl http://myweb.default.svc.cluster.local
curl: (6) Could not resolve host: myweb.default.svc.cluster.local
集群内的Pod使用svc的域名访问
创建用于测试的pod:
root@master01:~/kubernetes-cni# cat network-tools-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: network-tools
labels:
app: network-tools
spec:
containers:
- name: tools
image: harbor.apotos.com/network-tools/network-tools:latest
imagePullPolicy: IfNotPresent # 镜像拉取策略:如果本地不存在则拉取
command: ["/bin/sh", "-c"]
args: ["tail -f /dev/null"] # 保持容器运行
restartPolicy: Always
集群内的Pod使用svc的域名访问
root@master01:~/kubernetes-cni# kubectl exec -it network-tools -- curl myweb.default.svc.cluster.local
pod ip: 172.16.196.150
root@master01:~/kubernetes-cni# kubectl exec -it network-tools -- curl myweb.default.svc.cluster.local
pod ip: 172.16.196.149
root@master01:~/kubernetes-cni# kubectl exec -it network-tools -- curl myweb.default.svc.cluster.local
pod ip: 172.16.140.85
发现宿主机上使用svc的域名访问报错:Could not resolve host: myweb.default.svc.cluster.local
因为是在节点宿主机(master01)上直接 curl,宿主机默认不走 K8s 的集群 DNS(CoreDNS),而是走我们自己配置的DNS服务器,比如谷歌,阿里的dns服务器,所以curl myweb宿主机 DNS 解析不到 myweb(这是 Pod 内才默认能解析的短域名)
在宿主机上访问 ClusterIP 用这个就行:
curl -I http://10.111.115.235
curl http://10.111.115.235
在集群内(Pod 里)按服务名访问用这个域名:
curl http://myweb.default.svc.cluster.local
1.5.查看ipvs规则
root@master01:~/kubernetes-cni# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.111.115.235:80 rr
-> 172.16.140.85:80 Masq 1 0 0
-> 172.16.196.149:80 Masq 1 0 0
-> 172.16.196.150:80 Masq 1 0 0
可以看到ipvs的规则表中都有相关的负载均衡规则,访问10.111.115.235:80会按轮询(rr)的负载均衡策略转发到后端的172.16.140.85:80,172.16.196.149:80,172.16.196.150:80。
2.NodePort Service 实现
NodePort会在所有节点主机上,向外暴露一个指定或者随机的端口,供集群外部的应用能够访问 Kubernetes集群内部的Pod资源。 注意:nodePort 属性范围为 30000-32767,且type 类型只支持 NodePort或LoadBalancer 注意: 此方式有安全风险,端口为非标端口,而且性能一般,生产一般较少使用 生产中建议使用Ingress方式向外暴露集群内的服务
为了方便识别,将web改为web-clusterip,web-nodeport......
2.1.创建service-NodePort
apiVersion: v1
kind: Service
metadata:
name: myweb-nodeport
spec:
type: NodePort
selector:
app: myweb
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30066
2.2.查看service
root@master01:~/kubernetes-cni# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d20h
myweb-clusterip ClusterIP 10.96.250.54 <none> 80/TCP 56s
myweb-nodeport NodePort 10.103.29.129 <none> 80:30066/TCP 6s
2.3.访问service
#宿主机上使用nodeip:nodeport访问
root@master01:~/kubernetes-cni# curl 192.168.48.100:30066
pod ip: 172.16.140.85
root@master01:~/kubernetes-cni# curl 192.168.48.101:30066
pod ip: 172.16.196.150
root@master01:~/kubernetes-cni# curl 192.168.48.102:30066
pod ip: 172.16.196.149
其他的Clusterip可行的访问方式,nodeport类型的service也可以访问
root@master01:~/kubernetes-cni# curl 10.111.115.235
pod ip: 172.16.196.149
root@master01:~/kubernetes-cni# curl 172.16.196.149
pod ip: 172.16.196.149
root@master01:~/kubernetes-cni# kubectl exec -it network-tools -- curl myweb-clusterip.default.svc.cluster.local
pod ip: 172.16.196.150
root@master01:~/kubernetes-cni# kubectl exec -it network-tools -- curl myweb-nodeport.default.svc.cluster.local
pod ip: 172.16.196.150
2.4.查看ipvs规则
root@master01:~/kubernetes-cni# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.241.64:30066 rr
-> 172.16.140.85:80 Masq 1 0 0
-> 172.16.196.149:80 Masq 1 0 0
-> 172.16.196.150:80 Masq 1 0 0
3.LoadBalancer Service 实现
只有Kubernetes集群是部署在一个LBaaS平台上且指供有一个集群外部的LB才适合使用LoadBalancer 一般的公有云都提供了此功能,但可能会有费用产生 如果在一个非公用云的普通的Kubernetes集群上,创建了一个LoadBalancer类型的Service,一般情况 默认环境中是没有LBaaS的,所以会导致由于找不到指定的服务,状态会一直处于 Pending 状态 如果在私有云环境中使用 LoadBalancer Service,可以使用云原生的开源项目实现负载均衡器,比如 OpenELB, MetalLB 实现 Loadbalancer 可以获取用户访问的Service对应的Pod在哪个节点上,因此支持externaltrafficpolicy为 Local模式的流量转发
这里使用MetalLB
安装说明
https://metallb.universe.tf/installation/
https://metallb.io/installation/
如果 kube-proxy工作于ipvs模式,必须使用严格ARP(StrictARP)模式,因此若有必要,先运行如下 命令,配置kube-proxy。
kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl apply -f - -n kube-system
部署MetalLB至Kubernetes集群
METALLB_VERSION='v0.15.2'
kubectl apply -f
https://raw.githubusercontent.com/metallb/metallb/${METALLB_VERSION}/config/manifests/metallb-native.yaml
创建地址池:
注意: IPAddressPool 必须使用Kuberetes集群节点的IP地址段
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: localip-pool
namespace: metallb-system
spec:
addresses:
- 192.168.48.20-192.168.48.30
autoAssign: true
avoidBuggyIPs: true
创建二层公告机制:
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: localip-pool-l2a
namespace: metallb-system
spec:
ipAddressPools:
- localip-pool
interfaces:
- ens33 # 用于发送免费ARP公告
3.1.创建 Service
apiVersion: v1
kind: Service
metadata:
name: myweb-loadbalancer
spec:
type: LoadBalancer
selector:
app: myweb
ports:
- name: http
port: 80
targetPort: 80
3.2.查看Service
root@master01:~/kubernetes-cni# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d
myweb-clusterip ClusterIP 10.96.250.54 <none> 80/TCP 4h17m
myweb-loadbalancer LoadBalancer 10.106.36.213 192.168.48.20 80:32264/TCP 4h8m
myweb-nodeport NodePort 10.103.29.129 <none> 80:30066/TCP 4h16m
3.3.访问service
root@master01:~/kubernetes-cni# curl 192.168.48.20
pod ip: 172.16.196.149
root@master01:~/kubernetes-cni# curl 192.168.48.20
pod ip: 172.16.140.85
root@master01:~/kubernetes-cni# curl 192.168.48.20
pod ip: 172.16.196.150
查看ipvs规则
root@master01:~/kubernetes-cni# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.48.20:80 rr
-> 172.16.140.85:80 Masq 1 0 0
-> 172.16.196.149:80 Masq 1 0 0
-> 172.16.196.150:80 Masq 1 0 0
4.ExternalName Service 实现
Service 不仅可以实现Kubernetes集群内Pod应用之间的相互访问以及从集群外部访问集群中的Pod ExternalName Service 可以支持做为外部服务的代理实现集群中Pod访问集群外的服务或者另一个集群内的Service,因此也可以称为其它服务的"别名"服务 。
pod-->serviceName-->externalname-->外部dns-->外部服务ip
Service代理Kubernetes外部应用的使用场景:
-
在生产环境中Pod 希望使用某个固定的名称而非IP地址进行访问集群外部的服务
-
使用Service指向另一个Namespace中或其它Kubernetes集群中的服务
-
某个项目正在迁移至Kubernetes集群,但是一部分服务仍然在集群外部,此时可以使用service代 理至k8s集群外部的服务
4.1创建service
apiVersion: v1
kind: Service
metadata:
name: baidu
spec:
type: ExternalName
externalName: www.baidu.com
4.2.查看service
root@master01:~/kubernetes-cni# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
baidu ExternalName <none> www.baidu.com <none> 2m44s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d1h
myweb-clusterip ClusterIP 10.96.250.54 <none> 80/TCP 4h57m
myweb-loadbalancer LoadBalancer 10.106.36.213 192.168.48.20 80:32264/TCP 4h49m
myweb-nodeport NodePort 10.103.29.129 <none> 80:30066/TCP 4h57m
查看endpoint
root@master01:~/kubernetes-cni# kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.48.100:6443 4d1h
myweb-clusterip 172.16.140.85:80,172.16.196.149:80,172.16.196.150:80 5h6m
myweb-loadbalancer 172.16.140.85:80,172.16.196.149:80,172.16.196.150:80 4h57m
myweb-nodeport 172.16.140.85:80,172.16.196.149:80,172.16.196.150:80 5h5m
没有创建和service同名的endpoint资源对象。也没有CLUSTER-IP。
In client -->Cluster ServiceName --> CName --> External Service Name
root@master01:~/kubernetes-cni# kubectl exec -it network-tools -- nslookup baidu
;; Got recursion not available from 10.96.0.10
Server: 10.96.0.10
Address: 10.96.0.10#53
baidu.default.svc.cluster.local canonical name = www.baidu.com.
Name: www.baidu.com
Address: 198.18.0.4
4.3.访问service
root@master01:~/kubernetes-cni# kubectl exec -it network-tools -- ping baidu
PING www.baidu.com (198.18.0.4) 56(84) bytes of data.
64 bytes from 198.18.0.4 (198.18.0.4): icmp_seq=1 ttl=127 time=0.261 ms
64 bytes from 198.18.0.4 (198.18.0.4): icmp_seq=2 ttl=127 time=0.762 ms
^C
--- www.baidu.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1029ms
rtt min/avg/max/mdev = 0.261/0.511/0.762/0.250 ms