Оптимизация docker-образов
Apr 13, 2017 13:57 · 1131 words · 6 minute read
Как известно, docker-контейнеры создаются и запускаются из образов, которые можно загрузить из общедоступных репозиториев (например, Docker Hub) или собрать самостоятельно с помощью инструкций в Dockerfile.
Самостоятельная сборка образов, на мой взгляд, обладает целым рядом преимуществ, однако в результате можно получить образ неприлично большого размера. Давайте разберемся, как оптимизировать docker-образы, не жертвуя при этом их функциональностью!
Действия производятся на:
lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.2 LTS
Release: 16.04
Codename: xenial
Используемая версия docker-engine:
docker version
Client:
Version: 17.03.0-ce
API version: 1.26
Go version: go1.7.5
Git commit: 60ccb22
Built: Thu Feb 23 11:02:43 2017
OS/Arch: linux/amd64
Server:
Version: 17.03.0-ce
API version: 1.26 (minimum version 1.12)
Go version: go1.7.5
Git commit: 60ccb22
Built: Thu Feb 23 11:02:43 2017
OS/Arch: linux/amd64
Experimental: false
В примере рассмотрим сборку docker-образа для контейнера с Nginx (как полагается, с поддержкой Brotli и свеженьким OpenSSL для HTTP2.0). Кроме этого, при запуске контейнера из шаблонов будут генерироваться конфигурационные файлы сайтов (в зависимости от параметров из .env
-файла).
Dockerfile (инструкции по сборке образа) выглядит так:
FROM debian:jessie
ENV NGINX_VERSION=1.11.10
ENV OPENSSL_VERSION=1.0.2g
ENV NGX_CACHE_PURGE_VERSION=2.3
RUN apt-get update \
&& apt-get install -y \
build-essential \
automake \
autoconf \
libtool \
openssl \
zlib1g-dev \
libpcre3 \
libpcre3-dev \
libgeoip-dev \
unzip \
wget \
git \
curl
RUN cd /tmp \
&& git clone https://github.com/bagder/libbrotli \
&& cd libbrotli \
&& ./autogen.sh \
&& ./configure \
&& make && make install \
&& cd /tmp \
&& git clone https://github.com/google/ngx_brotli \
&& wget -qO - https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \
| tar xzf - -C /tmp \
&& wget http://labs.frickle.com/files/ngx_cache_purge-$NGX_CACHE_PURGE_VERSION.tar.gz \
&& tar -zxvf ngx_cache_purge-$NGX_CACHE_PURGE_VERSION.tar.gz && mv ngx_cache_purge-$NGX_CACHE_PURGE_VERSION ngx_cache_purge && rm ngx_cache_purge-$NGX_CACHE_PURGE_VERSION.tar.gz \
&& wget https://github.com/openresty/echo-nginx-module/archive/v0.60.tar.gz \
&& tar -zxvf v0.60.tar.gz && rm v0.60.tar.gz \
&& mkdir -p /var/cache/nginx/client_temp \
&& wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz \
&& tar -xvzf nginx-${NGINX_VERSION}.tar.gz \
&& cd /tmp/ngx_brotli && git submodule update --init \
&& cd /tmp/nginx-${NGINX_VERSION} \
&& ./configure \
--prefix=/opt/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/opt/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=www-data \
--group=www-data \
--with-http_stub_status_module \
--with-http_geoip_module \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_auth_request_module \
--without-http_ssi_module \
--with-threads \
--with-stream \
--with-stream_ssl_module \
--with-file-aio \
--with-http_v2_module \
--with-ipv6 \
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' \
--with-openssl=/tmp/openssl-${OPENSSL_VERSION} \
--add-module=/tmp/ngx_brotli \
--add-module=/tmp/ngx_cache_purge \
--add-module=/tmp/echo-nginx-module-0.60 \
&& make \
&& make install
RUN apt-get remove -y build-essential zlib1g-dev libpcre3-dev unzip \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /tmp/nginx-${NGINX_VERSION}* \
&& rm -rf /tmp/ngx_brotli \
&& rm -rf /tmp/ngx_cache_purge \
&& rm -rf /tmp/libbrotli \
&& rm -rf /tmp/openssl-1.0.2g \
&& rm -rf /tmp/echo-nginx-module-0.60
ENV DOCKERIZE_VERSION v0.3.0
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
ADD . /opt/nginx/
ARG PUID=1000
RUN usermod -u ${PUID} www-data
EXPOSE 80 443
CMD dockerize -template /opt/nginx/conf.d/main.tmpl:/opt/nginx/conf.d/main-gen.conf -template /opt/nginx/conf.d/landing.tmpl:/opt/nginx/conf.d/landing-gen.conf nginx
Для сборки образа, находясь в каталоге с Dockerfile, запускаем команду:
docker build -t nginx-test:latest .
Проверим наличие нового образа после сборки:
docker images nginx-test
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-test latest 43bf91aeb416 3 minutes ago 732 MB
Размер образа составляет целых 732 MB! Размер, конечно, не столь важен, если мы говорим про образы использующиеся только на локальной машине. Но это становится проблемой, когда нужно постоянно скачивать/отправлять эти образы через интернет.
Для оптимизации docker-образов можно воспользоваться утилитой docker-squash
. Установить ее довольно просто:
pip install docker-squash
Данная утилита позволяет объединять слои docker-образов (не забываем о том, что каждая инструкция в Dockerfile создает новый слой, и в нашем образе содержится аж 16 слоев). Слои можно объединять частично (например, N последних или с X по Y) или полностью (как мы и поступим).
Запускаем команду:
docker-squash -t nginx-test:squashed nginx-test:latest
2017-03-20 13:42:39,562 root INFO docker-squash version 1.0.5, Docker 60ccb22, API 1.26...
2017-03-20 13:42:39,562 root INFO Using v2 image format
2017-03-20 13:42:39,582 root INFO Old image has 16 layers
2017-03-20 13:42:39,582 root INFO Checking if squashing is necessary...
2017-03-20 13:42:39,582 root INFO Attempting to squash last 16 layers...
2017-03-20 13:42:39,582 root INFO Saving image sha256:43bf91aeb416da42a75d2a0b825567aaed9742200a081259c85fa2d10e96f2ed to /tmp/docker-squash-sUSIVv/old directory...
2017-03-20 13:43:08,947 root INFO Image saved!
2017-03-20 13:43:08,947 root INFO Squashing image 'nginx-test:latest'...
2017-03-20 13:43:09,848 root INFO Starting squashing...
2017-03-20 13:43:09,849 root INFO Squashing file '/tmp/docker-squash-sUSIVv/old/e0d4b8ce48d8fd1930b367ed7aff1f7795b74756ed4ed9062acd18e59f6bdf06/layer.tar'...
2017-03-20 13:43:09,871 root INFO Squashing file '/tmp/docker-squash-sUSIVv/old/36675d6554a8ebdf72265fe739214048565925b04a739733b84caddb92702b02/layer.tar'...
2017-03-20 13:43:10,189 root INFO Squashing file '/tmp/docker-squash-sUSIVv/old/968002289f33e062ba172190dadeb0046ec6d34407992cbf1ca239d93d23e684/layer.tar'...
2017-03-20 13:43:10,191 root INFO Squashing file '/tmp/docker-squash-sUSIVv/old/23bc9170b2cb9a7aee432d93d651288fa506178be14fbadfb630bcda09977ad3/layer.tar'...
2017-03-20 13:43:10,345 root INFO Squashing file '/tmp/docker-squash-sUSIVv/old/1d8ab492b6fbfd6d2c00c43cccac25053b2669f590261c58a071204977426dcd/layer.tar'...
2017-03-20 13:43:11,172 root INFO Squashing file '/tmp/docker-squash-sUSIVv/old/dcda4589a5d2be254d81216d1b22a26e633d8cc36ed3f1a27117f1ce4e852baf/layer.tar'...
2017-03-20 13:43:11,945 root INFO Squashing file '/tmp/docker-squash-sUSIVv/old/d904382d2c552061a5a4198a9e0dcb56fb8515eb0bdc17969b28b366edb8dfb6/layer.tar'...
2017-03-20 13:43:20,774 root INFO Squashing file '/tmp/docker-squash-sUSIVv/old/c22ce97cf003214a558f7a68d9a0538e504321bae40a8fe6aad393bf5be114bc/layer.tar'...
2017-03-20 13:43:36,312 root INFO Squashing finished!
2017-03-20 13:43:37,681 root INFO New squashed image ID is 3c1cfae6f24c14d4417aa6e32bc96d41e2e2caf8fbd0a351b027e2619fc493b1
2017-03-20 13:43:45,236 root INFO Image registered in Docker daemon as nginx-test:squashed
2017-03-20 13:43:45,297 root INFO Done
Проверим информацию о docker-образах с именем nginx-test (теперь их станет 2):
docker images nginx-test
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-test squashed 3c1cfae6f24c About a minute ago 399 MB
nginx-test latest 43bf91aeb416 9 minutes ago 732 MB
Как видим, последний образ с тегом squashed
уменьшился на 46%.
Следует отметить, что docker-engine начиная с версии 1.13 в экспериментальном режиме содержит возможность сборки уже сжатых образов. Для этого в команду сборки необходимо добавить параметр --squash
, например:
docker build --squash -t nginx-test:late .
Error response from daemon: squash is only supported with experimental mode
Чтобы включить експериментальный режим docker-engine в Ubuntu 16.04 необходимо в конфигурационном файле /lib/systemd/system/docker.service
добавить параметр --experimental=true
.
В моем случае это выглядит так:
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target docker.socket firewalld.service
Requires=docker.socket
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --experimental=true
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=1048576
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
[Install]
WantedBy=multi-user.target
После изменения конфигурационного файла выполняем:
sudo systemctl daemon-reload
sudo systemctl restart docker
Проверим информацию о версии docker-engine (следует обратить внимание на последнюю строку):
docker version
Client:
Version: 17.03.0-ce
API version: 1.26
Go version: go1.7.5
Git commit: 60ccb22
Built: Thu Feb 23 11:02:43 2017
OS/Arch: linux/amd64
Server:
Version: 17.03.0-ce
API version: 1.26 (minimum version 1.12)
Go version: go1.7.5
Git commit: 60ccb22
Built: Thu Feb 23 11:02:43 2017
OS/Arch: linux/amd64
Experimental: true
Теперь можно сразу собирать сжатые docker-образы командой:
docker build --squash -t nginx-test:late .