CérénIT

Le blog tech de Nicolas Steinmetz (Time Series, IoT, Web, Ops, Data)

Web, Ops & Data - Septembre 2019

kafkatraefikservice-meshmaeshrookcephkuberneteskubectlksqlkafka-streamstelegrafdockerlog

Container et Orchestration

Data

  • Say Hello World to event streaming : Confluent publie des tutoriels sur Kafka, Kafka Streams et KSQL pour illustrer différents cas d'usages avec le code associé. Ils ont l'air assez bien fait.

Time Series

  • Release Announcement: Telegraf 1.12.0 : Telegraf, le collecteur de métriques/logs passent en version 1.12 avec 13 nouveaux plugins dont un pour l'ingestion des logs docker et plein d'autres améliorations. L'autre grande nouveauté est l'arrivée des plugins - il est possible d'ajouter des plugins à Telegrad de façon dynamique ; il n'est plus nécessaire de recompiler telegrad avec ces plugins en son sein.

Web, Ops & Data - Aout 2019

gitlabcicdcontinous integrationcontinous deploymentgitdiffdockerrpitraefikkubernetesovhhelmpostgresperconaawspartiqlredistimeseriesinfluxdbkafkaprometheus

Surveillez le Time Series Paris Meetup, car la première édition du Meetup sera annoncée mardi avec une présentation des usages avancées des séries temporelles avec Warp10 (comprendre au-delà du monitoring classique) et une présentation par les équipes OVH sur du monitoring de datacenter aidé par du machine learning et leur offre Préscience.

CI/CD

  • How to trigger multiple pipelines using GitLab CI/CD : depuis une pipeline d'un dépôt gitlab, il va être possible d'appeler les pipelines des autres projets gitlab. Une fonctionnalité intéressante et qui pourrait lever la dépendance à Jenkins lorsque l'on a des pipelines un peu complexes et inter-projets.
  • New up and coming GitLab CI/CD Features : bilan et perspectives par le responsable produit de gitlab sur les fonctionnalités CI/CD qui ont été rajoutées cette année et celles à venir.

Code

Conteneurs & orchestration

SQL

time series

KubeCon + CloudNativeCon Europe 2019

kuberneteskubeconcncfcloud native

Je me suis rendu à KubeCon + CloudNativeCon Europe 2019 qui s'est tenu à Barcelone du 20 au 23 Mai. C'était la première fois que j'assistais à cette conférence.

Le veille de la conférence officielle, j'ai assisté à la première édition du Continus Delivery Summit organisé par la Continous Delivery Foundation. Différents événements en marge de la conférence officielle sont organisés par la communauté.

Plutôt que de faire un résumé par journée, je vais plutôt faire un résumé global sur ce que je retiens de la conférence.

Tout d'abord un écosystème toujours en ébullition et en pleine évolution :

  • Le village des sponsors était juste gigantesque avec environ 150 sponsors présents.
  • Les plus gros stands étaients ceux des acteurs du cloud et des gros éditeurs (Oracle, Red Hat, Google, Digital Ocean, VMWare, AWS, Azure, Cisco, IBM, etc)
  • Les habitués du secteur : Datadog et d'autres pour le monitoring, Aqua / Neu Vector / Sysdig pour la sécurité, Gitlab / JFrog / CloudBees pour la partie usine logicielle, etc.
  • Une multitude de startups présentant leur produit
  • Des entreprises utilisatrices des technologies CNCF étaient présentes comme Adidas, CookPad ou (la sulfureuse) Palantir - ce ne sont pas ces entreprises que l'on s'attend à voir dans une telle conférence
  • Un stand qui m'a étonné, c'est la petitesse du stand de Docker - s'il est pourtant un sponsor Gold de l'événement, le stand était aussi grand que celui de n'importe quelle startup. On sent vraiment que la mode est passé et que ce n'est plus Docker qui dirige l'écosystème alors que sa technologie, pourtant centrale, est devenue une commodité.

Sur les produits qui ont retenu mon attention :

  • Vitess : Vitess rend MySQL cloud native au sens qu'il gère nativement la réplication, le sharding, la bascule en cas de perte du master, etc.
  • Rook : la solution cloud native de stockage au dessus de Ceph principalement mais pas uniquement.
  • OpenEBS : une solution un peu plus universelle et complète que Rook pour gérer son stockage. Le projet vient de rejoindre la CNCF.
  • Loki : la solution d'ingestion de log de Grafana Labs et qui relie vos logs avec les méta données de prometheus.
  • Jenkins X : Si vous pensiez qu'il ne s'agissait que de Jenkins sur Kubernetes comme moi, vous vous trompiez - c'est une plateforme complète et "opinionated" de gestion de build et de déploiement en s'appuyant sur Kubernetes, Helm, Monocular, Skaffold, Tekton pour la couche logicielle et sur GitOps pour la partie méthodologie/workflow, Le moteur Jenkins n'est d'ailleurs pas forcément présent, il est possible d'utiliser Tekton pour exécuter les pipelines.
  • Tekton : l'outil permet de décrire et exécuter des pipelines dans un contexte Kubernetes. Via les CRD, des objets de type Step, Task et Pipeline sont mis à disposition dans Kubernetes. En les assemblant, on peut alors décrire notre pipeline de bout en bout. Des objets complémentaires permettent de décrire les ressources/propriétés des Pipeline et d'autres de suivre leur exécution.
  • BuildKit : le nouvel outil de création d'images Docker semble vraiment très performant et propose des options intéressantes.
  • Bazel : un outil de build permettant de faire des builds reproductibles.
  • Telepresence : une sorte de proxy bi-directionnel permettant de voir des ressources distantes de votre cluster kubernetes comme des ressources locales et inversement. Pratique pour le développement et le debug.
  • Kind : KinD pour Kubernetes in Docker vous permet de faire tourner un cluster kubernetes dans du docker sur votre poste de développement par ex.
  • Open Policy Agent : un framework de gouvernance et de validation des règles de votre cluster kubrenetes.

Au niveau des buzz word, même si le Service Mesh a encore de bons restes, les buzz word 2019 semblent être Operator, Fédération (communication inter clusters) et la sécurité (Open Policy Agent)

Sur les conférences en elles-mêmes, j'avoue être assez mitigé, voire déçu sur la qualité de nombreux talks. Le format 30mn y est peut être pour quelque chose mais n'explique pas tout - peut être que la conférence voulait surtout permettre à des profils plus jeunes de découvrir cet univers plutôt que de fournir des conférences plus riches. Je suis peut être tombé sur les mauvaises...

Néanmoins, celles que j'ai eu plaisir à voir - la playlist est déjà diponible.

Je recommande aussi la visualisation des vidéos de Keynote pour avoir une vision générale de l'écosystème, des retours d'expérience divers et sur la valorisation de la communauté et de la diversité. Ces deux sujets sont un véritable enjeux pour la pérénité des différents projets. D'ailleurs, on sent que le sujet de la diversité est important avec une petite moitié des keynotes présentée par des femmes. L'animation était aussi répartie entre un homme et une femme, de nombreuses conférences étaient présentées par des femmes. Je crois même au final qu'il s'agit de la conférence où j'ai vu le plus de femmes. C'est une bonne chose !

Sur la partie logistique, c'est très bien organisé - rien à redire de ces trois jours, c'est un véritable tour de force de savoir gérer aussi bien la venue de 7700 personnes.

Enfin, ce fut l'occasion de (re)voir et rencontrer des membres de la communauté (française) et de passer de bons moments en leur compagnie. Une mention spéciale pour l'équipe OVH avec qui j'ai passé une superbe semaine (et indépendamment des goodies et vouchers que j'ai pu obtenir).

Web, Ops & Data - Avril 2019

influxdbtimescaledbtraefikkubernetesssh-agentpostgresrecherchedockerloggoogle cloud nextserverlessapmglowrootdocker registry

Deux petites annonces pour démarrer cette édition :

  • Je serai à KubeCon EU du 20 au 23 Mai à Barcelone. Si vous y allez aussi, dites le moi, ce sera une occasion de se rencontrer.
  • Le BigData Hebdo a ouvert son slack - Vous pouvez nous rejoindre par vous même via ce lien

APM

  • Glowroot : Pour ceux qui s'intéressent au sujet de l'APM et qui ne veulent pas aller chez AppDynamics, Dynatrace ou Elastic, j'ai assisté à une démo intéressante sur Glowroot - il est forcément moins riche que ces concurrents mais il a l'air de faire l'essentiel de ce que l'on peut attendre d'un APM. Il ne marche qu'avec la JVM.

Cloud

Container et Orchestration

DevOps

  • JSON as configuration files: please don’t : Si certains pensaient utiliser JSON pour décrire des fichiers de configurations, l'article rappelle que JSON n'est qu'un format d'échange de données et surtout pas de fichiers de configuration. On peut comprendre la tentation mais on a déjà bien assez à faire avec YAML, INI voire XML. Aucun n'est parfait certes mais pas la peine d'en rajouter.
  • In Defense of YAML : L'auteur critique l'abus autour de YAML pour l'utiliser pour tout et n'importe quoi. Comme format de données, il est utilisable mais nous voyons des détournements où du yaml devient du pseudo code. L'auteur cite la CI Gitlab ou encore Tekton. On ne peut que lui donner raison. Il serait plus simpe d'avoir un vrai langage de programmation plutôt que de tout "YAMLiser".

Licences

  • Deprecation Notice: MIT and BSD (via Les Cast Codeurs) : Intéressant, les licences BSD/MIT serait à considérer comme dépréciée. L'auteur travaille pour le Blue Oak Council qui publie la licence du même nom. On peut éventuellement lui reprocher un certain biais mais il indique quand même que des licences modernes (comme ASL 2.0) seraient plus judicieuses que de rester sur du MIT/BSD.

Sécurité

SQL

Timeseries

Astuce du mois : gestion de la rotation des logs d'un container docker

Dans les bonnes pratiques Docker, il est dit d'utliser stdout/stderr pour avoir les logs de votre conteneur via docker logs. Toutefois, cette pratique va alimenter un fichier de log /var/lib/docker/containers/<container id>/<conteiner id>-json.log. Ce fichier peut donc saturer votre disque et aller jusqu'à corrompre vos conteneurs. L'autre bonne pratique étant que tout fichier de log doit avoir une politique de rotation du fichier associée pour éviter toute saturation de disque ou d'avoir des trop gros fichiers de logs.

Docker permet de configurer le driver de logs au niveau du démon (via /etc/docker/daemon.json), en argument lors d'un docker run ou dans docker-compose.yml.

Si l'on reste sur le driver json-file et que l'on veut piloter la rotation des logs au niveau de docker-compose.yml, cela donne par ex (version simplifiée) :

version: '3'
services:
  service_xxx:
    image: docker_image_xxx
    [...]
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "10"

Vous pouvez alors définir une stratégie de rotation des logs par container si vous le souhaitez. Ainsi, vous gérer la taille maximale de logs qui vont être générés et êtes ainsi assurés de ne pas avoir de mauvaises surprises à ce niveau là.

Web, Ops & Data - Mars 2019

continous deliverycontinous integrationcloudopensource*scalewaygpuawsrediselasticsearchgrafanakubernetestraefikk3sk3dk9s

CI/CD

  • Introducing the Continuous Delivery Foundation, the new home for Tekton, Jenkins, Jenkins X and Spinnaker : Google, Gitlab, CloudBees et bien d'autres acteurs du monde de la CI/CD lancent la fondation "Countinuous Delivery". Au delà des projets qui y seront hébergés (en commençant par Jenkins, Jenkins X, Tekton et Spinnaker), la fondation a pour but de prendre en charge l'ensemble du cycle de développement/déploiement d'une application et surtout de favoriser les bonnes pratiques associées. La fondation fera son premier "CDF Summit" la veille de KubeCon Barcelone (où j'aurais le plaisir de me rendre).

Cloud / Scaleway

Cloud vs OSS

Nous en avons beaucoup parlé dans l'épisode 69 de BigData Hebdo - je mets juste les liens et vous renvoie à notre discussion sur le sujet.

Conteneurs et orchestration

  • Red Hat Enterprise Linux 8 Beta: A new set of container tools Intéressant RHEL 8 ne contiendra plus docker mais les alternatives comme podman, buildah et skopeo.
  • Goodbye Docker and Thanks for all the Fish : Le titre est un peu provoc mais l'analyse est bonne du fait de l'évolution de l'écosystème des conteneurs et des techno associées. La modularisation de docker a permis de construire des outils plus spécialisés - reste que le package docker fourni pour le moment une expérience plus agréable et de bout en bout.
  • If You K8s, Please Try K9s… : k9s est un petit outil qui permet d'avoir une vision des ressources d'un cluster kubernetes.
  • k3s.io : une distribution de kubernetes amincie, éditée par les gens de Rancher. Elle n'en est pas moins une distribution certifiée. Cette distribution a fait le choix de supprimer des fonctionnalités non essentielles pour en arriver à un binaire de 40 Mo et un pré-requis de 512 Mo de RAM. Idéal pour des scénarios d'IoT, d'edge computing ou plu simplement pour se former à kubernetes en utilisant vos raspberry pi !
  • k3d - A fast kubernetes dev environment : k3d est un script qui se base sur k3s mais le déploie dans du docker. L'idée est alors d'avoir un mini cluster kubernetes pour tester ses développements. Si le challenge technique peut être intéressant, déployer un environnement via minikube ou un petit cluster kubernetes sur des vms pour du test est peut être plus judicieux...
  • Back to Traefik 2.0 : l'équipe Containous dévoile les nouveautés de Traefik 2.0 : proxy tcp (et plus uniquement http), meilleure intégration kubernetes avec une CRD et plein d'autres choses.
  • 'Cloud Native DevOps with Kubernetes' is published! : le livre sur les applications cloud native dans un contexte kubernetes et dont j'ai eu le plaisir de participer à la relecture est (enfin) sorti. Son principal intérêt pour moi est une bonne présentation des concepts de Kubernetes et surtout son retour d'exéprience pragmatique sur les bonnes pratiques autour de kubernetes. Je le recommande vivement !

Data

  • La veille techno dans la data : si vous vous intéressez à la data, un billet utile qui vous donne des nombreuses sources pour alimenter votre veille : newsletters, podcasts, conférence, blogs, etc.
  • Grafana v6.0 Released : La solution de dataviz opensource Grafana sort en version 6 avec comme nouveautés principale un explorateur de données et leur intégration de loki leur nouvelle solution d'ingestion de logs prometheus.

Web, Ops & Data - Février 2019

kubernetestraefikpostgrespandaspythondockerruncoperatoransiblevitesstidbshardingtimeseriesovhkubedbfsyncovhdns

Container et orchestration

  • The Journey to Traefik Enterprise Edition: Product Evaluation
  • Docker Security Update: CVE-2019-5736 and Container Security Best Practices : Vous avez sans doute tous entendus parlé de la faille de runc, sous le doux nom de CVE-2019-5736. Le billet indique qu'il faut utiliser une version 18.06.2+ pour Docker CE et rappelle quelques bonnes pratiques de gestion de containers. Il n'y a pas que les serveurs à mettre à jour, il y a aussi les postes de développeurs, tout aussi exposés.
  • rancher/runc-cve : Pour les gens qui ne peuvent pas mettre à jour le binaire docker, l'équipe de Rancher met à disposition des versions du binaire runc pour les versions depuis docker 1.12.6 jusqu'à 18.06.1
  • CVE 2019-5736 dans runC : l'article indique une façon d'exploiter la faille de RunC.
  • Ansible Operator: What is it? Why it Matters? What can you do with it? : Ansible ne fournit pas un "oprator" en tant que tel pour Kubernetes mais de quoi permettre de créer un operator en se basant sur des playbooks/roles ansible. Ainsi, si votre ressource change d'état par exemple, alors le playbook associé est joué. Idem pour la gestion d'un upgrade, etc. Cela s'inscrit dans la logique de pouvoir développer ses propres Operator sans avoir à les écrire en Go.
  • Mastering the KUBECONFIG file : différentes astuces autour de la gestion du fichier KUBECONFIG.
  • KubeDB : KubeDB est un operator kubernetes qui vise à pouvoir déployer et gérer différentes bases sur un cluster kubernetes. Les bases supportées sont MySQL, Postgres, Elasticsearch, Redis, MongoDB et Memcached. Le niveau de fonctionnalités dépend beaucoup de la base de données retenus (la réplication semble être gérée pour Postgres mais pas pour MySQL par ex). La version 0.10 vient de sortir, apportant le support du cluster Redis
  • Managed Kubernetes Service : OVH vient de lancer son offre kubernetes managé et pour l'utiliser depuis deux mois maintenant, elle fonctionne plutôt bien.

DNS

(No)SQL

  • Contrainte d'exclusion : nous connaissons tous les contraintes d'unicité mais parfois cela ne suffit pas. L'exmple montre comment mettre en place une contrainte d'exclusion sur la base de filtre de plage de réseaux : 192.168.122.0/28 est compris dans 192.168.122.0/24, donc si le 2nd est entré dans la base, le 1er ne pourra jamais être ajouté car il y a recouvrement. On retrouve un autre exemple de cette contrainte d'exclusion sur des dates dans l'astuce de la semaine de l'édition 289 de Posrgres Weekly.
  • Understanding Database Sharding : un billet très explicite sur le partitionnement (sharding) de base de données, pourquoi et comment le faire. Il rappelle aussi les inconvénients à le faire et ce qu'il vaut mieux faire avant d'en arriver au sharding.
  • TiDB: Distributed NewSQL with Kevin Xu : TIDB est une base qui se déploie sur Kubernetes et qui s'appuie sur RocksDB. Elle se veut "NewSQL" dans le sens où elle veut supporter à la fois des transactions et de l'analytique. Elle veut offrir notamment un support de MySQL mais dans les faits, le support reste encore limité. Pour ceux qui veulemnt déployer du MySQL sur Kubernetes avec du sharding, il vaut mieux aller voir du coté de Vitess
  • Farewell to fsync(): 10× faster database tests with Docker : alors que l'actualité était plutôt sur le fait que Postgres gérait mieux les erreurs lors d'un fsync(), l'astuce consiste ici à désactiver fsync() et/ou à mettre le dossier des données de votre base en RAM pour accélérer les temps de déroiulement de tests. Testé chez un client, c'est un gain d'au moins 20s qui a été constaté sur une opération de quelques minutes (< 5).

Timeseries

  • Tutorial: Time Series Analysis with Pandas : un tutoriel assez progressif et didactique sur la manipulation de données temporelles avec Pandas.
  • TSL: a developer-friendly Time Series query language for all our metrics : L'équipe d'OVH Metrics a crée son propre langage de requêtage orienté séries temporelles pour Prometheus et Warp10. Le billet raconte leur épopée dans le monde des base de données temporelles et comment ils en sont arrivés à créer TSL. On retrouve une syntaxe fonctionnelle et qui se retrouve assez proche de celle de Flux, qui lui supporte InfluxDB et Prometheus.

Web, Ops & Data - Janvier 2019

machine learningpythonrecaptchaflinkalibabacloudmongodbawsdocumentdbpostgrestestiackubernetesingressclusteriploadbalancervolumepersistent volume claimnodeportlogstashpythonpipvirtualenvpipenvpyenv

Cloud

Container et orchestration

  • APIServer dry-run and kubectl diff : Un des soucis majeurs avec Kubernetes est l'écriture de fichiers YAML où la moindre faute peut s'insérer très rapidement et à l'insu de son auteur. Le billet présente les efforts fait pour ajouter un mode "dry run" qui simule les modifications et retourne l'objet qui aurait du être créé. Dans la même veine, un kubectl diff montrera les différences entre la ressource existante et celle décrite dans la nouvelle version du fichier yaml.
  • 9 Kubernetes Security Best Practices Everyone Must Follow : rien de transcendental mais une petite piqure de rappel après la faille majeure découverte en fin d'année.
  • Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what? : billet synthétique sur les avantages et inconvénients d'utiliser un service de type ClusterIP, NodePort, LoadBalancer ou Ingress. Sachant que l'on peut combiner LoadBalancer & Ingress !.
  • Why Is Storage On Kubernetes So Hard? : Les données, c'est tout sauf stateless et le stockage distribué c'est pas facile non plus. Le billet revient sur les logiques de stockages sous Kubernetes (PV, PVC), la couche d'interface de stockage CSI et sur des solutions comme Ceph ou Rook.
  • Stateful Kubernetes with Saad Ali - Software Engineering Daily : une présentation globale des Volumes, Persistent Volume, Persistent Volume Claims et des StorageClass sous Kubernetes et de l'évolution de la gestion du stockage sous k8s
  • Kubernetes Podcast - #36 Rook : une présentation de Rook, un opérateur k8s de gestion de stockage (Ceph, NFS, etc).

Data

IDE

Infrastructure (as Code)

  • Tester son code d’infrastructure avec Terratest : le billet présente terratest, un outil en go qui permet de tester du code Terraform, des templates Packer ou encore des images Docker. La conclusion montre qu'il n'est pas parfait certes mais peut être intéressant.
  • Infrastructure as (real) code : Faire de l'IaC, ce n'est pas que rédiger des fichiers YAML. Le billet montre comment on pourrait avoir de l'IaC avec du vrai code (du go en l'occurence). Avoir un vrai langage et un moteur de template semble en effet plus complet que juste du YAML pour lequel les validateurs sont assez faibles et la probabilité d'écrire une faute assez importante.
  • Reactive planning is a cloud native pattern : Le reactive planning tiendrait dans l'idée que pour une action donnée, il va y avoir un plan et que ce plan est constitué d'une multitude de petites étapes. Chaque étape informant la/les précédentes et voire globalement sur l'état de l'étape en cours et peut décider des étapes suivantes.

Langages

  • Why you should use pyenv + Pipenv for your Python projects : Une solution propre pour mieux gérer ses versions de python installées sur son poste / sur un serveur avec pyenv et pipenv (mix de pip et virtualenv) pour gérer les dépendances. A tester !
  • Pipenv: promises a lot, delivers very little : le billet nuance les propos autour de pipenv comme le nouveau gestionnaire officiel (autopromu) et fait le point sur l'outil.
  • shiv : Shiv permet de packager des applications python en une seule archive zip avec toutes les dépendances incluses. Disponible pour Windows / Linux / OSX, il faut néanmoins builder sur l'OS Cible pour que cela fonctionne - pas de "build one, run everywhere".

Logs

(No)SQL

Kubernetes @ OVH - Traefik et Cert Manager pour le stockage des certificats en secrets

kubernetestraefikovhsecretscert-manager

L'objectif est de s'appuyer sur Cert-Manager pour la génération et le stockage des certificats Let's Encrypt qui seront utilisés par Traefik. L'idée est de stocker ces certificats sous la forme de secrets et de ne plus avoir à provisionner un volume pour les stocker.

Installons déjà cert-manager :

# Install the CustomResourceDefinition resources separately
kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.11/deploy/manifests/00-crds.yaml

# Create the namespace for cert-manager
kubectl create namespace cert-manager

# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io

# Update your local Helm chart repository cache
helm repo update

# Install the cert-manager Helm chart
helm install \
  --name cert-manager \
  --namespace cert-manager \
  --version v0.11.0 \
  jetstack/cert-manager

Nous allons ensuite devoir créer un Issuer dans chaque namespace pour avoir un générateur de certificats propre à chaque namespace. Cela est notamment du au fait que Traefik s'attend à ce que le secret et l'ingress utilisant ce secret soient dans le même namespace. Nous spécifions également que nous utiliserons traefik comme ingress pour la génération des certificats.

cert-manager/issuer.yml:

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: user@example.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prod
    # Enable HTTP01 validations
    solvers:
    - selector: {}
      http01:
        ingress:
          class: traefik

Puis créons le "issuer" dans la/les namespace(s) voulu(s) :

# Create issuer in a given namespace
kubectl create -n <namespace> -f issuer.yml

Notre contexte de déploiement utilisant Traefik comme ingress, je remets ci-dessous la configuration que j'utilise avec les ajustements nécessaires pour l'utilisation de cert-manager. Il n'est en effet plus possible et il devient désormais inutile de déclarer la section "acme" dans traefik.toml. J'ai aussi supprimé la redirect automatique http vers https, il faudra la gérer au niveau des ingress.

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/v1
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
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
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
---
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.https]
        address = ":443"
        [entryPoints.https.tls]
      [entryPoints.api]
        address = ":8080"

    [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é.

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

---
kind: Deployment
apiVersion: apps/v1
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.16
        name: traefik-ingress-lb
        volumeMounts:
        - mountPath: /config
          name: traefik-config
        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

Nous déployons donc :

  • Traefik en Deployment
  • Les ports 80, 443 et 8080 sont définis
  • La configuration est une ConfigMap

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:
  annotations:
    kubernetes.io/ingress.class: traefik
    cert-manager.io/issuer: letsencrypt-prod
    traefik.ingress.kubernetes.io/redirect-entry-point: https
    traefik.ingress.kubernetes.io/redirect-permanent: "true"
    ingress.kubernetes.io/ssl-redirect: "true"
    ingress.kubernetes.io/ssl-temporary-redirect: "false"
  name: traefik-web-ui
spec:
  rules:
  - host: traefik.k8s.cerenit.fr
    http:
      paths:
      - path: /
        backend:
          serviceName: traefik-ingress-service-clusterip
          servicePort: admin
  tls:
  - hosts:
    - traefik.k8s.cerenit.fr
    secretName: traefik-cert

L'idée est donc de rentre le dashboard accessible via l'url traefik.k8s.cerenit.fr.

La section tls de l'ingress indique le nom d'hôte pour lequel le certificat va être disponible et le nom du secret contenant le certificat du site que nous n'avons pas encore créé.

Les annotations permettent :

  • de déclarer le type d'ingress à utiliser ; ici: traefik
  • de déclarer que le certificat qui doit être fourni par cert-manager est un certificat de type Let's Encrypt
  • de faire une redirection http vers https systématique.

Les deux premières annotations permettent de ne pas avoir à déclarer soi même le certificat - il est automatiquement généré via ingress-shim. Cela vous fait donc un objet kubernetes en moins à gérer dans votre configuration. Si vous ne souhaitez pas vous appuyer sur ce méchanisme d'ingress-shim, il vous faudra ne pas utiliser ces annotations et gérer vous même un objet "Certificate".

Il ne reste plus qu'à faire pour instancier le tout :

kubectl create -f traefik/

Pour la génération du certificat, il conviendra de vérifier la sortie de

kubectl describe certificate traefik-cert

Et voilà - maintenant que le problème des certificats est corrigé, je vais pouvoir passer dans un contexte de déploiement multi-nodes.

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

kubernetestraefikovhdeploymentload-balanceringress

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.

Kubernetes @ OVH - Traefik en DaemonSets

kubernetestraefikovhdaemonset

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.

← Précédent 3 / 6 Suivant →