Використовуючи Docker-Compose, як виконати кілька команд


499

Я хочу зробити щось подібне, де я можу запускати кілька команд по порядку.

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

Відповіді:


859

Зрозумів це, використовуй 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
  "

6
@Pedram Переконайтеся, що ви використовуєте зображення, на якому фактично встановлено bash. Для деяких зображень також може знадобитися прямий шлях до /bin/bash
удару,

5
Якщо не буде встановлено bash, ви можете спробувати sh -c "ваша команда"
Chaoste

Переконайтесь, що ви загортаєте свої команди в лапки під час переходу на bash, і я повинен був пропустити "сон 5", щоб переконатися, що db піднявся, але це працювало для мене.
торг

74
На альпійських зображеннях насправді не встановлено жодного удару - роблять, як @Chaoste рекомендує, і використовувати shзамість цього:[sh, -c, "cd /usr/src/app && npm start"]
Флоріан Лох

1
Також можна використовувати просто ashна альпійських :)
Джонатан

160

Я запускаю елементи перед запуском, такі як міграції, в окремому ефемерному контейнері, як-от так (зауважте, композиційний файл має бути типу "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. Ви повинні забезпечити правильну послідовність запуску (використовуючи залежність_on)

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


2
Мені це здається найкращим варіантом, і я хотів би його використати. Чи можете ви детально розробити налаштування тегів, щоб уникнути декількох збірок? Я вважаю за краще уникати зайвих кроків, тому якщо для цього потрібні певні дії, я можу піти з bash -cвище.
Ставрос Корокітакіс

3
У вищезгаданій ямлі збір та позначення відбувається в розділі міграції. На перший погляд це насправді не очевидно, але теги докер-композиції це, коли ви вказуєте властивості збірки AND image - при цьому властивість зображення визначає тег для цієї збірки. Потім їх можна використовувати згодом, не викликаючи нову збірку (якщо ви подивитесь на веб, ви побачите, що у нього немає збірки, а лише властивість зображення). Ось ще кілька деталей docs.docker.com/compose/compose-file )
Бьорн Стіл,

26
Хоча мені подобається ідея цього, проблема полягає в тому, що залежність_on лише забезпечує те, що вони починаються в тому порядку, а не те, що вони готові в тому порядку. wait-for-it.sh може бути рішенням, яке потребує деяких людей.
торг

2
Це абсолютно правильно і трохи прикро, що докер-композиція не підтримує жодного тонкого контролю, як очікування виходу контейнера або початку прослуховування в порту. Але так, нестандартний сценарій вирішує це, добре!
Бьорн Стіл

1
Ця відповідь дає невірну та потенційно руйнівну інформацію про те, як залежить робота.
antonagestam

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 - чекайте, коли db буде готовий
  • python manage.py migrate - виконувати будь-які міграції
  • python manage.py runserver 0.0.0.0:8000 - запустити мій сервер розробки

2
Особисто це моє улюблене і найчистіше рішення.
BugHunterUK

1
Мій також. Як зазначає @LondonAppDev, bash доступний за замовчуванням у всіх контейнерах для оптимізації простору (наприклад, більшість контейнерів, побудованих на вершині Alpine Linux)
ewilan

2
Мені довелося уникнути багатолінійного і&з \
Андре Ван Зуйдама

@AndreVanZuydam Хммм, це дивно, мені це не потрібно було робити. Ви оточили цитатами? Який аромат докера ти працюєш?
LondonAppDev

2
@oligofren >використовується для запуску введення багатостроковий (див stackoverflow.com/a/3790497/2220370 )
LondonAppDev

39

Це працює для мене:

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

... інакше ви отримаєте помилку:

Недійсний формат інтерполяції для параметра "command" в службі "web":


Привіт, друже. У мене виникла проблема: `` `нерозпізнані аргументи: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows` `` команда в моєму docker-compose.yml:: command: - / bin / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Клієнт} -T $ {Типи} Чи знаєте ви, як це виправити? Я додаю клієнт і типи у свій .env файл.
Нютт

Ось документ для вас: docs.docker.com/compose/compose-file/#variable-substitution Я думаю, що відбувається так, що ваш .env файл розміщує ці змінні в середовищі контейнера, але docker-composite шукає у вашому середовищі оболонки . Спробуйте замість цього $${Types}і $${Client}. Я думаю, що це не дозволить docker compose інтерпретувати ці змінні та шукати їх значення в будь-якій оболонці, з якої ви викликаєте docker-compose, що означатиме, що вони все ще знаходяться в баші, щоб їх знешкодити ( після того, як Docker обробив ваш .envфайл).
MatrixManAtYrService

Дякуємо за ваш коментар Я зробив те, що ти сказав насправді. Тому я отримав $ (Клієнт) в інформації про помилку. Я змінив спосіб зчитування змінних середовища, щоб використовувати os.getenv в python, що простіше. Все одно, дякую.
Нютт

23

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

зробити файл сценарію оболонки може бути таким 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

Ще одна ідея:

Якщо, як у цьому випадку, ви будуєте контейнер, просто помістіть у нього сценарій запуску і запустіть це командою. Або змонтуйте сценарій запуску як гучність.


Так, наприкінці я створив сценарій run.sh: #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(потворний один рядок)
fero

9

* ОНОВЛЕННЯ *

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

docker-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.

docker-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

5

Якщо вам потрібно запустити більше одного процесу демон, в документації Docker є пропозиція використовувати Supervisord в нероз'єднаному режимі, щоб усі піддемони виводили в stdout.

З іншого питання SO, я виявив, що ви можете перенаправляти дочірні процеси на вихід у stdout. Таким чином ви зможете побачити весь результат!


Подивившись на це знову, ця відповідь здається більш підходящою для виконання декількох команд паралельно, а не послідовно.
Тім Тісдалл


1

Використовуйте такий інструмент, як очікування або докерізація . Це невеликі сценарії обгортки, які ви можете включити до зображення програми. Або напишіть власний скрипт для обгортки, щоб виконати більш конкретні команди. відповідно до: https://docs.docker.com/compose/startup-order/


0

Я наткнувся на це, намагаючись змусити мій контейнер jenkins створити контейнери для докерів як користувачі jenkins.

Мені потрібно було торкнутися файлу docker.sock у Dockerfile, коли я посилаюсь на нього пізніше у файлі docker-compose. Якщо я не торкнувся його спочатку, він ще не існував. Це працювало для мене.

Докерфайл:

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

docker-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'"

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.