Kubernetes @ OVH - Traefik en DaemonSets


07/01/2019 kubernetes traefik ovh daemonset

Sortant de la formation Déployer ses applications avec Kubernetes animée par Jérome Petazzoni - slides - j’ai voulu mettre en oeuvre différents enseignements. OVH proposant un service kubernetes managé en version beta basé sur une infrastructure Openstack, j’en ai profité pour jouer un peu avec.

En parcourant la documentation disponible et le canal gitter, on note que :

  • La version de kubernetes est la version 1.11.3
  • Les services de type Load Balancer ne sont pas encore supportés - cela devrait arriver prochainement
  • Il faut en attendant passer par un NodePort pour accéder aux applications.

J’ai voulu donc voir comment déployer Traefik sur mon cluster qui ne contient qu’une seule node pour me facilier la gestion des volumes. En effet, la classe de stockage “cinder” ne supporte pas un accès depuis plusieurs nodes (ReadOnlyMany ou mieux ReadWriteMany) mais seulement depuis une node (ReadWriteOnce).

C’est donc clairement sous-optimal comme configuration mais ça permet de se faire la main à un prix raisonnable et sans trop se casser la tête. Dans le cadre d’un vrai déploiement, il faudrait trouver une solution de stockage plus intéressante pour les données de traefik (en l’occurence les certificats).

L’idée est donc de déployer Traefik sous la forme d’un DaemonSet et de mapper les ports 80/443 de chaque node du cluster.

Pour se faire, Traefik founi un exemple de DaemonSet que j’ai largement repris.

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: kube-system
---
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: kube-system

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
  namespace: kube-system
data:
  traefik.toml: |
    defaultEntryPoints = ["http", "https"]

    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    

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-ds.yml :

---
kind: DaemonSet
apiVersion: extensions/v1beta1
metadata:
  name: traefik-ingress-controller
  namespace: kube-system
  labels:
    k8s-app: traefik-ingress-lb
spec:
  template:
    metadata:
      labels:
        k8s-app: traefik-ingress-lb
        name: traefik-ingress-lb
    spec:
      hostNetwork: true
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 60
      containers:
      - image: traefik
        name: traefik-ingress-lb
        volumeMounts:
        - mountPath: /config
          name: traefik-config
        - mountPath: /acme
          name: certificates
        ports:
        - name: http
          containerPort: 80
          hostPort: 80
        - name: https
          containerPort: 443
          hostPort: 443
        - name: admin
          containerPort: 8080
          hostPort: 8080
        securityContext:
          capabilities:
            drop:
            - ALL
            add:
            - NET_BIND_SERVICE
        args:
        - --kubernetes
        - --logLevel=INFO
        - --configfile=/config/traefik.toml
      volumes:
        - name: traefik-config
          configMap:
            name: traefik-conf
        - name: certificates
          persistentVolumeClaim:
            claimName: traefik-certificates
---
kind: Service
apiVersion: v1
metadata:
  name: traefik-ingress-service
  namespace: kube-system
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: https

Nous déployons donc :

  • Traefik en DaemonSet
  • Les ports 80, 443 et 8080 sont ouverts au niveau de l’hôte
  • La configuration est une ConfigMap
  • Les certificats sont à déployer dans un volume

A partir de ce moment là, vous avez accès au dashboard via http://<node ip>:8080/

Pour améliorer un peu les choses, nous pouvons vouloir donner accès au dashboard via une url et sécurisé par un certificat Let’s Encrypt.

Pour se faire, il faut déclarer un Ingress, dans le fichier traefik/traefik-api-ingress.yml :

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

Il ne nous reste plus qu’à faire :

kubectl create -f traefik/
ingress.extensions/traefik-web-ui created
persistentvolumeclaim/traefik-certificates created
daemonset.extensions/traefik-ingress-controller created
service/traefik-ingress-service created
serviceaccount/traefik-ingress-controller created
clusterrole.rbac.authorization.k8s.io/traefik-ingress-controller created
clusterrolebinding.rbac.authorization.k8s.io/traefik-ingress-controller created

Dès lors, vous pouvez accéder au dashboard de Traefik via l’url définie.

Nous arrivons au bout de ce tutoriel permettant de jouer rapidement avec Traefik sous la forme d’un DaemonSet. Le contenu est criticable et améliorable par bien des aspects :

  • Il faudrait ne pas exposer le port 8080 de Traefik au niveu de la node et n’y accéder que via le service,
  • Le stockage des certificats est à améliorer dans un contexte multi-nodes

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

Syndication

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

Nuage de tags

docker kubernetes influxdb timeseries warp10 grafana traefik python ansible elasticsearch kafka postgres aws sécurité terraform mysql redis telegraf dashboard docker-compose git ovh tick cassandra chronograf cloud hashicorp helm timescaledb flux podman ptsm swarm test vector kapacitor rancher résilience timescale gcp gitlab influxdata iot log machine-learning monitoring postgresql prometheus raspberrypi s3 spark sql vscode api architecture arm bilan comptabilité confluent cérénit devops gitlab-ci iac java ksql microservice nomad optimisation perspective serverless service-mesh angularjs cert-manager cli cncf consul container discovery dns flows gke graphql influxace ingress javascript npm opensource operator performance pipeline rook scaleway ssh stream vault warpscript windows bigdatahebdo containerd csp documentation elastic forecast geospatial golang hpkp jenkins json kafka-streams kibana kubedb lambda lean licence maesh maintenance mariadb microsoft mobile mqtt nginx orientdb quasardb redhat registry rest rethinkdb reverse-proxy rgpd sauvegarde warpstudio wireguard agile anomalie apm arima audit automatisation azure bash big-data ceph certificat challenge ci/cd cluster co2 continous-delivery continous-integration cookie data datatask dataviz dbt deployment diff django edge esp32 facebook fec fluxlang framework gdpr google-analytics grav hsts http/3 https hypriot hébergement ia influxdays istio jq k3s lets-encrypt linux load-balancer longhorn meetup metabase molecule mongodb nosql nvidia openebs openhab openssh ovhcloud pandas parquet percona php pip questdb reaper replication rootless rpi rsyslog runc scale secrets société solr sre systemd tempo timezone tinygo tls virtualenv vitess vue.js wagtail warpfleet yarn accessibilité acme adoptopenjdk agpl akka alerte alertes alerting alibaba amazon-emr amqp anonymisation anthos apache-pulsar ara arduino arrow artefact asgi automation automl awstats banque bastion beam beat bi bme680 bootstrap bounded-context branche brigade browser buildah buildkit cahier-des-charges calico cassandra-reaper cd cdc cdk centos centralisation-de-logs certificats cgroups chart check checklist chrome ci cilium circuitpython clever-cloud clickhouse cloud-init cloud-native cloud-storage cloudflare clusterip cnab cni cockroachdb code codeurs-en-seine commit confluence conftest consul-connect context continous-deployment conventional-commit coreos cors covid19 cqrs crash cri cron crontab csi csrf css curl d3.js daemonset data-engineer data-pipelining data.gouv.fr databricks datacenter date date-scientist ddd debezium debian delta deprek8 desktop devoxx dig distributed-systems dive docker-app docker-hub docker-registry docker-swarm dockerfile dockershim documentdb dog dokcer données-personnelles draft dredd drop-in duckdb duration déploiement développement-du-site e-commerce ebs ec2 elassandra electron elk engineering entreprise ergonomie etcd euclidia event-sourcing faas faisabilité falco falcor feature-policy fedora feed filebeat firebase firefox fish flash flask fleet flink fluentd font formation foundation frenchtech frontend fsync fugue fullstack git-filter-repo github gitignore gitpod glacier glowroot go goaccess google google-cloud-next gpg gpu grep grid géospatial hacker hadoop haproxy harbor hdfs header holt-winters html html5 http httpx hue iaac ibm immutable incident index indluxdata influxcloud infrastructure-as-code ingénierie inspec jless jquery jvm jwt k3d k6 k8s k9s kaniko katz kotlin kubeadm kubecon kubectl label laravel leap-second lens letsencrypt libssh linky linter liste-de-diffusion lmap loadbalancer logstash logstatsh loi loki lstm mailing-list management matomo maturité mesh mesos message metallb micro-service minio mot-de-passe multi-cloud médecine métrique n8n nebula network newsletter nodejs nodeport notebook notifications nrtsearch null numérique object-storage observability observabilité opa opendata openmetrics openshit openstack openweb opnsense over-engineering packaging partiql password persistent-volume-claim pico pipenv pivot pod portainer portworx prediction prescience privacy-shield production promql prophet prévision psp ptyhon publicité pubsub pulsar push pyenv pérénnité qualité quay queue quic ram rambleed raml react readme recaptcha recherche redistimeseries reindex reinvent reliability remote-execution repository responsive retention-policy revocation revue-de-code rexec rhel rkt robotframework rolespec root rpo rto rust rwd réseau résultat safe-harbor sarima scalabilité scanner schema scp sdk search select semiconducteur serverless-architecture service service-account service-worker setuptools sftp sha1 shard shard-duration shard-group sharding shell shipyard sidecar singer socket souveraineté-numérique spectre spinnaker spécifications sqlite sri ssh-agent ssl stabilité stash statistique stm32 storage sudo superset suse sympa sysdig syslog-ng sérénité task tavern template terracost terrascan test-unitaire thingspeak tidb tiers time timecale timer timestream training transformation travail trésorerie tsfel tsfr tsl ubuntu unikernel unit ux velero vendredi victoria-metrics vie-privée virtualbox virtualisation vm vnc volume voxxeddays vpc vpn wasm web workflow yaml yield yq yubikey zip