# Kubernetes网络

# Service

# Service存在的意义

service

  • 防止Pod失联(服务发现)

    解决Pod的IP不固定的问题,添加一个控制器负责动态获取列表IP,动态更新到负载均衡器配置

  • 定义一组Pod的访问策略(负载均衡)

    Pod是多副本的,在前面添加一个负载均衡器,通过负载均衡器统一访问Pod

引入service是为了解决以上两个问题

# Pod与Service的关系

pod-service

  • 通过label-selector相关联
  • 通过Service实现Pod的负载均衡( TCP/UDP 4层)
  • 下面通过一个简单的例子解释相关参数

# 一个基本的yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: web
  name: web
spec:
  type: NodePort    # 暴露类型
  ports:
  - port: 81        # service(负载均衡器)端口,只能在k8s集群内部访问(node和pod)
    protocol: TCP   # 协议
    targetPort: 80  # 容器中服务的端口
    nodePort: 30002 # 每个节点可以访问的端口
  selector:         # 标签选择器,这里用来关联Pod, 这里是指关联打有标签app=web的pod
    app: web

# 一个nginx的配置

upstream web {
	server 127.0.0.1:80;		 # 当前port对应targetPort
}
server {
	listen 127.0.0.1:81;         # 当前port对应port
	server_name blog.lxlit.cn; 
	location / {
		proxy_pass http://web;
	}
}

# 负载均衡器四层和七层怎么理解?

  • 四层和七层都是指OSI七层模型。
  • 四层传输层,基于IP+端口转发
  • 七层应用层,基于应用层协议转发,例如HTTP(域名、Cookie、Request)

# Service三种类型

  • ClusterIP

    service

    • 集群内部使用,类似于公有云的内网ip,用于内部访问,内部通信

    • 默认,分配一个稳定的IP地址,即VIP,只能在集群内部访问(同Namespace内的Pod)

    • 一个简单的yaml

      apiVersion: v1
      kind: Service
      metadata:
        labels:
          app: web
        name: web-cluster-ip
      spec:
        type: ClusterIP
        ports:
        - port: 80
          protocol: TCP
          targetPort: 80
        selector:
          app: web
      
    • 执行效果

      $ kubectl get svc
      NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
      web-cluster-ip   ClusterIP   10.96.112.69    <none>        80/TCP         118s
      
    • 说明

      只能在集群内部通过80端口访问

  • NodePort nodeport

    • 对外暴露应用,超出集群内部范围,让浏览器访问

    • 一个简单的yaml

      apiVersion: v1
      kind: Service
      metadata:
        labels:
          app: web
        name: web
      spec:
        type: NodePort
        ports:
        - port: 81
          protocol: TCP
          targetPort: 80
          nodePort: 30002
        selector:
          app: web
      
    • 执行效果

      $ kubectl get svc
      NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
      web              NodePort    10.97.108.187   <none>        81:30002/TCP   5m27s
      
    • 说明

      可以在集群内部通过81端口访问,还能通过宿主机的30002端口,通过浏览器访问

      nodePort范围:30000-32767

  • LoadBalancer loadbalancer

    • 对外暴露应用,对接对外提供api的负载均衡器产品,一般适用于指公有云。

    • 与NodePort类似,在每个节点上启用一个端口来暴露服务。除此之外,Kubernetes会请求底层云平台上的负载均衡器, 将每个Node([NodeIP]:[NodePort])作为后端添加进去。

# Service代理模式

# userspace(已弃用)

# Iptables(默认)

iptables

# 简介

Netfileter/iptables (以下简称iptables)是nuix/linux 系统自带的优秀且完全免费的基于包过滤的防火墙工具、它的功能十分强大、使用非常灵活、可以对流入、流出及流经服务器的数据包进行精细的控制。特别是它可以在一台非常低配置下跑的非常好。提供400台机器的办公上网共享服务丝毫不逊色数万RMB企业级专业路由器防火墙

Iptables 是linux2.4及2.6内核中集成的服务、其功能与安全性比老一辈ipvfwadm、ipchanins强大的多、一般认为iptables工作在OSI七层的、二、三层、四层。

# 查看负载均衡规则
iptables-save |grep <SERVICE-NAME>
# 访问流程

客户端 ->NodePort/ClusterIP(iptables/Ipvs负载均衡规则) -> 分布在各节点Pod

# 访问service的两种方式
  • 通过浏览器访问(集群之外),通过nodeport方式

    相比第二种方式多加了一条规则,为了接收nodePort的流量

    -A KUBE-NODEPORTS -p tcp -m comment --comment "default/web" -m tcp --dport 30002 -j KUBE-SVC-LOLE4ISW44XBNF3G
    
  • 在pod中访问service或者node curl

    第一步

    $ kubectl get svc
    NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
    web              NodePort    10.97.108.187   <none>        81:30002/TCP   24h
    
    $ iptables-save |grep web|grep 10.97.108.187
    -A KUBE-SERVICES -d 10.97.108.187/32 -p tcp -m comment --comment "default/web cluster IP" -m tcp --dport 81 -j KUBE-SVC-LOLE4ISW44XBNF3G
    

    第二步

    $ iptables-save |grep web|grep KUBE-SVC-LOLE4ISW44XBNF3G
    -A KUBE-SVC-LOLE4ISW44XBNF3G -m comment --comment "default/web" -j KUBE-SEP-JJQU2ZJFW4XFAEGZ
    

    第三步

    $ iptables-save |grep web|grep KUBE-SEP-JJQU2ZJFW4XFAEGZ
    -A KUBE-SEP-JJQU2ZJFW4XFAEGZ -p tcp -m comment --comment "default/web" -m tcp -j DNAT --to-destination 10.244.17.177:80
    

# IPVS(lvs)

ipvs

# 简介

PVS基本上是一种高效的Layer-4交换机,它提供负载平衡 (opens new window)的功能。当一个TCP连接的初始SYN报文到达时,IPVS就选择一台服务器,将报文转发给它。此后通过查发报文的IP和TCP报文头地址,保证此连接的后继报文被转发到相同的服务器。这样,IPVS不用检查到请求的内容再选择服务器,这就要求后端的服务器组是提供相同的服务,不管请求被送到哪一台服务器,返回结果都应该是一样的。但是在有一些应用中后端的服务器可能功能不一,有的是提供HTML文档的Web服务器,有的是提供图片的Web服务器,有的是提供CGI的Web服务器。这时,就需要基于内容请求分发 (Content-Based Request Distribution),同时基于内容请求分发可以提高后端服务器上访问的局部性。

# 查看负载均衡规则
ipvsadm -L -n
# 修改Iptables为IPVS
# kubeadm
$ kubectl edit configmap kube-proxy -n kube-system 
...
mode: “ipvs“
...

$ kubectl delete pod kube-proxy-btz4p -n kube-system 
注:
1、kube-proxy配置文件以configmap方式存储 
2、如果让所有节点生效,需要重建所有节点kube-proxy pod

$ kubectl logs -f kube-proxy-22qgg -n kube-system
I0518 14:50:44.020917       1 node.go:172] Successfully retrieved node IP: 10.69.1.161
I0518 14:50:44.021061       1 server_others.go:140] Detected node IP 10.69.1.161
I0518 14:50:44.061198       1 server_others.go:206] kube-proxy running in dual-stack mode, IPv4-primary
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
I0518 14:50:44.061354       1 server_others.go:274] Using ipvs Proxier.
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
I0518 14:50:44.061383       1 server_others.go:276] creating dualStackProxier for ipvs.
W0518 14:50:44.061436       1 server_others.go:506] detect-local-mode set to ClusterCIDR, but no IPv6 cluster CIDR defined, , defaulting to no-op detect-local for IPv6
E0518 14:50:44.062427       1 proxier.go:389] can't set sysctl net/ipv4/vs/conn_reuse_mode, kernel version must be at least 4.1
W0518 14:50:44.062802       1 proxier.go:445] IPVS scheduler not specified, use rr by default
E0518 14:50:44.063134       1 proxier.go:389] can't set sysctl net/ipv4/vs/conn_reuse_mode, kernel version must be at least 4.1
W0518 14:50:44.063299       1 proxier.go:445] IPVS scheduler not specified, use rr by default
W0518 14:50:44.063374       1 ipset.go:113] ipset name truncated; [KUBE-6-LOAD-BALANCER-SOURCE-CIDR] -> [KUBE-6-LOAD-BALANCER-SOURCE-CID]
W0518 14:50:44.063404       1 ipset.go:113] ipset name truncated; [KUBE-6-NODE-PORT-LOCAL-SCTP-HASH] -> [KUBE-6-NODE-PORT-LOCAL-SCTP-HAS]
I0518 14:50:44.063778       1 server.go:643] Version: v1.21.0
I0518 14:50:44.072629       1 conntrack.go:52] Setting nf_conntrack_max to 131072
I0518 14:50:44.073324       1 config.go:315] Starting service config controller
I0518 14:50:44.073389       1 shared_informer.go:240] Waiting for caches to sync for service config
I0518 14:50:44.073418       1 config.go:224] Starting endpoint slice config controller
I0518 14:50:44.073447       1 shared_informer.go:240] Waiting for caches to sync for endpoint slice config
W0518 14:50:44.078839       1 warnings.go:70] discovery.k8s.io/v1beta1 EndpointSlice is deprecated in v1.21+, unavailable in v1.25+; use discovery.k8s.io/v1 EndpointSlice
W0518 14:50:44.082284       1 warnings.go:70] discovery.k8s.io/v1beta1 EndpointSlice is deprecated in v1.21+, unavailable in v1.25+; use discovery.k8s.io/v1 EndpointSlice
I0518 14:50:44.173907       1 shared_informer.go:247] Caches are synced for service config
I0518 14:50:44.174456       1 shared_informer.go:247] Caches are synced for endpoint slice config

从日志中标注出来的那部分,就可以看到已经修改成功了

# 二进制
$ vi kube-proxy-config.yml 
mode: ipvs
ipvs:
scheduler: "rr“
$ systemctl restart kube-proxy
注:配置文件路径根据实际安装目录为准
# 执行命令查看规则
$ yum install ipvsadm       # 已安装的忽略该命令
$ ipvsadm -L -n							# 找不到ipvsadm这个命令的话,使用上一行的命令进行安装
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.17.0.1:30001 rr
  -> 10.244.10.200:8443           Masq    1      0          0
TCP  10.69.1.160:30001 rr
  -> 10.244.10.200:8443           Masq    1      0          0
TCP  10.69.1.160:30002 rr
  -> 10.244.17.177:80             Masq    1      0          0
TCP  10.96.0.1:443 rr
  -> 10.69.1.160:6443             Masq    1      0          0
TCP  10.96.0.10:53 rr
  -> 10.244.10.196:53             Masq    1      0          0
  -> 10.244.10.199:53             Masq    1      0          0
TCP  10.96.0.10:9153 rr
  -> 10.244.10.196:9153           Masq    1      0          0
  -> 10.244.10.199:9153           Masq    1      0          0
TCP  10.96.112.69:80 rr
  -> 10.244.17.177:80             Masq    1      0          0
TCP  10.97.108.187:81 rr
  -> 10.244.17.177:80             Masq    1      0          0
TCP  10.102.124.135:8000 rr
  -> 10.244.10.198:8000           Masq    1      0          0
TCP  10.109.104.52:443 rr
  -> 10.244.10.200:8443           Masq    1      0          0
TCP  10.244.10.192:30001 rr
  -> 10.244.10.200:8443           Masq    1      0          0
TCP  10.244.10.192:30002 rr
  -> 10.244.17.177:80             Masq    1      0          0
TCP  127.0.0.1:30001 rr
  -> 10.244.10.200:8443           Masq    1      0          0
TCP  127.0.0.1:30002 rr
  -> 10.244.17.177:80             Masq    1      0          0
TCP  172.17.0.1:30002 rr
  -> 10.244.17.177:80             Masq    1      0          0
UDP  10.96.0.10:53 rr
  -> 10.244.10.196:53             Masq    1      0          0
  -> 10.244.10.199:53             Masq    1      0          0
# 访问流程

curl service ip -> kube-ipvs0(virtual server) -> pod(real server)

# IPVS和Iptables的比较

# Iptables
  • 灵活,功能强大
  • 规则遍历匹配和更新,呈线性时延
# IPVS
  • 工作在内核态,有更好的性能
  • 调度算法丰富:rr(轮询),wrr(加强轮询),lc(最小连接),wlc(加强最小连接),ip hash(HASH)...

# ServiceDNS名称

# CoreDNS

是一个DNS服务器,Kubernetes默认采用,以Pod部署在集群中,CoreDNS服务监视Kubernetes API,为每一个Service创建DNS记录用于域名解析(解析名称和对应关系)。

CoreDNS YAML文件 (opens new window)(了解)

# ClusterIP A记录格式

<service-name>.<namespace-name>.svc.cluster.local
示例:my-svc.my-namespace.svc.cluster.local

# Ingress

# Ingress为弥补NodePort不足而生

# NodePort存在的不足
  • 一个端口只能一个服务使用,端口需提前规划
  • 只支持4层负载均衡

# Ingress是什么

iptables

  • Ingress公开了从集群外部到集群内服务的HTTP和HTTPS路由的规则集合,而具体实现流量路由则是由Ingress Controller负责。
  • ingress:K8s中的一个抽象资源,给管理员 提供一个暴露应用的入口定义方法
  • Ingress Controller:根据Ingress生成具体 的路由规则,并对Pod负载均衡器

# IngressController是什么

iptables

Ingress管理的负载均衡器,为集群提供全局的负载均衡能力。

# 使用流程
  • 部署Ingress Controller
  • 创建Ingress规则

Ingress Controller有很多实现,我们这里采用官方维护的Nginx控制器。

项目地址 (opens new window)

  • 下载YAML文件
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml(课件中名称是ingress-controller.yaml)
  • YAML文件

    文件有点长,不过因为网络问题下载不下来的话,可以拷贝以下文件

apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
kind: ConfigMap
apiVersion: v1
metadata:
  name: tcp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
kind: ConfigMap
apiVersion: v1
metadata:
  name: udp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress-serviceaccount
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: nginx-ingress-clusterrole
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - "extensions"
      - "networking.k8s.io"
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - "extensions"
      - "networking.k8s.io"
    resources:
      - ingresses/status
    verbs:
      - update

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: nginx-ingress-role
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - pods
      - secrets
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - configmaps
    resourceNames:
      # Defaults to "<election-id>-<ingress-class>"
      # Here: "<ingress-controller-leader>-<nginx>"
      # This has to be adapted if you change either parameter
      # when launching the nginx-ingress-controller.
      - "ingress-controller-leader-nginx"
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - get

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: nginx-ingress-role-nisa-binding
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: nginx-ingress-role
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: nginx-ingress-clusterrole-nisa-binding
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: nginx-ingress-clusterrole
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx

---

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      hostNetwork: true
      # wait up to five minutes for the drain of connections
      terminationGracePeriodSeconds: 300
      serviceAccountName: nginx-ingress-serviceaccount
      nodeSelector:
        kubernetes.io/os: linux
      containers:
        - name: nginx-ingress-controller
          image: lizhenliang/nginx-ingress-controller:0.30.0
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            # www-data -> 101
            runAsUser: 101
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown

---

apiVersion: v1
kind: LimitRange
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  limits:
  - min:
      memory: 90Mi
      cpu: 100m
    type: Container
  • 修改YAML
    • 镜像地址修改成国内的:lizhenliang/nginx-ingress-controller:0.30.0

    • 将Ingress Controller暴露,一般使用宿主机网络(hostNetwork: true)或者使用NodePort

    • 使用DaemonSet的原因是,这种类型会在每个节点上去部署一个ingress-controller, 而ingress-controller也只会在自己的当前节点对80和443端口进行监听,为了保证高可用,所以我们使用DaemonSet的方式

    • 上诉yaml已经修改好了

    • 其他控制器 (opens new window)

# Pod与Ingress的关系

使用ingress暴露service

  • 查看即将暴露的service

    $ kubectl get svc
    NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
    web              NodePort    10.97.108.187   <none>        81:30002/TCP   3d1h
    web-cluster-ip   ClusterIP   10.96.112.69    <none>        80/TCP         3d1h
    

    可以看到,之前web这个服务,我们使用的nodePort的方式部署,所以能通通过宿主机的30002端口访问,但web-cluster-ip这个service我们没有对外进行暴露,只能在集群内部访问,所以接下来我们使用ingress将web-cluster-ip对外暴露

  • 给ingress controller创建规则

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: liuxiaolu
    spec:
      rules:
      - host: web.liuxiaolu.cn
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-cluster-ip
                port:
                  number: 80
    
  • 执行

    $ kubectl apply -f ingress.yaml
    ingress.networking.k8s.io/aliangedu-https created
    $ vi ingress.yaml
    $ kubectl get ingress
    NAME              CLASS    HOSTS              ADDRESS   PORTS   AGE
    aliangedu-https   <none>   web.aliangedu.cn             80      23s
    
  • 本地hosts中添加解析

    master或者node节点的地址    web.liuxiaolu.cn
    
  • 通过浏览器访问web.liuxiaolu.cn进行验证

Last Updated: 6/26/2022, 4:44:06 PM