Kubernetes上使用Ingress Nginx将服务发布到外部IP

Kubernetes的网络结构

K8s的网络相对比较复杂, 包含了如下几类IP地址:

Host Network

运行K8s集群的宿主服务器的内网IP, 其网段在配置宿主机时设置. 这些服务器可能是物理机, 也可能是ESXi或KVM虚机. 可以根据这个IP在K8s初始化时设置--apiserver-advertise-address, 配置外网访问时, 前端的负载均衡要通过这些IP访问服务.

Docker Bridge Network

运行Docker服务时启动的虚拟网卡docker0的IP, 通常为 172.17.0.1/16, 对应docker内部默认的bridge网段. 这个网络在K8s中不会用到.

Pod Network

K8s中Pod单元的IP, 其网段在K8s初始化时使用--pod-network-cidr设置, 此处使用172.16.0.0/16, 在每个宿主(node节点)上, 都会有一个对应的二级网段, 例如 172.16.1.0/24, 如果使用的是flannel, 那么在宿主机上会对应成对出现的两个虚拟网卡 cni0 和 flannel.1 (如果宿主机上还没部署pod, cni0可能不会出现)

Pod网络在集群内可以跨节点相互ping通, 在master和node主机上也可以直接访问Pod的IP

Service Network

K8s中Service单元的IP, 其网段在K8s初始化时使用--service-cidr设置, 在master和node主机上不能直接访问. 这里使用10.1.0.0/16.

Ingress的机制

要解决的问题

因为K8s中Pod是易变的, Pod IP在更新中会自动修改, 使用Service能使访问入口相对固定, 但是Service IP在集群外不能访问, 要对外提供访问, 只能把Service以NodePort, LoadBalancer这些方式Expose出去, 但是NodePort会与每一个Node主机绑定, 而LoadBalancer需要云服务商提供相应的服务(或自己安装).

原理

Ingress 启动一个独立的Pod来运行七层代理, 可以是 Nginx, Traefik 或者是 Envoy. Ingress Pod会直接代理后端提供服务的Pod, 为了能监听后端Pod的变化, 需要一个 Headless Service 通过Selector选择指定的Pod, 并收集到Pod对应的IP. 一旦后端Pod产生变化, Headless Service 会自动根据变化更改配置文件并重载.

如果使用的是 Nginx 类型的 Ingress Pod, 则每次变化后通过reload修改过的配置文件实现规则更新.

Ingress Controller

在kubernetes集群内节点上运行web七层代理所对应的Pod, 由此Pod代理集群内部的Service, Service再把流量转发给集群内部对应的Pod, 这就叫做 Ingress Controller, Ingress Controller 基于 DaemonSet 控制器实现, DaemonSet用于保证集群内部每个节点上都运行一个指定的Pod.

部署Ingress Nginx

软件版本

这里使用的环境是 K8s 1.17, Docker 18.06.3, Ingress Nginx 0.26.2

Ingress Nginx项目地址: https://github.com/kubernetes/ingress-nginx/

Ingress Nginx的安装文档: https://kubernetes.github.io/ingress-nginx/deploy/ 使用 https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/ 

安装前提

已经配置好K8s集群

 

部署公共部分

现在最新的标签是0.26.2, 使用部署模板

https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.26.2/deploy/static/mandatory.yaml

下载后在master主机上执行

kubectl apply -f mandatory.yaml

可以使用下面的命令查看部署的结果

kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx --watch
kubectl get services

 

部署Nodeport部分

因为不使用云服务商的服务, 所以要使用Nodeport的方式, 对应的部署模板在

https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.26.2/deploy/static/provider/baremetal/service-nodeport.yaml

这一步实际上是创建一个Nodeport将ingress-nginx服务发布到Node节点的Host Network上, 原模板中未指定端口, 创建后会随机设置端口, 可以修改一下, 只能使用端口范围 3000 ~ 32767. 修改后的service-nodeport.yaml, 将80端口映射到了30080, 443映射到了30443

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
      nodePort: 30080
    - name: https
      port: 443
      targetPort: 443
      protocol: TCP
      nodePort: 30443
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

可以使用下面的命令查看发布的结果, 以及映射出去的实际端口

kubectl get services -n ingress-nginx

 

创建服务并测试

以下都是使用defalut的namespace. 都使用kubectl apply -f 模板名 命令进行发布

Deployment, 创建两个nginx的pod

# nginx-deployment.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.cn-shanghai.aliyuncs.com/jovi/nginx:alpine
        ports:
        - containerPort: 80

Service, 将上面创建的两个Pod发布为Servce, 将Pod的80端口映射到Service的8080端口

# nginx-service.yml 
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 80

查看service详情

$ kubectl describe service my-service
Name:              my-service
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"my-service","namespace":"default"},"spec":{"ports":[{"port":8080,...
Selector:          app=nginx
Type:              ClusterIP
IP:                10.1.116.99
Port:              <unset>  8080/TCP
TargetPort:        80/TCP
Endpoints:         172.16.1.6:80,172.16.1.7:80
Session Affinity:  None
Events:            <none>

  

Ingress, 将上面的Service的8080端口映射到Ingress的http

# ingress-test.yaml 
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    # use the shared ingress-nginx
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - http:
      paths:
      - backend:
          serviceName: my-service
          servicePort: 8080

查看Ingress详情

$ kubectl get services -n ingress-nginx
NAME            TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.1.60.152   <none>        80:30080/TCP,443:30443/TCP   69m
milton@k8s00:~/backup$ kubectl describe ingress test-ingress
Name:             test-ingress
Namespace:        default
Address:          10.1.60.152
Default backend:  default-http-backend:80 (<none>)
Rules:
  Host  Path  Backends
  ----  ----  --------
  *     
           my-service:8080 (172.16.1.6:80,172.16.1.7:80)
Annotations:
  kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"networking.k8s.io/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"test-ingress","namespace":"default"},"spec":{"rules":[{"http":{"paths":[{"backend":{"serviceName":"my-service","servicePort":8080}}]}}]}}

  kubernetes.io/ingress.class:  nginx
Events:                         <none>

这时候, 就可以通过 http://宿主机IP:30080 访问到这两个Pod的Nginx默认页.

posted on 2020-01-08 14:45  Milton  阅读(8935)  评论(0编辑  收藏  举报

导航