Bonjour,
Depuis quelques années maintenant, nous avons vu arriver dans le milieu de l’informatique, le phénomène de “containerisation” !
C’est quoi au juste ! Et qu’est ce qui change par rapport à la virtualisation ?
On va dire que c’est un peu, comparativement, des concepts d’architecture qui se différencient comme peuvent l’être le Paas et le Iaas. Toute la question est qu’est ce qui est inclut dans chaque couche !
Principes
Une VM englobe un OS, ses librairies (DLL windows, rpm/pkg linux), avec son schéma d’architecture stockage et les applications installées et embarquées.
Docker, avec la containerisation, ne prend en charge que l’environnement applicatif. En quelque sorte, avec Docker, vous n’avez besoin que d’une solution logiciel avec toutes les bibliothèques nécessaires, utiles à son bon fonctionnement, qui seront inclus dans le container que vous déploierez à ce moment précis.
Rien de mieux que des schémas pour visualiser concrètement le concept. Nous prendrons comme exemple, ces 2 croquis issus du site http://www.docker.com.
Voici le schéma d’architecture d’une virtualisation classique comme nous la connaissons sur une infra HyperV, par exemple :
L’hyperviseur prend en charge 1 ou plusieurs VM qui elle(s) même(s) comporte(nt) des couches OS et applicatives. Ainsi “n” OS seront déployés en fonction des “n” VM gérées par l’hyperviseur.
Avec Docker, nous avons le schéma suivant :
Nous voyons que nous n’avons qu’un seul OS sur l’hôte. Celui ci comporte un processus système appelé “Docker Engine” qui se chargera de communiquer avec les containers. Chaque container docker contiendra son application embarquée avec les dépendances nécessaires (compilateurs C, librairies, packages auxiliaires …..).
Mais l’OS de la machine hôte, de son coté, pourra ne contenir qu’un ensemble de packages et bibliothèques natives et donc, ne pas embarquer différents packages supplémentaires.
Les avantages :
Les principaux avantages que l’on pourra retenir vis à vis de Docker et de la containerisation :
- 1 seul OS, celui ci est capable de gérer l’ensemble des containers fonctionnant sur la machine.
- la simplicité, l’utilisateur peut déployer un ensemble de containers déjà pré-packagés ou en créer un via un “dockerfile”
- Réduire le temps d’installation, pas d’OS dans l’image d’un container.
- La portabilité. Des backup/restauration d’images de containers très rapides avec seulement l’applicatif embarquée dans une image.
- Scalabilité avec possibilité de créer de multiples environnements identiques avec 1 seule image (très pratique pour créer un environnement UAT à partir d’un environnement de production)
- Docker garantit maintenant un fonctionnement sur une grande partie des OS (Linux, Windows, Mac).
- Linux gère cet ensemble de containers via le système LXC (LinuX Container) qui permet une parfaite isolation de chaque container au sein du même OS.
Quelques inconvénients :
- A l’origine, Docker ne fonctionnait que sur Linux. Il fallait monter un environnement virtualisé type “boot2docker” pour fonctionner sur Mac ou Windows. A présent, Windows sait gérer Docker avec les dernières version de Windows.
- Il est nécessaire d’installer la couche Docker Engine sur la machine hôte, ce qui peut être un frein pour certains clients ne maitrisant pas ce type de produit.
- Toutes les applications ne sont pas “container compliance”, en outre, qu’en est-il d’une base de données Oracle avec 200To de données ? Et pour le patching de type one-off ?
- Puisque les containers partagent le même OS, ils partagent donc les mêmes composants physiques. Qu’en est-il d’un phénomène de “out of memory” ou d’un “stack overflow” ? Dans un environnement virtualisé, chaque VM comporte son OS qui gère sa mémoire propre, avec Docker les applications se “partagent” également le même noyau.
- En cas de virus sur la machine hôte, c’est toute l’architecture Docker et des containers qui est impactée, surtout si le virus s’attaque à une partie vitale de l’OS/!\
Installation sous Linux
Nous partons d’une VM de type EC2 AWS. C’est un Red Hat Linux RHEL version 8.2 qui est installé sur celle ci
$ cat /etc/*relea* NAME="Red Hat Enterprise Linux" VERSION="8.2 (Ootpa)" ID="rhel" ID_LIKE="fedora" VERSION_ID="8.2" PLATFORM_ID="platform:el8" PRETTY_NAME="Red Hat Enterprise Linux 8.2 (Ootpa)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:redhat:enterprise_linux:8.2:GA" HOME_URL="https://www.redhat.com/" BUG_REPORT_URL="https://bugzilla.redhat.com/"
La première étape consiste à installer la couche Docker. Cette opération installera la partie serveur avec le processus “Docker Engine”, c’est un “runtime environnement” qui fera tourner l’ensemble des containers. Puis la partie gestion des différents containers. Ceux ci communiqueront avec l’OS via le “Docker Engine”.
Afin de simplifier au mieux cette installation, nous partirons des dépôts Docker que nous enregistrerons sur la machine via “yum-config”.
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
Une fois chargé dans les sources, nous pourrons lancer l’installation des packages suivants :
# yum install docker-ce docker-ce-cli containerd.io
================================================================================================================================================================= Package Architecture Version Repository Size ================================================================================================================================================================= Installing: containerd.io x86_64 1.4.9-3.1.el8 docker-ce-stable 30 M docker-ce x86_64 3:20.10.8-3.el8 docker-ce-stable 22 M docker-ce-cli x86_64 1:20.10.8-3.el8 docker-ce-stable 29 M Installing dependencies: container-selinux noarch 2:2.164.1-1.module+el8.4.0+11870+8b6f7018 rhel-8-appstream-rhui-rpms 52 k docker-ce-rootless-extras x86_64 20.10.8-3.el8 docker-ce-stable 4.6 M docker-scan-plugin x86_64 0.8.0-3.el8 docker-ce-stable 4.2 M fuse-common x86_64 3.2.1-12.el8 rhel-8-baseos-rhui-rpms 21 k fuse-overlayfs x86_64 1.6-1.module+el8.4.0+11822+6cc1e7d7 rhel-8-appstream-rhui-rpms 73 k fuse3 x86_64 3.2.1-12.el8 rhel-8-baseos-rhui-rpms 50 k fuse3-libs x86_64 3.2.1-12.el8 rhel-8-baseos-rhui-rpms 94 k iptables x86_64 1.8.4-10.el8_2.1 rhel-8-baseos-rhui-rpms 581 k libcgroup x86_64 0.41-19.el8 rhel-8-baseos-rhui-rpms 70 k libnetfilter_conntrack x86_64 1.0.6-5.el8 rhel-8-baseos-rhui-rpms 65 k libnfnetlink x86_64 1.0.1-13.el8 rhel-8-baseos-rhui-rpms 33 k libnftnl x86_64 1.1.5-4.el8 rhel-8-baseos-rhui-rpms 83 k libslirp x86_64 4.3.1-1.module+el8.4.0+11822+6cc1e7d7 rhel-8-appstream-rhui-rpms 69 k policycoreutils-python-utils noarch 2.9-9.el8 rhel-8-baseos-rhui-rpms 251 k slirp4netns x86_64 1.1.8-1.module+el8.4.0+11822+6cc1e7d7 rhel-8-appstream-rhui-rpms 51 k Enabling module streams: container-tools rhel8
Vérifier la bonne installation de Docker sur la machine :
# docker version Client: Docker Engine - Community Version: 20.10.8 API version: 1.41 Go version: go1.16.6 Git commit: 3967b7d Built: Fri Jul 30 19:53:39 2021 OS/Arch: linux/amd64 Context: default Experimental: true
Puis démarrer le process via systemctl
# systemctl start docker
# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: active (running) since Wed 2021-09-15 14:05:28 UTC; 3s ago
Docs: https://docs.docker.com
Main PID: 21769 (dockerd)
Tasks: 7
Memory: 78.7M
CGroup: /system.slice/docker.service
└─21769 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
Votre système est maintenant prêt à accepter la containerisation
Et sur les autres OS
Comme évoqué plus haut, à ses débuts, Docker n’était compatible que sur Linux.
Heureusement, avec le temps, Microsoft en a permis l’utilisation de Docker sur sa plateforme Windows.
Il vous suffira d’installer l’un des outils compatible avec Windows en fonction de votre utilisation.
- Docker desktop , outil complet permettant de monter des containers Windows ou Linux.
- VS Code, utilisé pour un environnement de développement au sein d’un container
- Visual Studio prend également en charge l’environnement Docker avec compatibilité Azure Container Registry.
Les dernières version de Windows 10 et Windows 2016 server embarquent une image Linux (ubuntu par exemple), le déploiement Docker se fera alors comme sur un serveur Linux classique, à peu de chose près.
Pour MacOS :
- utiliser également Docker Desktop qui permet de monter des containers sur un environnement Mac.
- boot2docker a été le premier outil à pouvoir gérer Docker sous MacOS, mais celui ci nécessitait de configurer une VM qui comportait un OS Linux + les applications packagés.
PostgreSQL sous Docker
Une fois notre installation Docker effectuée, nous pouvons voir que, pour le moment, notre Docker Engine ne fait pas grand chose.
Par d’image container à gérer
# docker images REPOSITORY TAG IMAGE ID CREATED SIZE
pas de processus container non plus
# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Nous pourrons alors débuter la création de containers.
Méthodes
Nous avons plusieurs méthodes nous permettant de construire des containers PostgreSQL.
- En allant chercher directement l’image la plus récente présente sur les dépôts Docker. Une simple commande “docker pull postgres” suffira à aller chercher cette image de PostgreSQL. En revanche, ce sera la toute dernière version qui sera télécharger. En septembre 2021, il s’agit de PostgreSQL 13.4.
- Il est possible de choisir l’image d’une version antérieure bien évidement. Pour cela, il faudra aller sur le site du Hub Docker et télécharger la version voulue. En fait il suffit juste de choisir la version PG dans notre commande “docker pull“. Par exemple, pour une version 9.6, c’est “docker pull postgres:9.6“.
- Il sera également possible de configurer soi même son image, grâce à un fichier “dockerfile“. Ce fichier texte nous laissera le choix entre de nombreuses options de configurations pour notre instance PostgreSQL, et nous permettra même de lancer des commandes de création de bases et/ou rôles sur l’instance. Le hub docker propose également des exemples de dockerfile, mais vous pourrez en trouver sur github ou autres sites communautaires.
Vous pourrez également le “construire” vous même en respectant certaines syntaxes précises.
Installation simple
Dans ce premier exemple, nous partirons d’une image pré-packagée directement extraite des dépôts linux Docker.
Rien de plus simple :
# docker run --name postgresql-container_test1 -p 5442:5432 -e POSTGRES_PASSWORD=test2021 -d postgres Unable to find image 'postgres:latest' locally latest: Pulling from library/postgres a330b6cecb98: Pull complete 3b0b899b4747: Pull complete cc0b2671a552: Pull complete 1a7c7505993a: Pull complete 02cdead79556: Pull complete 0d8fbe9259d6: Pull complete 974e6d476aa7: Pull complete e9abf0d5d0bc: Pull complete................ ..............
Et ce que l’on peut voir, avec le warning “Unable to find image ‘postgres:latest’ locally“, c’est que le processus de lancement est capable d’aller directement chercher sur les dépôts Docker une image PostgreSQL qu’il pourra alors utiliser pour démarrer notre container.
L’option -p est utilisé afin de changer le port par défaut. Ici nous utiliserons cette instance sur le 5442. L’option -e permet d’ajouter des variables d’environnement en paramètres, comme ici le password du user ‘postgres”. L’option -d démarre le container en mode ‘background’.
Si le message suivant s’affiche, c’est que l’on a réussi le lancement de notre container
Digest: sha256:97e5e91582e89514277912d4b7c95bceabdede3482e32395bcb40099abd9c506
Status: Downloaded newer image for postgres:latest
c0214f610a796c34b526c54217f36e0e02c1e43753c7eb0363ffd8dc07abceba
La dernière ligne nous donne l’ID crypté de notre processus container. Un “docker ps” permet de voir ce qui tourne
# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c0214f610a79 postgres "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 0.0.0.0:5442->5432/tcp, :::5442->5432/tcp postgresql-container_test1
Au prochain lancement d’un container PostgreSQL, avec cette même image, il ne sera pas nécessaire d’aller chercher les sources dans les dépôts. Cette image est maintenant enregistrée dans le docker local.
# docker images -a REPOSITORY TAG IMAGE ID CREATED SIZE postgres latest 346c7820a8fb 12 days ago 315MB
Nous pourrons tester une connexion, via psql, sous le user “postgres”.
# su - postgres $ psql -p 5442 -h 127.0.0.1 Password for user postgres: ******* psql (13.0, server 13.4 (Debian 13.4-1.pgdg100+1)) Type "help" for help. postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+------------+------------+----------------------- postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres (3 rows)
On peut voir que le “data_directory” est celui par défaut pour une instance PostgreSQL. Et que notre version est la toute dernière, à savoir la 13.4 compilée sur Debian !
postgres=# show data_directory; data_directory -------------------------- /var/lib/postgresql/data (1 row) postgres=# select version (); version ------------------------------------------------------------------------------------------------------------------ PostgreSQL 13.4 (Debian 13.4-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit (1 row)
Mais sur notre machine, bien entendu, rien dans “/var/lib/postgresql/data” ! Les fichiers PostgreSQL sont dans le container directement.
[postgres@ip-172-44-2-96 ~]$ ls -l /var/lib/postgresql ls: cannot access '/var/lib/postgresql': No such file or directory
On note la présence d’un nouveau montage de type “overlay”. c’est le driver de stockage générique fonctionnant avec Docker (remplacé par OverlayFS par la suite).
$ df -hT Filesystem Type Size Used Avail Use% Mounted on ..... overlay overlay 10G 4.2G 5.8G 42% /var/lib/docker/overlay2/50846bbd5d32162e138f91df136d7877fc33c7f2f17ef3e9c2d2de1704e7a190/merged
C’est dans ce montage nommé “/var/lib/docker/overlay2/50846bbd5d32162e138f91df136d7877fc33c7f2f17ef3e9c2d2de1704e7a190/merged’ que sont tous les packages système nécessaires uniquement à notre instance PostgreSQL ainsi que ses fichiers bases de données. Ce montage est présent tant que notre container est démarré.
Créer une image
Cette première étape nous a permis de créer un container PostgreSQL dans un temps très court.
Il est bien sur possible de gérer soi même ce que l’on souhaite installer dans son container, sa version, les options …..
Pour cela, nous allons créer un “dockerfile“. C’est un fichier texte que l’on utilisera pour la configuration de notre container.
Des exemples existent sur le site Github que nous pourrons utiliser et modifier selon notre convenance.
Nous naviguerons sur le site et irons copier le “dockerfile” que l’on souhaite en fonction de notre version. Par exemple, si nous souhaitons la version 11.13.
Nous chargerons les 2 fichiers. Tout d’abord “dockerfile” qui est le fichier appeler pour construire l’image, puis le “docker-entrypoint.sh” qui est le script lancé pour la partie configuration de l’application.
Ces 2 fichiers sont au format txt et donc utilisable sur un éditeur de texte classique.
# mkdir PG11 # cd PG11 # vi Dockerfile # vi docker-entrypoint.sh
Ne pas oublier de passer un chmod755 sur le fichier “docker-entrypoint.sh“, en effet, ce script est exécuté à chaque lancement de notre container Docker /!\
Dans un “dockerfile” chaque instruction va commencer par un ordre à interpréter. Les principaux ordres étant les suivants :
- FROM : les sources que l’on prend pour construire notre image (ce peut être une distribution linux, un package applicatif….)
- RUN : l’action qui devra être exécutée (installer des packages, télécharger des sources ….)
- ENV : définir des variables d’environnement utilisées durant le cycle de vie de construction de cette image.
- CMD : il s’agit de l’exécution par défaut prise par le dockerfile, en quelque sorte la dernière instruction à effectuer.
D’autres instructions facultatives peuvent apparaitre afin d’effectuer des actions bien précises :
- COPY : faire une copie d’un fichier local vers l’image
- VOLUME : définir un FS d’installation pour notre container
- ENTRYPOINT : un argument à exécuter lorsque notre container est lancé (ici nous exécuterons le script “docker-entrypoint.sh“)
On pourra par exemple aller changer le répertoire du PGDATA, et le faire pointer vers “/var/lib/postgresql/11” au lieu de “/var/lib/postgresql/data“.
# vi Dockerfile ..... ENV PGDATA /var/lib/postgresql/data # this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values) RUN mkdir -p "$PGDATA" && chown -R postgres:postgres "$PGDATA" && chmod 777 "$PGDATA" VOLUME /var/lib/postgresql/data
par
ENV PGDATA /var/lib/postgresql/11 # this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values) RUN mkdir -p "$PGDATA" && chown -R postgres:postgres "$PGDATA" && chmod 777 "$PGDATA" VOLUME /var/lib/postgresql/11
Dans le fichier “docker-entrypoint.sh”, nous pourrons définir l’option “data-checksums” à la création de l’instance.
Nous allons aussi créer un rôle nommé “manu”, et une base de test pour notre instance dont le nom sera donné en paramètre.
# vi docker-entrypoint.sh .... eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
par
eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") --data-checksums '"$POSTGRES_INITDB_ARGS"' "$@"'
et
docker_setup_db_manu() { psql -U ${POSTGRES_USER} -c "CREATE ROLE \"${POSTGRES_USER_MANU}\" with LOGIN CREATEDB PASSWORD '${USER_MANU_PASS}';" >/dev/null psql -U ${POSTGRES_USER} -c "CREATE DATABASE \"${DB_MANU}\" with owner '${POSTGRES_USER_MANU}';" >/dev/null } ... file_env 'POSTGRES_USER_MANU' 'manu' file_env 'DB_MANU'
Sur cette base, nous y installerons l’extension “pg_cron”.
La modification va se dérouler en 3 étapes, un peu comme cela peut être fait sur une installation on-prem.
- Tout d’abord, télécharger et installer le package présent sur les dépôts système PGDG.
- configurer dans le fichier “postgresql.conf” les données propres à cette extension et à son fonctionnement.
- créer celle ci directement sur la base souhaitée.
Les modifications devront être effectuées sur nos 2 fichiers “Dockerfile” et “docker-entrypoint.sh”
Sur le “Dockerfile”, installer le package pour cette extension” :
# vi Dockerfile ... FROM debian:stretch-slim ... RUN set -ex; \ .... apt-get install -y postgresql-11-cron;\ ....
Cela va ajouter le package “postgresql-11-cron” dans notre image packagée.
C’est dans le fichier “docker-entrypoint.sh” que nous configurerons l’extension en base.
Tout d’abord avec une procédure qui chargera dans le fichier “postgresql.conf” la configuration de notre extension. Le nom de la base ou sera installée cette extension sera celui que l’on passera en paramètre.
# vi docker-entrypoint.sh ....
docker_setup_pg_cron() {
{
echo "shared_preload_libraries = 'pg_cron'"
} >> "$PGDATA/postgresql.conf"
{
echo "cron.database_name='${DB_MANU}'"
} >> "$PGDATA/postgresql.conf"
}
Puis en la créant directement dans cette base passée en paramètre.
docker_setup_db_manu() {
....
psql -U ${POSTGRES_USER} -d ${DB_MANU} -c "CREATE EXTENSION pg_cron;" >/dev/null
}
Les procédures “docker_setup_pg_cron()” et “docker_setup_db_manu()” seront appelées dans la partie “main” de notre script “docker-entrypoint.sh“.
Construire l’image
La construction se fait à partir du “dockerfile“
# docker build . -t image_pg_11_manu
Tout un environnement Linux debian est chargé, puis mis à jour avec les derniers packages. Le -t permet de tagger notre image. A la fin de ce processus, nous devrions voir :
Successfully built f5ebf2fe8083 Successfully tagged image_pg_11_manu:latest
Cela veut dire que notre image est créée. Nous allons le vérifier
# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE image_pg_11_manu latest f5ebf2fe8083 29 seconds ago 283MB postgres latest 346c7820a8fb 2 weeks ago 315MB debian stretch-slim 27a36d9cbe0f 2 weeks ago 55.3MB
Nous avons donc notre image nommée “image_pg_11_manu” qui est prête. Notons que nous avons également conserver une partie des libraires propres à Debian pour construire cette image.
Démarrer le container
Nous passons au démarrage du container à partir de cette nouvelle image :
# docker run --name postgresql11manu -itd --restart always --publish 5438:5432 -e "POSTGRES_PASSWORD=test2021" -e "DB_MANU=manudb" -e "USER_MANU_PASS=test2021" image_pg_11_manu 62a270bc27a71585208fd2223ee92fc6a4e49eceb11ea4cd6ad2cf383bd3103d
Un id d’opération nous est alors renvoyé par la commande. Pour confirmer la création de ce container :
# docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 62a270bc27a7 image_pg_11_manu "docker-entrypoint.s…" 19 seconds ago Up 18 seconds 0.0.0.0:5438->5432/tcp, :::5438->5432/tcp postgresql11manu
Voir le log du container :
# docker logs --tail 100 --details postgresql11manu ... Success. You can now start the database server using: pg_ctl -D /var/lib/postgresql/11 -l logfile start waiting for server to start....2021-09-22 12:36:06.779 UTC [45] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" 2021-09-22 12:36:06.794 UTC [46] LOG: database system was shut down at 2021-09-22 12:36:06 UTC 2021-09-22 12:36:06.798 UTC [45] LOG: database system is ready to accept connections 2021-09-22 12:36:06.801 UTC [52] FATAL: database "manudb" does not exist 2021-09-22 12:36:06.803 UTC [45] LOG: background worker "pg_cron launcher" (PID 52) exited with exit code 1 done server started PostgreSQL init process complete; ready for start up. 2021-09-22 12:36:07.456 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 2021-09-22 12:36:07.456 UTC [1] LOG: listening on IPv6 address "::", port 5432 2021-09-22 12:36:07.459 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" 2021-09-22 12:36:07.473 UTC [92] LOG: database system was shut down at 2021-09-22 12:36:07 UTC 2021-09-22 12:36:07.477 UTC [1] LOG: database system is ready to accept connections 2021-09-22 12:36:07.486 UTC [98] LOG: pg_cron scheduler started
L’instance est démarrée sur le port 5432 dans le container, mais bien accessible via le port 5438 depuis notre serveur local, comme nous le voyons sur la commande “docker container ls“.
A première vue, nous voyons que PostgreSQL a du redémarrer car il ne trouvait pas la base “manudb”, celle ci a été créé par la suite.
Test de connexion avec psql :
[postgres]$ psql -p 5438 -h localhost Password for user postgres: psql (13.0, server 11.13 (Debian 11.13-1.pgdg90+1)) Type "help" for help. postgres=# \l+ List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges | Size | Tablespace | Description -----------+----------+----------+------------+------------+-----------------------+---------+------------+-------------------------------------------- manudb | manu | UTF8 | en_US.utf8 | en_US.utf8 | | 7529 kB | pg_default | postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | 7669 kB | pg_default | default administrative connection database template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +| 7529 kB | pg_default | unmodifiable empty database | | | | | postgres=CTc/postgres | | | template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +| 7529 kB | pg_default | default template for new databases | | | | | postgres=CTc/postgres | | | (4 rows) postgres=# \du+ List of roles Role name | Attributes | Member of | Description -----------+------------------------------------------------------------+-----------+------------- manu | Create DB | {} | postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} | postgres=# select version(); version ------------------------------------------------------------------------------------------------------------------------------------ PostgreSQL 11.13 (Debian 11.13-1.pgdg90+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, 64-bit (1 row)
L’extension “pg_cron” a bien été inscrite dans les librairies préchargées avec un fonctionnement sur notre base “manudb” passée en paramètre à l’appel du “docker run” :
# docker exec -it postgresql11manu psql -U postgres -c "select name, setting from pg_settings where name like '%shared%'" name | setting ----------------------------+--------- dynamic_shared_memory_type | posix shared_buffers | 16384 shared_preload_libraries | pg_cron # docker exec -it postgresql11manu psql -U postgres -c "select name, setting from pg_settings where name like '%cron%'" name | setting -----------------------------+----------- cron.database_name | manudb cron.host | localhost cron.log_run | on cron.log_statement | on cron.max_running_jobs | 32 cron.use_background_workers | off
# docker exec -it postgresql11manu psql -U postgres -d manudb -c "select * from pg_extension" extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition ---------+----------+--------------+----------------+------------+---------------------------+--------------- plpgsql | 10 | 11 | f | 1.0 | | pg_cron | 10 | 2200 | f | 1.3 | {16390,16388,16411,16409} | {"","","",""}
Notre instance a été créée avec succès. Elle est accessible depuis le port 5438 de notre machine, et notre rôle “manu” est créé avec sa base “manudb“.
Sur cette base, l’extension “pg_cron” est bien active.
Cette instance est une version 11.13.
Gérer son container
Il est possible d’accéder directement à notre container et de lancer un “bash” dessus, accessible depuis le compte root de notre machine locale.
# docker exec -it postgresql11manu bash root@62a270bc27a7:/# ls -l
A partir de la, nous pourrons accéder à notre instance PostgreSQL, accessible depuis le port par défaut, 5432.
postgres@62a270bc27a7:~$ psql psql (11.13 (Debian 11.13-1.pgdg90+1)) Type "help" for help. postgres=# \conninfo You are connected to database "postgres" as user "postgres" via socket in "/var/run/postgresql" at port "5432".
Attention, nous sommes sur un container, donc une image Linux “light”. De nombreuses commandes ne sont pas accessibles sur le bash courant.
Il sera possible de vérifier la configuration complète du container, avec l’option “inspect”. Le retour nous est donnée via un affichage JSON
# docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 62a270bc27a7 image_pg_11_manu "docker-entrypoint.s…" 51 minutes ago Up 51 minutes 0.0.0.0:5438->5432/tcp, :::5438->5432/tcp postgresql11manu
# docker container inspect postgresql11manu [ { "Id": "62a270bc27a71585208fd2223ee92fc6a4e49eceb11ea4cd6ad2cf383bd3103d", "Created": "2021-09-17T15:24:39.528442352Z", "Path": "docker-entrypoint.sh", "Args": [ "postgres" ], "State": { "Status": "running", "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 30809, "ExitCode": 0, "Error": "", "StartedAt": "2021-09-17T15:24:40.13504382Z", "FinishedAt": "0001-01-01T00:00:00Z" }, .........
La persistance des données
Il est possible de rendre persistent les fichiers de son instance sur son système local. Pour cela, nous utiliserons l’option -v ou —volume afin de faire pointer les fichiers bases de données sur un FS de notre machine. Ceci nous permettra de conserver une copie en locale des fichiers, même une fois notre container supprimé !
Créer le FS localement :
# mkdir /data/postgres/11.13
La suite consistera à créer un nouveau container, à partir de notre image construite auparavant, qui porte le nom “image_pg_11_manu”.
# docker run --name pg11manu_local -itd --restart always --publish 5439:5432 -e "POSTGRES_PASSWORD=test2021" -e "DB_MANU=manudb2" -e "USER_MANU_PASS=test2021" -v "/data/postgres/11.13:/var/lib/postgresql/11" image_pg_11_manu 51a6a4c8e9e3cf7b80fb1870415446ef9f38ac2847df0791e8e9332ad84f95f6
Nous créons donc un nouveau container, avec une base nommée “manudb2“, avec des fichiers persistés vers le nouveau dossier “/data/postgres/11.13” sur notre machine locale.
Si l’on vérifie les bases en se connectant sur le container :
# docker exec -it pg11manu_local psql -U postgres -c "\l" List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+------------+------------+----------------------- manudb2 | manu | UTF8 | en_US.utf8 | en_US.utf8 | postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres
Et sur le FS local :
# ls -lrt /data/postgres/11.13/ total 120 drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_dynshmem drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_commit_ts -rw-------. 1 systemd-coredump input 3 Sep 21 15:14 PG_VERSION drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_twophase drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_tblspc drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_snapshots drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_serial drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_replslot drwx------. 4 systemd-coredump input 4096 Sep 21 15:14 pg_multixact -rw-------. 1 systemd-coredump input 88 Sep 21 15:14 postgresql.auto.conf -rw-------. 1 systemd-coredump input 1636 Sep 21 15:14 pg_ident.conf drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_xact drwx------. 3 systemd-coredump input 4096 Sep 21 15:14 pg_wal drwx------. 2 systemd-coredump input 4096 Sep 21 15:14 pg_subtrans -rw-------. 1 systemd-coredump input 4535 Sep 21 15:14 pg_hba.conf drwx------. 6 systemd-coredump input 4096 Sep 21 15:14 base -rw-------. 1 systemd-coredump input 24084 Sep 21 15:16 postgresql.conf drwx------. 4 systemd-coredump input 4096 Sep 21 15:17 pg_logical drwx------. 2 systemd-coredump input 4096 Sep 21 15:18 pg_notify -rw-------. 1 systemd-coredump input 36 Sep 21 15:18 postmaster.opts drwx------. 2 systemd-coredump input 4096 Sep 21 15:18 global -rw-------. 1 systemd-coredump input 92 Sep 21 15:18 postmaster.pid drwx------. 2 systemd-coredump input 4096 Sep 21 15:18 pg_stat_tmp drwx------. 2 systemd-coredump input 4096 Sep 21 15:18 pg_stat
Nos fichiers sont la !
Passons, par exemple, le “shared_buffer” de 128M par défaut à 256M dans le fichier “postgresql.conf”.
# vi /data/postgres/11.13//postgresql.conf ... shared_buffers = 256MB
# docker container stop pg11manu_local # docker container start pg11manu_local # docker exec -it pg11manu_local psql -U postgres -c "show shared_buffers" shared_buffers ---------------- 256MB
Maintenant, supprimons complètement le container.
# docker stop pg11manu_local pg11manu_local # docker rm pg11manu_local pg11manu_local
Nous remarquons que les fichiers sont toujours la dans le FS
# ls -lrt /data/postgres/11.13/ | wc -l 25
Et s’il l’on recrée un container vers ce même volume !
# docker run --name pg11manu_local_2 -itd --restart always --publish 5439:5432 -e "POSTGRES_PASSWORD=test2021" -e "DB_MANU=manudb2" -e "USER_MANU_PASS=test2021" -v "/data/postgres/11.13:/var/lib/postgresql/11" image_pg_11_manu c4a7705e4d38709ae33e16a7f9791dc727bbca23dea32e8c11337575e255b40c
# docker exec -it pg11manu_local_2 psql -U postgres -c "\l" List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+------------+------------+----------------------- manudb2 | manu | UTF8 | en_US.utf8 | en_US.utf8 | postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + | | | | | postgres=CTc/postgres
# docker exec -it pg11manu_local_2 psql -U postgres -c "show shared_buffers" shared_buffers ---------------- 256MB (1 row)
la valeur de notre “shared_buffer” a été conservée car c’est celle qui est configurée sur le “postgresql.conf” sur le FS local.
Sauvegarder un container
Pour effectuer la sauvegarde de notre container PostgreSQL, 2 solutions.
– via un export du container complet dans un fichier “tar”
# docker container export pg11manu_local_2 -o container_manu_2.tar # ls -l container_manu_2.tar -rw-------. 1 root root 282522624 Sep 21 15:44 container_manu_2.tar
Attention, dans cette méthode, toute l’arborescence du montage “overlay” sera prise en charge dans l’archive. un “tar tvf” sur le fichier permettra de voir le contenu.
– via la création d’un “snapshot” pour faire une image de notre container. L’option -p permet de faire une image dite consistente, le container est mis en “pause” durant le backup.
# docker container commit -a "Emmanuel RAMI" -p pg11manu_local_2 backup_pg11manu_local_2 sha256:310b8475239840be9de470e574a94e74122b481bdc23abc300d2f2451388c649 # docker image ls backup_pg11manu_local_2 REPOSITORY TAG IMAGE ID CREATED SIZE backup_pg11manu_local_2 latest 310b84752398 2 minutes ago 283MB
Par la suite, nous pouvons envoyer vers un fichier “tar” le contenu de cette image
# docker save -o /data/postgres/backup/backup_pg11manu_local_2 backup_pg11manu_local_2 # ls -l /data/postgres/backup/backup_pg11manu_local_2 -rw-------. 1 root root 290162176 Sep 21 15:53 /data/postgres/backup/backup_pg11manu_local_2
A noter que les fichiers d’export du container et de l’image seront sensiblement aussi volumineux.
Il sera cependant possible d’externaliser son image via un “docker push” vers un compte sur DockerHub.
Supprimer le container et son image
Attention, avant de supprimer l’image, il faut tout d’abord supprimer les containers qui y sont rattachés. Sinon :
# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE backup_pg11manu_local_2 latest 310b84752398 16 hours ago 283MB image_pg_11_manu latest ea64f1b1c6ff 4 days ago 283MB postgres latest 346c7820a8fb 2 weeks ago 315MB debian stretch-slim 27a36d9cbe0f 2 weeks ago 55.3MB
# docker image rm image_pg_11_manu Error response from daemon: conflict: unable to remove repository reference "image_pg_11_manu" (must force) - container c4a7705e4d38 is using its referenced image ea64f1b1c6ff
Voyons le container portant l’ID c4a7705e4d38
# docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c4a7705e4d38 image_pg_11_manu "docker-entrypoint.s…" 16 hours ago Up About a minute 0.0.0.0:5439->5432/tcp, :::5439->5432/tcp pg11manu_local_2 1fcc85219a3e postgres "docker-entrypoint.s…" 40 hours ago Up About a minute 0.0.0.0:5440->5432/tcp, :::5440->5432/tcp postgresql13 62a270bc27a7 image_pg_11_manu "docker-entrypoint.s…" 4 days ago Up About a minute 0.0.0.0:5438->5432/tcp, :::5438->5432/tcp postgresql11manu
C’est le second container portant les fichiers PostgreSQL sur un FS en local. Nous allons donc arrêter et supprimer celui ci
# docker container stop pg11manu_local_2 pg11manu_local_2 # docker container rm pg11manu_local_2 pg11manu_local_2
Puis
# docker image rm image_pg_11_manu Untagged: image_pg_11_manu:latest
et
# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE backup_pg11manu_local_2 latest 310b84752398 16 hours ago 283MB postgres latest 346c7820a8fb 2 weeks ago 315MB debian stretch-slim 27a36d9cbe0f 2 weeks ago 55.3MB
N’hésitez pas si vous avez des commentaires 🙂
Merci à vous.
Emmanuel RAMI
Continuez votre lecture sur le blog :
- OrioleDB : la promesse d’un No-Vacuum (Sarah FAVEERE) [PostgreSQL]
- PostgreSQL : planifier une tâche avec pg_cron (Emmanuel RAMI) [Non classéPostgreSQL]
- PostgreSQL : la streaming replication en 12. (Emmanuel RAMI) [PostgreSQL]
- PostgreSQL sur la solution Kubernetes locale Minikube (Emmanuel RAMI) [ContainerPostgreSQL]
- PostgreSQL 17 : des sauvegardes incrémentales avec pg_basebackup (Emmanuel RAMI) [Non classéPostgreSQL]
Bonjour,
Article intéressant merci !
Sur les désavantages je rajouterai que certes Docker tourne sur Mac et Windows mais à quel prix ? celui de la lenteur…
Pour travailler sur une machine local (environnement de développement) je conseille vivement d’utiliser un Linux comme OS hôte, si on souhaite utiliser Docker.
Bonjour Adam
merci pour votre retour.
Je suis assez d’accord, j’ai essayé sur Windows, clairement le runtime Docker n’est pas performant et en plus trop compliqué pour la mise en place en terme de sécurité. Je recommande largement l’utilisation de la couche Ubuntu embarquée sur les dernières versions de Windows 10.
Cordialement.
Bonjour
j’ai lu votre article avec beaucoup d’attention car je tente de créer un conteneur postgres sous centos8 mais avec des incompréhensions sur l’entrypoint car des erreurs pas trop clair apparaissent
l’image se crée correctement mais lorsque j’essaye d’instancier mon image avec docker-compose j’obtiens une erreur OCI : Cannot start service test: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: “/usr/local/bin/docker-entrypoint.sh”: permission denied: unknown
Bonjour David
j’ai eu cette erreur moi aussi.
EN fait, avant d’importer votre fichier pour créer l’image, n’oubliez pas de faire un chmod 755 sur le fichier “docker-entreypoint.sh”.
Par la suite, relancer la creation de votre image avec :
$ docker build . -t
Merci Mr Rami pour cet article fort intéressant.
Je vais me lancer dans l’apprentissage de PostgreSql et de Docker sur un portable MacBook Pro donc votre article va m’aider dans ma démarche.
F.Lam