Когда я учился водить машину, на первом же занятии инструктор выехал на перекресток задним ходом, а потом сказал, что делать так нельзя — вообще никогда. Это правило я запомнил сразу и на всю жизнь.
Читаешь детям «Вредные советы» Григория Остера, и видишь, как легко и непринужденно до них доходит, что так делать нельзя.
О том, как правильно писать Dockerfile, написана куча статей. Но мне не попадалось инструкций, как писать неправильные Dockerfile. Восполняю этот пробел. И, может быть, в проектах, которые я получаю на поддержку, таких докерфайлов станет меньше.
Все герои, ситуации и Dockerfile вымышленные. Если вы узнали себя, сорри.
Создаем Dockerfile, зловещий и ужасный
Петр (Senior java/rubby/php developer): Коллега Василий, вы уже залили в Docker новый модуль?
Василий (junior): Нет, не успел, никак не разберусь с этим Docker. Столько статей по нему, глаза разбегаются.
Петр: У нас дедлайн год назад вышел. Давай помогу, в процессе разберемся. Рассказывай, что там у тебя не получается.
Василий: Не могу выбрать базовый образ, чтобы минимальный, но было все, что нужно.
Петр: Бери образ ubuntu, в нем есть все, что нужно. А что много лишнего, потом еще пригодится. И не забудь поставить тэг latest, чтобы версия всегда была самая последняя.
И в Dockerfile появляется первая строка:
FROM ubuntu:latest
Петр: Что там дальше, на чем мы писали наш модуль?
Василий: Так ruby жe, там веб сервер и пара служебных демонов должно запускаться.
Петр: Ага, что нам надо: ruby, bundler, nodejs, imagemagick ну и что там еще … И заодно, сделай upgrade, чтобы точно получить новые пакеты.
Василий: А пользователя не будем создавать, чтобы не из под root?
Петр: Да ну его, потом еще морочиться с правами.
Василий: Мне нужно время, минут 15, чтобы это все в одну команду слепить, я читал, что…
(Петр грубо прерывает дотошного и шибко умного джуна.)
Петр: Пиши отдельными командами, так и читать проще будет.
Dockerfile растет:
FROM ubuntu:latest
RUN apt-get update
RUN apt-get upgrade
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full
RUN gem install bundler
RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -
RUN apt-get install -y nodejs
RUN bundle install --without development test --path vendor/bundle
RUN rm -rf /usr/local/bundle/cache/*.gem
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Тут в кабинет врывается Игорь Иванович, DevOps (но больше Ops, чем Dev), с криками:
ИИ: Петя, твои разработчики опять разломали прод БД, когда же это закончится….
После небольшой перепалки Игорь Иванович остывает и начинает выяснять, чем коллеги тут занимаются.
ИИ: Чем заняты?
Василий: Петр помогает мне составить Dockerfile для нового модуля.
ИИ: Дайте глянуть… Да что вы тут понаписали, вы же репозиторий чистите отдельной командой, это же дополнительный слой… Да как же вы ставите зависимости, если не скопировали Gemfile! И вообще, это никуда не годится.
Петр: Идите, пожалуйста, по своим делам, мы тут как-нибудь разберемся.
Игорь Иванович тоскливо вздыхает и уходит разбираться, кто-таки сломал БД.
Петр: Да, но про код-то он правильно сказал, надо его в имидж запихнуть. И давай сразу поставим ssh и supervisor, а то как мы демонов запускать будем.
Василий: Я тогда сначала скопирую Gemfile и Gemfile.lock, потом все поставлю, и потом уже копирую весь проект. Если Gemfile не меняется, слой возьмется из кэша.
Петр: Что вы все с этими слоями, копируй сразу все. Сразу копируй. Первой же строкой.
Dockerfile теперь выглядит так:
FROM ubuntu:latest
COPY ./ /app
WORKDIR /app
RUN apt-get update
RUN apt-get upgrade
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor
RUN gem install bundler
RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -
RUN apt-get install -y nodejs
RUN bundle install --without development test --path vendor/bundle
RUN rm -rf /usr/local/bundle/cache/*.gem
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Петр: Так, что дальше. У тебя есть конфиги для supervisor?
Василий: Нее, нету. Но я быстро сделаю.
Петр: Потом сделаешь. Давай сейчас набросаем init скрипт, который будет все запускать. Так-с, значит, запускаешь ssh, с nohup, чтобы мы могли подключиться к контейнеру и посмотреть, что пошло не так. Потом так же запускай supervisor. Ну и потом просто запустишь passenger.
В: Но я читал, что должен быть один процесс, так Docker будет знать, что что-то пошло не так, и сможет перезапустить контейнер.
П: Не забивай голову ерундой. И вообще, как? Как ты это все запустишь в одном процессе? Пусть о стабильности Игорь Иванович думает, не зря же он зарплату получает. Наше дело код писать. И вообще, пусть скажет спасибо, что мы написали за него Dockefile.
Спустя 10 минут и два видеоролика про котиков.
В: Я все сделал. Еще комментариев понадобавлял.
П: Показывай!
Свежая версия Dockerfile:
FROM ubuntu:latest
# Копируем исходный код
COPY ./ /app
WORKDIR /app
# Обновляем список пакетов
RUN apt-get update
# Обновляем пакеты
RUN apt-get upgrade
# Устанавливаем нужные пакеты
RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor
# Устанавливаем bundler
RUN gem install bundler
# Устанавливаем nodejs используется для сборки статики
RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -
RUN apt-get install -y nodejs
# Устанавливаем зависимости
RUN bundle install --without development test --path vendor/bundle
# Чистим за собой кэши
RUN rm -rf /usr/local/bundle/cache/*.gem
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Запускаем скрипт, при старте контейнера, который запустит все остальное.
CMD [“/app/init.sh”]
П: Отлично, мне нравится. И комментарии на русском, удобно и читаемо, все бы так работали. Я тебя всему научил, дальше сможешь сам. Пошли пить кофе...
Ну, вот мы и получили идеально ужасный Dockerfile, от вида которого Игорь Иванович захочет уволиться и у него еще неделю будут болеть глаза. Dockerfile, конечно же, мог быть еще хуже, нет предела совершенству. Но для начала и так сойдет.
Закончить хотелось бы цитатой Григория Остера:
Если вы еще не твердо
В жизни выбрали дорогу,
И не знаете, с чего бы
Трудовой свой путь начать,
Бейте лампочки в подъездах —
Люди скажут вам «Спасибо».
Вы поможете народу
Электричество беречь.
Автор: Владимир