TL;DR: Přehledný článek – průvodce porovnáváním prostředí pro spouštění aplikací v kontejnerech. Budou zváženy možnosti Dockeru a dalších podobných systémů.
Trochu historie o tom, kde se to všechno vzalo
Příběh
Prvním známým způsobem, jak izolovat aplikaci, je chroot. Stejnojmenné systémové volání poskytuje změnu kořenového adresáře – tedy poskytuje přístup k programu, který jej volal, přístup pouze k souborům uvnitř tohoto adresáře. Ale pokud má program uvnitř práva superuživatele, může potenciálně „utéct“ z chrootu a získat přístup k hlavnímu operačnímu systému. Kromě změny kořenového adresáře nejsou omezeny ani další prostředky (RAM, procesor) ani přístup k síti.
Dalším způsobem je spuštění plnohodnotného operačního systému uvnitř kontejneru pomocí mechanismů jádra operačního systému. Tato metoda se v různých operačních systémech nazývá různě, ale podstata je stejná – provozování několika nezávislých operačních systémů, z nichž každý běží na stejném jádře, na kterém běží hlavní operační systém. To zahrnuje FreeBSD Jails, Solaris Zones, OpenVZ a LXC pro Linux. Izolace je zajištěna nejen pro místo na disku, ale také pro další zdroje, zejména každý kontejner může mít omezení na čas procesoru, RAM, šířku pásma sítě. Ve srovnání s chrootem je opuštění kontejneru obtížnější, protože superuživatel v kontejneru má přístup pouze do vnitřku kontejneru, nicméně kvůli nutnosti udržovat operační systém uvnitř kontejneru aktuální a použití starého jádra verze (relevantní pro Linux, v menší míře FreeBSD), existuje nenulová pravděpodobnost „prolomení“ izolačního systému jádra a získání přístupu k hlavnímu operačnímu systému.
Místo spouštění plnohodnotného operačního systému v kontejneru (s inicializačním systémem, správcem balíčků atd.) lze aplikace spouštět okamžitě, hlavní je poskytnout aplikacím tuto příležitost (přítomnost potřebných knihoven a ostatní soubory). Tato myšlenka posloužila jako základ pro virtualizaci kontejnerových aplikací, jejichž nejvýraznějším a nejznámějším představitelem je Docker. Ve srovnání s předchozími systémy vedly flexibilnější izolační mechanismy spolu s vestavěnou podporou virtuálních sítí mezi kontejnery a stavem aplikace uvnitř kontejneru k možnosti vybudovat jediné holistické prostředí z velkého počtu fyzických serverů pro provoz kontejnerů – bez potřeba manuální správy zdrojů.
přístavní dělník
Docker je nejznámější aplikační kontejnerizační software. Je napsán v jazyce Go a využívá běžné schopnosti linuxového jádra – cgroups, jmenné prostory, schopnosti atd., stejně jako souborové systémy Aufs a další podobné pro úsporu místa na disku.
Zdroj: wikimedia
architektura
Před verzí 1.11 fungoval Docker jako jediná služba, která prováděla všechny operace s kontejnery: stahování obrázků pro kontejnery, spouštění kontejnerů, zpracování požadavků API. Od verze 1.11 je Docker rozdělen do několika částí, které se vzájemně ovlivňují: kontejnery, které zvládají celý životní cyklus kontejnerů (přidělení místa na disku, stahování obrazů, síťování, spouštění, instalace a sledování stavu kontejnerů) a runC , kontejnerové runtime, založené na použití cgroups a dalších funkcích linuxového jádra. Samotná služba dockeru zůstává, ale nyní slouží pouze ke zpracování požadavků API vysílaných do kontejneru.
Instalace a konfigurace
Můj oblíbený způsob instalace dockeru je docker-machine, který kromě přímé instalace a konfigurace dockeru na vzdálených serverech (včetně různých cloudů) umožňuje pracovat se souborovými systémy vzdálených serverů a umí také spouštět různé příkazy.
Od roku 2018 se však projekt téměř nerozvíjel, a tak jej nainstalujeme běžným způsobem pro většinu linuxových distribucí – přidáním repozitáře a instalací potřebných balíčků.
Tato metoda se také používá pro automatizovanou instalaci, například pomocí Ansible nebo jiných podobných systémů, ale v tomto článku ji nebudu uvažovat.
Instalace bude provedena na Centos 7, jako server použiji virtuální stroj, k instalaci stačí spustit níže uvedené příkazy:
# 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
Po instalaci musíte službu spustit, dát ji do automatického načítání:
# systemctl enable docker
# systemctl start docker
# firewall-cmd --zone=public --add-port=2377/tcp --permanent
Dále si můžete vytvořit docker skupinu, jejíž uživatelé budou moci pracovat s dockerem bez sudo, nastavovat logování, povolit přístup k API zvenčí, nezapomenout doladit firewall (vše, co není povoleno, je zakázáno v příkladech výše a níže - to jsem pro jednoduchost a vizualizaci vynechal), ale nebudu se zde blíže rozepisovat.
Další funkce
Kromě výše uvedeného docker stroje existuje také docker registr, nástroj pro ukládání obrázků pro kontejnery, stejně jako docker compose - nástroj pro automatizaci nasazení aplikací v kontejnerech, soubory YAML slouží k vytváření a konfiguraci kontejnerů a další související věci (například sítě, trvalé souborové systémy pro ukládání dat).
Může být také použit k organizaci potrubí pro CICD. Další zajímavou funkcí je práce v clusterovém režimu, tzv. swarm mode (před verzí 1.12 byl znám jako docker swarm), který umožňuje sestavit jedinou infrastrukturu z několika serverů pro provoz kontejnerů. Nad všemi servery existuje podpora pro virtuální síť, je zde vestavěný nástroj pro vyrovnávání zatížení a také podpora pro tajemství pro kontejnery.
Soubory YAML z docker compose lze pro takové clustery s drobnými úpravami použít, což plně automatizuje údržbu malých a středních clusterů pro různé účely. Pro velké clustery je výhodnější Kubernetes, protože náklady na údržbu v režimu roje mohou převážit nad náklady Kubernetes. Kromě runC, jako prováděcího prostředí pro kontejnery, můžete nainstalovat např
Práce s Dockerem
Po instalaci a konfiguraci se pokusíme postavit cluster, do kterého nasadíme GitLab a Docker Registry pro vývojový tým. Jako servery použiji tři virtuální stroje, na které dodatečně nasadím distribuovaný FS GlusterFS, použiji jej jako úložiště svazků dockerů, například pro spuštění bezpečné verze docker registru. Klíčové komponenty ke spuštění: Docker Registry, Postgresql, Redis, GitLab s podporou GitLab Runner nad Swarmem. Postgresql bude spuštěn s clusteringem
Chcete-li nasadit GlusterFS na všechny servery (nazývají se node1, node2, node3), musíte nainstalovat balíčky, povolit firewall, vytvořit potřebné adresáře:
# 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
Po instalaci musí práce na konfiguraci GlusterFS pokračovat z jednoho uzlu, například 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 docker
Poté musíte připojit výsledný svazek (příkaz musí být spuštěn na všech serverech):
# mount /srv/docker
Režim Swarm je nakonfigurován na jednom ze serverů, kterým bude Leader, zbytek se bude muset připojit ke clusteru, takže výsledek spuštění příkazu na prvním serveru bude nutné zkopírovat a spustit na zbytku.
Počáteční nastavení clusteru spustím příkaz na 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
Zkopírujte výsledek druhého příkazu, proveďte na node2 a node3:
# docker swarm join --token SWMTKN-x-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxx xx.xx.xx.xx:2377
This node joined a swarm as a manager.
Tím je předběžná konfigurace serverů dokončena, začněme konfigurovat služby, příkazy, které se mají provést, budou spouštěny z node1, pokud není uvedeno jinak.
Nejprve vytvořte sítě pro kontejnery:
# 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
Poté označíme servery, to je nutné pro navázání některých služeb na servery:
# docker node update --label-add nodename=node1 node1
# docker node update --label-add nodename=node2 node2
# docker node update --label-add nodename=node3 node3
Dále vytvoříme adresáře pro ukládání dat etcd, KV úložiště, které Traefik a Stolon potřebují. Podobně jako u Postgresql se bude jednat o kontejnery vázané na servery, takže tento příkaz provedeme na všech serverech:
# mkdir -p /srv/etcd
Dále vytvořte soubor pro konfiguraci etcd a použijte jej:
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
Po chvíli zkontrolujeme, že se cluster etcd zvedl:
# 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
Vytvořte adresáře pro Postgresql, spusťte příkaz na všech serverech:
# mkdir -p /srv/pgsql
Dále vytvořte soubor pro konfiguraci 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
Vygenerujeme tajemství, použijeme soubor:
# </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
O něco později (podívejte se na výstup příkazu docker service lsže všechny služby vzrostly) inicializujte 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
Kontrola připravenosti clusteru 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
Nakonfigurujeme traefik pro otevření přístupu ke kontejnerům zvenčí:
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
Spustíme Redis Cluster, za tímto účelem vytvoříme adresář úložiště na všech uzlech:
# 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
Přidat registr Docker:
06registry.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
A nakonec - 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
Konečný stav clusteru a služeb:
# 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
Co lze ještě zlepšit? Nezapomeňte nakonfigurovat Traefik pro práci s kontejnery https, přidejte šifrování tls pro Postgresql a Redis. Ale obecně to už můžete dát vývojářům jako PoC. Podívejme se nyní na alternativy k Dockeru.
Podman
Další poměrně známý engine pro provoz kontejnerů seskupených podle podů (pody, skupiny kontejnerů rozmístěné společně). Na rozdíl od Dockeru nevyžaduje ke spuštění kontejnerů žádnou službu, veškerá práce probíhá přes knihovnu libpod. Také napsáno v Go, potřebuje runtime kompatibilní s OCI, aby spouštělo kontejnery jako runC.
Práce s Podmanem se obecně podobá práci s Dockerem, a to do té míry, že to můžete udělat takto (tvrdí to mnozí, kteří to vyzkoušeli, včetně autora tohoto článku):
$ alias docker=podman
a můžete pokračovat v práci. Obecně je situace s Podmanem velmi zajímavá, protože pokud rané verze Kubernetes fungovaly s Dockerem, pak zhruba od roku 2015 po standardizaci světa kontejnerů (OCI – Open Container Initiative) a rozdělení Dockeru na kontejnery a runC vznikla alternativa k Docker je vyvíjen pro běh v Kubernetes: CRI-O. Podman je v tomto ohledu alternativou k Dockeru, postavenou na principech Kubernetes, včetně seskupování kontejnerů, ale hlavním cílem projektu je provozovat kontejnery ve stylu Dockeru bez dalších služeb. Ze zřejmých důvodů neexistuje režim rojení, protože vývojáři jasně říkají, že pokud potřebujete cluster, vezměte Kubernetes.
Instalace
Chcete-li nainstalovat na Centos 7, stačí aktivovat úložiště Extras a poté vše nainstalovat pomocí příkazu:
# yum -y install podman
Další funkce
Podman může generovat jednotky pro systemd, čímž řeší problém se spouštěním kontejnerů po restartu serveru. Navíc je systemd deklarováno, že funguje správně jako pid 1 v kontejneru. Pro vytváření kontejnerů existuje samostatný nástroj buildah, existují také nástroje třetích stran - analogy docker-compose, které také generují konfigurační soubory kompatibilní s Kubernetes, takže přechod z Podman na Kubernetes je co nejjednodušší.
Práce s Podmanem
Vzhledem k tomu, že neexistuje režim roje (v případě potřeby clusteru se má přepnout na Kubernetes), sestavíme jej do samostatných kontejnerů.
Nainstalujte podman-compose:
# yum -y install python3-pip
# pip3 install podman-compose
Výsledný konfigurační soubor pro podman je mírně odlišný, protože jsme například museli přesunout samostatnou sekci svazků přímo do sekce služeb.
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
Výsledek práce:
# 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
Podívejme se, co to vygeneruje pro systemd a kubernetes, k tomu musíme zjistit název nebo id 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: {}
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.target
Bohužel kromě spouštění kontejnerů nedělá vygenerovaná jednotka pro systemd nic jiného (například uklízí staré kontejnery při restartu takové služby), takže si takové věci budete muset přidávat sami.
Podman v zásadě stačí vyzkoušet, co jsou kontejnery, přenést staré konfigurace pro docker-compose a pak jít směrem ke Kubernetes, pokud je to nutné, na clusteru, nebo získat snáze použitelnou alternativu k Dockeru.
Rkt
projekt
Louže
Více
Závěry
Situace s Kubernetes je velmi zajímavá: na jedné straně s Dockerem můžete sestavit cluster (v režimu swarm), se kterým můžete dokonce provozovat produkční prostředí pro klienty, to platí zejména pro malé týmy (3-5 lidí ), nebo s malým celkovým zatížením nebo nedostatkem touhy porozumět složitosti nastavení Kubernetes, a to i pro vysoké zatížení.
Podman neposkytuje plnou kompatibilitu, ale má jednu důležitou výhodu – kompatibilitu s Kubernetes včetně doplňkových nástrojů (buildah a další). Proto přistoupím k výběru nástroje pro práci následovně: pro malé týmy, nebo s omezeným rozpočtem - Docker (s možným režimem swarm), pro vývoj pro sebe na osobním localhostu - Podman spolubojovníci a pro všechny ostatní - Kubernetes.
Nejsem si jistý, že se situace s Dockerem v budoucnu nezmění, přeci jen jsou průkopníci, a také se pomalu krok za krokem standardizují, ale Podman se všemi jeho nedostatky (funguje pouze na Linuxu, žádné shlukování, montáž a další akce jsou rozhodnutí třetích stran) budoucnost je jasnější, proto vyzývám všechny k diskusi o těchto zjištěních v komentářích.
PS 3. srpna spouštíme „
Cena předobjednávky před vydáním: 5000 rublů. Program "Docker Video Course" lze nalézt
Zdroj: www.habr.com