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

_ Автор выбрал Code.org, чтобы получить пожертвование в рамках программы Write for DOnations.

Вступление

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

Большие изображения Docker могут увеличить время, необходимое для создания и отправки изображений между кластерами и облачными провайдерами. Если, например, у вас есть изображение размером с гигабайт, которое нужно выдвигать каждый раз, когда один из ваших разработчиков запускает сборку, пропускная способность, создаваемая в вашей сети, будет увеличиваться во время процесса CI / CD, что делает ваше приложение медленным и, в конечном итоге, стоит ваших ресурсов. , Из-за этого в образах Docker, подходящих для производства, должны быть установлены только самые необходимые компоненты.

Существует несколько способов уменьшить размер образов Docker, чтобы оптимизировать их для производства. Во-первых, эти изображения обычно не нуждаются в средствах сборки для запуска своих приложений, поэтому их вообще не нужно добавлять. Используя multi-stage процесс сборки, вы можете использовать промежуточные образы для компиляции и сборки кода, установки зависимостей и упаковки всего в Наименьший возможный размер, затем скопируйте окончательную версию вашего приложения в пустое изображение без инструментов сборки. Кроме того, вы можете использовать изображение с крошечной основой, например, Alpine Linux. Alpine является подходящим дистрибутивом Linux для производства, поскольку в нем есть только то, что нужно вашему приложению.

В этом руководстве вы оптимизируете изображения Docker за несколько простых шагов, делая их меньше, быстрее и лучше подходят для производства. Вы создадите образы для примера Go API в нескольких различных контейнерах Docker, начиная с Ubuntu и специфичных для языка изображений, затем перейдя к Alpine распределение. Вы также будете использовать многоэтапные сборки для оптимизации ваших изображений для производства. Конечная цель этого учебного пособия - показать разницу в размерах между использованием изображений Ubuntu по умолчанию и оптимизированных аналогов и показать преимущество многоэтапных сборок. Прочитав это руководство, вы сможете применить эти методы к своим собственным проектам и конвейерам CI / CD.

Предпосылки

Перед началом вам понадобится:

  • Сервер Ubuntu 18.04 с учетной записью пользователя без полномочий root с правами + sudo +. Следуйте нашему руководству по Initial Server Setup с Ubuntu 18.04. Хотя это руководство было протестировано на Ubuntu 18.04, вы можете выполнить многие шаги в любом дистрибутиве Linux.

  • Docker установлен на вашем сервере. Пожалуйста, следуйте шагам 1 и 2 из Как установить и использовать Docker в Ubuntu 18.04 для инструкций по установке.

Шаг 1 - Загрузка Sample Go API

Перед оптимизацией образа Docker вы должны сначала загрузить sample API, из которого вы будете создавать образы Docker. Использование простого API Go покажет все ключевые этапы построения и запуска приложения внутри контейнера Docker. Этот учебник использует Go, потому что это скомпилированный язык, такой как C++ или Java, но в отличие от них. имеет очень маленький след.

На вашем сервере начните с клонирования примера Go API:

git clone https://github.com/do-community/mux-go-api.git

После того, как вы клонировали проект, у вас будет каталог с именем + mux-go-api _ на вашем сервере. Переместитесь в этот каталог с помощью + cd +:

cd mux-go-api

Это будет домашний каталог для вашего проекта. Вы создадите свои изображения Docker из этого каталога. Внутри вы найдете исходный код API, написанный на Go, в файле + api.go +. Хотя этот API минимален и имеет всего несколько конечных точек, он подойдет для имитации готового к работе API для целей этого учебного пособия.

Теперь, когда вы загрузили пример Go API, вы готовы создать базовый образ Ubuntu Docker, с которым вы можете сравнить более поздние оптимизированные образы Docker.

Шаг 2 - Создание базового образа Ubuntu

Для вашего первого образа Docker будет полезно посмотреть, как он выглядит, когда вы начинаете с базового образа Ubuntu. Это упакует ваш пример API в среду, аналогичную программному обеспечению, которое вы уже используете на своем сервере Ubuntu. Внутри образа вы будете устанавливать различные пакеты и модули, необходимые для запуска вашего приложения. Однако вы обнаружите, что этот процесс создает довольно тяжелый образ Ubuntu, который влияет на время сборки и читаемость кода вашего Dockerfile.

Начните с написания Dockerfile, который инструктирует Docker создать образ Ubuntu, установить Go и запустить пример API. Обязательно создайте Dockerfile в каталоге клонированного репозитория. Если вы клонировали в домашний каталог, это должно быть + $ HOME / mux-go-api +.

Создайте новый файл с именем + Dockerfile.ubuntu. Откройте его в + nano + или в вашем любимом текстовом редакторе:

nano ~/mux-go-api/Dockerfile.ubuntu

В этом Dockerfile вы определите образ Ubuntu и установите Golang. Затем вы приступите к установке необходимых зависимостей и созданию двоичного файла. Добавьте следующее содержимое в + Dockerfile.ubuntu:

~ / MUX-гоу-апи / Dockerfile.ubuntu

FROM ubuntu:18.04

RUN apt-get update -y \
 && apt-get install -y git gcc make golang-

ENV GOROOT /usr/lib/go-
ENV PATH $GOROOT/bin:$PATH
ENV GOPATH /root/go
ENV APIPATH /root/go/src/api

WORKDIR $APIPATH
COPY . .

RUN \
 go get -d -v \
 && go install -v \
 && go build

EXPOSE 3000
CMD ["./api"]

Начиная сверху, команда + FROM + указывает, какую базовую операционную систему будет иметь образ. Затем команда + RUN + устанавливает язык Go во время создания образа. + ENV + устанавливает конкретные переменные окружения, которые нужны компилятору Go для правильной работы. + WORKDIR + указывает каталог, в который мы хотим скопировать код, а команда + COPY + берет код из каталога, где находится + Dockerfile.ubuntu +, и копирует его в изображение. Последняя команда + RUN устанавливает зависимости Go, необходимые для исходного кода для компиляции и запуска API.

Сохраните и выйдите из файла. Теперь вы можете запустить команду + build +, чтобы создать образ Docker из только что созданного файла Docker:

docker build -f Dockerfile.ubuntu -t ubuntu .

Команда + build + создает образ из Dockerfile. Флаг + -f + указывает, что вы хотите собрать из файла + Dockerfile.ubuntu +, а + -t + обозначает тег, то есть вы помечаете его именем + ubuntu +. Последняя точка представляет текущий контекст, в котором находится + Dockerfile.ubuntu +.

Это займет некоторое время, поэтому не стесняйтесь сделать перерыв. Как только сборка будет завершена, у вас будет образ Ubuntu, готовый для запуска вашего API. Но окончательный размер изображения может быть не идеальным; все, что превышает несколько сотен МБ для этого API, будет считаться слишком большим изображением.

Запустите следующую команду, чтобы получить список всех образов Docker и найти размер вашего образа Ubuntu:

docker images

Вы увидите вывод с изображением, которое вы только что создали:

OutputREPOSITORY  TAG     IMAGE ID        CREATED         SIZE
ubuntu      latest  61b2096f6871    33 seconds ago
. . .

Как подчеркивается в выводе, это изображение имеет размер * 636 МБ * для базового API Golang, число, которое может незначительно отличаться от машины к машине. В случае нескольких сборок этот большой размер значительно влияет на время развертывания и пропускную способность сети.

В этом разделе вы создали образ Ubuntu со всеми необходимыми инструментами Go и зависимостями для запуска API, клонированного на шаге 1. В следующем разделе вы будете использовать предварительно скомпонованный образ Docker для конкретного языка, чтобы упростить ваш Dockerfile и упростить процесс сборки.

Шаг 3 - Создание базового образа для конкретного языка

Предварительно созданные изображения - это обычные базовые изображения, которые пользователи модифицировали, чтобы включить инструменты для конкретной ситуации. Затем пользователи могут отправить эти изображения в хранилище изображений Docker Hub, что позволяет другим пользователям использовать общий образ вместо того, чтобы писать свои собственные файлы Docker. Это распространенный процесс в производственных ситуациях, и вы можете найти различные предварительно созданные образы в Docker Hub практически для любого случая использования. На этом шаге вы создадите свой пример API-интерфейса с использованием образа Go, в котором уже установлен компилятор и установлены зависимости.

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

Создайте еще один Dockerfile и назовите его + Dockerfile.golang +. Откройте его в вашем текстовом редакторе:

nano ~/mux-go-api/Dockerfile.golang

Этот файл будет значительно более кратким, чем предыдущий, поскольку в нем предварительно установлены все зависимости, инструменты и компилятор Go.

Теперь добавьте следующие строки:

~ / MUX-гоу-апи / Dockerfile.golang

FROM golang:

WORKDIR /go/src/api
COPY . .

RUN \
   go get -d -v \
   && go install -v \
   && go build

EXPOSE 3000
CMD ["./api"]

Начиная сверху, вы обнаружите, что оператор + FROM + теперь является + golang: +. Это означает, что Docker будет извлекать предварительно собранный образ Go из Docker Hub, в котором уже установлены все необходимые инструменты Go.

Теперь, еще раз, создайте образ Docker с помощью:

docker build -f Dockerfile.golang -t golang .

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

docker images

Это даст результат, подобный следующему:

OutputREPOSITORY  TAG     IMAGE ID        CREATED         SIZE
golang      latest  eaee5f524da2    40 seconds ago
. . .

Несмотря на то, что сам Dockerfile более эффективен и время сборки короче, общий размер изображения фактически увеличился. Предварительно созданное изображение Golang составляет около * 744 МБ *, что значительно.

Это предпочтительный способ создания образов Docker. Он дает вам базовый образ, который сообщество утвердило в качестве стандарта для использования на указанном языке, в данном случае Go. Однако, чтобы подготовить образ к производству, вам нужно вырезать части, которые не нужны выполняющемуся приложению.

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

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

Шаг 4 - Создание базовой альпийской картинки

Одним из самых простых шагов по оптимизации ваших образов Docker является использование меньших базовых образов. Alpine - это легкий дистрибутив Linux, разработанный для обеспечения безопасности и эффективности использования ресурсов. Образ Alpine Docker использует musl libc и BusyBox для сохранения компактности, для работы которого требуется не более 8 МБ в контейнере , Крошечный размер обусловлен тем, что двоичные пакеты прореживаются и разделяются, что дает вам больший контроль над тем, что вы устанавливаете, что делает среду максимально маленькой и эффективной.

Процесс создания образа Alpine аналогичен тому, как вы создавали образ Ubuntu на шаге 2. Сначала создайте новый файл с именем + Dockerfile.alpine +:

nano ~/mux-go-api/Dockerfile.alpine

Теперь добавьте этот фрагмент:

~ / MUX-гоу-апи / Dockerfile.alpine

FROM alpine:

RUN apk add --no-cache \
   ca-certificates \
   git \
   gcc \
   musl-dev \
   openssl \
   go

ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
ENV APIPATH $GOPATH/src/api
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" "$APIPATH" && chmod -R 777 "$GOPATH"

WORKDIR $APIPATH
COPY . .

RUN \
   go get -d -v \
   && go install -v \
   && go build

EXPOSE 3000
CMD ["./api"]

Здесь вы добавляете команду + apk add +, чтобы использовать менеджер пакетов Alpine для установки Go и всех необходимых библиотек. Как и в образе Ubuntu, вам также необходимо установить переменные окружения.

Идем дальше и строим образ:

docker build -f Dockerfile.alpine -t alpine .

Еще раз проверьте размер изображения:

docker images

Вы получите вывод, подобный следующему:

OutputREPOSITORY  TAG     IMAGE ID        CREATED         SIZE
alpine      latest  ee35a601158d    30 seconds ago
. . .

Размер уменьшился до * 426MB *.

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

Затем попробуйте использовать предварительно созданный образ Alpine для Go. Это сделает Dockerfile короче, а также уменьшит размер конечного изображения. Поскольку предварительно созданный образ Alpine для Go создан с использованием Go, скомпилированного из исходного кода, его площадь значительно меньше.

Начните с создания нового файла с именем ++:

nano ~/mux-go-api/Dockerfile.golang-alpine

Добавьте следующее содержимое в файл:

~ / MUX-гоу-апи / Dockerfile.golang-альпийский

FROM golang:

RUN apk add --no-cache --update git

WORKDIR /go/src/api
COPY . .

RUN go get -d -v \
 && go install -v \
 && go build

EXPOSE 3000
CMD ["./api"]

Единственные различия между + Dockerfile.golang-alpine + и + Dockerfile.alpine + - это команда + FROM + и первая команда + RUN +. Теперь команда + FROM + задает изображение + golang + с тегом ++, а + RUN + содержит только команду для установки Git. Git нужен для того, чтобы команда + go get работала во второй команде` + RUN + внизу + Dockerfile.golang-alpine`.

Создайте образ с помощью следующей команды:

docker build -f Dockerfile.golang-alpine -t golang-alpine .

Получить ваш список изображений:

docker images

Вы получите следующий вывод:

OutputREPOSITORY      TAG     IMAGE ID        CREATED         SIZE
golang-alpine   latest  97103a8b912b    49 seconds ago

Теперь размер изображения уменьшен до * 288 МБ *.

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

Шаг 5 - Исключение инструментов сборки с многоэтапной сборкой

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

Начните с создания другого файла с именем ++:

nano ~/mux-go-api/Dockerfile.multistage

То, что вы добавите сюда, будет знакомо. Начните с добавления того же кода, что и для + Dockerfile.golang-alpine +. Но на этот раз также добавьте второе изображение, куда вы скопируете двоичный файл из первого изображения.

~ / MUX-гоу-апи / Dockerfile.multistage

FROM golang:1.10-alpine3.8

RUN apk add --no-cache --update git

WORKDIR /go/src/api
COPY . .

RUN go get -d -v \
 && go install -v \
 && go build

##

FROM alpine:3.8
COPY  /go/bin/api /go/bin/
EXPOSE 3000
CMD ["/go/bin/api"]

Сохраните и закройте файл. Здесь у вас есть две команды + FROM +. Первый идентичен + Dockerfile.golang-alpine +, за исключением того, что в команде + FROM + есть дополнительный + AS multisttage +. Это даст ему имя + multistage +, на которое вы будете ссылаться в нижней части файла + Dockerfile.multistage +. Во второй команде + FROM вы берете базовое изображение` + alpine + и + COPY + поверх скомпилированного приложения Go из изображения + multistage`. Этот процесс еще больше сократит размер конечного изображения, делая его готовым к производству.

Запустите сборку с помощью следующей команды:

docker build -f Dockerfile.multistage -t prod .

Проверьте размер изображения сейчас, после использования многоступенчатой ​​сборки.

docker images

Вы найдете два новых изображения вместо одного:

OutputREPOSITORY      TAG     IMAGE ID        CREATED         SIZE
prod            latest  82fc005abc40    38 seconds ago
<none>          <none>  d7855c8f8280    38 seconds ago
. . .

Образ + <none> + является изображением + multistage +, созданным с помощью команды + FROM golang: 1.10-alpine3.8 +. Это всего лишь посредник, используемый для сборки и компиляции приложения Go, в то время как образ + prod + в этом контексте является окончательным изображением, которое содержит только скомпилированное приложение Go.

От начальных * 744 МБ * теперь вы сократили размер изображения до * 11,3 МБ *. Отслеживать такое маленькое изображение и отправлять его по сети на рабочие серверы будет намного проще, чем с изображением размером более 700 МБ, и вы сэкономите значительные ресурсы в долгосрочной перспективе.

Заключение

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

Если вы хотите узнать больше о создании приложений с помощью Docker, ознакомьтесь с нашими How To Создайте приложение Node.js с Docker. Для получения дополнительной концептуальной информации по оптимизации контейнеров см. Https://www.digitalocean.com/community/tutorials/building-optimized-containers-for-kubernetes[Building Оптимизированные контейнеры для Kubernetes].

Related