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
RustyShackleford
źródło

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
  "
RustyShackleford
źródło
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

Bjoern Stiel
źródło
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
LondonAppDev
źródło
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”:

MatrixManAtYrService
źródło
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.

Harshad Yeola
źródło
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.

rweng
źródło
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
radtek
źródło
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!

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

Użyj narzędzia, takiego jak czekanie na niego lub dokowanie . Są to małe skrypty otoki, które można dołączyć do obrazu aplikacji. Lub napisz własny skrypt opakowania, aby wykonać polecenia bardziej specyficzne dla aplikacji. zgodnie z: https://docs.docker.com/compose/startup-order/

Eran
źródło
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
Jason Anderson
źródło
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'"

Chanllen
źródło