Две части курса – вводная и основная: 8 + 8 ак. часов
- RAM ≥ 4Gb
- Права локального админа для аккаунта участника
- Доступен git repo с данным руководством {{ git-repo }}
https://github.com/eugene-krivosheyev/docker
- Доступен {{ registry-host }}
https://hub.docker.com
(Docker Hub) - Зарегистрирована учетная запись {{ registry-account }} на {{ registry-host }}
- Установлен Docker CE или совместимый менеджер контейнеров (e.g. Podman)
sudo dnf install -y docker
- Intro to Docker
- Введение в Docker 45
- Hands-on practice quest #00: prerequisites sound-check 30 + 5
- Жизненный цикл готового образа 60
- Hands-on practice quest #01: pre-built disk image lifecycle 30 + 5
- Жизненный цикл контейнера 45
- Hands-on practice quest #02: container lifecycle 30 + 5
- Контейнеризация простого сервиса: автоматическая сборка образа "с нуля" 45
- Hands-on practice quest #03-1: preparing base image with JRE 25 + 5
- Hands-on practice quest #03-2: simple application containerization 25 + 5
- Введение в контейнеризацию составного приложения 20
- Hands-on practice quest #04: multi-component application containerization 15 + 5
- Виртуализация сети 30
- Hands-on practice quest #05: networked multi-component application containerization 15 + 5
- Изоляция данных 45
- Hands-on practice quest #06: multi-component stateful application containerization 15 + 5
- Изоляция хостовых ресурсов 30
- Hands-on practice quest #07: networked multi-component stateful app resource-limited containerization 15 + 5
- Оптимизация сборки образов 30
- Hands-on practice quest #08: build-optimized networked multi-component stateful application resource-limited containerization 15 + 5
- Оркестрация 30
- Hands-on practice quest #09: orchestration of build-optimized networked multi-component stateful application resource-limited containerization 15 + 5
- Рекомендуемые практики 30
- Hands-on practice quest #10: build-optimized networked multi-component stateful application resource-limited best practice based containerization 15 + 5
- Docker + QA = ♡ 30 [optional]
-
Зачем нужен Docker? Метафора морского контейнера
-
Каковы функции инструментов контейнеризации?
-
development environment?
-
testing environment?
-
production environment?
-
Виртуализация (инфраструктуры) VS контейнеризация (приложения)?
-
место на диске для гостевой ОС
-
время запуска гостевой ОС
-
overhead операций гостевой ОС
-
Контейнер – процесс ОС "на стероидах": какие ресурсы необходимо изолировать и инкапсулировать?
-
disk image provisioning tool (dockercli) and Dockerfile
-
disk image
-
disk image registries: docker hub and corporate registries
-
container = running process + container data (container layer)
puml
@startuml
node "container\nregistry" {
database "disk\nimage" as disk_image
}
node "host" {
rectangle "container" {
component "application"
database "container\nlayer" as layer
}
database "disk" {
database "image"
file "volume"
folder "shared\nfolder" as folder
disk_image - image
}
image --layer
volume -# container
folder -# container
}
container #--# host : "port\nmapping"
@enduml
-
Клиентский UI
-
docker cli
-
podman cli
-
GUI в дистрибутиве для MacOS
-
GUI в IDEA
-
...
-
dockerd может быть удаленный или в VM, а клиенты – локальные, в т.ч. windows
-
Альтернативные сборщики OCI-образов без участия нативных инструментов docker: jib
-
Итого
Контейнер – это новый экзешник. Переносимый, безопасный и управляемый.
-
Given
-
сделан форк данного руководства для собственных пометок
-
форк открыт в браузере для внесения пометок
-
для последующей удобной работы с copy+paste для ресурсов раздела Prerequisites плейсхолдеры заменены актуальными значениями
-
Сформированы пары участников с чередованием ролей в паре
-
Hint: синонимы команд docker cli
-
Hint:
... --help
-
Hint: docker cli reference
-
Hint: при работе в терминале используйте
Tab
и↑
для автоподстановки значений -
"Как описать сценарий использования команд?"
В общем случае:
- предварительные команды подготовки системы к действию
- команды действия
- команды проверки успешности и корректности действия
- When участники именуют сценарии, выполняют команды и анализируют их вывод и поведение
- Сценарий "Как ...?"
docker version # TODO: собственные пометки участников для будущего использования в проектах
docker system info
docker system df
docker events
- Сценарий "Как ...?"
(в новом ssh shell, чтобы параллельно видеть вывод
docker events
)
docker logout
open https://hub.docker.com/settings/security # to make Access Token
docker login -u {{ registry-account }} -p {{ access-token }} # login default hub.docker.com registry
- Сценарий "Как ...?"
docker image pull alpine
docker system df
- Сценарий "Как ...?"
docker container ls [--all]
docker container run --name demo -it alpine
/# cat /etc/os-release
/# exit
- Сценарий "Как ...?"
docker container ls [--all]
docker container rm [--force] demo
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Успешна ли сконфигурирована система для использования docker?
- Какая версия API?
- Откуда взялся образ диска?
- Сколько места занимает образ?
- Сколько места занимает контейнер?
- Какая версия образа скачивается по умолчанию?
- Какая гостевая команда запускается при запуске контейнера?
- Задача среды исполнения контейнеров: изоляция диска
- Отображение диска контейнера на диск хостовой системы: образ
- Что должно быть на диске для запуска и работы контейнеризованного приложения?
- Состав образа диска (от
scratch
до prod-ready) - OS libraries
- OS package manager (для удобной установки зависимостей, нужен на этапе сборки образа)
- OS commands (для отладки, мониторинга и траблшутинга контейнеров на этапе эксплуатации)
- Application libraries
- Application executables
- Config files
- Data files
$ docker run --rm -it alpine ls
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
- Идентификация образов:
id
как хеш [слоя] образахост/репозиторий/имя:тег
хост/репозиторий/группа/имя:тег
- Жизненный цикл образа в репозитории и аналогии с git
docker container run
+ side effects >docker container commit
>docker image push
# редко используемый "ручной" ЖЦdocker image build
>docker image push
>docker image pull
|docker container run
# штатный автоматизированный ЖЦ
$ docker image ls [--all]
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest b0e47758dc53 6 weeks ago 5.33MB
hello-world latest a29f45ccde2a 19 months ago 9.14kB
$ docker image history ekr26/backend:1.0.0
IMAGE CREATED CREATED BY SIZE COMMENT
e96641ea7cdf 2 days ago COPY dbo-1.0-SNAPSHOT.jar /dbo # buildkit 65.9MB buildkit.dockerfile.v0
<missing> 2 days ago ENTRYPOINT ["java" "-jar" "dbo-1.0-SNAPSHOT.… 0B buildkit.dockerfile.v0
<missing> 2 days ago WORKDIR /dbo 0B buildkit.dockerfile.v0
<missing> 2 days ago RUN /bin/sh -c mkdir -p /dbo # buildkit 0B buildkit.dockerfile.v0
<missing> 2 days ago EXPOSE map[8080/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 5 days ago /bin/sh -c #(nop) ENV JAVA_HOME=/opt/java/o… 0B
<missing> 5 days ago /bin/sh -c set -eux; apk add --no-cache … 96.9MB
<missing> 5 days ago /bin/sh -c #(nop) COPY multi:b8938281d618ac3… 16.7kB
<missing> 5 days ago /bin/sh -c #(nop) ENV JAVA_VERSION=jdk8u282… 0B
<missing> 5 days ago /bin/sh -c apk add --no-cache tzdata --virtu… 14.2MB
<missing> 5 days ago /bin/sh -c #(nop) ENV LANG=en_US.UTF-8 LANG… 0B
<missing> 6 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 6 days ago /bin/sh -c #(nop) ADD file:f77db8e5b937d8ebb… 5.58MB
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz images -t
├─<missing> Virtual Size: 5.6 MB
│ └─<missing> Virtual Size: 5.6 MB
│ └─<missing> Virtual Size: 5.6 MB
│ └─<missing> Virtual Size: 19.8 MB
│ └─<missing> Virtual Size: 19.8 MB
│ └─<missing> Virtual Size: 19.8 MB
│ └─<missing> Virtual Size: 116.7 MB
│ └─<missing> Virtual Size: 116.7 MB
│ ├─<missing> Virtual Size: 116.7 MB
│ │ └─<missing> Virtual Size: 116.7 MB
│ │ └─<missing> Virtual Size: 116.7 MB
│ │ └─<missing> Virtual Size: 136.7 MB
│ │ └─<missing> Virtual Size: 136.7 MB
│ │ └─3c1355b22f16 Virtual Size: 136.7 MB Tags: training-docker/ekr-stub:1.0.0
│ └─<missing> Virtual Size: 116.7 MB
│ └─<missing> Virtual Size: 116.7 MB
│ └─<missing> Virtual Size: 116.7 MB
│ └─<missing> Virtual Size: 116.7 MB
│ └─e96641ea7cdf Virtual Size: 182.6 MB Tags: training-docker/ekr-backend:1.0.0
- Ограничение количества слоев? 127
-
docker image history
+docker image inspect
- Вспомогательные утилиты
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nate/dockviz images -t
docker run -v /var/run/docker.sock:/run/docker.sock -ti -e TERM tomastomecek/sen
- dive
-
Given пары участников
-
When участники именуют сценарии, выполняют команды и анализируют их вывод и поведение
-
Сценарий "Как ...?"
docker image ls # TODO: собственные пометки участников для будущего использования в проектах
- Сценарий "Как ...?"
docker image pull alpine
docker image ls
- Сценарий "Как ...?"
docker image history alpine
docker image inspect alpine
docker image inspect --format='{{.Id}} -> {{.Parent}}' alpine
- Сценарий "Как ...?"
docker container run --name demo -it alpine
/# touch side-effect.txt
/# exit
docker container diff demo
docker container commit demo {{ registry-account }}/demo
docker image ls
- Сценарий "Как ...?"
docker image tag {{ registry-account }}/demo:latest {{ registry-account }}/demo:1.0.0
docker image ls
- Сценарий "Как ...?"
docker image push {{ registry-account }}/demo:1.0.0
- Сценарий "Как ...?"
docker image ls
docker container rm demo
docker image prune
docker image ls
docker image rm {{ registry-account }}/demo:1.0.0
docker image ls
docker image rm {{ registry-account }}/demo:latest
docker image ls
docker image prune --all
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Какие способы идентификации образа?
- Какой тег у образа по умолчанию при создании коммитом?
- Какой тег у образа по умолчанию при операции
pull
? - В чем физический смысл удаления образа командой
rm
? - Всегда ли удаляется образ по команде
rm
? - Что делает prune?
- Что такое dangling image?
-
container = running process + container data (container layer)
-
Что значит "запуск" контейнера? Что именно там запускается?
-
Как можно доопределить команду, запускаемую в контейнере?
-
Что нужно определить для запуска контейнера?
-
имя контейнера (+defaults)
-
disk image
-
virtual network
-
folder | volume mapping
-
entry point (image
entrypoint
override) -
guest environment variables
-
command line arguments (image
cmd
override) -
Экстернализация конфигурации приложения при запуске контейнера
-
docker container create
+docker container start
=docker container run
[args]
-
docker container pause
,docker container unpause
-
docker container commit
-
просмотр работающих и остановленных контейнеров
docker container ls [--all]
-
подключение к рабочему контейнеру
-
просмотр лога контейнера
-
docker container stop
-
docker container rm
-
Запуск контейнера в интерактивном и фоновом режимах:
-d
vs-it
-
Сколько по времени будет работать контейнер?
-
Можно ли запускать несколько сервисов в одном контейнере?
-
отслеживается только один процесс c PID 1 (из директив ENTRYPOINT + CMD) и по его завершению контейнер завершается тоже, жестко останавливая остальные процессы
-
поэтому технически можно, но если очень аккуратно запустить свой менеджер процессов и управлять дочерними процессами
-
в целом вся философия, инструментарий и паттерны Docker рекомендуют подход "service per container"
-
иначе сложно контейнеры отлаживать, мониторить, анализировать логи, разделять ресурсы и данные, обрабатывать падения сервисов
-
для управления группой контейнеров существуют оркестраторы
-
Given пары участников
-
[optional]
sudo yum install -y jq
# json cli viewer -
When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
-
Сценарий "Как посмотреть список работающих и остановленных контейнеров?"
docker container ls --all
docker container ls --format '{{.ID}} | {{.Names}} | {{.Status}} | {{.Image}}'
- Сценарий "Как запустить 'одноразовый' контейнер?"
docker container run --rm -it alpine # note `--rm`
/# exit
docker container ls
- Сценарий "Как запустить контейнер в фоновом режиме?"
docker container run --detach --name proxy --publish 80:80 nginx:1.19.4 # note `--detach`
docker container ls
curl localhost:80
- Сценарий "Как 'подключиться' к работающему контейнеру?"
docker container logs
docker container attach --sig-proxy=false # otherwise detach key `ctrl-c` will stop container
docker container top
docker container exec -it /bin/sh
- Сценарий "Как посмотреть свойства контейнера?"
docker container port
docker container inspect [| jq]
- Сценарий "Как поставить на паузу контейнер?"
docker container pause
docker container unpause
- Сценарий "Как создать контейнер с сервисом без запуска?"
docker container create
- Сценарий "Как запустить созданный контейнер?"
docker container start
- Сценарий "Как остановить и запустить снова работающий контейнер?"
docker container start
docker container restart
docker container stop # send SIGTERM, and then SIGKILL after grace period
docker container kill # send SIGKILL, or specified signal
- Сценарий "Как удалить работающий контейнер?"
docker container rm --force
- Сценарий "Как удалить остановленный контейнер?"
docker container rm
docker container prune
- Сценарий "Как узнать и сохранить container data (container layer)?"
docker container diff
docker container commit
- Опциональный сценарий "Как обменяться файлами с контейнером?"
docker container cp
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Какие способы идентификации контейнера?
- Какое имя у контейнера по умолчанию?
- В чем физический смысл удаления контейнера?
- Что делает
prune
? - Сколько новых layers добавила команда
commit
к базовому образу?
-
Автоматическая сборка – "это другое!"
-
часть CI/CD pipeline:
-
требования к частоте сборок
-
требования к скорости сборок
-
требования к ресурсоемкости
-
Какие действия необходимо совершить для подготовки образа в случае автоматической сборки?
-
Экстернализация конфигурации приложения при сборке образа
-
Команда сборки образа
docker [image] build
-
в Docker входят два сборщика: классический
build
и экспериментальныйbuildx
$ docker image build .
Uploading context 6.76 MB
Step 1/2 : FROM busybox
---> 769b9341d937
Step 2/2 : CMD echo Hello world
---> 99cc1ad10469
Successfully built 99cc1ad10469
- Структура Dockerfile и декларативность директив
- Ключевые директивы Dockerfile
FROM
+--platform=
WORKDIR
создаст папку при необходимостиCOPY
andADD
from build context (+.dockerignore
)RUN
(+shell
and preferredexec
forms) andSHELL
for non-default shellENV
ARG
EXPOSE
documentationVOLUME
ENTRYPOINT
andCMD
(+ preferredexec
and similardefault parameters to ENTRYPOINT
,shell
forms)LABEL
docker container run [--entrypoint Dockerfile's ENTRYPOINT override] IMAGE [Dockerfile's CMD defaults override]
FROM alpine
ENTRYPOINT ["echo", "Hello"]
CMD ["World"] # 'default parameters to ENTRYPOINT' form
...
$ docker build --tag test .
...
$ docker run --rm test
Hello World
...
$ docker run --rm test Alpine
Hello Alpine
-
Системные образы для базы VS прикладные образы с приложениями
-
Dockerfile should specify at least one of
CMD
orENTRYPOINT
commands -
ENTRYPOINT
should be defined when using the container as an executable. -
CMD
should be used as a way of defining default arguments for anENTRYPOINT
command or for executing an ad-hoc command in a container -
CMD
will be overridden when running the container with alternative arguments -
If
CMD
is defined from the base image, settingENTRYPOINT
will resetCMD
to an empty value -
Версионирование создаваемого образа через теги
-
опасность
:latest
-
semantic versioning
-
unique tags
-
Кратко по оптимизации сборки: (подробнее в отдельном модуле)
-
Сборка
FROM scratch
, "пинцетный метод" -
Использование легковесных базовых образов
alpine 5.33MB
registry.access.redhat.com/ubi8/ubi-micro 51.6MB
ubuntu 65.6MB
debian:stable-slim 74.3MB
- Понятие build context
- Кеширование при сборке
- Изменение порядка директив в Dockerfile, чтобы максимально повторно использовать кеш Docker builder
- Объединение директив, чтобы снизить количество слоёв образа
- Multi-stage build, чтобы не тащить в итоговый образ инфраструктуру сборки
- Объединение слоёв образа в один слой
- Given пары участников
- активная учетная запись на Docker Hub
- проведена аутентификация консольного Docker CLI на Docker Hub для будущих операций
push
:docker login
- Будущая структура папок, которую участники создадут в процессе этой и следующих практик
application
├── base
│ └── Dockerfile
├── backend
│ ├── Dockerfile
│ ├── dbo-1.0-SNAPSHOT-sources.zip
│ └── dbo-1.0-SNAPSHOT.jar
├── db
│ └── Dockerfile
├── proxy
│ ├── Dockerfile
│ └── nginx.conf.template
├── stub
│ ├── mappings
│ │ └── legacyAccountingSystemResponse.json
│ ├── Dockerfile
│ └── wiremock-standalone-2.27.2.jar
└── docker-compose.yml
- Задана рабочая папка
cd application
- When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
- Сценарий "Как создать собственный образ на основе Dockerfile?"
mkdir base
nano base/Dockerfile
# TODO: update Dockerfile
# - describe image that based on Alpine _fixed_ fresh version available at your docker registry
# - update package manager cache with `apk update`
# - install package `openjdk11-jre-headless` with `apk add`
docker image build \
--tag {{ registry-account }}/base:1.0.0 \ # -t
./base # folder where Dockerfile located
- Сценарий "Как опубликовать образ в репозитории?"
docker image push {{ registry-account }}/base:1.0.0
- Then
- Какой ENTRYPOINT и/или CMD у получившегося образа? То есть что запуститься при старте контейнера?
- Как протестировать полученный образ, что он соответствует решению задачи (базовый образ для будущих образов с упакованными java-приложениями)?
- Given
- пары участников
- опубликованные базовые образы других команд
- Dockerfiles для основных сервисов приложения
- активная учетная запись на Docker Hub
- проведена аутентификация консольного Docker CLI на Docker Hub для будущих операций
push
:docker login
- Задана рабочая папка
cd application
- When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
- Сценарий "Как задать "чужой" образ как базовый для своих следующих образов?"
nano backend/Dockerfile # TODO fix FROM directive to new base image
- Сценарий "Как собрать свой образ с приложением на базе Dockerfile?"
docker image build \
--tag {{ registry-account }}/backend:1.0.0 \ # -t
./backend
- Сценарий "Как опубликовать образ в репозитории?"
docker image push ...
- Сценарий "Как запустить 'одноразовый' контейнер на базе своего образа с приложением?"
docker container run \
--name backend \
--rm \ # одноразовый: удалится после остановки
--detach \ # -d
--publish 8080:8080 \ # -p [host address:]8080:8080
--env SPRING_PROFILES_ACTIVE=qa \ # -e: в контейнере действует переменная окружения
--volume $(pwd)/log:/dbo/log \ # -v: папка в конейнере /dbo/log отображена на папку на хосте /current-path/log. Windows caution for $()!
{{ registry-account }}/backend:1.0.0 \ # репозиторий и тег
--spring.profiles.active=qa # параметры командной строки
- Сценарий "Как протестировать запущенное в контейнере приложение"
docker container ls --all
curl localhost:8080/dbo/actuator/health
open http://localhost:8080/dbo/swagger-ui/
- Сценарий "Как остановить приложение"
curl -X POST localhost:8080/dbo/actuator/shutdown
- Сценарий "Как остановить контейнер"
docker container stop
- Then участники делятся проблемами и отвечают на вопросы
- В каком порядке выполнялись директивы Dockerfile?
- Сколько новых layers добавила сборка к базовому образу?
- Когда и по какой причине остановился контейнер?
- Что происходит с процессом приложения, когда останавливаем контейнер?
- Сколько раз вы столкнулись с настройкой экстернализированной конфигурации приложения?
- Какие приоритеты у этих точек конфигурации?
- Что случится при запуске контейнера с параметром командной строки
docker run ... --spring.profiles.active=preprod
?
-
Какие ресурсы необходимо виртуализировать?
-
network
-
volumes/folders
-
cpu, memory
-
Что нужно для целостной работы multi-container приложения?
-
Целостная сборка образов (ответственность build pipeline)
-
Целостный запуск, работа и завершение (ответственность оркестратора)
-
Варианты сетевой топологии
-
host network
portability issue
puml
@startuml
actor User
node host {
rectangle "host\nnetwork" as host_net #line.dotted {
[curl]
[browser]
}
rectangle "virtual\nnetwork" as virt_net_backend #line.dotted {
[backend]
}
rectangle "virtual\nnetwork" as virt_net_db #line.dotted {
database "db"
}
rectangle "virtual\nnetwork" as virt_net_stub #line.dotted {
component "external\nservice" as stub
}
virt_net_backend #--# host_net : "port mapping"
virt_net_db #-# host_net
virt_net_stub #-# host_net
}
User - browser
@enduml
- Given пары участников
- Задана дефолтная папка
cd application
- When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
- Сценарий "Как ...?"
nano backend/Dockerfile # TODO fix default Spring profile to `preprod` instead of `qa`
docker image build --tag {{ registry-account }}/backend:1.0.0 ./backend
nano stub/Dockerfile # TODO fix FROM for new custom base image
docker image build --tag {{ registry-account }}/stub:1.0.0 ./stub
- Сценарий "Как ...?"
docker container run \
--detach \
--name db \
--publish 5432:5432 \
--volume db:/var/lib/postgresql/data \ # volume created and mounted
--env POSTGRES_DB=dbo-db \ # postgres
--env POSTGRES_USER=dbo \ # postgres
--env POSTGRES_PASSWORD=dbo \ # required
postgres:11-alpine
docker container run \
--detach \
--name stub \
--publish 8888:8888 \
{{ registry-account }}/stub:1.0.0
curl localhost:8888/api/account [| jq]
docker container run \
--detach \
--name backend \
--publish 8080:8080 \
--env SPRING_PROFILES_ACTIVE=preprod \ # необязательно, установили как дефолт командной строки запуска в Dockerfile
--env SPRING_DATASOURCE_URL="jdbc:postgresql://$(hostname -i)/dbo-db" \
--env SPRING_DATASOURCE_USERNAME=dbo \
--env SPRING_DATASOURCE_PASSWORD=dbo \
--env SPRING_INTEGRATION_LEGACYACCOUNTINGSYSTEM_BASEURL="http://$(hostname -i):8888/api" \
{{ registry-account }}/backend:1.0.0 \
--spring.profiles.active=preprod # необязательно, установили как дефолт командной строки запуска в Dockerfile
curl -H "X-API-VERSION:1" localhost:8080/dbo/actuator/health [| jq]
curl -H "X-API-VERSION:1" localhost:8080/dbo/api/account [| jq]
open "http://$(hostname -i):8080/dbo/swagger-ui/"
- Сценарий "Как ...?"
docker container ls [--all]
- Сценарий "Как ...?"
docker container stop
docker container rm [--force]
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- По какому URL получили доступ к веб-интерфейсу приложения для тестирования работоспособности?
- Исходя из экстернализированных настроек, как связаны компоненты системы?
- Какие ресурсы были виртуализированы?
- Система работоспособна? Почему? Локализуйте проблему и предложите решения.
puml
@startuml
actor User
node host {
rectangle "host\nnetwork" as host_net #line.dotted {
[curl]
[browser]
}
rectangle "virtual\nnetwork" as virt_net #line.dotted {
component "proxy"
component "backend"
component "external\nservice" as stub
database "db"
}
proxy -# backend
backend --# stub
backend -# db
host_net #--# proxy : "port\nmapping"
browser -- host_net : "localhost"
}
User - browser
@enduml
- Отображение портов
- Разрешение адресов и имен в виртуальных сетях
--name
default hostname--hostname
explicit hostname-
localhost
issue - слушайте
0.0.0.0
- Given пары участников
- В случае podman для работы символьных имен хостов в виртуальных сетях необходимо собрать и сконфигурировать плагин
dnsname
- Задана дефолтная папка
cd application
- When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
-
- Сценарий "Как посмотреть сети?"
docker network ls
- Сценарий "Как создать виртуальную сеть?"
docker network create [--help]
- Сценарий "Как запустить контейнеры в виртуальной сети?"
Look ma, no port mappings!
docker container run \
--detach \
--network my_deployment \
--name db \
--volume db:/var/lib/postgresql/data \
--env POSTGRES_DB=dbo-db \
--env POSTGRES_USER=dbo \
--env POSTGRES_PASSWORD=dbo \
postgres:11-alpine
docker container run \
--detach \
--network my_deployment \
--name stub \
{{ registry-account }}/stub:1.0.0
docker container run \
--detach \
--network my_deployment \
--name backend \
--env SPRING_PROFILES_ACTIVE=preprod \ # необязательно, установили как параметр командной строки в Dockerfile
--env SPRING_DATASOURCE_URL="jdbc:postgresql://db/dbo-db" \ # hostname instead of external ip is the result of virtualizing network
--env SPRING_DATASOURCE_USERNAME=dbo \
--env SPRING_DATASOURCE_PASSWORD=dbo \
--env SPRING_INTEGRATION_LEGACYACCOUNTINGSYSTEM_BASEURL="http://stub:8888/api" \ # hostname instead of external ip is the result of virtualizing network
{{ registry-account }}/backend:1.0.0
--spring.profiles.active=preprod
nano proxy/Dockerfile #TODOs
nano proxy/nginx.conf.template #TODOs
docker image build --tag {{ registry-account }}/proxy:1.0.0 ./proxy
docker container run \
--detach \
--network my_deployment \
--name proxy \
--publish 80:80 \ # Notice mandatory port mapping
{{ registry-account }}/proxy:1.0.0
open [http://localhost/dbo/actuator/health](http://localhost/dbo/actuator/health)
- Сценарий "Как подключить работающий контейнер к виртуальным сетям?"
docker network connect [--help]
docker network disconnect
- Сценарий "Как посмотреть состояние виртуальных сетей?"
docker network ls
docker network inspect
- Сценарий "Как удалить виртуальную сеть?"
docker network rm
docker network prune
- Сценарий "Как управлять виртуальными сетями в docker-compose?"
nano docker-compose.yml
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Какая сетевая топология определяется по умолчанию?
- Для каких контейнеров мы определили меппинг портов? Почему не для всех?
- Как мы задаем хосты в экстернализированной конфигурации приложений? Почему не ip?
- Что происходит с изменениями в образе при остановке контейнера?
- Как зафиксировать изменения в образе?
- Как откатить изменения в образе?
- Как можно сохранять изменения на диске вне образа? (stateful containers)
- Управление данными на хостовой машине
- tmpfs, Linux only feature
- Shared folders как подмонтированные FS
docker container run --volume "$(pwd)"/folder/file:/folder/file:ro # пути у folder абсолютные, начинаются с "/"
docker container run --volume my_volume:/folder/file:ro # имя volume не начинается с "/"
-
консольные логи: stdout/stderr
-
собираются и упаковываются в выбранный формат (драйвер)
docker logs [--until=10s] test
- Given пары участников
- Задана дефолтная папка
cd application
- When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
- Сценарий "Как посмотреть все текущие volumes?"
docker volume ls
- Сценарий "Как удалить неиспользуемую volume?"
docker volume rm ... [--help]
docker volume prune [--help]
- Сценарий "Как создать volume?"
docker volume create ... [--help] # TODO Создать volume `db` с драйвером `local`
- Сценарий "Как пробросить volume в контейнер?"
docker container run # TODO Сделать контейнер с СУБД stateful: сохранять данные из папки `/var/lib/postgresql/data` на volume `db`
--name db \
--volume \ # volume создастся автоматом, если не существует
...
- Сценарий "Как вынести ненужные изменения на временную папку?" [Linux host only]
docker container run
--name backend
--tmpfs /tmp
--tmpfs /var/log
--tmpfs /dbo/log
- Сценарий "Как пробросить shared folder с хостовой системы в контейнер?"
docker container run # TODO Сделать proxy/Dockerfile ненужным: пробросить `$(pwd)/proxy/nginx.conf` как read-only файл `/etc/nginx/nginx.conf`
--name proxy \
--volume \ # в случае shared folder абсолютный хостовой путь
nginx:1.21 # стоковый образ, не наш
- Сценарий "Как посмотреть volumes/folders контейнера?"
docker container inspect # | grep "Mounts"
- Сценарий "Как посмотреть консольные логи процессов?"
docker logs ... [--help]
- Сценарий "Как посмотреть изменения, сделанные процессом на файловой системе?"
docker container diff ... [--help] # отслеживаются ли изменения на подмонтированных внешних folder/volume?
- Сценарий "Как управлять volume и shared folder в docker-compose?"
nano docker-compose.yml
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Где физически храняться volume в нашем случае?
- Что такое "неиспользуемые" volume?
- Отображение портов
- Установка переменных окружения ОС для контейнера
- Просмотр статистики потребления ресурсов
- Лимитирование памяти и CPU и неочевидность поведения лимитирования памяти
- Лимитирование дискового объема контейнера
- Лимитирование объема volume
- Лимитирование ресурсов при запуске контейнера
- Как ведет себя одиночный контейнер при ошибках контейнеризуемого приложения
- Как ведет себя контейнер в swarm при ошибках контейнеризуемого приложения
- Лимитирование ресурсов в docker-compose
Hands-on practice quest #07: networked multi-component stateful app resource-limited containerization 15 + 5
-
Given пары участников
-
When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
-
Сценарий "Как понять текущее потребление ресурсов?"
docker stats
- Сценарий "Как лимитировать вычислительные ресурсы при запуске контейнера?"
docker container run # ограничить по памяти, чтобы получить OOME
--name backend
- Сценарий "Как лимитировать ресурсы в docker-compose?"
nano docker-compose.yml # ограничить по CPU и по памяти
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Какие лимиты на ресурсы устанавливаются Docker по умолчанию?
- Какое поведение отдельного контейнера при OOME по умолчанию?
-
Как уменьшить размер образа?
-
Сборка
FROM scratch
, "пинцетный метод" -
Как ускорить сборку образа?
-
Кеширование сборщиком результатов сборки = кеширование слоев
-
Понятие build context
-
Директивы Dockerfile как слои образа
-
каждая директива выполняется своим контейнером
-
RUN, COPY, ADD create layers
-
Other instructions create temporary intermediate images, and do not increase the size of the build
docker image build --tag stub ./stub
[+] Building 2.2s (10/10) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 328B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 34B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:8-jre-slim 1.3s
=> [1/5] FROM docker.io/library/openjdk:8-jre-slim@sha256:0330883ffeb5e14c4c15271004cdf6a2df21e827420b71dca01c34e41a23690d 0.1s
=> => resolve docker.io/library/openjdk:8-jre-slim@sha256:0330883ffeb5e14c4c15271004cdf6a2df21e827420b71dca01c34e41a23690d 0.0s
=> => sha256:5563c7e505fa828bd868ae99f24c5a56bb0bd5488a10184f7175d10f167b0898 1.16kB / 1.16kB 0.0s
=> => sha256:a6c8e47b54ca34cb71f873f586aaac09f73ace9bffe1b5b62eb2b05f8c974deb 7.14kB / 7.14kB 0.0s
=> => sha256:0330883ffeb5e14c4c15271004cdf6a2df21e827420b71dca01c34e41a23690d 320B / 320B 0.0s
=> [internal] load build context 0.4s
=> => transferring context: 20.00MB 0.4s
=> [2/5] RUN mkdir -p /stub/mappings 0.5s
=> [3/5] WORKDIR /stub 0.0s
=> [4/5] COPY mappings/* mappings/ 0.0s
=> [5/5] COPY wiremock-standalone-2.27.2.jar /stub 0.1s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:0a8dfafa48b9c717b862532fe441e8f2db7146a6deb8380768e6a713c75a6da4 0.0s
=> => naming to docker.io/library/stub
- Кеширование включаемых файлов и результатов директив
docker image build --tag stub ./stub
[+] Building 1.9s (11/11) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 328B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 32B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:8-jre-slim 1.8s
=> [auth] library/openjdk:pull token for registry-1.docker.io 0.0s
=> [1/5] FROM docker.io/library/openjdk:8-jre-slim@sha256:0330883ffeb5e14c4c15271004cdf6a2df21e827420b71dca01c34e41a23690d 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 689B 0.0s
=> CACHED [2/5] RUN mkdir -p /stub/mappings 0.0s
=> CACHED [3/5] WORKDIR /stub 0.0s
=> CACHED [4/5] COPY mappings/* mappings/ 0.0s
=> CACHED [5/5] COPY wiremock-standalone-2.27.2.jar /stub 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:1b7c6f374f61947cd1e96233307f4fa56dfc3c0e5fd6bf228b0e7e27803ca011 0.0s
=> => naming to docker.io/library/stub
-
Оптимизации
-
Выбор образа-предка: легковесные ОС, busybox и
scratch
-образ -
Используйте build cache: порядок директив в Dockerfile
-
Управление кешированием
-
Сквошинг
docker build --squash ...
-
Multi-Stage Build
-
"multi-stage build" как реализация простого build pipeline
-
Docker BuildKit (экспериментальный сборщик buildx) позволяет монтировать хостовые папки в слоевые build-time контейнеры для кеширования данных между сборками
Hands-on practice quest #08: build-optimized networked multi-component stateful application resource-limited containerization 15 + 5
-
Given пары участников
-
When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
-
Сценарий "Как оптимизировать сборку?"
nano application/backend/Dockerfile # TODO оптимизировать сборку и убедиться в ускорении
nano application/proxy/Dockerfile
nano application/stub/Dockerfile
- Сценарий "Как управлять кешем сборки?"
docker builder prune [--all]
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Насколько получилось оптимизировать сборки в измеряемых метриках?
- Простейшая "оркестрация" на политиках рестарта одиночного контейнера
- Оркестраторы:
compose
,swarm
,k8s (+minikube)
и их ограничения - Клиенты оркестраторов: Docker Compose (+build) и Docker Stack over Swarm/k8s/minikube (-build)
- Демо
cat docker-compose.yml
- Понятие сервиса в stack и реплики контейнера
- k8s больше не поддерживает docker: все пропало?
- Как устроен Docker? Элементы.
- Как устроены аналоги? Элементы.
Hands-on practice quest #09: orchestration of build-optimized networked multi-component stateful application resource-limited containerization 15 + 5
-
Given пары участников
-
Инициализация сервиса Swarm на хосте
-
When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
-
Сценарий "Как посмотреть список кластеров, управляемых оркестратором сервисов (подов), контейнеров в сервисе?"
docker stack ls
docker service ls
docker service ps ...
- Сценарий "Как запустить кластер (stack API)?"
docker stack deploy --compose-file docker-compose.yml app_stack
- Сценарий "Как посмотреть логи сервиса?"
docker service logs ...
- Сценарий "Как убедиться в самовосстановлении контейнеров в сервисе?"
docker container ps -a
docker rm -f
docker container ps -a
- Сценарий "Как остановить кластер (stack API)?"
docker stack rm
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Минимизировать security риски
- Используйте минимальные образы: быстрее и меньше зависимостей + меньше рисков
- Запуск в rootless mode или от пользователя с минимальными привилегиями с возможностью при запуске добавить прав
- Подписывать образы и проверять подписи
- Сканеры уязвимостей для образов
docker scan --dependency-tree <image>
- Линтеры для Dockerfile
- COPY вместо ADD
- Аккуратно с рекурсивным копированием + .dockerignore
- Фиксированные теги для идентификации образов (Semantic versioning or Unique tags)
- Multi-Stage Build в том числе для того, чтобы в итоговый образ не утекли чувствительные данные
-
Хранение и передача конфигурации и чувствительных данных
-
Локальное журналирование и доступ к логам
- docker logging drivers
- dedicated logs shared folders/volumes
- remote log collectors
Hands-on practice quest #10: build-optimized networked multi-component stateful application resource-limited best practice based containerization 15 + 5
-
Given пары участников
-
When участники именуют сценарии, формируют свои команды и проверяют их вывод и поведение
-
Сценарий "Как ...?"
docker info --format '{{.LoggingDriver}}'
docker container run -it --log-driver local --log-opt mode=non-blocking --log-opt max-buffer-size=4m
docker inspect -f '{{.HostConfig.LogConfig.Type}}'
docker logs
- Сценарий "Как осуществить multi-stage сборку образа?"
cd application/backend
nano Dockerfile # TODO: BUILD stage with `mvn clean verify` and QA stage with `java -jar ... --spring.profiles.active=qa`
- Then участники делятся проблемами и отвечают на вопросы
- Как назвали сценарии?
- Какие интересные Вам метрики можно снимать?
- Как Docker упрощает ручное тестирование
- Версионирование как дисциплина
- Образ для ручного тестирования с подготовленными тестовыми данными
- Легкий старт нужного окружения on demand
- Окружение тестирования больше не узкое место
- Как Docker упрощает автоматизированное тестирование
- TestContainers