Docker — флагман рынка ПО для контейнеризации и микросервисов, появившийся в 2013 году и до сих пор остающийся основным игроком. Для его использования в Ubuntu не требуется установки отдельной ВМ, т.к. он использует возможности ядра.
Читать
Архив метки: docker
Docker Tips: Очистите свою машину от хлама /Как удалить старые и не используемые образы Docker
Сегодня мы поговорим о том, как Docker использует дисковое пространство хостовой машины, а также разберемся в том, как это пространство освободить от ошметков неиспользуемых образов и контейнеров.

Общее потребление
Docker – крутая штука, наверное сегодня мало кто в этом сомневается. Всего несколько лет назад этот продукт предоставил нам совершенно новый способ построения, доставки и запуска любого окружения, позволяя значительно сэкономить ресурсы процессора и оперативной памяти. В дополнение к этому (а для кого-то это будет даже самым важным) Docker позволил нам невероятно упростить и унифицировать управление жизненным циклом используемых рабочих сред.
Однако, за все эти прелести современной жизни приходится платить. Когда мы запускаем контейнеры, скачиваем или создаем собственные образы, разворачиваем сложные экосистемы, нам приходится платить. И платим мы, в том числе, дисковым пространством.
Если вы никогда не задумывались о том, сколько же места реально занято на вашей машине Docker’ом, то можете быть неприятно удивлены выводом этой команды:
$ docker system df

Здесь отображено использование диска Docker’ом в различных разрезах:
- образы (images) – общий размер образов, которые были скачаны из хранилищ образов и построены в вашей системе;
- контейнеры (containers) – общий объем дискового пространства, используемый запущенными контейнерами (имеется ввиду общий объем слоев чтения-записи всех контейнеров);
- локальные тома (local volumes) – объем локальных хранилищ, примонтированных к контейнерам;
- кэш сборки (build cache) – временные файлы, сгенерированные процессом построения образов (при использовании инструмента BuildKit, доступного начиная с Docker версии 18.09).
Готов поспорить, что уже после этого простого перечисления вы горите желанием почистить диск от мусора и вернуть к жизни драгоценные гигабайты (прим. перев.: особенно, если за эти гигабайты вы ежемесячно перечисляете арендную плату).
Использование диска контейнерами
Каждый раз при создании контейнера на хостовой машине в каталоге /var/lib/docker создается несколько файлов и каталогов, среди которых стоит отметить следующие:
- Каталог /var/lib/docker/containers/ID_контейнера – при использовании стандартного драйвера логгирования именно сюда сохраняются журналы событий в JSON-формате. Слишком подробные логи, а также логи, которые никто не читает и не обрабатывает иными способами, часто становятся причиной переполнения дисков.
- Каталог /var/lib/docker/overlay2 – содержит слои чтения-записи контейнеров (overlay2 – предпочитаемые драйвер в большинстве дистрибутивов Linux). Если контейнер сохраняет данные в своей файловой системе, то именно в этом каталоге они и будут размещены.
Давайте представим себе систему, на которой установлен девственно чистый Docker, ни разу не участвовавший в запуске контейнеров и сборке образов. Его отчет об использовании дискового пространства будет выглядеть так:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
Запустим какой-нибудь контейнер, например, NGINX:
$ docker container run --name www -d -p 8000:80 nginx:1.16
Что происходит с диском:
- образы (images) занимают 126 Мб, это тот самый NGINX, который мы запустили в контейнере;
- контейнеры (containers) занимают смешные 2 байта.
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 1 126M 0B (0%)
Containers 1 1 2B 0B (0%)
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
Судя по выводу, у нас еще нет пространства, которое мы могли бы высвободить. Так как 2 байта это совершенно несерьезно, давайте представим, что наш NGINX неожиданно для всех написал куда-то 100 Мегабайт данных и создал внутри себя файл test.img именно такого размера.
$ docker exec -ti www
dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*100]
Снова исследуем использование дискового пространства на хосте. Мы увидим, что контейнер (containers) занимает там 100 Мегабайт.
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 1 126M 0B (0%)
Containers 1 1 104.9MB 0B (0%)
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
Думаю, ваш пытливый мозг уже задается вопросом, где же находится наш файл test.img. Давайте его поищем:
$ find /var/lib/docker -type f -name test.img
/var/lib/docker/overlay2/83f177...630078/merged/test.img
/var/lib/docker/overlay2/83f177...630078/diff/test.img
Не вдаваясь в подробности можно отметить, что файл test.img удобно расположился на уровне чтения-записи, управляемом драйвером overlay2. Если же мы остановим наш контейнер, то хост подскажет нам, что это место, в принципе, можно высвободить:
# Stopping the www container
$ docker stop www
# Visualizing the impact on the disk usage
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 1 126M 0B (0%)
Containers 1 0 104.9MB 104.9MB (100%)
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
Как мы можем это сделать? Удалением контейнера, которое повлечет за собой очистку соответствующего пространства на уровне чтения-записи.
С помощью следующей команды вы можете удалить все установленные контейнеры одним махом и очистить ваш диск от всех созданных ими на уровне чтения-записи файлов:
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
5e7f8e5097ace9ef5518ebf0c6fc2062ff024efb495f11ccc89df21ec9b4dcc2
Total reclaimed space: 104.9MB
Итак, мы высвободили 104,9 Мегабайта удалением контейнера. Но так как мы уже не используем скачанный ранее образ, то он тоже становится кандидатом на удаление и высвобождение наших ресурсов:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 0 126M 126M (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
Внимание: до тех пор, пока образ используется хотя бы одним контейнером, вы не сможете использовать этот трюк.
Субкоманда prune, которую мы использовали выше, дает эффект только на остановленных контейнерах. Если мы хотим удалить не только остановленные, но и запущенные контейнеры, следует использовать одну из этих команд:
# Historical command
$ docker rm -f $(docker ps –aq)
# More recent command
$ docker container rm -f $(docker container ls -aq)
Заметки на полях: если при запуске контейнера использовать параметр —rm, то при его остановке будут высвобождено все дисковое пространство, которое он занимал.
Использование диска образами
Несколько лет назад размер образа в несколько сотен мегабайт был совершенно нормальным: образ Ubuntu весил 600 Мегабайт, а образ Microsoft .Net – несколько Гигабайт. В те лохматые времена скачивание одного только образа могло нанести большой урон вашему свободному месту на диске, даже если вы расшаривали уровни между образами. Сегодня – хвала великим – образы весят намного меньше, но даже в этом случае можно быстро забить имеющиеся ресурсы, если не принимать некоторых мер предосторожности.
Есть несколько типов образов, которые напрямую не видны конечному пользователю:
- intermediate образы, на основе которых собраны другие образы в – они не могут быть удалены, если вы используете контейнеры на базе этих самых «других» образов;
- dangling образы – это такие intermediate образы, на которые не ссылается ни один из запущенных контейнеров – они могут быть удалены.
- С помощью следующей команды вы можете проверить наличие в вашей системе dangling образов:
$ docker image ls -f dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
none none 21e658fe5351 12 minutes ago 71.3MB
Удалить их можно следующим способом:
$ docker image rm $(docker image ls -f dangling=true -q)
Мы можем использовать также субкоманду prune:
$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
deleted: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
deleted: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
deleted: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
deleted: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f
Total reclaimed space: 12.9kB
Если мы вдруг захотим удалить вообще все образы (а не только dangling) одной командой, то можно сделать так:
$ docker image rm $(docker image ls -q)
Использование диска томами
Тома (volumes) применяются для хранения данных за пределами файловой системы контейнера. Например, если мы хотим сохранить результаты работы какого-либо приложения, чтобы использовать их как-то еще. Частым примером являются базы данных.
Давайте запустим контейнер MongoDB, примонтируем к нему внешний по отношению к контейнеру том, и восстановим из него бэкап базы данных (у нас он доступен в файле bck.json):
# Running a mongo container
$ docker run --name db -v $PWD:/tmp -p 27017:27017 -d mongo:4.0
# Importing an existing backup (from a huge bck.json file)
$ docker exec -ti db mongoimport
--db 'test'
--collection 'demo'
--file /tmp/bck.json
--jsonArray
Данные будут находиться на хостовой машине в каталоге /var/lib/docker/volumes. Но почему не на уровне чтения-записи контейнера? Потому что в Dockerfile образа MongoDB каталог /data/db (в котором MongoDB по умолчанию хранит свои данные) определен как том (volume).

Заметки на полях: многие образы, в результате работы которых должны создаваться данные, используют тома (volumes) для сохранения этих самых данных.
Когда мы наиграемся с MongoDB и остановим (а может даже и удалим) контейнер, том не будет удален. Он продолжит занимать наше драгоценное дисковое пространство до тех пор, пока мы явно не удалим его такой командой:
$ docker volume rm $(docker volume ls -q)
Ну или мы можем использовать уже знакомую нам субкоманду prune:
$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da
Total reclaimed space: 25.82GB
luc@saturn:~$
Использование диска для кэша сборки образов
В Docker 18.09 процесс создания образов претерпел некоторые изменения благодаря инструменту BuildKit. С помощью этой штуки увеличивается скорость процесса, оптимизируется управление хранением данных и безопасностью. Здесь мы не будем рассматривать все детали этого замечательного инструмента, остановимся лишь нам том, как он затрагивает вопросы использования дискового пространства.
Предположим, что у нас есть совершенно простое приложение Node.Js:
- файл index.js запускает простой HTTP сервер, который отвечает строкой на каждый полученный запрос:
- файл package.json определяет зависимости, из которых используется только expressjs для запуска HTTP сервера:
$ cat index.js
var express = require('express');
var util = require('util');
var app = express();
app.get('/', function(req, res) {
res.setHeader('Content-Type', 'text/plain');
res.end(util.format("%s - %s", new Date(), 'Got Request'));
});
app.listen(process.env.PORT || 80);
$ cat package.json
{
"name": "testnode",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.14.0"
}
}
Dockerfile для сборки образа выглядит так:
FROM node:13-alpine
COPY package.json /app/package.json
RUN cd /app && npm install
COPY . /app/
WORKDIR /app
EXPOSE 80
CMD ["npm", "start"]
Давайте соберем образ обычным способом, без использования BuildKit:
$ docker build -t app:1.0 .
Если мы проверим использование дискового пространства, то увидим, что место занимают только базовый образ (node:13-alpine) и конечный образ (app:1.0):
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 2 0 109.3MB 109.3MB (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
Давайте соберем вторую версию нашего приложения, уже с использованием BuildKit. Для этого нам лишь необходимо установить переменную DOCKER_BUILDKIT в значение 1:
$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .
Если мы сейчас проверим использование диска, то увидим, что теперь там участвует кэш сборки (buid-cache):
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 2 0 109.3MB 109.3MB (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 11 0 8.949kB 8.949kB
Для его очистки воспользуемся следующей командой:
$ docker builder prune
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Deleted build cache objects:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi
Total reclaimed space: 8.949kB
Очистить все!
Итак, мы рассмотрели очистку дискового пространства, занятого контейнерами, образами и томами. В этом нам помогает субкоманда prune. Но ее можно использовать и на системном уровне docker, и она очистит все, что только сможет:
$ docker system prune
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache
Are you sure you want to continue? [y/N]
Если вы по каким-либо причинам экономите дисковое пространство на машине с Docker, то периодический запуск этой команды стоит ввести в привычку.
====================================================================
Так как в Docker более менее адекватный механизм удаления старых образов и контейнеров появился в версии 1.13: PR 26108 (за счет параметра prune который удаляет все старые контейнеры volume без контейнеров и образа без контейнеров), но зная что с каждой новой версией кол-во багов и проблем ростет, я лично не рискую обновляться, потому использую такие механизмы:
Удаление всех не используемых images
docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
Удаление контейнеров в статусе “exited”
docker rm $(docker ps -qa --no-trunc --filter "status=exited")
Удаление не используемых volume
docker volume ls -qf "dangling=true" | xargs docker volume rm
Все это можно совместить в один алиас для bash окружения:
alias docker-clean='
docker ps --no-trunc -aqf "status=exited" | xargs docker rm ;
docker images --no-trunc -aqf "dangling=true" | xargs docker rmi ;
docker volume ls -qf "dangling=true" | xargs docker volume rm'
Источник:
- https://demi4.com/kak-udalit-staryie-i-ne-ispolzuemyie-obrazyi-docker/
- https://habr.com/ru/post/486200/
Настройка репликации PostgreSQL в контейнерах Docker
Мы рассмотрим процесс поднятия двух контейнеров с PostgreSQL и настройки репликации данных между ними. Использовать будем систему на базе Linux, однако, сам процесс настройки Docker и репликации не зависит от операционной системы.
Подготовка компьютера
На компьютере, где мы будем запускать наш кластер баз данных должен быть установлен Docker. Также мы сразу рассмотрим развертывание нужной нам инфраструктуры в docker-compose. Для установки необходимой одноименной платформы смотрим инструкцию Установка Docker на Linux.
После мы можем переходить к поднятию контейнеров.
Запуск контейнеров с СУБД
Как говорилось выше, мы будем поднимать наши контейнеры с помощью docker-compose.
Создадим каталог, в котором будем работать:
mkdir -p /opt/docker/postgresql
Переходим в него:
cd /opt/docker/postgresql
Создаем файл для docker-compose:
vi docker-compose.yml
---
services:
postgresql_01:
image: postgres
container_name: postgresql_01
restart: always
volumes:
- /data/postgresql_01:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgres024
postgresql_02:
image: postgres
container_name: postgresql_02
restart: always
volumes:
- /data/postgresql_02/:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgres024
* рассмотрим некоторый опции подробнее:
- postgresql_01/postgresql_02 — названия для сервисов, контейнеры для которых мы будем поднимать.
- image — образ, из которого будут создаваться контейнеры. В данном примере мы берем официальный образ postgres.
- container_name — имя, которое будет присвоено контейнеру после его запуска. В нашем примере это postgresql_01 и postgresql_02.
- restart — режим перезапуска. Говорит, в каких случаях наш контейнер должен стартовать.
- volumes — хорошим тоном для работы базы данных в контейнере является проброс каталога хостового компьютера внутрь контейнера. Таким образом, после удаления контейнера данные останутся на компьютере. В нашем примере в каталоге /data будут созданы каталоги postgresql_01 и postgresql_02, которые будут прокинуты в каталог /var/lib/postgresql/data контейнера.
- environment — для первого запуска необходима инициализация базы данных. Без пароля системы выдает ошибку. Поэтому мы задаем переменную среды POSTGRES_PASSWORD.
Запускаем наши контейнеры:
docker-compose up -d
Мы должны увидеть:
Creating postgresql_02 ... done
Creating postgresql_01 ... done
А если вывести список контейнеров:
docker ps
… мы должны увидеть наши два.
Теперь можно переходить к настройке репликации.
Настройка репликации
Условимся, что первичный сервер или master будет в контейнере с названием postgresql_01. Вторичный — postgresql_02. Мы будем настраивать потоковую (streaming) асинхронную репликацию.
Настройка на мастере
Подключаемся к контейнеру docker:
docker exec -it postgresql_01 bash
Заходим под пользователем postgres:
su - postgres
Создаем пользователя, под которым будем подключаться со стороны вторичного сервера:
createuser --replication -P repluser
* в данном примере будет создаваться учетная запись repluser с правами репликации.
Система потребует ввода пароля. Придумываем его и набираем дважды.
Выходим из-под пользователя postgres:
exit
Выходим из контейнера:
exit
Открываем конфигурационный файл postgresql.conf:
vi /data/postgresql_01/postgresql.conf
Приводим к следующием виду некоторые параметры:
wal_level = replica
max_wal_senders = 2
max_replication_slots = 2
hot_standby = on
hot_standby_feedback = on
* где
- wal_level указывает, сколько информации записывается в WAL (журнал операций, который используется для репликации). Значение replica указывает на необходимость записывать только данные для поддержки архивирования WAL и репликации.
- max_wal_senders — количество планируемых слейвов;
- max_replication_slots — максимальное число слотов репликации (данный параметр не нужен для postgresql 9.2 — с ним сервер не запустится);
- hot_standby — определяет, можно или нет подключаться к postgresql для выполнения запросов в процессе восстановления;
- hot_standby_feedback — определяет, будет или нет сервер slave сообщать мастеру о запросах, которые он выполняет.
Посмотрим подсеть, которая используется для контейнеров с postgresql:
docker network inspect postgresql_default | grep Subnet
В моем случае, ответ был:
"Subnet": "172.19.0.0/16",
Теперь открываем файл:
vi /data/postgresql_01/pg_hba.conf
И добавляем строку после остальных «host replication»:
host replication all 172.19.0.0/16 md5
* в данном примере мы разрешили подключение пользователю replication из подсети 172.19.0.0/16 с проверкой подлинности по паролю.
Перезапустим докер контейнер:
docker restart postgresql_01
Настройка на слейве
Выполним настройку вторичного сервера. Для начала, удалим содержимое рабочего каталога вторичной базы:
rm -r /data/postgresql_02/*
* в данном примере мы удалим все содержимое каталога /data/postgresql_02.
Мы должны быть уверены, что в базе нет ничего важного. Только после этого стоить удалять данные.
Заходим внутрь контейнера postgresql_02:
docker exec -it postgresql_02 bash
Выполняем команду:
su - postgres -c "pg_basebackup --host=postgresql_01 --username=repluser --pgdata=/var/lib/postgresql/data --wal-method=stream --write-recovery-conf"
* где postgresql_01 — наш мастер; /var/lib/postgresql/data — путь до каталога с данными слейва.
Система должна запросить пароль для пользователя repluser — вводим его. Начнется процесс репликации, продолжительность которого зависит от объема данных.
Проверка
Смотрим статус работы мастера:
docker exec -it postgresql_01 su - postgres -c "psql -c 'select * from pg_stat_replication;'"
Смотрим статус работы слейва:
docker exec -it postgresql_02 su - postgres -c "psql -c 'select * from pg_stat_wal_receiver;'"
Источник: https://www.dmosk.ru/miniinstruktions.php?mini=postgresql-replication-docker
Многоступенчатая сборка Docker-образов / Multi-Stage Docker Builds
Это особенно актуально для приложений, разработка которых ведется на компилируемых языках программирования. Используя эту возможность, вы сможете существенно сокращать размер вашего итогового образа не, прибегая к хитрым трюкам, которые я описывал в статье «6 советов по уменьшению Docker образа» Суть подхода заключается в том, чтобы не заботиться о количестве получающихся слоев в процессе сборки вашего приложения и копировать результаты сборки из одного образа в другой. В этой статье я покажу, как это реализуется на практике. Выдумывать ничего не буду, а просто покажу вам это на уже готовых примерах из официальной документации.
Процесс до появления многоэтапных сборок
Одной из самых сложных задач по созданию Docker образов является уменьшение размера итогового образа, ведь каждая команда в Dockerfile добавляет отдельный слой к итоговому образу. Поэтому нам всегда нужно помнить, что необходимо очищать любые не нужные нам артефакты в текущем слое, прежде чем перейти к следующему. Чтобы написать действительно эффективный Dockerfile, нам традиционно необходимо было использовать кучу shell-трюков, чтобы с одной стороны сделать как можно меньше слоев, а с другой, чтобы оставить в каждом созданном слое минимальное количество артефактов.
Вообще говоря, это очень распространенная практика использовать несколько Dockerfile-ов в одном проекте: один для разработки (содержит все необходимое для создания вашего приложения), другой оптимизированный для создания итогового образа, который будет использоваться в продуктиве, в котором должно быть только ваше приложение и все то, что необходимо для его запуска. Эта практика в свое время получила название «шаблон строителя». Однако, поддержание в актуальном состоянии двух Dockerfile-ов не есть что-то удобное.
Вот пример двух Dockerfile-ов (Dockerfile.build и Dockerfile), речь о которых идет выше:
Dockerfile:
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
Dockerfile.build:
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN go get -d -v golang.org/x/net/html
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
И конечно же скрипт, автоматизирующий всю работу:
build.sh:
#!/bin/sh
echo Building alexellis2/href-counter:build
docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy
-t alexellis2/href-counter:build . -f Dockerfile.build
docker create --name extract alexellis2/href-counter:build
docker cp extract:/go/src/github.com/alexellis/href-counter/app ./app
docker rm -f extract
echo Building alexellis2/href-counter:latest
docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app
Когда вы запускаете build.sh скрипт, он создает первый образ, делает из него контейнер, чтобы скопировать артефакты, а затем создать второй образ. Оба образа занимают место на вашей системе, и еще какое-то место у вас занимают артефакты приложения на вашем локальном диске.
Многоэтапные сборки значительно упрощают эту ситуацию!
Использование многоэтапных (multi-stage) сборок
При многоэтапной сборке вы используете несколько операторов FROM в вашем Dockerfile. Каждая инструкция FROM использует произвольный базовый образ и начинает новый этап сборки. Вы можете выборочно копировать артефакты с одного этапа на другой, оставляя только то, что вам необходимо в конечном образе. Чтобы показать, как это работает, давайте адаптируем Dockerfile из предыдущего раздела для этого процесса.
Dockerfile:
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
Теперь вам нужен только один Dockerfile. Более того, вам больше не нужен и отдельный скрипт сборки. Просто запустите сборку образа привычной командой.
docker build -t avmaksimov/href-counter:latest .
Конечным результатом вашей является точно такой же крошечный продуктивный образ, однако сложность процесса значительно сократилась. Теперь вам не нужно создавать промежуточные образы, и вам больше не нужно извлекать промежуточные артефакты на вашу локальную систему.
Как это работает? Вторая команда FROM начинает новый этап сборки с базового образа alpine:latest. Инструкция COPY — from = 0 копирует артефакты с предыдущего этапа сборки на текущий этап, благодаря чему необходимые для сборки Go SDK и любые промежуточные артефакты не сохраняются в конечном образе.
Именование этапов сборки
По умолчанию этапы никак не называются, и вы ссылаетесь на них по их целочисленному номеру, начиная с 0 для первой инструкции FROM. Тем не менее, у вас есть возможность давать своим этапам понятные имена, добавив как в команду FROM. Этот пример улучшает предыдущий, используя имена для этапов. Это также означает, что даже если порядок инструкций в вашем Dockerfile со временем изменится, инструкции COPY не сломаются.
Dockerfile:
FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
С появлением в Docker поддержки многоэтапных сборок управлять процессами самих сборок стало значительно проще. Надеюсь, вы это тоже оцените! Побольше вам автоматизации и поменьше ручных операций!
Многоступенчатая сборка Docker-образов
В прошлом я уже говорил о создании миниатюрных Docker-образов, но теперь, когда у Docker появилась многоступенчатая сборка, пришла пора вернуться к этому вопросу. Раньше нам приходилось создавать бинарный файл на одном шаге и собирать docker-образ на другом. Это было немного неудобно и требовало некоторых ухищрений. Давайте посмотрим, как многоступенчатые сборки улучшили ситуацию.
Замечание. Для этого нужен Docker 17.05 или более поздний.
Начнем с простой программы на Go:
package mainimport "fmt"func main() {
fmt.Println("Hello world!")
}
Соберем ее с помощью образа golang:alpine одноступенчатым способом (single-stage build). Вот Dockerfile:
FROM golang:alpine
WORKDIR /app
ADD . /app
RUN cd /app && go build -o goapp
ENTRYPOINT ./goapp
Теперь соберем образ и запустим контейнер:
docker build -t treeder/hello .
docker run --rm treeder/hello
Все работает, но давайте посмотрим на размер с помощью docker images | grep treeder/hello.

258 МБ — многовато для крошечного бинарного файла. Теперь давайте попробуем многоступенчатуюсборку (multi-stage build) с использованием нового Dockerfile:
# стадия сборки
FROM golang:alpine AS build-env
ADD . /src
RUN cd /src && go build -o goapp
# финальная стадия
FROM alpine
WORKDIR /app
COPY --from=build-env /src/goapp /app/
ENTRYPOINT ./goapp
Соберем и запустим снова:
docker build -t treeder/hello .
docker run --rm treeder/hello
Проверим размер:

6,35 МБ — намного лучше.
Что это означает? Что многоступенчатые сборки — отличная вещь. Ими стоит пользоваться практически в любом случае!
Установка Docker Portainer
В последнее время Docker набирает всё больше и больше популярности благодаря возможности быстро развертывать сложные приложения, состоящие из большого количества сервисов. Portainer — это система управления docker контейнерами в Linux. Она позволяет управлять как локальными контейнерами, так и удалёнными с помощью Docker API.
С помощью Portainer вы сможете отслеживать состояние контейнеров, запускать, останавливать и удалять их, развертывать новые приложения, а также многое другое. Сегодня мы поговорим как выполняется установка Docker Portainer на ваш компьютер, а также как пользоваться программой
КАК УСТАНОВИТЬ DOCKER PORTAINER
Для выполнения этой статьи вам понадобится уже установленный в вашей системе Docker. Я не буду подробно рассказывать как установить docker и docker-compose. Для этого воспользуйтесь этой статьей для Ubuntu или этой для CentOS.
После того, как Docker будет установлен, можно развернуть контейнер с Portainer. Было бы странно, если бы программа поставлялась в каком-либо другом виде. Создайте хранилище данных для Portainer:
docker volume create portainer_data
Для установки и запуска контейнера выполните:
docker run -d -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer

Эта команда настраивает автоматический запуск Portainer после перезагрузки, а также постоянное хранилище, чтобы ваши настройки не потерялись при удалении и повторном разворачивании контейнера. Чтобы убедится, что Portainer запущен выполните такую команду:
docker ps
НАСТРОЙКА PORTAINER
1. ВХОД
Получить доступ к программе вы можете через веб-интерфейс на порту 9000. Откройте его в браузере. На первом шаге надо будет ввести имя пользователя и пароль, под которым вы будете входить в систему:

Затем выберите метод подключения к Docker. Для начала можно подключиться к локальному сервису Docker. Для этого выберите Local:

2. СПИСОК УЗЛОВ И КОНТЕЙНЕРОВ
После нажатия кнопки Connect вы попадите в панель управления контейнерами:

Сначала вам надо выбрать узел, на котором вы будете управлять контейнерами, в данном случае, это local. Здесь вы можете уже управлять вашими контейнерами. Например, в разделе Containers можно посмотреть все доступные контейнеры:

А в разделе Stacks — все доступные приложения:

3. РАЗВОРАЧИВАНИЕ ПРИЛОЖЕНИЯ
В разделе App Templates вы можете развернуть новое приложение на основе одного из существующих шаблонов. Например, давайте развернем WordPress. Для этого найдите его в списке:

Затем шаблон надо настроить. В данном случае, надо указать пароль для базы данных и имя приложения:

После этого нажмите кнопку Deploy the stack и новое приложение появится в списке раздела Stasks. Как видите, теперь программа сообщает, что у неё есть полный контроль над этим приложением, потому что она его создала:
4. УПРАВЛЕНИЕ ПРИЛОЖЕНИЕМ
Если вы откроете приложение, там будут отображены его контейнеры. По каждому контейнеру можно посмотреть логи, открытые порты и даже подключится через терминал. Чтобы увидеть логи нажмите на иконку похожую на записную книжку возле состояния контейнера:

Для командной строки рядом есть значок с символом приглашения ввода. Вот так выглядит командная строка:

А открытые порты находятся в самом конце характеристик контейнера в разделе Published ports:

Если вы кликните по ссылке с надписью 32768:80 для контейнера WordPress, то попадёте на сайт WordPress:

КАК ОБНОВИТЬ PORTAINER
Чтобы обновить Portainer надо удалить текущий образ и запустить его снова. Это не вызовет никаких проблем, так как при создании предыдущего образа мы использовали внешнее хранилище для хранения данных. Остановите образ и удалите его:
docker stop portainer
docker rm portainer
Скачайте новую версию:
docker pull portainer/portainer
Затем осталось снова установить Portainer:
docker run -d -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
ВЫВОДЫ
Как видите установка Portainer очень простая, если у вас есть уже установленный docker, а далее программа только помогает пользоваться контейнерами. Вы можете подключить к ней и удалённые узлы, однако для этого надо, чтобы у них был публичный IP адрес, потому что в локальной сети взаимодействовать с ними вы не сможете. А вы используете Portainer или пользуетесь другим интерфейсом для управления Docker? Напишите в комментариях!
Источник: https://losst.ru/ustanovka-docker-portainer
🐳 Ручной деплой Docker образов в продакшен
Когда-то в нашей стране существовали различные необычные профессии, которые теперь остались далеко в истории, но многие удалось упомянуть и запечатлеть, например, в литературных произведениях и картинах. Знаменитая картина Ильи Репина “Бурлаки на Волге” как раз показывает ту самую профессию, что была когда-то и потом просто исчезла, но память о ней осталась и по сей день.
Кто такие “Бурлаки”
В 18 веке в нашей стране имела популярность деятельность бурлаков. Бурлаками являлись наемные рабочие, которые выполняли очень тяжелую и монотонную работу. Их работа заключалась в том, чтобы вдоль берега реки тянуть судно против течения. Такая работа выполнялась с помощью специальной веревки, так называемой бечевы. Бечева обвязывалась на плечах бурлака, а другой конец цеплялся за нос корабля. Такую работу могла выполнить только группа людей, поэтому Бурлаки объединились в бригады и трудились все вместе. В каждой бригаде были свои роли ведущего, замыкающего и других. У бурлаков были даже свои собственные песни, которые они часто напевали во время работы. Их деятельность стала исчезать в тот момент, когда стало больше пароход.