Korzystanie z Docker-Compose: sposób wykonywania wielu poleceń


499

Chcę zrobić coś takiego, w którym mogę uruchamiać wiele poleceń w kolejności.

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

Odpowiedzi:


859

Zrozumiałem, użyj bash -c.

Przykład:

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

Ten sam przykład w multiliniach:

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

Lub:

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

6
@Pedram Upewnij się, że używasz obrazu, który faktycznie ma zainstalowany bash. Niektóre obrazy mogą także wymagać bezpośredniej ścieżki do /bin/bash
— ataku,

5
Jeśli nie ma zainstalowanej bash, możesz spróbować sh -c "twoje polecenie"
— Chaoste

Upewnij się, że zawijasz swoje polecenia w cudzysłowie, gdy przechodzisz na bash, a ja musiałem przełączyć „sleep 5”, aby upewnić się, że db był w górze, ale zadziałało to dla mnie.
— traday

74
Wygląda na to, że na obrazach w Alpach nie ma zainstalowanej bash - zrób tak, jak @Chaoste zaleca i użyj shzamiast tego:[sh, -c, "cd /usr/src/app && npm start"]
— Florian Loch

1
Można go również używać tylko ashna alpejskim :)
— Jonathan

160

Przed uruchomieniem uruchamiam rzeczy takie jak migracje w osobnym efemerycznym kontenerze, tak jak (uwaga, plik komponowania musi być typu „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

Pomaga to w utrzymaniu czystości i oddzielności. Dwie rzeczy do rozważenia:

  1. Musisz upewnić się, że sekwencja uruchamiania jest poprawna (przy użyciu depend_on)

  2. chcesz uniknąć wielu kompilacji, które osiąga się przez oznaczenie go za pierwszym razem przy użyciu kompilacji i obrazu; możesz wtedy odwoływać się do obrazu w innych pojemnikach


2
Wydaje mi się to najlepszą opcją i chciałbym jej użyć. Czy możesz rozwinąć konfigurację tagowania, aby uniknąć wielu kompilacji? Wolałbym unikać dodatkowych kroków, więc jeśli to będzie potrzebne, mogę przejść bash -cwyżej.
— Stavros Korokithakis

3
W powyższym yaml kompilacja i tagowanie odbywa się w sekcji migracji. Na pierwszy rzut oka nie jest to tak naprawdę oczywiste, ale kompozycja dokująca oznacza to, gdy określasz kompilację ORAZ właściwości obrazu - przy czym właściwość image określa znacznik dla tej kompilacji. Można go później użyć bez uruchamiania nowej kompilacji (jeśli spojrzysz na Internet, zobaczysz, że nie ma on kompilacji, a jedynie właściwość image). Oto więcej szczegółów docs.docker.com/compose/compose-file )
— Bjoern Stiel

26
Chociaż podoba mi się ten pomysł, problem polega na tym, że zależy tylko od tego, czy startują w tej kolejności, a nie czy są gotowe w tej kolejności. wait-for-it.sh może być rozwiązaniem, którego potrzebują niektórzy ludzie.
— traday

2
Jest to absolutnie poprawne i odrobina wstydu, że komponowanie dokerów nie obsługuje żadnej drobiazgowej kontroli, takiej jak czekanie na wyjście kontenera lub rozpoczęcie nasłuchiwania na porcie. Ale tak, skrypt niestandardowy rozwiązuje ten problem, dobra uwaga!
— Bjoern Stiel

1
Ta odpowiedź zawiera niepoprawne i potencjalnie destrukcyjne informacje o tym, jak zależy od pracy.
— antonagestam

96

Polecam używanie shw przeciwieństwie do, bashponieważ jest łatwiej dostępne na większości obrazów opartych na Uniksie (alpine itp.).

Oto przykład 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"

Spowoduje to wywołanie następujących poleceń w kolejności:

  • python manage.py wait_for_db - poczekaj, aż baza danych będzie gotowa
  • python manage.py migrate - uruchom wszelkie migracje
  • python manage.py runserver 0.0.0.0:8000 - uruchom mój serwer programistyczny

2
Osobiście jest to moje ulubione i najczystsze rozwiązanie.
— BugHunterUK

1
Moje też. Jak wskazuje @LondonAppDev, bash nie jest domyślnie dostępny we wszystkich kontenerach, aby zoptymalizować przestrzeń (np. Większość kontenerów zbudowanych na Alpine Linux)
— ewilan

2
Musiałem uciec od multiline && z \
— Andre Van Zuydam

@AndreVanZuydam hmmm to dziwne, nie musiałem tego robić. Czy otaczałeś się cytatami? Jakiego smaku dokera używasz?
— LondonAppDev

2
@oligofren >służy do uruchomienia wprowadzania wieloliniowego (patrz stackoverflow.com/a/3790497/2220370 )
— LondonAppDev

39

To działa dla mnie:

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 próbuje wyzerować zmienne przed uruchomieniem polecenia, więc jeśli chcesz, aby bash obsługiwał zmienne, musisz uciec od znaków dolara poprzez ich podwojenie ...

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

... w przeciwnym razie pojawi się błąd:

Niepoprawny format interpolacji dla opcji „polecenie” w usłudze „WWW”:


Cześć kolego. Spotkałem problem: `` nierozpoznane argumenty: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Lokalny -T Windows '' polecenie w moim docker-compose.yml to: polecenie: - / bin / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Klient} -T $ {Rodzaje} Czy wiesz, jak to naprawić? Dodam klienta i typy w moim pliku .env.
— Traszka

Oto dokument dla ciebie: docs.docker.com/compose/compose-file/#variable-substitution Myślę, że dzieje się tak, że plik .env umieszcza te zmienne w środowisku kontenerowym, ale kompilacja dokerów szuka w środowisku powłoki . Spróbuj zamiast $${Types}i $${Client}. Myślę, że zapobiegnie to interpretowaniu przez Dockera tych zmiennych i szukaniu ich wartości w dowolnej powłoce, z której wywołujesz Docker-compose, co oznacza, że ​​nadal są w gotowości, by je odrzucić ( po przetworzeniu .envpliku przez dokera ).
— MatrixManAtYrService

Dzięki za komentarz. Zrobiłem to, co powiedziałeś. Mam więc $ (Client) w informacji o błędzie. Zmieniłem sposób odczytywania zmiennych środowiskowych, aby używać os.getenv w pythonie, co jest łatwiejsze. W każdym razie dzięki.
— Traszka

23

Możesz użyć punktu wejścia tutaj. punkt wejścia w oknie dokowanym jest wykonywany przed poleceniem, podczas gdy polecenie jest domyślnym poleceniem, które należy uruchomić przy uruchomieniu kontenera. Tak więc większość aplikacji na ogół przeprowadza procedurę instalacji w pliku punktu wejścia, aw ostatnim zezwala na uruchomienie polecenia.

stwórz plik skryptu powłoki jako docker-entrypoint.sh(nazwa nie ma znaczenia) z następującą zawartością.

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

w pliku docker-compose.yml użyj go entrypoint: /docker-entrypoint.shi zarejestruj polecenie jako command: python manage.py runserver 0.0.0.0:8000 PS: nie zapomnij skopiować docker-entrypoint.shwraz z kodem.


Pamiętaj, że wykona się to również wtedy, gdy to zrobiszdocker-compose run service-name ....
— thisismydesign

18

Inny pomysł:

Jeśli, tak jak w tym przypadku, budujesz kontener, po prostu umieść w nim skrypt startowy i uruchom go za pomocą polecenia. Lub zamontuj skrypt startowy jako wolumin.


Tak, na koniec stworzyłem skrypt run.sh: #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(brzydki oneline)
— fero

9

* AKTUALIZACJA *

Uznałem, że najlepszym sposobem na uruchomienie niektórych poleceń jest napisanie niestandardowego pliku Docker, który robi wszystko, co chcę, zanim oficjalny plik CMD zostanie uruchomiony z obrazu.

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)

Jest to prawdopodobnie najczystszy sposób na zrobienie tego.

* STARA DROGA *

Za pomocą moich poleceń utworzyłem skrypt powłoki. W tym przypadku chciałem zacząć mongodi uruchomić, mongoimportale wywołanie mongodblokuje ci uruchomienie pozostałych.

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

Więc to rozwidla mongo, wykonuje monogimport, a następnie zabija rozwidlone mongo, które jest odłączone, i uruchamia je ponownie bez odłączania. Nie jestem pewien, czy istnieje sposób na dołączenie do rozwidlonego procesu, ale to działa.

UWAGA: Jeśli chcesz ściśle załadować początkowe dane bazy danych, możesz to zrobić w następujący sposób:

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 pliki zostały utworzone za pomocą komendy 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

Jeśli potrzebujesz uruchomić więcej niż jeden proces demona, w dokumentacji Dockera jest sugestia, aby używać Supervisord w trybie odłączonym, aby wszystkie pod-demony były wysyłane do standardowego wyjścia.

Z innego pytania SO odkryłem, że możesz przekierować dane wyjściowe procesów potomnych na standardowe wyjście. W ten sposób możesz zobaczyć wszystkie dane wyjściowe!


Patrząc na to ponownie, ta odpowiedź wydaje się bardziej odpowiednia do uruchamiania wielu poleceń równolegle zamiast szeregowo.
— Tim Tisdall,



0

Natknąłem się na to, próbując skonfigurować mój kontener Jenkins, aby budować kontenery dokujące jako użytkownik Jenkins.

Musiałem dotknąć pliku docker.sock w pliku Docker, ponieważ łączę go później w pliku komponowania dokera. Chyba że najpierw go dotknąłem, jeszcze nie istniał. To zadziałało dla mnie.

Plik Docker:

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

To wydaje się być odpowiedzią na inne pytanie.
— kenorb

-7

spróbuj użyć „;” aby rozdzielić polecenia, jeśli jesteś w dwóch wersjach, np

command: "sleep 20; echo 'a'"

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.