service 概念
SVC 通过 Label Selector 标签选择的方式,匹配一组 Pod,对外访问服务。每一个 SVC可以理解成为一个微服务。
service 能够提供负载均衡的能力,但是在使用上有以下限制:
- 只提供4层负载均衡能力(只有 RR 轮询算法),而没有7层功能,如果需要更多的匹配规则来转发请求,4层上的负载均衡是不支持的。
service 类型
Clusterip:默认类型,自动分配一个仅 Cluster 内部可以访问的 虚拟IP,一般用作集群内部负载均衡。
NodePort(service向外暴露):在ClusterIP 基础上为 Service 在每台机器上绑定一个映射端口,外网客户端可以通过 NodeIP,Nodeport访问。
LoadBalancer(service向外暴露):在 NodePort 基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到 NodeIP 和 NodePort
ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,只有1.7之后版本的 kube-dns支持。
SVC 访问流程组件
首先 apiserver 监听 kube-proxy 去进行服务和端口的发现。
通过kube-proxy监控,kube-proxy监控所有 Pod 节点信息,标签、IP、port 等,并负责把它们写入到 iptables 规则当中。
client 访问 SVC,其实访问的是 iptables规则。再由 iptables 导向后端 Pod 节点。
ipvs,图片上没有,现在的ClusterIP,NodePort 都是采取 ipvs 调度算法对后端 Pod 节点进行调度访问。
VIP 和 Service 代理
在kubernetes集群中,每个 Node 运行一个 Kube-porxy 进程。kube-proxy负责为 service 实现了一种 VIP (虚拟IP) 的形式,而不是 ExternaName 的形式。
-
1.0版本,代理完全在 userspace
-
1.1版本,新增 iptables 代理,但并不是默认的运行模式
-
1.2版本,默认采用 iptables 代理
-
1.4版本,默认采用 ipvs 代理
service 是 “4层(TCP/UDP over IP)概念”,在1.1版本 新增了 Ingress API(beat 版),用来表示“7层代理”。
代理模式分类
Ⅰ、userspace 代理模式
client Pod 想要访问 Server Pod
- 首先经过 Service IP(iptables),由 iptables转发至kube-proxy,kube-proxy根据访问的地址,端口进行转发,需要 kube-proxy 充当代理功能。
- 其次 kube-apiserver 也会去监听 kube-proxy,kube-proxy 是否进行所有 Pod 信息的写入。
- 导致 kube-proxy 的工作负载量过大,压力过大。
Ⅱ、Iptables 代理模式
client Pod 访问 Server Pod
所有的请求,代理全部由 iptables 来完成,不再需要 kube-proxy 充当代理角色。 提高了 稳定性和访问效率,压力减少
Ⅲ、ipvs 代理模式
ipvs (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的4层LAN交换,作为 Linux 内核的一部分。ipvs运行在主机上,在真实服务器集群前充当负载均衡器。ipvs可以将基于TCP和UDP的服务请求转发到真实服务器上,并使真实服务器的服务在单个 IP 地址上显示为虚拟服务。
ipvs 对比 iptables
我们知道kube-proxy支持 iptables 和 ipvs 两种模式, 在kubernetes v1.8 中引入了 ipvs 模式,在 v1.9 中处于 beta 阶段,在 v1.11 中已经正式可用了。iptables 模式在 v1.1 中就添加支持了,从 v1.2 版本开始 iptables 就是 kube-proxy 默认的操作模式,ipvs 和 iptables 都是基于netfilter的,那么 ipvs 模式和 iptables 模式之间有哪些差异呢?
-
ipvs 为大型集群提供了更好的可扩展性和性能
-
ipvs 支持比 iptables 更复杂的复制均衡算法(最小负载、最少连接、加权等等)
-
ipvs 支持服务器健康检查和连接重试等功能
ipvs为负载均衡提供算法:
- rr:轮询带哦都
- lc:最小连接数
- dh:目标地址哈希
- sh:源地址哈希
- sed:最短期望延迟
- nq:不排队调度
注意:ipvs 模式假定在运行 kube-proxy 之前在节点上都已经安装了 IPVS 内核模块。当 kube-proxy 以 ipvs 代理模式启动时,kube-proxy 将验证节点上是否安装了 IPVS 模块,如果未安装,则 kube-proxy 将回退到 iptables 代理模式
client Pod 访问 Server Pod
- 首先,service IP 的 iptables代理变成了 ipvs 模块,实现负载均衡和流量导向。
- client 访问到 IPvs 服务,将流量分发到不同的 Pod 上运行。
kubernetes暴露端口的方式
1:集群内部实现访问:Clusterip
Clusterip是集群内部的私有ip,在集群内部访问服务非常方便,也是kuberentes集群默认的方式,直接通过service的Clusterip访问,也可以直接通过ServiceName访问。集群外部则是无法访问的。
默认类型,自动分配一个仅Cluster内部能够访问的虚拟IP
[root@master ~]# cat cluster.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
spec:
containers:
- image: bravealove1/apache:v1.0
imagePullPolicy: IfNotPresent
name: apache
---
apiVersion: v1
kind: Service
metadata:
name: apache
namespace: default
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: apache
type: ClusterIP #指定网络模式
[root@master ~]# kubectl apply -f cluster.yml
deployment.apps/apache created
service/apache created
[root@master ~]# kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/apache-599bc546b8-xp5rg 1/1 Running 0 12s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/apache ClusterIP 10.98.249.14 <none> 80/TCP 12s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d12h
[root@master ~]# curl 10.98.249.14
hello,this is a test page 1
2:集群外部方式访问:NodePort
NodePort在kubenretes里是一个早期广泛应用的服务暴露方式。Kubernetes中的service默认情况下都是使用的ClusterIP这种类型,这样的service会产生一个ClusterIP,这个IP只能在集群内部访问,要想让外部能够直接访问service,需要将service type修改为 nodePort。将service监听端口映射到node节点。
nodePort的原理在于在node上开了一个端口,将向该端口的流量导入到kube-proxy,然后由 kube-proxy进一步到给对应的pod
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
spec:
containers:
- image: bravealove1/apache:v1.0
imagePullPolicy: IfNotPresent
name: apache
---
apiVersion: v1
kind: Service
metadata:
name: apache
namespace: default
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30000 //指定对外端口
selector:
app: apache
type: NodePort #指定网络模式
[root@master ~]# kubectl apply -f nodeport.yml
deployment.apps/apache created
service/apache created
[root@master ~]# kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/apache-599bc546b8-w6xnd 1/1 Running 0 10s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/apache NodePort 10.96.90.92 <none> 80:30000/TCP 10s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d12h
[root@master ~]# curl 192.168.153.168:30000
hello,this is a test page 1
3: LoadBalancer
LoadBlancer Service 是 kubernetes 深度结合云平台的一个组件;当使用 LoadBlancer Service 暴露服务时,实际上是通过向底层云平台申请创建一个负载均衡器来向外暴露服务;目前 LoadBlancer Service 支持的云平台已经相对完善,比如国外的 GCE、DigitalOcean,国内的 阿里云,私有云 Openstack 等等,由于 LoadBlancer Service 深度结合了云平台,所以只能在一些云平台上来使用.
4: Ingress
Ingress是自kubernetes1.1版本后引入的资源类型。必须要部署Ingress controller才能创建Ingress资源,Ingress controller是以一种插件的形式提供。
DNS解析
Service
- 创建普通service 会以my-svc.my-namespace.svc.cluster.local 的形式指派一个 DNS A 记录,并解析到该service的Cluster IP。
- 创建“Headless” Service(没有Cluster IP)也会以 my-svc.my-namespace.svc.cluster.local 的形式被指派一个 DNS A 记录,但是并不会解析到的Cluster IP,而是解析到一组被选中的pod 的IP,如果没有backend则不做处理。
Pod
- 创建Pod 会以 pod-ip-address.my-namespace.pod.cluster.local 这种形式被指派一个 DNS A 记录。
案例
1、创建一个deployment副本数3,然后滚动更新镜像版本,并记录这个更新记录,最后再回滚到上一个版本
[root@master ~]# vi deploy.yml
[root@master ~]# cat deploy.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
#参数--record可以记录当前版本的Deployment都执行过哪些命令
[root@master ~]# kubectl create -f deploy.yml --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-deployment created
#创建完成后立即执行get命令查看这个Deployment
[root@master ~]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 2m14s
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-74d589986c-kxcvx 1/1 Running 0 2m11s
nginx-deployment-74d589986c-s277p 1/1 Running 0 2m11s
nginx-deployment-74d589986c-zlf8v 1/1 Running 0 2m11s
#使用 nginx:1.9.1 的镜像来代替原来的 nginx的镜像
[root@master ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment.apps/nginx-deployment image updated
#查看更新进度
[root@master ~]# kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
#查看镜像信息
[root@master ~]# kubectl describe deployment/nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Fri, 24 Dec 2021 22:24:10 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 4
kubernetes.io/change-cause: kubectl create --filename=deploy.yml --record=true
Selector: app=nginx
Replicas: 3 desired | 3 updated | 4 total | 3 available | 1 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
#使用rollout history命令查看Deployment的版本(revision)
[root@master ~]# kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 kubectl create --filename=deploy.yml --record=true
2 kubectl create --filename=deploy.yml --record=true
#使用rollout undo命令回滚到前一个revision
[root@master ~]# kubectl rollout undo deployment/nginx-deployment
deployment.apps/nginx-deployment rolled back
查看镜像信息
[root@master ~]# kubectl describe deployment/nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Fri, 24 Dec 2021 22:24:10 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 3
kubernetes.io/change-cause: kubectl create --filename=deploy.yml --record=true
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
2、给一个应用扩容副本数为3
[root@master ~]# kubectl apply -f cluster.yml
deployment.apps/apache created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod/apache-599bc546b8-6tctl 1/1 Running 0 28s
[root@master ~]# kubectl scale deployment apache --replicas 4
deployment.apps/apache scaled
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod/apache-599bc546b8-6gx4p 0/1 ContainerCreating 0 2s
pod/apache-599bc546b8-6tctl 1/1 Running 0 2m3s
pod/apache-599bc546b8-8qzr4 0/1 ContainerCreating 0 2s
pod/apache-599bc546b8-j48v5 0/1 ContainerCreating 0 2s
3、创建一个pod,其中运行着nginx、redis、memcached 3个容器
[root@master ~]# vim pod.yml
---
apiVersion: v1
kind: Pod
metadata:
name: hellok8s
namespace: default
labels:
app: myapp
spec:
containers:
- name: mynginx
image: nginx
ports:
- containerPort: 80
- name: myredis
image: redis
ports:
- containerPort: 6379
- name: memcached
image: memcached
[root@master ~]# kubectl apply -f pod.yml
pod/hellok8s created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hellok8s 3/3 Running 0 2m49s
4、给一个pod创建service,并可以通过ClusterlP/NodePort访问
[root@master ~]# vi nodeport.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: apache
template:
metadata:
labels:
app: apache
spec:
containers:
- image: bravealove1/apache:v1.0
imagePullPolicy: IfNotPresent
name: apache
---
apiVersion: v1
kind: Service
metadata:
name: apache
namespace: default
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30000 //指定对外端口
selector:
app: apache
type: NodePort #指定网络模式
[root@master ~]# kubectl apply -f nodeport.yml
deployment.apps/apache created
service/apache created
[root@master ~]# kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/apache-599bc546b8-w6xnd 1/1 Running 0 10s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/apache NodePort 10.96.90.92 <none> 80:30000/TCP 10s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d12h
[root@master ~]# curl 192.168.153.168:30000
hello,this is a test page 1
5、创建deployment和service,使用busybox容器nslookup解析service
# 创建一个pod,service
[root@master ~]# cat host.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: httpd
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
name: myapp-externalname
namespace: default
spec:
type: ExternalName
externalName: web.test.example.com
# 部署
[root@master ~]# kubectl apply -f host.yml
deployment.apps/myapp-deploy created
service/apache created
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-deploy-55bd85c8b-9jgf2 1/1 Running 0 107s
# 查看pods,service状态
[root@master ~]# kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/myapp-deploy-55bd85c8b-9jgf2 1/1 Running 0 18m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d14h
service/myapp-externalname ExternalName <none> web.test.example.com <none> 3m35s
# 辨析一个busybox资源定义文件
[root@master ~]# vibusybox.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: busybox-pod
name: test-busybox
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
imagePullPolicy: Always
name: test-busybox
# 部署
[root@master ~]# kubectl apply -f busybox.yaml
pod/test-busybox created
# 查看pod状态
[root@master ~]# kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/myapp-deploy-55bd85c8b-9jgf2 1/1 Running 0 19m
pod/test-busybox 1/1 Running 0 8m15s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d14h
service/myapp-externalname ExternalName <none> web.test.example.com <none> 4m49s
# 使用exec -it 与busybox进行交互
[root@master ~]# kubectl exec -it test-busybox /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # nslookup myapp-externalname.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
myapp-externalname.default.svc.cluster.local canonical name = web.test.example.com