L’approche de Google Cloud Build en matière de continuous Delivery est assez simple. Vous créez un «Build Trigger», qui indique à Cloud Build quel repository devra t'il regarder pour les modifications. Chaque fois que vous "pushez" un tag ou "pushez" une branche, Cloud Build extrait le code source, recherche un fichier «cloudbuild.yaml» à la racine, puis suit les instructions de ce fichier pour exécuter un déploiement.  Assez simple.

Alors à quoi ressemblent ces instructions?

Les instructions contiennent une liste de commandes faisant référence à ce que Google appelle un «Cloud Builder». Ces générateurs sont en fait des images Docker dans lesquelles vous pouvez passer des arguments pour exécuter les étapes de construction.

Par exemple, vous pouvez exécuter une commande de construction Docker à l’aide de l’image «gcr.io/cloud-builders/docker». Un “cloudbuild.yaml” pour cette commande peut ressembler à ceci:

steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/superb-website:1.0.$BUILD_ID', '.']

Si vous deviez mettre cela à la racine de votre dépôt, Google chercherait à :

  • Détecter le changement
  • Pull the source
  • Définir le répertoire de travail pour le dépôt cloné
  • Run :  docker build -t gcr.io/$PROJECT_ID/superb-website:1.0.$BUILD_ID .

Vous pouvez voir une liste des générateurs de cloud intégrés ici.  

Maintenant que nous avons une idée de la façon dont cela fonctionne, construisons une application à partir de rien.

Conditions préalables :

  • SDK Google Cloud
  • Docker
  • NodeJS 8.X (pour générer l'exemple d'application. Vous pouvez utiliser un autre language si vous le souhaitez.)

Créer un cluster

Commençons par nous connecter à Google Cloud, créer un projet et le définir comme projet actif:

gcloud auth login
gcloud projects create YOUR_PROJECT_NAME_HERE --name="Cloud Build Example"
gcloud config set project YOUR_PROJECT_NAME_HERE

Avant de pouvoir créer un cluster, nous devons activer l'API Kubernetes Engine. Vous pouvez le faire via la section API de la console Cloud. Vous pouvez également accéder à l'URL suivante pour gagner du temps (assurez-vous de mettre à jour le nom du projet dans l'URL):

https://console.cloud.google.com/apis/api/container.googleapis.com/overview? project = YOUR_PROJECT_NAME_HERE.  

Ensuite, créons notre cluster:

gcloud container clusters create cloud-build-example \
      --zone europe-west1-b \
      --enable-autorepair \
      --num-nodes 2 \
      --enable-autoscaling \
      --min-nodes 2 \
      --max-nodes 4

Laissez-le travailler pendant quelques minutes pendant qu'il crée le cluster. En attendant, nous pouvons commencer à créer l’application.  

Construire l'application

Nous allons construire une simple application NodeJS et la transmettre à GitHub.

mkdir hello-world
cd hello-world
npm init
npm install --save express

Maintenant que nous avons une application de base configurée, créez un fichier nommé index.js avec le contenu suivant:

const express = require('express');
const app = express();

const PORT = process.env.PORT || 8080;

app.get('/', (req, res) => {
  res.send('Hello, World!');
});

app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});

Donnez pour consigne à NPM de démarrer notre serveur et comment. Ouvrez package.json et ajoutez-y une commande "start". Ça devrait ressembler a quelque chose comme ca:

{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.3"
  }
}

Testez-le en exécutant npm start et en ouvrant http://localhost:8080 dans un navigateur.  Vous devriez voir une page Web affichant «Hello, World!».

Push de l'application à GitHub

Allez sur GitHub et créez un repository vide. Nous allons utiliser cela pour stocker le code. Une fois créé, vous pouvez pousser votre code en lançant ce qui suit (pour moi) :

git init
echo "node_modules" > .gitignore
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/gabrielsagnard/hello-world-node
git push -u origin master

Si vous avez un repository git à supprimer, il faudra utiliser ces commandes :

git remote -v
# View current remotes
origin  https://github.com/OWNER/REPOSITORY.git (fetch)
origin  https://github.com/OWNER/REPOSITORY.git (push)
destination  https://github.com/FORKER/REPOSITORY.git (fetch)
destination  https://github.com/FORKER/REPOSITORY.git (push)
git remote rm destination
# Remove remote
git remote -v
# Verify it's gone
origin  https://github.com/OWNER/REPOSITORY.git (fetch)
origin  https://github.com/OWNER/REPOSITORY.git (push)

Dockeriser notre application

Comme nous avons l'habitude de faire dans Kubernetes, nous devons exécuter notre application dans un conteneur Docker. Pour ce faire, nous devons créer un Dockerfile et, pour les tests, créer un fichier Docker-compose.yml.  Commençons par créer le fichier Dockerfile:

FROM node:8

WORKDIR /usr/src/site/
COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 8080

CMD ["npm", "start"]

Ensuite, créons le fichier docker-compose.yml:

version: '3'
services:
  web:
    build: .
    volumes:
      - .:/usr/src/site
      - /usr/src/site/node_modules
    ports:
      - 8080:8080

Testez-le en exécutant:

docker-compose up

Ouvrez un navigateur sur http://localhost:8080 pour vous assurer que cela fonctionne.

Déployer l'application manuellement

Nous déploierons la première version manuellement, puis nous utiliserons Cloud Build pour mettre à jour l'image dans le déploiement existant. Voyons ce dont nous avons besoin pour que cela fonctionne:  

  • deployment.yml - Ceci stockera les informations sur la création d'instances de notre conteneur.
  • service.yml - Ceci crée un domaine local grâce auquel d'autres ressources Kubernetes peuvent accéder aux instances de notre conteneur.
  • ingress.yml - Ceci crée un load balancer qui expose notre service local à Internet.

Construisons-les.  Premièrement, construisons et poussons notre image (help ici):

gcloud auth configure-docker
docker build -t gcr.io/YOUR_PROJECT_NAME_HERE/hello-world_web:latest
docker push gcr.io/YOUR_PROJECT_NAME_HERE/hello-world_web:latest

La première ligne permet à Docker de transmettre à notre Google Container Registry.  Créez maintenant un fichier dans k8s/deployment.yml avec le contenu suivant:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: hello-world
  name: hello-world
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - name: web
        image: gcr.io/YOUR_PROJECT_NAME_HERE/hello-world_web:latest
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /
            port: 8080

Assurez-vous de remplacer YOUR_PROJECT_NAME_HERE par le nom de votre projet actuel.  Maintenant, créez notre service sur k8s/service.yml:

kind: Service
apiVersion: v1
metadata:
  name: hello-world
spec:
  selector:
    app: hello-world
  type: NodePort
  ports:
  - protocol: TCP
    nodePort: 32131
    port: 80
    targetPort: 8080

Et enfin, créez notre ingress sur k8s/ingress.yml:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-world
spec:
  rules:
    - host: myapp.gabrielsagnard.fr
      http:
        paths:
          - backend:
              serviceName: hello-world
              servicePort: 80
            path: /*

Maintenant, créez le tout en exécutant:

kubectl apply -f k8s/

Vous devrez ajouter un enregistrement CNAME pointant vers votre domaine à votre nouveau load balancer pour vérifier que cela fonctionne. Vous pouvez obtenir votre IP de load balancer en exécutant kubectl get input jusqu'à ce qu'il apparaisse sous la colonne ADDRESS.  Une fois que votre DNS se propage, vous devriez pouvoir visiter votre site Web via le domaine que vous avez entré dans l'ingress.  

Ou via curl :

MBP-de-admin:hello-world admin$ curl myapp.gabrielsagnard.fr
Hello, World

Ajout des Instructions Google Cloud Build

Passons maintenant à la partie fun: configurer Google Cloud Build.

Nous avons déjà défini dans notre fichier Dockerfile comment créer notre image. Nous devons donc indiquer à Google Cloud Build de suivre les étapes que nous venons de déployer manuellement.  

Créez un fichier à la racine de votre repository nommé cloudbuild.yaml et mettez-y le contenu suivant:

steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/hello-world:1.0.$BUILD_ID', '.']
  timeout: 180s
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/$PROJECT_ID/hello-world_web:latest.$BUILD_ID']
- name: 'gcr.io/cloud-builders/kubectl'
  args:
  - set
  - image
  - deployment
  - hello-world
  - web=gcr.io/$PROJECT_ID/hello-world_web:latest.$BUILD_ID
  env:
  - 'CLOUDSDK_COMPUTE_ZONE=us-central1-b'
  - 'CLOUDSDK_CONTAINER_CLUSTER=cloud-build-example'

La première étape consiste à construire l'image. Cela remplace automatiquement l'ID de projet et ajoute l'ID de construction (un guide généré de manière aléatoire) à la fin de la balise pour que Kubernetes sache extraire une nouvelle image.  

La deuxième étape transfère l'image dans votre registre de conteneurs.  

La troisième étape remplace manuellement l'image du conteneur Web dans le déploiement de hello-world. Cela oblige Kubernetes à extraire la nouvelle image et à la déployer automatiquement.  

Enfin, envoyez toutes vos modifications à GitHub:

git add .
git commit -m "Added CloudBuild."
git push origin master

et là magie :

Activation de Google Cloud Build

Par défaut, Cloud Build est désactivé. Vous devrez l'activer en accédant à la sous-section «API et services» de votre console Cloud.  Une fois que vous avez fait cela, Cloud Build est activé, mais il ne peut pas accéder à votre cluster Kubernetes. Vous devrez lui donner accès. Pour y parvenir :  

  • Ouvrez votre console Cloud dans la sous-section «IAM & admin».
  • Cliquez sur la section "IAM".
  • Cliquez sur l'icône en forme de crayon à côté de l'utilisateur nommé «######@cloudbuild.gserviceaccount.com».
  • Sélectionnez «Ajouter un nouveau rôle».
  • Recherchez «Kubernetes Engine Admin» et ajoutez-le.
  • Cliquez sur "Enregistrer".

Ajout d'un Build Trigger

C'est la dernière étape!  

  • Naviguez jusqu'à la console Cloud -> Cloud Build -> Section Triggers Build.
  • Cliquez sur «Créer un Trigger».
  • Cliquez sur "GitHub".
  • Cliquez sur «Continuer».
  • Accordez l'accès à Cloud Build sur GitHub.
  • Sélectionnez votre repository.
  • lisez et acceptez le contrat de licence.
  • Tapez ce qui suit dans le champ “Branch (regex)”: ^ master $
  • Sous «Configuration de la construction», sélectionnez «cloudbuild.yaml».
  • Cliquez sur «Créer un déclencheur»

C'est tout!  

Testez-le en insérant des modifications dans votre repository; dans une minute ou deux, votre infrastructure devrait être transférée en live.  

Cleanup (optionnel)

Vous ne voulez probablement pas continuer à payer pour cela, alors assurez-vous de supprimer votre cluster en lançant:

gcloud container clusters delete cloud-build-example --zone europe-west1-b

Have fun !

Bibliographie :