一、Service
1.1 Service 存在的意义:
- 防止 Pod 失联(服务发现)
- 定义一组 Pod 的访问策略(负载均衡)
1.2 Pod 与 Service 的关系
- 通过 label-selector 相关联
- 通过 Servic 实现 Pod 的负载均衡( TCP/UDP 4 层)
创建 service 的时候必须打标签,并且与创建的 deployment 或者 pod 的标签一致
通过 kubectl create deployment web --image=nginx --dry-run=client -o yaml > web-dp.yaml
命令导出 deployment 文件:
yaml 示例:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web # deployment的标签
name: web
spec:
replicas: 3
selector: # 标签选择器
matchLabels:
app: web
template:
metadata:
labels:
app: web # Pod的标签
project: blog
spec:
containers:
- image: nginx
name: nginx
resources: {}
注意:
- 标签选择器里的标签是筛选 Pod 用的
- Pod 的标签支持多个
应用:
kubectl apply -f web-dp.yaml
查看标签:
[root@k8s-master k8s]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
web-5b9bff6674-8wjlq 1/1 Running 0 12s app=web,pod-template-hash=5b9bff6674,project=blog
web-5b9bff6674-99ltd 1/1 Running 0 33s app=web,pod-template-hash=5b9bff6674,project=blog
web-5b9bff6674-skgsj 1/1 Running 0 15s app=web,pod-template-hash=5b9bff6674,project=blog
暴露服务:
kubectl expose deployment web --port=80 --target-port=80 --name=web --dry-run=client -o yaml > web-svc.yaml
参数解释:
--port
:k8s 集群内部访问端口--target-port
:容器中服务提供端口,即应用程序端口,如 nginx 提供 80 ,mysql 提供 3306--protocol
:指定协议类型,如 TCP、UDP,SCTP 等--name
:给 svc 起名,一般 svc 的名称与 deployment 一致
yaml 示例:
apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
ports:
- port: 80 # 内部访问端口
protocol: TCP
targetPort: 80 # 容器提供服务的端口
selector:
app: web # 标签与deployment中Pod定义的标签一致
project: blog
应用:
kubectl apply -f web-svc.yaml
查看 svc 标签:
[root@k8s-master k8s]# kubectl get svc --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 47m component=apiserver,provider=kubernetes
web ClusterIP 10.101.205.162 <none> 80/TCP 18m app=web
注意:从中可以发现,web 这个 Service 的标签中并没有 Project=blog 这个标签,这是因为 svc 中 app=web 标签是 svc 本身的,它不是用于去关联 Pod 的,svc 的详细信息可以使用
kubectl get svc -o wide
:
[root@k8s-master k8s]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 47m <none>
web ClusterIP 10.101.205.162 <none> 80/TCP 18m app=web,project=blog
正常来讲,在任何节点机器上 curl 10.101.205.162
是可以访问到的,而且 svc 中的 IP 地址是非常稳定的,只要这个 svc 资源不被删除,这个 IP 就会一直存在。
1.3 Service 三种常用类型
1.3.1 ClusterIP
:集群内部使用
expose 的默认类型,为一组 pod 分配一个稳定的虚拟 IP,作为这组 Pod 提供统一入口,集群之外无法问问,只能在集群内部访问
(同 Namespace 内的 Pod)
1.3.2 NodePort
:对外暴露应用
在每个节点上启用一个端口来暴露服务,可以在集群外部访问。也会分配一个稳定内部集群 IP 地址。访问地址:<NodeIP>:<NodePort>
apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: web
project: blog
type: NodePort
注意:
type
与ports
同级
[root@k8s-master k8s]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 61m
web NodePort 10.101.205.162 <none> 80:31495/TCP 11m
其中,31495
为宿主机端口,每个节点都会监听这个端口,而且这个端口是 kube-proxy
创建的
[root@k8s-master k8s]# netstat -antp | grep 31495
tcp 0 0 0.0.0.0:31495 0.0.0.0:* LISTEN 2581/kube-proxy
当然,这个宿主机端口号也可以固定,写法如下(端口号范围:3000
-32767
):
apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
ports:
- port: 80 # 集群内部端口
protocol: TCP # 协议
targetPort: 80 # 容器端口
nodePort: 30000 # 节点端口
selector: # 标签选择器,关联对应Pod
app: web
project: blog
type: NodePort # 指定类型
[root@k8s-master k8s]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 74m
web NodePort 10.101.205.162 <none> 80:30000/TCP 23m
[root@k8s-master k8s]# netstat -antp | grep 3000
tcp 0 0 0.0.0.0:30000 0.0.0.0:* LISTEN 2581/kube-proxy
补充:
kube-proxy
:实现 Service 的功能,包含服务发现和提供负载均衡的能力。
1.3.3 LoadBalancer
:对外暴露应用,适用公有云
与 NodePort
类似,在每个节点上启用一个端口来暴露服务。除此之外,Kubernetes 会请求底层云平台上的负载均衡器,将每个 Node ([NodeIP]:[NodePort]
)作为后端添加进去。
LB 解决的问题
前面加一个负载均衡器(公网):
- 把内网节点的端口提供的服务给暴露到公网
- 为 nodeport 提供高可用能力
工作流程:user
-> lb
-> node:port
-> [service]
-> pod
1.4 Service 代理模式
Service 代理模式分为:Iptables
和 IPVS
proxy 模式:
iptables
(默认使用):用户态的工具主要用于 netfilter 规则管理
入口流量规则-> 轮训 pod 机制-> 实际 DNAT 规则(目标地址转化)-> 容器ipvs
:
# kubectl edit configmap kube-proxy -n kube-system
mode: "ipvs"
# kubectl delete pod kube-proxy-btz4p -n kube-system
# yum install ipvsadm -y
# ipvsadm -L -n
查看 Service 网络规则
iptables-save | grep <svc-name>
1.4.1 iptables 改为 ipvs 步骤:
二进制部署
1.将 /opt/kubernetes/cfg/kube-proxy-config.yaml
配置文件中,注销 mode
参数,并在后面添加 ipvs
(下面的一些参数是 ipvs
的调度算法)
2.重启 kube-proxy 服务即可生效:
systemctl restart kube-proxy
3.安装 ipvsadm
工具去查看 ipvs 规则:
yum -y install ipvsadm
ipvsadm -L -n
参数解释:
-L
:列出规则-n
:以数字而不是以主机名的形式显示 IP 地址
kubeadm 部署
1.由于 kube-proxy
保存在 k8s 资源中,故需要编辑 configmap
的配置文件
[root@k8s-master k8s]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
kube-proxy-grnpw 1/1 Running 11 12d
kube-proxy-mshjk 1/1 Running 11 12d
kube-proxy-nkkk4 1/1 Running 11 12d
[root@k8s-master k8s]# kubectl get configmaps -n kube-system
NAME DATA AGE
kube-proxy 2 12d
2.打开 kube-peoxy 的 configmap:
kubectl edit configmap kube-proxy -n kube-system
3.找到 mode
参数,在引号中写入 ipvs
,保存即可
4.删除 kube-proxy
的 pod 重建,等待集群自动拉起,ipvs 即可生效:
kubectl delete pod kube-proxy-grnpw -n kube-system
kubectl delete pod kube-proxy-mshjk -n kube-system
kubectl delete pod kube-proxy-nkkk4 -n kube-system
[root@k8s-master k8s]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
kube-proxy-272qn 1/1 Running 0 42s
kube-proxy-gvq2n 1/1 Running 0 33s
kube-proxy-jbjfc 1/1 Running 0 61s
5.利用 ipvsadm -L -n
命令查看规则
1.4.2 Iptables VS IPVS 优缺点
Iptables:
- 灵活,功能强大
- 规则遍历匹配和更新,呈线性时延
IPVS:
- 工作在内核态,有更好的性能
- 调度算法丰富:rr,wrr,lc,wlc,ip hash...
参数解释:
rr
:轮询模式wrr
:加权轮询模式lc
:最小连接模式wlc
:加权连接模式ip hash
:
1.5 Service DNS 名称
[root@k8s-master k8s]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-7ff77c879f-cgfjw 1/1 Running 11 12d
coredns-7ff77c879f-pn8qk 1/1 Running 12 12d
DNS 服务监视 Kubernetes API,为每一个 Service 创建 DNS 记录用于域名解析。
测试 coredns
,使用 busybox:1.28.4
镜像并进入 pod 中
[root@k8s-master k8s]# kubectl run -it --rm --image=busybox:1.28.4 sh
If you don't see a command prompt, try pressing enter.
/ #
正常来讲,在这个 busybox 的 Pod 中是 ping 的通上面创建的 svc 的 IP 地址的,也可以访问 svc 的页面。
/ # ping 10.101.205.162
PING 10.101.205.162 (10.101.205.162): 56 data bytes
64 bytes from 10.101.205.162: seq=0 ttl=64 time=0.101 ms
64 bytes from 10.101.205.162: seq=1 ttl=64 time=0.099 ms
64 bytes from 10.101.205.162: seq=2 ttl=64 time=0.101 ms
64 bytes from 10.101.205.162: seq=3 ttl=64 time=0.107 ms
^C
--- 10.101.205.162 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.099/0.102/0.107 ms
/ # wget 10.101.205.162
Connecting to 10.101.205.162 (10.101.205.162:80)
index.html 100% |******************************************************************************************| 612 0:00:00 ETA
/ #
可以使用 nslookup
命令解析 dns 名称
/ # nslookup web
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: web
Address 1: 10.101.205.162 web.default.svc.cluster.local
解析出来的 IP 对应 svc 的 IP 地址
注意:
nslookup
默认不能跨命名空间使用,若想解析跨命名空间的 dns,则需要使用全格式
ClusterIP A 记录格式:<service-name>.<namespace-name>.svc.cluster.local
示例:my-svc.my-namespace.svc.cluster.local
Pod 在发送 dns 请求的时候,实际上是请求的 /etc/resolv.conf
文件中的 dns,这个 dns 就是部署 coredns
的 Service
,所以执行 nslookup
命令时是向 coredns 发出请求,而 coredns
里面有 Service 对应 IP 的记录,之后响应记录结果,并且 coredns
对域名也有区分,若判断为外部域名,则走上层宿主机 dns 进行解析,然后再响应给 Pod。
总结:
pod -> coredns service(10.0.0.2) -> coredns(service/clusterip 记录) -> 响应 A 记录结果
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
[root@k8s-node1 manifests]# 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 12d
总结:
-
采用 NodePort 对外暴露应用,前面加一个 LB 实现统一访问入口
-
优先使用 IPVS 代理模式
IPVS 性能高,调度算法丰富,可以满足多业务大并发的场景下 -
集群内应用采用 DNS 名称访问
当切换集群或者换 ServiceIP 的时候对应于程序没什么影响
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于