Docker och allt, allt, allt

TL;DR: en översiktsartikel-guide som jÀmför miljöer för att köra applikationer i containrar. Möjligheterna med Docker och andra liknande system kommer att diskuteras.

Docker och allt, allt, allt

Lite historia, varifrÄn allt kommer

Historien

Det första allmÀnt kÀnda sÀttet att isolera en applikation Àr chroot. Systemanropet med samma namn Àndrar rotkatalogen - pÄ sÄ sÀtt ges programmet som utför det Ätkomst endast till filer inuti den katalogen. Men om programmet inuti ges superanvÀndarbeteenden kan det potentiellt "fly" frÄn chroot och fÄ tillgÄng till det underliggande operativsystemet. Dessutom begrÀnsas inte andra resurser (arbetsminne, processor) eller nÀtverksÄtkomst vid Àndringen av rotkatalogen.

NÀsta metod Àr att köra ett fullt operativsystem inuti containern, tack vare kÀrnans mekanismer i operativsystemet. I olika operativsystem kallas denna metod pÄ olika sÀtt, men principen Àr densamma - att köra flera oberoende operativsystem, dÀr varje körs med samma kÀrna som det grundlÀggande operativsystemet. Detta inkluderar FreeBSD Jails, Solaris Zones, OpenVZ och LXC för Linux. Isoleringen omfattar inte bara diskutrymme, utan Àven andra resurser; varje container kan ha begrÀnsningar för processorutnyttjande, arbetsminne och nÀtverksbandbredd. JÀmfört med chroot Àr det svÄrare att ta sig ut ur containern, eftersom superanvÀndaren i containern har tillgÄng endast till innehÄllet i containern. Men pÄ grund av behovet av att hÄlla operativsystemet inuti containern uppdaterat och anvÀnda Àldre kÀrnversioner (aktuellt för Linux, i mindre utstrÀckning FreeBSD) finns det en icke-noll chans att "bryta" isoleringssystemet i kÀrnan och fÄ tillgÄng till det grundlÀggande operativsystemet.

IstĂ€llet för att köra ett fullt operativsystem i en container (med initieringssystem, paketförvaltare osv.) kan man direkt köra applikationer, det viktigaste Ă€r att ge applikationerna den möjligheten (genom att ha nödvĂ€ndiga bibliotek och andra filer). Denna idĂ© ligger till grund för containerbaserad virtualisering av applikationer, dĂ€r den mest framtrĂ€dande och kĂ€nda representanten Ă€r Docker. JĂ€mfört med tidigare system har mer flexibla isoleringsmekanismer tillsammans med inbyggt stöd för virtuella nĂ€tverk mellan containrar och övervakning av applikationens tillstĂ„nd inuti containern möjliggjort skapandet av en enhetlig miljö frĂ„n ett stort antal fysiska servrar för körning av containrar — utan behov av manuell resursadministration.

Docker

Docker Ă€r den mest kĂ€nda programvaran för containerisering av applikationer. Den Ă€r skriven i Go och anvĂ€nder standardfunktioner i Linux-kĂ€rnan — cgroups, namespaces, capabilities osv., samt filsystem som Aufs och andra liknande för att spara diskutrymme.

Docker och allt, allt, allt
KĂ€lla: wikimedia

Arkitektur

Före version 1.11 fungerade Docker som en enda tjÀnst som verkstÀllde alla operationer med containrar: nedladdning av bilder för containrar, körning av containrar, behandling av API-förfrÄgningar. FrÄn och med version 1.11 har Docker delats upp i flera komponenter som interagerar med varandra: containerd, för att hantera hela livscykeln för containrar (tilldelning av diskutrymme, nedladdning av bilder, arbete med nÀtverket, körning, installation och övervakning av tillstÄndet för containrar) och runC, en körmiljö för containrar baserad pÄ anvÀndning av cgroups och andra funktioner i Linux-kÀrnan. TjÀnsten docker finns fortfarande, men nu anvÀnds den endast för att behandla API-förfrÄgningar som översÀtts till containerd.

Docker och allt, allt, allt

Installation och konfiguration

Mitt favorit sÀtt att installera docker Àr docker-machine, som förutom sjÀlva installationen och konfigurationen av docker pÄ fjÀrrservrar (inklusive olika moln) ocksÄ ger möjlighet att arbeta med filsystem pÄ fjÀrrservrar, samt kan köra olika kommandon.

Men sedan 2018 har projektet knappt utvecklats, dĂ€rför kommer vi att göra installationen pĂ„ det vanliga sĂ€ttet för de flesta Linux-distributioner — genom att lĂ€gga till ett repository och installera nödvĂ€ndiga paket.

Denna metod anvÀnds ocksÄ vid automatiserad installation, till exempel med Ansible eller liknande system, men jag kommer inte att behandla det i denna artikel.

Installationen kommer att genomföras pÄ CentOS 7, jag kommer att anvÀnda en virtuell maskin som server, för installationen rÀcker det att köra kommandona nedan:

# yum install -y yum-utils
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# yum install docker-ce docker-ce-cli containerd.io

Efter installationen mÄste tjÀnsten startas och lÀggas till vid systemstart:

# systemctl enable docker
# systemctl start docker
# firewall-cmd --zone=public --add-port=2377/tcp --permanent

Dessutom kan man skapa en docker-grupp dÀr anvÀndare kan arbeta med docker utan sudo, konfigurera loggning, aktivera extern API-Ätkomst, och viktigt - konfigurera brandvÀggen mer specifikt (blockera allt som inte Àr tillÄtet, nÄgot jag utelÀmnar hÀr för enkelhetens skull), men jag kommer inte att gÄ in mer pÄ detaljer hÀr.

Andra möjligheter

Förutom den nÀmnda docker maskinen finns det Àven docker registry, ett verktyg för att lagra bilder för containrar, samt docker compose - ett verktyg för att automatisera distribution av applikationer i containrar, dÀr YAML-filer anvÀnds för att bygga och konfigurera containrar och andra relaterade objekt (som nÀtverk och permanenta filsystem för datalagring).

Med det hÀr verktyget kan man ocksÄ organisera pipelines för CI/CD. En annan intressant funktion Àr arbete i klusterlÀge, sÄ kallat swarm mode (kÀnd som docker swarm före version 1.12), vilket möjliggör att skapa en enhetlig infrastruktur för att köra containrar frÄn flera servrar. Det finns stöd för virtuella nÀtverk över alla servrar, en inbyggd belastningsbalanserare och stöd för hemligheter för containrar.

YAML-filer frÄn docker compose med smÄ Àndringar kan anvÀndas för sÄdana kluster, vilket helt automatiserar underhÄllet av smÄ och medelstora kluster för olika ÀndamÄl. För stora kluster Àr det att föredra att anvÀnda Kubernetes, eftersom underhÄllskostnaderna för swarm mode kan överstiga de för Kubernetes. Utöver runC kan exempelvis Kata containers

Arbete med Docker

Efter installation och konfiguration ska vi försöka skapa ett kluster dĂ€r vi deployar GitLab och Docker Registry för utvecklarteamet. Jag kommer att anvĂ€nda tre virtuella maskiner som servrar, dĂ€r jag dessutom kommer att deploya ett distribuerat filsystem GlusterFS, som jag kommer att anvĂ€nda som lagring för docker volumes, till exempel för att köra en redundant version av docker registry. Nyckelkomponenterna för att starta Ă€r: Docker Registry, Postgresql, Redis, GitLab med stöd för GitLab Runner ovanpĂ„ Swarm. Vi kommer att starta Postgresql med klustring. Stolon, dĂ€rför behöver vi inte anvĂ€nda GlusterFS för att lagra Postgresql-data. Övriga kritiska data kommer att lagras pĂ„ GlusterFS.

För att deploya GlusterFS pÄ alla servrar (dessa benÀmns node1, node2, node3) mÄste vi installera paket, tillÄta brandvÀggens arbete och skapa nödvÀndiga 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/fstab

Efter installationen bör arbetet med att konfigurera GlusterFS fortsÀtta frÄn en nod, till exempel node1:

# gluster peer probe node2
# gluster peer probe node3
# gluster volume create docker replica 3 node1:/srv/gluster node2:/srv/gluster node3:/srv/gluster force
# gluster volume start docker

DÀrefter mÄste vi montera den skapade volymen (kommandot ska köras pÄ alla servrar):

# mount /srv/docker

Konfigurationen av swarm-lÀget utförs pÄ en av servrarna, som kommer att vara Leader. De andra mÄste ansluta sig till klustret, sÄ resultatet av kommandot pÄ den första servern mÄste kopieras och köras pÄ de övriga.

Den första konfigurationen av klustret, jag kör kommandot 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 manager

Vi kopierar resultatet av det andra kommandot och kör det pÄ node2 och node3:

# docker swarm join --token SWMTKN-x-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxx xx.xx.xx.xx:2377
This node joined a swarm as a manager.

SÄ hÀr har vi slutfört den preliminÀra konfigurationen av servrarna, vi gÄr vidare till konfigurationen av tjÀnsterna, kommandona för utförande kommer att köras frÄn node1, om inget annat anges.

Det första vi gör Àr att skapa nÀtverk för containrar:

# docker network create --driver=overlay etcd
# docker network create --driver=overlay pgsql
# docker network create --driver=overlay redis
# docker network create --driver=overlay traefik
# docker network create --driver=overlay gitlab

DÀrefter mÀrker vi servrarna, detta behövs för att koppla vissa tjÀnster till servrar:

# docker node update --label-add nodename=node1 node1
# docker node update --label-add nodename=node2 node2
# docker node update --label-add nodename=node3 node3

Vidare skapar vi kataloger för lagring av etcd-data, KV-lagringen som behövs för Traefik och Stolon. Precis som Postgresql kommer dessa att vara kopplade till servercontainrar, sÄ vi kör detta kommando pÄ alla servrar:

# mkdir -p /srv/etcd

DÀrefter skapar vi en fil för konfiguration av etcd och tillÀmpar 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 etcd

Efter en stund kontrollerar vi att etcd-klustret har startat:

# docker exec $(docker ps | awk '/etcd/ {print $1}')  etcdctl member list
ade526d28b1f92f7: name=etcd1 peerURLs=http://etcd1:2380 clientURLs=http://etcd1:2379 isLeader=false
bd388e7810915853: name=etcd3 peerURLs=http://etcd3:2380 clientURLs=http://etcd3:2379 isLeader=false
d282ac2ce600c1ce: name=etcd2 peerURLs=http://etcd2:2380 clientURLs=http://etcd2:2379 isLeader=true
# docker exec $(docker ps | awk '/etcd/ {print $1}')  etcdctl cluster-health
member ade526d28b1f92f7 is healthy: got healthy result from http://etcd1:2379
member bd388e7810915853 is healthy: got healthy result from http://etcd3:2379
member d282ac2ce600c1ce is healthy: got healthy result from http://etcd2:2379
cluster is healthy

Vi skapar kataloger för Postgresql, kommandot körs pÄ alla servrar:

# mkdir -p /srv/pgsql

DÀrefter skapar vi en fil för konfigurationen av Postgresql:

01pgsql.yml

version: '3.7'

services:
  pgsentinel:
    image: sorintlab/stolon:master-pg10
    command:
      - gosu
      - stolon
      - stolon-sentinel
      - --cluster-name=stolon-cluster
      - --store-backend=etcdv3
      - --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
      - --log-level=debug
    networks:
      - etcd
      - pgsql
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 30s
        order: stop-first
        failure_action: pause
  pgkeeper1:
    image: sorintlab/stolon:master-pg10
    hostname: pgkeeper1
    command:
      - gosu
      - stolon
      - stolon-keeper
      - --pg-listen-address=pgkeeper1
      - --pg-repl-username=replica
      - --uid=pgkeeper1
      - --pg-su-username=postgres
      - --pg-su-passwordfile=/run/secrets/pgsql
      - --pg-repl-passwordfile=/run/secrets/pgsql_repl
      - --data-dir=/var/lib/postgresql/data
      - --cluster-name=stolon-cluster
      - --store-backend=etcdv3
      - --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
    networks:
      - etcd
      - pgsql
    environment:
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - pgkeeper1:/var/lib/postgresql/data
    secrets:
      - pgsql
      - pgsql_repl
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node1]
  pgkeeper2:
    image: sorintlab/stolon:master-pg10
    hostname: pgkeeper2
    command:
      - gosu
      - stolon 
      - stolon-keeper
      - --pg-listen-address=pgkeeper2
      - --pg-repl-username=replica
      - --uid=pgkeeper2
      - --pg-su-username=postgres
      - --pg-su-passwordfile=/run/secrets/pgsql
      - --pg-repl-passwordfile=/run/secrets/pgsql_repl
      - --data-dir=/var/lib/postgresql/data
      - --cluster-name=stolon-cluster
      - --store-backend=etcdv3
      - --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
    networks:
      - etcd
      - pgsql
    environment:
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - pgkeeper2:/var/lib/postgresql/data
    secrets:
      - pgsql
      - pgsql_repl
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node2]
  pgkeeper3:
    image: sorintlab/stolon:master-pg10
    hostname: pgkeeper3
    command:
      - gosu
      - stolon 
      - stolon-keeper
      - --pg-listen-address=pgkeeper3
      - --pg-repl-username=replica
      - --uid=pgkeeper3
      - --pg-su-username=postgres
      - --pg-su-passwordfile=/run/secrets/pgsql
      - --pg-repl-passwordfile=/run/secrets/pgsql_repl
      - --data-dir=/var/lib/postgresql/data
      - --cluster-name=stolon-cluster
      - --store-backend=etcdv3
      - --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
    networks:
      - etcd
      - pgsql
    environment:
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - pgkeeper3:/var/lib/postgresql/data
    secrets:
      - pgsql
      - pgsql_repl
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.nodename == node3]
  postgresql:
    image: sorintlab/stolon:master-pg10
    command: gosu stolon stolon-proxy --listen-address 0.0.0.0 --cluster-name stolon-cluster --store-backend=etcdv3 --store-endpoints http://etcd1:2379,http://etcd2:2379,http://etcd3:2379
    networks:
      - etcd
      - pgsql
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 30s
        order: stop-first
        failure_action: rollback

volumes:
  pgkeeper1:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/pgsql"
  pgkeeper2:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/pgsql"
  pgkeeper3:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/pgsql"

secrets:
  pgsql:
    file: "/srv/docker/postgres"
  pgsql_repl:
    file: "/srv/docker/replica"

networks:
  etcd:
    external: true
  pgsql:
    external: true

Vi genererar hemligheter, tillÀmpar 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 pgsql

Efter en stund (vi kollar kommandots utdata docker service ls, att alla tjÀnster har startats) initierar vi PostgreSQL-klustret:

# docker exec $(docker ps | awk '/pgkeeper/ {print $1}') stolonctl --cluster-name=stolon-cluster --store-backend=etcdv3 --store-endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379 init

Vi kontrollerar att PostgreSQL-klustret Àr redo:

# 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 konfigurerar Traefik för att öppna Ätkomst till containrar utifrÄn:

03traefik.yml

version: '3.7'

services:
  traefik:
    image: traefik:latest
    command: >
      --log.level=INFO
      --providers.docker=true
      --entryPoints.web.address=:80
      --providers.providersThrottleDuration=2
      --providers.docker.watch=true
      --providers.docker.swarmMode=true
      --providers.docker.swarmModeRefreshSeconds=15s
      --providers.docker.exposedbydefault=false
      --accessLog.bufferingSize=0
      --api=true
      --api.dashboard=true
      --api.insecure=true
    networks:
      - traefik
    ports:
      - 80:80
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      replicas: 3
      placement:
        constraints:
          - node.role == manager
        preferences:
          - spread: node.id
      labels:
        - traefik.enable=true
        - traefik.http.routers.traefik.rule=Host(`traefik.example.com`)
        - traefik.http.services.traefik.loadbalancer.server.port=8080
        - traefik.docker.network=traefik

networks:
  traefik:
    external: true

# docker stack deploy --compose-file 03traefik.yml traefik

Vi startar Redis-klustret, för detta skapar vi en katalog för lagring pÄ alla noder:

# mkdir -p /srv/redis

05redis.yml

version: '3.7'

services:
  redis-master:
    image: 'bitnami/redis:latest'
    networks:
      - redis
    ports:
      - '6379:6379'
    environment:
      - REDIS_REPLICATION_MODE=master
      - REDIS_PASSWORD=xxxxxxxxxxx
    deploy:
      mode: global
      restart_policy:
        condition: any
    volumes:
      - 'redis:/opt/bitnami/redis/etc/'

  redis-replica:
    image: 'bitnami/redis:latest'
    networks:
      - redis
    ports:
      - '6379'
    depends_on:
      - redis-master
    environment:
      - REDIS_REPLICATION_MODE=slave
      - REDIS_MASTER_HOST=redis-master
      - REDIS_MASTER_PORT_NUMBER=6379
      - REDIS_MASTER_PASSWORD=xxxxxxxxxxx
      - REDIS_PASSWORD=xxxxxxxxxxx
    deploy:
      mode: replicated
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: any

  redis-sentinel:
    image: 'bitnami/redis:latest'
    networks:
      - redis
    ports:
      - '16379'
    depends_on:
      - redis-master
      - redis-replica
    entrypoint: |
      bash -c 'bash -s <<EOF
      "/bin/bash" -c "cat < /opt/bitnami/redis/etc/sentinel.conf
      port 16379
      dir /tmp
      sentinel monitor master-node redis-master 6379 2
      sentinel down-after-milliseconds master-node 5000
      sentinel parallel-syncs master-node 1
      sentinel failover-timeout master-node 5000
      sentinel auth-pass master-node xxxxxxxxxxx
      sentinel announce-ip redis-sentinel
      sentinel announce-port 16379
      EOF"
      "/bin/bash" -c "redis-sentinel /opt/bitnami/redis/etc/sentinel.conf"
      EOF'
    deploy:
      mode: global
      restart_policy:
        condition: any

volumes:
  redis:
    driver: local
    driver_opts:
      type: 'none'
      o: 'bind'
      device: "/srv/redis"

networks:
  redis:
    external: true

# docker stack deploy --compose-file 05redis.yml redis

Vi lÀgger till 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 registry

Och slutligen — 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 gitlab

Det slutgiltiga tillstÄndet för klustret och tjÀnsterna:

# docker service ls
ID                  NAME                   MODE                REPLICAS            IMAGE                          PORTS
lef9n3m92buq        etcd_etcd1             replicated          1/1                 quay.io/coreos/etcd:latest
ij6uyyo792x5        etcd_etcd2             replicated          1/1                 quay.io/coreos/etcd:latest
fqttqpjgp6pp        etcd_etcd3             replicated          1/1                 quay.io/coreos/etcd:latest
hq5iyga28w33        gitlab_gitlab          replicated          1/1                 gitlab/gitlab-ce:latest        *:22222->22/tcp
dt7s6vs0q4qc        gitlab_gitlab-runner   replicated          1/1                 gitlab/gitlab-runner:latest
k7uoezno0h9n        pgsql_pgkeeper1        replicated          1/1                 sorintlab/stolon:master-pg10
cnrwul4r4nse        pgsql_pgkeeper2        replicated          1/1                 sorintlab/stolon:master-pg10
frflfnpty7tr        pgsql_pgkeeper3        replicated          1/1                 sorintlab/stolon:master-pg10
x7pqqchi52kq        pgsql_pgsentinel       replicated          3/3                 sorintlab/stolon:master-pg10
mwu2wl8fti4r        pgsql_postgresql       replicated          3/3                 sorintlab/stolon:master-pg10
9hkbe2vksbzb        redis_redis-master     global              3/3                 bitnami/redis:latest           *:6379->6379/tcp
l88zn8cla7dc        redis_redis-replica    replicated          3/3                 bitnami/redis:latest           *:30003->6379/tcp
1utp309xfmsy        redis_redis-sentinel   global              3/3                 bitnami/redis:latest           *:30002->16379/tcp
oteb824ylhyp        registry_registry      replicated          1/1                 registry:2.6
qovrah8nzzu8        traefik_traefik        replicated          3/3                 traefik:latest                 *:80->80/tcp, *:443->443/tcp

Vad mer kan förbÀttras? Se till att konfigurera Traefik för att köra containrar över https, lÀgg till tls-kryptering för Postgresql och Redis. Men i stort sett kan det redan lÀmnas till utvecklarna som en PoC. LÄt oss nu titta pÄ alternativa lösningar till Docker.

Podman

En annan ganska kÀnd motor för att köra containrar, grupperade i pods (pods, grupper av containrar som distribueras tillsammans). Till skillnad frÄn Docker krÀver den ingen tjÀnst för att köra containrar, allt arbete utförs via biblioteket libpod. Den Àr ocksÄ skriven i Go och krÀver en OCI-kompatibel runtime för att köra containrar, exempelvis runC.

Docker och allt, allt, allt

Arbetet med Podman liknar i stort sett arbetet med Docker, till och med sÄ att man kan göra sÄ hÀr (det har rapporterats av mÄnga testare, inklusive författaren av denna artikel):

$ alias docker=podman

och man kan fortsĂ€tta arbeta. I stort sett Ă€r situationen med Podman ganska intressant, för om de tidiga versionerna av Kubernetes arbetade med Docker, sĂ„ började det ungefĂ€r 2015, efter standardiseringen av containervĂ€rlden (OCI — Open Container Initiative) och uppdelningen av Docker i containerd och runC, att utvecklas ett alternativ till Docker för att köras i Kubernetes: CRI-O. Podman Ă€r i detta avseende ett alternativ till Docker, byggt pĂ„ principer frĂ„n Kubernetes, inklusive gruppering av containrar, men det huvudsakliga mĂ„let med projektet Ă€r att köra containrar i Docker-stil utan extra tjĂ€nster. Av uppenbara skĂ€l finns inget swarm-lĂ€ge, eftersom utvecklarna tydligt sĂ€ger att om man behöver kluster — ta Kubernetes.

Installation

För installation i Centos 7 rÀcker det med att aktivera Extras-repot, varefter man installerar allt med kommandot:

# yum -y install podman

Andra möjligheter

Podman kan generera enheter för systemd, vilket löser uppgiften att köra containrar efter en serveromstart. Dessutom har det rapporterats att systemd fungerar korrekt som pid 1 i containern. För att bygga containrar finns det ett separat verktyg, buildah, och det finns ocksĂ„ tredjepartsverktyg — analoger till docker-compose, som Ă€ven genererar konfigurationsfiler som Ă€r kompatibla med Kubernetes, sĂ„ övergĂ„ngen frĂ„n Podman till Kubernetes har gjorts sĂ„ enkel som möjligt.

Arbete med Podman

Eftersom det inte finns nĂ„got swarm-lĂ€ge (det förutsĂ€tts att man gĂ„r över till Kubernetes om man behöver kluster) — kommer vi att bygga med separata containrar.

Installera podman-compose:

# yum -y install python3-pip
# pip3 install podman-compose

Den resulterande konfigurationsfilen för podman skiljer sig nÄgot, sÄ t.ex. var man tvungen att flytta en separat sektion för volumes direkt till sektionen med tjÀnster.

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

Resultat av arbetet:

# 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

LÄt oss se vad det genererar för systemd och Kubernetes, först mÄste vi veta namnet eller ID:t pÄ podden:

# podman pod ls
POD ID         NAME   STATUS    CREATED          # OF CONTAINERS   INFRA ID
71fc2b2a5c63   root   Running   11 minutes ago   3                 db40ab8bf84b

Kubernetes:

# podman generate kube 71fc2b2a5c63
# Generation of Kubernetes YAML is still under development!
#
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-1.6.4
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2020-07-29T19:22:40Z"
  labels:
    app: root
  name: root
spec:
  containers:
  - command:
    - /assets/wrapper
    env:
    - name: PATH
      value: /opt/gitlab/embedded/bin:/opt/gitlab/bin:/assets:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    - name: TERM
      value: xterm
    - name: HOSTNAME
      value: gitlab.example.com
    - name: container
      value: podman
    - name: GITLAB_OMNIBUS_CONFIG
      value: |
        gitlab_rails['gitlab_shell_ssh_port'] = 22222
    - name: LANG
      value: C.UTF-8
    image: docker.io/gitlab/gitlab-ce:latest
    name: rootgitlab1
    ports:
    - containerPort: 22
      hostPort: 22222
      protocol: TCP
    - containerPort: 80
      hostPort: 80
      protocol: TCP
    resources: {}
    securityContext:
      allowPrivilegeEscalation: true
      capabilities: {}
      privileged: false
      readOnlyRootFilesystem: false
    volumeMounts:
    - mountPath: /var/opt/gitlab
      name: srv-podman-gitlab-data
    - mountPath: /var/log/gitlab
      name: srv-podman-gitlab-logs
    - mountPath: /etc/gitlab
      name: srv-podman-gitlab-conf
    workingDir: /
  - command:
    - run
    - --user=gitlab-runner
    - --working-directory=/home/gitlab-runner
    env:
    - name: PATH
      value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    - name: TERM
      value: xterm
    - name: HOSTNAME
    - name: container
      value: podman
    image: docker.io/gitlab/gitlab-runner:alpine
    name: rootgitlab-runner1
    resources: {}
    securityContext:
      allowPrivilegeEscalation: true
      capabilities: {}
      privileged: false
      readOnlyRootFilesystem: false
    volumeMounts:
    - mountPath: /etc/gitlab-runner
      name: srv-podman-gitlab-runner
    - mountPath: /var/run/docker.sock
      name: var-run-docker.sock
    workingDir: /
  volumes:
  - hostPath:
      path: /srv/podman/gitlab/runner
      type: Directory
    name: srv-podman-gitlab-runner
  - hostPath:
      path: /var/run/docker.sock
      type: File
    name: var-run-docker.sock
  - hostPath:
      path: /srv/podman/gitlab/data
      type: Directory
    name: srv-podman-gitlab-data
  - hostPath:
      path: /srv/podman/gitlab/logs
      type: Directory
    name: srv-podman-gitlab-logs
  - hostPath:
      path: /srv/podman/gitlab/conf
      type: Directory
    name: srv-podman-gitlab-conf
status: {}

Systemd:

# podman generate systemd 71fc2b2a5c63
# pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020

[Unit]
Description=Podman pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
Documentation=man:podman-generate-systemd(1)
Requires=container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
Before=container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service

[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa
ExecStop=/usr/bin/podman stop -t 10 db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaa/userdata/conmon.pid

[Install]
WantedBy=multi-user.target
# container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020

[Unit]
Description=Podman container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service
Documentation=man:podman-generate-systemd(1)
RefuseManualStart=yes
RefuseManualStop=yes
BindsTo=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
After=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service

[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864
ExecStop=/usr/bin/podman stop -t 10 da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864/userdata/conmon.pid

[Install]
WantedBy=multi-user.target
# container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service
# autogenerated by Podman 1.6.4
# Thu Jul 29 15:23:28 EDT 2020

[Unit]
Description=Podman container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service
Documentation=man:podman-generate-systemd(1)
RefuseManualStart=yes
RefuseManualStop=yes
BindsTo=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service
After=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service

[Service]
Restart=on-failure
ExecStart=/usr/bin/podman start 781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3
ExecStop=/usr/bin/podman stop -t 10 781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3
KillMode=none
Type=forking
PIDFile=/var/run/containers/storage/overlay-containers/781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3/userdata/conmon.pid

[Install]
WantedBy=multi-user.target

TyvÀrr gör den genererade enheten för systemd mer Àn att bara starta containrar (till exempel rensa gamla containrar vid omstart av en sÄdan tjÀnst), sÄ sÄdana saker mÄste skrivas in manuellt.

I princip Àr Podman tillrÀckligt för att prova vad containrar Àr, att överföra gamla konfigurationer för docker-compose, och efter det gÄ vidare till Kubernetes om man behöver ett kluster, annars fÄ en enklare alternativ till Docker.

rkt

Projektet har lagts ned ungefÀr för sex mÄnader sedan eftersom det köptes av RedHat, sÄ jag kommer inte att gÄ djupare in pÄ det. Generellt sett gav det ett ganska bra intryck, men jÀmfört med Docker, och speciellt med Podman, framstÄr det som en komplicerad lösning. Det fanns ocksÄ en distribution av CoreOS som byggdes pÄ rkt (Àven om de ursprungligen hade Docker), men dess stöd upphörde ocksÄ efter att RedHat köpte det.

Plash

Mer ett projekt, vars skapare ville helt enkelt samla och köra containrar. Enligt dokumentationen och koden följde skaparen inte standarder, utan bestÀmde sig för att skriva sin egen implementation, vilket han i princip gjorde.

Slutsatser

Situationen med Kubernetes ser mycket intressant ut: Ä ena sidan kan man med Docker bygga ett kluster (i swarm-lÀget), vilket till och med kan anvÀndas för att köra produktionsmiljöer för kunder, detta Àr sÀrskilt relevant för smÄ team (3-5 personer) eller vid en lÄg total belastning, eller om man inte vill hantera nyanserna i installationen av Kubernetes, sÀrskilt för hög belastning.

Podman erbjuder inte fullstĂ€ndig kompatibilitet, men det har en viktig fördel — kompatibilitet med Kubernetes, inklusive ytterligare verktyg (buildah med flera). DĂ€rför kommer jag att nĂ€rma mig valet av verktyg pĂ„ följande sĂ€tt: för smĂ„ team eller med begrĂ€nsad budget — Docker (eventuellt i swarm-lĂ€ge), för personlig utveckling pĂ„ lokal localhost — Podman med kollegor, och för alla andra — Kubernetes.

Jag Ă€r inte sĂ€ker pĂ„ att situationen med Docker inte kommer att förĂ€ndras i framtiden, de Ă€r trots allt pionjĂ€rer och standardiseras gradvis, men Podman, med alla sina nackdelar (fungerar endast pĂ„ Linux, ingen klustring, byggande och andra Ă„tgĂ€rder — genom tredjepartslösningar), har en tydligare framtid, dĂ€rför inbjuder jag alla intresserade att diskutera dessa slutsatser i kommentarerna.

P.S. 3 augusti lanserar vi "Videokurs om Docker", dÀr man kan fÄ mer information om dess funktion. Vi kommer att gÄ igenom alla dess verktyg: frÄn grundlÀggande abstraktioner till nÀtverksinstÀllningar, nyanser inom olika operativsystem och programmeringssprÄk. Du kommer att bekanta dig med teknologin och förstÄ var och hur du bÀst kan anvÀnda Docker. Vi kommer ocksÄ att dela best practice-fall.

FörhandsbestÀllning fram till lansering: 5000 kr. Programmet för "Videokurs om Docker" kan ses pÄ kursens sida.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster