Utiliser Drone et Traefik sur Kubernetes pour déployer une application de base

Drone est un système de livraison continue basé sur la technologie des conteneurs. Drone utilise un simple fichier de configuration YAML, un sur-ensemble de docker-compose, pour définir et exécuter des pipelines dans des conteneurs Docker.

Cela semble génial, un CD construit avec des conteneurs à l’esprit. Drone est écrit en Go, prend en charge de nombreux plugins et est très facile à configurer.

Si vous ne connaissez pas Traefik, vous pouvez consulter mes précédents messages.

Nous allons déployer Drone sur un cluster Kubernetes, ici en utilisant Google Cloud Platform (vous pouvez obtenir des crédits gratuits pendant un an). Vous pouvez choisir le fournisseur que vous préférez, mais les instructions peuvent changer.

Création du cluster

Donc, la première chose à faire, nous avons besoin d’un cluster actif. Avec Google Kubernetes Engine, vous pouvez le faire avec une simple commande. Je suppose que vous avez gcloud installé et un compte Google Cloud actif, sinon consultez la documentation ici.
Pour créer le cluster, il suffit de taper:

$ gcloud container clusters create  --cluster-version=1.8.3-gke.0
Creating cluster ...done.                                      
Created (https://container.googleapis.com/v1/projects//zones//clusters/).
kubeconfig entry generated for .
NAME  LOCATION        MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
    1.8.3-gke.0       n1-standard-1  1.8.3-gke.0   3          RUNNING

Nous allons utiliser Kubernetes 1.8+ alors assurez-vous d’avoir le bon kubectl version.

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"8", GitVersion:"v1.8.3+f0efb3cb88375", GitCommit:"f0efb3cb883751c5ffdbe6d515f3cb4fbe7b7acd", GitTreeState:"not a git tree", BuildDate:"2017-11-09T12:38:03Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"8+", GitVersion:"v1.8.3-gke.0", GitCommit:"86d3ac5eaf57223302c95e7d9fc1aeff55fb0c15", GitTreeState:"clean", BuildDate:"2017-11-08T21:42:58Z", GoVersion:"go1.8.3b4", Compiler:"gc", Platform:"linux/amd64"}

Utiliser Helm pour installer Traefik

Pour utiliser Traefik avec Let's Encrypt, le moyen le plus simple de l'installer consiste à utiliser Helm: le gestionnaire de paquets Kubernetes. Vous pouvez l'installer à partir d'ici.

Une fois Helm installé, vous pouvez exécuter helm init. Il installera Tiller (le service côté cluster) sur votre cluster.
Si vous avez plusieurs clusters, vérifiez que kubectl config current-context renvoie le cluster voulu, sinon vous pouvez utiliser le --kube-context option de helm pour l'installer sur un autre.

Créons notre configuration de graphique Traefik. Voici mon traefik-helm.yml

imageTag: 1.5                                    
ssl:
    enabled: true
    enforced: true
acme:
    enabled: true
    email: ""
    staging: false
    persistence.enabled: true
dashboard:
    enabled: true
    domain: ""

imageTag nous permet de spécifier la version de Traefik, ici la plus récente. ssl.enabled, ainsi, permet HTTPS tout en ssl.enforced active la redirection HTTP vers HTTPS.
Ensuite, le acme Cette section traite de la génération de certificats Let's Encrypt. La dernière section indique si vous souhaitez configurer le tableau de bord de Traefik. Vous trouverez ici d'autres options de configuration, telles que les types de volume pour le stockage persistant ou la connexion à Prometheus. De plus, je ne l’ai pas précisé, mais par défaut, le replicas compte sera un, ce qui est la meilleure option lors de l’utilisation de Let's Encrypt pour le moment.

Nous sommes maintenant prêts à courir:

$ helm install --namespace kube-system --name traefik --values traefik-helm.yml stable/traefik
Error: release traefik failed: namespaces "kube-system" is forbidden: User "system:serviceaccount:kube-system:default" cannot get namespaces in the namespace "kube-system": Unknown user "system:serviceaccount:kube-system:default"

Donc, cela n'a pas fonctionné comme prévu. Nous devons ajouter un ClusterRoleBinding pour ajouter le rôle cluster-admin au ServiceAccount kube-system:default

$ kubectl create clusterrolebinding add-on-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default
clusterrolebinding "add-on-cluster-admin" created

Maintenant, si nous réessayons:

$helm install --namespace kube-system --name traefik --values traefik-helm.yml stable/traefik                                                  
NAME:   traefik                                                                                                                                 
LAST DEPLOYED: Sat Dec 16 11:05:39 2017                                                                                                         
NAMESPACE: kube-system                                                                                                                          
STATUS: DEPLOYED                                                                                                                                
                                                                                                                                                
RESOURCES:                                                                                                                                      
==> v1/ConfigMap                                                                                                                                
NAME             DATA  AGE                                                                                                                      
traefik-traefik  1     1s                                                                                                                       
                                                                                                                                                
==> v1/PersistentVolumeClaim                                                                                                                    
NAME                  STATUS   VOLUME    CAPACITY  ACCESS MODES  STORAGECLASS  AGE                                                              
traefik-traefik-acme  Pending  standard  1s

==> v1/Service
NAME                       TYPE          CLUSTER-IP     EXTERNAL-IP  PORT(S)                     AGE
traefik-traefik-dashboard  ClusterIP     10.43.254.78          80/TCP                      1s
traefik-traefik            LoadBalancer  10.43.249.218      80:30922/TCP,443:30670/TCP  1s

==> v1beta1/Deployment
NAME             DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
traefik-traefik  1        1        1           0          1s

==> v1beta1/Ingress
NAME                       HOSTS        ADDRESS  PORTS  AGE
traefik-traefik-dashboard    80       1s

==> v1/Pod(related)
NAME                              READY  STATUS   RESTARTS  AGE
traefik-traefik-6fc99c65d7-qm44j  0/1    Pending  0         1s

==> v1/Secret
NAME                          TYPE    DATA  AGE
traefik-traefik-default-cert  Opaque  2     1s

NOTES:

1. Get Traefik's load balancer IP/hostname:

     NOTE: It may take a few minutes for this to become available.

     You can watch the status by running:

         $ kubectl get svc traefik-traefik --namespace
kube-system -w

     Once 'EXTERNAL-IP' is no longer '':

         $ kubectl describe svc traefik-traefik --namespace kube-system | grep Ingress | awk '{print $3}'

2. Configure DNS records corresponding to Kubernetes ingress resources to point to the load balancer IP/hostname found in step 1

Ça a marché! Nous pouvons obtenir l'adresse IP externe pointant sur Traefik avec cette commande (vous devrez peut-être attendre quelques secondes):

$ kubectl describe svc traefik-traefik --namespace kube-system | grep Ingress | awk '{print $3}'
35.198.124.40

Nous devons maintenant modifier nos enregistrements DNS pour qu'ils correspondent à l'URL de notre tableau de bord avec cette adresse IP. Supposons que vous ayez le domaine exemple.com: vous aurez besoin d'un enregistrement A de cette adresse IP vers dashboard.example.com, d'un enregistrement A pour drone.exemple.com et d'un dernier pour app.example.com. Voici ce que je reçois en accédant au tableau de bord:
Tableau de bord de Traefik

Tester Traefik avec un exemple

Maintenant que Traefik est opérationnel et avant de commencer à utiliser Drone, nous allons simplement déployer une application simple pour vérifier si Traefik fonctionne comme prévu.
Voici mon basic-deployment.yml:

apiVersion: apps/v1beta2                              
kind: Deployment
metadata:
    name: nginx
spec:
    selector:
        matchLabels:
            app: nginx
    replicas: 2
    template:
        metadata:
            labels:
                app: nginx
        spec:
            containers:
              - name: nginx
                image: stenote/nginx-hostname:latest
---
apiVersion: v1
kind: Service
metadata:
    name: nginx
    labels:
        apps: nginx
spec:
    selector:
      app: nginx
    ports:
      - protocol: TCP
        port: 80
        name: http

Il créera deux répliques d'un serveur NGINX de base qui imprimera le nom d'hôte.
Nous l'appliquons avec:

$ kubectl apply -f basic-deployment.yml          
deployment "nginx" created
service "nginx" created

Nous devons maintenant créer une entrée pour dire à Traefik d’utiliser ce service comme point de terminaison de la app.example.com URL Voici mon basic-ingress.yml:

apiVersion: extensions/v1beta1                        
kind: Ingress
metadata:
    name: drone
    annotations:
        kubernetes.io/ingress.class: traefik
spec:
    rules:
      - host: ""
        http:
            paths:
              - path: /
                backend: 
                    serviceName: nginx
                    servicePort: http

Et on l'applique.

$ kubectl apply -f basic-ingress.yml                  
ingress "drone" created

Si nous visitons et actualisons plusieurs fois cette URL, nous pouvons voir que le nom d'hôte est en train de changer. Traefik fonctionne et redirige correctement vers l’une des deux répliques créées, parfait!

Déploiement et configuration de Drone

Traefik fonctionne donc passons à Drone. Je vais configurer Drone avec GitHub, mais vous pouvez le faire avec GitLab, Gogs, Bitbucket, … Seules les variables d'environnement vont changer. Vous pouvez trouver les instructions ici.

Configurer un GitHub OAuth

Vous devez aller dans GitHub> Paramètres> Paramètres du développeur> Applications OAuth et cliquer sur New OAuth App. Vous devez remplir le formulaire comme ceci (en remplaçant https://drone.foo.com avec votre URL):
Page de l'application GitHub OAuth

Laissez cette page ouverte car nous aurons bientôt besoin du client et de la clé secrète.

Déploiement de drone

Nous déploierons Drone sur le cluster K8s, je regrouperai tous les objets Kubernetes nécessaires dans un fichier YAML, mais vous pourrez bien entendu le scinder en plusieurs fichiers. Voici mon drone-k8s.yml.

apiVersion: apps/v1beta2                               
kind: Deployment                                       
metadata:                                              
    name: drone                                        
spec:                                                  
    selector:                                          
        matchLabels:                                   
            app: drone                                 
    replicas: 1                                        
    template:                                          
        metadata:                                      
            labels:                                    
                app: drone                             
        spec:                                          
            containers:                                
              - name: drone                              
                image: drone/drone:0.8                   
                env:
                  - name: DRONE_OPEN                       
                    value: "false"                         
                  - name: DRONE_ADMIN                      
                    value: ""                         
                  - name: DRONE_HOST                       
                    value: ""         
                  - name: DRONE_GITHUB                     
                    value: "true"                          
                  - name: DRONE_GITHUB_CLIENT              
                    value: ""          
                  - name: DRONE_GITHUB_SECRET              
                    value: ""
                  - name: DRONE_SECRET
                    value: ""
                ports:
                - containerPort: 8000
                  name: drone-web-port
                - containerPort: 9000
                volumeMounts:
                - mountPath: /var/lib/drone
                  name: drone-db
            volumes:
            - name: drone-db
              hostPath:
                path: /var/lib/drone
---
apiVersion: v1 
kind: Service                                          
metadata:                                              
    name: drone                                        
    labels:                                            
        apps: drone                                    
spec:                                                  
    ports:                                             
    - name: http                                       
      port: 80                                         
      targetPort: drone-web-port                       
      protocol: TCP                                    
    - name: agent                                      
      port: 9000                                       
    selector:                                          
      app: drone                                       
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
    name: drone-agent
spec:
    selector:
        matchLabels:
            app: drone-agent
    replicas: 1
    template:
        metadata:
            labels:
                app: drone-agent
        spec:
            containers:
              - name: drone-agent
                image: drone/agent:0.8
                args: ("agent")
                env:
                  - name: DRONE_SERVER
                    value: "drone:9000"
                  - name: DRONE_SECRET
                    value: ""
                volumeMounts:
                  - mountPath: /var/run/docker.sock
                    name: docker-socket
            volumes:
              - name: docker-socket
                hostPath:
                    path: /var/run/docker.sock
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
    name: drone
    annotations:
        kubernetes.io/ingress.class: traefik
spec:
    rules:
      - host: ""
        http:
            paths:
              - path: /
                backend:
                    serviceName: drone
                    servicePort: http

Nous avons un déploiement, un service pour le serveur de Drone et un agent de déploiement pour Drone. Nous n'avons besoin d'aucun service pour l'agent car il n'expose aucun port. Enfin, nous avons une entrée pour Traefik.

Ok, alors maintenant appliquons-le!

$ kubectl apply -f drone-k8s.yml
deployment "drone" created
service "drone" created
deployment "drone-agent" created
ingress "drone" configured

Et, ta-da, tu peux accéder à Drone à l'URL de ton choix!

Utilisation de drone

Maintenant que Drone fonctionne, je vais vous montrer comment l'utiliser. Drone va créer le fichier Docker de mon référentiel choisi, le télécharger sur Dockerhub, étiqueté avec le hachage du commit, et mettre à jour le déploiement sur Kubernetes avec la nouvelle image de téléchargement.

Tout d'abord, créons le .drone.yml dans le repo que vous voulez construire.

pipeline:                                             
    docker:
        image: plugins/docker
        repo: /
        tags: ${DRONE_COMMIT_SHA:0:8}
        username: 
        secrets: ( docker_password ) 
    deploy:
        image: quay.io/honestbee/drone-kubernetes
        kubernetes_server: ${KUBERNETES_SERVER}
        kubernetes_cert: ${KUBERNETES_CERT}
        kubernetes_token: ${KUBERNETES_TOKEN}
        deployment: 
        repo: /
        container: 
        tag:
            - ${DRONE_COMMIT_SHA:0:8}

Ce fichier utilise deux plugins: un pour pousser vers Dockerhub et un pour mettre à jour le déploiement.

Pour que cela fonctionne, nous devons ajouter les bons secrets.
Vous devrez installer le CLI Drone. Sous Linux, il vous suffit d'entrer:

$ curl -L https://github.com/drone/drone-cli/releases/download/v0.7.0/drone_linux_amd64.tar.gz | tar zx
$ sudo install -t /usr/local/bin drone

La prochaine étape consiste à obtenir les bons jetons: allez sur votre nouveau drone et en haut à droite, cliquez sur l’icône du menu, puis sur Token.
Copiez et collez les deux premières lignes du Example CLI Usage dans votre terminal:

export DRONE_SERVER=""
export DRONE_TOKEN=""

Si cela fonctionne, vous devriez obtenir le bon résultat lorsque vous tapez drone info.

Ajouter des secrets à Drone

Avant d'ajouter un secret à un dépôt, celui-ci doit être ajouté via l'interface utilisateur de Drone.
Pour que le premier plugin fonctionne, nous devons ajouter le docker_password secret:

drone secret add -repository / -image plugins/docker -name docker_password -value ""

Les plugins suivants utilisent trois secrets différents: kubernetes_server, kubernetes_cert, et kubernetes_token.

Cependant, nous devons créer un RBAC pour pouvoir déployer le plug-in.

apiVersion: v1                                        
kind: ServiceAccount
metadata:
    name: drone-deploy
    namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
    name: drone-deploy
    namespace: default
rules:
  - apiGroups: ("extensions")
    resources: ("deployments")
    verbs: ("get","list","patch","update")
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
    name: drone-deploy
    namespace: default
subjects:
  - kind: ServiceAccount
    name: drone-deploy
    namespace: default
roleRef:
    kind: Role
    name: drone-deploy
    apiGroup: rbac.authorization.k8s.io

Et l'appliquer: kubectl apply -f drone-rbac.yml
Si vous utilisez des K8 sur GKE, vous devez d’abord ajouter le rôle. cluster-admin à votre utilisateur gcloud (voir ce numéro).

$ kubectl create clusterrolebinding  --clusterrole=cluster-admin (email protected)

Ensuite, nous devons extraire le ca.crt et le token du compte de service.

$ kubectl -n  get secrets
NAME                       TYPE                                  DATA      AGE
default-token-ms8cl        kubernetes.io/service-account-token   3         3h
drone-deploy-token-4zv8j   kubernetes.io/service-account-token   3         10m 
$ kubectl -n  get secret/drone-deploy-token-4zv8j -o yaml | grep 'ca.crt:'
  ca.crt: 
$ kubectl describe secret drone-deploy-token-4zv8j | grep 'token:'
  token: 

Une fois que cela est fait, nous pouvons créer nos secrets.

  • Pour obtenir l’adresse IP de votre nœud maître, vous pouvez utiliser le kubectl cluster-info commander. Et nous créons le secret avec drone secret add --image=quay.io/honestbee/drone-kubernetes -repository / -name kubernetes_server -value "https://"
  • Pour créer le secret de jeton, nous courons drone secret add --image=quay.io/honestbee/drone-kubernetes -repository / -name kubernetes_token -value
  • Pour créer le jeton de cert, nous courons drone secret add --image=quay.io/honestbee/drone-kubernetes -repository / -name kubernetes_cert -value

Cependant, pour que ce plugin fonctionne, le déploiement doit déjà être créé. Vous pouvez vérifier ce plugin, si vous voulez que Drone soit lancé kubectl apply -f deploy.yml au lieu de simplement changer l'image du déploiement.

Maintenant, une fois que vous aurez poussé un nouveau code sur GitHub, cela déclenchera Drone et démarrera votre construction / déploiement. À votre santé!

Nous serions ravis de connaître votre avis

      Laisser un commentaire