05-k8s网络

05-k8s网络

Kubernetes本身并不负责网络通信,Kubernetes提供了容器网络接口CNI(Container Network Interface),具体的网络通信交给CNI插件来负责,开源的CNI插件非常多,像Flannel、Calico。

Kubernetes虽然不负责网络,但要求集群中的Pod能够互相通信,且Pod必须通过非NAT网络连接,即收到的数据包的源IP就是发送数据包Pod的IP。

同时Pod与节点之间的通信也是通过非NAT网络。

但是Pod访问集群外部时源IP会被修改成节点的IP。

pod内部通过虚拟Ethernet接口与外界pod外部链接

Veth pair就像一根网线,一端留在Pod内部,一端在Pod之外。

而同一个节点上的Pod通过网桥(Linux Bridge)通信,如下图所示

创建pod,查看网络情况

复制代码
[root@k8s-master-10 ~]#cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx-1

spec:
  nodeName: k8s-node-12
  containers:
  - image: nginx:alpine
    name: container-1

创建结果

复制代码
[root@k8s-master-10 ~]#kubectl create -f pod1.yml 
pod/my-nginx-1 created

[root@k8s-master-10 ~]#kubectl get po -owide
NAME         READY   STATUS    RESTARTS   AGE   IP          NODE          NOMINATED NODE   READINESS GATES
my-nginx-1   1/1     Running   0          8s    10.2.2.48   k8s-node-12   <none>           <none>
[root@k8s-master-10 ~]#

同一个节点 下的2个pod

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx-2

spec:
  nodeName: k8s-node-12
  containers:
  - image: nginx:alpine
    name: container-2

创建结果

复制代码
[root@k8s-master-10 ~]#kubectl create -f pod2.yml 
pod/my-nginx-2 created


[root@k8s-master-10 ~]#kubectl get po -owide
NAME         READY   STATUS    RESTARTS   AGE   IP          NODE          NOMINATED NODE   READINESS GATES
my-nginx-1   1/1     Running   0          83s   10.2.2.48   k8s-node-12   <none>           <none>
my-nginx-2   1/1     Running   0          10s   10.2.2.49   k8s-node-12   <none>           <none>

同一个节点下pod通信

pod > cni > ens33

跨主机pod通信原理

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx-13

spec:
  nodeName: k8s-node-11
  containers:
  - image: nginx:alpine
    name: container-3

创建,查看新节点的pod

复制代码
[root@k8s-master-10 ~]#kubectl create -f pod-3.yml 
pod/my-nginx-13 created

1.service

复制代码
查看官网解释service资源
[root@k8s-master-10 ~]#kubectl explain service

Pod创建完成后,如何访问Pod呢?直接访问Pod会有如下几个问题:

  • Pod会随时被Deployment这样的控制器删除重建,那访问Pod的结果就会变得不可预知。(ip动态)
  • Pod的IP地址是在Pod启动后才被分配,在启动前并不知道Pod的IP地址。
  • 应用往往都是由多个运行相同镜像的一组Pod组成,逐个访问Pod也变得不现实。(负载均衡入口)

举个例子,假设有这样一个应用程序,使用Deployment创建了前台和后台,前台会调用后台做一些计算处理

后台运行了3个Pod,这些Pod是相互独立且可被替换的,当Pod出现状况被重建时,新建的Pod的IP地址是新IP

前台的Pod无法直接感知。

Service解决Pod访问问题

Kubernetes中的Service对象就是用来解决上述Pod访问问题的。

Service有一个固定IP地址,Service将访问它的流量转发给Pod,具体转发给哪些Pod通过Label来选择,而且Service可以给这些Pod做负载均衡。

那么对于上面的例子,为后台添加一个Service,通过Service来访问Pod,这样前台Pod就无需感知后台Pod的变化。

创建3个后端pod

复制代码
# 基于deployment创建3个pod,加上标签。
apiVersion: apps/v1      
kind: Deployment         
metadata:
  name: nginx
  namespace: yuchaoit       
spec:
  replicas: 3                    
  selector:              
    matchLabels:
      app: nginx
  template:              
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:latest
        name: container-0
        resources:
          limits:
            cpu: 100m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi

创建查看pod

复制代码
[root@k8s-master-10 ~]#kubectl create -f deploy-nginx.yml 
deployment.apps/nginx created

[root@k8s-master-10 ~]#kubectl -n yuchaoit get po -owide
NAME                     READY   STATUS    RESTARTS   AGE   IP          NODE          NOMINATED NODE   READINESS GATES
nginx-56d58c56c7-99dfv   1/1     Running   0          37s   10.2.1.37   k8s-node-11   <none>           <none>
nginx-56d58c56c7-frwxx   1/1     Running   0          37s   10.2.2.50   k8s-node-12   <none>           <none>
nginx-56d58c56c7-j2xn2   1/1     Running   0          37s   10.2.1.36   k8s-node-11   <none>           <none>
[root@k8s-master-10 ~]#

三种Service类型IP

https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/

复制代码
[root@k8s-master-10 ~]#kubectl explain Service.spec.type

NodeIP  节点对外提供访问的IP
ClusterIP 用来提供动态发现、负载均衡Pod的IP
PodIP  提供Pod使用的IP

创建ClusterIP

下面示例创建一个名为"nginx"的Service,通过selector选择到标签"app:nginx"的Pod,目标Pod的端口为80,Service对外暴露的端口为8080。

访问服务只需要通过"服务名称:对外暴露的端口"接口,对应本例即"nginx:8080"。

这样,在其他Pod中,只需要通过"nginx:8080"就可以访问到"nginx"关联的Pod。

yaml

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx        # Service的名称
  namespace: yuchaoit
spec:
  selector:          # Label Selector,选择包含app=nginx标签的Pod
    app: nginx
  ports:
  - name: service0
    targetPort: 80   # Pod的端口
    port: 80         # Service对外暴露的端口,也就是ClusterIP的port
    protocol: TCP    # 转发协议类型,支持TCP和UDP
  type: ClusterIP    # Service的类型

查看svc

复制代码
[root@k8s-master-10 ~]#kubectl create -f svc-nginx.yml 
service/nginx created


[root@k8s-master-10 ~]#kubectl -n yuchaoit get svc -owide
NAME    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
nginx   ClusterIP   10.1.15.2    <none>        80/TCP    5s    app=nginx

可以看到Service有个Cluster IP,这个IP是固定不变的,除非Service被删除,所以也可以使用ClusterIP在集群内部访问Service。

此时ClusterIP也只是集群内的IP,只能在集群内访问到后端Pod。

服务发现原理

复制代码
1.k8s内置DNS插件CoreDNS,实现在k8s集群中以DNS服务提供名称解析
2.CoreDNS为每一个pod提供Service的名称解析服务
- 可以查看每一个pod的dns解析文件,查看nameserver,其实就是CoreDNS自己的ClusterIp
- 是因为安装了CoreDNS集群后,在kubelet配置文件里,默认添加了参数
[root@k8s-master-10 ~]#cat /var/lib/kubelet/config.yaml 
clusterDNS:
- 10.1.0.10
clusterDomain: cluster.local

因此每一个pod在被创建时,默认使用该DNS服务器。

在每一个pod中都可以基于如下规则,访问Service

servicename.default.svc.cluster.local
servicename.namespace
servicename

使用ServiceName访问Service

k8s集群内默认提供了DNS解析插件coredns

在kube-system命名空间下可以查看到CoreDNS的Pod。

复制代码
[root@k8s-master-10 ~]#kubectl get po -n kube-system --show-labels  -l k8s-app=kube-dns
NAME                       READY   STATUS    RESTARTS   AGE    LABELS
coredns-6d56c8448f-g6m9z   1/1     Running   1          7d6h   k8s-app=kube-dns,pod-template-hash=6d56c8448f
coredns-6d56c8448f-ws7rb   1/1     Running   1          7d6h   k8s-app=kube-dns,pod-template-hash=6d56c8448f

CoreDNS安装成功后会成为DNS服务器,当创建Service后,CoreDNS会将Service的名称与IP记录起来,这样Pod就可以通过向CoreDNS查询Service的名称获得Service的IP地址。

访问时通过nginx...svc.cluster.local访问,其中nginx为Service的名称,为命名空间名称,svc.cluster.local为域名后缀

在实际使用中,在同一个命名空间下可以省略.svc.cluster.local,直接使用ServiceName即可。

例如上面创建的名为nginx的Service,直接通过**"nginx:80"**就可以访问到Service,进而访问后台Pod。

复制代码
使用ServiceName的方式有个主要的优点就是可以在开发应用程序时可以将ServiceName写在程序中,这样无需感知具体Service的IP地址。

下面创建一个Pod并进入容器,查询nginx域名的地址,可以发现是解析出nginx这个Service的IP地址10.1.15.2

同时访问Service的域名,可以看到能直接返回内容。

复制代码
# 注意pod创建,添加namespace
[root@k8s-master-10 ~]#kubectl run -n yuchaoit -i --tty --image tutum/dnsutils dnsutils --restart=Never --rm /bin/sh

图解service和pod关系

Service的类型与使用场景

Service的类型除了ClusterIP还有NodePort、LoadBalancer和None,这几种类型的Service有着不同的用途。

  • ClusterIP:用于在集群内部互相访问的场景,通过ClusterIP访问Service。
  • NodePort:用于从集群外部访问的场景,通过节点上的端口访问Service,详细介绍请参见NodePort类型的Service
  • LoadBalancer:用于从集群外部访问的场景,其实是NodePort的扩展,通过一个特定的LoadBalancer访问Service,这个LoadBalancer将请求转发到节点的NodePort,而外部只需要访问LoadBalancer,详细介绍请参见LoadBalancer类型的Service
  • None:用于Pod间的互相发现,这种类型的Service又叫Headless Service,详细介绍请参见Headless Service

下面是一个创建NodePort类型的Service。创建完成后,可以通过节点的IP:Port访问到后台Pod。

yaml

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nodeport-service
  namespace: yuchaoit
spec:
  type: NodePort
  ports:
  - port: 8080        # service的8080端口
    targetPort: 80    # 目标pod的端口
    nodePort: 30120   # 宿主机暴露的端口
    name: service1  # 若不指定,svc里显示unset
  selector:           # 标签选择器
    app: nginx

创建nodePort类型SVC

复制代码
[root@k8s-master-10 ~]#kubectl create -f nodeport.yml 
service/nodeport-service created


[root@k8s-master-10 ~]#kubectl -n yuchaoit get svc -owide
NAME               TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE   SELECTOR
nginx              ClusterIP   10.1.15.2     <none>        80/TCP           85m   app=nginx
nodeport-service   NodePort    10.1.134.13   <none>        8080:30120/TCP   24s   app=nginx

[root@k8s-master-10 ~]#netstat -tunlp |grep 30120
tcp        0      0 0.0.0.0:30120           0.0.0.0:*               LISTEN      11849/kube-proxy    

# 集群里每一个节点都会暴露30120端口。

访问NodePort类型SVC

先修改pod的nginx页面,容易对比效果

复制代码
[root@k8s-master-10 ~]#kubectl -n yuchaoit get po
NAME                     READY   STATUS    RESTARTS   AGE
nginx-56d58c56c7-frwxx   1/1     Running   0          104m
nginx-56d58c56c7-pndzz   1/1     Running   0          56m
[root@k8s-master-10 ~]#
[root@k8s-master-10 ~]#kubectl -n yuchaoit exec -it nginx-56d58c56c7-frwxx -- bash -c 'echo frwxx > /usr/share/nginx/html/index.html'
[root@k8s-master-10 ~]#kubectl -n yuchaoit exec -it nginx-56d58c56c7-pndzz -- bash -c 'echo pndzz > /usr/share/nginx/html/index.html'
[root@k8s-master-10 ~]#

2.Ingress资源

https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。

Ingress 可以提供负载均衡、为什么会出现Ingress

复制代码
将应用部署在k8s中,也就是pod后,如何在集群内访问、以及如何在集群外访问,才是核心目的,毕竟还是以web项目为主。

1.在没有ingress(入口)之前,pod对集群外提供访问,只能通过NodePort方式,也就是端口映射方式,但是这个缺点很明显,一个Node上的port有限,并且不能重复使用。

podA占用了80端口,其他服务就无法使用了。

2.我们上一节使用的Service且NodePort类型,是基于四层的代理转发,基于TCP、UDP协议转发。
缺点已经说过了,就像早期于超老师带着大家学nginx一样,Nginx也是支持基于四层、七层的代理转发的。
很明显,基于七层的http转发,粒度更细,能直接基于域名区分请求。
基于七层的http、https协议转发,以及通过域名、路径的转发,能实现更细粒度的请求划分,并且解决端口问题。

【思考下多端口虚拟主机、多域名虚拟主机,是不是区别很大?】

3.为了解决这个问题,Ingress控制器资源出现了,作用就是实现七层的协议转发,通过域名、路径的匹配转发,提供k8s集群的访问入口。

4.有同学想,既然nginx可以实现传统的七层代理转发,为什么还要有ingress?
毕竟你要注意你的应用,以pod形式在k8s环境内运行,外部的nginx无法动态发现k8s创建的资源。

5.k8s下的ingress控制器,具体实现的产品,有ingress-nginx、traefik。

SSL 终结和基于名称的虚拟托管。

图解Ingress工作原理

创建基于域名的Ingress

https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/#path-types

先安装ingress-nginx,使用k8s权威指南第五版的ingress.yaml即可。

复制代码
https://github.com/kubeguide/K8sDefinitiveGuide-V5-Sourcecode/blob/main/Chapter04/4.6.1%20ingress.yaml

创建Ingress-nginx控制器

复制代码
[root@k8s-master-10 ~/ingress]#kubectl apply -f ingress.yml 
namespace/nginx-ingress created
serviceaccount/nginx-ingress created
clusterrole.rbac.authorization.k8s.io/nginx-ingress created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress created
secret/default-server-secret created
configmap/nginx-config created
deployment.apps/nginx-ingress created


# [root@k8s-master-10 ~/ingress]#kubectl -n nginx-ingress describe po nginx-ingress-75c88594dc-klfqc 

# 发现需要有node-Selector
# 给节点打上标签
[root@k8s-master-10 ~/ingress]#kubectl label nodes k8s-master-10 role=ingress-nginx-controller

# 又发现,k8s-master默认有污点,pod不能容忍,因此没法部署。

设置master也参与pod调度

复制代码
[root@k8s-master-10 ~/ingress]#kubectl describe nodes k8s-master-10  |grep Taint
Taints:             node-role.kubernetes.io/master:NoSchedule

# 去掉污点
kubectl taint node k8s-master-10 node-role.kubernetes.io/master:NoSchedule-

验证ingress-nginx控制正常

复制代码
[root@k8s-master-10 ~/ingress]#kubectl -n nginx-ingress get po -owide
NAME                             READY   STATUS    RESTARTS   AGE     IP         NODE            NOMINATED NODE   READINESS GATES
nginx-ingress-75c88594dc-klfqc   1/1     Running   0          7m47s   10.2.0.6   k8s-master-10   <none>           <none>

[root@k8s-master-10 ~/ingress]#kubectl -n nginx-ingress get all -owide
NAME                                 READY   STATUS    RESTARTS   AGE     IP         NODE            NOMINATED NODE   READINESS GATES
pod/nginx-ingress-75c88594dc-klfqc   1/1     Running   0          8m33s   10.2.0.6   k8s-master-10   <none>           <none>

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS      IMAGES                      SELECTOR
deployment.apps/nginx-ingress   1/1     1            1           8m33s   nginx-ingress   nginx/nginx-ingress:1.7.2   app=nginx-ingress

NAME                                       DESIRED   CURRENT   READY   AGE     CONTAINERS      IMAGES                      SELECTOR
replicaset.apps/nginx-ingress-75c88594dc   1         1         1       8m33s   nginx-ingress   nginx/nginx-ingress:1.7.2   app=nginx-ingress,pod-template-hash=75c88594dc


# 其实就是运行了一个nginx在pod里

创建ingree规则

复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
  namespace: yuchaoit
spec:
  rules:                                 # 转发规则
  - host: "www.yuchaoit.cn"            # 填入你们的业务域名
    http:                                # 基于http协议解析
      paths:                            # 基于url路径匹配
      - pathType: Prefix                 #要设置路径类型,否则不合法,
        path: "/"                     # 以 / 分割的URL路径前缀匹配,区分大小写,这里表默认所有路径。
        backend:                      # 后端Service信息的组合
          service:                    
            name: service1    # 代理到名字是service1的ClusterIP
            port:                    # 代理到的Service的端口号。
              number: 80

创建,查看ingress

复制代码
[root@k8s-master-10 ~/ingress]#kubectl create -f my-ingress.yml 
ingress.networking.k8s.io/test-ingress created


[root@k8s-master-10 ~/ingress]#kubectl -n yuchaoit get ingress -o wide
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
NAME           CLASS    HOSTS             ADDRESS   PORTS   AGE
test-ingress   <none>   www.yuchaoit.cn             80      10s

# 查看ingress是否正确
[root@k8s-master-10 ~/ingress]#kubectl -n yuchaoit describe ingress test-ingress

创建ingress后端的service、pod

创建deployment,运行应用。

yaml 复制代码
apiVersion: apps/v1      # 注意这里与Pod的区别,Deployment是apps/v1而不是v1
kind: Deployment         # 资源类型为Deployment
metadata:
  name: nginx-deployment            # Deployment的名称
  namespace: yuchaoit
spec:
  replicas: 2            # Pod的数量,Deployment会确保一直有2个Pod运行         
  selector:              # Label Selector
    matchLabels:
      app: nginx
  template:              # Pod的定义,用于创建Pod,也称为Pod template
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.0
        name: nginx-containers
        imagePullPolicy: IfNotPresent
        ports:
          - name: http
            containerPort: 80 # 指明容器内要暴露的端口
        resources:
          limits:
            cpu: 100m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi

创建svc

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: service1        # Service的名称
  namespace: yuchaoit
spec:
  selector:          # Label Selector,选择包含app=nginx标签的Pod
    app: nginx
  ports:
  - name: service0
    targetPort: 80   # Pod的端口
    port: 80         # Service对外暴露的端口,也就是ClusterIP的port
    protocol: TCP    # 转发协议类型,支持TCP和UDP
  type: ClusterIP    # Service的类型

创建结果

复制代码
[root@k8s-master-10 ~/ingress]#kubectl -n yuchaoit get svc service1 -owide
NAME       TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service1   ClusterIP   10.1.56.133   <none>        80/TCP    15s   app=nginx

访问七层负载均衡的k8s集群

修改pod首页,查看效果。

复制代码
kubectl -n yuchaoit exec -it nginx-deployment-6f7886b6db-xsj8n -- bash -c 'echo "<meta charset=utf8>于超老师带你学k8s,222" > /usr/share/nginx/html/index.html'

kubectl -n yuchaoit exec -it nginx-deployment-6f7886b6db-pvjd5 -- bash -c 'echo "<meta charset=utf8>于超老师带你学k8s,
相关推荐
lichenyang4531 天前
Docker 学习笔记(四):Dockerfile,把项目打成自己的镜像
docker·容器
lichenyang4531 天前
Docker 学习笔记(三):Docker 网络、bridge、子网和容器互通
docker·容器
lichenyang4531 天前
Docker 学习笔记(二):docker run 的参数到底在控制什么?
docker·容器
运维开发故事4 天前
基于 Arthas 的多集群在线诊断系统设计与实现
kubernetes
Patrick_Wilson6 天前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
探索云原生6 天前
K8s 1.36 这个 GA 特性,把 initContainer 拉模型的 hack 干掉了
ai·云原生·kubernetes
云恒要逆袭7 天前
运行你的第一个Docker容器
后端·docker·容器
Java之美8 天前
一次k8s升级引发的DevicePlugin注册失败
云原生·kubernetes
程序员老赵8 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程