Docker en alles, alles, alles

TL;DR: een overzichtsartikel - een gids voor het vergelijken van omgevingen voor het uitvoeren van applicaties in containers. Er wordt gekeken naar de mogelijkheden van Docker en andere vergelijkbare systemen.

Docker en alles, alles, alles

Een beetje geschiedenis van waar het allemaal vandaan kwam

Verhaal

De eerste bekende manier om een ​​applicatie te isoleren is chroot. De systeemaanroep met dezelfde naam zorgt voor een wijziging in de hoofdmap - waardoor toegang wordt verkregen tot het programma dat het heeft aangeroepen, alleen toegang tot bestanden in deze map. Maar als het programma binnenin superuser-rechten krijgt, kan het potentieel "ontsnappen" uit de chroot en toegang krijgen tot het hoofdbesturingssysteem. Naast het wijzigen van de hoofdmap zijn ook andere bronnen (RAM, processor) en toegang tot het netwerk niet beperkt.

De volgende manier is om een ​​volwaardig besturingssysteem in de container te lanceren, met behulp van de mechanismen van de kernel van het besturingssysteem. Deze methode wordt in verschillende besturingssystemen anders genoemd, maar de essentie is hetzelfde: het uitvoeren van verschillende onafhankelijke besturingssystemen, die allemaal op dezelfde kernel draaien waarop het hoofdbesturingssysteem draait. Dit omvat FreeBSD Jails, Solaris Zones, OpenVZ en LXC voor Linux. Isolatie wordt niet alleen geboden voor schijfruimte, maar ook voor andere bronnen, in het bijzonder kan elke container beperkingen hebben op processortijd, RAM, netwerkbandbreedte. In vergelijking met chroot is het moeilijker om de container te verlaten, aangezien de superuser in de container alleen toegang heeft tot de binnenkant van de container, vanwege de noodzaak om het besturingssysteem in de container up-to-date te houden en het gebruik van oude kernel versies (relevant voor Linux, in mindere mate FreeBSD), is er een niet-nul kans om door het kernel-isolatiesysteem te breken en toegang te krijgen tot het hoofdbesturingssysteem.

In plaats van een volwaardig besturingssysteem in een container te lanceren (met een initialisatiesysteem, een pakketbeheerder, enz.), kunnen applicaties onmiddellijk worden gestart, het belangrijkste is om applicaties deze mogelijkheid te bieden (de aanwezigheid van de nodige bibliotheken en andere bestanden). Dit idee diende als basis voor gecontaineriseerde applicatievirtualisatie, waarvan Docker de meest prominente en bekende vertegenwoordiger is. Vergeleken met eerdere systemen resulteerden flexibelere isolatiemechanismen, samen met ingebouwde ondersteuning voor virtuele netwerken tussen containers en applicatiestatus binnen een container, in de mogelijkheid om een ​​enkele holistische omgeving te bouwen van een groot aantal fysieke servers om containers te draaien - zonder de behoefte aan handmatig resourcebeheer.

havenarbeider

Docker is de meest bekende applicatie-containerisatiesoftware. Geschreven in de Go-taal, gebruikt het de reguliere mogelijkheden van de Linux-kernel - cgroups, naamruimten, mogelijkheden, enz., evenals Aufs-bestandssystemen en andere soortgelijke om schijfruimte te besparen.

Docker en alles, alles, alles
Bron: wikimedia

Architectuur

Voorafgaand aan versie 1.11 werkte Docker als een enkele service die alle bewerkingen met containers uitvoerde: afbeeldingen voor containers downloaden, containers lanceren, API-aanvragen verwerken. Sinds versie 1.11 is Docker opgesplitst in verschillende delen die met elkaar communiceren: containerd, om de volledige levenscyclus van containers af te handelen (toewijzing van schijfruimte, downloaden van afbeeldingen, netwerken, starten, installeren en bewaken van de staat van containers) en runC , container-runtimes, gebaseerd op het gebruik van cgroups en andere kenmerken van de Linux-kernel. De docker-service zelf blijft, maar dient nu alleen voor het verwerken van API-verzoeken die naar containerd worden uitgezonden.

Docker en alles, alles, alles

Installatie en configuratie

Mijn favoriete manier om docker te installeren is docker-machine, waarmee je, naast het rechtstreeks installeren en configureren van docker op externe servers (inclusief verschillende clouds), kunt werken met de bestandssystemen van externe servers en ook verschillende opdrachten kunt uitvoeren.

Sinds 2018 is het project echter nauwelijks ontwikkeld, dus zullen we het op de gebruikelijke manier voor de meeste Linux-distributies installeren - door een repository toe te voegen en de benodigde pakketten te installeren.

Deze methode wordt ook gebruikt voor geautomatiseerde installatie, bijvoorbeeld met behulp van Ansible of andere vergelijkbare systemen, maar daar zal ik in dit artikel niet op ingaan.

De installatie zal worden uitgevoerd op Centos 7, ik zal een virtuele machine als server gebruiken om te installeren, voer gewoon de onderstaande opdrachten uit:

# 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

Na de installatie moet u de service starten, in autoload plaatsen:

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

Bovendien kunt u een docker-groep maken, waarvan de gebruikers zonder sudo met docker kunnen werken, logging instellen, toegang tot de API van buitenaf mogelijk maken, vergeet niet de firewall te verfijnen (alles wat niet is toegestaan, is verboden in de voorbeelden hierboven en hieronder - ik heb dit weggelaten voor de eenvoud en visualisatie), maar ik zal hier niet dieper op ingaan.

Andere functies

Naast de bovenstaande docker-machine is er ook een docker-register, een tool voor het opslaan van afbeeldingen voor containers, evenals docker compose - een tool voor het automatiseren van de implementatie van applicaties in containers, YAML-bestanden worden gebruikt om containers te bouwen en te configureren en andere gerelateerde zaken (bijvoorbeeld netwerken, persistente bestandssystemen voor het opslaan van gegevens).

Het kan ook worden gebruikt om pijplijnen voor CICD te organiseren. Een andere interessante functie is het werken in clustermodus, de zogenaamde zwermmodus (vóór versie 1.12 heette het docker-zwerm), waarmee je een enkele infrastructuur uit verschillende servers kunt samenstellen om containers te laten draaien. Er is ondersteuning voor een virtueel netwerk bovenop alle servers, er is een ingebouwde load balancer, evenals ondersteuning voor geheimen voor containers.

De YAML-bestanden van docker compose kunnen met kleine aanpassingen voor dergelijke clusters worden gebruikt, waardoor het onderhoud van kleine en middelgrote clusters voor verschillende doeleinden volledig wordt geautomatiseerd. Voor grote clusters heeft Kubernetes de voorkeur omdat de onderhoudskosten van de zwermmodus hoger kunnen zijn dan die van Kubernetes. Naast runC kun je als uitvoeringsomgeving voor containers bijvoorbeeld installeren Kata-containers

Werken met Docker

Na installatie en configuratie gaan we proberen een cluster te bouwen waarin we GitLab en Docker Registry gaan inzetten voor het ontwikkelteam. Als servers zal ik drie virtuele machines gebruiken, waarop ik bovendien de door GlusterFS gedistribueerde FS zal implementeren, ik zal het gebruiken als docker-volumeopslag, bijvoorbeeld om een ​​faalveilige versie van het docker-register uit te voeren. Belangrijkste componenten om uit te voeren: Docker Registry, Postgresql, Redis, GitLab met ondersteuning voor GitLab Runner bovenop Swarm. Postgresql wordt gelanceerd met clustering stolon, dus u hoeft GlusterFS niet te gebruiken om Postgresql-gegevens op te slaan. De rest van de kritieke gegevens worden opgeslagen op GlusterFS.

Om GlusterFS op alle servers te implementeren (ze worden node1, node2, node3 genoemd), moet u pakketten installeren, de firewall inschakelen en de benodigde mappen maken:

# 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

Na installatie moet het werk aan het configureren van GlusterFS worden voortgezet vanaf één knooppunt, bijvoorbeeld knooppunt1:

# 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

Vervolgens moet u het resulterende volume koppelen (de opdracht moet op alle servers worden uitgevoerd):

# mount /srv/docker

De zwermmodus is geconfigureerd op een van de servers, die leider zal zijn, de rest moet lid worden van het cluster, dus het resultaat van het uitvoeren van de opdracht op de eerste server moet worden gekopieerd en uitgevoerd op de rest.

Initiële clusterconfiguratie, ik voer de opdracht uit op knooppunt1:

# 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

Kopieer het resultaat van de tweede opdracht, voer uit op knooppunt2 en knooppunt3:

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

Dit voltooit de voorlopige configuratie van de servers, laten we beginnen met het configureren van de services, de uit te voeren opdrachten worden gestart vanaf node1, tenzij anders aangegeven.

Laten we eerst netwerken voor containers maken:

# 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

Vervolgens markeren we de servers, dit is nodig om sommige services aan de servers te binden:

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

Vervolgens maken we mappen voor het opslaan van etcd-gegevens, de KV-opslag die Traefik en Stolon nodig hebben. Net als bij Postgresql zijn dit containers die zijn gebonden aan servers, dus voeren we deze opdracht uit op alle servers:

# mkdir -p /srv/etcd

Maak vervolgens een bestand om etcd te configureren en pas het toe:

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

Na een tijdje controleren we of het etcd-cluster is gestegen:

# 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

Maak mappen voor Postgresql, voer de opdracht uit op alle servers:

# mkdir -p /srv/pgsql

Maak vervolgens een bestand om Postgresql te configureren:

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

We genereren geheimen, passen het bestand toe:

# </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

Enige tijd later (kijk naar de uitvoer van het commando docker-service lsdat alle services zijn gestegen) initialiseer het Postgresql-cluster:

# 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

De gereedheid van het Postgresql-cluster controleren:

# 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

We configureren traefik om de toegang tot containers van buitenaf te openen:

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

We starten Redis Cluster, hiervoor maken we een opslagmap op alle knooppunten:

# mkdir -p /srv/redis

05redis.yml

version: '3.7'

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

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

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

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

networks:
  redis:
    external: true

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

Docker-register toevoegen:

06register.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

En tot slot - GitLab:

08gitlab-runner.yml

version: '3.7'

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    networks:
      - pgsql
      - redis
      - traefik
      - gitlab
    ports:
      - 22222:22
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        postgresql['enable'] = false
        redis['enable'] = false
        gitlab_rails['registry_enabled'] = false
        gitlab_rails['db_username'] = "gitlab"
        gitlab_rails['db_password'] = "XXXXXXXXXXX"
        gitlab_rails['db_host'] = "postgresql"
        gitlab_rails['db_port'] = "5432"
        gitlab_rails['db_database'] = "gitlab"
        gitlab_rails['db_adapter'] = 'postgresql'
        gitlab_rails['db_encoding'] = 'utf8'
        gitlab_rails['redis_host'] = 'redis-master'
        gitlab_rails['redis_port'] = '6379'
        gitlab_rails['redis_password'] = 'xxxxxxxxxxx'
        gitlab_rails['smtp_enable'] = true
        gitlab_rails['smtp_address'] = "smtp.yandex.ru"
        gitlab_rails['smtp_port'] = 465
        gitlab_rails['smtp_user_name'] = "[email protected]"
        gitlab_rails['smtp_password'] = "xxxxxxxxx"
        gitlab_rails['smtp_domain'] = "example.com"
        gitlab_rails['gitlab_email_from'] = '[email protected]'
        gitlab_rails['smtp_authentication'] = "login"
        gitlab_rails['smtp_tls'] = true
        gitlab_rails['smtp_enable_starttls_auto'] = true
        gitlab_rails['smtp_openssl_verify_mode'] = 'peer'
        external_url 'http://gitlab.example.com/'
        gitlab_rails['gitlab_shell_ssh_port'] = 22222
    volumes:
      - gitlab_conf:/etc/gitlab
      - gitlab_logs:/var/log/gitlab
      - gitlab_data:/var/opt/gitlab
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
        - node.role == manager
      labels:
        - traefik.enable=true
        - traefik.http.routers.gitlab.rule=Host(`gitlab.example.com`)
        - traefik.http.services.gitlab.loadbalancer.server.port=80
        - traefik.docker.network=traefik
  gitlab-runner:
    image: gitlab/gitlab-runner:latest
    networks:
      - gitlab
    volumes:
      - gitlab_runner_conf:/etc/gitlab
      - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
        - node.role == manager

volumes:
  gitlab_conf:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/gitlab/conf"
  gitlab_logs:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/gitlab/logs"
  gitlab_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/gitlab/data"
  gitlab_runner_conf:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: "/srv/docker/gitlab/runner"

networks:
  pgsql:
    external: true
  redis:
    external: true
  traefik:
    external: true
  gitlab:
    external: true

# mkdir -p /srv/docker/gitlab/conf
# mkdir -p /srv/docker/gitlab/logs
# mkdir -p /srv/docker/gitlab/data
# mkdir -p /srv/docker/gitlab/runner
# docker stack deploy --compose-file 08gitlab-runner.yml gitlab

De uiteindelijke status van het cluster en de services:

# 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

Wat kan er nog verbeterd worden? Zorg ervoor dat u Traefik configureert om met https-containers te werken, voeg tls-codering toe voor Postgresql en Redis. Maar over het algemeen kun je het al als PoC aan ontwikkelaars geven. Laten we nu kijken naar alternatieven voor Docker.

podman

Een andere redelijk bekende engine voor het uitvoeren van containers gegroepeerd op pods (pods, groepen containers die samen worden ingezet). In tegenstelling tot Docker heeft het geen service nodig om containers uit te voeren, al het werk wordt gedaan via de libpod-bibliotheek. Ook geschreven in Go, heeft een OCI-compatibele runtime nodig om containers zoals runC uit te voeren.

Docker en alles, alles, alles

Werken met Podman lijkt in het algemeen op dat van Docker, voor zover je het zo kunt doen (beweerd door velen die het hebben geprobeerd, waaronder de auteur van dit artikel):

$ alias docker=podman

en je kunt blijven werken. Over het algemeen is de situatie met Podman erg interessant, want als de vroege versies van Kubernetes met Docker werkten, dan sinds ongeveer 2015, na de standaardisatie van de containerwereld (OCI - Open Container Initiative) en de scheiding van Docker in containerd en runC , wordt er een alternatief voor Docker ontwikkeld om in Kubernetes te draaien: CRI-O. Podman is in dit opzicht een alternatief voor Docker, gebouwd op de principes van Kubernetes, inclusief containergroepering, maar het belangrijkste doel van het project is om Docker-achtige containers te gebruiken zonder aanvullende services. Om voor de hand liggende redenen is er geen zwermmodus, aangezien de ontwikkelaars duidelijk zeggen dat als je een cluster nodig hebt, je Kubernetes moet nemen.

installatie

Om op Centos 7 te installeren, activeert u gewoon de Extras-repository en installeert u vervolgens alles met de opdracht:

# yum -y install podman

Andere functies

Podman kan eenheden voor systemd genereren, waardoor het probleem van het starten van containers na het opnieuw opstarten van een server wordt opgelost. Bovendien wordt verklaard dat systemd correct werkt als pid 1 in de container. Om containers te bouwen, is er een aparte buildah-tool, er zijn ook tools van derden - analogen van docker-compose, die ook Kubernetes-compatibele configuratiebestanden genereert, dus de overgang van Podman naar Kubernetes is zo eenvoudig mogelijk.

Werkt bij Podman

Aangezien er geen zwermmodus is (het wordt verondersteld over te schakelen naar Kubernetes als er een cluster nodig is), zullen we het in aparte containers assembleren.

Podman-compose installeren:

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

Het resulterende configuratiebestand voor podman is iets anders, omdat we bijvoorbeeld een aparte volumessectie rechtstreeks naar de servicessectie moesten verplaatsen.

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

Resultaat werk:

# 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

Laten we eens kijken wat het zal genereren voor systemd en kubernetes, hiervoor moeten we de naam of id van de pod weten:

# 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: {}

systeemd:

# 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

Helaas doet de gegenereerde unit voor systemd behalve het opstarten van containers niets anders (bijvoorbeeld oude containers opschonen als zo'n service opnieuw wordt opgestart), dus dergelijke dingen zul je zelf moeten toevoegen.

Podman is in principe voldoende om te proberen wat containers zijn, oude configuraties over te dragen voor docker-compose en vervolgens, indien nodig, richting Kubernetes op een cluster te gaan, of een gebruiksvriendelijker alternatief voor Docker te krijgen.

RKT

Project naar archief gegaan ongeveer zes maanden geleden vanwege het feit dat RedHat het heeft gekocht, dus ik zal er niet dieper op ingaan. Over het algemeen liet hij een zeer goede indruk achter, maar vergeleken met Docker, en nog meer met Podman, lijkt hij op een maaidorser. Er was ook een CoreOS-distributie bovenop rkt gebouwd (hoewel ze oorspronkelijk Docker hadden), maar die eindigde ook na de aankoop van RedHat.

Plas

Meer één project, waarvan de auteur gewoon containers wilde bouwen en gebruiken. Aan de hand van de documentatie en code te oordelen, volgde de auteur de normen niet, maar besloot eenvoudig zijn eigen implementatie te schrijven, wat hij in principe deed.

Bevindingen

De situatie met Kubernetes is erg interessant: aan de ene kant kun je met Docker een cluster samenstellen (in zwermmodus), waarmee je zelfs productieomgevingen voor klanten kunt draaien, dit geldt vooral voor kleine teams (3-5 personen ), of met een kleine totale belasting, of het gebrek aan verlangen om de fijne kneepjes van het opzetten van Kubernetes te begrijpen, ook voor hoge belastingen.

Podman biedt geen volledige compatibiliteit, maar heeft één belangrijk voordeel: compatibiliteit met Kubernetes, inclusief extra tools (buildah en andere). Daarom zal ik de keuze van een tool voor werk als volgt benaderen: voor kleine teams, of met een beperkt budget - Docker (met een mogelijke zwermmodus), voor ontwikkeling voor mezelf op een persoonlijke localhost - Podman-kameraden, en voor alle anderen - Kubernetes.

Ik weet niet zeker of de situatie met Docker in de toekomst niet zal veranderen, ze zijn tenslotte pioniers en standaardiseren ook langzaam stap voor stap, maar Podman, met al zijn tekortkomingen (werkt alleen op Linux, er is geen clustering , montage en andere acties zijn beslissingen van derden) de toekomst is duidelijker, dus ik nodig iedereen uit om deze bevindingen in de commentaren te bespreken.

PS Op 3 augustus lanceren we "Docker-videocursuswaar u meer te weten kunt komen over zijn werk. We zullen al zijn tools analyseren: van basisabstracties tot netwerkparameters, nuances van het werken met verschillende besturingssystemen en programmeertalen. Je maakt kennis met de techniek en begrijpt waar en hoe je Docker het beste kunt gebruiken. We zullen ook best practice-cases delen.

Pre-orderkosten vóór release: 5000 roebel. Het programma "Docker Video Cursus" is te vinden op de cursuspagina.

Bron: www.habr.com

Voeg een reactie