Kubernetes @ OVH - Traefik en Deployment et intégration des Load Balancers


23/01/2019 kubernetes traefik ovh deployment load-balancer ingress

Pour faire suite au billet sur le déploiement de Traefik sous la forme d’un DaemonSet chez OVH, j’ai profité de la sortie en mode beta des Load Balancers pour revoir ma copie :

  • Déploiement de Traefik sous la forme d’un Deployment plutôt qu’un DaemonSet,
  • Intégration des Load Balancers,
  • Utilisation d’un namespace “traefik” plutôt que de tout mettre dans kube-system.

Par simplicité, je n’ai toujours qu’une node en plus du master fourni par OVH. Cela m’évite la problématique du stockage distribué des certificats. Cela fera l’objet d’un autre billet.

Créons le namespace traefik :

# Create namespace
kubectl create ns traefik
# Change context to this namespace so that all commands are by default run for this namespace
# see https://github.com/ahmetb/kubectx
kubens traefik

Commençons par traefik/rbac.yml - le fichier défini le compte de service (Service Account), le rôle au niveau du cluster (Cluster Role) et la liaison entre le rôle et le compte de service (Cluster Role Binding)

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: traefik-ingress-controller
  namespace: traefik
---
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
---
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: traefik

Ensuite, pour Traefik, j’ai besoin d’un fichier traefik.toml avec la configuration que je mets à disposition sous la forme d’une ConfigMap dans un fichier traefik/traefik-toml-configmap.yml :

apiVersion: v1
kind: ConfigMap
metadata:
  name: traefik-conf
data:
  traefik.toml: |
    defaultEntryPoints = ["http", "https"]

    logLevel = "INFO"

    insecureSkipVerify = true

    [entryPoints]
      [entryPoints.http]
        address = ":80"
        [entryPoints.http.redirect]
          entryPoint = "https"
      [entryPoints.https]
        address = ":443"
        [entryPoints.https.tls]
      [entryPoints.api]
        address = ":8080"

    [acme]
    email = "contact@cerenit.fr"
    storage = "/acme/acme.json"
    entryPoint = "https"
    onHostRule = true
    [acme.httpChallenge]
      entryPoint = "http"

    [api]
    entryPoint = "api"
    dashboard = true
    debug = false

    [kubernetes]

Le dashboard est à protéger par une authentification pour éviter tout accès non souhaité. Je l’ai supprimé de la configuration par simplicité.

Ensuite, pour stocker mes certificats, il me faut un volume que je défini via le fichier traefik/traefik-certificates-pvc.yml :

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: traefik-certificates
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi
  storageClassName: cinder-classic

1 Go pour des certificats, c’est clairement trop mais il n’est pas possible pour le moment d’avoir un stockage plus réduit.

Je peux donc enfin déployer Traefik via le fichier traefik/traefik-deployment.yml :

---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: traefik-ingress-controller
  labels:
    k8s-app: traefik-ingress-lb
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: traefik-ingress-lb
  template:
    metadata:
      labels:
        k8s-app: traefik-ingress-lb
        name: traefik-ingress-lb
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 60
      containers:
      - image: traefik:1.7.7
        name: traefik-ingress-lb
        volumeMounts:
        - mountPath: /config
          name: traefik-config
        - mountPath: /acme
          name: certificates
        ports:
        - name: http
          containerPort: 80
        - name: admin
          containerPort: 8080
        - name: secure
          containerPort: 443
        args:
        - --configfile=/config/traefik.toml
      volumes:
        - name: traefik-config
          configMap:
            name: traefik-conf
        - name: certificates
          persistentVolumeClaim:
            claimName: traefik-certificates

Nous déployons donc :

  • Traefik en Deployment
  • Les ports 80, 443 et 8080 sont définis
  • La configuration est une ConfigMap
  • Les certificats sont à déployer dans un volume

Pour permettre au cluster d’accéder aux différents ports, il faut définir un service via le fichier traefik-service-clusterip.yml :

---
kind: Service
apiVersion: v1
metadata:
  name: traefik-ingress-service-clusterip
spec:
  selector:
    k8s-app: traefik-ingress-lb
  ports:
    - protocol: TCP
      port: 80
      name: web
    - protocol: TCP
      port: 8080
      name: admin
    - protocol: TCP
      port: 443
      name: secure
  type: ClusterIP

Et pour avoir un accès de l’extérieur, il faut instancier un load-balancer via le fichier traefik/traefik-service-loadbalancer.yml

kind: Service
apiVersion: v1
metadata:
  name: traefik-ingress-service-lb
spec:
  selector:
    k8s-app: traefik-ingress-lb
  ports:
    - protocol: TCP
      port: 80
      name: web
    - protocol: TCP
      port: 443
      name: secure
  type: LoadBalancer

Pour donner l’accès au dashboard via une url sécurisée par un certificat Let’s Encrypt, il faut déclarer un Ingress, dans le fichier traefik/traefik-api-ingress.yml :

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: traefik-web-ui
spec:
  rules:
  - host: traefik.k8s.cerenit.fr
    http:
      paths:
      - path: /
        backend:
          serviceName: traefik-ingress-service
          servicePort: admin

Il ne nous reste plus qu’à faire :

# Create k8s ressources for traefik
kubectl create -f traefik/
# Watch service to get IPs
kubectl get svc -w

Une fois votre IP obtenue, il suffit de faire pointer votre entrée DNS vers cette IP ou de tester via :

curl -H "Host: traefik.k8s.cerenit.fr" https://xxx.xxx.xxx.xxx/

Pour l’obtention du certificat Let’s Encrypt, il faut que votre enregistrement DNS soit à jour préalablement. Sinon vous aurez un certificat autosigné par Traefik en attendant.

Dès lors, vous pouvez accéder au dashboard de Traefik via l’url définie. Pour donner accès à d’autres sites, il faut déclarer d’autres ingress sur le même modèle et le tour est joué.

Comparativement au dernier tutoriel :

  • Nous n’exposons plus le port 8080 au niveau de l’hôte,
  • Nous respectons plus les guidelines kubernetes à savoir de donner accès à une ressource via un service de type Load-Balancer ou NodePort
  • Nous utilisons une seule IP externe et nous appuyons sur les ingress pour mutualiser le load balancer et éviter d’avoir une IP publique par service à exposer
  • Nous ne sommes pas sur d’avoir un pod traefik par noeud mais nous gagnons en flexibilité - il faudra jouer avec les replicas dès qu’on ajoutera des nodes dans le cluster.

Il reste encore le problème des stockage des certificats à résoudre pour passer à un contexte multi-nodes. Ce sera l’objet d’un prochain billet avec idéalement l’intégration de Traefik avec cert-manager (plutôt que de devoir déployer une base clé/valeur comme etcd ou consul pour y stocker les infos de traefik).

N’hésitez pas à me faire part de vos retours.

Le Blog

Nous partageons ici notre veille et nos réflexions

Nuage de tags

docker kubernetes traefik influxdb ansible elasticsearch kafka postgres grafana timeseries python aws redis sécurité mysql terraform tick warp10 cassandra cloud ovh helm swarm telegraf test timescaledb chronograf docker-compose git rancher résilience architecture confluent ksql log machine-learning microservice prometheus ptsm serverless spark angularjs api cert-manager cncf container devops flux gitlab graphql hashicorp iac java javascript monitoring opensource operator optimisation raspberrypi s3 service-mesh stream windows arm bilan csp cérénit dashboard dns documentation elastic gcp hpkp ingress jenkins kafka-streams kapacitor kibana kubedb lambda lean licence maesh maintenance microsoft mobile nginx npm orientdb perspective redhat rest rethinkdb reverse-proxy rook sauvegarde scaleway sql ssh vscode agile apm automatisation azure bash big-data ceph certificat ci/cd cli cluster containerd continous-delivery continous-integration cookie deployment diff fluxlang forecast framework gdpr grav hsts http/3 https hypriot hébergement influxace influxdata influxdays istio json k3s lets-encrypt linux load-balancer mariadb meetup molecule mongodb nosql percona performance php pip pipeline postgresql reaper registry replication rpi rsyslog scale secrets société solr sre systemd tls vault virtualenv vue.js wagtail yarn accessibilité acme akka alerte alibaba amazon-emr anonymisation anthos apache-pulsar ara arima audit bastion beam beat bigdatahebdo bounded-context branche brigade browser buildkit cahier-des-charges cassandra-reaper cd cdc cdk centralisation-de-logs certificats chart checklist chrome ci cloud-init cloud-native cloud-storage clusterip cnab cockroachdb code codeurs-en-seine commit confluence conftest consul continous-deployment conventional-commit coreos cors covid19 cqrs crash cron crontab csrf css curl d3.js daemonset data-pipelining data.gouv.fr datacenter dataviz date ddd debezium debian delta deprek8 desktop devoxx distributed-systems dive docker-app docker-hub docker-registry docker-swarm documentdb dokcer données-personnelles draft drop-in déploiement développement-du-site e-commerce ebs ec2 edge elassandra electron elk engineering entreprise ergonomie etcd event-sourcing faas facebook faisabilité falcor feature-policy fedora feed filebeat firebase firefox fish flash flask fleet flink flows fluentd formation foundation frontend fsync fullstack github gitignore gitlab-ci gke glacier glowroot google google-cloud-next gpu grid géospatial hacker hadoop haproxy harbor hdfs header html html5 http hue ia iaac ibm immutable incident index influxcloud infrastructure-as-code ingénierie inspec jq jquery jwt k3d k8s k9s kotlin kubeadm kubecon kubectl laravel letsencrypt linky liste-de-diffusion loadbalancer logstash logstatsh loi longhorn mailing-list management message metallb micro-service mot-de-passe multi-cloud médecine métrique newsletter nodeport nomad null object-storage observabilité opa opendata openebs openmetrics openshit openssh openstack openweb over-engineering packaging pandas partiql password persistent-volume-claim pipenv pod portainer prediction prescience ptyhon publicité pubsub push pyenv quasardb quay questdb queue quic ram rambleed raml react recaptcha recherche redistimeseries reindex reinvent reliability responsive revocation revue-de-code rgpd rkt rolespec root rpo rto runc rwd safe-harbor scalabilité scanner schema sdk search select serverless-architecture service-account service-worker setuptools sha1 sharding shell shipyard sidecar souveraineté-numérique spinnaker spécifications sri ssh-agent ssl stash statistique superset suse sympa syslog-ng test-unitaire tidb tiers timer timezone training travail tsl ubuntu unikernel unit ux vendredi victoria-metrics vie-privée virtualbox virtualisation vitess vm vnc volume voxxeddays vpc warpscript warpstudio web yubikey

Syndication

Restez informé(s) de notre actualité en vous abonnant au flux du blog (Atom)