Как правильно делать deploy с помощью docker registry?

Здравствуйте,
Есть Rails-проект, который сейчас нужно поднять на серваке в связке nginx + puma, точнее я уже его поднял, все сделал используя докер. Отдельный контейнер для nginx и по контейнеру для staging и production-веток моего rails-приложения. База крутится на отдельном сервере и тоже в докер-контейнере. Все это было поднято и собрано в первый раз. т.е. это мой первый опыт работы с докером. Решил заняться настройкой деплоя, к этому моменту уже нахватался вершков и был уверен, что докер просто замечательно подходил для деплоя. В качестве первого шага наладил свой docker-registry, сделал в него push с dev-машины, а на хостинге из директории production, соответственно сделал pull. Тут реестр отработал не совсем так как я ожидал, я то думал, что он как git развернет всю структуру проекта внутри директории, в которой я делал пул, но он просто стянул image. "Ну и фиг с ним" подумал я, сделаю просто docker run, но почему-то стартанула dev-версия проекта.

Это было вкратце, теперь попробую сопроводить все вышеизложенное конфигами и командами. :)

Я использую docker-compose, т.к. мне не понравилось городить огромные docker-команды с кучей параметров, примерно так выглядит мой docker-compose.yml:
version: '2'
services:
  development:
    container_name: app_dev
    build: .
    expose:
      - "3000"
    network_mode: host
    environment:
      PORT: 3000
      RACK_ENV: development
      RAILS_ENV: development
      DATABASE_URL: 'localhost:27017'

  production:
    container_name: app_prod
    image: app_prod
    build:
      context: .
      dockerfile: Dockerfile-prod
    restart: always
    env_file: prod.env
    ports:
      - '8080:8080'
    volumes:
      - /puma
      - /puma/log
      - /puma/pids
  staging:
    ...

Параметр image использую для добавления образа в registry, если я все правильно понял, то это аналог тега (-t), типа:
docker build -t app_prod.

Поправьте меня, если это не так. Вообще пока я учил все это дело, последние недели две, кроме документации читал всякие тутриалы где советовали создавать docker-compose.yml под каждый env и запускать потом как-то так:
docker-compose -f docker-compose.prod.yml build/up

Мне показалось, это неразумным, поэтому я описал все env в одном конфиге, а потом удобно запускал то, что мне нужно:
docker-compose up production

По-моему красиво, все работало на локальной машине. На хосте тоже работало (до того как попытался работать с registry). С registry алгоритм моих действий был примерно таким:
  1. смотрю "docker ps", вижу работающий контейнер, у котрого name и image равны app_prod
  2. docker tag app_prod my_registry:5000/app_prod:latest
  3. docker push my_registry:5000/app_prod:latest - пуш проходит
  4. иду на сервер, захожу директорию подготовленную для продакшна
  5. docker pull my_registry:5000/app_prod:latest - пул успешно выполняется
  6. делаю ls - удивляюсь тому, что директория пуста (как писал выше, ожидал, что это будет похоже на git-pull)
  7. ни о каком удобном "docker-compose up production" и речи быть не может.
  8. пробую сделать "docker run app_prod" - проект стартует но в режиме dev, запустить продакшн у меня не получается.


Вот наверное полный список моего тупления в докер :). Помогите разобраться в своих ошибках, наставьте на путь истинный :). Официальная документация мне, что-то не заходит. Особенно не понятно как используя composer нормально сформировать образ и отослать его в regitry.

После вышеописанного опыта я малость переосмыслил алгоритм, но видимо все-равно не верно. Сейчас я ожидаю такую работу:
  1. когда я делаю "docker-compose up production", то запускаю контейнер app_prod
  2. потом командой "tag app_prod" создаю образ на основе контейнера
  3. а "push" уже реально отправляет образ в мой registry-сервер
  4. на сервере делаю "pull" и получаю образ, который можно стартануть (start), который не надо ни собирать и настраивать, что-то типа "скомпилированное", но блин "start" не работает, а "run" вроде как делает внутри себя "build + start", т.е. что-то пересобирает.


Голова уже никакая, каша одна. Спасите! :)
  • Вопрос задан
  • 6390 просмотров
Решения вопроса 1
Tyranron
@Tyranron
Официальную документацию надо все же читать, проявив усидчивость. Тогда не будет путаницы в базовых понятиях, из-за которой возникает каша в голове.

Давайте разберемся с инструментами и их предназначением, которые Вы используете:
  • Комманда docker - это консольный интерфейс (CLI) для работы с Docker
  • docker build создает по заданному Dockerfile образ контейнера
  • docker tag присваивает указанному образу указанный тег (опция -t для build делает то же самое)
  • docker pull скачивает указанный образ из удаленного регистра на текущую машину
  • docker push заливает указанный образ в удаленный регистр
  • docker run запускает новый контейнер из указанного образа
  • docker ps выводит список текущих "бегущих" контейнеров

Команда docker не жонглирует файлами, она жонглирует образами и контейнерами, а они от нас абстрагированы Docker'ом, как что-то эфемерное. То есть выполняя комманду docker pull Вы не скачиваете образ в ту папку, где выполняете команду, и уж точно не скачиваете какие-либо файлы. Все что Вы делаете этой командой - это скачиваете образ в локальное хранилище Docker'а, дабы Docker daemon мог запустить контейнер на основании этого образа.

Команда docker-compose - это уже совсем другая команда. Все что она делает - это читает указанный YAML-манифест и выполняет соответствующие команды Docker. Это всего лишь позволяет декларативно указывать желаемые сценарии при работе с Docker в удобном формате. Но ни команда docker, ни docker-compose, не предоставляют ничего для того, чтобы транспортировать/обновлять версии Ваших манифестов где-то там. Docker, опять таки, жонглирует образами и контейнерами, не более.

У Вас в манифесте указана директива build:. Таким образом docker-compose пытается сначала собрать контейнер, вместо того, чтобы просто запустить его из образа.

Касательно совета docker-compose.yml под каждый env, все правильно советовали. Именно так это и задумывалось. Вы в манифесте указываете отнюдь не разные окружения (development, production), а набор контейнеров, которые должны бежать в одной связке одной логической единицей (концепция POD'ов). Ничего не запрещает делать и так, как сделали Вы, но это сродни использованию дуршлага в роли миски для еды: сегодня Вы нормально из него наворачиваете пельмени, а завтра супчик в нем уже куда-то не туда утекает.

Воркфлоу в Вашем случае можно организовать следующим образом:
  1. На dev-машине у Вас один docker-compose.yml, согласно которому контейнер перед запуском собирается из сорцов.
  2. Когда у Вас готова новая версия приложения, Вы его собираете через docker build, присваиваете ему номер версии через docker tag и отправляете в удаленный регистр образов через docker push.
  3. На prod-машине у Вас отдельный docker-compose.ymlв котором указано запускать конкретную версию образа (и никаких build). Новая версия приложения - меняем тег образа в манифесте и перезапускаем контейнеры. При выполнении docker-compose образ автоматически скачается перед запуском, если его нет локально. Если же Вы обновили образ конкретной версии, которая уже была скачана ранее, то да, нужно выполнить docker pull перед стартом, дабы скачать новый образ.


Дополнительный совет:
Сборку образа и его заливку в удаленный регистр часто удобно автоматизировать с помощь Makefile'ов.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы