Ingress Controller的安装部署

3,680 阅读5分钟

0-前言

此教程仅适合学习和开发环境部署,强烈不建议上生产

网上有很多关于ingress controller部署的文章,部署方式也各种各样,结合实际环境,找到一个适合自己的方式,记录一下,给未来的自己。

安装部署 个人理解ingress controller的安装部署是分为两步:

  • 第一步是--安装,即在k8s集群里安装这个组件;
  • 第二步是--部署,即如何把这个集群里的组件暴露到外部以供调用 经过第一步和第二步,就在k8s集群的内外交界处搭起了一座沟通的桥梁。

1-Ingress Controller的实现方案

Ingress Controller只是一个抽象的统称,其实现的组件有如下:

  • Ingress NGINX: Kubernetes 官方维护的方案,也是本次安装使用的 Controller。
  • F5 BIG-IP Controller: F5 所开发的 Controller,它能够管理员通过 CLI 或 API 让 Kubernetes 与 OpenShift 管理 F5 BIG-IP 设备。
  • Ingress Kong: 著名的开源 API Gateway 方案所维护的 Kubernetes Ingress Controller。
  • Traefik: 是一套开源的 HTTP 反向代理与负载均衡器,而它也支援了 Ingress。
  • Voyager: 一套以 HAProxy 为底的 Ingress Controller。 Source

NOTE:Ingress NGINX 是本文中安装部署的对象,以下所有的Ingress Controller均指Ingress NGINX方案

2-安装

一行命令(已在K8S v19.2上验证通过):

kubectl apply -f mandatory.yml

这是一份官方提供的mandatory.yml文件,好多ingress controller的部署教程都不提供完整的mandatory.yml文件,只会提供一个URL,但是这个URL往往会有两个问题:

  • 1.URL点进去找不到文件
  • 2.提供的版本太老了 所以我直接贴出来一份完整版的mandatory.yml文件,给大家快速安装验证功能的同时也给自己做了一个备份(如果是要上生产,强烈不建议直接使用该yml文件)。

修改了一个地方:

kind.deployment.spec.template.spec.containers.imagePullPolicy=IfNotPresent

之所以修改,是因为直接使用kubectl apply -f mandatory.yml部署的时候,发现:始终拉不下来镜像:quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.31.1。所以我在node节点上,先把docker image下载到本地(docker pull quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.31.1),然后再部署(kubectl apply -f mandatory.yml)。

知识点

默认的镜像拉取策略是 IfNotPresent:在镜像已经存在的情况下, kubelet 将不再去拉取镜像。 如果希望强制总是拉取镜像,你可以执行以下操作之一:

  • 设置容器的 imagePullPolicy 为 Always。
  • 省略 imagePullPolicy,并使用 :latest 作为要使用的镜像的标签。
  • 省略 imagePullPolicy 和要使用的镜像标签。
  • 启用 AlwaysPullImages 准入控制器(Admission Controller)。 如果 imagePullPolicy 未被定义为特定的值,也会被设置为 Always

Source

3-部署

部署ingress controller有很多方式,以下举例了三种常见的方式:

  • Deployment+Service(NodePort)
  • DaemonSet+HostNetwork+nodeSelector
  • Deployment+Service(LoadBalancer)

这里,我主要还是针对前两种部署方式做展开。

3.1-Deployment+Service(NodePort)

nodePort的部署思路:

  • 首先在所有的节点之上提供一个nodeport service,
  • 然后在每个节点上开辟nodePort的端口(该端口为宿主机的端口),
  • 通过nodeport service将流量引入进来到任意一个节点上,如果该节点没有部署ingress controller容器,那么就通过iptables将流量转发到ingress-controller容器中(图中的nginx容器),
  • 而后由nginx根据ingress的规则进行判断,将其转发到对应的应用web容器中。

需要注意的是,ingress controller容器可以部署在其中一个node节点或者master节点上(不建议),也可以部署到所有的node节点和master节点上。后者的好处是,可以减少一次NAT的网络流量转发,提高效率;缺点也显而易见,在资源有限的情况下无法在每个节点都部署一个ingress controller容器。

该部署方式的svc yaml文件ingress-controller-nodeport-svc.yml。在node节点上开一个30080端口作为nodeport,暴露给集群外部。

外部访问方式: http://nodeport_ip:30080/url

3.2-DaemonSet+HostNetwork+nodeSelector

HostNetwork的部署思路:

  • 在每个节点创建一个ingress controller容器,
  • 将这些容器的网络模式设为hostNetwork(这会导致每个节点的80和443端口都会被ingress controller容器占用)
  • 当外部流量通过节点的80/443端口进来后,都会进入到ingress controller容器里,而后nginx根据ingress规则再将流量转发到对应的web应用容器中。 相比较与nodePort模式,hostNetwork模式不需要创建一个svc来统一收口流量,而是让各个节点自行转发流量。

该方案部署,需要修改mandatory.yml:

  • 删除kind.Deployment.spec.replicas: 1
  • 修改kind.Deploymentkind.DeamonSet
  • 添加kind.DeamonSet.spec.template.spec.hostNetwork: true

外部访问方式: http://nodeport_ip/url

知识点:什么是DeamonSet部署?

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。 Source

知识点:为什么要用DeamonSet部署?

结合DS的定义和hostNetwork的部署思路,我们可以看到,利用DS可以很方便的实现:在所有的节点上都部署一个ingress controller容器的副本。当然,也可以利用K8S的标签选择器,在指定的一个或多个节点上部署ingress controller容器的副本。操作如下:

  • 首先给需要部署ingress controller容器的节点(如node-1)打上标签 $ kubectl label node node-1 isIngressController="true"
  • 修改mandatory.yml: kind.DeamonSet.spec.template.spec.NodeSelector: kubernetes.io/os: linux 改为 kind.DeamonSet.spec.template.spec.NodeSelector: isIngressController="true"

3.3-Nodeport和HostNetwork方案的比较

  • 容器资源消耗

    • nodePort部署模式中需要部署的ingress-controller容器较少。一个集群可以部署几个就可以了。
    • 而hostNetwork模式需要在每个节点部署一个ingress-controller容器,因此总起来消耗资源较多。
  • 对宿主机端口的占用

    • nodePort模式主要占用的是svc的nodePort端口。
    • 而hostNetwork则需要占用物理机的80和443端口。
  • 网络流量的流转

    • 通过hostNetwork访问时,每个node节点都部署了ingress controller容器,因此不需要对进入的流量进行转发,提高了效率
  • source ip追踪

    • 通过nodePort访问时,nginx接收到的http请求中的source ip将会被转换为接受该请求的node节点的ip,而非真正的client端ip。
    • 而使用hostNetwork的方式,ingress-controller将会使用的是物理机的DNS域名解析(即物理机的/etc/resolv.conf)。而无法使用内部的比如coredns的域名解析。

I-Reference

II-附录

ingress-controller-nodeport-svc.yml

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:
    - port: 80
      targetPort: 80
      nodePort: 30080
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

mandatory.yml

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/v1
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/v1
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/v1
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/v1
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: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  replicas: 1
  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:
      # 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: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.31.1
          imagePullPolicy: IfNotPresent
          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