Home Tags Anciens articles Mon CV

Ansible, PostgreSQL et les extensions

PostgreSQL est un SGBD formidablement puissant et très divers en matière de fonctionnalités. Certains le considèrent d’ailleurs comme le SGBD open source le plus avancé au monde, dixit par exemple le site officiel. En plus de ces fonctionnalités, il comporte un système d’extensions et chacune d’entre elles peut être déployée sur une base de donnée. Notamment via Ansible :-)

Pour cet article, j’ai monté l’instance de test via terraform, histoire de pratiquer un peu. Monter l’infra via terraform et déployer la couche logicielle via Ansible, c’est un peu une best practice que j’essaie d’appliquer de plus en plus.

L’architecture du projet est la suivante, générée via ansible-init :

.                               
├── ansible.cfg                                                                           
├── env                                          
│   ├── group_vars                                      
│   │   ├── ntp.yml                              
│   │   └── postgresql.yml                              
│   └── inventory.ini                              
├── play               
│   ├── ntp.yml                             
│   └── postgresql.yml                            
├── requirements.yml q                            
├── roles            
├── ssh
│   ├── config
│   ├── psqltest_key
│   └── psqltest_key.pub
└── tf
    └── main.tf

Cela vous permettra de mieux visualiser les concepts abordés.

Voici le code pour monter l’instance, écrit donc dans tf/main.tf :

provider "aws" {
  version = "~> 2.0"
  region  = "eu-west-1"
}

resource "aws_lightsail_instance" "pgsql-test-01" {
  name              = "pgsql-test-01"
  availability_zone = "eu-west-1a"
  blueprint_id      = "ubuntu_18_04"
  bundle_id         = "nano_2_0"
  key_pair_name     = "psqltest_key"
}

Les paramètres sont plutôt lisibles, je monte sur AWS une instance Lightsail type nano, utilisant Ubuntu 18.04 et à laquelle je pourrais me connecter via ma clef psqltest_key, que j’ai précédemment uploadé dans Lightsail puis déposé dans le répertoire ssh à la racine de mon projet.

Je lance terraform pour qu’il aille d’abord chercher le plugin pour le provider AWS (notez que pour cela, mes credentials sont à jour dans mon $HOME/.aws/credentials):

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aws" (terraform-providers/aws) 2.23.0...                                     

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see                                   
any changes that are required for your infrastructure. All Terraform commands                                   
should now work.

If you ever set or change modules or backend configuration for Terraform,                                       
rerun this command to reinitialize your working directory. If you forget, other                                 
commands will detect it and remind you to do so if necessary.                                                   
$

Parfait, maintenant je fais le plan :

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_lightsail_instance.pgsql-test-01 will be created
  + resource "aws_lightsail_instance" "pgsql-test-01" {
      + arn                = (known after apply)
      + availability_zone  = "eu-west-1a"
      + blueprint_id       = "ubuntu_18_04"
      + bundle_id          = "nano_2_0"
      + cpu_count          = (known after apply)
      + created_at         = (known after apply)
      + id                 = (known after apply)
      + ipv6_address       = (known after apply)
      + is_static_ip       = (known after apply)
      + key_pair_name      = "psqltest_key"
      + name               = "pgsql-test-01"
      + private_ip_address = (known after apply)
      + public_ip_address  = (known after apply)
      + ram_size           = (known after apply)
      + username           = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Le plan est correct, il m’indique qu’une nouvelle instance va être créée et les paramètres indiqués sont ceux attendus. Il ne me reste qu’à l’appliquer pour créer notre instance.

$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_lightsail_instance.pgsql-test-01 will be created
  + resource "aws_lightsail_instance" "pgsql-test-01" {
      + arn                = (known after apply)
      + availability_zone  = "eu-west-1a"
      + blueprint_id       = "ubuntu_18_04"
      + bundle_id          = "nano_2_0"
      + cpu_count          = (known after apply)
      + created_at         = (known after apply)
      + id                 = (known after apply)
      + ipv6_address       = (known after apply)
      + is_static_ip       = (known after apply)
      + key_pair_name      = "psqltest_key"
      + name               = "pgsql-test-01"
      + private_ip_address = (known after apply)
      + public_ip_address  = (known after apply)
      + ram_size           = (known after apply)
      + username           = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_lightsail_instance.pgsql-test-01: Creating...
aws_lightsail_instance.pgsql-test-01: Still creating... [10s elapsed]
aws_lightsail_instance.pgsql-test-01: Still creating... [20s elapsed]
aws_lightsail_instance.pgsql-test-01: Still creating... [30s elapsed]
aws_lightsail_instance.pgsql-test-01: Creation complete after 39s [id=pgsql-test-01]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Une fois mon instance créée, je vais modifier ma configuration ssh dans ssh/config et mon inventaire dans env/inventory.ini pour indiquer à Ansible comment la joindre:

$ cat ssh/config
Host pgtest-01
Hostname 34.243.6.43
Port     22
User    ubuntu
IdentityFile ssh/psqltest_key
$ cat env/inventory.ini
[postgresql]
pgtest-01

[ntp]
pgtest-01

[instance]
pgtest-01

Il me faut cependant me connecter une fois sur l’instance pour y installer Python et faire un upgrade. Une fois fait, Ansible voit mon instance :

$ ansible all -m ping
pgtest-01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Le plus dur est fait, maintenant il reste le plus fun. J’installe deux requirements via le fichier requirements.yml, le rôle postgresql et le rôle ntp.

$ cat requirements.yml
- name: geerlingguy.postgresql
  src: geerlingguy.postgresql

- name: geerlingguy.ntp
  src: geerlingguy.ntp
$ ansible-galaxy install -r requirements.yml
- downloading role 'postgresql', owned by geerlingguy
- downloading role from https://github.com/geerlingguy/ansible-role-postgresql/archive/1.4.6.tar.gz
- extracting geerlingguy.postgresql to /home/kharec/Bureau/dev/others/ansible/article_ext_postgres/roles/geerlingguy.postgresql
- geerlingguy.postgresql (1.4.6) was installed successfully
- downloading role 'ntp', owned by geerlingguy
- downloading role from https://github.com/geerlingguy/ansible-role-ntp/archive/1.6.3.tar.gz
- extracting geerlingguy.ntp to /home/kharec/Bureau/dev/others/ansible/article_ext_postgres/roles/geerlingguy.ntp
- geerlingguy.ntp (1.6.3) was installed successfully

Je n’ai donc que deux playbooks :

$ cat play/ntp.yml
- hosts: postgresql
  become: true

  roles:
    - geerlingguy.ntp
$ cat play/postgresql.yml
- hosts: postgresql
  become: true

  roles:
    - geerlingguy.postgresql

A mon playbook ntp.yml je lie uniquement un group_vars pour remettre le serveur à l’heure. Ce n’est pas nécessaire en soit mais je trouve que c’est indispensable :-) :

$ cat env/group_vars/ntp.yml
---

ntp_enabled: true

ntp_timezone: Europe/Paris

ntp_server1: 0.fr.pool.ntp.org
ntp_server2: 1.fr.pool.ntp.org
ntp_server3: 2.fr.pool.ntp.org
ntp_server4: 3.fr.pool.ntp.org

$ ansible-playbook play/ntp.yml
(...)

A mon playbook postgresql.yml, je lie un group_vars un peu plus concret. Pour les maigres besoins de cet article (et comme j’aurais détruit l’instance dès que j’aurais terminé de l’écrire aussi) les mots de passe sont en clair, mais utilisez ansible-vault en production ou dans un environnement professionnel :

$ cat env/group_vars/postgresql.yml
---

postgresql_users:
  - name: u_sandro
    password: slMKszU6IeptmnCwpdlt

postgresql_databases:
  - name: db_sandro

Tout est paré pour installer mon PostgreSQL et y créer une base à mon nom ainsi qu’un utilisateur :

$ ansible-playbook play/postgresql.yml
(...)

Maintenant je vais aborder le vif du sujet, l’ajout de modules sur ma base de donnée via Ansible. Ouf, tout ça pour en arriver là! Pour ce faire, il suffit d’utiliser le module postgresql_ext. Je vais ajouter à mon playbook postgresql.yml les lignes suivantes:

  tasks:
    - name: add earthdistance extension to db_sandro
      postgresql_ext:
        name: earthdistance
        db: db_sandro

En effet, cela doit passer par une task et non par une variable, je le rajoute donc à la suite de mon playbook. Pour finir, un nouveau run Ansible ajoute mon extension à ma base de donnée.

J’espère que cet article/tuto vous aura plu, vous pouvez en retrouver le code sur mon github.