À l'aide de Docker-Compose, comment exécuter plusieurs commandes


500

Je veux faire quelque chose comme ça où je peux exécuter plusieurs commandes dans l'ordre.

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

Réponses:


861

Compris, utilisez bash -c.

Exemple:

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

Même exemple dans les multilignes:

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

Ou:

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

6
@Pedram Assurez-vous que vous utilisez une image sur laquelle bash est installé. Certaines images peuvent également nécessiter un chemin direct vers bash, par exemple/bin/bash
codemaven

6
Si aucun bash n'est installé, vous pouvez essayer sh -c "votre commande"
Chaoste

Assurez-vous de mettre vos commandes entre guillemets lors du passage à bash et j'ai dû glisser un "sleep 5" pour m'assurer que la base de données était en place, mais cela a fonctionné pour moi.
traday

74
Les images alpines semblent n'avoir aucun bash installé - faites comme @Chaoste recommande et utilisez shplutôt:[sh, -c, "cd /usr/src/app && npm start"]
Florian Loch

1
Peut également être utilisé uniquement ashen alpin :)
Jonathan

160

J'exécute des trucs de pré-démarrage comme des migrations dans un conteneur éphémère séparé, comme ça (notez que le fichier de composition doit être de type '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

Cela aide à garder les choses propres et séparées. Deux choses à considérer:

  1. Vous devez vous assurer de la séquence de démarrage correcte (en utilisant depend_on)

  2. vous voulez éviter plusieurs builds ce qui est réalisé en le marquant la première fois en utilisant build et image; vous pouvez vous référer à l'image dans d'autres conteneurs puis


2
Cela me semble être la meilleure option et j'aimerais l'utiliser. Pouvez-vous élaborer sur votre configuration de marquage pour éviter plusieurs versions? Je préférerais éviter les étapes supplémentaires, donc si cela en a besoin, je pourrais aller plus bash -chaut.
Stavros Korokithakis

3
Dans le yaml ci-dessus, la génération et le balisage se produisent dans la section de migration. Ce n'est pas vraiment évident à première vue, mais docker-compose le balise lorsque vous spécifiez les propriétés de construction ET d'image - la propriété d'image spécifiant la balise pour cette construction. Cela peut ensuite être utilisé par la suite sans déclencher une nouvelle build (si vous regardez sur le Web, vous voyez qu'il n'a pas de build mais seulement une propriété d'image). Voici quelques détails supplémentaires docs.docker.com/compose/compose-file )
Bjoern Stiel

26
Bien que j'aime l'idée de cela, le problème est que depend_on s'assure seulement qu'ils commencent dans cet ordre, pas qu'ils soient prêts dans cet ordre. wait-for-it.sh peut être la solution dont certaines personnes ont besoin.
traday

2
C'est tout à fait correct et un peu dommage que docker-compose ne supporte aucun contrôle à grain fin comme attendre la sortie d'un conteneur ou commencer à écouter sur un port. Mais oui, un script personnalisé résout ce problème, bon point!
Bjoern Stiel

1
Cette réponse donne des informations incorrectes et potentiellement destructrices sur le fonctionnement de depend_on.
antonagestam

96

Je recommande d'utiliser shpar opposition à bashcar il est plus facilement disponible sur la plupart des images basées sur Unix (alpine, etc.).

Voici un exemple 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"

Cela appellera les commandes suivantes dans l'ordre:

  • python manage.py wait_for_db - attendez que la base de données soit prête
  • python manage.py migrate - exécuter toutes les migrations
  • python manage.py runserver 0.0.0.0:8000 - démarrer mon serveur de développement

2
Personnellement, c'est ma solution préférée et la plus propre.
BugHunterUK

1
Le mien aussi. Comme le souligne @LondonAppDev, bash n'est pas disponible par défaut dans tous les conteneurs pour optimiser l'espace (par exemple, la plupart des conteneurs construits sur Alpine Linux)
ewilan

2
J'ai dû échapper à la multiligne && avec un \
Andre Van Zuydam

@AndreVanZuydam hmmm c'est étrange, je n'avais pas besoin de faire ça. Avez-vous entouré de citations? Quelle saveur de docker utilisez-vous?
LondonAppDev

2
@oligofren le >est utilisé pour démarrer une entrée multiligne (voir stackoverflow.com/a/3790497/2220370 )
LondonAppDev

40

Cela fonctionne pour moi:

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 essaie de déréférencer les variables avant d' exécuter la commande, donc si vous voulez que bash gère les variables, vous devrez échapper aux signes dollar en les doublant ...

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

... sinon vous obtiendrez une erreur:

Format d'interpolation non valide pour l'option "commande" dans le service "web":


Salut mon pote. J'ai rencontré un problème: `` `arguments non reconnus: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows '' 'la commande dans mon docker-compose.yml est: commande: - / bin / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Client} -T $ {Types} Savez-vous comment résoudre ce problème? J'ajoute Client et Types dans mon fichier .env.
Newt

Voici un document pour vous: docs.docker.com/compose/compose-file/#variable-substitution Je pense que ce qui se passe, c'est que votre fichier .env place ces variables dans l'environnement conteneur, mais docker-compose regarde dans votre environnement shell . Essayez plutôt $${Types}et $${Client}. Je pense que cela empêchera Docker Composer d'interpréter ces variables et de rechercher leurs valeurs dans le shell à partir duquel vous appelez Docker-Composer, ce qui signifie qu'elles sont toujours là pour bash pour les déréférencer ( après que Docker a traité votre .envfichier).
MatrixManAtYrService

Merci pour votre commentaire. J'ai fait ce que tu as dit en fait. J'ai donc obtenu le $ (Client) dans les informations d'erreur. J'ai changé la façon de lire les variables d'environnement pour utiliser os.getenv en python, ce qui est plus facile. Merci quand même.
Newt

23

Vous pouvez utiliser le point d'entrée ici. Le point d'entrée dans Docker est exécuté avant la commande tandis que la commande est la commande par défaut qui doit être exécutée au démarrage du conteneur. Ainsi, la plupart des applications comportent généralement une procédure de configuration dans un fichier de point d'entrée et, dans le dernier, elles permettent l'exécution de la commande.

faire un fichier de script shell peut être comme docker-entrypoint.sh(le nom n'a pas d'importance) avec le contenu suivant.

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

dans le fichier docker-compose.yml, utilisez-le avec entrypoint: /docker-entrypoint.shet enregistrez la commande en tant que command: python manage.py runserver 0.0.0.0:8000 PS: n'oubliez pas de copier docker-entrypoint.shavec votre code.


Notez que cela s'exécutera également lorsque vous le ferezdocker-compose run service-name ....
thisismydesign

18

Une autre idée:

Si, comme dans ce cas, vous créez le conteneur, placez-y simplement un script de démarrage et exécutez-le avec la commande. Ou montez le script de démarrage en tant que volume.


Oui, à la fin, j'ai créé un script run.sh: #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(laid en ligne)
fero

9

* MISE À JOUR *

J'ai pensé que la meilleure façon d'exécuter certaines commandes est d'écrire un Dockerfile personnalisé qui fait tout ce que je veux avant que le CMD officiel ne soit exécuté à partir de l'image.

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)

C'est probablement la façon la plus propre de le faire.

* ANCIENNE VOIE *

J'ai créé un script shell avec mes commandes. Dans ce cas, je voulais démarrer mongodet exécuter, mongoimportmais appeler mongodvous empêche d'exécuter le reste.

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

Donc, cette fourche mongo, fait de la monogimportation puis tue le mongo fourchu qui est détaché, et le redémarre sans se détacher. Je ne sais pas s'il existe un moyen de s'attacher à un processus fourchu, mais cela fonctionne.

REMARQUE: Si vous souhaitez strictement charger des données de base de données initiales, voici la façon de procéder:

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

Les fichiers mongo_fixtures / *. json ont été créés via la commande 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

Si vous devez exécuter plusieurs processus démon, il est suggéré dans la documentation Docker d'utiliser Supervisord en mode non détaché pour que tous les sous-démons soient envoyés vers la sortie standard.

À partir d'une autre question SO, j'ai découvert que vous pouvez rediriger la sortie des processus enfants vers la sortie standard. De cette façon, vous pouvez voir toutes les sorties!


En examinant à nouveau cela, cette réponse semble plus adaptée pour exécuter plusieurs commandes en parallèle plutôt qu'en série.
Tim Tisdall



0

J'ai rencontré cela en essayant de configurer mon conteneur jenkins pour créer des conteneurs docker en tant qu'utilisateur jenkins.

Je devais toucher le fichier docker.sock dans le Dockerfile car je le lierai plus tard dans le fichier docker-compose. Sauf si je l'ai touché en premier, il n'existait pas encore. Cela a fonctionné pour moi.

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

Cela semble être la réponse à une question différente.
kenorb

-7

essayez d'utiliser ";" pour séparer les commandes si vous êtes dans les versions deux, par exemple

command: "sleep 20; echo 'a'"

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.