Usando o Docker-Compose, como executar vários comandos

500

Eu quero fazer algo assim, onde eu possa executar vários comandos em ordem.

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
fonte

Respostas:

861

Descobri, use bash -c.

Exemplo:

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

O mesmo exemplo em multilinhas:

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
  "
RustyShackleford
fonte
6
@Pedram Verifique se você está usando uma imagem que realmente possui o bash instalado. Algumas imagens também podem exigir um caminho direto para o bash por exemplo/bin/bash
codemaven
6
Se não houver nenhuma festa instalado, você poderia tentar sh -c "o comando"
Chaoste
Certifique-se de colocar seus comandos entre aspas ao passar para o bash e eu tive que inserir um "sono 5" para garantir que o banco de dados estivesse ativo, mas funcionou para mim.
traday 26/10/16
74
Imagens baseadas nos Alpes parecem não ter o bash instalado - faça como o @Chaoste recomenda e use sh:[sh, -c, "cd /usr/src/app && npm start"]
Florian Loch
1
Também é possível utilizar apenas ashem alpino :)
Jonathan
160

Eu corro coisas pré-inicialização, como migrações em um contêiner efêmero separado, assim (observe, o arquivo de composição deve ser do tipo versão 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

Isso ajuda as coisas a se manterem limpas e separadas. Duas coisas a considerar:

  1. Você precisa garantir a sequência de inicialização correta (usando o Depend_on)

  2. você deseja evitar várias compilações, o que é conseguido marcando-a na primeira vez usando compilação e imagem; você pode consultar a imagem em outros recipientes

Bjoern Stiel
fonte
2
Essa parece ser a melhor opção para mim e eu gostaria de usá-la. Você pode elaborar sua configuração de marcação para evitar várias compilações? Eu preferiria evitar etapas extras, por isso, se isso precisar de alguma coisa, eu posso ir com o bash -cacima.
Stavros Korokithakis
3
No yaml acima, a construção e a marcação acontecem na seção de migração. Não é realmente óbvio à primeira vista, mas o docker-componha marcá-lo quando você especifica as propriedades build AND image - em que a propriedade image especifica o tag para esse build. Isso pode ser usado posteriormente sem acionar uma nova compilação (se você observar a web, verá que ela não tem compilação, mas apenas uma propriedade de imagem). Aqui estão mais alguns detalhes docs.docker.com/compose/compose-file )
Bjoern Stiel
26
Embora eu goste da idéia, o problema é que depende_on apenas garante que eles iniciem nessa ordem, e não que estejam prontos nessa ordem. wait-for-it.sh pode ser a solução que algumas pessoas precisam.
traday 26/10/16
2
Isso está absolutamente correto e é uma pena que a composição do docker não suporte nenhum controle refinado, como esperar que um contêiner saia ou comece a ouvir em uma porta. Mas sim, um script personalizado resolve isso, bom ponto!
Bjoern Stiel
1
Esta resposta fornece informações incorretas e potencialmente destrutivas sobre como depende o trabalho.
antonagestam 7/08/19
96

Eu recomendo usar sho contrário, bashporque está mais prontamente disponível na maioria das imagens baseadas em unix (alpino, etc.).

Aqui está um exemplo 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"

Isso chamará os seguintes comandos em ordem:

  • python manage.py wait_for_db - aguarde o db estar pronto
  • python manage.py migrate - execute qualquer migração
  • python manage.py runserver 0.0.0.0:8000 - iniciar meu servidor de desenvolvimento
LondonAppDev
fonte
2
Pessoalmente, essa é minha solução favorita e mais limpa.
BugHunterUK 10/10
1
Meu também. Como pontos-out @LondonAppDev, o bash não está disponível por padrão em todas as embalagens para otimizar no espaço (por exemplo, a maioria dos recipientes construídos sobre Alpine Linux)
ewilan
2
Eu tive que escapar da multilinha && com um \
Andre Van Zuydam
@AndreVanZuydam hmmm isso é estranho, eu não precisava fazer isso. Você cercou entre aspas? Qual o sabor do docker que você está executando?
LondonAppDev
2
@oligofren o >é utilizado para iniciar uma entrada multi-linha (ver stackoverflow.com/a/3790497/2220370 )
LondonAppDev
40

Isso funciona para mim:

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 tenta desreferenciar variáveis antes de executar o comando, portanto, se você deseja que o bash manipule variáveis, precisará escapar dos cifrões dobrando-os ...

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

... caso contrário, você receberá um erro:

Formato de interpolação inválido para a opção "comando" no serviço "web":

MatrixManAtYrService
fonte
E aí, cara. Encontrei um problema: `` argumentos não reconhecidos: / bin / bash -c python3 /usr/local/airflow/__init__.py -C Local -T Windows `` `o comando no meu docker-compose.yml é: command: - / bin / bash - -c - | python3 /usr/local/airflow/__init__.py -C $ {Client} -T $ {Types} Você sabe como consertar isso? Eu adiciono Client e Types no meu arquivo .env.
Newt
Aqui está um documento para você: docs.docker.com/compose/compose-file/#variable-substitution Acho que o que está acontecendo é que o arquivo .env coloca essas variáveis ​​no ambiente do contêiner, mas o docker-compose está procurando no seu ambiente de shell . Tente em vez disso $${Types}e $${Client}. Eu acho que isso impedirá que o docker compose interprete essas variáveis ​​e procure seus valores em qualquer shell do qual você invoque o docker-composit, o que significa que eles ainda estão por aí para que o bash os desreferencie ( depois que o docker processou seu .envarquivo).
MatrixManAtYrService
Obrigado por seu comentário. Eu fiz o que você disse de fato. Então, recebi o $ (Client) nas informações de erro. Mudei a maneira de ler variáveis ​​de ambiente para usar os.getenv em python, o que é mais fácil. Obrigado mesmo assim.
Newt
23

Você pode usar o ponto de entrada aqui. O ponto de entrada na janela de encaixe é executado antes do comando while command é o comando padrão que deve ser executado quando o contêiner é iniciado. Portanto, a maioria dos aplicativos geralmente realiza o procedimento de instalação no arquivo de ponto de entrada e, no último, eles permitem a execução do comando.

make um arquivo de script de shell pode ser o seguinte docker-entrypoint.sh(o nome não importa) com o seguinte conteúdo.

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

no arquivo docker-compose.yml, use-o com entrypoint: /docker-entrypoint.she o comando register como command: python manage.py runserver 0.0.0.0:8000 PS: não esqueça de copiar docker-entrypoint.shjunto com o seu código.

Harshad Yeola
fonte
Observe que isso também será executado quando você fizer issodocker-compose run service-name ....
thisismydesign
18

Outra ideia:

Se, como neste caso, você criar o contêiner, basta colocar um script de inicialização nele e executá-lo com o comando Ou monte o script de inicialização como volume.

rweng
fonte
Sim, no final eu criei um script run.sh: #!/bin/bash \n python manage.py migrate \n python manage.py runserver 0.0.0.0:8000(oneline feio)
fero
9

* ATUALIZAÇÃO *

Achei que a melhor maneira de executar alguns comandos é escrever um Dockerfile personalizado que faça tudo o que eu quero antes que o CMD oficial seja executado a partir da imagem.

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)

Esta é provavelmente a maneira mais limpa de fazer isso.

* À MODA ANTIGA *

Eu criei um script de shell com meus comandos. Nesse caso, eu queria começar mongode executar, mongoimportmas chamar o mongodimpede de executar o resto.

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

Então, esse garfo mongo, monitora a importação e, em seguida, mata o mongo bifurcado que é desanexado e o inicia novamente sem desconectar. Não tenho certeza se existe uma maneira de conectar-se a um processo bifurcado, mas isso funciona.

NOTA: Se você deseja carregar estritamente alguns dados db iniciais, é assim que se faz:

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

Os arquivos mongo_fixtures / *. json foram criados por meio do comando 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
fonte
5

Se você precisar executar mais de um processo de daemon, há uma sugestão na documentação do Docker para usar o Supervisord em um modo desanexado, para que todos os sub-daemons sejam exibidos no stdout.

De outra pergunta do SO, descobri que você pode redirecionar a saída dos processos filho para o stdout. Dessa forma, você pode ver toda a saída!

Tim Tisdall
fonte
Olhando para isso novamente, esta resposta parece mais adequada para executar vários comandos em paralelo, em vez de serialmente.
precisa saber é o seguinte
1

Use uma ferramenta como espera por ele ou dockerize . Esses são pequenos scripts de wrapper que você pode incluir na imagem do seu aplicativo. Ou escreva seu próprio script de wrapper para executar comandos mais específicos do aplicativo. de acordo com: https://docs.docker.com/compose/startup-order/

Eran
fonte
0

Eu me deparei com isso enquanto tentava configurar meu contêiner jenkins para criar contêineres docker como o usuário jenkins.

Eu precisava tocar no arquivo docker.sock no Dockerfile, como eu o vinculo mais tarde no arquivo docker-compose. A menos que eu toquei primeiro, ele ainda não existia. Isso funcionou para mim.

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
fonte
Esta parece ser a resposta para uma pergunta diferente.
kenorb
-7

tente usar ";" para separar os comandos se você estiver na versão dois, por exemplo

command: "sleep 20; echo 'a'"

chanllen
fonte