TL;DR: En oversigt og guide til sammenligning af miljøer til kørsel af applikationer i containere. Vi vil se på funktionerne i Docker og andre lignende systemer.

Lidt historie om, hvor det hele startede
Historie
Den første velkendte metode til isolation af applikationer er chroot. Den tilsvarende systemopkald sikrer ændring af rodmappen — hvilket giver det kaldende program adgang kun til filer inden for denne mappe. Men hvis programmet indeni får superbrugerrettigheder, kan det potentielt "undslippe" chroot og få adgang til det underliggende operativsystem. Ud over ændringen af rodmappen er der heller ikke begrænsninger for andre ressourcer (RAM, CPU) samt netværksadgang.
Den næste metode er at køre et fuldt operativsystem inde i en container ved hjælp af mekanismer fra operativsystemets kerne. I forskellige operativsystemer kaldes denne metode forskellige ting, men essensen er den samme — at køre flere uafhængige operativsystemer, som hver arbejder med den samme kerne, som den primære operativsystem kører på. Dette inkluderer FreeBSD Jails, Solaris Zones, OpenVZ og LXC for Linux. Det sikrer isolation ikke kun af diskplads, men også af andre ressourcer; specifikt kan hver container have begrænsninger for CPU-tid, RAM og netværksbåndbredde. Sammenlignet med chroot er det sværere at bryde ud af en container, da superbrugeren i containeren kun har adgang til containerens indhold, men på grund af nødvendigheden af at holde operativsystemet inde i containeren opdateret og brugen af ældre versioner af kerner (relevant for Linux, i mindre grad FreeBSD) er der en ikke-ubetydelig risiko for "bøvser" i kerneisolationssystemet og adgang til det primære operativsystem.
I stedet for at køre et komplet operativsystem i en container (med init-system, pakkestyring osv.) kan man straks køre applikationer; det vigtigste er at sikre applikationerne denne mulighed (tilgængeligheden af nødvendige biblioteker og andre filer). Denne idé har lagt grundlaget for containerbaseret applikationsvirtualisering, hvor den mest fremtrædende og velkendte repræsentant er Docker. Sammenlignet med tidligere systemer giver mere fleksible isolationsmekanismer sammen med indbygget support til virtuelle netværk mellem containere og overvågning af applikationens tilstand inde i containeren muligheden for at opbygge et samlet miljø af mange fysiske servere til at køre containere — uden behov for manuel håndtering af ressourcer.
Docker
Docker er den mest kendte software til containerisering af applikationer. Det er skrevet i Go-sprog og bruger de indbyggede funktioner i Linux-kernen — cgroups, namespaces, capabilities osv., samt filsystemer som Aufs og andre lignende til at spare diskplads.

Kilde: wikimedia
Arkitektur
Inden version 1.11 arbejdede Docker som en enkelt service, der udførte alle operationer med containere: download af containerbilleder, start af containere, behandling af API-forespørgsler. Fra version 1.11 blev Docker opdelt i flere dele, der interagerer med hinanden: containerd til at håndtere hele livscyklussen for containere (tildeling af diskplads, download af billeder, netværksarbejde, opstart, installation og overvågning af containerens tilstand) og runC, en container-runtime baseret på brugen af cgroups og andre funktioner i Linux-kernen. Selve docker-servicen forblev, men fungerer nu kun til behandling af API-forespørgsler, der transmitteres til containerd.

Installasjon og konfigurasjon
Min yndlingsmetode til at installere docker er docker-machine, som udover selve installationen og konfigurationen af docker på fjernservere (inklusive forskellige cloud-løsninger) også giver mulighed for arbejde med filsystemer på de fjernede servere og kan også udføre forskellige kommandoer.
Men siden 2018 er projektet næsten ikke blevet udviklet, så vi vil installere det på den standardmetode for de fleste Linux-distributioner — ved at tilføje et repository og installere de nødvendige pakker.
Denne metoden brukes også ved automatisert installasjon, for eksempel med Ansible eller lignende systemer, men jeg vil ikke gå nærmere inn på det i denne artikkelen.
Installeringen vil bli utført på CentOS 7, og jeg vil bruke en virtuell maskin som server. For installasjonen er det nok å kjøre kommandoene 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.ioEtter installasjonen må du starte tjenesten og legge den til i oppstartsprogrammet:
# systemctl enable docker
# systemctl start docker
# firewall-cmd --zone=public --add-port=2377/tcp --permanentI tillegg kan du opprette en docker-gruppe slik at brukere kan jobbe med docker uten sudo, konfigurere loggføring, aktivere ekstern API-tilgang, og husk å konfigurere brannmuren grundigere (alt som ikke er tillatt er forbudt; i eksemplene over og nedenfor har jeg utelatt dette for enkelhets skyld), men jeg vil ikke gå nærmere inn på det her.
Andre muligheter
I tillegg til den ovennevnte docker maskinen, finnes det også docker registry, et verktøy for lagring av bilder for containere, samt docker compose — et verktøy for automatisering av distribusjon av applikasjoner i containere, hvor YAML-filer brukes for å bygge og konfigurere containere og andre relaterte elementer (for eksempel nettverk, vedvarende filsystemer for datalagring).
Med dette kan du også organisere CICD-pipelines. En annen interessant mulighet er drift i klynge-modus, kjent som swarm mode (til versjon 1.12 var det kjent som docker swarm), som gir mulighet for å samle flere servere til en enhetlig infrastruktur for kjøring av containere. Det er støtte for et virtuelt nettverk over alle servere, innebygd lastbalansering, samt støtte for hemmeligheter for containere.
YAML-filer fra docker compose kan med små endringer brukes for slike klynger, noe som automatiserer vedlikeholdet av små og mellomstore klynger for ulike formål. For store klynger er det å foretrekke å bruke Kubernetes, da kostnadene for drift av swarm-modus kan overstige de som er for Kubernetes. I tillegg til runC kan du installere for eksempel
Jobb med Docker
Etter installasjon og konfigurasjon skal vi prøve å sette opp et klynge der vi vil implementere GitLab og Docker Registry for utviklerteamet. Jeg vil bruke tre virtuelle maskiner som servere, der jeg også vil sette opp et distribuert filsystem, GlusterFS, som jeg vil bruke som lagringsplass for docker volum, for eksempel for å kjøre en høyt tilgjengelig versjon av docker registry. Nøkkelkomponentene for oppsettet er: Docker Registry, Postgresql, Redis, GitLab med støtte for GitLab Runner over Swarm. Postgresql vil bli kjørt med klustering. , så for lagring av data i Postgresql er det ikke nødvendig å bruke GlusterFS. Andre kritiske data vil bli lagret på GlusterFS.
For å sette opp GlusterFS på alle serverne (de kalles node1, node2, node3) må man installere pakkene, tillate brannmurarbeid, og opprette nødvendige kataloger:
# 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/fstabEtter installasjonen må man fortsette med konfigurasjonen av GlusterFS 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 dockerDeretter må man montere det opprettede volumet (kommandoen må utføres på alle serverne):
# mount /srv/dockerKonfigurasjonen av swarm-modus utføres på en av serverne, som vil bli leder, de andre må koble seg til klyngen, så resultatet av kommandoen på den første serveren må kopieres og utføres på de andre.
Den første konfigurasjonen av klyngen, jeg kjører 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 av den andre kommandoen, utfø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.Nå er den foreløpige konfigurasjonen av serverne avsluttet, vi begynner på konfigurasjonen av tjenester, kommandoene som skal utføres vil bli sendt fra node1, med mindre annet er angitt.
Først oppretter vi nettverk for containerne:
# 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 gitlabDeretter merker vi serverne, dette er nødvendig for å binde noen 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 node3Videre oppretter vi kataloger for lagring av data til etcd, KV-lageret som er nødvendig for Traefik og Stolon. På samme måte som Postgresql, vil dette være tilknyttede containere til serverne, så vi utfører denne kommandoen på alle serverne:
# mkdir -p /srv/etcdSå lager vi en fil for konfigurasjon av etcd og bruker den:
00etcd.yml
versjon: '3.7'
tenester:
etcd1:
bilde: quay.io/coreos/etcd:latest
vertsnavn: etcd1
kommando:
- 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
nettverk:
- etcd
volum:
- etcd1vol:/data.etcd
distribusjon:
replikar: 1
plassering:
restriksjoner: [node.labels.nodename == node1]
etcd2:
bilde: quay.io/coreos/etcd:latest
vertsnavn: etcd2
kommando:
- 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
nettverk:
- etcd
volum:
- etcd2vol:/data.etcd
distribusjon:
replikar: 1
plassering:
restriksjoner: [node.labels.nodename == node2]
etcd3:
bilde: quay.io/coreos/etcd:latest
vertsnavn: etcd3
kommando:
- 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
nettverk:
- etcd
volum:
- etcd3vol:/data.etcd
distribusjon:
replikar: 1
plassering:
restriksjoner: [node.labels.nodename == node3]
volum:
etcd1vol:
driver: lokal
driver_opts:
type: none
o: bind
enhet: "/srv/etcd"
etcd2vol:
driver: lokal
driver_opts:
type: none
o: bind
enhet: "/srv/etcd"
etcd3vol:
driver: lokal
driver_opts:
type: none
o: bind
enhet: "/srv/etcd"
nettverk:
etcd:
ekstern: true# docker stack deploy --compose-file 00etcd.yml etcdEtter en stund sjekker vi at etcd-klyngen har startet:
# 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 lager kataloger for Postgresql, kommandoen utføres på alle serverne:
# mkdir -p /srv/pgsqlDeretter lager vi en fil for konfigurasjon av Postgresql:
01pgsql.yml
versjon: '3.7'
tjenester:
pgsentinel:
bilde: sorintlab/stolon:master-pg10
kommando:
- 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
nettverk:
- etcd
- pgsql
distribuer:
replikaer: 3
oppdateringskonfig:
parallellitet: 1
forsinkelse: 30s
rekkefølge: stopp-først
feilhandling: pause
pgkeeper1:
bilde: sorintlab/stolon:master-pg10
vertsnavn: pgkeeper1
kommando:
- 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
nettverk:
- etcd
- pgsql
miljø:
- PGDATA=/var/lib/postgresql/data
volumer:
- pgkeeper1:/var/lib/postgresql/data
hemmeligheter:
- pgsql
- pgsql_repl
distribuer:
replikaer: 1
plassering:
begrensninger: [node.labels.nodename == node1]
pgkeeper2:
bilde: sorintlab/stolon:master-pg10
vertsnavn: pgkeeper2
kommando:
- 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
nettverk:
- etcd
- pgsql
miljø:
- PGDATA=/var/lib/postgresql/data
volumer:
- pgkeeper2:/var/lib/postgresql/data
hemmeligheter:
- pgsql
- pgsql_repl
distribuer:
replikaer: 1
plassering:
begrensninger: [node.labels.nodename == node2]
pgkeeper3:
bilde: sorintlab/stolon:master-pg10
vertsnavn: pgkeeper3
kommando:
- 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
nettverk:
- etcd
- pgsql
miljø:
- PGDATA=/var/lib/postgresql/data
volumer:
- pgkeeper3:/var/lib/postgresql/data
hemmeligheter:
- pgsql
- pgsql_repl
distribuer:
replikaer: 1
plassering:
begrensninger: [node.labels.nodename == node3]
postgresql:
bilde: sorintlab/stolon:master-pg10
kommando: 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
nettverk:
- etcd
- pgsql
distribuer:
replikaer: 3
oppdateringskonfig:
parallellitet: 1
forsinkelse: 30s
rekkefølge: stopp-først
feilhandling: tilbakekall
volumer:
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"
hemmeligheter:
pgsql:
fil: "/srv/docker/postgres"
pgsql_repl:
fil: "/srv/docker/replica"
nettverk:
etcd:
ekstern: true
pgsql:
ekstern: trueVi genererer hemmeligheter, bruker 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 pgsqlEtter en stund (se på utskriften av kommandoen docker service ls, som viser at alle tjenester er oppe) 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 initVi sjekker beredskapen til 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
Vi konfigurerer Traefik for å åpne tilgang til containere utenfra:
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 traefikVi starter Redis Cluster, så vi oppretter katalog for lagring 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 redisVi legger til Docker Registry:
06registry.yml
versjon: '3.7'
tenester:
registry:
bilde: registry:2.6
nettverk:
- traefik
volum:
- registry_data: /var/lib/registry
distribuer:
replikaer: 1
plassering:
restriksjoner: [node.role == manager]
restart_policy:
tilstand: on-failure
etiketter:
- traefik.enable=true
- traefik.http.routers.registry.rule=Host(`registry.example.com`)
- traefik.http.services.registry.loadbalancer.server.port=5000
- traefik.docker.network=traefik
volum:
registry_data:
driver: local
driver_opts:
type: none
o: bind
enhet: " /srv/docker/registry"
nettverk:
traefik:
ekstern: true# mkdir /srv/docker/registry
# docker stack deploy --compose-file 06registry.yml registryOg til slutt - GitLab:
08gitlab-runner.yml
versjon: '3.7'
tenester:
gitlab:
bilde: gitlab/gitlab-ce:latest
nettverk:
- pgsql
- redis
- traefik
- gitlab
porter:
- 22222:22
miljø:
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
volum:
- gitlab_conf: /etc/gitlab
- gitlab_logs: /var/log/gitlab
- gitlab_data: /var/opt/gitlab
distribuer:
modus: replicated
replikaer: 1
plassering:
restriksjoner:
- node.role == manager
etiketter:
- 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:
bilde: gitlab/gitlab-runner:latest
nettverk:
- gitlab
volum:
- gitlab_runner_conf: /etc/gitlab
- /var/run/docker.sock:/var/run/docker.sock
distribuer:
modus: replicated
replikaer: 1
plassering:
restriksjoner:
- node.role == manager
volum:
gitlab_conf:
driver: local
driver_opts:
type: none
o: bind
enhet: " /srv/docker/gitlab/conf"
gitlab_logs:
driver: local
driver_opts:
type: none
o: bind
enhet: " /srv/docker/gitlab/logs"
gitlab_data:
driver: local
driver_opts:
type: none
o: bind
enhet: " /srv/docker/gitlab/data"
gitlab_runner_conf:
driver: local
driver_opts:
type: none
o: bind
enhet: " /srv/docker/gitlab/runner"
nettverk:
pgsql:
ekstern: true
redis:
ekstern: true
traefik:
ekstern: true
gitlab:
ekstern: 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 tilstanden til klyngen og tjenestene:
# 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/tcpHva annet kan forbedres? Det er viktig å konfigurere Traefik for å få containerne til å kjøre over https, legge til tls-kryptering for Postgresql og Redis. Men generelt kan dette allerede overleveres til utviklerne som en PoC. La oss nå se på alternativene til Docker.
Podman
En annen ganske kjent motor for å kjøre containere, gruppert i pods (podder, grupper av containere distribuert sammen). I motsetning til Docker krever det ikke noen tjeneste for å kjøre containere, alt arbeidet gjøres gjennom biblioteket libpod. Det er også skrevet i Go, og trenger en OCI-kompatibel runtime for å kjøre containere, for eksempel runC.

Arbeidet med Podman ligner generelt på arbeidet med Docker, til og med slik at man kan gjøre dette (utloves av mange som har prøvd, inkludert forfatteren av denne artikkelen):
$ alias docker=podmanog man kan fortsette å jobbe. Generelt er situasjonen med Podman ganske interessant, for hvis tidligere versjoner av Kubernetes jobbet med Docker, så etter standardiseringen av containerverdenen (OCI - Open Container Initiative) og delingen av Docker i containerd og runC, har man utviklet et alternativ til Docker for kjøring i Kubernetes: CRI-O. Podman er i denne sammenhengen et alternativ til Docker, bygget etter prinsippene til Kubernetes, inkludert gruppering av containere, men hovedmålet med prosjektet er å kjøre containere i Docker-stil uten ekstra tjenester. Av åpenbare grunner finnes det ikke swarm mode, ettersom utviklerne tydelig sier at hvis man trenger et klynge, bør man ta Kubernetes.
Installasjon
For installasjon på Centos 7 er det tilstrekkelig å aktivere Extras-repositoriet, hvoretter man kan installere alt med kommandoen:
# yum -y install podmanAndre muligheter
Podman kan generere enheter for systemd, og på den måten løse problemet med å starte containere etter serverrestart. I tillegg er det uttalt at systemd fungerer korrekt som pid 1 i containeren. For å bygge containere er det et eget verktøy buildah, og det finnes også tredjepartsverktøy - alternativer til docker-compose, som også genererer konfigurasjonsfiler som er kompatible med Kubernetes, slik at overgangen fra Podman til Kubernetes er gjort så enkel som mulig.
Arbeid med Podman
Siden det ikke er swarm mode (det antas at man går over til Kubernetes hvis man trenger en klynge) - skal vi samle i separate containere.
Vi installerer podman-compose:
# yum -y install python3-pip
# pip3 install podman-composeDen resulterende konfigurasjonsfilen for podman er litt annerledes, for eksempel måtte vi flytte en egen seksjon for volumer direkte inn i seksjonen 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 av arbeidet:
# 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_1La oss se hva det vil generere for systemd og kubernetes, for dette må vi finne navnet eller ID-en til 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.targetDessverre gjør ikke den genererte enheten for systemd mer enn å starte containere (for eksempel å rydde opp gamle containere ved omstart av en slik tjeneste), så slike ting må skrives manuelt.
I prinsippet er Podman tilstrekkelig for å prøve hva containere er, overføre gamle konfigurasjoner for docker-compose, for deretter å gå mot Kubernetes hvis det er behov for en klynge, eller få et enklere alternativ til Docker.
rkt
Prosjekt cirka et halvt år siden fordi RedHat kjøpte det, så jeg vil ikke gå nærmere inn på det. Generelt sett etterlot det et ganske godt inntrykk, men sammenlignet med Docker og spesielt Podman ser det ut som en kombinasjon. Det fantes også en distribusjon av CoreOS, bygget på rkt (selv om de opprinnelig hadde Docker), men støtten for dette ble også avsluttet etter RedHat-kjøpet.
Plash
Mer , forfatteren av dette ønsket bare å samle og kjøre containere. Ifølge dokumentasjonen og koden – fulgte ikke forfatteren standardene, men bestemte seg for å skrive sin egen implementering, noe han i prinsippet gjorde.
Konklusjoner
Situasjonen med Kubernetes er ganske interessant: på den ene siden kan man bygge en klynge med Docker (i swarm-modus), som man til og med kan bruke til å kjøre produksjonsmiljøer for kunder, noe som er spesielt relevant for små team (3-5 personer), eller ved lav total belastning, eller mangel på lyst til å sette seg inn i detaljene ved å sette opp Kubernetes, også for høy belastning.
Podman tilbyr ikke fullstendig kompatibilitet, men har en viktig fordel — kompatibilitet med Kubernetes, inkludert ekstra verktøy (buildah og andre). Derfor vil jeg nærme meg valget av verktøy på denne måten: for små team eller med et begrenset budsjett — Docker (med mulig swarm mode), for personlig utvikling på localhost — Podman med kolleger, og for alle andre — Kubernetes.
Jeg er ikke sikker på om situasjonen med Docker vil endre seg i fremtiden, de er tross alt pionerer, og de standardiseres gradvis, men Podman, til tross for sine mangler (kun støtte for Linux, ingen klusterløsning, bygging og andre handlinger — eksterne løsninger), har en klarere fremtid. Derfor inviterer jeg alle interesserte til å diskutere disse konklusjonene i kommentarene.
P.S. 3. august lanserer vi "", hvor du kan lære mer om hvordan det fungerer. Vi vil gå gjennom alle verktøyene, fra grunnleggende abstraksjoner til nettverksinnstillinger, detaljene rundt arbeid med ulike OS og programmeringsspråk. Du vil bli kjent med teknologien og forstå hvor og hvordan det er best å bruke Docker. Vi deler også best practice caser.
Prisen for forhåndsbestilling før lansering: 5000 kr. Du kan finne programmet til "Videokurs om Docker" .
Kilde: habr.com
