TL;DR: un articol de revizuire-ghid pentru compararea medii pentru rularea aplicațiilor în containere. Vor fi analizate capabilitățile Docker și ale altor sisteme similare.

Puțin istorie, de unde a pornit totul
Istoria
Primul mod cunoscut de izolare a aplicațiilor este chroot. Apelul de sistem omonim permite modificarea directorului rădăcină, asigurând astfel accesul programului care l-a apelat doar la fișierele din acest director. Dar dacă programul din interior are drepturi de superutilizator, acesta poate 'fugi' din chroot și obține acces la sistemul de operare principal. De asemenea, în afară de schimbarea directorului rădăcină, nu sunt restricționate alte resurse (memorie RAM, procesor) și accesul la rețea.
Următorul mod este rularea unui sistem de operare complet în interiorul unui container, prin intermediul mecanismelor nucleului sistemului de operare. În diverse sisteme de operare, această metodă este cunoscută diferit, dar esența este aceeași: rularea mai multor sisteme de operare independente, fiecare dintre ele funcționând cu același nucleu ca și sistemul de operare principal. Exemple includ FreeBSD Jails, Solaris Zones, OpenVZ și LXC pentru Linux. Aceasta asigură izolare nu doar pe baza spațiului de stocare, ci și a altor resurse; în special, fiecare container poate avea limitări privind timpul procesorului, memoria RAM și lățimea de bandă a rețelei. Comparativ cu chroot, ieșirea din container este mai dificilă, deoarece superutilizatorul din container are acces doar la elementele interne ale containerului; totuși, din cauza necesității de a menține sistemul de operare din interiorul containerului actualizat și utilizării versiunilor mai vechi ale nucleelor (relevant pentru Linux, mai puțin pentru FreeBSD), există o probabilitate non-zero de 'spargere' a sistemului de izolare a nucleului și obținerea accesului la sistemul de operare principal.
În loc să pornești un sistem de operare complet într-un container (cu sistem de inițializare, manager de pachete etc.), poți porni aplicații direct, fiind esențial să le oferi această capacitate (disponibilitatea bibliotecilor necesare și a altor fișiere). Această idee a stat la baza virtualizării containers de aplicații, cel mai notabil și cunoscut reprezentant fiind Docker. Între sistemele anterioare, mecanismele de izolare mai flexibile împreună cu suportul încorporat pentru rețele virtuale între containere și monitorizarea stării aplicației în interiorul containerului au permis crearea unui mediu unificat dintr-un număr mare de servere fizice pentru rularea containerelor — fără necesitatea de a gestiona manual resursele.
Docker
Docker este cel mai cunoscut software pentru containerizarea aplicațiilor. Scris în limbajul Go, utilizează capacitățile de bază ale nucleului Linux — cgroups, namespaces, capabilities etc., precum și sisteme de fișiere Aufs și altele similare pentru economisirea spațiului pe disc.

Sursă: wikimedia
Arhitectură
Până la versiunea 1.11, Docker funcționa ca un singur serviciu, care efectua toate operațiunile cu containere: descărcarea imaginilor pentru containere, pornirea containerelor, procesarea cererilor prin API. Începând cu versiunea 1.11, Docker a fost împărțit în mai multe părți, interconectate între ele: containerd, pentru gestionarea întregului ciclu de viață al containerelor (alocarea spațiului pe disc, descărcarea imaginilor, lucrul cu rețeaua, pornirea, instalarea și monitorizarea stării containerelor) și runC, mediu de execuție a containerelor, bazat pe utilizarea cgroups și a altor caracteristici ale nucleului Linux. Serviciul Docker a rămas, dar acum servește doar pentru procesarea cererilor prin API, transmise la containerd.

Instalare și configurare
Metoda mea preferată de instalare a Docker este docker-machine, care, pe lângă instalarea și configurarea Docker pe servere la distanță (inclusiv pe diverse cloud-uri), permite lucrul cu sistemele de fișiere ale serverelor la distanță și poate executa diferite comenzi.
Cu toate acestea, din 2018, proiectul s-a dezvoltat foarte puțin, așa că vom efectua instalarea în modul standard pentru majoritatea distribuțiilor Linux — adăugând repository-ul și instalând pachetele necesare.
Această metodă este aplicată și în cazul instalării automate, de exemplu, folosind Ansible sau alte sisteme similare, dar nu voi discuta despre aceasta în acest articol.
Instalarea va fi efectuată pe CentOS 7, voi folosi o mașină virtuală ca server; pentru instalare, este suficient să executați comenzile de mai jos:
# 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.ioDupă instalare, trebuie să porniți serviciul și să-l setați să se pornească automat:
# systemctl enable docker
# systemctl start docker
# firewall-cmd --zone=public --add-port=2377/tcp --permanentDe asemenea, puteți crea un grup Docker, utilizatorii căruia vor putea lucra cu Docker fără sudo, să configurați jurnalizarea, să permiteți accesul la API din exterior, să nu uitați să configurați mai exact firewall-ul (toate permisiunile sunt interzise, cu excepția celor permise; în exemplele de mai sus și de mai jos am omis acest lucru pentru simplitate și claritate), dar nu mă voi opri aici pentru a detalia mai mult.
Alte funcționalități
Pe lângă Docker Machine menționat anterior, există și Docker Registry, un instrument pentru stocarea imaginilor containerelor, precum și Docker Compose - un instrument pentru automatizarea desfășurării aplicațiilor în containere, folosind fișiere YAML pentru construirea și configurarea containerelor și a altor lucruri asociate (de exemplu, rețele, sisteme de fișiere persistente pentru stocarea datelor).
De asemenea, acesta permite organizarea de pipeline-uri pentru CICD. O altă posibilitate interesantă este modul de lucru în cluster, numit swarm mode (până la versiunea 1.12 cunoscut ca Docker Swarm), care permite construirea unei infrastructuri unice din mai multe servere pentru executarea containerelor. Există suport pentru rețele virtuale peste toate serverele, un balansor de încărcare încorporat, precum și suport pentru secrete pentru containere.
Fișierele YAML ale Docker Compose, cu mici modificări, pot fi utilizate pentru aceste clustere, automatizând complet întreținerea clusterelor mici și medii pentru diverse scopuri. Pentru clustere mari, este preferabil să folosiți Kubernetes, deoarece costurile de întreținere a swarm mode pot depăși pe cele cu Kubernetes. În plus față de runC, ca mediu de execuție a containerelor, ar putea fi instalat, de exemplu,
Lucru cu Docker
După instalare și configurare, vom încerca să construim un cluster în care vom desfășura GitLab și Docker Registry pentru echipa de dezvoltare. Ca servere, voi folosi trei mașini virtuale, pe care voi desfășura, de asemenea, un sistem de fișiere distribuit GlusterFS, pe care îl voi utiliza ca stocare pentru volumele Docker, de exemplu, pentru a lansa o versiune tolerantă la defecțiuni a Docker Registry. Componentele cheie pentru pornire: Docker Registry, Postgresql, Redis, GitLab cu suport pentru GitLab Runner pe Swarm. Vom rula Postgresql cu clusterizare. , așadar pentru stocarea datelor Postgresql nu este necesar să folosim GlusterFS. Celelalte date critice vor fi stocate pe GlusterFS.
Pentru desfășurarea GlusterFS pe toate serverele (denumite node1, node2, node3) trebuie să instalăm pachetele, să permitem funcționarea firewall-ului, să creăm directoarele necesare:
# 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/fstabDupă instalare, lucrările de configurare a GlusterFS trebuie continuate de pe un nod, de exemplu node1:
# 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 dockerApoi trebuie să montăm volumul obținut (comanda trebuie executată pe toate serverele):
# mount /srv/dockerConfigurarea modului swarm se face pe unul dintre servere, care va fi Lider, ceilalți trebuie să se alăture cluster-ului, așa că rezultatul comenzii de pe primul server va trebui copiat și executat pe celelalte.
Configurarea inițială a cluster-ului, comanda o lansez pe 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 managerCopiem rezultatul celei de-a doua comenzi, executăm pe node2 și node3:
# docker swarm join --token SWMTKN-x-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxx xx.xx.xx.xx:2377
This node joined a swarm as a manager.Aici, configurarea preliminară a serverelor s-a încheiat, trecem la configurarea serviciilor, comenzile de execuție vor fi lansate de pe node1, dacă nu se specifică altfel.
Mai întâi, vom crea rețele pentru containere:
# 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 gitlabApoi, marcăm serverele, acest lucru este necesar pentru a lega anumite servicii de servere:
# docker node update --label-add nodename=node1 node1
# docker node update --label-add nodename=node2 node2
# docker node update --label-add nodename=node3 node3Apoi, creăm directoare pentru stocarea datelor etcd, un depozit KV necesar pentru Traefik și Stolon. În mod asemănător cu Postgresql, acestea vor fi containere legate de servere, de aceea această comandă o executăm pe toate serverele:
# mkdir -p /srv/etcdApoi, creăm un fișier pentru configurarea etcd și îl aplicăm:
00etcd.yml
versiune: '3.7'
servicii:
etcd1:
imagine: quay.io/coreos/etcd:latest
hostname: etcd1
comandă:
- 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
rețele:
- etcd
volume:
- etcd1vol:/data.etcd
desfășurare:
replici: 1
plasare:
constrângeri: [node.labels.nodename == node1]
etcd2:
imagine: quay.io/coreos/etcd:latest
hostname: etcd2
comandă:
- 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
rețele:
- etcd
volume:
- etcd2vol:/data.etcd
desfășurare:
replici: 1
plasare:
constrângeri: [node.labels.nodename == node2]
etcd3:
imagine: quay.io/coreos/etcd:latest
hostname: etcd3
comandă:
- 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
rețele:
- etcd
volume:
- etcd3vol:/data.etcd
desfășurare:
replici: 1
plasare:
constrângeri: [node.labels.nodename == node3]
volume:
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"
rețele:
etcd:
extern: true# docker stack deploy --compose-file 00etcd.yml etcdDupă un timp, verificăm că a fost ridicat clusterul etcd:
# 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 healthyCreăm directoare pentru Postgresql, comanda se execută pe toate serverele:
# mkdir -p /srv/pgsqlApoi creăm un fișier pentru configurarea Postgresql:
01pgsql.yml
versiune: '3.7'
servicii:
pgsentinel:
imagine: sorintlab\/stolon:master-pg10
comandă:
- gosu
- stolon
- stolon-sentinel
- --nume-cluster=stolon-cluster
- --back-end-store=etcdv3
- --puncte-finalizare-store=http:\/\/etcd1:2379,http:\/\/etcd2:2379,http:\/\/etcd3:2379
- --nivel-logare=debug
rețele:
- etcd
- pgsql
desfășurare:
replici: 3
configurație_actualizare:
paralelism: 1
întârziere: 30s
ordine: stop-primul
acțiune-eroare: pauză
pgkeeper1:
imagine: sorintlab\/stolon:master-pg10
nume-gazdă: pgkeeper1
comandă:
- gosu
- stolon
- stolon-keeper
- --adresa-listen-pg=pgkeeper1
- --nume-utilizator-repl-pg=replica
- --uid=pgkeeper1
- --nume-utilizator-su-pg=postgres
- --fișier-parolă-su-pg=\/run\/secrets\/pgsql
- --fișier-parolă-repl-pg=\/run\/secrets\/pgsql_repl
- --director-date=\/var\/lib\/postgresql\/data
- --nume-cluster=stolon-cluster
- --back-end-store=etcdv3
- --puncte-finalizare-store=http:\/\/etcd1:2379,http:\/\/etcd2:2379,http:\/\/etcd3:2379
rețele:
- etcd
- pgsql
mediu:
- PGDATA=\/var\/lib\/postgresql\/data
volume:
- pgkeeper1:\/var\/lib\/postgresql\/data
secrete:
- pgsql
- pgsql_repl
desfășurare:
replici: 1
plasare:
constrângeri: [node.labels.nodename == node1]
pgkeeper2:
imagine: sorintlab\/stolon:master-pg10
nume-gazdă: pgkeeper2
comandă:
- gosu
- stolon
- stolon-keeper
- --adresa-listen-pg=pgkeeper2
- --nume-utilizator-repl-pg=replica
- --uid=pgkeeper2
- --nume-utilizator-su-pg=postgres
- --fișier-parolă-su-pg=\/run\/secrets\/pgsql
- --fișier-parolă-repl-pg=\/run\/secrets\/pgsql_repl
- --director-date=\/var\/lib\/postgresql\/data
- --nume-cluster=stolon-cluster
- --back-end-store=etcdv3
- --puncte-finalizare-store=http:\/\/etcd1:2379,http:\/\/etcd2:2379,http:\/\/etcd3:2379
rețele:
- etcd
- pgsql
mediu:
- PGDATA=\/var\/lib\/postgresql\/data
volume:
- pgkeeper2:\/var\/lib\/postgresql\/data
secrete:
- pgsql
- pgsql_repl
desfășurare:
replici: 1
plasare:
constrângeri: [node.labels.nodename == node2]
pgkeeper3:
imagine: sorintlab\/stolon:master-pg10
nume-gazdă: pgkeeper3
comandă:
- gosu
- stolon
- stolon-keeper
- --adresa-listen-pg=pgkeeper3
- --nume-utilizator-repl-pg=replica
- --uid=pgkeeper3
- --nume-utilizator-su-pg=postgres
- --fișier-parolă-su-pg=\/run\/secrets\/pgsql
- --fișier-parolă-repl-pg=\/run\/secrets\/pgsql_repl
- --director-date=\/var\/lib\/postgresql\/data
- --nume-cluster=stolon-cluster
- --back-end-store=etcdv3
- --puncte-finalizare-store=http:\/\/etcd1:2379,http:\/\/etcd2:2379,http:\/\/etcd3:2379
rețele:
- etcd
- pgsql
mediu:
- PGDATA=\/var\/lib\/postgresql\/data
volume:
- pgkeeper3:\/var\/lib\/postgresql\/data
secrete:
- pgsql
- pgsql_repl
desfășurare:
replici: 1
plasare:
constrângeri: [node.labels.nodename == node3]
postgresql:
imagine: sorintlab\/stolon:master-pg10
comandă: gosu stolon stolon-proxy --adresa-listen 0.0.0.0 --nume-cluster stolon-cluster --back-end-store=etcdv3 --puncte-finalizare-store http:\/\/etcd1:2379,http:\/\/etcd2:2379,http:\/\/etcd3:2379
rețele:
- etcd
- pgsql
desfășurare:
replici: 3
configurație_actualizare:
paralelism: 1
întârziere: 30s
ordine: stop-primul
acțiune-eroare: rollback
volume:
pgkeeper1:
driver: local
opțiuni_driver:
tip: none
o: bind
dispozitiv: "\/srv\/pgsql"
pgkeeper2:
driver: local
opțiuni_driver:
tip: none
o: bind
dispozitiv: "\/srv\/pgsql"
pgkeeper3:
driver: local
opțiuni_driver:
tip: none
o: bind
dispozitiv: "\/srv\/pgsql"
secrete:
pgsql:
fișier: "\/srv\/docker\/postgres"
pgsql_repl:
fișier: "\/srv\/docker\/replica"
rețele:
etcd:
extern: true
pgsql:
extern: trueGenerăm secrete, aplicăm fișierul:
# </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 pgsqlDupă un timp (verificăm ieșirea comenzii docker service ls, care să ne arate că toate serviciile au fost pornite) inițializăm clusterul 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 initVerificăm disponibilitatea clusterului 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
Configurăm traefik pentru a deschide accesul la containere din exterior:
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 traefikPornim Redis Cluster, pentru aceasta creăm un director pentru stocare pe toate nodurile:
# mkdir -p /srv/redis05redis.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 < /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 redisAdăugăm Docker Registry:
06registry.yml
versiune: '3.7'
servicii:
registry:
imagine: registry:2.6
rețele:
- traefik
volume:
- registry_data: /var/lib/registry
deploy:
replici: 1
plasare:
constrângeri: [node.role == manager]
politica_restart:
condiție: on-failure
etichete:
- traefik.enable=true
- traefik.http.routers.registry.rule=Host(`registry.example.com`)
- traefik.http.services.registry.loadbalancer.server.port=5000
- traefik.docker.network=traefik
volume:
registry_data:
driver: local
opts_driver:
tip: none
o: bind
dispozitiv: "/srv/docker/registry"
rețele:
traefik:
extern: true# mkdir /srv/docker/registry
# docker stack deploy --compose-file 06registry.yml registryȘi în final — GitLab:
08gitlab-runner.yml
versiune: '3.7'
servicii:
gitlab:
imagine: gitlab/gitlab-ce:latest
rețele:
- pgsql
- redis
- traefik
- gitlab
porturi:
- 22222:22
mediu:
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'] = "noreply@example.com"
gitlab_rails['smtp_password'] = "xxxxxxxxx"
gitlab_rails['smtp_domain'] = "example.com"
gitlab_rails['gitlab_email_from'] = 'noreply@example.com'
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
volume:
- gitlab_conf:/etc/gitlab
- gitlab_logs:/var/log/gitlab
- gitlab_data:/var/opt/gitlab
deploy:
mod: replicated
replici: 1
plasare:
constrângeri:
- node.role == manager
etichete:
- 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:
imagine: gitlab/gitlab-runner:latest
rețele:
- gitlab
volume:
- gitlab_runner_conf:/etc/gitlab
- /var/run/docker.sock:/var/run/docker.sock
deploy:
mod: replicated
replici: 1
plasare:
constrângeri:
- node.role == manager
volume:
gitlab_conf:
driver: local
opts_driver:
tip: none
o: bind
dispozitiv: "/srv/docker/gitlab/conf"
gitlab_logs:
driver: local
opts_driver:
tip: none
o: bind
dispozitiv: "/srv/docker/gitlab/logs"
gitlab_data:
driver: local
opts_driver:
tip: none
o: bind
dispozitiv: "/srv/docker/gitlab/data"
gitlab_runner_conf:
driver: local
opts_driver:
tip: none
o: bind
dispozitiv: "/srv/docker/gitlab/runner"
rețele:
pgsql:
extern: true
redis:
extern: true
traefik:
extern: true
gitlab:
extern: 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 gitlabStarea finală a clusterului și serviciilor:
# 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/tcpCe altceva poate fi îmbunătățit? Este esențial să configurăm în Traefik funcționarea containerelor prin https, să adăugăm criptarea tls pentru Postgresql și Redis. În general, însă, poate fi oferit deja dezvoltatorilor ca PoC. Să vedem acum alternativele pentru Docker.
Podman
Un alt engine destul de cunoscut pentru rularea containerelor, grupate în poduri (pods, grupuri de containere desfășurate împreună). Spre deosebire de Docker, nu necesită niciun serviciu pentru a rula containere, toată munca se face prin biblioteca libpod. De asemenea, este scris în Go, necesită un runtime compatibil OCI pentru a rula containere, de exemplu runC.

Lucrul cu Podman este în general similar cu cel pentru Docker, până la punctul în care se poate face așa (declarat de mulți dintre cei care au încercat, inclusiv autorul acestui articol):
$ alias docker=podmanși se poate continua să lucrăm. În general, situația cu Podman este foarte interesantă, deoarece, dacă versiunile timpurii ale Kubernetes au lucrat cu Docker, începând cu 2015, după standardizarea lumii containerelor (OCI — Open Container Initiative) și separarea Docker în containerd și runC, se dezvoltă o alternativă la Docker pentru rularea în Kubernetes: CRI-O. În acest sens, Podman este o alternativă la Docker, construită pe principiile Kubernetes, inclusiv în gruparea containerelor, dar scopul principal al existenței proiectului este de a rula containere în stil Docker fără servicii suplimentare. Din motive evidente, nu există modul swarm, deoarece dezvoltatorii spun clar că, dacă este necesar un cluster, folosiți Kubernetes.
Instalare
Pentru instalare în Centos 7, este suficient să activăm repository-ul Extras, după care instalăm totul cu comanda:
# yum -y install podmanAlte funcționalități
Podman poate genera unități pentru systemd, rezolvând astfel problema rulării containerelor după repornirea serverului. De asemenea, este declarat că systemd funcționează corect ca pid 1 în container. Pentru construirea containerelor există un instrument separat, buildah; există, de asemenea, instrumente terțe — echivalente cu docker-compose, care generează în special fișiere de configurare compatibile cu Kubernetes, astfel încât trecerea de la Podman la Kubernetes este simplificată cât mai mult posibil.
Lucrul cu Podman
Deoarece nu există modul swarm (se presupune tranziția la Kubernetes, dacă este necesar un cluster) — vom aduna containere separate.
Instalăm podman-compose:
# yum -y install python3-pip
# pip3 install podman-composeFișierul de configurare rezultat pentru podman diferă puțin, având în vedere că, de exemplu, a fost necesar să mutăm o secțiune separată volumes direct în secțiunea cu servicii.
gitlab-podman.yml
versiune: '3.7'
servicii:
gitlab:
imagine: 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:
imagine: 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
rețele:
gitlab:# podman-compose -f gitlab-runner.yml -d upRezultatul execuției:
# 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_1Să vedem ce va genera pentru systemd și kubernetes, pentru aceasta trebuie să aflăm numele sau ID-ul podului:
# podman pod ls
POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
71fc2b2a5c63 root Running 11 minutes ago 3 db40ab8bf84bKubernetes:
# 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: {}Systemd:
# 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.targetDin păcate, în afară de lansarea containerelor, unitatea generată pentru systemd nu face nimic altceva (de exemplu, să curețe containerele vechi la relansarea unui astfel de serviciu), așadar va trebui să adăugați aceste lucruri manual.
În principiu, Podman este suficient pentru a experimenta cu containere, pentru a migra configurațiile vechi pentru docker-compose, după care să treceți la Kubernetes, dacă aveți nevoie de un cluster, sau pentru a obține o alternativă mai simplă la Docker.
rkt
Proiect aproximativ acum șase luni deoarece a fost achiziționat de RedHat, așa că nu voi insista asupra lui. În general, a lăsat o impresie destul de bună, dar comparativ cu Docker și cu atât mai mult cu Podman, pare un ansamblu. De asemenea, a existat o distribuție CoreOS, construită pe baza rkt (deși inițial aveau Docker), dar suportul său a încetat de asemenea după achiziția RedHat.
Plash
Altele , ale cărui autor a vrut pur și simplu să construiască și să ruleze containere. Judecând după documentație și cod — autorul nu a urmat standardele, ci a decis pur și simplu să scrie propria implementare, ceea ce, în principiu, și-a realizat.
Conclusions
Situația în prezența Kubernetes se dovedește a fi foarte interesantă: pe de o parte, cu Docker se poate construi un cluster (în modul swarm), cu care se pot rula medii productive pentru clienți, ceea ce este deosebit de relevant pentru echipe mici (3-5 persoane), sau în cazul unei sarcini generale reduse, sau în absența dorinței de a înțelege nuanțele configurării Kubernetes, inclusiv pentru sarcini mari.
Podman nu oferă o compatibilitate completă, dar are un avantaj important — compatibilitatea cu Kubernetes, inclusiv prin uneltele suplimentare (buildah și altele). De aceea, voi aborda alegerea instrumentului de lucru în felul următor: pentru echipe mici, sau cu bugete reduse — Docker (posibil în modul swarm), pentru dezvoltare personală pe localhost — Podman cu colegii, iar pentru ceilalți — Kubernetes.
Nu sunt sigur că situația cu Docker nu se va schimba în viitor, până la urmă ei sunt pionieri și, pas cu pas, devin standardizați, dar din toate neajunsurile lui Podman (funcționează numai pe Linux, nu are clustering, construcția și alte acțiuni — cu soluții externe) viitorul pare mai clar, așa că invit pe toți cei dornici să discutăm aceste concluzii în comentarii.
P.S. 3 august lansăm „”, unde veți putea afla mai multe despre funcționarea acestuia. Vom analiza toate uneltele sale: de la abtractele de bază la parametrii rețelei, nuanțele lucrului cu diferite sisteme de operare și limbaje de programare. Veți face cunoștință cu tehnologia și veți înțelege unde și cum să folosiți mai bine Docker. De asemenea, vom împărtăși cele mai bune practici.
Prețul precomenzii până la lansare: 5000 RON. Puteți consulta programul „Cursului video despre Docker” .
Sursa: habr.com
