Docker для Symfony 4 — от локалки до production

в 13:06, , рубрики: docker, docker for symfony, docker stack, docker swarm, docker для php, docker для Symfony 4, docker-compose, nginx, php, postgresql, symfony, Symfony 4, как поднять docker

Предистория

Одним прекрасным днём мне понадобилось развернуть среду разработки для своего проекта. Vagrant уже порядком поднадоел и хотелось иметь единую среду разработки для всех участников проекта которая была бы идентичной production серверу. Соответственно наслушавшись информации про хипстерский docker, я решил начать с ним разбираться. Далее я постараюсь максимально подробно описать все шаги начиная от установки докера на локалке вплоть до разворачивания продуктива на KVM.

Исходный стек технологий:

— Docker
— Symfony 4
— nginx
— php-fpm
— postgresql
— elasticsearch
— rabbitmq
— jenkins

Железо:

— ноутбук под ОС Ubuntu 16.04
— продакшн сервер на хостинге KVM

Почему кроме технологического стека я перечислил ещё и стек железа?

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

Первый и наверно самый важный аспект при начале работы с докером — это операционная система вашего ноутбука. Проще всего работать с докером именно на linux системах. Если вы работаете на Windows или Mac то у вас 100 % будут некоторые сложности, но эти сложности не будут являться критическими и при желании «нагуглить» как это исправляется не составит никаких проблем.

Второй вопрос — это хостинг. Зачем нужен Hosting именно с типом виртуализации KVM? Причина в том, что виртуализация VPS разительно отличается от KVM и установить сам docker на VPS у вас попросту не выйдет, так как VPS распределяет ресурсы сервера динамически.

Подитог: для самого быстрого старта на докере резоннее всего выбирать Ubuntu в качестве локальной операционки и KVM хостинг (либо собственный сервер). Далее рассказ пойдёт опираясь именно на эти две составляющие.

Docker-compose для локалки

Установка

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

curl -sSl https://get.docker.com/ | sh

Эта команда установит и docker и docker-compose. После этого проверить версию докера можно командой:

docker --version

Я все это дело запускаю на докере версии 18.06.0-ce.

Установка закончена!

Осознание

Чтобы с чем-то работать более менее успешно — необходимо иметь представление как оно работает. Если вы ранее работали только с Vagrant или чем-либо похожим, то будет крайне непривычно и непонятно поначалу, но это лишь поначалу.

Я постараюсь провести аналогию к Vagrant. Сейчас многие могут сказать, что сравнивать Vagrant и Docker коренным образом не верно. Да, я с этим согласен, но я их сравнивать и не собираюсь, я лишь попытаюсь донести до новичков, работавших только с Vagrant, систему работы Docker, апеллируя тем, что знают новички.

Мое видение контейнера «на пальцах» представляется так: каждый контейнер — это крохотный изолированный мирок. Каждый контейнер можно представить как будто бы это крохотный Vagrant на котором установлен всего 1 инструмент, например nginx или php. Изначально контейнеры изолированы вообще от всего вокруг, но путём не хитрых манипуляций можно настроить все так, чтобы они общались между собой и работали совместно. Это не значит, что каждый из контейнеров — это отдельная виртуальная машина, совсем нет. Но так проще для первоначального понимания, как мне кажется.

Vagrant просто откусывает у вашего компьютера часть ресурсов, создает виртуальную машину, устанавливает на нее операционную систему, устанавливает библиотеки, устанавливает все то, что вы прописали в скрипте после vagrant up. В конечном итоге это выглядит примерно так:

Посмотреть схемку

Docker в свою очередь работает кардинально иначе. Он не создает виртуальных машин. Docker создает контейнеры (можете воспринимать пока что их как микро-виртуалки) со своей операционной системой Alpine и 1-3 библиотеками, которые необходимы для работы приложения, например php или nginx. При этом Docker не блокирует под себя ресурсы вашей системы, а просто использует их по мере необходимости. В конечном итоге, если проиллюстрировать, то это выглядеть будет примерно так:

Посмотреть схемку

Каждый из контейнеров имеет образ из которого он создаётся. Подавляющая часть образов представляет из себя расширение другого образа, например Ubuntu xenial или Alpine или Debian, на которые сверху накатываются дополнительные драйверы и другие компоненты.

Мой первый образ был для php-fpm. Мой образ расширяет официальный образ php:7.2-fpm-alpine3.6. То есть по сути он берет официальный образ и доставляет на него нужные мне компоненты, например, pdo_pgsql, imagick, zip и прочее. Таким образом можно создать образ, который нужен именно вам. Если есть желание можете пользоваться тут.

С созданием образов все довольно просто на мой взгляд, если они сделаны на базе xenial например, но доставляют немного геморроя, если они сделаны на базе Alpine. До начала работы с докером я про Alpine в принципе и не слышал, так как Vagrant у меня всегда работал под Ubuntu xenial. Alpine представляет из себя пустую Linux операционную систему, в которой по сути вообще нет ничего (крайний минимум). Поэтому поначалу работать с ней крайне не удобно, так как нет например того же apt-get install (к которому так привыкаешь), а есть только apk add и не вполне вменяемый набор пакетов. Большой плюс Alpine заключается в его весе, например, если Xenial весит (абстрактно) 500мешков, то Alpine (абстрактно) порядка 78мешков. На что же это вообще влияет? А влияет это на скорость сборки и на конечный вес всех образов, которые будут храниться у вас на сервере в конечном итоге. Допустим, у вас 5 разных контейнеров и все на базе xenial суммарный их вес будет более 2,5 гигов, а alpine — порядка 500 мешков всего лишь. Поэтому в идеале надо стремиться к тому, чтобы контейнеры были как можно более худые. (Полезная ссылка для установки пакетов в Alpine — Пакеты Alpine).

На docker hub везде пишут как запускать контейнер используя команду docker run, и при этом почему-то не пишут как его можно запустить через docker-compose, а ведь именно через docker-compose он и будет запускаться большую часть времени, так как мало кому охота вручную запускать все контейнеры, сетки, порты открывать и прочее. Docker-compose от лица пользователя выглядит просто файл yaml с настройками. Он включает в себя описание каждого из сервисов, которые необходимо запустить. Моя сборка для локального окружения выглядит следующим образом:

version: '3.1'

services:

  php-fpm:
    image: otezvikentiy/php7.2-fpm:0.0.11
    ports:
      - '9000:9000'
    volumes:
      - ../:/app
    working_dir: /app
    container_name: 'php-fpm'

  nginx:
    image: nginx:1.15.0
    container_name: 'nginx'
    working_dir: /app
    ports:
      - '7777:80'
    volumes:
      - ../:/app
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf

  postgres:
    image: postgres:9.6
    ports:
      - '5432:5432'
    container_name: 'postgresql'
    working_dir: /app
    restart: always
    environment:
      POSTGRES_DB: 'db_name'
      POSTGRES_USER: 'db_user'
      POSTGRES_PASSWORD: 'db_pass'
    volumes:
      - ./data/dump:/app/dump
      - ./data/postgresql:/var/lib/postgresql/data

  rabbitmq:
    image: rabbitmq:3.7.5-management
    working_dir: /app
    hostname: rabbit-mq
    container_name: 'rabbit-mq'
    ports:
      - '15672:15672'
      - '5672:5672'
    environment:
      RABBITMQ_DEFAULT_USER: user
      RABBITMQ_DEFAULT_PASS: password
      RABBITMQ_DEFAULT_VHOST: my_vhost

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.3.0
    container_name: 'elastic-search'
    environment:
      - discovery.type=single-node
      - "discovery.zen.ping.unicast.hosts=elasticsearch"
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - 9200:9200
      - 9300:9300
    working_dir: /app
    volumes:
      - ../:/app
      - ./data/elasticsearch:/usr/share/elasticsearch/data

volumes:
  elasticsearch:
  postgresql:

docker-compose.yaml для SF4 представляет из себя определенный набор сервисов: nginx, php-fpm, postgresql, rabbitmq (если он вам нужен), elasticsearch (если он вам нужен). Для локального окружения этого хватит. Чтобы все это заработало — есть минимальный набор настроек, без которых ничего работать не будет. Чаще всего это image, volumes, ports, environment, working_dir и container_name. Все для запуска того или иного образа описано в его документации на hub.docker.com. Там не всегда есть описание для docker-compose, но это не значит, что оно с ним не работает. Просто необходимо перенести все входящие данные из команды docker run в docker-compose и все заработает.

Например, есть образ для RabbitMQ тут. Когда видишь ЭТО впервые — это вызывает смешанные чувства и эмоции, но не все так страшно. В Этом образе указаны тэги. Обычно тэги — представляют собой разные образы, разных версий приложения с различными расширяемыми образами. Например, тэг 3.7.7-alpine означает, что этот образ более тонкий, нежели чем, например, 3.7.7, так как он сделан на базе Alpine. Ну и так же в тэгах указываются чаще всего версии самого приложения. Я обычно выбираю наиболее свежую версию и стабильную версию самого приложения и образ alpine.

После того как вы изучили и выбрали тэг — далее зачастую вы видите что-то подобного рода:

docker run -d --hostname my-rabbit --name some-rabbit -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password rabbitmq:3-management

И первая мысль — WTF? Как перебросить это в docker-compose?

Все довольно не сложно. По факту, в этой строке указываются все те же параметры, что и в yaml файле, только сокращенные. Например, -e — это environment, в который передаются различные параметры, могут быть так же записи типа -p — это порты, которые в yaml называются ports. Соответственно, чтобы качественно использовать незнакомый образ, надо просто «загуглить» сокращения docker run команды и применять полные наименования в yaml файле.

Теперь вернемся в docker-compose.yml, который я привел в виде образца выше.

В данном примере используется мой образ для php7.2 сделанный как расширение для официального образа php7.2-fpm-alpine, но если вам не требуется такого количества дополнительных библиотек — то вы можете собрать своё расширение для официального образа и использовать его. Остальные образы для локалки у меня используются полностью оригинальные и официальные.

image — указываем какой образ скачать. Например (rabbitmq:3.7.7-management-alpine).

ports — указываем порты, которые будет использовать контейнер (см. документацию образа). Пример порт nginx это 80 по дефолту. Соответственно, если вы хотите использовать порт 80, то здесь необходимо указать 80:80 и ваш сайт будет доступен на localhost. Либо можно указать 7777:80, и тогда ваш сайт будет по url localhost:7777. Это необходимо бывает для того, чтобы несколько проектов можно было разворачивать на одном и том же хосте.

volumes — здесь указываются расшаренные директории. Например ваш проект лежит в директории ~/projects/my-sf4-app, а контейнер php настроен на работу с директорией /app (то же самое, что в варианте /var/www/my-sf4-app). Соответственно было бы удобно, чтобы контейнер имел доступ к проекту. Соответственно в volumes мы прописываем ~/projects/my-sf4-app:/app (см. этот пример в docker-compose.yml выше (у меня это указано относительным путем ../:/app)).

Таким образом для контейнера будет расшарена папка и он сможет выполнять в ней различные действия типа php bin/console doctrine:migrations:migrate. Так же эти директории удобно использовать для того, чтобы сохранять данные приложений. Например postgresql можно указать директорию для хранения данных БД и тогда при пересоздании контейнера не нужно будет накатывать дамп или фикстуры.

working_dir — указывается рабочая директория контейнера. В данном случае /app (или по аналогии с вагрантом /var/www/my-sf4-app).

environment — сюда передаются все переменные для контейнера. Например для rabbitmq передаются имя пользователя и пароль, для postgresql передаётся имя базы, имя пользователя, пароль.

container_name — не обязательное поле, однако я предпочитаю указывать, для удобства подсоединения к контейнерам. Если не указать, то будут присвоены имена по дефолту с хэшами.

Это основные параметры, которые необходимо указывать. Остальные могут быть по желанию для дополнительных настроек, либо по документации к контейнеру.

Теперь, чтобы все это запустить необходимо провести команду docker-compose up -d в директории, где расположен файл docker-compose.

Как и где все это хранить для локалки?

Для локалки я использую папку docker в корне проекта.

Docker для Symfony 4 — от локалки до production - 1

В ней находится папка data в которой я храню всю информацию postgresql и elasticsearch, чтобы при пересоздании проекта не приходилось накатывать фикстуры с нуля. Так же есть папочка nginx в которой я храню конфиг для локального nginx контейнера. Эти папки я синхронизирую в docker-compose.yml с соответствующими файлами и папками в контейнерах. Так же на мой взгляд очень удобно писать bash скрипты для работы с докером. Например, start.sh скрипт запускает контейнеры, потом проводит composer install, чистит кэш и проводит миграции. Для коллег по проекту это так же удобно, им не приходится что-либо делать, они просто запускают скрипт и все работает.

Пример скрипта start.sh


#!/usr/bin/env bash
green=$(tput setf 2)
toend=$(tput hpa $(tput cols))$(tput cub 6)

echo -n 'Как к вам обращаться?: '
read name
echo "Привет тебе $name! Мы начинаем старт докера для проекта tutmesto.ru"
echo -n "$name, ты хочешь использовать дамп для БД? (y/n): "
read use_dump
echo 'Сейчас мы запустим сборку докера!'
docker-compose up -d || exit
echo -en 'n'
echo -n "Докер успешно собрался! ${green}${toend}[OK]"
echo -en 'n'
echo 'Теперь нам необходимо собрать композер.'
./composer-install.sh
echo -en 'n'
echo -n "Композер успешно собрался ${green}${toend}[OK]"
echo -en 'n'
echo 'Сейчас надо будет заснуть на 40 секунд, чтобы успела развернуться postgres-ка'
sleep 5
echo 'Осталось еще 35 секунд...'
sleep 5
echo 'Осталось еще 30 секунд...'
sleep 5
echo 'Осталось еще 25 секунд...'
sleep 5
echo 'Осталось еще 20 секунд...'
sleep 5
echo 'Осталось еще 15 секунд...'
sleep 5
echo 'Осталось еще 10 секунд...'
sleep 5
echo 'Осталось еще 5 секунд...'
sleep 5
echo 'Сон завершился. По идее postgres-ка уже поднялась и сейчас мы будем закачивать дамп!'

case "$use_dump" in
    y|Y) ./dump.sh
         echo -en 'n'
         echo -n "Дамп успешно закачался! ${green}${toend}[OK]"
         echo -en 'n'
    ;;
    *) echo "$name, хорошо, обойдемся без дампа! =)"
    ;;
esac
echo 'Теперь нам надо провести миграции!'
./migrations-migrate.sh
echo -en 'n'
echo -n "Миграции успешно проведены! ${green}${toend}[OK]"
echo -en 'n'
echo 'Теперь почистим кэш!'
./php-fpm-command.sh rm -rf var/cache/*
./php-fpm-command.sh chmod 777 var/ -R
./cache-clear.sh
echo -en 'n'
echo -n "Кэш успешно очищен! ${green}${toend}[OK]"
echo -en 'n'
echo 'Теперь скопируем настройки для локалки!'
./env.sh
echo -en 'n'
echo -n "Настройки для локалки скопированы! ${green}${toend}[OK]"
echo -en 'n'
echo "Теперь, $name, ты можешь пользоваться локалкой! Открой в браузере localhost:7777 и наслаждайся!"
echo -en 'n'
echo "------------------------------------------------------------------------------"
echo -en 'n'
echo "ОСНОВНЫЕ КОМАНДЫ КОТОРЫЕ МОЖНО ИСПОЛЬЗОВАТЬ:"
echo "./cache-clear.sh                            |Очистка кэша symfony 4"
echo "./composer.sh [command(ex. install)]        |Обращение к композеру"
echo "./composer-install.sh                       |Запуск composer install"
echo "./connect-to-php-fpm.sh                     |Подключение к консоли php"
echo "./console.sh [command(ex. cache:clear)]     |Запуск команды php bin/console"
echo "./destroy.sh                                |Жесткое сворачивание локалки. Убивает все кроме образов."
echo "./dump.sh                                   |Закачать дамп, который находится в корне (dump.sql)"
echo "./env.sh                                    |Скопировать настройки для локалки"
echo "./migrations-migrate.sh                     |Провести миграции"
echo "./php-fpm-command.sh [command(ex. php -m)]  |Выполнить команду в php-fpm контейнере"
echo "./start.sh                                  |Запуск локалки (этот скрипт)"
echo "./stop.sh                                   |Gracefull shutdown локалки"
echo -en 'n'
echo "ДЛЯ УДОБНОГО ПОЛЬЗОВАНИЯ В ДАМПЕ БЫЛИ СОЗДАНЫ СЛЕДУЮЩИЕ ПОЛЬЗОВАТЕЛИ:"
echo "client@c.cc    | QWEasd123"
echo "admin@a.aa     | QWEasd123"
echo "moderator@m.mm | QWEasd123"
echo -en 'n'
echo "------------------------------------------------------------------------------"
echo -en 'n'
echo -en 'n'
echo 'OtezVikentiy brain corporation!'
echo -en 'n'
echo -en 'n'

Пример скрипта php-fpm-command.sh


#!/usr/bin/env bash

cd "`dirname "$0"`" && 
 docker-compose exec -T "php-fpm" sh -c "cd /app && $*"

Пример скрипта connect-to-php-fpm.sh


#!/usr/bin/env bash
docker exec -i -t --privileged php-fpm bash

Локальная среда разработки на этом заканчивается. Поздравляю, можете поделиться с коллегами готовым результатом! )

Продуктив

Подготовка

Допустим, вы уже что-то написали на локалке и хотите выложить это на production сервер или на тестовый сервер. У вас есть хостинг на KVM виртуализации или свой сервер в соседней комнате с кондиционером.

Чтобы развернуть продуктив или бету — на сервере должна быть операционная система (в идеале linux) и установленный docker. Docker можно установить точно так же, как и на локалку, отличий никаких.

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

Docker для продуктива использует docker swarm и docker stack. Если прям на пальцах, то эта система отличается лишь другими командами и тем, что docker swarm представляет из себя распределитель нагрузок для кластера (опять же немного абстрактно, но так будет проще для понимания).

P.S.: советую потренироваться настраивать docker swarm на Vagrant (как бы ни парадоксально это звучало). Простой рецепт для тренировки — поднимаете пустой Vagrant с той же операционной системой, что и в продуктиве и настраиваете его для начала.

Чтобы настроить docker swarm — необходимо просто выполнить несколько команд:


docker swarm init --advertise-addr 192.168.***.** (ip-адрес вашего сервера)
mkdir /app (в случае если ваш докер настроен на работу с директорией app)
chown docker /app (ну или раздать права на директорию)
docker stack deploy -c docker-compose.yml my-first-sf4-docker-app

Рассмотрим теперь все это немного подробнее.

docker swarm init --advertise-addr — оно запускает непосредственно сам docker swarm и шарит ссылку, чтобы вы могли бы подцепить к этому «рою» еще какой-то другой сервер, чтобы они работали в кластере.
mkdir /app && chown .. — необходимо создать заранее все необходимые директории для работы докера, чтобы во время сборки он не жаловался бы на отсутствие директорий.
docker stack deploy -c docker-compose.yml my-first-sf4-docker-app — эта команда запускает сборку самого вашего приложения, аналог docker-compose up -d только для docker swarm.

Чтобы началась какая-либо сборка, вам необходим все тот же docker-compose.yaml, но уже немного измененный именно под продуктив/бету.

version: '3.1'

services:

  php-fpm:
    image: otezvikentiy/php7.2-fpm:0.0.11
    ports:
      - '9000:9000'
    networks:
      - my-test-network
    depends_on:
      - postgres
      - rabbitmq
    volumes:
      - /app:/app
    working_dir: /app
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]

  nginx:
    image: nginx:1.15.0
    networks:
      - my-test-network
    working_dir: /app
    ports:
      - '80:80'
    depends_on:
      - php-fpm
    volumes:
      - /app:/app
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]

  postgres:
    image: postgres:9.6
    ports:
      - '5432:5432'
    working_dir: /app
    networks:
      - my-test-network
    secrets:
      - postgres_db
      - postgres_user
      - postgres_pass
    environment:
      POSTGRES_DB_FILE: /run/secrets/postgres_db
      POSTGRES_USER_FILE: /run/secrets/postgres_user
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_pass
    volumes:
      - ./data/dump:/app/dump
      - ./data/postgresql:/var/lib/postgresql/data
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]

  rabbitmq:
    image: rabbitmq:3.7.5-management
    networks:
      - my-test-network
    working_dir: /app
    hostname: my-test-sf4-app-rabbit-mq
    volumes:
      - /app:/app
    ports:
      - '5672:5672'
      - '15672:15672'
    secrets:
      - rabbitmq_default_user
      - rabbitmq_default_pass
      - rabbitmq_default_vhost
    environment:
      RABBITMQ_DEFAULT_USER_FILE: /run/secrets/rabbitmq_default_user
      RABBITMQ_DEFAULT_PASS_FILE: /run/secrets/rabbitmq_default_pass
      RABBITMQ_DEFAULT_VHOST_FILE: /run/secrets/rabbitmq_default_vhost
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.3.0
    networks:
      - my-test-network
    depends_on:
      - postgres
    environment:
      - discovery.type=single-node
      - discovery.zen.ping.unicast.hosts=elasticsearch
      - bootstrap.memory_lock=true
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    ports:
      - 9200:9200
      - 9300:9300
    working_dir: /app
    volumes:
      - /app:/app
      - ./data/elasticsearch:/usr/share/elasticsearch/data
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]

  jenkins:
    image: otezvikentiy/jenkins:0.0.2
    networks:
      - my-test-network
    ports:
      - '8080:8080'
      - '50000:50000'
    volumes:
      - /app:/app
      - ./data/jenkins:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]

volumes:
  elasticsearch:
  postgresql:
  jenkins:

networks:
  my-test-network:

secrets:
  rabbitmq_default_user:
    file: ./secrets/rabbitmq_default_user
  rabbitmq_default_pass:
    file: ./secrets/rabbitmq_default_pass
  rabbitmq_default_vhost:
    file: ./secrets/rabbitmq_default_vhost
  postgres_db:
    file: ./secrets/postgres_db
  postgres_user:
    file: ./secrets/postgres_user
  postgres_pass:
    file: ./secrets/postgres_pass

Как вы можете видеть — файл с настройками для продуктива немного отличается от файла для локалки. В нем добавились secrets, deploy и networks.

secrets — файлы для хранения ключей. Ключи создаются довольно просто. Вы создаете файл с названием ключа — внутрь пишете значение. После этого в docker-compose.yml вы указываете раздел secrets и в него передаете весь список файлов с ключами. Подробнее.
networks — это созадется некая внутренняя сетка, через которую общаются между собой контейнеры. На локалке — это делается автоматически, но на продуктиве — это необходимо немного сделать вручную. Плюс ко всему, можно указывать дополнительные настройки кроме дефолтных. Подробнее.
deploy — это основное отличие локалки от продуктива/беты.

    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]

Минимальный набор бойца:

replicas — указываете количество реплик, которое необходимо запускать (по сути это используется в случае, если у вас кластер и вы используете распределитель нагрузок от докера). Например, у вас есть два сервера и вы их соединили через docker swarm. Указывая здесь цифру 2, например, 1 инстанс у вас будет создан на 1 сервере, а второй на втором сервере. Таким образом, нагрузка на сервера будет разделяться напополам.
restart_policy — политика автоматического «переподнятия» контейнера в случае, если он по какой-то причине упал.
placement — расположение инстанса контейнера. Например бывает случаи, когда вы хотите, чтобы все инстансы контейнера крутились именно на 1 из 5 серверов, а не распределялись между ними.

Хочу почитать документацию!

Итак, мы немного подразобрались с тем, что отличает docker-compose.yaml для локалки от версии для продуктива/беты. Теперь давайте попробуем запустить это дело.

Допустим, вы тренируетесь на Vagrant'е и в корне сервера у вас лежит уже настроенный файл для продуктива docker-compose.yml


sudo apt-get update
sudo apt-get -y upgrade

sudo apt-get install -y language-pack-en-base

export LC_ALL=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8

curl -sSl https://get.docker.com/ | sh
sudo usermod -aG docker ubuntu

sudo apt-get install git

sudo docker swarm init --advertise-addr 192.168.128.77

sudo mkdir /app
sudo chmod 777 /app -R

docker stack deploy -c /docker-compose.yml my-app

git clone git@bitbucket.org:JohnDoe/my-app.git /app

docker stack ps my-app
docker stack ls
docker stack services my-app

P.S.: не пинайте за sudo и 777, естественно на продуктиве так делать не стоит. Это лишь для скорости обучения.

Итак, нас более всего интересуют строки связанные с докером.
Сначала мы инициализируем «рой» (docker swarm).
Потом создаем директории, необходимые для работы.
Скачиваем репу с нашим кодом на SF4 в директорию /app.
После этого идут три команды: ps, ls и services.

Каждая из них по-своему полезна. Я чаще всего использую ps, так как она отображает состояние контейнеров и часть ошибки, в случае, если таковая имеется.

Допустим, у нас поднялись контейнеры, но какой-то из них постоянно падает с ошибкой и в docker stack ps my-app вы видите кучу перезапусков. Чтобы увидеть причину падения необходимо выполнить docker container ps -a — и там будет отображаться контейнер, который постоянно падает. Их будет много инстансов одного и того же контейнера, например my-app_php-fpm.1.*какой-либо лютый hash*.

Соответственно, теперь, зная имя контейнера — выполняем docker logs my-app_php-fpm.1.*какой-либо лютый hash* и просматриваем логи. Исправляем ошибку и перезапускаем ВСЁ. Чтобы грохнуть все контейнеры можно сделать так:

docker stack rm my-app

После этого у вас будет чистый swarm без каких-либо контейнеров. Исправляете ошибку — и снова docker stack deploy -c docker-compose.yml my-app.

Автор: otezvikentiy

Источник

  1. Евгений:

    Добрый день пытаюсь разобраться с настройками для прода.
    У меня связка nginx+php-fpm+mysql+nodejs. И у меня все контейнеры выдают ошибки кроме php. Если вводить docker compose ps -a то запущены только php и node, при этом mysql и nginx вообще не запускаются, можете что-нибудь посоветовать? у node в docker-compose.yml стоит depends_on: mysql.

* - обязательные к заполнению поля