Docker e todo, todo, todo

TL;DR: Un artigo xeral: unha guía para comparar ambientes para executar aplicacións en contedores. Consideraranse as posibilidades de Docker e outros sistemas similares.

Docker e todo, todo, todo

Unha pequena historia de onde veu todo

Historia

A primeira forma coñecida de illar unha aplicación é chroot. A chamada ao sistema do mesmo nome proporciona un cambio ao directorio raíz, proporcionando así acceso ao programa que o chamou, acceso só aos ficheiros dentro deste directorio. Pero se o programa recibe dereitos de superusuario dentro, pode "escapar" do chroot e acceder ao sistema operativo principal. Ademais, ademais de cambiar o directorio raíz, outros recursos (RAM, procesador), así como o acceso á rede, non están limitados.

O seguinte xeito é lanzar un sistema operativo completo dentro do contedor, utilizando os mecanismos do núcleo do sistema operativo. Este método chámase de forma diferente en diferentes sistemas operativos, pero a esencia é a mesma: executar varios sistemas operativos independentes, cada un dos cales se executa no mesmo núcleo que executa o sistema operativo principal. Isto inclúe cárceres de FreeBSD, Solaris Zones, OpenVZ e LXC para Linux. O illamento ofrécese non só para o espazo en disco, senón tamén para outros recursos, en particular, cada contedor pode ter restricións sobre o tempo do procesador, a memoria RAM e o ancho de banda da rede. En comparación co chroot, deixar o contenedor é máis difícil, xa que o superusuario do contenedor só ten acceso ao interior do contenedor, non obstante, debido á necesidade de manter actualizado o sistema operativo dentro do contenedor e ó uso do núcleo antigo. versións (relevantes para Linux, en menor medida FreeBSD), hai unha probabilidade distinta de cero de romper o sistema de illamento do núcleo e acceder ao sistema operativo principal.

En lugar de lanzar un sistema operativo completo nun contedor (cun ​​sistema de inicialización, un xestor de paquetes, etc.), as aplicacións pódense lanzar inmediatamente, o principal é proporcionarlles esta oportunidade (a presenza das bibliotecas necesarias e outros ficheiros). Esta idea serviu de base para a virtualización de aplicacións en contedores, cuxo representante máis destacado e coñecido é Docker. En comparación cos sistemas anteriores, os mecanismos de illamento máis flexibles, xunto co soporte integrado para redes virtuais entre contedores e o estado das aplicacións dentro dun contedor, deron como resultado a capacidade de construír un único ambiente holístico a partir dun gran número de servidores físicos para executar contedores sen a necesidade dunha xestión manual dos recursos.

Estivador

Docker é o software de contenerización de aplicacións máis coñecido. Escrito na linguaxe Go, utiliza as capacidades habituais do núcleo de Linux: cgroups, espazos de nomes, capacidades, etc., así como sistemas de ficheiros Aufs e outros similares para aforrar espazo no disco.

Docker e todo, todo, todo
Fonte: wikimedia

Arquitectura

Antes da versión 1.11, Docker funcionaba como un servizo único que realizaba todas as operacións con contedores: descarga de imaxes para contedores, lanzamento de contedores, procesamento de solicitudes de API. Desde a versión 1.11, Docker dividiuse en varias partes que interactúan entre si: containerd, para xestionar todo o ciclo de vida dos contedores (asignación de espazo en disco, descarga de imaxes, conexión en rede, lanzamento, instalación e seguimento do estado dos contedores) e runC , tempos de execución de contedores, baseados no uso de cgroups e outras funcións do núcleo de Linux. O servizo docker en si permanece, pero agora só serve para procesar as solicitudes de API transmitidas a containerd.

Docker e todo, todo, todo

Instalación e configuración

A miña forma favorita de instalar docker é docker-machine, que, ademais de instalar e configurar directamente docker en servidores remotos (incluíndo varias nubes), permíteche traballar cos sistemas de ficheiros de servidores remotos e tamén pode executar varios comandos.

Non obstante, desde 2018, o proxecto apenas se desenvolveu, polo que instalarémolo do xeito habitual para a maioría das distribucións de Linux: engadindo un repositorio e instalando os paquetes necesarios.

Este método tamén se usa para a instalación automatizada, por exemplo, usando Ansible ou outros sistemas similares, pero non o vou considerar neste artigo.

A instalación realizarase en Centos 7, usarei unha máquina virtual como servidor, para instalala, só tes que executar os seguintes comandos:

# 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

Despois da instalación, cómpre iniciar o servizo, poñelo en carga automática:

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

Ademais, podes crear un grupo docker, cuxos usuarios poderán traballar con docker sen sudo, configurar o rexistro, habilitar o acceso á API desde o exterior, non esquezas axustar o firewall (todo o que non está permitido é prohibido nos exemplos anteriores e abaixo - omitiino por simplicidade e visualización), pero non entrarei en máis detalles aquí.

Outras características

Ademais da máquina docker anterior, tamén hai un rexistro docker, unha ferramenta para almacenar imaxes para contedores, así como docker compose - unha ferramenta para automatizar a implantación de aplicacións en contedores, os ficheiros YAML úsanse para construír e configurar contedores e outras cousas relacionadas (por exemplo, redes, sistemas de ficheiros persistentes para almacenar datos).

Tamén se pode usar para organizar canalizacións para CICD. Outra característica interesante é traballar en modo cluster, o chamado modo swarm (antes da versión 1.12 coñecíase como docker swarm), que permite montar unha única infraestrutura desde varios servidores para executar contedores. Hai soporte para unha rede virtual encima de todos os servidores, hai un equilibrador de carga integrado, así como soporte para segredos para contedores.

Os ficheiros YAML de docker compose pódense usar para tales clústeres con pequenas modificacións, automatizando totalmente o mantemento de clústeres pequenos e medianos para varios fins. Para clústeres grandes, é preferible Kubernetes porque os custos de mantemento do modo enxame poden superar os de Kubernetes. Ademais de runC, como ambiente de execución para contedores, podes instalar, por exemplo Recipientes Kata

Traballando con Docker

Despois da instalación e configuración, tentaremos construír un clúster no que implementaremos GitLab e Docker Registry para o equipo de desenvolvemento. Como servidores, usarei tres máquinas virtuais, nas que implementarei adicionalmente o FS distribuído GlusterFS, empregareino como almacenamento de volumes docker, por exemplo, para executar unha versión a prueba de fallos do rexistro docker. Compoñentes clave para executar: Docker Registry, Postgresql, Redis, GitLab con soporte para GitLab Runner enriba de Swarm. Postgresql lanzarase con clustering Estolon, polo que non precisa usar GlusterFS para almacenar datos de Postgresql. O resto dos datos críticos almacenaranse en GlusterFS.

Para implementar GlusterFS en todos os servidores (denomínanse node1, node2, node3), cómpre instalar paquetes, habilitar o firewall, crear os directorios necesarios:

# 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

Despois da instalación, o traballo na configuración de GlusterFS debe continuar desde un nodo, por exemplo nodo1:

# 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ón, cómpre montar o volume resultante (o comando debe executarse en todos os servidores):

# mount /srv/docker

O modo Swarm está configurado nun dos servidores, que será Leader, o resto terá que unirse ao clúster, polo que o resultado de executar o comando no primeiro servidor terá que ser copiado e executado no resto.

Configuración inicial do clúster, executo o comando no 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

Copie o resultado do segundo comando, execute no node2 e node3:

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

Deste xeito complétase a configuración preliminar dos servidores, imos comezar a configurar os servizos, os comandos a executar lanzaranse desde o nodo1, salvo que se especifique o contrario.

Primeiro de todo, imos crear redes para contedores:

# 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

Despois marcamos os servidores, isto é necesario para vincular algúns servizos aos servidores:

# 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ón, creamos directorios para almacenar datos etcd, o almacenamento KV que necesitan Traefik e Stolon. Do mesmo xeito que Postgresql, estes serán contedores ligados a servidores, polo que executamos este comando en todos os servidores:

# mkdir -p /srv/etcd

A continuación, cree un ficheiro para configurar etcd e aplícao:

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

Despois dun tempo, comprobamos que o clúster etcd aumentou:

# 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

Cree directorios para Postgresql, execute o comando en todos os servidores:

# mkdir -p /srv/pgsql

A continuación, cree un ficheiro para 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

Xeramos segredos, aplicamos o ficheiro:

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

Algún tempo despois (mira a saída do comando servizo docker lsque todos os servizos aumentaron) inicialice o 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

Comprobando a preparación do 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

Configuramos traefik para abrir o acceso aos contedores desde o 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

Iniciamos Redis Cluster, para iso creamos un directorio de almacenamento en todos os nodos:

# 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

Engadir Rexistro Docker:

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

E finalmente - 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

O estado final do clúster e dos servizos:

# 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

Que máis se pode mellorar? Asegúrate de configurar Traefik para que funcione con contedores https, engade o cifrado tls para Postgresql e Redis. Pero, en xeral, xa podes darllo aos desenvolvedores como PoC. Vexamos agora alternativas a Docker.

podman

Outro motor bastante coñecido para facer funcionar contedores agrupados por vainas (vainas, grupos de contedores despregados xuntos). A diferenza de Docker, non require ningún servizo para executar contedores, todo o traballo realízase a través da biblioteca libpod. Tamén escrito en Go, necesita un tempo de execución compatible con OCI para executar contedores como runC.

Docker e todo, todo, todo

Traballar con Podman en xeral aseméllase ao de Docker, na medida en que podes facelo así (reivindicado por moitos que o probaron, incluído o autor deste artigo):

$ alias docker=podman

e podes seguir traballando. En xeral, a situación con Podman é moi interesante, porque se as primeiras versións de Kubernetes funcionaban con Docker, entón desde aproximadamente 2015, despois de estandarizar o mundo dos contedores (OCI - Open Container Initiative) e dividir Docker en containerd e runC, unha alternativa a Docker estase a desenvolver para funcionar en Kubernetes: CRI-O. A este respecto, Podman é unha alternativa a Docker, construída sobre os principios de Kubernetes, incluíndo a agrupación de contedores, pero o obxectivo principal do proxecto é executar contedores ao estilo Docker sen servizos adicionais. Por razóns obvias, non hai modo enxame, xa que os desenvolvedores din claramente que se necesitas un clúster, toma Kubernetes.

Instalación

Para instalar en Centos 7, só tes que activar o repositorio de Extras e, a continuación, instala todo co comando:

# yum -y install podman

Outras características

Podman pode xerar unidades para systemd, resolvendo así o problema de iniciar os contedores despois do reinicio do servidor. Ademais, declárase que systemd funciona correctamente como pid 1 no contedor. Para construír contedores, hai unha ferramenta de compilación separada, tamén hai ferramentas de terceiros: análogos de docker-compose, que tamén xera ficheiros de configuración compatibles con Kubernetes, polo que a transición de Podman a Kubernetes é o máis sinxela posible.

Traballando con Podman

Dado que non hai modo de enxame (suponse que debe cambiar a Kubernetes se é necesario un clúster), ensamblarémolo en contedores separados.

Instalar podman-compose:

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

O ficheiro de configuración resultante para podman é lixeiramente diferente, xa que, por exemplo, tivemos que mover unha sección de volumes separada directamente á sección de servizos.

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

Resultado do traballo:

# 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

Vexamos que xerará para systemd e kubernetes, para iso necesitamos descubrir o nome ou id do 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: {}

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

Desafortunadamente, ademais do lanzamento de contedores, a unidade xerada para systemd non fai outra cousa (por exemplo, limpar contedores antigos cando se reinicia ese servizo), polo que terás que engadir tales cousas ti mesmo.

En principio, Podman é suficiente para probar cales son os contedores, transferir configuracións antigas para docker-compose e despois ir cara a Kubernetes, se é necesario, nun clúster ou obter unha alternativa máis fácil de usar a Docker.

rkt

Proxecto ido ao arquivo hai uns seis meses debido ao feito de que o comprou RedHat, polo que non vou determe con máis detalle. En xeral, deixou unha moi boa impresión, pero en comparación con Docker, e máis aínda con Podman, parece unha cosechadora. Tamén había unha distribución CoreOS construída sobre rkt (aínda que orixinalmente tiñan Docker), pero que tamén rematou despois da compra de RedHat.

Flash

Máis un proxecto, cuxo autor só quería construír e executar contedores. A xulgar pola documentación e o código, o autor non seguiu os estándares, senón que simplemente decidiu escribir a súa propia implementación, o que, en principio, fixo.

Descubrimentos

A situación con Kubernetes é moi interesante: por unha banda, con Docker, pódese montar un clúster (en modo enxame), co que incluso se poden executar contornas de produción para clientes, isto é especialmente certo para equipos pequenos (3-5 persoas). ), ou cunha pequena carga xeral , ou a falta de ganas de comprender as complejidades de configurar Kubernetes, incluso para cargas elevadas.

Podman non ofrece compatibilidade total, pero ten unha vantaxe importante: a compatibilidade con Kubernetes, incluíndo ferramentas adicionais (buildah e outras). Polo tanto, abordarei a elección dunha ferramenta para traballar do seguinte xeito: para equipos pequenos ou cun orzamento limitado - Docker (cun ​​posible modo de enxame), para desenvolver por min mesmo nun host local persoal - compañeiros de Podman e para todos os demais. - Kubernetes.

Non estou seguro de que a situación con Docker non cambie no futuro, ao fin e ao cabo, son pioneiros, e tamén se van estandarizando pouco a pouco paso a paso, pero Podman, con todas as súas deficiencias (funciona só en Linux, sen clustering, montaxe). e outras accións son decisións de terceiros) o futuro está máis claro, polo que invito a todos a comentar estes descubrimentos nos comentarios.

PS O 3 de agosto lanzamos "Curso de video dockeronde podes coñecer máis sobre o seu traballo. Analizaremos todas as súas ferramentas: desde abstraccións básicas ata parámetros de rede, matices de traballo con diversos sistemas operativos e linguaxes de programación. Coñecerás a tecnoloxía e entenderás onde e como usar mellor Docker. Tamén compartiremos casos de mellores prácticas.

Custo da reserva antes do lanzamento: 5000 rublos. Pódese atopar o programa "Docker Video Course". na páxina do curso.

Fonte: www.habr.com

Engadir un comentario