Docker-compose как правильно установить порядок создания и запуска контейнеров?
Доброго дня уважаемые.
Подскажите как правильно установить порядок билда и запуска контейнеров с помощью docker-compose?
Задача следующая и проблема с ней же.
В процессе создания контейнеров, а именно PostgreSQL, php-fpm, nginx, так же есть необходимость запустить Composer который начинает качать необходимые ему модули, а так же NodeJS с его NPM который тоже тянет модули.
Все это складывается на один Volume в разные папочки, а после окончания закачки Composer-ом еще и запускается миграция бд, которая смотрит в файлик который выкачал композер. После выкачки модулей NPM запускает их билд.
Проблема следующая:
В файле докер композа, я указываю, что первому надо запускаться кконтейнеру с композером который начинает качать файлы, ноя не могу выполнить команду по запуску миграции бд ибо докер думает что у него все ок, он все запустил, контейнер работает пора дальше выполнять сборку и запуск следующего, или же выполнять следующий RUN, а по факту файлы то не сразу выкачиваются, это занимает время, а на большом проекте это занимает до 5 минут и сильно зависит от размера канала на железе где все это стартует. Т.е. нет возможности например выставить слип для ожидания завершения загрузки и уже дальше выполнять стадии.
Тоже самое касается и NPM он тоже запускается, качает, но не нет возможности выполнить билд ибо не все еще скачалось ,а докер думает что все ок, контейнер запущен.
Игрался с
depends_on:
- composer
и пробовал сортировать по разному, один черт, докер считает что контейнер запущен и все ок, но да, он запущен, качает, после завершения закачки, он его тушит и либо внешнем скриптом поднимать контейнер и передавать ему команду на выполнение билда или миграции в случае с бд, но хотелось бы все попробовать решить средствами докера.
Может кто навести на правильные мысли? Как отследить хотя бы когда закончил работать контейнер и использовать это как событие для запуска внешнего скрипта для билда? Думал про мультистейдж билд, но пока я не до конца понимаю как оно рабоатет, да и как там можно решить с тем, что делает докер пока качаются файлы, не думаю что это кардинально поможет.
Если вопросы глупые, не судите строго, я только в процессе изучения докера.
Заранее всем спасибо.
апдейт.
решил немного добавить для полноты картины.
Изначально папка куда качает компосер пуста, список чего надо скачать лежит в файле composer.json
папка куда качает NPM тоже пуста, все что надо сказать в нее лежит в файле package.json
После того как все что перечислено в этих файлов скачивается в volume уже можно запускать следующие действия.
файл composer.json и package.json могут меняться и состав чего надо выкачать тоже может меняться.
Если я правильно понял, сейчас сделано так, что composer начинает свои делишки в запущенном контейнере, а не в процессе билда – создания собственно контейнера.
Кажется, решением было бы создавать собственный image на основе образа composer'а, и подкачивать файлы в процессе построения. А запускать его уже, построенного.
В подпапке ./Composer создать Dockerfile с чем-то подобным:
FROM composer:latest
VOLUME ./data /data
RUN apt-get update && apt-get install git && git pull trololo && composer install
Так пока образ билдится, выполняя все что там в Dockerfile, он не считается запущенным, и не стартуют зависимые от него (depends_on) следующие сервисы.
тут сразу при сборке он запускает инстал и читает файлк в директории проекта тот самый composer.json и все начинает с него ставить. Когда все сборки в докер композе отрабатывают, далее выполняется следующий шаг в баш скрипте который запускают миграцию бд. В файле стоит слип на овер дофига времени, чтоб дождаться выкачки всех файлов.
Криво, но пока только так нашел решение.
Что же касается NPM то тут так
encore:
build:
context: ./encore
dockerfile: Dockerfile-encore
container_name: encore
volumes:
- ../projectdir:/projectdir
ports:
- "8080:8080"
working_dir: /projectdir/ command: npm install
depends_on:
- composer
Тут он команду выполняет по установке всего что лежит в package.json т.е. начинает выкачивать.
чтоб запустить сборку выкачанного, я делаю отдельный контейнер.
encore_build:
image: node
container_name: encore_build
volumes:
- ../projectdir:/projectdir
ports:
- "8081:8081"
working_dir: /projectdir/
command: >
/bin/bash -c "
sleep 300; npm run build;
"
depends_on:
- composer
- encore
- web
- php
- postgres
Тут после опять же слипа выполняется билд, ну и ниже этой комманды уже пробы с depends_on.
которые бесполезные.
Так пока образ билдится, выполняя все что там в Dockerfile, он не считается запущенным, и не стартуют зависимые от него (depends_on) следующие сервисы.
У меня получается образ сбилделся и в конце запускается команда, поэтому не работает depends_on.
если добавить в DockerfileENTRYPOINT роли которая предоставляет сервис запуск в общем минимального сокет сервера по примеру эхо сервера на том языке или среде которая там уже есть (нода джава питон дотнет кобол фортран по вкусу - я вообще предпочитаю джаву но для вас вот пример на питоне
import socket
import sys
from os import getenv
import re
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = getenv('PORT')
if port == None :
port = 10000
server_address = ('0.0.0.0', int(port) )
print ('starting up on {} port {}'.format(server_address[0],server_address[1]))
sock.bind(server_address)
sock.listen(1)
while True:
connection, client_address = sock.accept()
print('accepted a connection from {}'.format(client_address))
data = connection.recv(4096)
if len(data):
connection.sendall(data)
text = data.decode('utf-8')
print('received {}'.format(text))
if re.match(r'QUIT', text):
break
connection.close()
а в ENTRYPOINT зависимой роли добавить пробник пока не начала работать
медленно зреющая зависимость
FROM openjdk:8-jre-alpine3.9
ARG app_jar="example.jar"
ADD "/delayed_start.sh" "delayed_start.sh"
ADD "target/${app_jar}" app.jar
EXPOSE 8086
ENTRYPOINT ["sh", "/delayed_start.sh"]
# то есть мы наарямую спринг уже не запускаем
delayed_start.sh
#!/bin/sh
SERVICE_HOST=имя ноды предостваояющ сервис и долго собираюше
SERVICE_PORT=порт
DELAY='50'
while true
do
echo "Waiting on the ${SERVICE_HOST} ${SERVICE_PORT}"
nc -z $SERVICE_HOST $SERVICE_PORT
if [ $? -eq 0 ]
then
echo 'Got Response'
# для тушения - не надо для mysql mogo и тп
echo QUIT | telnet -r $SERVICE_HOST $SERVICE_PORT
break
fi
echo "Waiting ${DELAY} sec"
sleep $DELAY
done
# ну не обязательно но например спринг
# java -jar $APP
по серьезному надо там форки группы и тада и тэпэ но это как нибудь потом
Руслан Мустаев, повторю идею, данную в моём ответе.
Вместо всех этих command: install, command: npm install в docker-compose, используйте инструкции в dockerfile.
Общий принцип: инсталляция и настройка происходит при сборке каждого образа. В docker-compose запускаются готовые-настроенные образы, которые нужно только стартануть.
Имхо именно это – «красиво и правильно». А бубен можно вернуть в муз. магазин по чеку.
Сергей Соколов, Понял, спасибо, буду завтра пробовать все переделать.
В целом у меня контейнер с php-fpm и постргрес собираются именно через докерфайл.
Изначально все было спланировано именно так, что каждый контейнер собирается через докер файл, структура файлов и папок для всей сборки тоже была изначально так и организована, но потом с этой выкачкой файлов все встало и начались эксперементы и все пошло по ....
сергей кузьмин, Сергей, спасибо за совет, я пока попробую все сделать средствами докера и не городить лишние серверы, если не получиться, буду пробовать ваш вариант.
Посмотрите в сторону https://github.com/jwilder/dockerize. Он умеет ждать когда зависимости будут готовы, работает с разными проколами, в т.ч. file/, можно дождаться когда на докере появится файл Х и тогда начать собирать второй. (ну или по аналогии с другими протоколами, как удобнее)
Самый простой вариант:
В скрипте старта сервисов в контейнере сделайте примерно такое:
touch /somedir/someflag
... # какая-то продолжительная последовательность инициализации
rm -f /somedir/someflag # это выполнить по успешному окончанию
В другом контейнере который зависит от результатов предыдущего контейнера:
sleep 5 # подождать немного пока основной контейнер инициализируется
while [ -f /somedir/someflag ];
do
sleep 1
echo 'Waiting for container x'
done;
... # старт сервисов зависимого контейнера
Здорово, тоже думал о таком варианте, но есть одно но, разработчик в процессе работы может добавлять/удалять модули и я не могу все их определить в файле старта сервисов в touch
Они будут динамически меняться.
А вот использовать вторую часть я в принципе могу ибо я точно знаю что нужно для миграции бд(какой конкретно файл).
Владимир Куц, Либо я не понимаю либо вы не поняли проблему :)
компосер сам выкачивает все что написано в фале composer.json
Этот процесс я не хотел бы менять. я не могу создавать каждый раз десятки папок, в них выкачивать все внешними скриптами. Или я не так понимаю ваше предложение?
touch /somedir/someflag # создаем файл - флаг начала процесса
# тут запускаем композер
... качаем кучу файлов
... компосер сам выкачивает все что написано в фале composer.json
# композер отработал - все файлы скачались
rm -f /somedir/someflag # удаляем файл - флаг конца процесса
Смысл в том что удаляете флаг-метку после успешного завершения вашего композера - когда он скачает все файлы и успокоится. Вы ничего не меняете - просто помечаете флагом начало и конец процесса
После удаления метки - "просыпается" ваш зависимый контейнер который делает миграции и прочее
Все, понял, создаем директорию, запускаем выкачку, как выкачалось удаляем.
на втором контейнере пока есть папка/файл, то ждем.
Тоже вариант, думаю куда сунуть это создание и удаление файла.
Ибо через RUN не работает, точнее работает, но пока в файлы в фоне качаются он смело переходит к следующим шагам RUN. C CMD тоже не вариант, оно как я понял может выполняться только раз уже после билда.
chupasaurus, А можно подробнее? Что именно туда и без вариантов это не будет работать или именно туда и надо? :) Как я понимаю, энтрипоинт это то, что выполняется при запуске контейнера каждый раз.
Руслан Мустаев, Entrypoint - единственное, что выполняется при старте контейнера всегда, поэтому все предварительные действия выносить надо в него, заодно получите правильную работу сигналов и проброса stdout/stderr.
chupasaurus, Ну так энтрипоинт же запускается каждый раз при запуске контейнера? Если так, то зачем мне каждый раз качать файлы и делать инстал, мне это надо сделать раз при создании всех контейнеров либо дальше уже когда будет делаться комит на гитхабе, но это отдельная история и я к ней дойду позже.
Кроме этого, по моему я уже пробовал через него запускать, да и проблем с запуском то нет, проблема в том, что как я понял, после запуска например того же композа, он начинает качать, работая дальше т.к. получается запустился и тарахтит, т.е. а мне надо после того как закачает все файлы, либо запустить еще один контейнер который уже сделает билд, либо в этом же запустить билд когда файлы все точно докачены.
Руслан Мустаев, Любите вы писать стены текста (:
"качать файлы и делать инсталл" не должно менять содержимого контейнера при повторном запуске, если правильно организовано.
Откладывать запуск зависимых сервисов в Compose можно через параметр healthcheck (и HEALTHCHECK в образе).
В процессе решения проблемы выше, столкнулся с необходимостью разместить основную workdir в каталоге выше расположения файла докеркомпоз и рядом лежащих папочек с докерфайлами.
Нарвался на проблему с непониманием путей.
У меня есть скрипт, который клонит проект в папочку, и затем скрипт запускает докер-композ который испольузет в ямле пути к каталогу до склониного проекта.
Сам проект лежит на пару папок выше в каталоге относительно файла докеркомпоза.
В общем в процессе выполнения докеркомпоз ругается что путь должен быть относительным и что как я понимаю конструкции типа ./../project_app не катит.
Придется задавать значения через ENV?