Google Cloud From Scratch - Des conteneurs dans le Cloud

Cet article fait partie d’une série autour du thème "Google Cloud From Scratch".

Quelques semaines après le Paris Container Day, il devenait urgent de vous faire un focus sur la gestion des conteneurs sur Google Cloud !

Mais tout d’abord un rappel sur les précédents articles à votre disposition pour bien commencer sur Google Cloud :

Regardons maintenant comment déployer et gérer nos conteneurs sur Google Cloud.

Modes de déploiement des conteneurs

Il existe plusieurs méthodes pour instancier les conteneurs, nous allons en voir trois différentes en partant de la plus simple à la complexe.

Mono-conteneur dans une instance GCE

La façon la plus simple pour commencer est de lancer des instances mono-conteneurs. Dans ce mode, nous allons créer une instance et lui indiquer comment démarrer un seul et unique conteneur. En faisant ainsi, nous pouvons déployer une application simple rapidement et sans contraintes.

La commande à utiliser est la suivante (en assumant que la règle firewall http-server existe et ouvre le port 80) :

gcloud beta compute instances create-with-container simple-web --container-image docker.io/httpd:2.4 --tags http-server

Vous pouvez aller vérifier en interrogeant le port HTTP sur l’IP publique de l’instance, au bout de quelques dizaines de secondes vous devriez voir apparaître le fameux It works !

Il faut noter qu’on peut également utiliser cette méthode pour définir des instances templates, et ainsi exécuter un instance group de VM mono-conteneur (Retour au troisème article pour un rappel sur les instances group).

Quoique simple, cette méthode souffre de plusieurs problèmes :

  • Il n’existe pas d’API publique pour déployer des instances mono-conteneurs. Seuls l’UI Web et l’outil gcloud permettent de le faire, il n’est donc pas possible d’utiliser Terraform pour créer ces instances.
  • Il n’est pas possible d’orchestrer les conteneurs, de gérer leur réseaux, le stockage, etc.

Pour en savoir plus, n’hésitez pas à consulter la documentation dédiée.

AppEngine flex

Si l’on souhaite avoir quelque chose d’un peu plus géré et intégré, on peut se tourner vers AppEngine.
AppEngine a été le premier service offert par Google Cloud, il s’agit d’un mode de déploiement applicatif complètement géré et scalable. Celui-ci prend en charge le réseau, le stockage, l’autoscaling, et permet de gérer finement le routage du trafic lors des mises à jour (par exemple, il est possible de n’envoyer qu’un fragment du trafic vers une version de test avant de déployer la version pour tout le monde).
L’un des plus grands déploiements utilisant AppEngine est Snapchat.

Plusieurs langages sont supportés nativement par AppEngine dans sa version standard, dans notre cas nous allons utiliser un mode un peu spécial, le mode custom apporté par la version Flex d’AppEngine. Pour en savoir un peu plus sur les différences entre AppEngine Flex et Standard, une page de comparaison est à disposition.

Le déploiement est très simple, pour cela deux fichiers sont nécessaires :

  • app.yaml : décrivant comment configurer AppEngine
runtime: custom
env: flex
  • Dockerfile : décrivant comment configurer et lancer l’image docker
FROM httpd:2.4
# AppEngine listen on port 8080
RUN sed -i "s/Listen 80/Listen 8080/g" /usr/local/apache2/conf/httpd.conf
# Healthchecks are performed on a specific page
RUN mkdir -p /usr/local/apache2/htdocs/_ah && echo "healthy" > /usr/local/apache2/htdocs/_ah/health
EXPOSE 8080

Et ensuite nous pouvons déployer l’application AppEngine sur notre projet GCP courant :

$ gcloud app deploy

Services to deploy:

descriptor:      [/home/bcadiot/Dev/perso/gcp-from-scratch/part05-containers/appengine/app.yaml]
source:          [/home/bcadiot/Dev/perso/gcp-from-scratch/part05-containers/appengine]
target project:  [sandbox-bcadiot]
target service:  [default]
target version:  [20180703t151954]
target url:      [https://sandbox-bcadiot.appspot.com]


Do you want to continue (Y/n)?  Y

[... logs ...]

Updating service [default] (this may take several minutes)...done.                                                                                                                                                
Setting traffic split for service [default]...done.                                                                                                                                                               
Deployed service [default] to [https://sandbox-bcadiot.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

Un dashboard tout intégré est disponible dans l’interface AppEngine, et vous pouvez également suivre les différentes versions que vous avez déployées. Si vous êtes observateur, vous remarquerez qu’à chaque mise à jour nous basculons tout le trafic d’une version à l’autre, mais il est tout à fait possible d’échantillonner le trafic pour tester la nouvelle version.
Ceci est déjà une grande avancée par rapport au déploiement mono-instance car le trafic est géré automatiquement.

Pour terminer sur AppEngine, il reste une dernière question légitime : comment faire pour utiliser plusieurs conteneurs ? La solution repose sur la notion de services, lorsque nous faisons un déploiement nous utilisons le service default mais il est tout à fait possible de définir plusieurs services, chacun utilisant un conteneur différent.

Google Kubernetes Engine

La dernière méthode d’instanciation que nous allons voir est GKE alias Google Kubernetes Engine. Le produit GKE est basé comme son nom l’indique sur la solution d’orchestration Kubernetes. Si vous ne connaissez pas, ou mal, Kubernetes je vous invite à parcourir le comics réalisé pour faciliter la compréhension de l’outil.

Beaucoup de déploiements utilisent GKE, l’un des plus connus est Pokémon Go.

GKE dispose de beaucoup d’avantages, mais le premier est le fait d’avoir un cluster entièrement géré par Google, et les masters du cluster sont même masqués et non facturés. Il y a beaucoup d’arguments en faveur de l’utilisation de services gérés plutôt que de les déployer, nous retiendrons ici que GKE est pleinement intégré dans l’environnement GCP et permet d’utiliser l’ensemble des outils liés, qu’il s’agisse des logs, des métriques, de la gestion des autorisations, de la répartition de charge, etc …

Malgré tout, Kubernetes reste une solution complexe et nécessitant des compétences allant plus loin que la connaissance des conteneurs Docker. Nous allons donc devoir spécifier un peu plus de choses afin que notre simple conteneur Apache fonctionne.

Une recette Terraform a été préparée, vous pouvez l’exécuter directement sur votre projet GCP :

cd gke/
terraform apply

# Get credentials
gcloud container clusters get-credentials gcp-from-scratch --region europe-west1

# Test access by fetching all pods
kubectl get pods --all-namespaces

Maintenant que notre cluster fonctionne, essayons de déployer notre serveur web. Cette démonstration ne visant pas à apprendre la syntaxe de Kubernetes, et pour plus de simplicité, nous n’allons pas utiliser de fichier YAML mais uniquement des commandes kubectl.

# Run app
kubectl run it-works --replicas=2 --image=docker.io/httpd:2.4 --port=80

# Create service
kubectl expose deployment it-works --type=LoadBalancer --name=it-works-service

# Check if everything is deployed
kubectl describe services it-works-service

Dans la console Google on peut observer que tout a été correctement déployé :

gke

Notre site web est disponible sur l’IP du LoadBalancer : http://${LoadBalancerIP}:80

Construire et gérer les images de conteneurs

Les méthodes principales de déploiement des conteneurs sont maintenant connues et ont été relativement faciles à mettre en place car nous avons utilisé des images publiques prêtes à l’emploi. Qu’en est-il lorsque nous voulons construire nos propres images et les utiliser au sein de GCP ?

Si vous y avez prêté attention, un début de réponse est apparu lors du déploiement sur AppEngine : nous avons construit et stocké les images sur un dépôt privé. Regardons plus attentivement comment construire et stocker les images sur Google Cloud.

Google Container Registry

Si on souhaite stocker des images Docker sur un registre privé, on peut utiliser GCR. Ce registre est hébergé en interne sur Google Cloud, et s’intègre parfaitement avec les permissions et l’ensemble de l’écosystème GCP.

Utiliser le registry interne de GCP a plusieurs avantages, d’une part il est accessible de manière privée (sans accès à internet) par tous les applicatifs déployés sur GCP, d’autre part les permissions sont intégrées et sont les mêmes que pour tous les autres services GCP. Enfin, un avantage important est que GCR utilise le stockage objet de GCP, donc on peut traiter les images et leurs permissions comme on traiterait n’importe quel objet sur GCS.

Passons à la pratique, comment utiliser GCR au quotidien ? L’intégration utilise un helper Docker afin d’unifier les commandes. Pour cela, il est nécessaire d’avoir l’outil gcloud installé, puis de passer la commande suivante :

$ gcloud auth configure-docker
The following settings will be added to your Docker config file 
located at [/home/bcadiot/.docker/config.json]:
 {
  "credHelpers": {
    "gcr.io": "gcloud", 
    "us.gcr.io": "gcloud", 
    "eu.gcr.io": "gcloud", 
    "asia.gcr.io": "gcloud", 
    "staging-k8s.gcr.io": "gcloud"
  }
}

On peut voir qu’il y a plusieurs URL GCR, chacune localisée sur un bucket multi-régional. Rappelez-vous, GCR utilise GCS pour son stockage, et donc toute la logique du stockage objet s’applique, les buckets sont donc multi-régionaux (à noter que gcr.io renvoie pour le moment sur us.gcr.io, il est conseillé d’utiliser plutôt les URL complètes).

Une fois que tout cela est configuré, nous pouvons essayer de construire l’image et de la pousser. Pour cela, nous allons réutiliser le Dockerfile que nous avions créé pour le déploiement AppEngine, nous allons simplement le reconstruire avec un tag différent (à noter qu’on aurait pu simplement utiliser la commande docker tag si l’image existait toujours).

cd appengine
docker build -t eu.gcr.io/sandbox-bcadiot/hello:1.0 .
docker push eu.gcr.io/sandbox-bcadiot/hello:1.0

Si on regarde dans l’interface on peut observer que l’image apparait bien dans le registre de conteneurs.
registry

Qu’en est il du stockage objet sur GCS ? Allons vérifier :
gcs

Un bucket spécial a été créé et l’image a été déposée, on peut constater que tous les layers de l’image y sont posés en tant qu’objets. Il est intéressant de le noter car cela signifie que le comportement des registres Docker est respecté.

En outre, on en déduit un élément important : il devient très facile de gérer les permissions sur les images en faisant simplement varier les policies IAM et ACL sur les objets et buckets. Rendre une image publique consistera alors simplement à rendre publics tous les layers qui la composent !

Google Container Builder

Il est possible de construire les images de conteneurs directement sur GCP à l’aide de GCB alias Google Container Builder.

Imaginons que nous n’avons pas de démon Docker prêt à builder sur notre machine, alors nous pouvons lancer simplement cette commande pour demander à GCB de faire le build pour nous (notez que nous avons incrémenté le tag) :

gcloud container builds submit --tag eu.gcr.io/sandbox-bcadiot/hello:1.1

Et voilà ! Au bout de quelques secondes, vous verrez apparaître le build sur la page des builds, et vous constaterez que notre image de conteneur est bien disponible.

A présent, comment faire si on souhaite automatiser les builds ? Par exemple si l’on souhaite construire une nouvelle image automatiquement à chaque fois que l’on pousse un nouveau commit sur la branche master ?
Nous allons utiliser la fonctionnalité de déclenchement des builds sur événement. Une recette Terraform est prête à l'emploi pour tester cela :

cd cloudbuilds
terraform apply

Un nouveau dépôt nommé itworks a été créé ainsi qu’un déclencheur. Le déclencheur est paramétré pour suivre les modifications sur la branche master. Je vous invite donc à cloner ce dépôt et à y ajouter un Dockerfile initial :

cd /tmp
# Clonage dans tmp à l’aide de gcloud
gcloud source repos clone itworks

# création du fichier
vi Dockerfile
	FROM httpd:2.4

# Ajout dans git et envoi sur le dépôt distant
git add Dockerfile
git commit -m “Add Dockerfile”
git push

A présent, si on retourne sur la page des builds nous pouvons voir qu’un nouveau build est en cours, et qu’une nouvelle image itworks va être produite. Celle-ci sera directement utilisable pour déployer de nouveaux conteneurs sur GCP !

Pour aller plus loin ?

Sans entrer dans les détails, il existe depuis peu un outil permettant de scanner les vulnérabilités des images entreposées dans GCR. L’outil est encore en Alpha mais vous pouvez d’ores et déja le tester en suivant cette procédure.

Conclusion

Le sujet des conteneurs dans le cloud mériterait beaucoup d'autres articles, et nous sommes restés sur une phase de découverte. J’espère que celle-ci vous aura donné envie de creuser et de tester de nouvelles choses sur GCP ! Le code de nos exemples est disponible sur GitHub : https://github.com/bcadiot/gcp-from-scratch.
Vous avez des questions ? Vous voulez plus de détails ? N’hésitez pas à le dire dans les commentaires de l’article ou via Twitter.

Bastien Cadiot

Spécialisé en OpenSource et GoogleDevExpert Cloud, Bastien est passionné par la conception de systèmes évolutifs. Il pense que chaque besoin est unique et cherche des solutions toujours adaptées.