TL;DR : Un article de présentation - un guide pour comparer les environnements pour exécuter des applications dans des conteneurs. Les possibilités de Docker et d'autres systèmes similaires seront considérées.
Une petite histoire d'où tout cela vient
histoire
Le premier moyen bien connu d’isoler une application est le chroot. L'appel système du même nom permet de modifier le répertoire racine - donnant ainsi accès au programme qui l'a appelé, accès uniquement aux fichiers contenus dans ce répertoire. Mais si le programme dispose des droits de superutilisateur, il peut potentiellement « s'échapper » du chroot et accéder au système d'exploitation principal. De plus, en plus du changement de répertoire racine, les autres ressources (RAM, processeur), ainsi que l'accès au réseau, ne sont pas limités.
La méthode suivante consiste à lancer un système d'exploitation à part entière à l'intérieur du conteneur, en utilisant les mécanismes du noyau du système d'exploitation. Cette méthode est appelée différemment selon les systèmes d'exploitation, mais l'essence est la même : exécuter plusieurs systèmes d'exploitation indépendants, chacun fonctionnant sur le même noyau qui exécute le système d'exploitation principal. Cela inclut les prisons FreeBSD, les zones Solaris, OpenVZ et LXC pour Linux. L'isolation est fournie non seulement pour l'espace disque, mais également pour d'autres ressources, en particulier, chaque conteneur peut avoir des restrictions sur le temps processeur, la RAM et la bande passante du réseau. Par rapport au chroot, quitter le conteneur est plus difficile, car le superutilisateur dans le conteneur n'a accès qu'à l'intérieur du conteneur, cependant, en raison de la nécessité de maintenir à jour le système d'exploitation à l'intérieur du conteneur et de l'utilisation de l'ancien noyau. versions (pertinentes pour Linux, dans une moindre mesure FreeBSD), il existe une probabilité non nulle de « percer » le système d'isolation du noyau et d'accéder au système d'exploitation principal.
Au lieu de lancer un système d'exploitation à part entière dans un conteneur (avec un système d'initialisation, un gestionnaire de packages, etc.), les applications peuvent être lancées immédiatement, l'essentiel est d'offrir cette opportunité aux applications (présence des bibliothèques et autres fichiers). Cette idée a servi de base à la virtualisation d'applications conteneurisées, dont le représentant le plus important et le plus connu est Docker. Par rapport aux systèmes précédents, des mécanismes d'isolation plus flexibles, associés à la prise en charge intégrée des réseaux virtuels entre conteneurs et de l'état des applications à l'intérieur d'un conteneur, ont permis de créer un environnement holistique unique à partir d'un grand nombre de serveurs physiques pour exécuter des conteneurs - sans la nécessité d’une gestion manuelle des ressources.
Docker
Docker est le logiciel de conteneurisation d'applications le plus connu. Écrit en langage Go, il utilise les capacités habituelles du noyau Linux - groupes de contrôle, espaces de noms, capacités, etc., ainsi que les systèmes de fichiers Aufs et autres similaires pour économiser de l'espace disque.
Source : wikimédia
Architecture
Avant la version 1.11, Docker fonctionnait comme un service unique qui effectuait toutes les opérations avec les conteneurs : téléchargement d'images pour les conteneurs, lancement de conteneurs, traitement des requêtes API. Depuis la version 1.11, Docker a été scindé en plusieurs parties qui interagissent entre elles : containersd, pour gérer tout le cycle de vie des conteneurs (allocation de l'espace disque, téléchargement des images, mise en réseau, lancement, installation et suivi de l'état des conteneurs) et runC , environnements d'exécution de conteneurs, basés sur l'utilisation de groupes de contrôle et d'autres fonctionnalités du noyau Linux. Le service Docker lui-même demeure, mais il ne sert désormais qu'à traiter les requêtes API diffusées vers containers.
Installation et configuration
Ma façon préférée d'installer Docker est Docker-machine, qui, en plus d'installer et de configurer directement Docker sur des serveurs distants (y compris divers cloud), vous permet de travailler avec les systèmes de fichiers de serveurs distants et peut également exécuter diverses commandes.
Cependant, depuis 2018, le projet n'a pratiquement pas été développé, nous l'installerons donc de la manière habituelle pour la plupart des distributions Linux - en ajoutant un référentiel et en installant les packages nécessaires.
Cette méthode est également utilisée pour l'installation automatisée, par exemple à l'aide d'Ansible ou d'autres systèmes similaires, mais je ne la considérerai pas dans cet article.
L'installation s'effectuera sur Centos 7, j'utiliserai une machine virtuelle comme serveur, pour installer, il suffit d'exécuter les commandes ci-dessous :
# yum install -y yum-utils
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# yum install docker-ce docker-ce-cli containerd.io
Après l'installation, vous devez démarrer le service, le mettre en chargement automatique :
# systemctl enable docker
# systemctl start docker
# firewall-cmd --zone=public --add-port=2377/tcp --permanent
De plus, vous pouvez créer un groupe Docker, dont les utilisateurs pourront travailler avec Docker sans sudo, configurer la journalisation, activer l'accès à l'API de l'extérieur, n'oubliez pas d'affiner le pare-feu (tout ce qui n'est pas autorisé est interdit dans les exemples ci-dessus et ci-dessous (j'ai omis cela pour des raisons de simplicité et de visualisation), mais je n'entrerai pas dans les détails ici.
Autres fonctionnalités
En plus de la machine Docker ci-dessus, il existe également un registre Docker, un outil de stockage d'images pour les conteneurs, ainsi que Docker Compose - un outil pour automatiser le déploiement d'applications dans des conteneurs, les fichiers YAML sont utilisés pour créer et configurer des conteneurs et d'autres éléments connexes (par exemple, les réseaux, les systèmes de fichiers persistants pour le stockage des données).
Il peut également être utilisé pour organiser des pipelines pour CICD. Une autre fonctionnalité intéressante est le travail en mode cluster, appelé mode swarm (avant la version 1.12, il était connu sous le nom de docker swarm), qui vous permet d'assembler une seule infrastructure à partir de plusieurs serveurs pour exécuter des conteneurs. Il existe une prise en charge d'un réseau virtuel au-dessus de tous les serveurs, un équilibreur de charge intégré, ainsi qu'une prise en charge des secrets pour les conteneurs.
Les fichiers YAML de docker compose peuvent être utilisés pour de tels clusters avec des modifications mineures, automatisant entièrement la maintenance des clusters de petite et moyenne taille à diverses fins. Pour les grands clusters, Kubernetes est préférable car les coûts de maintenance en mode essaim peuvent dépasser ceux de Kubernetes. En plus de runC, comme environnement d'exécution pour les conteneurs, vous pouvez installer, par exemple
Travailler avec Docker
Après l'installation et la configuration, nous essaierons de construire un cluster dans lequel nous déploierons GitLab et Docker Registry pour l'équipe de développement. En tant que serveurs, j'utiliserai trois machines virtuelles, sur lesquelles je déploierai en plus le FS distribué GlusterFS, je l'utiliserai comme stockage de volumes Docker, par exemple, pour exécuter une version sécurisée du registre Docker. Composants clés à exécuter : Docker Registry, Postgresql, Redis, GitLab avec prise en charge de GitLab Runner au-dessus de Swarm. Postgresql sera lancé avec le clustering
Pour déployer GlusterFS sur tous les serveurs (ils s'appellent node1, node2, node3), vous devez installer les packages, activer le pare-feu, créer les répertoires nécessaires :
# yum -y install centos-release-gluster7
# yum -y install glusterfs-server
# systemctl enable glusterd
# systemctl start glusterd
# firewall-cmd --add-service=glusterfs --permanent
# firewall-cmd --reload
# mkdir -p /srv/gluster
# mkdir -p /srv/docker
# echo "$(hostname):/docker /srv/docker glusterfs defaults,_netdev 0 0" >> /etc/fstab
Après l'installation, le travail de configuration de GlusterFS doit être poursuivi à partir d'un nœud, par exemple le nœud 1 :
# gluster peer probe node2
# gluster peer probe node3
# gluster volume create docker replica 3 node1:/srv/gluster node2:/srv/gluster node3:/srv/gluster force
# gluster volume start docker
Ensuite, vous devez monter le volume résultant (la commande doit être exécutée sur tous les serveurs) :
# mount /srv/docker
Le mode Swarm est configuré sur l'un des serveurs, qui sera Leader, les autres devront rejoindre le cluster, donc le résultat de l'exécution de la commande sur le premier serveur devra être copié et exécuté sur le reste.
Configuration initiale du cluster, j'exécute la commande sur node1 :
# docker swarm init
Swarm initialized: current node (a5jpfrh5uvo7svzz1ajduokyq) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-0c5mf7mvzc7o7vjk0wngno2dy70xs95tovfxbv4tqt9280toku-863hyosdlzvd76trfptd4xnzd xx.xx.xx.xx:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
# docker swarm join-token manager
Copiez le résultat de la deuxième commande, exécutez sur node2 et node3 :
# docker swarm join --token SWMTKN-x-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxx xx.xx.xx.xx:2377
This node joined a swarm as a manager.
Ceci termine la configuration préliminaire des serveurs, commençons la configuration des services, les commandes à exécuter seront lancées depuis le nœud1, sauf indication contraire.
Tout d'abord, créons des réseaux pour les conteneurs :
# docker network create --driver=overlay etcd
# docker network create --driver=overlay pgsql
# docker network create --driver=overlay redis
# docker network create --driver=overlay traefik
# docker network create --driver=overlay gitlab
Ensuite on marque les serveurs, cela est nécessaire pour lier certains services aux serveurs :
# docker node update --label-add nodename=node1 node1
# docker node update --label-add nodename=node2 node2
# docker node update --label-add nodename=node3 node3
Ensuite, nous créons des répertoires pour stocker les données etcd, le stockage KV dont Traefik et Stolon ont besoin. Semblable à Postgresql, ce seront des conteneurs liés aux serveurs, nous exécutons donc cette commande sur tous les serveurs :
# mkdir -p /srv/etcd
Ensuite, créez un fichier pour configurer etcd et appliquez-le :
00etcd.yml
version: '3.7'
services:
etcd1:
image: quay.io/coreos/etcd:latest
hostname: etcd1
command:
- etcd
- --name=etcd1
- --data-dir=/data.etcd
- --advertise-client-urls=http://etcd1:2379
- --listen-client-urls=http://0.0.0.0:2379
- --initial-advertise-peer-urls=http://etcd1:2380
- --listen-peer-urls=http://0.0.0.0:2380
- --initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- --initial-cluster-state=new
- --initial-cluster-token=etcd-cluster
networks:
- etcd
volumes:
- etcd1vol:/data.etcd
deploy:
replicas: 1
placement:
constraints: [node.labels.nodename == node1]
etcd2:
image: quay.io/coreos/etcd:latest
hostname: etcd2
command:
- etcd
- --name=etcd2
- --data-dir=/data.etcd
- --advertise-client-urls=http://etcd2:2379
- --listen-client-urls=http://0.0.0.0:2379
- --initial-advertise-peer-urls=http://etcd2:2380
- --listen-peer-urls=http://0.0.0.0:2380
- --initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- --initial-cluster-state=new
- --initial-cluster-token=etcd-cluster
networks:
- etcd
volumes:
- etcd2vol:/data.etcd
deploy:
replicas: 1
placement:
constraints: [node.labels.nodename == node2]
etcd3:
image: quay.io/coreos/etcd:latest
hostname: etcd3
command:
- etcd
- --name=etcd3
- --data-dir=/data.etcd
- --advertise-client-urls=http://etcd3:2379
- --listen-client-urls=http://0.0.0.0:2379
- --initial-advertise-peer-urls=http://etcd3:2380
- --listen-peer-urls=http://0.0.0.0:2380
- --initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
- --initial-cluster-state=new
- --initial-cluster-token=etcd-cluster
networks:
- etcd
volumes:
- etcd3vol:/data.etcd
deploy:
replicas: 1
placement:
constraints: [node.labels.nodename == node3]
volumes:
etcd1vol:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/etcd"
etcd2vol:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/etcd"
etcd3vol:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/etcd"
networks:
etcd:
external: true
# docker stack deploy --compose-file 00etcd.yml etcd
Au bout d'un moment, on vérifie que le cluster etcd est monté :
# docker exec $(docker ps | awk '/etcd/ {print $1}') etcdctl member list
ade526d28b1f92f7: name=etcd1 peerURLs=http://etcd1:2380 clientURLs=http://etcd1:2379 isLeader=false
bd388e7810915853: name=etcd3 peerURLs=http://etcd3:2380 clientURLs=http://etcd3:2379 isLeader=false
d282ac2ce600c1ce: name=etcd2 peerURLs=http://etcd2:2380 clientURLs=http://etcd2:2379 isLeader=true
# docker exec $(docker ps | awk '/etcd/ {print $1}') etcdctl cluster-health
member ade526d28b1f92f7 is healthy: got healthy result from http://etcd1:2379
member bd388e7810915853 is healthy: got healthy result from http://etcd3:2379
member d282ac2ce600c1ce is healthy: got healthy result from http://etcd2:2379
cluster is healthy
Créez des répertoires pour Postgresql, exécutez la commande sur tous les serveurs :
# mkdir -p /srv/pgsql
Ensuite, créez un fichier pour configurer Postgresql :
01pgsql.yml
version: '3.7'
services:
pgsentinel:
image: sorintlab/stolon:master-pg10
command:
- gosu
- stolon
- stolon-sentinel
- --cluster-name=stolon-cluster
- --store-backend=etcdv3
- --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
- --log-level=debug
networks:
- etcd
- pgsql
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 30s
order: stop-first
failure_action: pause
pgkeeper1:
image: sorintlab/stolon:master-pg10
hostname: pgkeeper1
command:
- gosu
- stolon
- stolon-keeper
- --pg-listen-address=pgkeeper1
- --pg-repl-username=replica
- --uid=pgkeeper1
- --pg-su-username=postgres
- --pg-su-passwordfile=/run/secrets/pgsql
- --pg-repl-passwordfile=/run/secrets/pgsql_repl
- --data-dir=/var/lib/postgresql/data
- --cluster-name=stolon-cluster
- --store-backend=etcdv3
- --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
networks:
- etcd
- pgsql
environment:
- PGDATA=/var/lib/postgresql/data
volumes:
- pgkeeper1:/var/lib/postgresql/data
secrets:
- pgsql
- pgsql_repl
deploy:
replicas: 1
placement:
constraints: [node.labels.nodename == node1]
pgkeeper2:
image: sorintlab/stolon:master-pg10
hostname: pgkeeper2
command:
- gosu
- stolon
- stolon-keeper
- --pg-listen-address=pgkeeper2
- --pg-repl-username=replica
- --uid=pgkeeper2
- --pg-su-username=postgres
- --pg-su-passwordfile=/run/secrets/pgsql
- --pg-repl-passwordfile=/run/secrets/pgsql_repl
- --data-dir=/var/lib/postgresql/data
- --cluster-name=stolon-cluster
- --store-backend=etcdv3
- --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
networks:
- etcd
- pgsql
environment:
- PGDATA=/var/lib/postgresql/data
volumes:
- pgkeeper2:/var/lib/postgresql/data
secrets:
- pgsql
- pgsql_repl
deploy:
replicas: 1
placement:
constraints: [node.labels.nodename == node2]
pgkeeper3:
image: sorintlab/stolon:master-pg10
hostname: pgkeeper3
command:
- gosu
- stolon
- stolon-keeper
- --pg-listen-address=pgkeeper3
- --pg-repl-username=replica
- --uid=pgkeeper3
- --pg-su-username=postgres
- --pg-su-passwordfile=/run/secrets/pgsql
- --pg-repl-passwordfile=/run/secrets/pgsql_repl
- --data-dir=/var/lib/postgresql/data
- --cluster-name=stolon-cluster
- --store-backend=etcdv3
- --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
networks:
- etcd
- pgsql
environment:
- PGDATA=/var/lib/postgresql/data
volumes:
- pgkeeper3:/var/lib/postgresql/data
secrets:
- pgsql
- pgsql_repl
deploy:
replicas: 1
placement:
constraints: [node.labels.nodename == node3]
postgresql:
image: sorintlab/stolon:master-pg10
command: gosu stolon stolon-proxy --listen-address 0.0.0.0 --cluster-name stolon-cluster --store-backend=etcdv3 --store-endpoints http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
networks:
- etcd
- pgsql
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 30s
order: stop-first
failure_action: rollback
volumes:
pgkeeper1:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/pgsql"
pgkeeper2:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/pgsql"
pgkeeper3:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/pgsql"
secrets:
pgsql:
file: "/srv/docker/postgres"
pgsql_repl:
file: "/srv/docker/replica"
networks:
etcd:
external: true
pgsql:
external: true
Nous générons des secrets, appliquons le fichier :
# </dev/urandom tr -dc 234567890qwertyuopasdfghjkzxcvbnmQWERTYUPASDFGHKLZXCVBNM | head -c $(((RANDOM%3)+15)) > /srv/docker/replica
# </dev/urandom tr -dc 234567890qwertyuopasdfghjkzxcvbnmQWERTYUPASDFGHKLZXCVBNM | head -c $(((RANDOM%3)+15)) > /srv/docker/postgres
# docker stack deploy --compose-file 01pgsql.yml pgsql
Quelque temps plus tard (regardez le résultat de la commande service docker lsque tous les services ont augmenté) initialisez le cluster Postgresql :
# docker exec $(docker ps | awk '/pgkeeper/ {print $1}') stolonctl --cluster-name=stolon-cluster --store-backend=etcdv3 --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379 init
Vérification de l'état de préparation du cluster Postgresql :
# docker exec $(docker ps | awk '/pgkeeper/ {print $1}') stolonctl --cluster-name=stolon-cluster --store-backend=etcdv3 --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379 status
=== Active sentinels ===
ID LEADER
26baa11d false
74e98768 false
a8cb002b true
=== Active proxies ===
ID
4d233826
9f562f3b
b0c79ff1
=== Keepers ===
UID HEALTHY PG LISTENADDRESS PG HEALTHY PG WANTEDGENERATION PG CURRENTGENERATION
pgkeeper1 true pgkeeper1:5432 true 2 2
pgkeeper2 true pgkeeper2:5432 true 2 2
pgkeeper3 true pgkeeper3:5432 true 3 3
=== Cluster Info ===
Master Keeper: pgkeeper3
===== Keepers/DB tree =====
pgkeeper3 (master)
├─pgkeeper2
└─pgkeeper1
Nous configurons traefik pour ouvrir l'accès aux conteneurs depuis l'extérieur :
03traefik.yml
version: '3.7'
services:
traefik:
image: traefik:latest
command: >
--log.level=INFO
--providers.docker=true
--entryPoints.web.address=:80
--providers.providersThrottleDuration=2
--providers.docker.watch=true
--providers.docker.swarmMode=true
--providers.docker.swarmModeRefreshSeconds=15s
--providers.docker.exposedbydefault=false
--accessLog.bufferingSize=0
--api=true
--api.dashboard=true
--api.insecure=true
networks:
- traefik
ports:
- 80:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock
deploy:
replicas: 3
placement:
constraints:
- node.role == manager
preferences:
- spread: node.id
labels:
- traefik.enable=true
- traefik.http.routers.traefik.rule=Host(`traefik.example.com`)
- traefik.http.services.traefik.loadbalancer.server.port=8080
- traefik.docker.network=traefik
networks:
traefik:
external: true
# docker stack deploy --compose-file 03traefik.yml traefik
On démarre Redis Cluster, pour cela on crée un répertoire de stockage sur tous les nœuds :
# mkdir -p /srv/redis
05redis.yml
version: '3.7'
services:
redis-master:
image: 'bitnami/redis:latest'
networks:
- redis
ports:
- '6379:6379'
environment:
- REDIS_REPLICATION_MODE=master
- REDIS_PASSWORD=xxxxxxxxxxx
deploy:
mode: global
restart_policy:
condition: any
volumes:
- 'redis:/opt/bitnami/redis/etc/'
redis-replica:
image: 'bitnami/redis:latest'
networks:
- redis
ports:
- '6379'
depends_on:
- redis-master
environment:
- REDIS_REPLICATION_MODE=slave
- REDIS_MASTER_HOST=redis-master
- REDIS_MASTER_PORT_NUMBER=6379
- REDIS_MASTER_PASSWORD=xxxxxxxxxxx
- REDIS_PASSWORD=xxxxxxxxxxx
deploy:
mode: replicated
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: any
redis-sentinel:
image: 'bitnami/redis:latest'
networks:
- redis
ports:
- '16379'
depends_on:
- redis-master
- redis-replica
entrypoint: |
bash -c 'bash -s <<EOF
"/bin/bash" -c "cat <<EOF > /opt/bitnami/redis/etc/sentinel.conf
port 16379
dir /tmp
sentinel monitor master-node redis-master 6379 2
sentinel down-after-milliseconds master-node 5000
sentinel parallel-syncs master-node 1
sentinel failover-timeout master-node 5000
sentinel auth-pass master-node xxxxxxxxxxx
sentinel announce-ip redis-sentinel
sentinel announce-port 16379
EOF"
"/bin/bash" -c "redis-sentinel /opt/bitnami/redis/etc/sentinel.conf"
EOF'
deploy:
mode: global
restart_policy:
condition: any
volumes:
redis:
driver: local
driver_opts:
type: 'none'
o: 'bind'
device: "/srv/redis"
networks:
redis:
external: true
# docker stack deploy --compose-file 05redis.yml redis
Ajouter le registre Docker :
06registre.yml
version: '3.7'
services:
registry:
image: registry:2.6
networks:
- traefik
volumes:
- registry_data:/var/lib/registry
deploy:
replicas: 1
placement:
constraints: [node.role == manager]
restart_policy:
condition: on-failure
labels:
- traefik.enable=true
- traefik.http.routers.registry.rule=Host(`registry.example.com`)
- traefik.http.services.registry.loadbalancer.server.port=5000
- traefik.docker.network=traefik
volumes:
registry_data:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/docker/registry"
networks:
traefik:
external: true
# mkdir /srv/docker/registry
# docker stack deploy --compose-file 06registry.yml registry
Et enfin - GitLab :
08gitlab-runner.yml
version: '3.7'
services:
gitlab:
image: gitlab/gitlab-ce:latest
networks:
- pgsql
- redis
- traefik
- gitlab
ports:
- 22222:22
environment:
GITLAB_OMNIBUS_CONFIG: |
postgresql['enable'] = false
redis['enable'] = false
gitlab_rails['registry_enabled'] = false
gitlab_rails['db_username'] = "gitlab"
gitlab_rails['db_password'] = "XXXXXXXXXXX"
gitlab_rails['db_host'] = "postgresql"
gitlab_rails['db_port'] = "5432"
gitlab_rails['db_database'] = "gitlab"
gitlab_rails['db_adapter'] = 'postgresql'
gitlab_rails['db_encoding'] = 'utf8'
gitlab_rails['redis_host'] = 'redis-master'
gitlab_rails['redis_port'] = '6379'
gitlab_rails['redis_password'] = 'xxxxxxxxxxx'
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.yandex.ru"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "[email protected]"
gitlab_rails['smtp_password'] = "xxxxxxxxx"
gitlab_rails['smtp_domain'] = "example.com"
gitlab_rails['gitlab_email_from'] = '[email protected]'
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_tls'] = true
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_openssl_verify_mode'] = 'peer'
external_url 'http://gitlab.example.com/'
gitlab_rails['gitlab_shell_ssh_port'] = 22222
volumes:
- gitlab_conf:/etc/gitlab
- gitlab_logs:/var/log/gitlab
- gitlab_data:/var/opt/gitlab
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.role == manager
labels:
- traefik.enable=true
- traefik.http.routers.gitlab.rule=Host(`gitlab.example.com`)
- traefik.http.services.gitlab.loadbalancer.server.port=80
- traefik.docker.network=traefik
gitlab-runner:
image: gitlab/gitlab-runner:latest
networks:
- gitlab
volumes:
- gitlab_runner_conf:/etc/gitlab
- /var/run/docker.sock:/var/run/docker.sock
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.role == manager
volumes:
gitlab_conf:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/docker/gitlab/conf"
gitlab_logs:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/docker/gitlab/logs"
gitlab_data:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/docker/gitlab/data"
gitlab_runner_conf:
driver: local
driver_opts:
type: none
o: bind
device: "/srv/docker/gitlab/runner"
networks:
pgsql:
external: true
redis:
external: true
traefik:
external: true
gitlab:
external: true
# mkdir -p /srv/docker/gitlab/conf
# mkdir -p /srv/docker/gitlab/logs
# mkdir -p /srv/docker/gitlab/data
# mkdir -p /srv/docker/gitlab/runner
# docker stack deploy --compose-file 08gitlab-runner.yml gitlab
L'état final du cluster et des services :
# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
lef9n3m92buq etcd_etcd1 replicated 1/1 quay.io/coreos/etcd:latest
ij6uyyo792x5 etcd_etcd2 replicated 1/1 quay.io/coreos/etcd:latest
fqttqpjgp6pp etcd_etcd3 replicated 1/1 quay.io/coreos/etcd:latest
hq5iyga28w33 gitlab_gitlab replicated 1/1 gitlab/gitlab-ce:latest *:22222->22/tcp
dt7s6vs0q4qc gitlab_gitlab-runner replicated 1/1 gitlab/gitlab-runner:latest
k7uoezno0h9n pgsql_pgkeeper1 replicated 1/1 sorintlab/stolon:master-pg10
cnrwul4r4nse pgsql_pgkeeper2 replicated 1/1 sorintlab/stolon:master-pg10
frflfnpty7tr pgsql_pgkeeper3 replicated 1/1 sorintlab/stolon:master-pg10
x7pqqchi52kq pgsql_pgsentinel replicated 3/3 sorintlab/stolon:master-pg10
mwu2wl8fti4r pgsql_postgresql replicated 3/3 sorintlab/stolon:master-pg10
9hkbe2vksbzb redis_redis-master global 3/3 bitnami/redis:latest *:6379->6379/tcp
l88zn8cla7dc redis_redis-replica replicated 3/3 bitnami/redis:latest *:30003->6379/tcp
1utp309xfmsy redis_redis-sentinel global 3/3 bitnami/redis:latest *:30002->16379/tcp
oteb824ylhyp registry_registry replicated 1/1 registry:2.6
qovrah8nzzu8 traefik_traefik replicated 3/3 traefik:latest *:80->80/tcp, *:443->443/tcp
Que peut-on améliorer d’autre ? Assurez-vous de configurer Traefik pour qu'il fonctionne avec les conteneurs https, ajoutez le cryptage tls pour Postgresql et Redis. Mais en général, vous pouvez déjà le donner aux développeurs sous forme de PoC. Voyons maintenant les alternatives à Docker.
Podman
Autre moteur assez connu pour faire tourner des conteneurs regroupés par pods (pods, groupes de conteneurs déployés ensemble). Contrairement à Docker, il ne nécessite aucun service pour exécuter les conteneurs, tout le travail est effectué via la bibliothèque libpod. Également écrit en Go, nécessite un runtime compatible OCI pour exécuter des conteneurs comme runC.
Travailler avec Podman ressemble en général à celui de Docker, dans la mesure où vous pouvez le faire comme ceci (affirmé par beaucoup de ceux qui l'ont essayé, y compris l'auteur de cet article) :
$ alias docker=podman
et vous pouvez continuer à travailler. En général, la situation avec Podman est très intéressante, car si les premières versions de Kubernetes fonctionnaient avec Docker, alors depuis 2015 environ, après avoir standardisé le monde des conteneurs (OCI - Open Container Initiative) et divisé Docker en containersd et runC, une alternative à Docker est en cours de développement pour fonctionner dans Kubernetes : CRI-O. Podman à cet égard est une alternative à Docker, construite sur les principes de Kubernetes, y compris le regroupement de conteneurs, mais l'objectif principal du projet est d'exécuter des conteneurs de style Docker sans services supplémentaires. Pour des raisons évidentes, il n'y a pas de mode essaim, puisque les développeurs disent clairement que si vous avez besoin d'un cluster, prenez Kubernetes.
Installation
Pour installer sur Centos 7, activez simplement le référentiel Extras, puis installez le tout avec la commande :
# yum -y install podman
Autres fonctionnalités
Podman peut générer des unités pour systemd, résolvant ainsi le problème du démarrage des conteneurs après un redémarrage du serveur. De plus, systemd est déclaré fonctionner correctement en tant que pid 1 dans le conteneur. Pour créer des conteneurs, il existe un outil buildah distinct, il existe également des outils tiers - analogues de docker-compose, qui génère également des fichiers de configuration compatibles Kubernetes, de sorte que la transition de Podman à Kubernetes est aussi simple que possible.
Travailler avec Podman
Puisqu'il n'y a pas de mode essaim (il est censé passer à Kubernetes si un cluster est nécessaire), nous l'assemblerons dans des conteneurs séparés.
Installez podman-compose :
# yum -y install python3-pip
# pip3 install podman-compose
Le fichier de configuration résultant pour podman est légèrement différent, car par exemple nous avons dû déplacer une section de volumes distincte directement vers la section services.
gitlab-podman.yml
version: '3.7'
services:
gitlab:
image: gitlab/gitlab-ce:latest
hostname: gitlab.example.com
restart: unless-stopped
environment:
GITLAB_OMNIBUS_CONFIG: |
gitlab_rails['gitlab_shell_ssh_port'] = 22222
ports:
- "80:80"
- "22222:22"
volumes:
- /srv/podman/gitlab/conf:/etc/gitlab
- /srv/podman/gitlab/data:/var/opt/gitlab
- /srv/podman/gitlab/logs:/var/log/gitlab
networks:
- gitlab
gitlab-runner:
image: gitlab/gitlab-runner:alpine
restart: unless-stopped
depends_on:
- gitlab
volumes:
- /srv/podman/gitlab/runner:/etc/gitlab-runner
- /var/run/docker.sock:/var/run/docker.sock
networks:
- gitlab
networks:
gitlab:
# podman-compose -f gitlab-runner.yml -d up
Résultat des travaux :
# podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da53da946c01 docker.io/gitlab/gitlab-runner:alpine run --user=gitlab... About a minute ago Up About a minute ago 0.0.0.0:22222->22/tcp, 0.0.0.0:80->80/tcp root_gitlab-runner_1
781c0103c94a docker.io/gitlab/gitlab-ce:latest /assets/wrapper About a minute ago Up About a minute ago 0.0.0.0:22222->22/tcp, 0.0.0.0:80->80/tcp root_gitlab_1
Voyons ce que cela générera pour systemd et kubernetes, pour cela nous devons connaître le nom ou l'identifiant du pod :
# podman pod ls
POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
71fc2b2a5c63 root Running 11 minutes ago 3 db40ab8bf84b
Kubernetes :
# podman generate kube 71fc2b2a5c63
# Generation of Kubernetes YAML is still under development!
#
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-1.6.4
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2020-07-29T19:22:40Z"
labels:
app: root
name: root
spec:
containers:
- command:
- /assets/wrapper
env:
- name: PATH
value: /opt/gitlab/embedded/bin:/opt/gitlab/bin:/assets:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: HOSTNAME
value: gitlab.example.com
- name: container
value: podman
- name: GITLAB_OMNIBUS_CONFIG
value: |
gitlab_rails['gitlab_shell_ssh_port'] = 22222
- name: LANG
value: C.UTF-8
image: docker.io/gitlab/gitlab-ce:latest
name: rootgitlab1
ports:
- containerPort: 22
hostPort: 22222
protocol: TCP
- containerPort: 80
hostPort: 80
protocol: TCP
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities: {}
privileged: false
readOnlyRootFilesystem: false
volumeMounts:
- mountPath: /var/opt/gitlab
name: srv-podman-gitlab-data
- mountPath: /var/log/gitlab
name: srv-podman-gitlab-logs
- mountPath: /etc/gitlab
name: srv-podman-gitlab-conf
workingDir: /
- command:
- run
- --user=gitlab-runner
- --working-directory=/home/gitlab-runner
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- name: TERM
value: xterm
- name: HOSTNAME
- name: container
value: podman
image: docker.io/gitlab/gitlab-runner:alpine
name: rootgitlab-runner1
resources: {}
securityContext:
allowPrivilegeEscalation: true
capabilities: {}
privileged: false
readOnlyRootFilesystem: false
volumeMounts:
- mountPath: /etc/gitlab-runner
name: srv-podman-gitlab-runner
- mountPath: /var/run/docker.sock
name: var-run-docker.sock
workingDir: /
volumes:
- hostPath:
path: /srv/podman/gitlab/runner
type: Directory
name: srv-podman-gitlab-runner
- hostPath:
path: /var/run/docker.sock
type: File
name: var-run-docker.sock
- hostPath:
path: /srv/podman/gitlab/data
type: Directory
name: srv-podman-gitlab-data
- hostPath:
path: /srv/podman/gitlab/logs
type: Directory
name: srv-podman-gitlab-logs
- hostPath:
path: /srv/podman/gitlab/conf
type: Directory
name: srv-podman-gitlab-conf
status: {}
système :
# podman generate systemd 71fc2b2a5c63
# pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020
[Unit]
Description=Podman pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
Documentation=man:podman-generate-systemd(1)
Requires=container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
Before=container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa
ExecStop=/usr/bin/podman stop -t 10 db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa/userdata/conmon.pid
[Install]
WantedBy=multi-user.target
# container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020
[Unit]
Description=Podman container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
Documentation=man:podman-generate-systemd(1)
RefuseManualStart=yes
RefuseManualStop=yes
BindsTo=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
After=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864
ExecStop=/usr/bin/podman stop -t 10 da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864/userdata/conmon.pid
[Install]
WantedBy=multi-user.target
# container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020
[Unit]
Description=Podman container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service
Documentation=man:podman-generate-systemd(1)
RefuseManualStart=yes
RefuseManualStop=yes
BindsTo=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
After=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start 781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3
ExecStop=/usr/bin/podman stop -t 10 781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3/userdata/conmon.pid
[Install]
WantedBy=multi-user.target
Malheureusement, à part exécuter des conteneurs, l'unité générée pour systemd ne fait rien d'autre (par exemple, nettoyer les anciens conteneurs lorsqu'un tel service est redémarré), vous devrez donc ajouter de telles choses vous-même.
En principe, Podman suffit pour essayer ce que sont les conteneurs, transférer d'anciennes configurations pour docker-compose, puis se diriger vers Kubernetes, si nécessaire, sur un cluster, ou obtenir une alternative plus simple à utiliser à Docker.
rkt
Projet
Clapotis
Plus
résultats
La situation avec Kubernetes est très intéressante : d'une part, avec Docker, vous pouvez assembler un cluster (en mode essaim), avec lequel vous pouvez même exécuter des environnements de production pour les clients, c'est particulièrement vrai pour les petites équipes (3-5 personnes ), ou avec une petite charge globale, ou le manque de désir de comprendre les subtilités de la mise en place de Kubernetes, y compris pour des charges élevées.
Podman n'offre pas une compatibilité totale, mais il présente un avantage important : la compatibilité avec Kubernetes, y compris des outils supplémentaires (buildah et autres). J'aborderai donc le choix d'un outil de travail comme suit : pour les petites équipes, ou avec un budget limité - Docker (avec un éventuel mode essaim), pour développer pour moi sur un hôte local personnel - camarades Podman, et pour tout le monde -Kubernetes.
Je ne suis pas sûr que la situation avec Docker ne changera pas à l'avenir, après tout, ils sont des pionniers, et standardisent aussi lentement étape par étape, mais Podman, avec toutes ses lacunes (fonctionne uniquement sous Linux, il n'y a pas de clustering , l'assemblage et d'autres actions sont des décisions de tiers), l'avenir est plus clair, j'invite donc tout le monde à discuter de ces résultats dans les commentaires.
PS Le 3 août, nous lançons "
Coût de précommande avant sortie : 5000 roubles. Le programme "Docker Video Course" peut être trouvé
Source: habr.com