TL;DR: pregledni članak koji služi kao vodič za usporedbu okruženja za pokretanje aplikacija u kontejnerima. Razmotrit će se mogućnosti Dockera i drugih sličnih sustava.

Malo povijesti — otkuda sve to dolazi
Zgodovina
Prvi općepoznati način izolacije aplikacija je chroot. Isteimeni sistemski poziv omogućava promjenu korijenskog direktorija — na taj način omogućuje pristup programu koji ga je pozvao, samo datotekama unutar tog direktorija. Međutim, ako se programu unutar dodijele superkorisničke privilegije, potencijalno se može "izvući" iz chroota i dobiti pristup glavnom operativnom sustavu. Osim promjene korijenskog direktorija, nisu ograničeni ni drugi resursi (operativna memorija, procesor), niti pristup mreži.
Sljedeći način je pokretanje pune operativne sustave unutar kontejnera, zahvaljujući mehanizmima jezgre operativnog sustava. U različitim operativnim sustavima ovaj se način naziva različito, ali suština je ista — pokretanje nekoliko neovisnih operativnih sustava, svaki od kojih radi s istom jezgrom na kojoj radi i glavni operativni sustav. Ovo uključuje FreeBSD Jails, Solaris Zones, OpenVZ i LXC za Linux. Osigurava se izolacija ne samo prema diskovnom prostoru već i drugim resursima, pri čemu svaki kontejner može imati ograničenja po procesorskom vremenu, operativnoj memoriji i propusnosti mreže. U usporedbi s chrootom, teže je izaći iz kontejnera, budući da superkorisnik unutar kontejnera ima pristup samo komponentama kontejnera, međutim zbog potrebe održavanja operativnog sustava unutar kontejnera u ažurnom stanju i korištenja starih verzija jezgre (što je posebno relevantno za Linux, u manjoj mjeri za FreeBSD) postoji neznatna mogućnost "probijanja" sustava izolacije jezgre i dobivanja pristupa glavnom operativnom sustavu.
Namesto zagonu popolne operacijske sistema v kontejnerju (s sistemom za inicializacijo, upraviteljem paketov itd.) lahko takoj zaženete aplikacije, ključno je, da zagotovite aplikacijam to možnost (prisotnost potrebnih knjižnic in drugih datotek). Ta ideja je služila kot osnova za kontejnersko virtualizacijo aplikacij, katere najbolj izstopajoč in splošno znan predstavnik je Docker. V primerjavi s prejšnjimi sistemi so bolj fleksibilni mehanizmi izolacije skupaj z vgrajeno podporo virtualnim omrežjem med kontejnerji in s spremljanjem stanja aplikacije znotraj kontejnerja omogočili izgradnjo enotnega celostnega okolja iz velikega števila fizičnih strežnikov za zagon kontejnerjev — brez potrebe po ročnem upravljanju virov.
Docker
Docker je najbolj znana programska oprema za kontejnerizacijo aplikacij. Napisan je v jeziku Go, uporablja standardne zmogljivosti jedra Linux — cgroups, namespaces, capabilities itd., ter datotečne sisteme Aufs in druge podobne za varčevanje s prostorom na disku.

Vir: wikimedia
Arhitektura
Pred različico 1.11 je Docker deloval kot enotni servis, ki je izvajal vse operacije s kontejnerji: prenos slik za kontejnerje, zagon kontejnerjev, obdelavo zahtevkov po API-ju. Od različice 1.11 so Docker razdelili na več delov, ki medsebojno delujejo: containerd, za obdelavo celotnega življenjskega cikla kontejnerjev (dodelitev diska, prenos slik, delo z omrežjem, zagon, namestitev in spremljanje stanja kontejnerjev) in runC, okolje za izvajanje kontejnerjev, ki temelji na uporabi cgroups in drugih zmogljivosti jedra Linux. Sam servis docker je ostal, vendar zdaj služi samo za obdelavo zahtevkov po API-ju, ki se prenesejo v containerd.

Namestitev in nastavitev
Moj najljubši način namestitve dockerja je docker-machine, ki poleg neposredne namestitve in nastavitve dockerja na oddaljene strežnike (vključno z različnimi oblačnimi storitvami) omogoča delo z datotečnimi sistemi oddaljenih strežnikov, prav tako pa lahko izvaja različne ukaze.
Vendar se od leta 2018 projekt skoraj ne razvija, zato bomo namestitev izvedli na način, ki je standarden za večino distribucij Linux — z dodajanjem repozitorija in namestitvijo potrebnih paketov.
Ta metoda se uporablja tudi pri samodejni namestitvi, na primer s pomočjo Ansible ali drugih podobnih sistemov, vendar je v tem članku ne bom obravnaval.
Namestitev bo potekala na Centos 7, kot strežnik bom uporabil virtualni stroj; za namestitev preprosto izvedite spodnje ukaze:
# 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.ioPo namestitvi je treba zagnati storitev in jo dodati v samodejno nalaganje:
# systemctl enable docker
# systemctl start docker
# firewall-cmd --zone=public --add-port=2377/tcp --permanentDodatno lahko ustvarite skupino docker, katere uporabniki bodo lahko delali z dockerjem brez sudo, nastavite beleženje, omogočite dostop do API-ja od zunaj, ne pozabite natančneje nastaviti požarnega zidu (vse kar ni dovoljeno, je prepovedano; v primerih zgoraj in spodaj sem to izpustil zaradi enostavnosti in preglednosti), vendar se tu ne bom podrobneje ustavil.
Druge možnosti
Poleg že omenjenega docker machine obstaja še docker registry, orodje za shranjevanje slik za kontejnerje, ter docker compose — orodje za avtomatizacijo uvajanja aplikacij v kontejnerjih, ki uporablja YAML datoteke za sestavljanje in nastavitev kontejnerjev ter vse druge povezane stvari (na primer omrežja, trajne datotečne sisteme za shranjevanje podatkov).
S pomočjo tega orodja lahko prav tako organizirate cevi za CICD. Druga zanimiva funkcionalnost je delovanje v gruči, tako imenovani swarm mode (do različice 1.12 je bil znan kot docker swarm), ki omogoča združitev več strežnikov v enotno infrastrukturo za zagon kontejnerjev. Podprto je virtualno omrežje nad vsemi strežniki, prav tako je na voljo vgrajen uravnoteževalnik obremenitve in podpora za skrivnosti za kontejnerje.
YAML datoteke iz docker compose s minoritnimi spremembami se lahko uporabljajo za take gruče, popolnoma avtomatizirajoč vzdrževanje manjših in srednjih grozdov za različne namene. Za velike gruče je priporočljivo uporabiti Kubernetes, saj lahko stroški vzdrževanja swarm mode presegajo stroške z Kubernetesom. Poleg runC kot izvajalnega okolja za kontejnerje lahko namestite na primer
Rad sa Docker-om
Po namestitvi in konfiguraciji bomo poskusili zgraditi klaster, v katerem bomo namestili GitLab in Docker Registry za razvojno ekipo. Kot strežnike bom uporabil tri virtualne naprave, na katerih bom dodatno namestil distribuirani FS GlusterFS, ki ga bom uporabil kot shrambo za docker volume, na primer za zagon visoko razpoložljive različice docker registry. Ključne komponente za zagon: Docker Registry, Postgresql, Redis, GitLab s podporo za GitLab Runner na Swarmu. Postgresql bomo zagnali s klasterizacijo. , zato za shranjevanje podatkov Postgresql ni potrebno uporabljati GlusterFS. Druge kritične podatke bomo hranili na GlusterFS.
Za namestitev GlusterFS na vseh strežnikih (ti so poimenovani node1, node2, node3) je treba namestiti pakete, omogočiti delovanje požarnega zidu, ustvariti potrebne mape:
# 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/fstabPo namestitvi se mora nadaljevati s konfiguracijo GlusterFS z enega vozlišča, na primer 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 dockerNato je treba priključiti pridobljen volume (ukaz je treba izvesti na vseh strežnikih):
# mount /srv/dockerNastavitev swarm mode se izvede na enem od strežnikov, ki bo vodja, ostali se bodo morali pridružiti klasterju, zato je treba rezultat izvedbe ukaza na prvem strežniku kopirati in izvesti na preostalih.
Prvotna nastavitev klasterja, ukaz izdajam 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 managerKopiramo rezultat drugega ukaza in ga izvedemo na node2 in node3:
# docker swarm join --token SWMTKN-x-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxx xx.xx.xx.xx:2377
This node joined a swarm as a manager.S tem je predhodna nastavitev strežnikov končana, preidemo na nastavitev storitev, ukazi za izvedbo se bodo izvajali z node1, razen če ni drugače navedeno.
Najprej bomo ustvarili omrežja za vse kontejnerje:
# 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 gitlabNato označimo strežnike, kar je potrebno za vezavo nekaterih storitev na strežnike:
# docker node update --label-add nodename=node1 node1
# docker node update --label-add nodename=node2 node2
# docker node update --label-add nodename=node3 node3Nato ustvarimo mape za shranjevanje podatkov etcd, KV-shranjevanja, ki je potrebno za Traefik in Stolon. Podobno kot Postgresql bodo to kontejnere, vezane na strežnike, zato ta ukaz izvedemo na vseh strežnikih:
# mkdir -p /srv/etcdNato ustvarimo datoteko za nastavitev etcd in jo uporabimo:
00etcd.yml
verzija: '3.7'
storitev:
etcd1:
slika: quay.io/coreos/etcd:latest
gostiteljski_naziv: etcd1
ukaz:
- 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
omrežja:
- etcd
volumni:
- etcd1vol:/data.etcd
uvajanje:
replikati: 1
postavitev:
omejitve: [node.labels.nodename == node1]
etcd2:
slika: quay.io/coreos/etcd:latest
gostiteljski_naziv: etcd2
ukaz:
- 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
omrežja:
- etcd
volumni:
- etcd2vol:/data.etcd
uvajanje:
replikati: 1
postavitev:
omejitve: [node.labels.nodename == node2]
etcd3:
slika: quay.io/coreos/etcd:latest
gostiteljski_naziv: etcd3
ukaz:
- 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
omrežja:
- etcd
volumni:
- etcd3vol:/data.etcd
uvajanje:
replikati: 1
postavitev:
omejitve: [node.labels.nodename == node3]
volumni:
etcd1vol:
gonilnik: lokalno
gonilnik_opts:
tip: none
o: bind
naprava: "/srv/etcd"
etcd2vol:
gonilnik: lokalno
gonilnik_opts:
tip: none
o: bind
naprava: "/srv/etcd"
etcd3vol:
gonilnik: lokalno
gonilnik_opts:
tip: none
o: bind
naprava: "/srv/etcd"
omrežja:
etcd:
zunanje: true# docker stack deploy --compose-file 00etcd.yml etcdPo nekaj časa preverimo, ali je klaster etcd zagnan:
# 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 healthyUstvarimo imenike za Postgresql, ukaz izvedemo na vseh strežnikih:
# mkdir -p /srv/pgsqlNato ustvarimo datoteko za nastavitev Postgresql:
01pgsql.yml
verzija: '3.7'
usluge:
pgsentinel:
slika: sorintlab/stolon:master-pg10
komanda:
- 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
mreže:
- etcd
- pgsql
implementacija:
replikati: 3
konfiguracija_azuriranja:
paralelizam: 1
odlaganje: 30s
redosled: stop-prvo
akcija_u_slucaju_greske: pauza
pgkeeper1:
slika: sorintlab/stolon:master-pg10
hostname: pgkeeper1
komanda:
- 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
mreže:
- etcd
- pgsql
okruženje:
- PGDATA=/var/lib/postgresql/data
volumeni:
- pgkeeper1:/var/lib/postgresql/data
tajne:
- pgsql
- pgsql_repl
implementacija:
replikati: 1
raspored:
ograničenja: [node.labels.nodename == node1]
pgkeeper2:
slika: sorintlab/stolon:master-pg10
hostname: pgkeeper2
komanda:
- 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
mreže:
- etcd
- pgsql
okruženje:
- PGDATA=/var/lib/postgresql/data
volumeni:
- pgkeeper2:/var/lib/postgresql/data
tajne:
- pgsql
- pgsql_repl
implementacija:
replikati: 1
raspored:
ograničenja: [node.labels.nodename == node2]
pgkeeper3:
slika: sorintlab/stolon:master-pg10
hostname: pgkeeper3
komanda:
- 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
mreže:
- etcd
- pgsql
okruženje:
- PGDATA=/var/lib/postgresql/data
volumeni:
- pgkeeper3:/var/lib/postgresql/data
tajne:
- pgsql
- pgsql_repl
implementacija:
replikati: 1
raspored:
ograničenja: [node.labels.nodename == node3]
postgresql:
slika: sorintlab/stolon:master-pg10
komanda: 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
mreže:
- etcd
- pgsql
implementacija:
replikati: 3
konfiguracija_azuriranja:
paralelizam: 1
odlaganje: 30s
redosled: stop-prvo
akcija_u_slucaju_greske: vraćanje
volumeni:
pgkeeper1:
vozač: lokalno
opcije_vozača:
tip: none
o: bind
uređaj: "/srv/pgsql"
pgkeeper2:
vozač: lokalno
opcije_vozača:
tip: none
o: bind
uređaj: "/srv/pgsql"
pgkeeper3:
vozač: lokalno
opcije_vozača:
tip: none
o: bind
uređaj: "/srv/pgsql"
tajne:
pgsql:
datoteka: "/srv/docker/postgres"
pgsql_repl:
datoteka: "/srv/docker/replica"
mreže:
etcd:
spoljašnje: true
pgsql:
spoljašnje: trueGeneriramo skrivnosti, uporabljamo datoteko:
# </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 pgsqlPo določenem času (pregledamo izhod ukaza docker service ls, da preverimo, ali so vsi storitve zagnane) inicializiramo PostgreSQL klaster:
# 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 initPreverimo stanje PostgreSQL klasterja:
# 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
Konfiguriramo Traefik za omogočanje dostopa do kontejnerjev od zunaj:
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 traefikZagnamo Redis klaster, zato na vseh vozliščih ustvarimo imenik za shranjevanje:
# 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 redisDodajamo Docker Registry:
06registry.yml
verzija: '3.7'
usluge:
registry:
slika: registry:2.6
mreže:
- traefik
volumeni:
- registry_data: /var/lib/registry
raspored:
replikati: 1
pozicioniranje:
ograničenja: [node.role == manager]
pravila_povratka:
stanje: on-failure
oznake:
- traefik.enable=true
- traefik.http.routers.registry.rule=Host(`registry.example.com`)
- traefik.http.services.registry.loadbalancer.server.port=5000
- traefik.docker.network=traefik
volumeni:
registry_data:
drajver: local
opcije_drajvera:
tip: none
o: bind
uređaj: "/srv/docker/registry"
mreže:
traefik:
spoljašnje: true# mkdir /srv/docker/registry
# docker stack deploy --compose-file 06registry.yml registryI konačno — GitLab:
08gitlab-runner.yml
verzija: '3.7'
usluge:
gitlab:
slika: gitlab/gitlab-ce:latest
mreže:
- pgsql
- redis
- traefik
- gitlab
portovi:
- 22222:22
okruženje:
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
volumeni:
- gitlab_conf:/etc/gitlab
- gitlab_logs:/var/log/gitlab
- gitlab_data:/var/opt/gitlab
raspored:
način: replicated
replikati: 1
pozicioniranje:
ograničenja:
- node.role == manager
oznake:
- 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:
slika: gitlab/gitlab-runner:latest
mreže:
- gitlab
volumeni:
- gitlab_runner_conf:/etc/gitlab
- /var/run/docker.sock:/var/run/docker.sock
raspored:
način: replicated
replikati: 1
pozicioniranje:
ograničenja:
- node.role == manager
volumeni:
gitlab_conf:
drajver: local
opcije_drajvera:
tip: none
o: bind
uređaj: "/srv/docker/gitlab/conf"
gitlab_logs:
drajver: local
opcije_drajvera:
tip: none
o: bind
uređaj: "/srv/docker/gitlab/logs"
gitlab_data:
drajver: local
opcije_drajvera:
tip: none
o: bind
uređaj: "/srv/docker/gitlab/data"
gitlab_runner_conf:
drajver: local
opcije_drajvera:
tip: none
o: bind
uređaj: "/srv/docker/gitlab/runner"
mreže:
pgsql:
spoljašnje: true
redis:
spoljašnje: true
traefik:
spoljašnje: true
gitlab:
spoljašnje: 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 gitlabKonačno stanje klastera i usluga:
# 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/tcpKaj še lahko izboljšate? Obvezno nastavite delovanje kontejnerjev prek https v Traefiku, dodajte šifriranje tls za PostgreSQL in Redis. Na splošno pa je že možno to predati razvijalcem kot PoC. Zdaj si oglejmo še alternative Dockerju.
Podman
Še en precej znan motor za zagon kontejnerjev, združenih v skupine (pods, skupine kontejnerjev, ki so razporejeni skupaj). V nasprotju z Dockerjem ne zahteva nobene storitve za zagon kontejnerjev, vse delo poteka preko knjižnice libpod. Prav tako je napisan v Goju in potrebuje OCI-združljiv runtime za zagon kontejnerjev, na primer runC.

Delo s Podmanom na splošno spominja na delo z Dockerjem, kar zadeva tudi to, da lahko storite naslednje (zatrjujejo mnogi, ki so poskusili, vključno z avtorjem tega članka):
$ alias docker=podmanin lahko nadaljujete z delom. Splošna situacija s Podmanom je precej zanimiva, saj so zgodnje različice Kubernetes delovale z Dockerjem, od leta 2015, po standardizaciji sveta kontejnerjev (OCI — Open Container Initiative) in razdelitvi Dockerja na containerd in runC, se razvija alternativa Dockerju za zagon v Kubernetes: CRI-O. Podman v tem smislu predstavlja alternativo Dockerju, zgrajeno po načelih Kubernetes, vključno s skupinami kontejnerjev, vendar je osnovni cilj projekta zagon kontejnerjev v slogu Docker brez dodatnih storitev. Iz razumljivih razlogov ne obstaja swarm mode, saj razvijalci jasno povedo, da če potrebujete grozd, vzemite Kubernetes.
Namestitev
Za namestitev v Centos 7 je dovolj aktivirati repozitorij Extras, nato pa vse namestiti z ukazom:
# yum -y install podmanDruge možnosti
Podman lahko generira enote za systemd, tako da reši težavo z zagonom kontejnerjev po ponovnem zagonu strežnika. Dodatno je bila navedena pravilna uporaba systemd kot pid 1 v kontejnerju. Za gradnjo kontejnerjev obstaja ločeno orodje buildah, na voljo so tudi različna orodja — analogi docker-compose, ki generirajo tudi konfiguracijske datoteke, združljive s Kubernetes, tako da je prehod z Podmana na Kubernetes olajšan kolikor je le mogoče.
Delo s Podmanom
Ker ni swarm mode (predvideva se prehod na Kubernetes, če potrebujete grozd) — se bomo zbrali v ločenih kontejnerjih.
Namestimo podman-compose:
# yum -y install python3-pip
# pip3 install podman-composeKonfiguracijska datoteka za podman se nekoliko razlikuje, tako da je bilo na primer potrebno prenesti ločen odsek volumes neposredno v odsek s storitvami.
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Результат работы:
# 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Давайте посмотрим, что он сгенерирует для systemd и kubernetes, для этого надо узнать имя или id пода:
# 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.targetК сожалению кроме запуска контейнеров сгенерированный юнит для systemd больше ничего не делает (например зачистку старых контейнеров при перезапуске такого сервиса), поэтому такие вещи придется дописывать самостоятельно.
В принципе Podman достаточно для того, чтобы попробовать, что такое контейнеры, перенести старые конфигурации для docker-compose, после чего уйти в сторону Kubernetes, если надо на кластер, либо получить более простую в работе альтернативу Docker.
rkt
Projekt примерно полгода назад из-за того, что его купил RedHat, поэтому не буду останавливаться на нем детальнее. В целом он оставлял весьма неплохое впечатление, однако по сравнению с Docker и тем более с Podman выглядит комбайном. Существовал также дистрибутив CoreOS, построенный на основе rkt (хотя у них изначально был Docker), однако его поддержка также окончилась после покупки RedHat.
Plash
Še , автор которого хотел просто собирать и запускать контейнеры. Судя по документации и коду — автор не следовал стандартам, а просто решил написать свою реализацию, что в принципе и сделал.
Zaključki
Ситуация при наличии Kubernetes складывается весьма интересная: с одной стороны с Docker можно собрать кластер (в swarm mode), с которым даже можно запускать продуктовые среды для клиентов, это особенно актуально для небольших команд (3-5 человек), либо при небольшой общей нагрузке, или же отсутствию желания разбираться в тонкостях настройки Kubernetes в том числе и для высоких нагрузок.
Podman ne zagotavlja popolne združljivosti, vendar ima eno pomembno prednost — združljivost s Kubernetesom, vključno z dodatnimi orodji (buildah in drugi). Zato bom pri izbiri orodja za delo pristopil tako: za majhne ekipe ali ob omejenem proračunu — Docker (z morebitnim načinom swarm), za razvoj zase na osebnem localhostu — Podman s kolegi, za vse ostale pa — Kubernetes.
Nisem prepričan, da se situacija z Dockerjem ne bo spremenila v prihodnosti, saj so vseeno pionirji ter korak za korakom postopoma standardizirajo, ampak ima Podman, kljub vsem svojim pomanjkljivostim (deluje le na Linuxu, ni klasterizacije, sestavljanje in druge aktivnosti — s strani rešitev), bolj jasno prihodnost, zato vabim vse, ki jih zanima, da te ugotovitve razpravljajo v komentarjih.
P.S. 3. avgusta začenjamo „“, kjer boste lahko podrobneje spoznali njegovo delovanje. Razložili bomo vsa njegova orodja: od osnovnih abstrakcij do parametrov omrežja, podrobnosti dela z različnimi operacijskimi sistemi in programskimi jeziki. Spoznali boste tehnologijo in razumeli, kje in kako najbolje uporabljati Docker. Prav tako bomo delili best practice primere.
Cena prednaročila do izida: 5000 r. S programom „Video tečaja o Dockerju“ se lahko seznanite .
Izvor: habr.com
