Русский
Русский
English
Статистика
Реклама

Docker и все, все, все

TL;DR: обзорная статья-руководство по сравнению сред для запуска приложений в контейнерах. Будут рассмотрены возможности Docker и других схожих систем.



Немножко истории, откуда все взялось

История


Первым общеизвестным способом изоляции приложения является chroot. Одноименный системный вызов обеспечивает изменение корневого каталога таким образом обеспечивая доступ программе, его вызвавшей, доступ только к файлам внутри этого каталога. Но если программе внутри дать права суперпользователя, потенциально она может убежать из chroot и получить доступ к основной операционной системе. Также кроме смены корневого каталога не ограничиваются другие ресурсы (оперативная память, процессор), а также доступ к сети.


Следующий способ запуск полноценной операционной системы внутри контейнера, за счет механизмов ядра операционной системы. В различных операционных системах этот способ называют по-разному, но суть одна и та же запуск нескольких независимых операционных систем, каждая из которых работает с тем же ядром, на котором работает и основная операционная система. Сюда относятся FreeBSD Jails, Solaris Zones, OpenVZ и LXC для Linux. Обеспечивается изоляция не только по дисковому пространству, но и другим ресурсам, в частности каждый контейнер может иметь ограничения по процессорному времени, оперативной памяти, полосе пропускания сети. По сравнению с chroot выйти из контейнера сложнее, поскольку суперпользователь в контейнере обладает доступом только к начинке контейнера, однако из-за необходимости поддерживать операционную систему внутри контейнера в актуальном состоянии и использования старых версий ядер (актуально для Linux, в меньшей мере FreeBSD) есть ненулевая вероятность пробития системы изоляции ядра и получения доступа к основной операционной системе.


Вместо запуска полноценной операционной системы в контейнере (с системой инициализации, пакетным менеджером и т.п.) можно запускать сразу же приложения, главное обеспечить приложениям такую возможность (наличие необходимых библиотек и прочих файлов). Эта идея и послужила основой для контейнерной виртуализации приложений, наиболее ярким и общеизвестным представителем которой является Docker. По сравнению с предыдущими системами более гибкие механизмы изоляции совместно с встроенной поддержкой виртуальных сетей между контейнерами и с отслеживанием состояния приложения внутри контейнера дали в результате возможность построения единой целостной среды из большого числа физических серверов для запуска контейнеров без необходимости ручного управления ресурсами.


Docker


Docker это наиболее известное ПО для контейнеризации приложений. Написан на языке Go, использует штатные возможности ядра Linux cgroups, namespaces, capabilities и т.п., а также файловые системы Aufs и другие подобные для экономии дискового пространства.



Источник: wikimedia


Архитектура


До версии 1.11 Docker работал в виде единого сервиса, который осуществлял все операции с контейнерами: скачивание образов для контейнеров, запуск контейнеров, обработку запросов по API. Начиная с версии 1.11 Docker разбили на несколько частей, взаимодействующих между собой: containerd, для обработки всего жизненного цикла контейнеров (выделение дискового пространства, скачивание образов, работа с сетью, запуск, установка и наблюдение за состоянием контейнеров) и runC, среды исполнения контейнеров, основанной на использовании cgroups и прочих возможностей ядра Linux. Сам сервис docker остался, но теперь он служит только для обработки запросов по API, транслируемых в containerd.



Установка и настройка


Моим любимым способом установки docker является docker-machine, который кроме непосредственно установки и настройки docker на удаленные сервера (включая различные облака) дает возможность работы с файловыми системами удаленных серверов, а также может производить запуск различных команд.


Однако с 2018 года проект почти не развивается, поэтому установку будем производить штатным для большинства дистрибутивов Linux способом добавлением репозитория и установкой необходимых пакетов.


Также этот способ применяется и при автоматизированной установке, например с помощью Ansible или других подобных систем, но в этой статье я его рассматривать не буду.


Установка будет производиться на Centos 7, в качестве сервера я буду использовать виртуальную машину, для установки достаточно выполнить команды ниже:


# 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

После установки надо запустить сервис, поставить его в автозагрузку:


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

Дополнительно можно создать группу docker, пользователи которой смогут работать с docker без sudo, настроить журналирование, включить доступ к API извне, но тут я более подробно не буду останавливаться.


Другие возможности


Кроме вышеназванной docker machine еще есть docker registry, средство для хранения образов для контейнеров, а также docker compose средство для автоматизации развертывания приложений в контейнерах, используются файлы YAML для сборки и настройки контейнеров и прочих связанных вещей (например сети, постоянные файловые системы для хранения данных).


Также с его помощью можно организовать конвейеры для CI\CD. Другая интересная возможность работа в кластерном режиме, так называемый swarm mode (до версии 1.12 был известен как docker swarm), позволяющая собрать из нескольких серверов единую инфраструктуру для запуска контейнеров. Имеется поддержка виртуальной сети поверх всех серверов, есть наличие встроенного балансировщика нагрузки, а также поддержка секретов для контейнеров.


Файлы YAML от docker compose с небольшими изменениями могут быть использованы для таких кластеров, полностью автоматизируя обслуживание малых и средних кластеров для различных целей. Для больших кластеров предпочтительнее использовать Kubernetes, поскольку затраты на обслуживание swarm mode могут превзойти таковые с Kubernetes. Кроме runC в качестве среды исполнения контейнеров можно установить например Kata containers


Работа с Docker


После установки и настройки попробуем собрать кластер, в котором развернем GitLab и Docker Registry для команды разработчиков. В качестве серверов я буду использовать три виртуальные машины, на которых дополнительно разверну распределенную ФС GlusterFS, ее я буду использовать в качестве хранилища docker volumes, например для запуска отказоустройчивой версии docker registry. Ключевые компоненты для запуска: Docker Registry, Postgresql, Redis, GitLab с поддержкой GitLab Runner поверх Swarm. Postgresql будем запускать с кластеризацией Stolon, поэтому для хранения данных Postgresql не надо использовать GlusterFS. Остальные критические данные будут храниться на GlusterFS.


Для разворачивания GlusterFS на всех серверах (они именуются node1, node2, node3) нужно установить пакеты, разрешить работу firewall, создать нужные каталоги:


# 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

После установки работу по настройке GlusterFS надо продолжать с одного узла, например 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

Затем надо смонтировать полученный volume (команду нужно выполнить на всех серверах):


# mount /srv/docker

Настройка swarm mode производится на одном из серверов, который будет Leader, остальные должны будут присоединяться к кластеру, поэтому результат выполнения команды на первом сервере надо будет скопировать и выполнить на остальных.


Первичная настройка кластера, команду запускаю на node1:


# docker swarm initSwarm 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:2377To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.# docker swarm join-token manager

Копируем результат второй команды, выполняем на node2 и node3:


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

На этом предварительная настройка серверов окончена, приступаем к настройке сервисов, команды для выполнения будут запускаться с node1, если не указано иначе.


Первым делом создадим сети для контейнеров:


# 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

Затем помечаем сервера, это нужно для привязки некоторых сервисов к серверам:


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

Далее создаем каталоги для хранения данных etcd, KV-хранилища, которое нужно для Traefik и Stolon. Аналогично Postgresql это будут привязанные к серверам контейнеры, поэтому эту команду выполняем на всех серверах:


# mkdir -p /srv/etcd

Далее создаем файл для настройки etcd и применяем его:


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

Через некоторое время проверяем, что поднялся etcd кластер:


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

Создаем каталоги для Postgresql, команду выполняем на всех серверах:


# mkdir -p /srv/pgsql

Далее создаем файл для настройки 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: rollbackvolumes:  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

Генерируем секреты, применяем файл:


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

Спустя некоторое время (смотрим вывод команды docker service ls, что поднялись все сервисы) инициализируем кластер 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

Проверяем готовность кластера 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      LEADER26baa11d    false74e98768    falsea8cb002b    true=== Active proxies ===ID4d2338269f562f3bb0c79ff1=== Keepers ===UID     HEALTHY PG LISTENADDRESS    PG HEALTHY  PG WANTEDGENERATION PG CURRENTGENERATIONpgkeeper1   true    pgkeeper1:5432         true     2           2pgkeeper2   true    pgkeeper2:5432          true            2                   2pgkeeper3   true    pgkeeper3:5432          true            3                   3=== Cluster Info ===Master Keeper: pgkeeper3===== Keepers/DB tree =====pgkeeper3 (master)pgkeeper2pgkeeper1

Настраиваем traefik для открытия доступа к контейнерам извне:


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=traefiknetworks:  traefik:    external: true

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

Запускаем Redis Cluster, для этого создаем на всех узлах каталог для хранения:


# 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: anyvolumes:  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 Registry:


06registry.yml
version: '3.7'services:  registry:    image: registry:2.6    networks:      - traefik    volumes:      - registry_data:/var/lib/registry    deploy:      replicas: 1      placement:        constraints: [node.role == manager]      restart_policy:        condition: on-failure      labels:        - traefik.enable=true        - traefik.http.routers.registry.rule=Host(`registry.example.com`)        - traefik.http.services.registry.loadbalancer.server.port=5000        - traefik.docker.network=traefikvolumes:  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

Ну и наконец 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'        gitlab_rails['redis_port'] = '6379'        gitlab_rails['redis_password'] = 'xxxxxxxxxxx'        gitlab_rails['smtp_enable'] = true        gitlab_rails['smtp_address'] = "smtp.yandex.ru"        gitlab_rails['smtp_port'] = 465        gitlab_rails['smtp_user_name'] = "noreply@example.com"        gitlab_rails['smtp_password'] = "xxxxxxxxx"        gitlab_rails['smtp_domain'] = "example.com"        gitlab_rails['gitlab_email_from'] = 'noreply@example.com'        gitlab_rails['smtp_authentication'] = "login"        gitlab_rails['smtp_tls'] = true        gitlab_rails['smtp_enable_starttls_auto'] = true        gitlab_rails['smtp_openssl_verify_mode'] = 'peer'        external_url 'http://gitlab.example.com/'        gitlab_rails['gitlab_shell_ssh_port'] = 22222    volumes:      - gitlab_conf:/etc/gitlab      - gitlab_logs:/var/log/gitlab      - gitlab_data:/var/opt/gitlab    deploy:      mode: replicated      replicas: 1      placement:        constraints:        - node.role == manager      labels:        - traefik.enable=true        - traefik.http.routers.gitlab.rule=Host(`gitlab.example.com`)        - traefik.http.services.gitlab.loadbalancer.server.port=80        - traefik.docker.network=traefik  gitlab-runner:    image: gitlab/gitlab-runner:latest    networks:      - gitlab    volumes:      - gitlab_runner_conf:/etc/gitlab      - /var/run/docker.sock:/var/run/docker.sock    deploy:      mode: replicated      replicas: 1      placement:        constraints:        - node.role == managervolumes:  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

Итоговое состояние кластера и сервисов:


# docker service lsID                  NAME                   MODE                REPLICAS            IMAGE                          PORTSlef9n3m92buq        etcd_etcd1             replicated          1/1                 quay.io/coreos/etcd:latestij6uyyo792x5        etcd_etcd2             replicated          1/1                 quay.io/coreos/etcd:latestfqttqpjgp6pp        etcd_etcd3             replicated          1/1                 quay.io/coreos/etcd:latesthq5iyga28w33        gitlab_gitlab          replicated          1/1                 gitlab/gitlab-ce:latest        *:22222->22/tcpdt7s6vs0q4qc        gitlab_gitlab-runner   replicated          1/1                 gitlab/gitlab-runner:latestk7uoezno0h9n        pgsql_pgkeeper1        replicated          1/1                 sorintlab/stolon:master-pg10cnrwul4r4nse        pgsql_pgkeeper2        replicated          1/1                 sorintlab/stolon:master-pg10frflfnpty7tr        pgsql_pgkeeper3        replicated          1/1                 sorintlab/stolon:master-pg10x7pqqchi52kq        pgsql_pgsentinel       replicated          3/3                 sorintlab/stolon:master-pg10mwu2wl8fti4r        pgsql_postgresql       replicated          3/3                 sorintlab/stolon:master-pg109hkbe2vksbzb        redis_redis-master     global              3/3                 bitnami/redis:latest           *:6379->6379/tcpl88zn8cla7dc        redis_redis-replica    replicated          3/3                 bitnami/redis:latest           *:30003->6379/tcp1utp309xfmsy        redis_redis-sentinel   global              3/3                 bitnami/redis:latest           *:30002->16379/tcpoteb824ylhyp        registry_registry      replicated          1/1                 registry:2.6qovrah8nzzu8        traefik_traefik        replicated          3/3                 traefik:latest                 *:80->80/tcp, *:443->443/tcp

Что еще можно улучшить? Обязательно настроить в Traefik работу контейнеров по https, добавить шифрование tls для Postgresql и Redis. Но в целом уже можно отдавать разработчикам в качестве PoC. Посмотрим теперь альтернативы Docker.


Podman


Еще один достаточно известный engine для запуска контейнеров, сгруппированные по подам (pods, группы контейнеров, развернутых совместно). В отличие от Docker не требует какого-либо сервиса для запуска контейнеров, вся работа производится через библиотеку libpod. Также написан на Go, нуждается в OCI-совместимом runtime для запуска контейнеров, например runC.



Работа с Podman в целом напоминает таковую для Docker, вплоть до того, что можно сделать так (заявлено у многих попробовавших, в том числи и автором этой статьи):


$ alias docker=podman

и можно продолжать работать. В целом ситуация с Podman весьма интересная, ведь если ранние версии Kubernetes работали с Docker, то примерно с 2015 года, после стандартизации мира контейнеров (OCI Open Container Initiative) и выделения из Docker на containerd и runC, развивается альтернатива Docker для запуска в Kubernetes: CRI-O. Podman в этом плане является альтернативой Docker, построенной по принципам Kubernetes, в том числе и по группировке контейнеров, но основная цель существования проекта запуск контейнеров в стиле Docker без дополнительных сервисов. По понятным причинам нет наличия swarm mode, так как разработчики явно говорят о том, что если надо кластер берите Kubernetes.


Установка


Для установки в Centos 7 достаточно активировать репозиторий Extras, после чего установить все командой:


# yum -y install podman

Другие возможности


Podman может генерировать юниты для systemd, таким образом решая задачу запуска контейнеров после перезагрузки сервера. Дополнительно заявлена корректная работа systemd в качестве pid 1 в контейнере. Для сборки контейнеров идет отдельный инструмент buildah, есть также сторонние инструменты аналоги docker-compose, генерирующий в том числе конфигурационные файлы, совместимые с Kubernetes, так что переход с Podman на Kubernetes упрощен насколько это возможно.


Работа с Podman


Поскольку нет swarm mode (предполагается переход на Kubernetes, если надо кластер) собирать будем отдельными контейнерами.


Устанавливаем podman-compose:


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

Результирующий конфигурационный файл для podman немного отличается, так к примеру пришлось перенести отдельную секцию volumes напрямую в секцию с сервисами.


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:      - gitlabnetworks:  gitlab:

# podman-compose -f gitlab-runner.yml -d up

Результат работы:


# podman psCONTAINER ID  IMAGE                                  COMMAND               CREATED             STATUS                 PORTS                                      NAMESda53da946c01  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_1781c0103c94a  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

Давайте посмотрим, что он сгенерирует для systemd и kubernetes, для этого надо узнать имя или id пода:


# podman pod lsPOD ID         NAME   STATUS    CREATED          # OF CONTAINERS   INFRA ID71fc2b2a5c63   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.4apiVersion: v1kind: Podmetadata:  creationTimestamp: "2020-07-29T19:22:40Z"  labels:    app: root  name: rootspec:  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-confstatus: {}

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.serviceDocumentation=man:podman-generate-systemd(1)Requires=container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.serviceBefore=container-781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3.service container-da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864.service[Service]Restart=on-failureExecStart=/usr/bin/podman start db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaaExecStop=/usr/bin/podman stop -t 10 db40ab8bf84bf35141159c26cb6e256b889c7a98c0418eee3c4aa683c14fccaaKillMode=noneType=forkingPIDFile=/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.serviceDocumentation=man:podman-generate-systemd(1)RefuseManualStart=yesRefuseManualStop=yesBindsTo=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.serviceAfter=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service[Service]Restart=on-failureExecStart=/usr/bin/podman start da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864ExecStop=/usr/bin/podman stop -t 10 da53da946c01449f500aa5296d9ea6376f751948b17ca164df438b7df6607864KillMode=noneType=forkingPIDFile=/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.serviceDocumentation=man:podman-generate-systemd(1)RefuseManualStart=yesRefuseManualStop=yesBindsTo=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.serviceAfter=pod-71fc2b2a5c6346f0c1c86a2dc45dbe78fa192ea02aac001eb8347ccb8c043c26.service[Service]Restart=on-failureExecStart=/usr/bin/podman start 781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3ExecStop=/usr/bin/podman stop -t 10 781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3KillMode=noneType=forkingPIDFile=/var/run/containers/storage/overlay-containers/781c0103c94aaa113c17c58d05ddabf8df4bf39707b664abcf17ed2ceff467d3/userdata/conmon.pid[Install]WantedBy=multi-user.target

К сожалению кроме запуска контейнеров сгенерированный юнит для systemd больше ничего не делает (например зачистку старых контейнеров при перезапуске такого сервиса), поэтому такие вещи придется дописывать самостоятельно.


В принципе Podman достаточно для того, чтобы попробовать, что такое контейнеры, перенести старые конфигурации для docker-compose, после чего уйти в сторону Kubernetes, если надо на кластер, либо получить более простую в работе альтернативу Docker.


rkt


Проект ушел в архив примерно полгода назад из-за того, что его купил RedHat, поэтому не буду останавливаться на нем детальнее. В целом он оставлял весьма неплохое впечатление, однако по сравнению с Docker и тем более с Podman выглядит комбайном. Существовал также дистрибутив CoreOS, построенный на основе rkt (хотя у них изначально был Docker), однако его поддержка также окончилась после покупки RedHat.


Plash


Еще один проект, автор которого хотел просто собирать и запускать контейнеры. Судя по документации и коду автор не следовал стандартам, а просто решил написать свою реализацию, что в принципе и сделал.


Выводы


Ситуация при наличии Kubernetes складывается весьма интересная: с одной стороны с Docker можно собрать кластер (в swarm mode), с которым даже можно запускать продуктовые среды для клиентов, это особенно актуально для небольших команд (3-5 человек), либо при небольшой общей нагрузке, или же отсутствию желания разбираться в тонкостях настройки Kubernetes в том числе и для высоких нагрузок.


Podman не обеспечивает полной совместимости, но у него есть одно важное преимущество совместимость с Kubernetes, в том числе и по дополнительным инструментам (buildah и прочие). Поэтому к выбору инструмента для работы я буду подходить так: для малых команд, либо при ограниченном бюджете Docker (с возможным swarm mode), для разработки для себя на личном localhost Podman сотоварищи, а всем остальным Kubernetes.


Я не уверен, что ситуация с Docker не поменяется в будущем, все-таки они являются пионерами, а также шаг за шагом потихоньку стандартизируются, но у Podman при всех его недостатках (работа только на Linux, нету кластеризации, сборка и прочие действия сторонними решениями) будущее более ясное, поэтому я приглашаю всех желающих обсудить данные выводы в комментариях.


P.S. 3 августа запускаем Видеокурс по Docker, где можно будет подробнее узнать о его работе. Мы разберем все его инструменты: от основных абстракций до параметров сети, нюансов работы с различными ОС и языками программирования. Вы познакомитесь с технологией и поймете, где и как лучше использовать Docker. Также поделимся best practice кейсами.


Стоимость предзаказа до релиза: 5000 р. С программой Видеокурса по Docker можно ознакомиться на странице курса.

Источник: habr.com
К списку статей
Опубликовано: 31.07.2020 04:19:23
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании southbridge

Devops

Kubernetes

Серверное администрирование

Системное администрирование

Docker

Podman

Rkt

Plash

Видеокурс по docker

Образование

Учебный курс

Docker и ci/cd

Best practice по docker

Категории

Последние комментарии

  • Имя: Murshin
    13.06.2024 | 14:01
    Нейросеть-это мозг вселенной.Если к ней подключиться,то можно получить все знания,накопленные Вселенной,но этому препятствуют аннуннаки.Аннуннаки нас от неё отгородили,установив в головах барьер. Подр Подробнее..
  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru