Verwenden von Docker-Compose zum Ausführen mehrerer Befehle

500

Ich möchte so etwas tun, wo ich mehrere Befehle nacheinander ausführen kann.

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
quelle

Antworten:

861

Ich habe es herausgefunden, benutze bash -c.

Beispiel:

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

Gleiches Beispiel in mehreren Zeilen:

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

Oder:

command: bash -c "
    python manage.py migrate
    && python manage.py runserver 0.0.0.0:8000
  "
RustyShackleford
quelle
6
@Pedram Stellen Sie sicher, dass Sie ein Image verwenden, auf dem Bash tatsächlich installiert ist. Einige Bilder erfordern möglicherweise auch einen direkten Pfad zum Bash, z. B./bin/bash
Codemaven
5
Wenn keine Bash installiert ist, können Sie versuchen, sh -c "Ihr Befehl"
Chaoste
Stellen Sie sicher, dass Sie Ihre Befehle in Anführungszeichen setzen, wenn Sie zu Bash übergehen, und ich musste eine "Schlaf-5" eingeben, um sicherzustellen, dass die Datenbank aktiv war, aber es hat bei mir funktioniert.
Tag
74
Auf alpinen Bildern scheint tatsächlich keine Bash installiert zu sein - wie von @Chaoste empfohlen und shstattdessen verwendet:[sh, -c, "cd /usr/src/app && npm start"]
Florian Loch
1
Kann auch nur ashauf alpinen verwenden :)
Jonathan
160

Ich führe Pre-Startup-Inhalte wie Migrationen in einem separaten kurzlebigen Container aus (beachten Sie, dass die Compose-Datei vom Typ '2' sein muss):

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

Dies hilft, die Dinge sauber und getrennt zu halten. Zwei Dinge zu beachten:

  1. Sie müssen die richtige Startreihenfolge sicherstellen (mit abhängigen_on)

  2. Sie möchten mehrere Builds vermeiden, indem Sie sie beim ersten Mal mit Build und Image markieren. Sie können dann auf das Bild in anderen Containern verweisen

Björn Stiel
quelle
2
Dies scheint mir die beste Option zu sein, und ich würde sie gerne nutzen. Können Sie Ihr Tagging-Setup näher erläutern, um mehrere Builds zu vermeiden? Ich würde es vorziehen, zusätzliche Schritte zu vermeiden. Wenn dies einige erfordert, könnte ich bash -coben vorgehen.
Stavros Korokithakis
3
Im obigen Yaml erfolgt das Erstellen und Markieren im Migrationsabschnitt. Es ist auf den ersten Blick nicht wirklich offensichtlich, aber Docker-Compose markiert es, wenn Sie die Build- UND Image-Eigenschaften angeben - wobei die Image-Eigenschaft das Tag für diesen Build angibt. Dies kann anschließend verwendet werden, ohne einen neuen Build auszulösen (wenn Sie sich das Web ansehen, sehen Sie, dass es keinen Build hat, sondern nur eine Image-Eigenschaft). Hier sind einige weitere Details docs.docker.com/compose/compose-file )
Bjoern Stiel
26
Obwohl mir die Idee dazu gefällt, besteht das Problem darin, dass abhängige_ nur sicherstellen, dass sie in dieser Reihenfolge beginnen, nicht dass sie in dieser Reihenfolge bereit sind. wait-for-it.sh ist möglicherweise die Lösung, die manche Leute brauchen.
Tag
2
Das ist absolut richtig und ein bisschen schade, dass Docker-Compose keine feinkörnige Steuerung unterstützt, wie das Warten auf das Verlassen eines Containers oder das Abhören eines Ports. Aber ja, ein benutzerdefiniertes Skript löst dieses Problem, guter Punkt!
Bjoern Stiel
1
Diese Antwort enthält falsche und möglicherweise zerstörerische Informationen darüber, wie abhängige_von funktionieren.
Antonagestam
96

Ich empfehle die Verwendung shim Gegensatz zu, bashda es auf den meisten Unix-basierten Bildern (alpine usw.) leichter verfügbar ist.

Hier ist ein Beispiel 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"

Dadurch werden die folgenden Befehle der Reihe nach aufgerufen:

  • python manage.py wait_for_db - Warten Sie, bis die Datenbank fertig ist
  • python manage.py migrate - Führen Sie alle Migrationen durch
  • python manage.py runserver 0.0.0.0:8000 - Starten Sie meinen Entwicklungsserver
LondonAppDev
quelle
2
Persönlich ist dies meine bevorzugte und sauberste Lösung.
BugHunterUK
1
Meine auch. Wie @LondonAppDev hervorhebt, ist Bash nicht standardmäßig in allen Containern verfügbar, um den Speicherplatz zu optimieren (z. B. die meisten Container, die auf Alpine Linux
basieren
2
Ich musste dem mehrzeiligen && mit einem \
Andre Van Zuydam
@AndreVanZuydam hmmm das ist seltsam, das musste ich nicht machen. Haben Sie mit Zitaten umgeben? Welchen Docker-Geschmack haben Sie?
LondonAppDev
2
@oligofren >wird verwendet, um eine mehrzeilige Eingabe zu starten (siehe stackoverflow.com/a/3790497/2220370 )
LondonAppDev
40

Das funktioniert bei mir:

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 versucht, Variablen zu dereferenzieren, bevor der Befehl ausgeführt wird. Wenn Sie also möchten, dass bash Variablen verarbeitet, müssen Sie den Dollarzeichen entkommen, indem Sie sie verdoppeln ...

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

... sonst erhalten Sie eine Fehlermeldung:

Ungültiges Interpolationsformat für die Option "Befehl" im Dienst "Web":

MatrixManAtYrService
quelle
Hallo Kumpel. Ich bin auf ein Problem gestoßen: `` `nicht erkannte Argumente: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows` `` Der Befehl in meiner docker-compose.yml lautet: Befehl: - / bin / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Client} -T $ {Types} Wissen Sie, wie Sie das beheben können? Ich füge Client und Typen zu meiner .env-Datei hinzu.
Newt
Hier ist ein Dokument für Sie: docs.docker.com/compose/compose-file/#variable-substitution Ich denke, was passiert, ist, dass Ihre .env-Datei diese Variablen in der Containerumgebung platziert, aber Docker-Compose sucht in Ihrer Shell-Umgebung . Versuchen Sie stattdessen $${Types}und $${Client}. Ich denke, dies wird verhindern, dass Docker Compose diese Variablen interpretiert und nach ihren Werten in der Shell sucht, von der aus Sie Docker Compose aufrufen. Dies bedeutet, dass sie immer noch für Bash verfügbar sind, um sie zu dereferenzieren ( nachdem Docker Ihre .envDatei verarbeitet hat ).
MatrixManAtYrService
Vielen Dank für Ihren Kommentar. Ich habe getan, was du gesagt hast. Also habe ich das $ (Client) in den Fehlerinformationen bekommen. Ich habe die Art und Weise geändert, Umgebungsvariablen zu lesen, um os.getenv in Python zu verwenden, was einfacher ist. Danke trotzdem.
Newt
23

Hier können Sie den Einstiegspunkt verwenden. Der Einstiegspunkt im Docker wird vor dem Befehl ausgeführt, während der Befehl der Standardbefehl ist, der beim Starten des Containers ausgeführt werden soll. Daher führen die meisten Anwendungen im Allgemeinen Setup-Prozeduren in der Einstiegspunktdatei aus und in der letzten erlauben sie die Ausführung des Befehls.

Erstellen Sie eine Shell-Skriptdatei kann wie docker-entrypoint.shfolgt sein (Name spielt keine Rolle) mit folgenden Inhalten.

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

Verwenden Sie es in der Datei docker-compose.yml mit entrypoint: /docker-entrypoint.shund registrieren Sie den Befehl als command: python manage.py runserver 0.0.0.0:8000 PS: Vergessen Sie nicht, docker-entrypoint.shzusammen mit Ihrem Code zu kopieren .

Harshad Yeola
quelle
Beachten Sie, dass dies auch ausgeführt wird, wenn Sie dies tundocker-compose run service-name ....
thisismydesign
18

Eine andere Idee:

Wenn Sie wie in diesem Fall den Container erstellen, platzieren Sie einfach ein Startskript darin und führen Sie dieses mit dem Befehl aus. Oder hängen Sie das Startskript als Volume ein.

rweng
quelle
Ja, am Ende habe ich ein run.sh-Skript erstellt: #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(hässlich online)
fero
9

* UPDATE *

Ich dachte, der beste Weg, einige Befehle auszuführen, besteht darin, eine benutzerdefinierte Docker-Datei zu schreiben, die alles tut, was ich will, bevor das offizielle CMD aus dem Image ausgeführt wird.

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)

Dies ist wahrscheinlich der sauberste Weg, dies zu tun.

* ALTER WEG *

Ich habe mit meinen Befehlen ein Shell-Skript erstellt. In diesem Fall wollte ich starten mongodund ausführen, mongoimportaber ein Anruf mongodhindert Sie daran, den Rest auszuführen .

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

Dieser Gabel-Mongo führt also einen Monogimport durch und tötet dann den abgetrennten Gabel-Mongo und startet ihn erneut, ohne sich zu lösen. Ich bin mir nicht sicher, ob es eine Möglichkeit gibt, eine Verbindung zu einem gegabelten Prozess herzustellen, aber dies funktioniert.

HINWEIS: Wenn Sie unbedingt einige anfängliche Datenbankdaten laden möchten, gehen Sie folgendermaßen vor:

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-Dateien wurden mit dem Befehl mongoexport erstellt.

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
quelle
5

Wenn Sie mehr als einen Daemon-Prozess ausführen müssen, wird in der Docker-Dokumentation empfohlen, Supervisord in einem nicht getrennten Modus zu verwenden, damit alle Sub-Daemons auf dem Standard ausgegeben werden.

Bei einer anderen SO-Frage habe ich festgestellt, dass Sie die Ausgabe der untergeordneten Prozesse an stdout umleiten können. Auf diese Weise können Sie die gesamte Ausgabe sehen!

Tim Tisdall
quelle
Wenn Sie dies noch einmal betrachten, scheint diese Antwort eher dazu geeignet zu sein, mehrere Befehle parallel als seriell auszuführen.
Tim Tisdall
1

Verwenden Sie ein Tool wie "Wait-for-It" oder " Dockerize" . Dies sind kleine Wrapper-Skripte, die Sie in das Image Ihrer Anwendung aufnehmen können. Oder schreiben Sie Ihr eigenes Wrapper-Skript, um anwendungsspezifischere Befehle auszuführen. laut: https://docs.docker.com/compose/startup-order/

Eran
quelle
0

Ich bin darauf gestoßen, als ich versucht habe, meinen Jenkins-Container so einzurichten, dass Docker-Container als Jenkins-Benutzer erstellt werden.

Ich musste die Datei docker.sock in der Docker-Datei berühren, da ich sie später in der Docker-Compose-Datei verknüpfe. Sofern ich es nicht zuerst berührt habe, existierte es noch nicht. Das hat bei mir funktioniert.

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

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
quelle
Dies scheint die Antwort auf eine andere Frage zu sein.
Kenorb
-7

versuche es mit ";" um die Befehle zu trennen, wenn Sie sich in zwei Versionen befinden, z

command: "sleep 20; echo 'a'"

Chanllen
quelle