Обзор задачи
Часто в процессе сборки образа приложения происходит скачивание временных файлов — пакетов установки, архивов программ и т.п. В результате в образе могут оставаться файлы, которые необходимы в процессе сборки, но уже не требуется конечному пользователю собранного образа для запуска приложения.
werf может импортировать ресурсы из других образов и образов артефактов. Это позволяет вынести часть процесса сборки (сборку вспомогательных инструментов) в отдельный образ, копируя в конечный образ приложения только необходимые файлы. Этот функционал werf похож на соответствующий в Docker (поддерживаемый, начиная с Docker версии 17.05), но в werf имеется больше возможностей, в частности, по импорту файлов.
В руководстве сначала рассматривается сборка тестового приложения на GO, а затем оно оптимизируется для существенного уменьшения размера конечного образа с использованием артефактов.
Требования
- Установленные зависимости werf.
- Установленный multiwerf.
Выбор версии werf
Перед началом работы необходимо выбрать версию werf. Для выбора актуальной версии werf в канале stable, релиза 1.1, выполним следующую команду:
. $(multiwerf use 1.1 stable --as-file)
Тестовое приложение
Возьмем в качестве примера приложение Go Web App, написанное на Go.
Сборка
Создадим папку gowebapp
и файл werf.yaml
со следующим содержимым:
project: gowebapp
configVersion: 1
---
image: gowebapp
from: golang:1.14
docker:
WORKDIR: /app
ansible:
install:
- name: Getting packages
shell: go get github.com/josephspurrier/gowebapp
setup:
- file:
path: /app
state: directory
- name: Copying config
shell: |
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/config /app/config
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/static /app/static
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/template /app/template
cp $GOPATH/bin/gowebapp /app/
Приведенные инструкции описывают сборку одного образа — gowebapp
.
Соберём образ приложения, выполнив следующую команду в папке gowebapp
:
werf build --stages-storage :local
Запуск
Запустим приложение, выполнив следующую команду в папке gowebapp
:
werf run --stages-storage :local --docker-options="-d -p 9000:80 --name gowebapp" gowebapp -- /app/gowebapp
Убедитесь, что контейнер запустился, выполнив следующую команду:
docker ps -f "name=gowebapp"
Вы должны увидеть запущенный контейнер gowebapp
, например, вывод может быть подобен следующему:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41d6f49798a8 werf-stages-storage/gowebapp:84d7...44992265 "/app/gowebapp" 2 minutes ago Up 2 minutes 0.0.0.0:9000->80/tcp gowebapp
Откройте в браузере адрес http://localhost:9000 — вы должны увидеть страницу Go Web App
. Перейдите по ссылке “Click here to login”, где вы сможете зарегистрироваться и авторизоваться в приложении.
Размер собранного образа
Получим размер собранного образа, выполнив:
docker images `docker ps -f "name=gowebapp" --format='{{.Image}}'`
Пример вывода:
REPOSITORY TAG IMAGE ID CREATED SIZE
werf-stages-storage/gowebapp 84d7...44992265 07cdc430e1c8 10 minutes ago 857MB
Обратите внимание, что размер образа приложения получился более 800 Мегабайт.
Оптимизация сборки приложения с использованием артефактов
Непосредственно для запуска приложения необходимы только файлы в папке /app
, поэтому из образа можно удалить скачанные пакеты и сам компилятор Go. Использование функционала артефактов в werf позволяет импортировать в образ только конкретные файлы.
Сборка
Заменим имеющийся файл werf.yaml
следующим содержимым:
project: gowebapp
configVersion: 1
---
artifact: gowebapp-build
from: golang:1.14
ansible:
install:
- name: Getting packages
shell: go get github.com/josephspurrier/gowebapp
setup:
- file:
path: /app
state: directory
- name: Copying config
shell: |
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/config /app/config
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/static /app/static
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/template /app/template
cp $GOPATH/bin/gowebapp /app/
---
image: gowebapp
docker:
WORKDIR: /app
from: ubuntu:18.04
import:
- artifact: gowebapp-build
add: /app
to: /app
after: install
В оптимизированных инструкциях сборки само приложение собирается в артефакте gowebapp-build
, после чего получившиеся файлы импортируются в образ gowebapp
.
Обратите внимание, что при сборке образа gowebapp
используется образ ubuntu
, а не golang
.
Соберём приложение с измененным файлом инструкций:
werf build --stages-storage :local
Запуск
Перед запуском измененного приложения нужно остановить и удалить запущенный контейнер gowebapp
, собранный и запущенный ранее. В противном случае новый контейнер не сможет запуститься из-за того, что контейнер с таким именем уже существует, порт 9000 занят. Например, выполните следующие команды для остановки и удаления контейнера gowebapp
:
docker rm -f gowebapp
Запустим измененное приложение, выполнив следующую команду:
werf run --stages-storage :local --docker-options="-d -p 9000:80 --name gowebapp" gowebapp -- /app/gowebapp
Убедитесь, что контейнер запустился, выполнив следующую команду:
docker ps -f "name=gowebapp"
Вы должны увидеть запущенный контейнер gowebapp
, например, вывод может быть следующим:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41d6f49798a8 werf-stages-storage/gowebapp:84d7...44992265 "/app/gowebapp" 2 minutes ago Up 2 minutes 0.0.0.0:9000->80/tcp gowebapp
Откройте в браузере адрес http://localhost:9000 — вы должны увидеть страницу Go Web App
. Перейдите по ссылке “Click here to login”, где вы сможете зарегистрироваться и авторизоваться в приложении.
Размер собранного образа
Получим размер образа, выполнив:
docker images `docker ps -f "name=gowebapp" --format='{{.Image}}'`
Пример вывода:
REPOSITORY TAG IMAGE ID CREATED SIZE
werf-stages-storage/gowebapp 84d7...44992265 07cdc430e1c8 10 minutes ago 79MB
Сравнивая размеры можно увидеть, что образ, собранный с использованием артефактов, меньше на 90%, чем первоначальный.
Вывод
Приведенный пример показывает, что использование артефактов — отличный способ выбросить ненужное из конечного образа. Более того, вы можете использовать один и тот же артефакт (или артефакты) в нескольких образах, определенных в одном werf.yaml
. Этот прием позволяет увеличить скорость сборки и сократить размер конечного образа.