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

    В комментариях к моей статье Docker: вредные советы было много просьб объяснить, чем так ужасен описанный в ней Dockerfile.


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



    Сейчас разберемся, что не так с этим Dockerfile.


    Итак, прошла неделя.


    Dev Петя встречается в столовой за чашкой кофе с Ops Игорем Ивановичем.


    П: Игорь Иванович, вы сильно заняты? Хотелось бы разобраться, где мы напортачили.


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


    1. Идеология Docker: один контейнер — один процесс.
    2. Чем меньше контейнер, тем лучше.
    3. Чем больше берется из кэша, тем лучше.

    П: А почему в одном контейнере должен быть один процесс?


    ИИ: Docker при запуске контейнера отслеживает состояние процесса с pid 1. Если процесс умирает, Docker пытается перезапустить контейнер. Допустим, у вас в контейнере запущено несколько приложений или основное приложение запущено не с pid 1. Если процесс умрет, Docker об этом не узнает.


    Если больше нет вопросов, показывай ваш 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/* 
    RUN rake assets:precompile
    # Запускаем скрипт, при старте контейнера, который запустит все остальное.
    CMD ["/app/init.sh"]

    ИИ: Ох, давай разбираться по порядку. Начнем с первой строчки:


    FROM ubuntu:latest

    Вы берете тэг latest. Использование тэга latest приводит к непредсказуемым последствиям. Представьте, мейнтейнер образа собирает новую версию образа с другим списком ПО, этот образ получает тэг latest. И ваш контейнер в лучшем случае перестает собираться, а в худшем вы ловите баги, которых ранее не было.


    Вы берете образ с полноценной ОС с большим количеством ненужного ПО, что раздувает объем контейнера. И чем больше ПО, тем больше дырок и уязвимостей.


    Вдобавок чем больше образ, тем больше он занимает места на хосте и в registry (ты же где-то хранишь образы)?


    П: Да, конечно, у нас registry, вы же его и настраивали.


    ИИ: Так, о чем это я?.. Ах да, объемы… Так же растет нагрузка на сеть. Для единичного образа это незаметно, но когда идет непрерывная сборка, тесты и деплой, это ощутимо. А если у тебя нет God’s mode на AWS, тебе еще и космический счет прилетит.


    Поэтому нужно выбирать наиболее подходящий образ, с точной версией и минимумом ПО. Например, возьми: FROM ruby:2.5.5-stretch


    П: О, понятно. А как и где посмотреть имеющиеся образы? Как понять, какой мне нужен?


    ИИ: Обычно образы берутся с докерхаба, не путай с порнхабом :). Для образа обычно существует несколько сборок:
    Alpine: образы собраны на минималистичном образе Linux, всего 5 Мб. Его минус: он собран с собственной реализацией libc, стандартные пакеты в нем не работают. На поиск и установку нужного пакета уйдет немало времени.
    Scratch: базовый образ, не используется для сборки других образов. Он предназначен исключительно для запуска бинарных, подготовленных данных. Идеально подойдет для запуска бинарных приложений, которые включают в себя все необходимое, например go-приложения.
    На базе какой-либо ОС, например Ubuntu или Debian. Ну тут, думаю, пояснять не надо.


    ИИ: Теперь нам нужно поставить все доп. пакеты и почистить за собой кэши. И сразу можно выкинуть apt-get upgrade. Иначе при каждой сборке, несмотря на фиксированный тэг базового имиджа, будут получаться разные образы. Обновление пакетов в образе — это задача мейнтейнера, она сопровождается изменением тэга.


    П: Да, я пробовал это сделать, у меня получилось так:


    WORKDIR /app
    COPY ./ /app
    
    RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \
        && apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor nodejs \
        && gem install bundler \
        && 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/*

    ИИ: Неплохо, но тут тоже есть, над чем поработать. Смотри, вот эта команда:


    RUN rm -rf /usr/local/bundle/cache/*.gem \
        && apt-get clean  \
        && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*  

    … не удаляет данные из итогового образа, а лишь создает дополнительный слой без этих данных. Правильно так:


    RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \
        && apt-get -y install libpq-dev imagemagick gsfonts nodejs \
        && gem install bundler \
        && bundle install --without development test --path vendor/bundle \  
        && rm -rf /usr/local/bundle/cache/*.gem \
        && apt-get clean  \
        && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

    Но это еще не все. Что у вас там, Ruby? Тогда не надо в начале копировать весь проект. Достаточно скопировать Gemfile и Gemfile.lock.


    При таком подходе bundle install не будет выполняться на каждое изменение исходников, а только если изменился Gemfile или Gemfile.lock.


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


    Ну и наконец, помнишь, в начале я говорил про идеологию Docker «один контейнер — один процесс»? Это означает, что supervisor не нужен. Так же не стоит устанавливать systemd, по тем же причинам. По сути, Docker сам является supervisor. И когда ты пытаешься запускать в нем несколько процессов, это как в одном процессе supervisor запускать несколько приложений.
    При сборке вы сделаете единый образ, а потом запустите нужное количество контейнеров, чтобы в каждом работал один процесс.


    Но об этом позже.


    П: Кажется, понял. Смотрите, что получается:


    FROM ruby:2.5.5-stretch
    
    WORKDIR /app
    COPY Gemfile* /app
    
    RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \
        && apt-get -y install libpq-dev imagemagick gsfonts nodejs \
        && gem install bundler \
        && bundle install --without development test --path vendor/bundle \  
        && rm -rf /usr/local/bundle/cache/*.gem \
        && apt-get clean  \
        && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 
    
    COPY . /app
    RUN rake assets:precompile
    
    CMD ["bundle”, “exec”, “passenger”, “start"]

    А запуск демонов переопределим при запуске контейнера?


    ИИ: Да, все верно. Кстати, можно использовать как CMD так и ENTRYPOINT. А разобраться, в чем отличие, это тебе домашнее задание. На эту тему на Хабре есть хорошая статья.


    Так, давай дальше. Ты качаешь файл для установки node, но при этом нет никакой гарантии, что в нем будет то, что тебе нужно. Надо добавить валидацию. Например, так:


    RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x \
        && echo "958c9a95c4974c918dca773edf6d18b1d1a41434  setup_9.x" | sha1sum -c - \
        &&  bash  setup_9.x \
        && rm -rf setup_9.x \
        && apt-get -y install libpq-dev imagemagick gsfonts nodejs \
        && gem install bundler \
        && bundle install --without development test --path vendor/bundle \  
        && rm -rf /usr/local/bundle/cache/*.gem \
        && apt-get clean  \
        && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

    По контрольной сумме ты сможешь проверить, что скачал верный файл.


    П: Но если файл изменится, то сборка не пройдет.


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


    П: Спасибо. Получается, итоговый Dockerfile будет выглядеть так:


    FROM ruby:2.5.5-stretch
    
    WORKDIR /app
    COPY Gemfile* /app
    
    RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x \
        && echo "958c9a95c4974c918dca773edf6d18b1d1a41434  setup_9.x" | sha1sum -c - \
        &&  bash  setup_9.x \
        && rm -rf setup_9.x \
        && apt-get -y install libpq-dev imagemagick gsfonts nodejs \
        && gem install bundler \
        && bundle install --without development test --path vendor/bundle \  
        && rm -rf /usr/local/bundle/cache/*.gem \
        && apt-get clean  \
        && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 
    
    COPY . /app
    RUN rake assets:precompile
    
    CMD ["bundle”, “exec”, “passenger”, “start"]

    П: Игорь Иванович, спасибо за помощь. Мне уже пора бежать, надо еще 10 коммитов за сегодня сделать.


    Игорь Иванович, взглядом остановив торопливого коллегу, делает глоток крепкого кофе. Поразмыслив несколько секунд об SLA 99.9% и коде без багов, он задает вопрос.


    ИИ: А где вы логи храните?


    П: Конечно, в production.log. Кстати да, а как мы без ssh получим к ним доступ?


    ИИ: Если вы их оставите в файлах, решение для вас уже придумали. Команда docker exec позволяет исполнить любую команду в контейнере. Например, вы можете сделать cat для логов. А использовав ключ -it и запустив bash (если он установлен в контейнере), вы получите интерактивный доступ к контейнеру.


    Но хранить логи в файлах не стоит. Как минимум это приводит к бесконтрольному росту контейнера, логи же никто не ротирует. Все логи нужно кидать в stdout. Там их уже можно посмотреть с помощью команды docker logs.


    П: Игорь Иванович, а может вынести логи в смонтированную директорию, на физическую ноду, как данные пользователей?


    ИИ: Хорошо, что вы не забыли вынести данные, загруженные на диск ноды. С логами так тоже можно, только не забудь настроить ротирование.
    Все, можешь бежать.


    П: Игорь Иванович, а посоветуйте, что почитать?


    ИИ: Для начала прочитай рекомендации от разработчиков Docker, вряд ли кто-то знает Docker лучше них.


    А если хочешь пройти практику, сходи на интенсив. Ведь теория без практики мертва.

    Southbridge
    283,84
    Обеспечиваем стабильную работу серверов
    Поделиться публикацией

    Похожие публикации

    Комментарии 75

      0
      docker build --squash
        0

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

        +16
        Мне одному кажется что простыни
        ```
        RUN && && && && && && && && && &&
        ```
        явно говорят о проблемах с изначальным дизайном?
          0
          Это не обязательно говорит о проблемах. Часто надо настроить окружение для работы, и не всегда можно найти идеально подходящий образ. Вот и приходится брать наиболее близкий к тому, что надо, и докидывать туда недостоющие компоненты.
          Если не нравятся простыни, то можно вынести их в отдельный образ и залить на докерхаб)) И рабочий докерфайл будет более чистым.
            +2
            Ну или хотя бы в свой registry
              +6
              Я думаю, что имелось в виду, что это костыль для управления слоями.
              При правильно дизайне управление слоями было бы реализовано как-то по другому, например:

              LAYER 1
              RUN…
              RUN…
              COPY…

              LAYER 2
              RUN…
              RUN…
              COPY…
                +4
                Это говорит о проблемах. Никто же не пишет код `a.b() && c.d()` кроме редких случаев. И то есть апологеты, которые говорят о разбивке этих случаев тоже ради дебага.

                Вообще скваш должен решать такую проблему. Но потом начинають вылазить артефакты при оптимизации размера образа. Или версионности.

                На самом деле сам подход когда докер файл становится баш файлом с привкусом докера — уже говорит о многом.
                +2
                Там и в самой это простыне ад. curl, apt-get, gem install, bundle install… Вспоминается башорг. Только в случае докера это почему-то считается нормальным.
                +1
                Да что уж там, сама по себе возможность легко получить работающее решение простым, понятным и неправильным способом − это уже проблема с дизайном.
                  0
                  Да, и эти проблемы уже решены
                  docs.docker.com/develop/develop-images/multistage-build
                  0
                  Всё хорошо до просмотра логов. Нельзя пускать девов на ноды, пусть в сборщике логов их смотрят.
                    +2
                    Но, есть же и локальный запуск, на компе дева.
                    Но, в общем я полностью согласен. Но, это бы удлинило статью. И немного, выходит за область рассматриваемой теме.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      +2

                      А как надо?

                        +1
                        Тут не «завязывается на glibc», а собрано для glibc. Просто так взять бинарник для glibc и запустить на musl или практически любой другой реализации libc, не нацеленные на совместимость с glibc (всякие eglibc в расчёт не берём) не выйдет.
                        +3
                        FROM ubuntu:latest


                        Использование тэга latest приводит к непредсказуемым последствиям.

                        а вот устанавливать пакеты последних версий это уже нормально, да?


                        apt-get -y install libpq-dev imagemagick gsfonts nodejs
                          +2
                          Ну по факту, да пакеты тоже лучше ставить определенной версии. Тут вы правы.
                          +3
                          Вы берете тэг latest. Использование тэга latest приводит к непредсказуемым последствиям.

                          Т.к. все равно есть свой registry, то можно положить конкретную версию базового образа туда и сделать так:


                          ARG registry=myregistry.exampe.com
                          FROM $registry/nginx:latest

                          В итоге мы сами себе мейнтейнер и сами решаем какая у нас версия текущая.


                          Плюсы:


                          1. Даже если кто-то как-то подменит версию в докерхабе на версию с бекдором нас это не затронет.
                          2. Если нужно выкатить срочный патч для уязвимости — обновляем версию в нашем registry и пересобираем образы, а не бегаем по всем докерфайлам и обновляем версии руками.
                          3. За счет ARG=registry можно локально собрать контейнер с другой версией не меняя докерфайла. Качаем новую версию, тегаем ее local/nginx:latest и собираем с registry=local.

                          Минусы:


                          1. Нужно очень внимательно следить за состоянием своего registry — потенциально любое обновление затрагивает все.
                          2. В идеале нужно хранить историю тегов — в какой момент и на какой образ указывал.

                          Ну на то мы и мейнтейнеры, чтобы за этим следить. В идеале перед переназначением latest нужно собрать все контейнеры с новой версией и прогнать тесты. Например, заведя еще один registry next-release или принимая тег в качестве аргумента.

                            +1
                            Даже в случае своего registry, я не рекомендовал использовать тэг latest.

                            Допустим, базовый образ у вас используется в 10 микросервисах. Как только вы делаете новую версию базового образа, вам сразу же надо перетестировать все 10 микросервисов.
                              0

                              Ну так сначала тестируем, а потом обновляем:


                              1. T минус месяц — дорогие разработчики, через месяц дефолтной станет версия Java 10.0.500. В ней такие-то изменения, из которых вот эти два потенциально могут что-то сломать. Мы собрали ваши образы с этой новой версией и прогнали тесты, посмотреть результаты можно тут. Если вы хотите переехать на новую версию уже сейчас — поменяйте в своем пайплайне arg registry на новый.
                              2. Раз в неделю-две шлем напоминания с возможностью нажать кнопку "Я уже проверил, у меня все ок". Особенно надоедаем тем, у кого тесты на новой версии падают.
                              3. Т минус день — напоминаем еще раз.
                              4. Т — обновляем версию, снова все тестируем и всем напоминаем.
                              5. Если все сделано по уму, то у ленивых жоп падает сборка или тесты, в прод ничего не уезжает. Ленивые жопы не могут релизить свои новые фичи и начинают жаловаться. Тыкаем их носом в 5 предупреждений и инструкцию по переходу, они фиксят.

                              Само собой если обновление серьезное, то не месяц даем, а больше.


                              Зато если прилетела критическая уязвимость в каком-то софте, мы ее патчим (и только ее), обновляем базовый образ и запускаем стандартный пайплайн build-test-release-deploy для всего. Если что-то упало — оно не деплоится и срочно фиксится. В случае же с фиксированной версией придется подменять образ не меняя тега, а это уже моветон.

                                +1
                                А зачем так усложнять? Можно же, базовый образ собирать с версией и делать рассылку, что появилась новая версия. Кому надо протестирует и обновит у себя версию тэга в dockerfile.
                                  +3
                                  Нельзя. Потому что все забьют и никто ничего не будет обновлять.
                                    0
                                    Я не специалист — а можно старую версию из registry удалить?
                                    Разумеется, после письменного уведомления.
                                      0

                                      Если наш registry, то конечно можно. С чужим — зависит от того, поддерживают ли они это или нет.

                                        0
                                        Ну тогда и без проблем — SLO не распространяется на deprecated версии, которые, к слову, в любой момент могут быть удалены из registry из соображений целесообразности.

                                        Наша команда с радостью поможет (покажет документацию) с апгрейдом!
                                        +1

                                        Можно.
                                        Но, по моему мнению, лучше донести до разработчиков необходимость и ценность своевременного обновления.
                                        И это возможно, проверено))


                                        Мне, не нравиться, любые механизмы, где что-либо происходит насильно.

                                          +1
                                          > Мне, не нравиться, любые механизмы, где что-либо происходит насильно.

                                          Это по-человечески понятно, но иногда консенсус невозможен — у какой-то команды релиз, им какая-то вещь нужна ну прям счас, и непременно на этой версии, а на апгрейд времени нет. И не было. Может, в конце года, после запуска. И вообще, у них приоритеты, что вы тут со своими апгрейдами, мы тут прибыль делаем (и дальше по списку).

                                          Кстати, почитайте про liberum veto, к слову. Для общего, так сказать, развития.
                                      0

                                      Можно конечно, но тогда второй сценарий с критическим патчем становится намного сложнее. Нам придется искать все ссылки на непоследнюю версию и обновлять их. Если у кого-то используется лохматая версия при этом сильно возрастают шансы все поломать.


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


                                      Если же у нас ситуация когда non-latest в докерфайле это временное исключение и мы периодически скрываем старые версии из registry, то у нас все разработчики сидят более-менее на одной и той же довольно новой версии и мы можем оперативно выкатывать патчи когда надо.

                                    +1

                                    Кажется, докеру тоже нужны свои лок-файлы...

                                  +1

                                  Блин, я вот читаю такие статьи и вспоминаю, что совсем недавно вот совсем совсем недавно говорили, что цепочки вызовов команд это плохо, потому что это всё рассчитано только на успешный путь выполнения и для конфигурации системы нужно использовать ansible и идемпотентные скрипты декларативного характера.


                                  И тут опять шелл скрипт для конфигурации системы, только в докере. Что же с нами стало? Как так вышло то? Почему это опять хорошо?

                                    +1

                                    История циклична. Уверен, рано или поздно придумают "ansible для Dockerfile":)

                                      +1
                                      Вы не поверите… docs.ansible.com/ansible-container
                                        +1

                                        Спасибо. Прикольно. Интересно, надо будет попробовать и протестировать.

                                        +1
                                        А ведь с момента вброса идеи прошел всего час…
                                      +1

                                      А тут разные ниши — Docker предполагает сборку нового артефакта, а не изменение существующего сервиса. Если что-то в цепочке падает — это повод сборке немедленно остановиться, заорать и заставить пересмотреть процесс сборки до того, как этот артефакт будет выкачен.

                                        0
                                        В теории я полностью согласен. На практике — фиг отдебажеш. Поэтому в 99% (а может и больше девяток) — сначала перезапустят, а потом будут проверять.

                                        Тем самым, баги сборки годами сидят на своих местах и никому нет дела. До тех пор пока они не просто ломают вборку — а ломают все сборки постоянно и блочат вообще все.
                                          0

                                          Можно конкретный пример, как правильный Ansible помог бы отдебажить такой плавающий баг в сравнении с правильным Docker-ом?

                                            0

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

                                              0
                                              Докер построен вокруг принципа отказоустойчивости и переиспользования образов. Если надо сегодня не релизить — значит не будем релизить и пережить на старом образе. А релизить с заведомо известным багом — подход известной картинки «хк, хк и в продакшен»
                                              0
                                              первое, ансибл все централизованно логирует
                                              второе, его доки просят писать каждую команду отдельной я не гонять простыню `RUN && &&`
                                                0

                                                Можно пример бага, пожалуйста?

                                                  0
                                                  деталей уже не помню
                                                  Связано было с разными зеркалами дебиана расположенными в порядке увеличения дальности. Одно из зеркал содержало битый пакет.

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

                                                  Еще было про пим (питон) пакеты. Но там когда начали разбираться начала вылазить такая дичь, что мы не пришли к общему мнению в чем была проблема
                                                    +1

                                                    Как ансибл бы решил проблему с битым пакетом в одном из зеркал?


                                                    Что значит "не успевал вытянуть зависимость" — у вас ограничение по времени на сборку докера? Или (фу-фу) зависимости подтягиваются в момент запуска контейнера? Как Ansible решил бы аналогичную проблему?

                                                      0
                                                      ансибл не решает ни одну из проблем.

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

                                                      Докер строился с идеей — все сбилдится на ура, давайте решать другие проблемы. Если надо что-то починить — делай это вне докера.
                                                        0

                                                        Ну вот в корневом комментарии противопоставляют Docker и Ansible — что-де опять шелл-скрипты вместо идемпотентого Ansible.


                                                        Вопрос в том, зачем здесь идемпотентность и логируемость и как она в конкретном случае иммутабельного артефакта, собираемого на CI, помогает решить проблему.


                                                        Пока ветка выглядит примерно так:


                                                        • Ansible лучше
                                                        • чем лучше?
                                                        • чем Docker
                                                          Не надо так.
                                                          0

                                                          По мне, тут вопрос не в том, что лучше ansible или Docker.
                                                          Это, все-таки разные инструменты. И вопрос, похож на: а что лучше помидоры или сотовый телефон.


                                                          Тут, идея, что bash портянки такое себе, и было бы круто собирать docker через yml.

                                                            0
                                                            было бы круто
                                                            Почему?

                                                            С мутабельными серверами и зоопарком систем — да, круто — не надо следить за исходным состоянием каждого индивидуального сервера.


                                                            Что даст дополнительный уровень абстракции в докерфайле, когда состояние гарантировано (если не делать глупости типа использования latest и apt-get update)?


                                                            Что будет точно хуже — дополнительная зависимость от версий плейбуков и отвалившееся кеширование слоев (как раз из-за того, что порядок действий не будет задан явно).

                                                            +1
                                                            Мы наблюдаем уникальную ситуацию. Это действительно две разные тулы, которые должны быть просто разными, как теплое и мягкое. Но они сделаны так, что используя одну вторую уже нельзя использовать. Точнее — конечно можно, но это будет полностью нарушать философию использования первой тулы.
                                                0

                                                Что именно там перезапустят при упавшей сборке?

                                            +2
                                            в debian-based имеджах apt-get install сам за собой чистит, отдельной команды не нужно
                                              +1
                                              Сделал тесты, вы правы.
                                              Но, все же, разработчики образа советуют выполнять: rm -rf /var/lib/apt/lists/*
                                                0
                                                lists — не то же самое, что и файлы пакетов, обновляются по apt-get update и их наличие требуется для последующего apt-get install, который с точки зрения apt может прилететь когда угодно, поэтому никто их удалять автоматически не будет.
                                              –7
                                              Класс! Появилась ещё одна прослойка (я о Docker), а проблемы остались всё те же (тупенькие разрабы не вникающие в детали, потому что некогда разбираться)…
                                              Кто-нибудь расскажет мне вообще для чего docker нужен-то?.. Это реторический вопрос админа старовера не нужно отвечать. Мне нужно просто выговориться.
                                                0

                                                Серьезно, у админа есть вопрос для чего нужен Docker? А что вы администрируете если не секрет?
                                                Сколько у вас приложений? Предположу что Windows стек. Какой свежести у вас дистрибутивы?

                                                0

                                                Не уверен, что есть такая практика для ruby, но для дотнет обычно в одном образе билдят, а во втором запускают, предварительно скопировав артефакты:


                                                FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build
                                                WORKDIR /app
                                                
                                                # copy csproj and restore as distinct layers
                                                COPY *.sln .
                                                COPY aspnetapp/*.csproj ./aspnetapp/
                                                RUN dotnet restore
                                                
                                                # copy everything else and build app
                                                COPY aspnetapp/. ./aspnetapp/
                                                WORKDIR /app/aspnetapp
                                                RUN dotnet publish -c Release -o out
                                                
                                                FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime
                                                WORKDIR /app
                                                COPY --from=build /app/aspnetapp/out ./
                                                ENTRYPOINT ["dotnet", "aspnetapp.dll"]

                                                Не удобнее ли будет поступить так же, что бы не вычищать образ?

                                                  +1
                                                  Для ruby, так не делают, а вот например для go, это заходит хорошо. Да и для любого языка, где на выходе вы получаете исполняемый файл.

                                                  Так же, такой подход, хорош когда надо скопировать статику на фронт.
                                                    0

                                                    Для тех случаев, когда продакшн-образ содержит только самое необходимое, также встречал вариант, когда существуют как минимум два образа:


                                                    1) минимальный — для запуска продакшна
                                                    2) с компилятором/исходниками/полным рантаймом/дебаггером (ненужное в зависимости от языка — вычеркнуть) — для локальной разработки

                                                      0

                                                      На первом образе заодно можно тесты гонять и не тащить их вместе с зависимостями в прод.


                                                      Для фронтенда подход вообще замечательный, т.к. для сборки JS/CSS нужно одно окружение (Node, Babel, Webpack и т.п.), а в проде совсем другое.

                                                        +1
                                                        Для go у меня был такой вот Dockerfile:

                                                        FROM scratch
                                                        MAINTAINER Developer <developer@company.example>
                                                        COPY binary-artifact /
                                                        ENTRYPOINT ["/binary-artifact"]
                                                        

                                                        Сам `binary-artifact` возникал в пайплайне bitbucket.
                                                      +2
                                                      Тут, много обсуждали тэг latest. У него, есть еще одна особенность. Docker его кэширует, так же как и любой другой тэг. Как следствие, если вы соберете образ в двух местах с разными версиями кэша, то получите разные образы.
                                                        0

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


                                                        Теоретически это может быть проблемой и с фиксированной версией, но намного менее вероятно.

                                                        +3
                                                        Наконец-то Docker здорового человека…
                                                          0
                                                          Ну и наконец, помнишь, в начале я говорил про идеологию Docker «один контейнер — один процесс»? Это означает, что supervisor не нужен. Так же не стоит устанавливать systemd, по тем же причинам. По сути, Docker сам является supervisor. И когда ты пытаешься запускать в нем несколько процессов, это как в одном процессе supervisor запускать несколько приложений.


                                                          У меня есть проект на node.js в Docker. Он запускается через pm2. Можно ли выкинуть pm2?
                                                            0

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

                                                              +2

                                                              Не можно, а нужно.
                                                              Так же, как и в случае с супервизором, docker выполняет роль pm2

                                                              0
                                                              Подводя итог пары веток, можно сказать что весь кусок 'apt… ' должен быть не просто отдельным слоем — я отдельным билд образом.

                                                              Без этого — текущая статья является такой же вредной как и первая.
                                                                +1

                                                                Обоснуете?

                                                                  0
                                                                  Да как бы треть написанных комментариев относится к этой теме.

                                                                  Вы написали образ так, что в среднем раз в неделю он будет менятся если запустить. То есть на каждое добавление пакета в ноду или руби — практически новая виртуалка. Вместо обновления последних пару слоев (пакетный слой + кодовый слой). То есть — переиспользование и экономия места локально и в регистре стремтся к нулю.
                                                                    +1

                                                                    До тех пор, пока не пересобирать образ специально локально или в registry, меняться он не будет.

                                                                      0
                                                                      Вы игнорируете предложение про обновление пакетов. Пакеты обновляются. Я бы сказал в среднем раз в неделю. Не обновляются они только на древних проектах где докером и не пахнет. То есть образ надо обнавлять вместе с разработкой не реже чем раз в неделю.

                                                                      А конкретно этот образ написан так — что он обновляется весь. А не минимально возможная часть.
                                                                        +1

                                                                        Не соглашусь с Вами. Базовый образ уменьшает гибкость, приводит к сложностям при необходимости внести изменения.
                                                                        Единственное, что стоит сделать, это указать версию пакетов.
                                                                        Так же, слой будет браться из кэша, до момента, пока команда RUN не будет изменена.

                                                                          0
                                                                          > Так же, слой будет браться из кэша, до момента, пока команда RUN не будет изменена.

                                                                          Я добавил пакет в руби. Я хочу пресобрать образ чтобы новый пакет был. Что я должен изменить в ране? Ничего. Что в Вашем случае, что в моем.

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

                                                              Самое читаемое