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


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


О том, как правильно писать 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. На днях напишу отдельную статью с разбором ошибок.

Комментарии (66)


  1. t1gger
    29.04.2019 12:45
    +1

    Вот бы еще был разбор получившегося Dockerfile на предмет того, что и чем в нём не правильно… Нет, все понятно, но что конкретно?..


    1. Magvai69 Автор
      29.04.2019 12:49

      Добрый день.
      Я, с удовольствием, в комментариях, поясню непонятные моменты.
      Писать отдельную статью, не вижу смыла, их и так уже 100500.

      Так же, советую, почитать: docs.docker.com/develop/develop-images/dockerfile_best-practices


      1. Omrigan
        29.04.2019 14:52

        Мне кажется, всё же стоит пояснить. Мало ли, я чего-то не заметил.


      1. ferocactus
        29.04.2019 15:03

        Ну, комментарии должны быть на английском и отвечать на вопрос «зачем?», а не «что?».
        RUN'ы надо объединить в один (один ли?).
        Исходники надо копировать последним шагом перед точкой входа? Или прямо монтировать их директорию снаружи?
        Вместо CMD, наверное, надо использовать ENTRYPOINT? Supervisor не нужен тогда, kubernetes сам справится. Но они хотят ещё ssh зачем-то, хотя Kubernetes позволяет и без них в контейнеры заходить. Да ещё passenger какой-то, что это вообще? Может тогда и правильно, что Supervisor.
        Базовый образ может оказаться слишком затратным по ресурсам и тогда придётся перейти на alpine или centos? Но возможно у них лишние сервера простаивают и это преждевременная оптимизация (как и всё остальное, кстати).
        Все компоненты и пакеты ставятся из внешних источников, а не со своих безопасных доверенных зеркал, это риск как обычной поломки, так и взлома.
        Версии всех внешних зависимостей (и базового образа в том числе) надо фиксировать.
        apt-get upgrade не нужен, актуальность пакетов базового образа — ответственность его автора.
        Кэши сделаны не для того, чтобы их чистить. Но я не знаю что это за кэши, возможно, тут это оправдано, хотя в таком случае надо отразить в комментарии.
        Наверное не всё верно и многое не замечено. Короче говоря, давайте вместе поможем Даше путешественнице исправить dockerfile.


        1. Akela_wolf
          29.04.2019 15:30

          init.sh, как следует из текста, запускает несколько процессов. Концепция докера: один контейнер — один процесс под конкретную задачу. Соответственно контейнер надо разбить на несколько.


        1. Magvai69 Автор
          29.04.2019 15:35

          Что не так? Последовательно:
          1. Очистка кэшей и тд сделано отдельным RUN, это приводит к созданию еще одного слоя, где эти данные удалены. Но, предыдущих слоях они остаются. Что бы это было ок, надо чистить в том же RUN. Правильно:

          RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor && \
          apt-get clean && \
          rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*


          В этом случае, в слое не будет лишних данных. Аналогично и с зависимостями ruby.

          2. Для выполнения RUN gem install bundler весь код не нужен. Достаточно скопировать:
          Gemfile и Gemfile.lock. Они редко меняются и в большинстве случаев слой будет браться из кэша.
          3. ssh там не нужен
          4. Supervicor там тоже не нужен, на выходе должно быть несколько контейнеров:
          С бэком и passenger в качестве бэкэнда.
          С демонами, по одному на каждый демон.

          Идеалогия Docker один контейнер, один процесс.
          5. Версии должны фиксироваться. Использовать тэг latest это плохо, так как каждый раз мы можем получить совершенно разные контейнеры с разным поведением.
          6. Желательно использовать не root в контейнере. По этому поводу, даже была недавно статья на хабр.
          7. Использовать большие образы, это плохая практика. Так как, чем больше базовый образ, тем больше в нем багов.

          Это из основного.


          1. ferocactus
            29.04.2019 15:44

            ssh не нужен, потому что можно коннектиться с помощью самого докера?


            1. Akela_wolf
              29.04.2019 15:50

              С помощью докера можно выполнить любую команду в контейнере. В том числе /bin/sh и получить локальный шелл с помощью которого делать все что хочется.


          1. berez
            29.04.2019 16:01

            Я как человек, нихрена не понимающий в слоях и суперви(з, к)орах, все-таки ожидал увидеть все это не среди комментариев, а в самой статье. И желательно — с более подробным разбором, на пальцах и «для полностью тупых» — что такое слой и почему в нем плодятся данные, что за Gemfile, почему не нужен ssh и т. п.
            Так у вас бы получилась хорошая, годная статья для новичков, которая им помогала бы освоиться с докером.
            Сейчас у вас получилась статья-байка для посвященных: почитать, посмеяться, сказать «да, так все и бывает!». Для новичка толку в такой статье маловато. А заставлять читать комментарии в поисках ответов на вопрос «что же не так?» — это такое изощренное издевательство над читателем.


            1. Magvai69 Автор
              29.04.2019 16:40

              > все-таки ожидал увидеть все это не среди комментариев, а в самой статье.
              Цель этой статьи была другая. Тут, совсем не преследовалась цель показать, как надо делать и уж тем более, объяснить почему так не надо делать. Разобрать все кейсы, все ошибки невозможно. Я постарался показать, наиболее распространённые ошибки, а кому это нужно, может посмотреть документацию, комментарии и множество статей и тем самым хорошо разобраться с данным вопросом.

              Если бы, статья содержала подробный разбор, это было бы не так полезно. ИМХО.


              1. baldr
                29.04.2019 16:47
                +2

                Ну вообще-то вам правильно указали — статья либо «для посвященных, посмеяться, поскольку они и так это знают», либо ни о чем.
                Для кого эта статья?


                1. Magvai69 Автор
                  29.04.2019 16:52
                  -1

                  Скажите, а этот стих, требует разъяснений?

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

                  Я думаю что нет. Так же и со статьей, если ты не понял, почему так не надо делать. То, стоит почитать соответствующий раздел документации.
                  Иначе мы так и будет жить в мире копипаста.


                  1. baldr
                    29.04.2019 17:21
                    +7

                    Если вы сравниваете со стихами Остера — может быть следует просто отправить всю статью в «Юмор»?

                    Вкратце вся статья выглядит так: «вот этот код — плохой. Почему плохой и какой будет хороший — выясните сами».


                  1. berez
                    29.04.2019 17:26
                    +2

                    Так же и со статьей, если ты не понял, почему так не надо делать. То, стоит почитать соответствующий раздел документации.

                    Какие именно «соответствующие»?

                    Чтобы понять, какие разделы читать, нужно сначала понять, в чем именно ошибка. А именно это вы почему-то оставили за рамками статьи.

                    Это для вас ясно, как божий день, что не надо много строчек RUN писать. А для новичка что так, что этак — одинаково выглядит. Именно поэтому новички и хреначат такой вот лапшекод. Ну и — спрашивается в задачнике — что для себя новичок полезного почерпнет из статьи, если плохие примеры в ней есть, а объяснений, почему они плохи — нет?

                    UPD: вот и первые ласточки: habr.com/ru/company/southbridge/blog/449944/#comment_20090634


          1. agmt
            29.04.2019 22:00

            А что не так с (5)? В этой статье предлагают фиксировать версии, а в соседней называют суицидниками тех, кто сидит на фиксированной WinXP (докер же, как я понял, будет доступен из внешнего мира?). Где та грань и в каких условиях проявляется?


            1. VolCh
              29.04.2019 22:53
              +1

              Желательно фиксировать версии в докерфайле, чтобы минимизировать количество ситуаций «ничего не знаю, у меня всё работает — проблема на вашей стороне». Часто это одна из основных целей внедрения докера.

              Это не значит, что не надо обновляться. Просто обновление происходить должно путём указания новой версии в докерфайле.


          1. prijutme4ty
            29.04.2019 22:44

            Можете объяснить или дать ссылку на объяснение, чем плохо использовать рута?


            1. Magvai69 Автор
              29.04.2019 22:52
              +1

              1. prijutme4ty
                29.04.2019 23:12

                Спасибо!


      1. ferocactus
        29.04.2019 15:54

        И всё-таки что было бы системным правильным решением junior'а?

        1. смена места работы (риск умереть с голода)
        2. спор до упора с senior'ом с привлечением остальной части команды (риск обзавестись личным врагом и потерять работу, и см. пункт 1)
        3. жалоба руководству на некомпетентность коллеги (риск стать проблемой, а не решением, goto пункт 1)
        4. молчаливое согласие, в надежде в будущем стать senior'ом и навести порядок (риск преждевременного выгорания или ухудшения здоровья)


        1. Magvai69 Автор
          29.04.2019 16:01

          На мой взгляд, тут было варианта:
          1. Пообщаться с ИИ, и попросить его помочь. Потом, предложить TeamLead уже готовое решение.
          2. За пару выходных разобраться самому и предложить TeamLead готовое решение.

          Вряд ли Lead настолько глуп, что откажется уже от готового решения. А если это и так, то продолжать потихоньку прокачивать свои знания и предлагать свои решения, более правильные. И как знать, может через полгода год, jinior станет Lead:)


      1. Clasen01
        29.04.2019 17:29
        +2

        На мой взгляд было бы здорово, если бы вы после каждого отдельно взятого плохого примера действий под спойлером кратко поясняли почему это плохо) а то я вот повелся на заголовок, с докером дел не имел и вообще не понял, как делать нужно, если не так, как в статье)


  1. hd_keeper
    29.04.2019 13:13
    +1

    А что не так с этим докер-файлом?
    image


    1. begemoth3663
      29.04.2019 13:23
      +3

      девляпс:
      curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -


      P.S. чего потом удивляться, что докерхаб ломанули? культура такая — не приучила мама в детстве грязное не брать в рот...


    1. Magvai69 Автор
      29.04.2019 14:41

      Да все отлично, как раз завтра, в конце рабочего дня, планируем его в прод выкатить)


      1. HPro
        29.04.2019 15:37

        Как ваши глаза поживают?)


        1. Magvai69 Автор
          29.04.2019 15:37

          Спасибо, уже лучше)


  1. baldr
    29.04.2019 15:19
    +1

    Нипонял, а где код из репозитория копируется в имедж?

    RUN git clone git@mainrepo.int.company.com:software/software.git ./


    А захардкоженные пароли в Dockerfile забыли что ли?

    Какие-то вредные советы слабаков…


    1. Magvai69 Автор
      29.04.2019 15:25

      Вот же, код копируется: COPY ./ /app

      Да, еще можно еще много, чего добавить. Например, установку systemd.


      1. TonyLorencio
        29.04.2019 15:40
        +2

        К примеру из поста следующее, очевидно, не подходит, но тем не менее:


        Для компилируемых языков до сих пор можно встретить, что в production-образе висит компилятор со всеми зависимостями.


        1. Magvai69 Автор
          29.04.2019 16:02

          Да, да, такое тоже встречаем периодически. И под go, видел контейнеры по 500Мб. Вместо, 2-3 мб.


          1. VolCh
            29.04.2019 22:57
            +1

            Под go сотню-другую мегабайт может занимать просто потому что используется универсальный базовый образ, та же ubunta. А задачи минимизации образа не стоит. Или есть из разряда «nice to have».


            1. TonyLorencio
              30.04.2019 09:23

              Под Go чаще можно увидеть в качестве базового образа alpine, или вообще образ собран с нуля (scratch), чем Ubuntu/Debian и прочее даже в минимальном образе.


              1. VolCh
                30.04.2019 09:48

                Где-то в паблике может и чаще («чтоб не позориться»?). Но кто знает, что творится в инхаус репозиториях :)

                По моему опыту, в «кровавом ентерпрайзе» не склонны утверждать предложения " давайте поменяем базовый образ всех наших контейнеров" только потому что «меньше места занимает». Вот принесите ТЭО типа «с текущим базовым образом мы тратим 1000$ в месяц, с новым будем тратить 200$, полный регресс-тест системы обойдётся в 10000$, срок окупаемости без учёта рисков 12,5 месяцев»


                1. gecube
                  30.04.2019 10:58

                  Вот принесите ТЭО типа «с текущим базовым образом мы тратим 1000$ в месяц, с новым будем тратить 200$, полный регресс-тест системы обойдётся в 10000$, срок окупаемости без учёта рисков 12,5 месяцев»

                  На самом деле это правильно… А вдруг — экономия на размере будет мифическая?


                  1. VolCh
                    30.04.2019 11:08

                    Экономия на размере будет, а вот экономии на деньгах может и не быть :) Особенно если учесть обучение разработчиков, девопсов и админов работе с новым базовым образом. Переписывание докерфайлов. Инциденты, когда внезапно в новом образе не оказывается привычных инструментов типа bash и из-за этого привычные пути диагностики и локализации перестают работать, а перед запуском никто о них не вспоминал.

                    Ну и собственно риски, что прикладное ПО или непосредственная среда его исполнения в каких-то случаях рассчитывает на одно поведение ОС и относимых к ней библиотек, а новом дистрибутиве оно другое.


                    1. TonyLorencio
                      30.04.2019 11:11

                      в новом образе не оказывается привычных инструментов типа bash

                      Но ведь докер совсем не об этом


                      1. VolCh
                        30.04.2019 11:20

                        docker exec о чём?


                        1. TonyLorencio
                          30.04.2019 11:37

                          То, что в одном контейнере можно запустить более одного приложения, в том числе и через docker exec, не значит, что так нужно делать


                          1. VolCh
                            30.04.2019 11:53

                            В каждое приложение добавлять функциональность coreutils и ко для мониторинга, диагностики и локализации проблем, какой-то шелл, чтобы это смотреть и т. д.?


                            1. TonyLorencio
                              30.04.2019 13:45
                              -1

                              У вас неправильное представление о контейнерах. Контейнер — не виртуальная машина. Один контейнер — одна задача.


                              Приложение должно уметь отправить некоторые метрики о себе наружу.


                              Чем это будет отлавливаться снаружи, кто из людей и как это будет смотреть и через какой шелл/веб-интерфейс/что-то ещё — уже совсем не является ответственностью этого приложения и этого контейнера.


                              1. VolCh
                                30.04.2019 15:00
                                +1

                                У меня нормальное практичное представление о контейнерах. Не «один контейнер — одна задача» (отправка метрик о себе наружу — это вторая задача, кстати), а ограничение прав процессов по принципу «всё что не разрешено явно — запрещено».


  1. baldr
    29.04.2019 15:45

    Да ну, до реальной жизни тут далеко…

    Василий: а как нам либу [libaname] устанавливать?
    Петр: а как ты ее себе на комп ставил?
    Василий: а я по инструкции «быстрой установки» ее с какого-то докер-репозитория стащил и запустил в докере локально
    Петр: хмм, ну ладно, давай пока так же сделаем, а потом разберемся как ее собирать. По-быстрому поставь там внутри контейнера еще один докер и там же имдж скопируй внутрь — быстро устанавливаться будет, нам девопсы еще спасибо скажут.


  1. ShashkovS
    29.04.2019 16:07

    Хм. Я могу ошибаться, но лучше так не делать в самом начале:
    # Копируем исходный код
    COPY ./ /app
    WORKDIR /app

    Это приводит к тому, что любое изменение в сорсах — и весь кеш в помойку.
    ИМХО, должно быть что-то такое:

    # Ой, не надо делать latest, ой сломается когда-нибудь что-нибудь из-за этого!
    FROM ubuntu:latest

    # Папка приложения
    WORKDIR /app

    # Обновляем список пакетов и обновляем их, устанавливаем пакеты и bundler
    RUN apt-get update && \
    apt-get upgrade && \
    apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor && \
    gem install bundler

    # Устанавливаем nodejs используется для сборки статики
    RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash -
    RUN apt-get install -y nodejs

    # Копируем Gemfile'ы
    COPY Gemfile* ./

    # Устанавливаем зависимости
    RUN bundle install --without development test --path vendor/bundle

    # Чистим за собой кэши
    RUN rm -rf /usr/local/bundle/cache/*.gem && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

    # Копируем исходный код
    COPY ./ ./

    # Запускаем скрипт, при старте контейнера, который запустит все остальное.
    CMD ["/app/init.sh"]


    1. baldr
      29.04.2019 16:12

      Там выше картинка со слоупоком была уже.
      Вы заголовок статьи пропустили, наверное.


      1. Magvai69 Автор
        29.04.2019 16:14

        Возможно, не только заголовок, но и всю статью


    1. Magvai69 Автор
      29.04.2019 16:13

      Да, вы правы, так делать не надо.

      Весь Dockerfile показывает, как делать не надо. На то, они и вредные советы;)


    1. ShashkovS
      29.04.2019 21:40
      +1

      (Не трогайте комментаторов моего сообщения, плз. Я сначала не заметил тег и написал комментарий. А потом понял и добавил PS. В результате комментарии от baldr и далее выглядят неуместно)


      1. baldr
        29.04.2019 22:21

        Да, теперь мой коммент выглядит грубо, сорри.


    1. jaroslavdextems
      30.04.2019 14:35

      А еще:
      1. сборку статики лучше сделать отдельно в предстоящих шагах CI, и в контейнер забрать готовую — Сборка отдельно \ продакшн отдельно

      2. скриптом запускать всё же как-то не камильфо, стартуем 1 процесс

      3. а точно ли нам нужен целый образ большой убунты? пока так не выглядит


  1. LuckySB
    29.04.2019 17:47
    +2

    Открываете свой Dockerfile, сравниваете с тем, что в статье.
    Если есть похожие места — пора срочно искать ИИ.


    1. ferocactus
      29.04.2019 17:59
      +3

      Искусственный интеллект?


      1. Cerberuser
        30.04.2019 04:22

        Который самозародится в этом докерфайле?


  1. limassolsk
    29.04.2019 19:30
    +4

    Спасибо, посмеялся от души узнав многие свои ошибки, которые делал вначале, а также пару, которые делал до сих пор. На проде контейнеры не используем, только на дев-машинах.
    Статья реально для тех кто уже «набил шишки», именно поэтому многие негодуют, что не было разбора ошибок. Отправлять всех кому непонятно, что здесь не так, читать документацию — по-моему не выход. Документации по докеру на неделю чтения и если бы вы описали проблемные места вашего докерфайла, то закрыли бы процентов 80% документации за 10 минут. В данном же виде статья годиться только для того, чтобы поделиться своей болью с другими бывалыми и посмеяться над новичками, но ни как не для образовательных целей. В таком случае вам действительно нужно указать тег «юмор».
    К понятию, что «так делать не стоит» многие новички могут прийти и самостоятельно, но на это уйдёт куча времени. Хорошо, что уже несколько комментаторов дали пояснения проблемных участков.

    Я тогда сначала скопирую Gemfile и Gemfile.lock, потом все поставлю, и потом уже копирую весь проект. Если Gemfile не меняется, слой возьмется из кэша.
    Вот эту ошибку я допускаю до сих пор, потому что в зависимостях у меня всего один пакет и установка происходит быстрее чем в моей голове успевает появиться мысль, что здесь что-то неоптимально и надо бы исправить. После прочтения комментариев, в которых «разжевали» почему так делать не стоит, я больше не буду наступать на эти грабли. Согласно вашему посылу, я из-за этого одного непонятного мне момента должен был идти в документацию по докеру и читать всё ещё раз. Но кого мы пытаемся обмануть? Я думаю после прочтения вашей статьи никто не пойдёт читать ещё раз документацию по докеру. Кроме докера каждый разработчик использует ещё кучу инструментов и терять столько времени на каждый из них — непозволительная роскошь.
    Большинство из нас и не собирается стать гуру в докере, достаточно всего лишь не совершать грубых ошибок.


    1. Magvai69 Автор
      29.04.2019 19:56
      -1

      Спасибо за развернутый комментарий.
      Да, я уже понял, что надо еще одну статью писать. В ближайшее время напишу и опубликую. К этой статье добавлю ссылку.


    1. gecube
      29.04.2019 21:32
      +2

      На самом деле половина бестпрактисов про написание докерфайлов — это компромиссы. И, как говорится, your mileage (experience) may vary. Например, взять четко указанные версии ПО. В тегах и в пакетах. С одной стороны — это гарантирует более повторяющиеся сборки, чем лупить везде latest. С другой стороны — выше нагрузка на разработчика. Вот давайте подумаем. Докерфайл — это по сути чертеж. В одном чертеже важно указывать материалы, а в каком-то месте материал может быть не принципиален. И стоит ли тогда тратиться на то, чтобы полностью все определять? Потребительские качества конечного изделия одинаковы. И ещё момент, что даже наличие четких тегов не гарантирует, что разработчик этого образа не перезапишет его. Единственное спасение — пин по sha, но кто его использует?
      С вендорами (go, nodejs etc.) примерно аналогичная история.


      Поэтому самое главное — научить видеть подводные камни в каждом из способов/решений, а не рассуждать догмами.


      1. baldr
        29.04.2019 22:22

        Да, тем более что через даже полгода четко указанные версии могут внезапно пропасть из репозиториев (привет npm!).


  1. shurshur
    30.04.2019 03:28
    +1

    RUN wget гиг_данных
    RUN rm гиг_данных

    создаёт два fslayer, в одном из которых гиг_данных…

    В итоге удалять те же кэши apt в указанном Dockerfile нет никакого смысла, ничего не сэкономишь. Весьма распространённая ошибка.


  1. xPomaHx
    30.04.2019 05:34

    Можете пояснить про COPY ./ /app
    Не совсем понял почему это не верно и как нужно делать правильно и почему.
    И еще офтоп столкнулся с проблемой, верно ли что микросервисы сами должны быть в подвешенном состоянии если они требуют зависимости которые еще не подняты, например база запускается позже чем приложение, значит на уровне приложения нужно создавать какой то чекер раз в 10 сек и ждать базу?


    1. Akela_wolf
      30.04.2019 06:16

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

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


    1. gecube
      30.04.2019 07:12

      микросервисы сами должны быть в подвешенном состоянии если они требуют зависимости которые еще не подняты, например база запускается позже чем приложение, значит на уровне приложения нужно создавать какой то чекер раз в 10 сек и ждать базу?

      Нет, это в общем случае неверно.
      Есть две крайности:


      1. Сервис, который при условии отсутствии своей зависимости падает всегда (и потом рестартуется)
      2. Сервис, который вечно ждёт своей зависимости ( и при этом не падает с исключением, а в логах пусто).
        Обе ситуации очень мерзкими могут быть. Ещё может быть третий вариант — например, зависимость "ушла" во время работы Вашего сервиса (ну, там сеть моргнула, автоскейлинг не сработал или что-то ещё). Вот и думай — как лучше. Зависит от конкретной ситуации.


    1. VolCh
      30.04.2019 09:59

      Правильно после установки глобальных системеных зависимостей (всякие apt install, wget | bash -):


      COPY Gemfile* ./
      RUN bundle install --without development test --path vendor/bundle && rm -rf /usr/local/bundle/cache/*.gem
      COPY  ./ /app

      При таком подходе bundle install не будет запускаться на каждое изменение исходников. Только если изменился Gemfile или Gemfile.lock будет обновляться vendor/bundle. Также и кэш бандлера в образ не попадёт.


      Те же методы работают и для других менеджеров заисимости, таких как npm, yarn, composer и других базирующихся на паре или одном файле со списком зависимостей


    1. Magvai69 Автор
      30.04.2019 10:55
      +1

      Тут, дело не столько в микросервисах, подобный кейс бывает и при монолите.
      Тут, надо учесть 2 момента:
      1. Автоматическое переподключение приложения к БД.
      2. Helth check который отдает данные по работе приложения, в частности с учетом доступности/недоступности БД. И вешать его на мониторинг.


  1. riv1329
    30.04.2019 13:21

    прочитал статью и подумал, хорошо бы этим докером никогда не пользоваться. Взять хотя бы эти RUN-ы. Более корявого и контр-интуитивного способа управления слоями нельзя придумать. Неужели нельзя было в каком-то явном виде сделать управление этими слоями? Присвоить им какой-то идентификатор что ли? Заставлять писать команды в 1 строку — что может быть хуже? А если длинны строки не хватит? Там, наверняка ограничение в 255 символов. Впечатление костыля из костылей.


    1. gecube
      30.04.2019 13:33

      А если длинны строки не хватит? Там, наверняка ограничение в 255 символов.

      что за глупости? Можно и multi-line делать. Проблем-то?
      Ограничений на длину выполняемой команды в блоке RUN, кстати, я не увидел.
      Более корявого и контр-интуитивного способа управления слоями нельзя придумать. Неужели нельзя было в каком-то явном виде сделать управление этими слоями?

      не используйте docker build. Есть же kaniko и buildah.


    1. VolCh
      30.04.2019 15:03

      Dockeкfile лишь самое популярное средство построения образов. Их много разніх, начиная от ручного снятия снэпшотов контейнера