» » » Как разделить окружение для сборки и запуска сервиса в Docker сегодня и как это cделать завтра

 

Как разделить окружение для сборки и запуска сервиса в Docker сегодня и как это cделать завтра

Автор: admin от 2-05-2017, 06:15, посмотрело: 35

Как разделить окружение для сборки и запуска сервиса в Docker сегодня и как это cделать завтра


Большинство из нас уже давно научилось готовить Docker и используют его на локальных машинах, на тестовых стендах и на боевых серверах. Docker, который недавно превратился в Moby, прочно вошел в процессы доставки кода до пользователя. Но best practice работы с контейнерной виртуализацией и, в частности, с Docker вырабатываются до сих пор.


Как это было


В начале становления Docker как основного инструмента изоляции процессов, многие использовали его аналогично использованию виртуальных машин. Подход был максимально прост: устанавливаем все необходимые зависимости в образ (Docker Image), там же билдим всё, что должно билдиться а что не должно двигаем и билдим, получаем артефакт сборки и запекаем всё это в итоговый образ.


Такой подход имеет явные недостатки: софт, который нужен для сборки, не всегда нужен для работы, например, для сборки программы на C++ или Go понадобится компилятор, но полученный бинарник можно запускать уже без компилятора. При этом софт, необходимый для сборки, может весить намного больше, чем полученный артефакт.
Второй недостаток вытекает из первого: больше софта в итоговом образе — больше уязвимостей, а значит, мы теряем в безопасности наших сервисов.


Актуально сегодня


Сегодня устоявшейся практикой является отделение образа для сборки от образа для запуска.


Выглядит и используется это примерно так:



  • Все необходимые для сборки сервиса зависимости мы ставим внутри build.Dockerfile и собираем, так называемый, buildbox-image из этого файла.
    # Флаги:
    #
    # -f — название Dockerfile, который будет использоваться для сборки образа (в нашем случае "build.Dockerfile")
    # -t — название образа, который будет получен после билда (в нашем случае "buildbox-image")
    #
    docker build -f build.Dockerfile -t buildbox-image .

  • Теперь используем buildbox-image для сборки сервиса. Для этого при запуске прокидываем внутрь контейнера исходники и запускаем сборку. (В примере запуск сборки происходит командой make build)
    # Флаги:
    #
    # --rm — удалить контейнер после завершения операции
    # -v — прокидывает текущую директорию в директорию "/app" внутри контейнера
    #
    docker run --rm -v $(pwd):/app -w /app buildbox-image make build

  • Получив артефакт сборки, например в $(pwd)/bin/myapp, мы можем просто запечь его внутрь образа с минимальным количеством софта. Для этого рядом с build.Dockerfile кладем Dockerfile, который и будет использоваться для запуска сервиса на бою. Выглядеть этот Dockerfile может так:
    FROM alpine:3.5
    COPY bin/myapp /myapp
    EXPOSE 65122
    CMD ["/myapp"]


  • Подход с разделением Dockerfile хорошо себя зарекомендовал, но само разделение — это довольно рутинная и не всегда приятная задача, поэтому идеи над упрощением процесса появились довольно давно.


    Что станет стандартом завтра?


    Об идее build-stages внутри Dockerfile я услышал от ребят из Grammarly. Они давно реализовали стадии сборки в фасаде над Docker'ом и назвали его Rocker. Но в самом Docker Engine такой функциональности не было.


    И вот, в Docker наконец смержили пулл-реквест, который реализует стадии сборки (https://github.com/moby/moby/pull/32063), сейчас они доступны в версии v17.05.0-ce-rc2 https://github.com/moby/moby/releases/tag/v17.05.0-ce-rc2


    Теперь отдельные Dockerfile'ы для билда перестали быть нужны, так как появилась возможность разделять стадии сборки в одном Dockerfile.


    В build stage возможно произвести все операции, связанные со сборкой, а уже только артефакт отправлять в следующую стадию, из которой на выходе получим image только с необходимыми для работы сервиса обвесами.


    Как пример возьмем сервис на Golang. Dockerfile такого сервиса с разделением стадий в общем случае может выглядеть так:


    # Стадия сборки "build-env"
    FROM golang:1.8.1-alpine AS build-env
    # Устанавливаем зависимости, необходимые для сборки
    RUN apk add --no-cache 
        git 
        make
    ADD . /go/src/github.com/username/project
    WORKDIR /go/src/github.com/username/project
    # Запускаем сборку
    RUN make build
    
    # --------
    
    # Стадия подготовки image к бою
    FROM alpine:3.5
    # Копируем артефакт сборки из стадии "build-env" в указанный файл
    COPY --from=build-env /go/src/github.com/username/project/bin/service /usr/local/bin/service
    EXPOSE 65122
    CMD ["service"]

    Результаты сборки:


    REPOSITORY                                   TAG                   IMAGE ID               CREATED              SIZE
    registry.my/username/project                 master              ce784fb88659        2 seconds ago       16.5MB
    <none>                                       <none>              9cc9ed2befc5        6 seconds ago       330MB

    330MB на стадии билда, 16.5MB после билда и готовое к запуску. Всё в одном Dockerfile с минимальной конфигурацией.


    В системе build-стадия сохраняется на диск как :.


    Возможно использование более двух стадий, например если вы собираете отдельно бекенд и фронтенд. При этом не обязательно наследоваться от предыдущего шага, вполне легально запускать шаг с новым родителем. Если образ родителя не будет найден на машине, то Docker подгрузит его в момент перехода к шагу. Каждая инструкция FROM обнуляет все предыдущие команды.


    Вот пример того, как можно использовать несколько стадий сборки:


    # Стадия сборки "build-env"
    FROM golang:1.8.1-alpine AS build-env
    ADD . /go/src/github.com/username/project
    WORKDIR /go/src/github.com/username/project
    # Запускаем сборку
    RUN make build
    
    # --------
    # Вторая стадия сборки "build-second"
    FROM build-env AS build-second
    RUN touch /newfile
    RUN echo "123" > /newfile
    
    # --------
    # Стадия сборки frontend "build-front"
    FROM node:alpine AS build-front
    ENV PROJECT_PATH /app
    ADD . $PROJECT_PATH
    WORKDIR $PROJECT_PATH
    RUN npm run build
    
    # --------
    # Стадия подготовки image к бою
    FROM alpine:3.5
    # Копируем артефакт сборки из стадии "build-env" в указанный файл
    COPY --from=build-env /go/src/github.com/username/project/bin/service /usr/local/bin/service
    # Копируем артефакт сборки из стадии "build-front" в указанную директорию
    COPY --from=build-front /app/public /app/static
    EXPOSE 65122
    CMD ["service"]

    Для выбора стадии сборки предлагается использовать флаг --target. С этим флагом сборка осуществляется до указанной стадии. (Включая все предыдущие) На диск в этом случае сохранится и отметится тегом именно эта стадия.


    Когда можно пользоваться?


    Релиз 17.05.0 запланирован на 2017-05-03. И насколько можно судить, это действительно полезный функционал, особенно для компилируемых языков.


    Спасибо за внимание.



    Источник: Хабрахабр

    Категория: Админитстрирование » Системное администрирование

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

    Добавление комментария

    Имя:*
    E-Mail:
    Комментарий:
    Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
    Введите два слова, показанных на изображении: *