kubernetes 网络简介(上)

本贴最后更新于 1625 天前,其中的信息可能已经时移世异

一、Service

1.1 Service 存在的意义:

  • 防止 Pod 失联(服务发现)
  • 定义一组 Pod 的访问策略(负载均衡)

1.2 Pod 与 Service 的关系

  • 通过 label-selector 相关联
  • 通过 Servic 实现 Pod 的负载均衡( TCP/UDP 4 层)

创建 service 的时候必须打标签,并且与创建的 deployment 或者 pod 的标签一致

image.png

通过 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)

image.png

1.3.2 NodePort:对外暴露应用

在每个节点上启用一个端口来暴露服务,可以在集群外部访问。也会分配一个稳定内部集群 IP 地址。访问地址:<NodeIP>:<NodePort>

image.png

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

注意:typeports 同级

[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])作为后端添加进去。

image.png

LB 解决的问题
前面加一个负载均衡器(公网):

  • 把内网节点的端口提供的服务给暴露到公网
  • 为 nodeport 提供高可用能力

工作流程:user -> lb -> node:port -> [service] -> pod

1.4 Service 代理模式

Service 代理模式分为:IptablesIPVS

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>

image.png

1.4.1 iptables 改为 ipvs 步骤:

二进制部署

1.将 /opt/kubernetes/cfg/kube-proxy-config.yaml 配置文件中,注销 mode 参数,并在后面添加 ipvs(下面的一些参数是 ipvs 的调度算法)

image.png

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,保存即可

image.png

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 就是部署 corednsService,所以执行 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

总结:

  1. 采用 NodePort 对外暴露应用,前面加一个 LB 实现统一访问入口

  2. 优先使用 IPVS 代理模式
    IPVS 性能高,调度算法丰富,可以满足多业务大并发的场景下

  3. 集群内应用采用 DNS 名称访问
    当切换集群或者换 ServiceIP 的时候对应于程序没什么影响

  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    110 引用 • 54 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...