Используя Docker-Compose, как выполнить несколько команд

500

Я хочу сделать что-то вроде этого, где я могу запустить несколько команд по порядку.

db:
  image: postgres
web:
  build: .
  command: python manage.py migrate
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - .:/code
  ports:
    - "8000:8000"
  links:
    - db
RustyShackleford
источник

Ответы:

861

Разобрался, используй bash -c.

Пример:

command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"

Тот же пример в мультилинии:

command: >
    bash -c "python manage.py migrate
    && python manage.py runserver 0.0.0.0:8000"

Или:

command: bash -c "
    python manage.py migrate
    && python manage.py runserver 0.0.0.0:8000
  "
RustyShackleford
источник
6
@Pedram Убедитесь, что вы используете образ, на котором установлен bash. Некоторые изображения могут также требовать прямого пути к bash, например/bin/bash
codemaven
6
Если bash не установлен, вы можете попробовать sh -c "ваша команда"
Chaoste
Убедитесь, что вы заключили свои команды в кавычки при переходе к bash, и мне пришлось добавить «сон 5», чтобы убедиться, что БД не работает, но это сработало для меня.
Традиция
74
На альпийских изображениях на самом деле, кажется, нет установленного bash - делайте, как @Chaoste рекомендует и используйте shвместо этого:[sh, -c, "cd /usr/src/app && npm start"]
Florian Loch
1
Можно также использовать только ashна альпийском :)
Джонатан
160

Я запускаю перед запуском такие вещи, как миграция, в отдельном временном контейнере, например, так (обратите внимание, файл compose должен иметь тип версии '2'):

db:
  image: postgres
web:
  image: app
  command: python manage.py runserver 0.0.0.0:8000
  volumes:
    - .:/code
  ports:
    - "8000:8000"
  links:
    - db
  depends_on:
    - migration
migration:
  build: .
  image: app
  command: python manage.py migrate
  volumes:
    - .:/code
  links:
    - db
  depends_on:
    - db

Это помогает держать вещи в чистоте и отдельно. Две вещи для рассмотрения:

  1. Вы должны убедиться в правильности последовательности запуска (используя зависимость_)

  2. Вы хотите избежать нескольких сборок, что достигается с помощью первого тега, используя build и image; Вы можете обратиться к изображению в других контейнерах, то

Бьерн Стил
источник
2
Это кажется лучшим вариантом для меня, и я хотел бы использовать его. Можете ли вы уточнить настройки тегов, чтобы избежать нескольких сборок? Я предпочел бы избежать дополнительных шагов, поэтому, если это нужно, я мог бы пойти с bash -cвыше.
Ставрос Корокитакис
3
В приведенном выше примере сборка и тегирование выполняются в разделе миграции. На первый взгляд, это неочевидно, но docker-compose помечает его при указании свойств сборки AND и изображения, в результате чего свойство image определяет тег для этой сборки. Затем его можно использовать впоследствии, не вызывая новую сборку (если вы посмотрите на веб, вы увидите, что у него нет сборки, а есть только свойство изображения). Вот еще некоторые подробности docs.docker.com/compose/compose-file )
Бьорн Стил
26
В то время как мне нравится идея этого, проблема в том, что зависимость_ обеспечивает только то, что они запускаются в этом порядке, а не то, что они готовы в этом порядке. wait-for-it.sh может быть решением, в котором нуждаются некоторые люди.
Традиция
2
Это абсолютно правильно, и немного стыдно, что docker-compose не поддерживает какой-либо мелкозернистый элемент управления, например, ожидание выхода контейнера или начало прослушивания порта. Но да, пользовательский скрипт решает эту проблему, хороший момент!
Бьорн Стил
1
Этот ответ дает неверную и потенциально разрушительную информацию о том, как работают зависимости.
антагонестам
96

Я рекомендую использовать shвместо, bashпотому что он более доступен на большинстве изображений на основе Unix (альпийский, и т. Д.).

Вот пример docker-compose.yml:

version: '3'

services:
  app:
    build:
      context: .
    command: >
      sh -c "python manage.py wait_for_db &&
             python manage.py migrate &&
             python manage.py runserver 0.0.0.0:8000"

Это вызовет следующие команды по порядку:

  • python manage.py wait_for_db - ждать, пока БД будет готов
  • python manage.py migrate - запустить любые миграции
  • python manage.py runserver 0.0.0.0:8000 - запустить мой сервер разработки
LondonAppDev
источник
2
Лично это мое любимое и самое чистое решение.
BugHunterUK
1
Мой тоже. Как указывает @LondonAppDev, bash не доступен по умолчанию во всех контейнерах для оптимизации в пространстве (например, большинство контейнеров построено на базе Alpine Linux)
ewilan
2
Мне пришлось убежать от мультилинии && с \
Андре Ван Зюдамом
@AndreVanZuydam Хм, это странно, мне не нужно было этого делать. Вы окружали цитаты? Какой вид докера ты используешь?
LondonAppDev
2
@oligofren >используется для запуска многострочного ввода (см. stackoverflow.com/a/3790497/2220370 )
LondonAppDev,
40

Это работает для меня:

version: '3.1'
services:
  db:
    image: postgres
  web:
    build: .
    command:
      - /bin/bash
      - -c
      - |
        python manage.py migrate
        python manage.py runserver 0.0.0.0:8000

    volumes:
      - .:/code
    ports:
      - "8000:8000"
    links:
      - db

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

    command:
      - /bin/bash
      - -c
      - |
        var=$$(echo 'foo')
        echo $$var # prints foo

... в противном случае вы получите ошибку:

Неверный формат интерполяции для опции «команда» в сервисе «сеть»:

MatrixManAtYrService
источник
Привет помошник. Я столкнулся с проблемой: `` `нераспознанные аргументы: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows` `` команда в моем docker-compose.yml это: команда: - / bin / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Client} -T $ {Типы} Вы знаете, как это исправить? Я добавляю Client и Types в мой файл .env.
Ньют
Вот документ для вас: docs.docker.com/compose/compose-file/#variable-substitution Я думаю, что ваш файл .env помещает эти переменные в контейнерную среду, а docker-compose ищет в вашей оболочке , Попробуй вместо этого $${Types}и $${Client}. Я думаю, что это не позволит docker compose интерпретировать эти переменные и искать их значения в любой оболочке, из которой вы вызываете docker-compose, что будет означать, что bash все еще собирается разыменовать их ( после того, как docker обработал ваш .envфайл).
MatrixManAtYrService
Спасибо за ваш комментарий. Я сделал то, что вы сказали на самом деле. Итак, я получил $ (клиент) в информации об ошибке. Я изменил способ чтения переменных окружения для использования os.getenv в python, что проще. Спасибо, в любом случае.
Ньют
23

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

Файл сценария make shell может иметь вид docker-entrypoint.sh(имя не имеет значения) со следующим содержимым.

#!/bin/bash
python manage.py migrate
exec "$@"

в файле docker-compose.yml используйте его entrypoint: /docker-entrypoint.shи зарегистрируйте команду как command: python manage.py runserver 0.0.0.0:8000 PS: не забудьте скопировать docker-entrypoint.shвместе с вашим кодом.

Харшад Йола
источник
Обратите внимание, что это также будет выполнено, когда вы это сделаетеdocker-compose run service-name ....
thisismydesign
18

Еще одна идея:

Если, как и в этом случае, вы создаете контейнер, просто поместите в него скрипт запуска и запустите его с помощью команды. Или смонтируйте скрипт запуска как том.

rweng
источник
Да, в конце я создал скрипт run.sh: #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(некрасиво oneline)
fero
9

* ОБНОВИТЬ *

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

докер-compose.yaml:

version: '3'

# Can be used as an alternative to VBox/Vagrant
services:

  mongo:
    container_name: mongo
    image: mongo
    build:
      context: .
      dockerfile: deploy/local/Dockerfile.mongo
    ports:
      - "27017:27017"
    volumes:
      - ../.data/mongodb:/data/db

Dockerfile.mongo:

FROM mongo:3.2.12

RUN mkdir -p /fixtures

COPY ./fixtures /fixtures

RUN (mongod --fork --syslog && \
     mongoimport --db wcm-local --collection clients --file /fixtures/clients.json && \
     mongoimport --db wcm-local --collection configs --file /fixtures/configs.json && \
     mongoimport --db wcm-local --collection content --file /fixtures/content.json && \
     mongoimport --db wcm-local --collection licenses --file /fixtures/licenses.json && \
     mongoimport --db wcm-local --collection lists --file /fixtures/lists.json && \
     mongoimport --db wcm-local --collection properties --file /fixtures/properties.json && \
     mongoimport --db wcm-local --collection videos --file /fixtures/videos.json)

Это, наверное, самый чистый способ сделать это.

* СТАРЫЙ ПУТЬ *

Я создал сценарий оболочки с моими командами. В этом случае я хотел запустить mongodи запустить, mongoimportно вызов mongodблокирует запуск остальных.

docker-compose.yaml :

version: '3'

services:
  mongo:
    container_name: mongo
    image: mongo:3.2.12
    ports:
      - "27017:27017"
    volumes:
      - ./fixtures:/fixtures
      - ./deploy:/deploy
      - ../.data/mongodb:/data/db
    command: sh /deploy/local/start_mongod.sh

start_mongod.sh :

mongod --fork --syslog && \
mongoimport --db wcm-local --collection clients --file /fixtures/clients.json && \
mongoimport --db wcm-local --collection configs --file /fixtures/configs.json && \
mongoimport --db wcm-local --collection content --file /fixtures/content.json && \
mongoimport --db wcm-local --collection licenses --file /fixtures/licenses.json && \
mongoimport --db wcm-local --collection lists --file /fixtures/lists.json && \
mongoimport --db wcm-local --collection properties --file /fixtures/properties.json && \
mongoimport --db wcm-local --collection videos --file /fixtures/videos.json && \
pkill -f mongod && \
sleep 2 && \
mongod

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

ПРИМЕЧАНИЕ. Если вы строго хотите загрузить некоторые начальные данные БД, это способ сделать это:

mongo_import.sh

#!/bin/bash
# Import from fixtures

# Used in build and docker-compose mongo (different dirs)
DIRECTORY=../deploy/local/mongo_fixtures
if [[ -d "/fixtures" ]]; then
    DIRECTORY=/fixtures
fi
echo ${DIRECTORY}

mongoimport --db wcm-local --collection clients --file ${DIRECTORY}/clients.json && \
mongoimport --db wcm-local --collection configs --file ${DIRECTORY}/configs.json && \
mongoimport --db wcm-local --collection content --file ${DIRECTORY}/content.json && \
mongoimport --db wcm-local --collection licenses --file ${DIRECTORY}/licenses.json && \
mongoimport --db wcm-local --collection lists --file ${DIRECTORY}/lists.json && \
mongoimport --db wcm-local --collection properties --file ${DIRECTORY}/properties.json && \
mongoimport --db wcm-local --collection videos --file ${DIRECTORY}/videos.json

Файлы mongo_fixtures / *. json были созданы с помощью команды mongoexport.

докер-compose.yaml

version: '3'

services:
  mongo:
    container_name: mongo
    image: mongo:3.2.12
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db:cached
      - ./deploy/local/mongo_fixtures:/fixtures
      - ./deploy/local/mongo_import.sh:/docker-entrypoint-initdb.d/mongo_import.sh


volumes:
  mongo-data:
    driver: local
radtek
источник
5

Если вам нужно запустить более одного процесса-демона, в документации Docker есть предложение использовать Supervisord в автономном режиме, чтобы все подчиненные демоны выводились на стандартный вывод.

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

Тим Тисдалл
источник
Если посмотреть на это снова, этот ответ кажется более подходящим для параллельного запуска нескольких команд.
Тим Тисдалл
1

Используйте такой инструмент, как wait-for-it или dockerize . Это небольшие скрипты-обёртки, которые вы можете включить в изображение вашего приложения. Или напишите свой собственный скрипт-обертку для выполнения более специфичных для приложения команд. в соответствии с: https://docs.docker.com/compose/startup-order/

Эран
источник
0

Я столкнулся с этим, пытаясь настроить мой контейнер jenkins для создания док-контейнеров как пользователь jenkins.

Мне нужно было прикоснуться к файлу docker.sock в Dockerfile, так как я позже свяжу его с файлом docker-compose. Пока я не прикоснулся к нему первым, его еще не было. Это сработало для меня.

Dockerfile:

USER root
RUN apt-get update && \
apt-get -y install apt-transport-https \
ca-certificates \
curl \
software-properties-common && \
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; 
echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
$(lsb_release -cs) \
stable" && \
apt-get update && \
apt-get -y install docker-ce
RUN groupmod -g 492 docker && \
usermod -aG docker jenkins  && \
touch /var/run/docker.sock && \
chmod 777 /var/run/docker.sock

USER Jenkins

докер-compose.yml:

version: '3.3'
services:
jenkins_pipeline:
    build: .
    ports:
      - "8083:8083"
      - "50083:50080"
    volumes:
        - /root/pipeline/jenkins/mount_point_home:/var/jenkins_home
        - /var/run/docker.sock:/var/run/docker.sock
Джейсон Андерсон
источник
Это похоже на ответ на другой вопрос.
kenorb
-7

попробуйте использовать ";" разделить команды, если вы находитесь в двух версиях, например,

command: "sleep 20; echo 'a'"

chanllen
источник