Как запустить команду один раз в Docker compose

Итак, я работаю над файлом компоновки докеров, чтобы развернуть мой веб-сервер Go. Мой сервер использует mongo, поэтому я добавил контейнер объема данных и службу mongo в docker compose. Затем я написал файл Docker для создания моего проекта Go и, наконец, запустил его.

Однако есть еще один шаг, который нужно сделать. Как только мой проект был скомпилирован, мне нужно запустить следующую команду: ./my-project -setup

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

Итак, как я могу это достичь? Даже если я перезапущу сервер, а затем снова запустив docker-compose up, я не хочу, чтобы эта команда выполнялась снова.

Я думаю, что мне не хватает понимания Docker, потому что я фактически не понимаю все о контейнерах объема данных (они просто остановили контейнеры, которые монтируют том?). Кроме того, если я перезапущу сервер, а затем запустите docker-compose up, какие команды будут запущены? Будет ли он запускать тот же контейнер, который был остановлен с данным CMD?

В любом случае, вот мой docker-compose.yml:

version: '2'
services:
  mongodata:
    image: mongo:latest
    volumes:
      - /data/db
    command: --break-mongo
  mongo:
    image: mongo:latest
    volumes_from:
      - mongodata
    ports:
      - "28001:27017"
    command: --smallfiles --rest --auth
  my_project:
    build: .
    ports:
      - "6060:8080"
    depends_on:
      - mongo
      - mongodata
    links:
      - mongo

И вот мой файл Dockerfile для создания моего изображения проекта:

FROM golang

ADD . /go/src/my_project
RUN cd /go/src/my_project && go get
RUN go install my_project
RUN my_project -setup
ENTRYPOINT /go/bin/my_project

EXPOSE 8080

Ответ 1

Я предлагаю добавить в ваш контейнер точку входа script; в этой точке входа script вы можете проверить, была ли инициализирована база данных, а если нет, выполните требуемые шаги.

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

В качестве примера рассмотрим официальное изображение WordPress, которое выполняет однократную инициализацию базы данных в этой точке входа - script. script пытается подключиться к базе данных (и повторяет попытку, если с базой данных не удается связаться (пока)), и проверяет, нужна ли инициализация; https://github.com/docker-library/wordpress/blob/df190dc9c5752fd09317d836bd2bdcd09ee379a5/apache/docker-entrypoint.sh#L146-L171

Примечание

Я заметил, что вы создали "контейнер только для данных" для присоединения своего тома. Начиная с докера 1.9, докер имеет управление томами, включая объемы именования. Из-за этого вам больше не нужно использовать контейнеры только для данных.

Вы можете удалить контейнер данных только из своего файла компоновки и изменить свою службу mongo, чтобы выглядеть примерно так:

  mongo:
    image: mongo:latest
    volumes:
      - mongodata:/data/db
    ports:
      - "28001:27017"
    command: --smallfiles --rest --auth

Это должно создать новый том с именем mongodata, если он не существует, или повторно использовать существующий том с этим именем. Вы можете перечислить все тома с помощью docker volume ls и удалить том с помощью docker volume rm <some-volume>, если он вам больше не нужен

Ответ 2

Для вашего приложения требуется некоторое начальное состояние для работы. Это означает, что вы должны:

  • Проверьте, существует ли требуемое состояние
  • Зависит от состояния инициализации первого шага или нет

Вы можете написать программу для проверки текущего состояния базы данных (здесь я буду использовать bash script, но это может быть любая другая языковая программа):

RUN if $(./check.sh); then my_project -setup; fi

В моем случае, если script вернет 0 (статус выхода успеха), тогда будет вызываться команда setup.

Ответ 3

Вы можете попытаться использовать ONBUILD инструкцию:

Команда ONBUILD добавляет к изображению триггерную инструкцию, которая будет выполнена позднее, когда изображение используется в качестве базы для другой сборки. Триггер будет выполнен в контексте нисходящей сборки, как если бы он был вставлен сразу после инструкции FROM в нисходящем потоке Dockerfile.

Любая инструкция сборки может быть зарегистрирована как триггер.

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

Например, если ваше изображение является многоразовым конструктором приложений Python, для его применения в конкретном каталоге потребуется исходный код приложения, и после этого может потребоваться вызывать конструкцию script. Вы не можете просто вызвать ADD и RUN сейчас, потому что у вас еще нет доступа к исходному коду приложения, и это будет отличаться для каждой сборки приложения. Вы можете просто предоставить разработчикам приложений шаблонную табличку Dockerfile для копирования-вставки в свое приложение, но это неэффективно, подвержено ошибкам и сложно обновить, поскольку оно смешивается с кодом приложения.

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

Вот как это работает:

  • Когда он встречает команду ONBUILD, строитель добавляет триггер к метаданным создаваемого образа. Эта инструкция не влияет на текущую сборку.
  • В конце сборки список всех триггеров хранится в манифесте изображения под ключом ONBUILD. Они могут быть проверены командой docker inspect.
  • Позже изображение можно использовать в качестве основы для новой сборки, используя инструкцию FROM. Как часть обработки инструкции FROM, построитель нисходящего потока ищет триггеры ONBUILD и выполняет их в том же порядке, в котором они были зарегистрированы. Если какой-либо из триггеров терпит неудачу, команда FROM прерывается, что, в свою очередь, приводит к сбою сборки. Если все триггеры преуспевают, команда FROM завершается, и сборка продолжается, как обычно.
  • Триггеры очищаются от окончательного изображения после выполнения. Другими словами, они не унаследованы сборниками "grand-children".