TL;DR: en oversigtsartikel-guide til sammenligning af miljøer til kørsel af applikationer i containere. Mulighederne for Docker og andre lignende systemer vil blive belyst.

Lidt historie, hvor det hele startede
Historik
Den første kendte metode til isolering af applikationer er chroot. Den tilsvarende systemopringning ændrer rodkataloget — hvilket giver programmet, der kalder det, adgang kun til filer inde i dette katalog. Men hvis programmet får superbrugerrettigheder, kan det potentielt "slippe" ud af chroot og få adgang til det primære operativsystem. Desuden begrænser man ikke bare rodkataloget, men også andre ressourcer (RAM, CPU) samt netværksadgang.
Den næste metode er at køre et fuldt operativsystem inden i en container ved hjælp af mekanismerne i operativsystemkernen. I forskellige operativsystemer kaldes denne metode forskelligt, men essensen er den samme — kørsel af flere uafhængige operativsystemer, hver med den samme kerne som det primære operativsystem. Dette inkluderer FreeBSD Jails, Solaris Zones, OpenVZ og LXC for Linux. Isoleringen opnås ikke kun for diskplads, men også for andre ressourcer, idet hver container kan have begrænsninger for CPU-tid, RAM og netværksbåndbredde. Sammenlignet med chroot er det sværere at komme ud af en container, da superbrugeren i containeren kun har adgang til containerens indhold, men på grund af behovet for at holde operativsystemet i containeren opdateret og brugen af ældre kerneversioner (aktuelt for Linux, i mindre grad FreeBSD) er der en ikke-nul sandsynlighed for "at bryde" isolationssystemet i kernen og få adgang til det primære operativsystem.
I stedet for at køre et fuldt operativsystem i en container (med init-system, pakkehåndtering osv.) kan man straks køre applikationer, det vigtigste er at sikre applikationerne denne mulighed (tilstedeværelsen af nødvendige biblioteker og andre filer). Denne idé har været grundlaget for container-virtualisering af applikationer, hvor Docker er den mest fremtrædende og velkendte repræsentant. Sammenlignet med tidligere systemer har mere fleksible isolationsmekanismer sammen med indbygget understøttelse af virtuelle netværk mellem containere og overvågning af applikationernes tilstand inden i containeren givet mulighed for at opbygge et samlet miljø af mange fysiske servere til kørsel af containere — uden behov for manuel ressourcehåndtering.
Docker
Docker er det mest kendte software til containerisering af applikationer. Det er skrevet i Go og udnytter de indbyggede funktioner i Linux-kernen — cgroups, namespaces, capabilities osv., samt filsystemerne Aufs og andre lignende til at spare på diskpladsen.

Kilde: wikimedia
Arkitektur
Indtil version 1.11 fungerede Docker som en enkelt service, der udførte alle operationer med containere: hentning af billeder til containere, kørsel af containere, behandling af API-anmodninger. Fra og med version 1.11 blev Docker opdelt i flere dele, der interagerer med hinanden: containerd, til håndtering af hele livscyklussen for containere (tildeling af diskplads, hentning af billeder, netværksarbejde, kørsel, installation og overvågning af containerens tilstand) og runC, miljøet til udførelse af containere, baseret på brugen af cgroups og andre funktioner i Linux-kernen. Selve Docker-servicen er stadig til stede, men den bruges nu kun til at håndtere API-anmodninger, som blev sendt til containerd.

Installation og konfiguration
Min yndlingsmetode til installation af Docker er docker-machine, der udover selve installationen og opsætningen af Docker på fjerne servere (inklusive forskellige skyer) giver mulighed for at arbejde med filsystemerne på fjerne servere samt kan udføre forskellige kommandoer.
Men siden 2018 har projektet næsten ikke haft udvikling, så vi vil installere det på den ordinaire måde for de fleste Linux-distributioner — ved at tilføje repositoryet og installere de nødvendige pakker.
Denne metode anvendes også ved automatiseret installation, for eksempel ved brug af Ansible eller andre lignende systemer, men jeg vil ikke gå ind i det i denne artikel.
Installationen vil blive udført på CentOS 7, og jeg vil bruge en virtuel maskine som server. For installationen er det tilstrækkeligt at udføre kommandoerne nedenfor:
# 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.ioEfter installationen skal du starte tjenesten og tilføje den til opstart:
# systemctl enable docker
# systemctl start docker
# firewall-cmd --zone=public --add-port=2377/tcp --permanentDerudover kan du oprette en docker-gruppe, hvor brugere kan arbejde med docker uden sudo, konfigurere logning, aktivere adgang til API'et udefra og huske at justere firewall'en mere præcist (alt, hvad der ikke er tilladt, er forbudt; jeg har undladt dette i eksemplerne ovenfor og nedenfor for enkelhedens skyld), men jeg vil ikke gå nærmere ind i det her.
Andre funktioner
Udover den tidligere nævnte docker machine er der også docker registry, et værktøj til opbevaring af billeder til containere, samt docker compose — et værktøj til automatisering af deployment af applikationer i containere, hvor YAML-filer anvendes til at bygge og konfigurere containere og andre relaterede objekter (såsom netværk, permanente filsystemer til datalagring).
Med det kan du også organisere pipelines til CICD. En anden interessant funktion er drift i klyngetilstand, såkaldt swarm mode (tidligere kendt som docker swarm før version 1.12), der tillader at samle flere servere i en samlet infrastruktur til kørsel af containere. Der er understøttelse af virtuelle netværk over alle servere, en indbygget load balancer samt understøttelse af hemmeligheder for containere.
YAML-filer fra docker compose med små ændringer kan anvendes til sådanne klynger, helt automatiserende vedligeholdelse af små og mellemstore klynger til forskellige formål. For store klynger er det mere præferabelt at bruge Kubernetes, da omkostningerne til vedligeholdelse af swarm mode kan overstige dem ved Kubernetes. Udover runC kan du for eksempel installere
Arbejder med Docker
Efter installation og opsætning vil vi forsøge at samle et klynge, hvor vi vil implementere GitLab og Docker Registry til udviklingsteamet. Som servere vil jeg bruge tre virtuelle maskiner, hvor jeg yderligere vil implementere det distribuerede filsystem GlusterFS, som jeg vil bruge som lager til docker volumes, for eksempel til at køre en fejltolerant version af docker registry. Nøglekomponenterne til opsætning: Docker Registry, Postgresql, Redis, GitLab med støtte til GitLab Runner over Swarm. Postgresql vil vi køre med clustering. , så for lagring af data skal Postgresql ikke bruge GlusterFS. De andre kritiske data vil blive lagret på GlusterFS.
For at implementere GlusterFS på alle servere (de kaldes node1, node2, node3) skal pakkerne installeres, firewall-arbejdet skal tillades, og de nødvendige kataloger skal oprettes:
# 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/fstabEfter installationen skal arbejdet med at konfigurere GlusterFS fortsætte fra en node, for eksempel 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 dockerDerefter skal det opnåede volume monteres (kommandoen skal udføres på alle servere):
# mount /srv/dockerKonfigurationen af swarm mode udføres på en af serverne, som vil være Leader; de andre skal tilslutte sig klyngen, så resultatet af kommandoen på den første server skal kopieres og udføres på de øvrige.
Den indledende opsætning af klyngen, jeg starter kommandoen på 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 managerVi kopierer resultatet af den anden kommando og udfører det på node2 og node3:
# docker swarm join --token SWMTKN-x-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxx xx.xx.xx.xx:2377
This node joined a swarm as a manager.Hermed er den foreløbige opsætning af serverne afsluttet, og vi går videre til at konfigurere tjenesterne; kommandoerne vil blive kørt fra node1, medmindre andet er angivet.
Først skal vi oprette netværk til 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 gitlabDerefter markerer vi serverne; dette er nødvendigt for at knytte nogle tjenester til serverne:
# docker node update --label-add nodename=node1 node1
# docker node update --label-add nodename=node2 node2
# docker node update --label-add nodename=node3 node3Derefter opretter vi kataloger til lagring af data etcd, KV-lageret, der er nødvendigt for Traefik og Stolon. Ligesom med Postgresql vil dette være knyttet til serverne, så vi udfører denne kommando på alle servere:
# mkdir -p /srv/etcdDernæst opretter vi en fil til konfigurationen af etcd og anvender den:
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 etcdEfter nogen tid tjekker vi, at etcd-klustret er oppe:
# 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 healthyVi opretter kataloger til Postgresql; kommandoen udføres på alle servere:
# mkdir -p /srv/pgsqlDerefter opretter vi en fil til at konfigurere 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: trueGenererer hemmeligheder, anvender filen:
# </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 pgsqlEfter noget tid (se output fra kommandoen docker service ls, som viser at alle tjenester er startet) initialiserer vi Postgresql-klyngen:
# 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 initTjekker klarheden af Postgresql-klyngen:
# 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
Konfigurerer Traefik for at åbne adgang til containerne udefra:
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 traefikStarter Redis Klynge, for dette opretter vi en mappe til opbevaring på alle noder:
# 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 redisTilføjer Docker Registry:
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 registryOg til slut - 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'] = "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
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 gitlabDen endelige tilstand for klyngen og tjenesterne:
# 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/tcpHvad kan der ellers forbedres? Sørg for at konfigurere Traefik til at arbejde med containere over https, tilføj tls-kryptering til Postgresql og Redis. Men generelt kan det allerede gives videre til udviklerne som en PoC. Lad os nu se på alternativerne til Docker.
Podman
En anden ret kendt engine til at køre containere, grupperet i pods (pods, grupper af containere, der kører sammen). I modsætning til Docker kræver det ikke nogen service for at køre containere, alt arbejde udføres gennem bibliotektet libpod. Det er også skrevet i Go og har brug for en OCI-kompatibel runtime til at køre containere, såsom runC.

Arbejdet med Podman minder generelt om arbejdet med Docker, til det punkt at man kan gøre sådan (hævdede af mange der har prøvet det, herunder forfatteren af denne artikel):
$ alias docker=podmanog man kan fortsætte med at arbejde. Generelt er situationen med Podman meget interessant, for hvis de tidlige versioner af Kubernetes arbejdede med Docker, så siden omkring 2015, efter standardiseringen af containerverdenen (OCI — Open Container Initiative) og opdelingen af Docker i containerd og runC, er der udviklet et alternativ til Docker til kørsel i Kubernetes: CRI-O. Podman er i denne sammenhæng et alternativ til Docker, bygget efter princippet fra Kubernetes, herunder også hvad angår gruppering af containere, men den primære målsætning med projektet er at køre containere i Docker-stil uden ekstra services. Af åbenlyse grunde findes der ikke swarm mode, da udviklerne klart angiver, at hvis man ønsker et kluster — så bør man tage Kubernetes.
Installation
For at installere på CentOS 7 er det tilstrækkeligt at aktivere Extras-repositoriet, hvorefter man installerer alt med følgende kommando:
# yum -y install podmanAndre funktioner
Podman kan generere enheder til systemd, hvilket løser opgaven med at starte containere efter servergenstart. Derudover er korrekt arbejde med systemd som pid 1 i containeren angivet. Til opbygning af containere bruges et separat værktøj, buildah, der findes også tredjeparts værktøjer — svarende til docker-compose, der genererer konfigurationsfiler, der er kompatible med Kubernetes, så overgangen fra Podman til Kubernetes er gjort så nem som muligt.
Arbejdet med Podman
Da der ikke er swarm mode (overgangen til Kubernetes antages, hvis man har brug for et kluster) — vil vi samle dem som separate containere.
Vi installerer podman-compose:
# yum -y install python3-pip
# pip3 install podman-composeDen resulterende konfigurationsfil til podman adskiller sig lidt, da det var nødvendigt at flytte en separat sektion for volumes direkte ind i sektionen med tjenester.
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 upResultat af arbejdet:
# 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_1Lad os se, hvad den genererer til systemd og kubernetes, for dette skal vi finde navnet eller id'et på pod'en:
# 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.targetDesværre gør den genererede enhed til systemd ikke andet end at starte containere (f.eks. rydning af gamle containere ved genstart af en sådan tjeneste), så sådanne ting skal man skrive selv.
I princippet er Podman nok til at prøve, hvad containere er, overføre gamle konfigurationer til docker-compose, hvorefter man kan overveje Kubernetes, hvis man skal have et cluster, eller få et enklere alternativ til Docker.
rkt
Projekt cirka for seks måneder siden, fordi det blev købt af RedHat, så jeg vil ikke gå mere i dybden med det. Generelt efterlod det et ganske godt indtryk, men i forhold til Docker og især Podman virker det klodset. Der eksisterede også en distribution, CoreOS, bygget på rkt (selvom de oprindeligt havde Docker), men dens støtte ophørte også efter opkøbet af RedHat.
Plash
Mere , hvis forfatter blot ønskede at samle og køre containere. Ifølge dokumentationen og koden fulgte forfatteren ikke standarderne, men besluttede blot at skrive sin egen implementering, hvilket han i princippet gjorde.
Konklusioner
Situationen med Kubernetes er meget interessant: på den ene side kan man med Docker opbygge et cluster (i swarm-tilstand), hvilket endda kan bruges til at køre produktionsmiljøer for kunder, især relevant for små teams (3-5 personer), eller ved lav samlet belastning, eller hvis man ikke ønsker at forstå detaljerne ved opsætning af Kubernetes, inklusiv for høj belastning.
Podman giver ikke fuld kompatibilitet, men det har en vigtig fordel — kompatibilitet med Kubernetes, herunder ekstra værktøjer (buildah og andre). Derfor vil jeg vælge værktøjet til mit arbejde sådan: til små teams eller ved begrænset budget — Docker (muligvis med swarm mode), til personligt brug på localhost — Podman med kolleger, og til alle andre — Kubernetes.
Jeg er ikke sikker på, at situationen med Docker ikke vil ændre sig i fremtiden, de er trods alt pionerer, og de bliver succesivt standardiseret, men fremtiden ser mere klar ud for Podman, trods alle dets ulemper (kun at køre på Linux, ingen clustering, bygning og andre handlinger — med eksterne løsninger). Derfor inviterer jeg alle interesserede til at diskutere disse konklusioner i kommentarerne.
P.S. 3. august lancerer vi "", hvor man kan lære mere om dets funktioner. Vi vil gennemgå alle dets værktøjer: fra de grundlæggende abstraktioner til netværksparametre, nuancer ved brug af forskellige operativsystemer og programmeringssprog. Du vil lære teknologien at kende og forstå, hvor og hvordan man bedst bruger Docker. Vi vil også dele bedste praksis cases.
Prisen for forudbestilling indtil lanceringen: 5000 kr. Programmet for "Videokurset om Docker" kan ses .
Kilde: habr.com
