Tutoriel localstack (partie 3) - Automatiser le déploiement des ressources AWS
10 Feb 2020Pré-requis
J’ai écris les 2 premiers articles sur Localstack bien avant celui-ci. Localstack a été amélioré depuis. Certaines problèmes ont été résolus et certaines fonctionnalités sont apparues. Les articles précédents ont été mis à jour pour refléter les changements. Si vous arrivez sur cette page sans les avoir lu, je vous conseille fortement de le faire et de revenir sur cette page après.
Pour suivre ce tutoriel il vous faudra :
- Les fichiers de configurations de la deuxième partie (fichier lambda.zip inclu)
- Docker et docker-compose installés
Vérifier que le réseau docker localstack-tutorial soit présent. Si ce n’est pas le cas, créez-le avec la commande docker network create localstack-tutorial
.
Il n’y a même plus besoin d’installer Terraform ou le cli AWS : ici tout pourra fonctionner sous docker.
Si vous êtes pressé, tous les fichiers créés dans ce tuto sont disponibles sur ce repository github avec des instructions pour tout installer en accéléré.
Introduction
Quel est l’objectif de ce tutoriel ? Comme vu précédemment, même si Terraform aide à déployer des ressources AWS dans localstack, il y a encore quelques aspects négatifs:
- Toutes les ressources ne sont pas sauvegardés sur le disque via des volumes docker. Il faut appliquer la configuration Terraform à chaque redémarrage du projet.
- Exécuter des fonctions lambdas prend du temps à la première invocation. Ce n’est pas un problème pour des fonctions asychrones. Cependant, pour des fonctions dont la réponse est bloquante, ce n’est pas si simple. De plus, après 10 minutes d’inactivité le conteneur est tué automatiquement par localstack.
Pour pallier à ces problèmes je suis parvenu à implémenter 2 solutions :
-
En utilisant les évenements docker on peut déclencher la commande
terraform apply
immédiatement après le démarrage de localstack. En tant que développeur il n’est plus necessaire de penser à déployer les ressources à chaque fois. -
On utilisera notre propre conteneur localstack pour empêcher la destruction des conteneurs de fonctions lambda. (mise à jour du 3 mars 2020 : ma contribution à localstack a été acceptée et il n’est plus necessaire de créer une autre image. Plus d’information ci-dessous.)
Implémentons tout cela.
Création du conteneur docker-events-listener
Nous allons ici créé un conteneur docker qui écoutera les évenements docker. Dès que localstack démarre, ce conteneur se chargera de déployer les ressources AWS avec Terraform.
Le service docker-compose
Dans le fichier docker-compose.yml, ajouter le service docker-events-listener.
services:
localstack:
...
container_name: localstack # 1
depends_on:
- docker-events-listener # 2
docker-events-listener:
build:
context: docker-events-listener-build # 3
volumes:
- /var/run/docker.sock:/var/run/docker.sock # 4
- ./terraform:/opt/terraform/ # 5
- On précise le nom du conteneur localstack. Ce nom sera nécessaire pour savoir quels évenements proviennent du conteneur localstack lors de l’écoute des évenements docker.
- Le service docker-events-listener doit démarrer avant localstack (sinon l’évenement de démarrage du conteneur localstack sera déclenché avant même que le service docker-events-listener n’ai pu l’écouter)
- On créé un un dossier appelé docker-events-listener-build. Il s’agira du contexte de build de l’image docker et contiendra donc tous les fichiers nécessaire à la construction de l’image.
- Le conteneur a besoin d’un accès à la socket docker pour écouter les évenements.
- Le conteneur a besoin d’accéder aux fichiers terraform pour pouvoir effectuer le déploiement
Fichiers terraform
Comme défini dans le fichier docker-compose.yml, créeons un dossier nommé terraform, et déplacons les fichiers localstack.tf et lambda.zip à l’intérieur.
mkdir terraform
mv lambda.zip terraform
mv localstack.tf terraform
Les fichiers de build
Le conteneur va exécuter des commands terraform et du CLI AWS. Il nous faut donc les installer. Le CLI AWS tirera sa configuration des fichiers .txt.
Si ce n’est pas dejà fait, créez le répertoire docker-events-listener-build. Puis créez les fichiers suivants à l’intérieur:
Le fichier aws_config.txt avec le contenu suivant:
[default]
output = json
region = ap-southeast-2
Puis le fichier aws_credentials.txt avec le contenu suivant:
[default]
aws_secret_access_key = fake
aws_access_key_id = fake
La partie la plus importante concerne le script bash qui constituera le processus principal du conteneur. Ce script écoute les évenements docker et effectue des actions en conséquences. Dans notre cas, il exécutera terraform apply
dès que localstack démarre.
Ci-dessous le contenu du script appelé listen-docker-events.sh :
#!/bin/bash
docker events --filter 'event=create' --filter 'event=start' --filter 'type=container' --format '{{.Actor.Attributes.name}} {{.Status}}' | while read event_info
do
event_infos=($event_info)
container_name=${event_infos[0]}
event=${event_infos[1]}
echo "$container_name: status = ${event}"
if [[ $container_name = "localstack" ]] && [[ $event == "start" ]]; then
sleep 20 # laissons 20 secondes à localstack le temps de démarrer
terraform init
terraform apply --auto-approve
echo "La configuration terraform à été appliqué"
fi
done
Grâce aux options –filter, seuls les événements des conteneurs qui sont démarrés ou bien crées seront écoutés, et seuls les noms des conteneurs suivi du nom de l’événement seront affichés.
Pour finir avec la création des fichiers docker, voici le contenu du Dockerfile.
FROM docker:19.03.5
RUN apk update && \
apk upgrade && \
apk add --no-cache bash wget unzip
# Installe le CLI AWS
RUN echo -e 'http://dl-cdn.alpinelinux.org/alpine/edge/main\nhttp://dl-cdn.alpinelinux.org/alpine/edge/community\nhttp://dl-cdn.alpinelinux.org/alpine/edge/testing' > /etc/apk/repositories && \
wget "s3.amazonaws.com/aws-cli/awscli-bundle.zip" -O "awscli-bundle.zip" && \
unzip awscli-bundle.zip && \
apk add --update groff less python curl && \
rm /var/cache/apk/* && \
./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws && \
rm awscli-bundle.zip && \
rm -rf awscli-bundle
COPY aws_credentials.txt /root/.aws/credentials
COPY aws_config.txt /root/.aws/config
# Installe Terraform
RUN wget https://releases.hashicorp.com/terraform/0.12.20/terraform_0.12.20_linux_amd64.zip \
&& unzip terraform_0.12.20_linux_amd64 \
&& mv terraform /usr/local/bin/terraform \
&& chmod +x /usr/local/bin/terraform
RUN mkdir -p /opt/terraform
WORKDIR /opt/terraform
COPY listen-docker-events.sh /var/listen-docker-events.sh
CMD ["/bin/bash", "/var/listen-docker-events.sh"]
Si les 4 fichiers ci-dessus sont bien présents dans le dossier docker-events-listener-build, continuez à lire.
Les événements docker en action
Buildons la nouvelle images et démarrons les conteneurs. Pour être que tout est à zéro avant vous pouvez exécuter la commande docker-compose down -v
.
docker-compose build
docker-compose up -d
docker-compose logs -f docker-events-listener
Si vous n’avez pas modifié les fichiers terraform, vous devriez voir l’erreur suivante au bout de quelques secondes :
Que ce passe-t-il ? Si vous jetez un oeil au fichier localstack.tf, vous allez voir que terraform est configuré pour atteindre dynamodb à l’adresse localhost, qui est le conteneur docker-events-listener lui même. Évidemment, dynamodb n’est pas disponible dans le conteneur docker-events-listener, mais dans le conteneur localstack.
Pour résoudre ce problème, mettons à jour le fichier localstack.tf et remplaçons chaque occurence de localhost avec localstack. Pourquoi localstack ? Car il s’agit du nom du service localstack dans le fichier docker-compose.
Comme expliqué dans le premier tutoriel, la documentation docker spécifie qu’un conteneur créé à partir d’un service docker-compose sera atteignable par tous les conteneurs partageant un réseau en commun, et pourra être découvert avec le même hostname que son nom de service. (Par défaut docker-compose créé un réseau commun à tous les conteneurs définis dans un fichier docker-compose.yml)
Essayons à nouveau après modification du fichier localstack.tf :
docker-compose down -v
docker-compose build
docker-compose up -d
docker-compose logs -f docker-events-listener
Si vous obtenez l’erreur suivante: