Docker
- Запуск контейнера
- Еще полезные аргументы:
- Запуск команды в контейнере:
- Остановка контейнера
- Удаление контейнера
- Просмотр всех созданных контейнеров
- Запуск консоли в контейнере винды:
- Сохранение образа в файл:
- Загрузка образа из файла:
- Удаление образа с диска:
- Список образов:
- Удаление всех образов с тэгом none:
- Удаление всех образов с rancher в названии:
- Вывод инфы об контейнере:
- Open container
- Links
- Вывод всех образов в приватном репозитории:
- Ограничение контейнеров**
- Пользователи
- Создание образа из контейнера:**
- Union file system
- Docker file system
- Network problems
Запуск контейнера
docker run --name mytarantool -d -p 3301:3301 -v c:/tarantool/data:/var/lib/tarantool tarantool/tarantool:1.7
--name - задает имя для контейнера
-d - detach, означает что контейнер будет запущен в фоне. Следует использовать для всяких бэкграундных сервисов/демонов
-p - проброс портов от контейнера на хост. Первое значение - порт хоста, второе - порт контейнера.
-v - маунт папки хоста в папку контейнера. Формат - key:value, где key - папка хоста, value - папка контейнера (можно не указывать key, тогда будет создана docker-managed volume, см. ниже). При этом если у образа уже есть такая папка с каким-то содержимым, то это содержимое будет стерто полностью. Чтобы этого избежать, можно маунтить не директории, а отдельные файлы.
Если на windows ошибок при маунте директорий нет, а директории в образе все равно пустые, то надо в настройках докера поставить галочки у дисков в разделе Shared Drives. Если уже стоят - снять и поставить обратно
-v key:value:ro - указание :ro в конце позволяет замаунтить папку как read-only.
--volumes-from - позволяет указать ранее созданный контейнер, с которым будут шариться волюмы
Еще полезные аргументы:
-i --interactive - оставлять стандартный поток ввода открытым даже если не приаттачено никакого терминала
-t --tty - выделить виртуальный терминал для контейнера, что позволит передавать сигналы внутрь контейнера
Обычно для интерактивных программ используется сразу -it
-e --env - позволяет задать переменную окружения, например, -e MY_ENVIRONMENT_VAR="test"
--restart always - указывает, что после завершения работы контейнер нужно рестартануть. Первый раз рестартует через 1 секунду, потом через 2, 4 и так далее.
docker run -it <image_name> /bin/sh \- запускает контейнер и сразу в интерактивном режиме баш в нем
docker logs <container_name> \- выводит все, что вывелось в контейнере в stdout и stderr
docker create <image_name% \- то же, что и run, только запускается остановленным
docker ps \- показывает список ==запущенных== контейнеров. Если нужны все, то указать флаг **-a**
Запуск команды в контейнере:
docker exec -it mytarantool bash
здесь mytarantool - имя контейнера, bash
- команда
Остановка контейнера
docker stop mytarantool
Удаление контейнера
docker rm mytarantool
Чтобы docker for windows имел доступ к дискам, нужно сделать диск доступным в меню Settings -> Shared Drives
Просмотр всех созданных контейнеров
docker ps
Создание своего образа: https://docs.docker.com/engine/getstarted/step_four/#step-2-build-an-image-from-your-dockerfile
Запуск консоли в контейнере винды:
docker run -it microsoft/windowsservercore cmd
## Поиск образов:
Искать образы удобнее в консоли:
docker search rabbitmq
Найдя нужный образ, качаем его:
docker pull rabbitmq
Сохранение образа в файл:
docker save -o myfile.tar busybox:latest
Загрузка образа из файла:
docker load -i myfile.tar
Удаление образа с диска:
docker rmi busybox
Список образов:
docker images
Удаление всех образов с тэгом none:
docker rmi $(docker images -f 'dangling=true' -q)
Удаление всех образов с rancher в названии:
docker rmi $(docker images | gren 'rancher' | tr -s ' ' | cut -d ' ' -f 3)
Вывод инфы об контейнере:
docker inspect <container id>
Выдастся большой json, если нужен его определенный узел, то можно через -f указать узел, пример:
docker inspect -f "{{json .HostConfig.Binds}}" bmweb
выдаст все мапы волюмов
docker inspect -f "{{json .Mounts}}" bmweb
аналогично, но более подробно
Построение образа из Dockerfile:
docker build -t dia_ch3/dockerfile:latest ch3_dockerfile
-t - имя полученного образа. По умолчанию будет установлен в локальный регистр с указанным именем.
последний аргумент - папка, в которой лежит Dockerfile и все сопутствующие файлы
Волюмы бывают двух типов:
bind mount - обычный, где папка контейнера мапится на пользовательскую папку хоста
docker-managed volume - папка контейнера маппится на папку хоста, созданную демоном докера (/var/lib/docker/vfs/dir/<some volume ID>
)
Быстрое поднятие веб-сервера, раздающего папку с файлами с локального диска:
docker run -d --name bmweb -v c:\userfiles:/usr/local/apache2/htdocs -p 80:80 httpd:latest**
Volume container - контейнер, предоставляющий волюм, чтобы остальные контейнерымогли использовать его в флаге --volumes-from. Так же позволяет избежать orphane volume, когда на волюм не осталось ссылок и его никто не удалил и он занимает место на диске.
Data-packed volume container - это такой контейнер, который не содержит в себе никакого приложения, но содержит контент, который при старте копируется в шаренный волюм.
Полиформный контейнер - такой контейнер, поведение которого определяется содержимым его волюма. Например, контейнер содержит NodeJS и запускает приложение по адресу /app/app.js, а это волюм и определяется хостом.
А вот так можно задавать разные конфиги одному и тому же приложению в образе:
docker run --name devConfig -v /config dockerinaction/ch4_packed_config:latest /bin/sh -c 'cp /development/* /config/'
docker run --name prodConfig -v /config dockerinaction/ch4_packed_config:latest /bin/sh -c 'cp /production/* /config/'
docker run --name devApp --volumes-from devConfig dockerinaction/ch4_polyapp
docker run --name prodApp --volumes-from prodConfig dockerinaction/ch4_polyapp
4 архетипа контейнеров по отношению к закрытости сети:
- closed container
- bridged container
- joined container
- open container
Closed container**
Может общаться только с собой через loopback-интерфейс. Создается, используя --net none
при создании контейнера
Bridged container
Дефолтный вариант. Имеет loopback-интерфейс и приватный интерфейс, соединенный с хостом через мост. Создается, используя --net bridge
.
docker port <container id>
- показывает список всех замапленных портов.
--hostname - задает имя хоста, видимое только изнутри этого же самого контейнера.
--dns - задает главный DNS-сервер. Можно задать несколько таких серверов, а еще можно этот флаг задать демону dockerd и эти сервера будут по умолчанию заданы всем создаваемым контейнерам.
--dns-search - позволяет задать задать домен, по которому будут искаться все адреса, для которых не указан домен. Таким образом можно, например, различать тестовое и продакшн-окружение.
--add-host - позволяет задать ip для определенного хоста (как запись в hosts в Windows)
-p/--publish - позволяет задать порт-форвардинг
-P//--publish-all - паблишит все порты, которые заданы через EXPOSE
--expose - заэкспоузить дополнительный порт
--icc=false - отключает inter-container communication
--bip - позволяет задать IP-адрес контейнера и его subnet range, пример: --bip "192.168.0.128/25"
--mtu - задает MTU, максимальный размер пакета в байтах
-b/--bridge - позволяет задать собственный интерфейс моста
Joined container
Это несколько контейнеров, которые шарят общий сетевой стэк.
Docker в этом случае предоставляет интерфейсы, созданные специально для одного контейнера, другим контейнерам.
Для этого типа соединения первый контейнер создается как угодно, а затем к нему присоединяются остальные.
Присоединяются так: docker run --net container:<first container id> ...
Полезно, когда нужно чтобы 2 контейнера могли общаться меж собой, но не могли общаться с внешним миром. Тогда создаем первый закрытым и присоединяем к нему второй.
Open container
--net host Никакой изоляции, видит все те же сетевые интерфейсы, что и хост.
Links
Можно связать контейнеры однонаправленной связью через --link.
Пример:
docker run -d --name a mysql
docker run -d --name b --link a:db
При этом в контейнер b будут добавлены:
- переменные окружения для связи с a
- запись в dns, сопоставляющая ip контейнера a с хостом db
- если icc=false, то будут также добавлены правила в фаерволлы контейнеров для сетевого взаимодействия между ними
Сценарий использования: в контейнере b сервис ожидает, что по адресу tcp://db:3306 будет база данных. Или же он может брать порт базы из переменной окружения DB_PORT. Когда делаем --link оба этих значения будут автоматически заполнены.
Хорошей практикой считается на старте контейнера проверять, что заданы все необходимые переменные окружения/записи днс. Пример такого скрипта:
if [ -z ${DATABASE_PORT+x} ]
then
echo "Link alias 'database' was not set!"
exit
else
exec "$@"
fi
Список переменных окружения, задаваемых при линке:
docker run -d --name mydb --expose 2222 --expose 3333 --expose 4444/udp alpine:latest nc -l 0.0.0.0:2222
docker run -it --rm --link mydb:database dockerinaction/ch5_ff env
DATABASE_PORT=tcp://172.17.0.23:3333 - любой из полных адресов для exposed-портов
DATABASE_PORT_3333_TCP=tcp://172.17.0.23:3333
DATABASE_PORT_2222_TCP=tcp://172.17.0.23:2222
DATABASE_PORT_4444_UDP=udp://172.17.0.23:4444
DATABASE_PORT_2222_TCP_PORT=2222
DATABASE_PORT_3333_TCP_PORT=3333
DATABASE_PORT_4444_UDP_PORT=4444
DATABASE_PORT_3333_TCP_ADDR=172.17.0.23
DATABASE_PORT_2222_TCP_ADDR=172.17.0.23
DATABASE_PORT_4444_UDP_ADDR=172.17.0.23
DATABASE_PORT_2222_TCP_PROTO=tcp
DATABASE_PORT_3333_TCP_PROTO=tcp
DATABASE_PORT_4444_UDP_PROTO=udp
DATABASE_NAME=/furious_lalande/database - имя текущего контейнера + / + имя алиаса
Проблема с линками в том, что если контейнер-зависимость перезапустится, то у него будет новый Ip и все эти переменные станут неактуальными. Решением может быть использование DNS, либо же включение nter-process communication.
Вывод всех образов в приватном репозитории:
curl -X GET gitlab.factory.vocord.ru:5000/v2/_catalog
Ограничение контейнеров**
-m/--memory - ограничение по памяти, пример --memory 256m
\--cpu-shares - относительное ограничение CPU. Если одному связанному контейнеру указать 512, а другому 256, то второй будет получать 1 цикл на каждые 2цикла первого.
\--cpuset-cpus - указывает контейнерам, что они должны выполняться только на определенных ядрах
\--device /dev/video0:/dev/video0 - маппит девайсы внутрь контейнера
\--ipc container:<container id> \- шарит shared memory с указанным контейнером
\--ipc host - шарить всю память с хостом
Пользователи
docker run --rm --entrypoint "" busybox:latest whoami
- вывод дефолтного пользователя внутри контейнера
-u/--user - указывает пользователя, под которым запустить контейнер
Пример: docker run -u nobody:default
- устанавливает юзера nobody и группу default
Создание образа из контейнера:**
- Скачиваем образ
- Делаем изменения в его файловой системе
docker run --name hw_container ubuntu:latest touch /HelloWorld
- Коммитим эти изменения как новый контейнер
docker commit hw_container hw_image
Флаги commit:
-a - подписывает образ авторской строкой
-m - commit message
-c --change - позволяет дописывать строки в Dockerfile, пример: docker commit -c "CMD /notebook.sh" notebook notebook-my
Пример:
docker commit -a "@dockerinaction" -m "Added git" image-dev ubuntu-git
entrypoint
- Это программа, которая будет запущена при старте. Она тоже коммитится в образ.
docker run --name cmd-git --entrypoint git ubuntu-git
Кроме этого коммитятся:
- все переменные окружения
- рабочая директория
- открытые порты
- объявления волюмов
- точка входа в контейнер
- сама команда и все ее аргументы
docker diff <container id>
- выводит список изменений в файловой системе контейнера относительно образа, из которого он создан. A - added, C - changed, D - deleted.
Union file system
В докере используется UFS, в которой файловая система представляется в виде объединения слоев. Редактируемый слой - только верхний, остальные все read-only. Новый слой сохраняется при коммите образа.
Благодаря такой системе, слои могут переиспользоваться в нескольких контейнерах, вместо того чтобы каждый раз копироваться. Однако есть и недостаток - старые слои никогда не удаляются, а значит образ может только увеличиваться в размерах и никогда не уменьшаться.
Максимальное количество слоев - 42.
docker history
- выводит все коммиты/слои файловой системы
docker export --output export.tar ubuntu
- сохраняет всю файловую систему контейнера в архив на хосте
docker import -c "ENTRYPOINT [\"/hello\"]" hello.tar dockerinaction/ch7_static
- берет .tar с файлами и создает из них образ
Dockerfile
Пример докерфайла:
FROM ubuntu:latest
MAINTAINER "dockerinaction@allingeek.com"
RUN apt-get update
RUN apt-get install -y git
ENTRYPOINT ["git"]
Построение:
docker build --tag ubuntu-git:auto .
- точка на конце означает текущую директорию
Все команды, описанные в докерфайле (включая RUN
) будут выполнены на этапе построения образа.
Первая команда всегда должна быть FROM
. Если нужен пустой родитель, то можно указать FROM scratch
.
Каждая инструкция в докерфайле создает новый слой в образе. Поэтому лучше эти инструкции по максимуму объединять, чтобы уменьшить размер образа. Если возникла ошибка на шаге N, то при повторной попытке шаги 1..N-1 будут взяты из кэша. Но если хочется не юзать кэш, то можно указать флаг --no-cache
.
Всего есть 14 инструкций Dockerfile.
В файле .dockerignore
можно перечислить, какие файлы не включать в образ.
Инструкции
ENV - задает переменные окружения, пример: ENV APPROOT="/app" APP="mailer.sh" VERSION="0.6" (здесь задается 3 переменных). Заданные переменные окружения можно использовать в последующих инструкциях, используя синтаксис ${NAME}
** LABEL** - дополнительные пары ключ-значение, записываемые в метаданные образа. Пример: LABEL base.name="Mailer Archetype" base.version="${VERSION}"
WORKDIR- задает working directory, то есть ту директорию, в которой начнется исполнение образа. Пример: WORKDIR $APPROOT
EXPOSE- Открывает порт. Пример: EXPOSE 33333
ENTRYPOINT- команда, которую запускать при старте образа. Можно либо задать команду консоли (shell form), либо массив строк, в котором первая строка - путь к исполняемому файлу, а остальные - аргументы запуска (exec form). shell form при этом менее гибок, так как не позволяет задавать последний аргумент в docker run или предоставлять дополнительные аргументы в инструкции CMD. Дефолтный entrypoint - /bin/sh
COPY - копирует файлы в образ. Принимает массив строк, где последний аргумент - папка назначения, а все остальные - копируемые файлы. Пример: COPY ["./log-impl", "${APPROOT}"]
VOLUME- создает волюм, то же самое что и --volume. Только нельзя задать bind-mount или read-only волюм. В массиве аргументов для каждого значения будет создан волюм. Пример: VOLUME ["/var/log"]
.
CMD- задает аргументы для entrypoint. Если entrypoint не задан, либо задан в shell-форме, то это может быть команда для шелла, так как будет использован дефолтный entrypoint.
ONBUILD- инструкция, которая будет выполняться, если текущий образ будет использован как базовый для другого образа. Эти инструкции записываются в метаданные текущего образа. С их помощью можно, например, сбилдить программу, предоставляемую образом-потомком. ONBUILD-инструкции выполняются сразу после FROM в потомке.
Docker file system
Используется файловая система overlay2.
https://docs.docker.com/storage/storagedriver/overlayfs-driver/#how-the-overlay2-driver-works
Слои для каждого контейнера перечислены здесь:
/var/lib/docker/image/overlay2/layerdb/mounts/%CONTAINER_ID%
Там есть 3 файла:
- init-id - это идентификатор слоя, используемый при создании контейнера
- mount-id - идентификатор слоя, который был образован в процессе работы контейнера
- parent - какой-то идентификатор, видимо, родительского слоя
По идентификатору слоя можно получить его содержимое так:
/var/lib/docker/overlay2/%LAYER_ID%
Network problems
Если во всех образах перестала работать сеть, то можно пересоздать мост:
pkill docker
iptables -t nat -F
ifconfig docker0 down
ip link del docker0
systemctl start docker
Если ifconfig
не найден, то apt install net-tools
Автоматическая очистка места на диске
По адресу /etc/cron.daily/docker-gc
создаем скрипт следующего содержания:
#!/bin/sh -e
# Delete all stopped containers (including data-only containers).
docker ps -a -q --no-trunc --filter "status=exited" | xargs --no-run-if-empty docker rm -v
# Delete all tagged images more than a month old
docker images --no-trunc --format '{{.ID}} {{.CreatedSince}}' | grep ' months' | awk '{ print $1 }' | xargs --no-run-if-empty docker rmi -f || true
# Delete all 'untagged/dangling' (<none>) images
# Those are used for Docker caching mechanism.
docker images -q --no-trunc --filter dangling=true | xargs --no-run-if-empty docker rmi
# Delete all dangling volumes.
docker volume ls -qf dangling=true | xargs --no-run-if-empty docker volume rm
Даем ему права:
sudo chmod +x /etc/cron.daily/docker-gc
Добавляем новый джоб в crontab
:
crontab -e
Открывается текстовый редактор, нужно в него добавить строчку:
0 0 * * * /etc/cron.daily/docker-gc
не забыть последнюю строчку сделать пустой (иначе кронтаб ругаться будет) и выйти с сохранением
Файловая система докера
По умолчанию находится в папке /var/lib/docker
Структура файловой системы зависит от используемого драйвера хранилища. У меня на Debian это по уомлчанию overlay2, поэтому дальнейшая информация справедлива для него. Узнать свой драйвер можно командой docker info
в графе Storage Driver
В /var/lib/docker
можем найти следующие подпапки:
- builder
- buildkit
- containerd
- containers
- image
- network
- overlay2
- plugins
- runtimes
- swarm
- tmp
- trust
- volumes
Описания образов лежат по адресу: /var/lib/docker/image/overlay2/imagedb/content/sha256
.
Например, если мы ходим посмотреть описание образа с идентификатором ebaee1a37cda...
, то нам нужно в текстовом редакторе открыть файл /var/lib/docker/image/overlay2/imagedb/content/sha256/ebaee1a37cda...
. В этом текстовом файле лежит в основном та же инфа, которую мы получаем через docker inspect ebaee1a37cda
, только иначе структурирована.
Но в этом файле мы не найден идентификаторы слоев образа для overlay2, те идентификаторы которые там указаны - они для RootFS (так и не понял, зачем нужны). Полезные идентификаторы будем брать из вывода docker inspect ebaee1a37cda
:
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/1ef930458b9b7c4b107589bfa8708a5a905c0bff78fe82c04db1b65908ca356f/diff:/var/lib/docker/overlay2/990d47bc6c20549415caca3a6f6cfffc43b34491f128eeafffc2a8214c86ff1a/diff:/var/lib/docker/overlay2/6465e9cbb477e85f312139fd430c82649d5559190f2eed4fe32d1b2c2ca9bd9b/diff:/var/lib/docker/overlay2/7a924b04842adb199e077c02c4661955fb318d5d276c5c66b2e07628a69fc49d/diff:/var/lib/docker/overlay2/b4cf7f55f924b2167262f246ecdd8d2400adfbac69d0258049c619293228b6f0/diff",
"MergedDir": "/var/lib/docker/overlay2/cf7ef417035f8dcbaabecdb4897a94ef1ad00b0919ddf4f7af0b1150b9c7bd8f/merged",
"UpperDir": "/var/lib/docker/overlay2/cf7ef417035f8dcbaabecdb4897a94ef1ad00b0919ddf4f7af0b1150b9c7bd8f/diff",
"WorkDir": "/var/lib/docker/overlay2/cf7ef417035f8dcbaabecdb4897a94ef1ad00b0919ddf4f7af0b1150b9c7bd8f/work"
},
"Name": "overlay2"
},
Верхний слой описан в поле MergedDir
. Пройдем по этому адресу:
$ cd /var/lib/docker/overlay2/cf7ef417035f8dcbaabecdb4897a94ef1ad00b0919ddf4f7af0b1150b9c7bd8f/
$ ls
diff link lower work
В файле link
содержится сокращенный идентификатор слоя:
$ cat link
HRKAB26PG22YEF25QW3DFUSPVO
В lower
- сокращенные идентификаторы нижних слоев, разделенные двоеточием:
$ cat lower
l/ZJPNNPNGB2QUT7UHS6FGJAJEXS:l/ITT6VDHHVLTL4XSV4MKXFARWZX:l/D73DFE6U73XW47KLW2IWYFN3X4:l/GPI73DKAIBG4M4HEPH22KTQBLS:l/AIQ55Z4PNLZZZTY5PWAD7EY74K
В папке /var/lib/docker/overlay2/l
содержатся симлинки от сокращенных идентификаторов слоев к нормальным:
y# ls /var/lib/docker/overlay2/l/ZJPNNPNGB2QUT7UHS6FGJAJEXS -l
lrwxrwxrwx 1 root root 72 Jan 22 11:17 /var/lib/docker/overlay2/l/ZJPNNPNGB2QUT7UHS6FGJAJEXS -> ../1ef930458b9b7c4b107589bfa8708a5a905c0bff78fe82c04db1b65908ca356f/diff
Как видим, второй сверху слой нашего образа ZJPNNPNGB2QUT7UHS6FGJAJEXS
соответствует слою 1ef930458b9b7c4b107589bfa8708a5a905c0bff78fe82c04db1b65908ca356f
. А его содержимое можем посмотреть в папке diff
(посмотрим самый нижний слой нашего образа, содержащий файловую систему базового образа):
y# ls /var/lib/docker/overlay2/l/AIQ55Z4PNLZZZTY5PWAD7EY74K
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var