CérénIT

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.

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.

Rien de tel que la finalisation du bilan de cette seconde année d’activité pour faire un petit bilan sur l’année écoulée et les perspectives pour 2019.

Bilan 2018

Au global, tout va bien, tant d’un point de vue comptable que d’activité. L’année a été moins morcelée et compliquée que 2017.

D’un point de vue comptable, cela donne :

2018 2017 Variation
Chiffre d’affaires ~130 K€ ~100 K€ +30%
Résultat après impôts ~10 K€ ~20 K€ -50%
Jours facturés ~190 ~160 +20%
TJM ~685€ 625€ +10%

La baisse du résultat au regard de l’augmentation du chiffre s’explique surtout par une meilleure rémunération.

Comptablement, c’est donc une bonne année, les objectifs de soutenabilité de l’entreprise sont atteints.

J’en profite pour remercier Fabrice et son équipe pour son accompagnement. Je l’ai déjà dit, mais avoir confiance dans son expert comptable et pouvoir compter sur lui pour apporter de bons conseils aux bons moments et être serein sur la gestion de l’entreprise, c’est indispensable.

D’un point de vue activité, c’est aussi une bonne année :

  • Garder du temps pour soi et de ne pas travailler à temps plein est nécessaire pour pouvoir faire autre chose : se former, aller à des conférences (en tant qu’orateur ou participant), etc. La contre-partie de cela étant qu’il faut avoir un nombre de jours facturés suffisant pour amortir les charges et ne pas grignoter sa trésorerie. Heureusement que le second semestre m’a permis de corriger le tir et reconstituer ma trésorerie.
  • Une belle année pour l’hébergement de Compta-online.com avec 13 millions de visites et 23 millions de pages vues et presque pas d’incidents avec une infrastructure optimisée au maximum. Pas d’incidents majeurs mais quelques indisponibilités en période de résultats d’examens. Quelques défis pour 2019 avec une nécessaire mise à jour de la plateforme et diverses évolutions pour une plus grande résilience.
  • Une belle mission chez LesFurets.com, aussi bien d’un point de vue humain que technique. Des sujets intéressants et un cadre propice. Je ne pouvais espérer mieux pour cette année.
  • De belles sollicitations pour des missions ou des recrutements qui permettent d’apprécier mon profil et d’évaluer la demande sur le marché. Néanmoins, je reste focalisé sur le développement de la société et voir jusqu’où je peux mener ma barque.

D’un point de vue contribution à la communauté :

  • Plusieurs présentations autour des séries temporelles et de la plateforme TICK (Telegraf Influxdb Chronograf Kapacitor) et Grafana, à Breizchamp, lors d’un BBL chez LesFurets.com ou encore au JUG Nantes.
  • De contribuer à des Podcasts tels que Big Data Hebdo ou DevObs. J’ai d’ailleurs eu le plaisir en ce début d’année 2019 de devenir un membre permanent de l’équipe du Big Data Hebdo.
  • Quelques contributions de code modestes ici et là.

Dans le cadre du partage de connaissance, ce fut aussi l’occasion de tester un partage de connaissance chez LesFurets.com sous la forme d’un meetup hebdomadaire autour de Docker, Docker-Compose et Swarm auprès des équipes de développement et d’infrastructure en vue d’un passage de relais. L’occasion de prendre le temps de présenter les concepts et leurs applications sur une longue période.

D’un point de vue formation et veille, je me suis rendu aux conférences suivantes :

Coté formation, j’ai pu suivre la formation Kubernetes Fundamentals et la formation Déployer ses applications avec Kubernetes. Pour la certification Certified Kubernetes Administrator, j’ai jusqu’à Novembre 2019 pour la passer…

Perspectives 2019

L’activité jusqu’à fin juin est assurée - n’hésitez pas à me contacter si vous avez des sujets à me proposer pour le second semestre.

Sur les conférences, j’ai décidé de me limiter en terme de nombre de conférences mais d’aller à des conférences où je n’étais pas allé comme KubeCon & CloudNativeCon à Barcelone et la SRECon à Dublin. Peut-être irais-je également à la prochaine DockerCon Euope ?

Voici quelques objectifs que je me suis fixé :

  • Maintenir et développer le coté pérenne de CérénIT - l’entreprise doit pouvoir être en mesure de me payer mon salaire mais avec un rythme soutenable et inversement. Pas de course à la croissance folle / à tout prix mais une évolution raisonnable de l’entreprise. L’objectif est donc d’arriver aux mêmes chiffres et résultats que cette année.
  • Rester positionné sur mes deux grandes activités. D’une part l’architecture et la direction technnique. D’autre part, l’automatisation et l’industrialisation dans une perspective de mise en place de pratiques DevOps/SRE. L’idée de travailler plus sur de l’encadrement d’équipes fait aussi son chemin,
  • Maintenir une contribution aux communautés open source,
  • Si je suis pleinement satisfait du statut d’indépendant, j’aimerai bien travailler sur la notion de réseau d’indépendants pour réduire “l’isolement” et faire jouer des synergies sans pour autant tomber dans une structure trop rigide à laquelle personne n’aspire,
  • Fin 2017, je m’étais intéressé au problème de la diversité dans la tech - en 2018, même si le sujet m’intéresse toujours, aucun progrès sensible n’a été réalisé. Peut-être que 2019 trouvera une forme de contribution à ce sujet.

Si certains sujets vous interpellent ou si vous avez des contacts à me suggérer, n’hésitez pas à me contacter.

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

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 :

# Create a namespace for cert-manager
kubectl create ns cert-manager
# Install cert-manager via helm
helm install \
    --name cert-manager \
    --namespace cert-manager \
    stable/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.

cert-manager/issuer.yml:

apiVersion: certmanager.k8s.io/v1alpha1
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
    http01: {}

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/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.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: 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
        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:
    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:
  - 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 du secret contenant le certificat du site que nous n’avons pas encore créé.

Les annotations permettent de faire une redirection http vers https systématique.

Il ne nous reste plus qu’à créer le certificat via le fichier traefik/certificate.yml

apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: traefik-cert
spec:
  secretName: traefik-cert
  issuerRef:
    name: letsencrypt-prod
  commonName: traefik.k8s.cerenit.fr
  acme:
    config:
    - http01:
        ingressClass: traefik
      domains:
      - traefik.k8s.cerenit.fr

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

Name:         traefik-cert
Namespace:    traefik
Labels:       <none>
Annotations:  <none>
API Version:  certmanager.k8s.io/v1alpha1
Kind:         Certificate
Metadata:
  Creation Timestamp:  2019-01-27T19:49:10Z
  Generation:          1
  Resource Version:    5392945333
  Self Link:           /apis/certmanager.k8s.io/v1alpha1/namespaces/traefik/certificates/traefik-cert
  UID:                 9d7bac06-226c-11e9-8901-daa66ba82679
Spec:
  Acme:
    Config:
      Domains:
        traefik.k8s.cerenit.fr
      Http 01:
        Ingress:
        Ingress Class:  traefik
  Common Name:          traefik.k8s.cerenit.fr
  Issuer Ref:
    Name:       letsencrypt-prod
  Secret Name:  traefik-cert
Status:
  Acme:
    Order:
      URL:  https://acme-v02.api.letsencrypt.org/acme/order/50348094/290143012
  Conditions:
    Last Transition Time:  2019-01-27T19:49:52Z
    Message:               Certificate issued successfully
    Reason:                CertIssued
    Status:                True
    Type:                  Ready
    Last Transition Time:  <nil>
    Message:               Order validated
    Reason:                OrderValidated
    Status:                False
    Type:                  ValidateFailed
Events:
  Type    Reason          Age   From          Message
  ----    ------          ----  ----          -------
  Normal  CreateOrder     58m   cert-manager  Created new ACME order, attempting validation...
  Normal  DomainVerified  58m   cert-manager  Domain "traefik.k8s.cerenit.fr" verified with "http-01" validation
  Normal  IssueCert       58m   cert-manager  Issuing certificate...
  Normal  CertObtained    58m   cert-manager  Obtained certificate from ACME server
  Normal  CertIssued      58m   cert-manager  Certificate issued successfully

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

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 elasticsearch postgres kafka ansible grafana python traefik aws influxdb tick mysql sécurité cloud redis chronograf swarm test cassandra hashicorp microservice ovh serverless spark terraform angularjs cncf confluent container graphql javascript log opensource rancher stream timescaledb timeseries windows api architecture arm csp devops dns docker-compose documentation elastic hpkp iac ingress java kapacitor kibana lambda lean licence microsoft nginx npm orientdb rest rethinkdb reverse-proxy service-mesh sql ssh agile apm azure bash big-data bilan certificat cli cluster continous-delivery cookie cérénit fluxlang gcp gdpr git grav hsts https hypriot istio json ksql kubedb lets-encrypt linux load-balancer machine-learning mobile mongodb monitoring perspective php pip prometheus redhat replication rook rsyslog s3 scale scaleway schema solr sre systemd telegraf vault virtualenv vue.js wagtail yarn accessibilité akka alerte alibaba amazon-emr anonymisation ara automatisation bastion beam beat bounded-context branche brigade browser buildkit cdc cert-manager certificats checklist chrome cloud-init cloud-native cloud-storage clusterip cockroachdb code codeurs-en-seine confluence consul containerd continous-integration coreos cors cqrs crash cron crontab csrf css curl d3.js daemonset dashboard data-pipelining data.gouv.fr dataviz date ddd debezium debian deployment desktop devoxx distributed-systems dive docker-app docker-registry documentdb dokcer draft drop-in déploiement ebs ec2 edge elassandra electron elk engineering etcd event-sourcing facebook falcor feature-policy feed filebeat firebase firefox fish flash flask fleet flink fluentd flux foundation framework frontend fsync fullstack github glacier glowroot google google-cloud-next gpu grid géospatial hacker hadoop haproxy hdfs header helm html html5 http http/3 hue ia iaac ibm immutable incident index influxace influxcloud influxdata influxdays infrastructure-as-code ingénierie inspec jq jquery jwt k3d k3s k8s k9s kubeadm kubecon laravel liste-de-diffusion loadbalancer logstash logstatsh loi mailing-list management mariadb message metallb micro-service molecule mot-de-passe multi-cloud médecine newsletter nodeport nomad nosql null opendata openebs openmetrics openshit openssh openweb operator over-engineering packaging pandas password performance persistent-volume-claim pipenv portainer publicité push pyenv queue quic ram rambleed raml react reaper recaptcha recherche reindex reinvent reliability responsive revocation revue-de-code rkt rolespec root rpi rpo rto runc rwd résilience search secrets select serverless-architecture service-worker sha1 sharding shell shipyard société souveraineté-numérique spinnaker sri ssh-agent ssl statistique superset sympa syslog-ng test-unitaire tidb tiers timer timezone tls training travail ubuntu unikernel unit ux vendredi vie-privée virtualbox virtualisation vitess vm vnc volume voxxeddays vpc vscode warp10 yubikey

Syndication

Atom