Docker i tot, tot, tot

TL;DR: una guia general per comparar marcs per executar aplicacions en contenidors. Es tindran en compte les capacitats de Docker i altres sistemes similars.

Docker i tot, tot, tot

Una petita història d'on va sortir tot

Història

El primer mètode conegut per aïllar una aplicació és chroot. La trucada del sistema del mateix nom garanteix que es canvia el directori arrel, assegurant així que el programa que l'ha cridat només té accés als fitxers d'aquest directori. Però si un programa té privilegis d'arrel internament, pot "escapar" del chroot i accedir al sistema operatiu principal. A més, a més de canviar el directori arrel, altres recursos (RAM, processador), així com l'accés a la xarxa, no estan limitats.

El següent mètode és llançar un sistema operatiu complet dins d'un contenidor, utilitzant els mecanismes del nucli del sistema operatiu. Aquest mètode s'anomena de manera diferent en diferents sistemes operatius, però l'essència és la mateixa: llançar diversos sistemes operatius independents, cadascun dels quals executa el mateix nucli en què s'executa el sistema operatiu principal. Aquests inclouen les presons FreeBSD, Solaris Zones, OpenVZ i LXC per a Linux. L'aïllament està garantit no només per l'espai en disc, sinó també per altres recursos; en particular, cada contenidor pot tenir limitacions en el temps del processador, la memòria RAM i l'amplada de banda de la xarxa. En comparació amb chroot, sortir del contenidor és més difícil, ja que el superusuari del contenidor només té accés al contingut del contenidor, però, per la necessitat de mantenir el sistema operatiu dins del contenidor actualitzat i l'ús de versions anteriors. dels nuclis (rellevants per a Linux, en menor mesura FreeBSD), hi ha una probabilitat diferent de zero de "sfondre" el sistema d'aïllament del nucli i obtenir accés al sistema operatiu principal.

En lloc de llançar un sistema operatiu complet en un contenidor (amb un sistema d'inicialització, gestor de paquets, etc.), podeu llançar aplicacions immediatament, el més important és oferir a les aplicacions aquesta oportunitat (la presència de les biblioteques necessàries). i altres fitxers). Aquesta idea va servir de base per a la virtualització d'aplicacions en contenidors, el representant més destacat i conegut de les quals és Docker. En comparació amb els sistemes anteriors, els mecanismes d'aïllament més flexibles, juntament amb el suport integrat per a xarxes virtuals entre contenidors i el seguiment de l'estat de l'aplicació dins del contenidor, van donar com a resultat la capacitat de crear un únic entorn coherent a partir d'un gran nombre de servidors físics per executar contenidors. sense necessitat de gestió manual de recursos.

estibador

Docker és el programari de contenidorització d'aplicacions més famós. Escrit en l'idioma Go, utilitza les característiques estàndard del nucli de Linux: cgroups, espais de noms, capacitats, etc., així com sistemes de fitxers Aufs i altres per estalviar espai al disc.

Docker i tot, tot, tot
Font: wikimedia

arquitectura

Abans de la versió 1.11, Docker funcionava com un servei únic que realitzava totes les operacions amb contenidors: descàrrega d'imatges per a contenidors, llançament de contenidors, processament de sol·licituds d'API. A partir de la versió 1.11, Docker es va dividir en diverses parts que interactuen entre elles: containerd, per processar tot el cicle de vida dels contenidors (assignar espai en disc, descarregar imatges, treballar amb la xarxa, llançar, instal·lar i controlar l'estat dels contenidors) i runC, l'entorn d'execució de contenidors, basat en l'ús de cgroups i altres característiques del nucli Linux. El servei Docker en si es manté, però ara només serveix per processar sol·licituds d'API traduïdes a containerd.

Docker i tot, tot, tot

Instal·lació i configuració

La meva manera preferida d'instal·lar docker és docker-machine, que, a més d'instal·lar i configurar directament docker en servidors remots (inclosos diversos núvols), permet treballar amb sistemes de fitxers de servidors remots i també pot executar diverses ordres.

Tanmateix, des del 2018, el projecte gairebé no s'ha desenvolupat, per la qual cosa l'instal·larem de la manera estàndard per a la majoria de distribucions de Linux: afegint un repositori i instal·lant els paquets necessaris.

Aquest mètode també s'utilitza per a la instal·lació automatitzada, per exemple utilitzant Ansible o altres sistemes similars, però no ho tindré en compte en aquest article.

La instal·lació es durà a terme a Centos 7, faré servir una màquina virtual com a servidor, per instal·lar només cal que executeu les ordres següents:

# 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

Després de la instal·lació, heu d'iniciar el servei i posar-lo a l'inici:

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

A més, podeu crear un grup docker, els usuaris del qual podran treballar amb docker sense sudo, configurar el registre, habilitar l'accés a l'API des de l'exterior i no oblideu configurar el tallafoc amb més precisió (tot el que no estigui permès). està prohibit als exemples anteriors i següents; ho vaig ometre per simplicitat i claredat), però no entraré en més detalls aquí.

Altres característiques

A més de la màquina docker esmentada anteriorment, també hi ha el registre docker, una eina per emmagatzemar imatges per a contenidors, així com la composició docker, una eina per automatitzar el desplegament d'aplicacions en contenidors, els fitxers YAML s'utilitzen per construir i configurar contenidors. i altres coses relacionades (per exemple, xarxes, sistemes de fitxers persistents per a l'emmagatzematge de dades).

També es pot utilitzar per organitzar transportadors per CICD. Una altra característica interessant és treballar en mode clúster, l'anomenat mode swarm (abans de la versió 1.12 es coneixia com a docker swarm), que permet muntar una única infraestructura a partir de diversos servidors per executar contenidors. Hi ha suport per a una xarxa virtual a la part superior de tots els servidors, hi ha un equilibrador de càrrega integrat, així com suport per a secrets per als contenidors.

Els fitxers YAML de Docker Compose, amb petites modificacions, es poden utilitzar per a aquests clústers, automatitzant completament el manteniment de clústers petits i mitjans per a diversos propòsits. Per a clústers grans, Kubernetes és preferible perquè els costos de manteniment del mode eixam poden superar els de Kubernetes. A més de runC, podeu instal·lar, per exemple, com a entorn d'execució del contenidor Contenidors Kata

Treballant amb Docker

Després de la instal·lació i configuració, intentarem muntar un clúster en el qual desplegarem GitLab i Docker Registry per a l'equip de desenvolupament. Faré servir tres màquines virtuals com a servidors, en els quals, addicionalment, desplegaré el FS GlusterFS distribuït; el faré servir com a emmagatzematge de volums docker, per exemple, per executar una versió tolerant a errors del registre docker. Components clau per executar: Docker Registry, Postgresql, Redis, GitLab amb suport per a GitLab Runner a la part superior de Swarm. Llançarem Postgresql amb clustering Estolon, de manera que no cal que utilitzeu GlusterFS per emmagatzemar dades de Postgresql. La resta de dades crítiques s'emmagatzemaran a GlusterFS.

Per implementar GlusterFS a tots els servidors (s'anomenen node1, node2, node3), heu d'instal·lar paquets, habilitar el tallafoc i crear els directoris necessaris:

# 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

Després de la instal·lació, el treball en la configuració de GlusterFS s'ha de continuar des d'un node, per exemple 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

A continuació, heu de muntar el volum resultant (l'ordre s'ha d'executar a tots els servidors):

# mount /srv/docker

El mode eixam es configura en un dels servidors, que serà el Leader, la resta s'haurà d'unir al clúster, per tant caldrà copiar i executar en els altres el resultat d'executar l'ordre al primer servidor.

Configuració inicial del clúster, executo l'ordre al 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

Copiem el resultat de la segona ordre i l'executem al node2 i al node3:

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

En aquest punt, s'ha completat la configuració preliminar dels servidors, procedim a la configuració dels serveis; les ordres a executar s'executaran des del node1, tret que s'especifiqui el contrari.

En primer lloc, creem xarxes per als contenidors:

# 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

Després marquem els servidors, això és necessari per vincular alguns serveis als servidors:

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

A continuació, creem directoris per emmagatzemar dades etcd, emmagatzematge KV, que és necessari per a Traefik i Stolon. De manera similar a Postgresql, aquests seran contenidors lligats a servidors, de manera que executem aquesta ordre a tots els servidors:

# mkdir -p /srv/etcd

A continuació, creeu un fitxer per configurar etcd i utilitzeu-lo:

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

Després d'un temps, comprovem que el clúster etcd estigui activat:

# 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

Creem directoris per a Postgresql, executem l'ordre a tots els servidors:

# mkdir -p /srv/pgsql

A continuació, creeu un fitxer per configurar 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

Generem secrets i fem servir el fitxer:

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

Després d'un temps (vegeu la sortida de l'ordre servei docker lsque tots els serveis estiguin activats) inicialitzem el clúster Postgresql:

# 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

Comprovació de la preparació del clúster Postgresql:

# 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

Configurem traefik per obrir l'accés als contenidors des de l'exterior:

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

Llencem Redis Cluster, per fer-ho creem un directori d'emmagatzematge a tots els nodes:

# 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

Afegeix el registre Docker:

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

I finalment - 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

Estat final del clúster i dels serveis:

# 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

Què més es pot millorar? Assegureu-vos de configurar Traefik per executar contenidors sobre https, afegiu el xifratge tls per a Postgresql i Redis. Però, en general, ja es pot donar als desenvolupadors com a PoC. Vegem ara les alternatives a Docker.

Podman

Un altre motor força conegut per fer funcionar contenidors agrupats per beines (pods, grups de contenidors desplegats junts). A diferència de Docker, no requereix cap servei per executar contenidors; tot el treball es fa a través de la biblioteca libpod. També escrit a Go, requereix un temps d'execució compatible amb OCI per executar contenidors, com ara runC.

Docker i tot, tot, tot

Treballar amb Podman en general recorda això de Docker, fins al punt que podeu fer-ho així (com diuen molts que ho han provat, inclòs l'autor d'aquest article):

$ alias docker=podman

i pots continuar treballant. En general, la situació amb Podman és molt interessant, perquè si les primeres versions de Kubernetes funcionaven amb Docker, llavors cap al 2015, després de l'estandardització del món dels contenidors (OCI - Open Container Initiative) i la divisió de Docker en containerd i runC, s'ha desenvolupat una alternativa a Docker per executar-se a Kubernetes: CRI-O. Podman en aquest sentit és una alternativa a Docker, basada en els principis de Kubernetes, inclosa l'agrupació de contenidors, però l'objectiu principal del projecte és llançar contenidors a l'estil Docker sense serveis addicionals. Per raons òbvies, no hi ha mode eixam, ja que els desenvolupadors diuen clarament que si necessiteu un clúster, preneu Kubernetes.

Instal · lació

Per instal·lar-lo a Centos 7, només cal que activeu el repositori d'Extres i, a continuació, instal·leu-ho tot amb l'ordre:

# yum -y install podman

Altres característiques

Podman pot generar unitats per a systemd, resolent així el problema d'iniciar contenidors després d'un reinici del servidor. A més, es declara que systemd funciona correctament com a pid 1 al contenidor. Hi ha una eina de construcció separada per construir contenidors, també hi ha eines de tercers: anàlegs de docker-compose, que també generen fitxers de configuració compatibles amb Kubernetes, de manera que la transició de Podman a Kubernetes es simplifica al màxim.

Treballant amb Podman

Com que no hi ha mode eixam (se suposa que hem de canviar a Kubernetes si cal un clúster), el recollirem en contenidors separats.

Instal·leu podman-compose:

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

El fitxer de configuració resultant per a podman és lleugerament diferent, de manera que, per exemple, hem hagut de moure una secció de volums independent directament a la secció amb serveis.

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:

# 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

Vegem què genera per a systemd i kubernetes, per això hem d'esbrinar el nom o id del pod:

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

Sistema:

# 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

Malauradament, a part de llançar contenidors, la unitat generada per a systemd no fa res més (per exemple, netejar contenidors antics quan es reinicia aquest servei), de manera que haureu d'escriure aquestes coses vosaltres mateixos.

En principi, Podman n'hi ha prou per provar què són els contenidors, transferir configuracions antigues per a docker-compose i després avançar cap a Kubernetes, si necessiteu un clúster, o obtenir una alternativa més fàcil d'utilitzar a Docker.

rkt

Projecte va entrar als arxius fa uns sis mesos a causa del fet que RedHat el va comprar, així que no m'hi aprofundiré amb més detall. En general, va deixar una molt bona impressió, però en comparació amb Docker i sobretot Podman, sembla una combinació. També hi havia una distribució CoreOS construïda a sobre de rkt (encara que originalment tenien Docker), però això també va acabar en suport després de la compra de RedHat.

Flash

Més un projecte, l'autor del qual només volia construir i executar contenidors. A jutjar per la documentació i el codi, l'autor no va seguir els estàndards, sinó que simplement va decidir escriure la seva pròpia implementació, cosa que, en principi, va fer.

Troballes

La situació amb Kubernetes és molt interessant: d'una banda, amb Docker es pot construir un clúster (en mode eixam), amb el qual fins i tot es poden executar entorns de producte per a clients, això és especialment cert per a equips petits (3-5 persones). , o amb una petita càrrega general o manca de ganes d'entendre les complexitats de la configuració de Kubernetes, fins i tot per a càrregues elevades.

Podman no ofereix una compatibilitat total, però té un avantatge important: la compatibilitat amb Kubernetes, incloses eines addicionals (buildah i altres). Per tant, abordaré l'elecció d'una eina per treballar de la següent manera: per a equips petits o amb un pressupost limitat: Docker (amb un possible mode d'eixam), per desenvolupar-me en un host local personal: camarades Podman i per a tots els altres. - Kubernetes.

No estic segur que la situació amb Docker no canviarà en el futur, al cap i a la fi, són pioners, i també s'estan estandarditzant a poc a poc, però Podman, per totes les seves mancances (funciona només a Linux, sense clúster, muntatge i altres accions són solucions de tercers) el futur és més clar, així que convido a tothom a discutir aquestes troballes als comentaris.

PS El 3 d'agost estrenen “Videocurs de Docker", on podreu conèixer més sobre la seva obra. Analitzarem totes les seves eines: des d'abstraccions bàsiques fins a paràmetres de xarxa, matisos de treball amb diversos sistemes operatius i llenguatges de programació. Us familiaritzareu amb la tecnologia i entendreu on i la millor manera d'utilitzar Docker. També compartirem casos de bones pràctiques.

Preu de reserva abans del llançament: 5000 RUB. Podeu veure el programa del curs de vídeo Docker a la pàgina del curs.

Font: www.habr.com

Afegeix comentari