Southbridge corporate blog
System administration
Server Administration
DevOps
April 29

Docker: вредные советы


Когда я учился водить машину, на первом же занятии инструктор выехал на перекресток задним ходом, а потом сказал, что делать так нельзя — вообще никогда. Это правило я запомнил сразу и на всю жизнь.


Читаешь детям «Вредные советы» Григория Остера, и видишь, как легко и непринужденно до них доходит, что так делать нельзя.


О том, как правильно писать Dockerfile, написана куча статей. Но мне не попадалось инструкций, как писать неправильные Dockerfile. Восполняю этот пробел. И, может быть, в проектах, которые я получаю на поддержку, таких докерфайлов станет меньше.


Все герои, ситуации и Dockerfile вымышленные. Если вы узнали себя, сорри.


Создаем Dockerfile, зловещий и ужасный


Петр (Senior java/ruby/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, конечно же, мог быть еще хуже, нет предела совершенству. Но для начала и так сойдет.


Закончить хотелось бы цитатой Григория Остера:


Если вы еще не твердо
В жизни выбрали дорогу,
И не знаете, с чего бы
Трудовой свой путь начать,
Бейте лампочки в подъездах —
Люди скажут вам «Спасибо».
Вы поможете народу
Электричество беречь.


UPD: В комментариях спрашивают, что не так в этих Dockerfile. На днях напишу отдельную статью с разбором ошибок.

+32
32k 180
Comments 66
Top of the day