Aller au contenu principal

Developper Roles et Collections Ansible AWS EC2

· 7 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Jusqu'à maintenant, je n'utilisais que les drivers docker et vagrant pour provisionner des machines de tests avec molecule lors de mes développements de roles et collections Ansible. Comme je n'ai plus assez de ressources à la maison pour les provisionner, je me suis dit pourquoi utiliser des instances AWS EC2. Après avoir tâtonnè longtemps, j'ai fini par réussir, j'ai décidé de vous en faire profiter.

attention

Je vais comme souvent utiliser quelques raccourcis pour tout le monde puisse réussir à reproduire ce tutoriel. Ici, il faudrait créer la VM dans un réseau privé et non public et y accéder via un bastion par exemple.

Installation

Par défaut molécule n'installe que les drivers docker et delegated. Il faut donc installer le driver ec2, ainsi que la librairie boto3.

pip install molecule molecule-ec2 boto3
attention

Les dernières versions (2.13.6 et 2.14.0) de la lib ansible-core possède un bug sur le lookup file ! Solution de contournement en attendant la sortie d'ansible-core 2.13.7 on réinstalle la 2.13.5

pip install ansible==6.4.0 ansible-core==2.13.5

Création de la configuration AWS

Maintenant que nous avons tout ce qu'il nous, passons à l'installation et la configuration la cli awx. Comme très souvent je vais utiliser asdf pour l'installer.

asdf plugin add aws-cli
asdf install aws-cli latest
asdf global aws-cli latest

Passons à la configuration du compte en créant au prélable un compte sur IAM :

aws configure
AWS Access Key ID [None]: xxxxxxxxxx
AWS Secret Access Key [None]: xxxxx/xx/xxxxxxxxxxxxxxxxxxxx
Default region name [None]: eu-west-3
Default output format [None]: json

Initatialisation du role Ansible avec le role EC2

Pour ceux qui ne connaissent pas molecule je vous renvoie à mon premier tutoriel. Créons le role :

molecule init role -d ec2 steph.pyenv

Configuration du scenario molecule

Bon il reste un peu de travail avant d'instancier notre première instance EC2. Commencons par éditer le scenario avec le fichier présent dans le dossier molecule/<nom_scenario>/molecule.yml.

---
dependency:
  name: galaxy
driver:
  name: ec2
platforms:
  - name: instance
    image_owner: "099720109477"
    image_name: "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-20221201"
    instance_type: t3a.micro
    vpc_subnet_id: subnet-0b13a91a5bd16a5bf
    key_inject_method: ec2
    ssh_user: ubuntu
    tags:
      Name: molecule_instance

provisioner:
  name: ansible
verifier:
  name: ansible

Quelques explications sur les variables :

  • image_owner : le nom du owner de l'AMI. Ici canonical
  • image_name : le nom de l'AMI
  • instance_type : le type d'instance. Ici une t3a.micro une des moins chères, quelques centimes/heures.
  • vpc_subnet_id : le sous-réseau à utiliser. Dans mon exemple j'utilise le réseau publique accessible directement.
  • key_inject_method : La méthode pour injecter la clé. J'utilise de l'injecter au moment de la création de l'instance.
  • ssh_user: l'utilisateur défini dans l'image qui est fonction du type d'AMI. Pour une ubuntu : ubuntu, pour une Amazon Linux, ec2_user...

Toutes les variables sont documentés dans le fichier molecule/<nom_scenario>/create.yml

Création et destruction de l'instance.

attention

Attention la version actuelle du driver possède une petite qui fait planter le converge. Editez le fichier molecule/<nom_scenario>/create.yml et ajouter à la ligne 217 ceci state: "running". Ce qui donne :

216        wait: true
217        state: "running"
218      vars:
219        platform_security_groups: "{{ item.security_groups or [item.security_group_name] }}"

Bon tout est prêt lancons le converge pour créer l'instance :

molecule converge

INFO     default scenario test matrix: dependency, create, prepare, converge
INFO     Performing prerun with role_name_check=0...
INFO     Set ANSIBLE_LIBRARY=/home/vagrant/.cache/ansible-compat/4489a2/modules:/home/vagrant/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/home/vagrant/.cache/ansible-compat/4489a2/collections:/home/vagrant/.ansible/collections:/usr/share/ansible/collections
INFO     Set ANSIBLE_ROLES_PATH=/home/vagrant/.cache/ansible-compat/4489a2/roles:/home/vagrant/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
INFO     Using /home/vagrant/.cache/ansible-compat/4489a2/roles/steph.pyenv symlink to current repository in order to enable Ansible to find the role using its expected full name.
INFO     Running default > dependency
WARNING  Skipping, missing the requirements file.
WARNING  Skipping, missing the requirements file.
INFO     Running default > create

PLAY [Create] ******************************************************************

TASK [Validate platform configurations] ****************************************
[WARNING]: Unable to find '/home/vagrant/.cache/molecule/pyenv/default/run-
config.yml' in expected paths (use -vvvvv to see paths)
ok: [localhost] => (item=instance)

TASK [Write run config to file] ************************************************
[WARNING]: Unable to find '/home/vagrant/.cache/molecule/pyenv/default/run-
config.yml' in expected paths (use -vvvvv to see paths)
changed: [localhost]

TASK [Generate local key pairs] ************************************************
changed: [localhost] => (item=instance)

TASK [Look up EC2 AMI(s) by owner and name (if image not set)] *****************
ok: [localhost] => (item=instance)

TASK [Look up subnets to determine VPCs (if needed)] ***************************
ok: [localhost] => (item=instance)

TASK [Validate discovered information] *****************************************
ok: [localhost] => (item=instance)

TASK [Create ephemeral EC2 keys (if needed)] ***********************************
changed: [localhost] => (item=instance)

TASK [Create ephemeral security groups (if needed)] ****************************
changed: [localhost] => (item=instance)

TASK [Create ephemeral EC2 instance(s)] ****************************************
changed: [localhost] => (item=instance)

TASK [Wait for instance creation to complete] **********************************
FAILED - RETRYING: [localhost]: Wait for instance creation to complete (300 retries left).
FAILED - RETRYING: [localhost]: Wait for instance creation to complete (299 retries left).
FAILED - RETRYING: [localhost]: Wait for instance creation to complete (298 retries left).
FAILED - RETRYING: [localhost]: Wait for instance creation to complete (297 retries left).
changed: [localhost] => (item=instance)

TASK [Collect instance configs] ************************************************
ok: [localhost] => (item=instance)

TASK [Write Molecule instance configs] *****************************************
changed: [localhost]

TASK [Start SSH pollers] *******************************************************
changed: [localhost] => (item=instance)

TASK [Wait for SSH] ************************************************************
[WARNING]: The 'finished' test expects an async task, but a non-async task was
tested
ok: [localhost] => (item=instance)

TASK [Wait for boot process to finish] *****************************************
Pausing for 120 seconds
                       (ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
                                                                                  ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=15   changed=8    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

INFO     Running default > prepare

PLAY [Prepare] *****************************************************************

TASK [Make sure python3 is installed] ******************************************
ok: [instance]

PLAY RECAP *********************************************************************
instance                   : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

INFO     Running default > converge

PLAY [Converge] ****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [instance]

TASK [Include steph.pyenv] *****************************************************

PLAY RECAP *********************************************************************
instance                   : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Pour se connecter dessus puis la détruire :

molecule login
INFO     Running default > login
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-1026-aws x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Dec  4 15:20:15 UTC 2022

  System load:  0.0927734375      Processes:             108
  Usage of /:   20.0% of 7.57GB   Users logged in:       0
  Memory usage: 23%               IPv4 address for ens5: 172.31.149.83
  Swap usage:   0%


0 updates can be applied immediately.


Last login: Sun Dec  4 15:17:43 2022 from 91.166.20.90
-bash: warning: setlocale: LC_ALL: cannot change locale (fr_FR.UTF-8)
ubuntu@ip-172-31-149-83:~$ exit

molecule destroy

Quelques indications en cas difficultés

Destruction de la VM dans la console AWS

Il se peut que vous rencontriez quelques problèmes. Par exemple vous avez détruit l'instance à la main dans la console EC2 (pas bien), résultat la commande molecule destroy plante. Pour rétablir la situation :

rm -rf ~/.cache/molecule/<nom_du_role>/<nom_scenario/>

Ouf, c'est moins compliqué que lorsque vous perdez le state terraform!

Utilisation dans une CI.

Attention à l'utilisation du scenario Test.

Si vous utilisez la commande molecule test sur ne nombreux rôles, vous risquez de détruire toutes les VM présentes dans le même subnet_id ! Donc 2 solutions, Créez des subnet dédiés à chaque rôle ou retirer destroy du scenario test. Pour cela à la fin du fichier molecule.yml ajoutez ces lignes :

scenario:
  test_sequence:
  - dependency
  - cleanup
  - create
  - prepare
  - converge
  - cleanup
  - destroy

Je dois vérifier s'il est possible de filtrer les instances par ta, ce qui serait plus simple à mettre en oeuvre.

D'autres cas

Si je trouve d'autres situations, je complèterai le billet.

Plus loin

Il ne vous reste plus qu'à développer vos roles et collections Ansible ! N'oubliez pas la sécurité avant tout en durcissant vos roles Ansible.

A bienôt.