Как установить traefik в качестве Ingress Controller в Kubernetes

Kubernetes Ingress Controller Traefik

Всем привет. Сегодня я хочу рассказать о том, как установить traefik в свой собственный on-premise kubernetes кластер в качестве Ingress Controller.

Traefik может работать в Kubernetes в 2 режимах:

  1. kubernetesingress - это когда он будет мониторить стандартные объекты с kind: Ingress с строить маршруты на базе их
  2. kubernetescrd - это когда для описания маршрутов надо будет создавать объкты kind: IngressRoute. Этот способ более гибкий, однако при переходе на другой Ingress Controller, вам полностью придется переписать Ingress правила. Кроме того, с точки зрения kubernetes, такой способ развертывания не является Ingress Controller'ом, но нам это не так важно. Важно что мы получим тот же результат.

Мы будем разворачивать и то, и то. Кроме того, мы еще и Let's Encrypt подключим.

Что и как мы будет делать: мы пометим один из узлов кластера меткой, которую будем использовать в nodeSelector для определения того узла, где будет запускаться наш daemonset. Этот daemonset будет запускаться в режиме hostNetwork: true для того, чтобы мы могли открыть порты 80 и 443. Выделенный узел не будет непосредственно доступен из интернета. Для него будут лишь "проброшены" порты 80 и 443.

Итак, метим наш узел:

kubectl label nodes k8s-us1804-a traefik=true

Описываем все необходимые ресурсы для traefik:

# All resources definition must be declared
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressrouteudps.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteUDP
    plural: ingressrouteudps
    singular: ingressrouteudp
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsstores.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSStore
    plural: tlsstores
    singular: tlsstore
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: traefikservices.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TraefikService
    plural: traefikservices
    singular: traefikservice
  scope: Namespaced

Выдаем права для service account:

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller

rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - traefik.containo.us
    resources:
      - middlewares
      - ingressroutes
      - traefikservices
      - ingressroutetcps
      - ingressrouteudps
      - tlsoptions
      - tlsstores
    verbs:
      - get
      - list
      - watch

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller

roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
  - kind: ServiceAccount
    name: traefik-ingress-controller
    namespace: default

И запускаем наш traefik:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: traefik-ingress-controller
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: traefikconfig
data:
  traefik.yaml: |-
    api:
      dashboard: true
    entryPoints:
      web:
        address: ":80"
        forwardedHeaders:
          insecure: true
      websecure:
        address: ":443"
        forwardedHeaders:
          insecure: true
    providers:
      kubernetesingress: {}
      kubernetescrd: {}
    certificatesResolvers:
      letsencrypt:
        acme:
          email: vitaliy@leschenko.by
          storage: /acme/acme.json
          caserver: https://acme-v02.api.letsencrypt.org/directory
          httpChallenge:
            entryPoint: web
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: traefik
  labels:
    app: traefik
spec:
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      hostNetwork: true
      serviceAccountName: traefik-ingress-controller
      nodeSelector:
        kubernetes.io/os: linux
        traefik: "true"
      volumes:
        - name: acme
          hostPath:
            path: /acme
        - name: config
          configMap:
            name: traefikconfig
      containers:
        - name: traefik
          image: traefik:v2.2
          volumeMounts:
            - name: acme
              mountPath: /acme
            - name: config
              mountPath: /traefik
          args:
            - --configFile=/traefik/traefik.yaml
            - --log.level=DEBUG
          ports:
            - name: web
              containerPort: 80
            - name: https
              containerPort: 443
            - name: admin
              containerPort: 8080

В качестве бонуса создаем правило для dashboard самого traefik'а:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-dashboard
spec:
  entryPoints:
    - websecure
  routes:
  - match: Host(`traefik.vitaliy.org`)
    kind: Rule
    services:
    - name: api@internal
      kind: TraefikService
    middlewares:
      - name: auth
  tls:
    certResolver: letsencrypt
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: auth
spec:
  basicAuth:
    secret: authsecret
---
# A file containing test/test and test2/test2
apiVersion: v1
kind: Secret
metadata:
  name: authsecret
  namespace: default
data:
  users: |2
    dGVzdDokYXByMSRINnVza2trVyRJZ1hMUDZld1RyU3VCa1RycUU4d2ovCnRlc3QyOiRhcHIxJGQ5
    aHI5SEJCJDRIeHdnVWlyM0hQNEVzZ2dQL1FObzAK

Всё. Через несколько секунд можно обращаться по адресу https://traefik.vitaliy.org.