Basics
- Container security – три основных риска/угрозы/проблемы
- плохая конфигурация
- проблемы runtime
- цепочки поставок
- Применение bestpractice до/после
- На основе dockerfile создается docker image, который используется для создания containers
Docker builds images automatically by reading the instructions from a Dockerfile which is a text file that contains all commands, in order, needed to build a given image.
- Прослойка в виде docker в любом случае имеет некий performance penalty (напр. по умолчанию для контейнеров работает MAT, хотя и можно указать прямое использование хостовой сети) в сравнении с деплоем приложения в хостов ОС без контейнера. При этом нужно понимать, что это не говорит о том, что контейнеры не предназначены для high load – зачастую может быть не так: удобство деплоя контейнера позволяет простое горизонтальное масштабирование. Но бывает и иначе, напр. когда нагрузка должна быть на одном физическом сервере/серверов очень мало и проч.
- Docker/container security: По умолчанию docker запускается контейнеры из под root пользователя. Если в приложении, которое работает в контейнере, будет обнаружена уязвимость, то это может позволить злоумышленникам выйти из контейнера и выполнить различные действия с правами root на хостовой ОС. При этом можно от этого отказаться и не одним способом, напр. Remapping uid внутри контейнера (т.е. запуск контейнера из под root, но внутри контейнера используется другой пользователь): An excellent security feature in Docker, is the userns-remap or user namespace remapping. It makes use of the Linux USER namespace to re-map the root user within the container to a less-privileged user in the host machine. In this way, the container will be running as root, but that root is mapped to an user that has no privileges on the host.
- Podman (docker от redhat) не требует запуска от root (rootless), работа изначально построена как консольная утилита, а не демон (daemonless). Недостаток podman это отсутствие доступа к lan bridge, отсутствие аналога простой кластеризации в виде dockerswarm, без использования k8s. Познакомится с ним легко – ставишь дистрибутив AlmaLinux, запускаешь установку docker через встроенный пакетный менеджер (dns install podman), а он ставит по факту podman и делает alias на docker в сторону podman🤣 Обман/навязывание вскрывается когда пытаешься загрузить image (напр. Nginx) – он лезет не в dockerhub, а хранилище redhat, где требуется регистрация.
Podman работает как обычная CLI утилита, а Docker необходимо несколько демонов Linux с привилегиями root. Podman может запускать контейнеры в разных пользовательских пространствах имен в отличии от Docker. Podman поддерживает запуск systemd в контейнерах а также многие другие возможности systemd.
- Docker сильно помогает тем, что на всех этапах/стендах (разработка, тестирование, продакшн) создается идентичное окружение, пример: для дев, тест, стейджинга и прода везде используем docker, управляем kubernates
- Docker очень сильно помогает для разработчиков сдавать проекты – есть достаточно изолированная сущность в виде образа, в который включен не только код, но и зависимости (dependency), плюс вся эта сборка за счет изоляции не повлияет на другие приложения/контейнеры, запущенные в том же гипервизоре/хосте как с точки зрения зависимостей, так и с точки зрения ресурсов/изоляции ресурсов (этим можно управлять); причем именно изоляция ресурсов отличает docker от технологий упаковки приложений, которые решают проблемы зависимостей пустем сохранения этих зависимостей в пакете с приложением (
- Docker унифицирует запуск приложения – есть контейнер и контейнер запускается единообразно (по сути сделана абстракция), при этом конкретные приложения в контейнерах могут запускаться очень по разному.
- Контейнерам можно предоставить файловое пространство примонтировав/при’bind’ить директорию хоста (по сути проброс директории хоста в коонтейнер), но более правильно создать отдельный docker volume. В целом volume является рекомендуемым способом – более органичным (напр. возможность простого переноса контейнеров на другой хост) и безопасным (напр. видимость вне контейнеров объектов хоста) способом. При этом и bind тоже имеет применения – напр. проброс конфига/исходников/бинарников в контейнер. С точки зрения своей работы volume похожи на bind – это монтирование определенной директории хост в контейнер, но директория изолирована от других процессов хоста. При этом один volume можно примонтировать сразу в несколько контейнеров.
When you create a volume, it is stored within a directory on the Docker host. When you mount the volume into a container, this directory is what is mounted into the container. This is similar to the way that bind mounts work, except that volumes are managed by Docker and are isolated from the core functionality of the host machine. A given volume can be mounted into multiple containers simultaneously. Docker recommends the use of volumes over the use of binds, as volumes are created and managed by docker and binds have a lot more potential of failure (also due to layer 8 problems).If you use binds and want to transfer your containers/applications on another host, you have to rebuild your directory-structure, where as volumes are more uniform on every host. Volumes are the preferred mechanism for persisting data generated by and used by Docker containers. While bind mounts are dependent on the directory structure of the host machine, volumes are completely managed by Docker. One of the key differentiators of a bind mount is that a bind mount can be accessed and modified by processes outside Docker. As previously mentioned, this can be a benefit when you want to integrate Docker with other processes, but it could also cause a headache if you’re concerned with security. One side effect of using bind mounts, for better or for worse, is that you can change the host filesystem via processes running in a container, including creating, modifying, or deleting important system files or directories. This is a powerful ability which can have security implications, including impacting non-Docker processes on the host system.
- Expose в докер файле не открывает порты, а декларирует, за открытие портов отвечает флаг -p
- При создании докер файла рекомендуется указывать конкретную версию образа в инструкции from, а не latest или не указывать вообще (в таком случае по умолчанию работает latest, причем этого тега может не быть у дистрибутива вообще по факту и ничего не заработает тогда :)). В случае очередного обновления это может позволить быстро откатится на рабочую версию. Аналогичное справедливо и для пакетов, которые устанавливаются/обновляются внутри образа докер контейнера по инструкции.
- Рекомендуется из докер образов удалять все лишнее – файлы, сборочные пакеты, другие ненужные зависимости (autoremove), утилиты (даже такие как ip, vim, nano) и даже оболочку bash кто то не ставит.
- В том числе выбор базового дистрибутива для образа может влиять на объем конечного образа – напр. использовать Alpine вместо Debian, при этом нужно учитывать:
- выигрыш по объему образа/безопасности действительно может быть весомым: A minimal Docker image based on Alpine Linux with a complete package index and only 5 MB in size! Docker Alpine is the “Dockerized” version of Alpine Linux, a Linux distribution known for being exceptionally lightweight and secure. For these reasons and others, Docker Alpine is a popular choice for developers looking for a base image on which to create their own containerized apps.
- что образ нужно в первую очередь выбирать от приложения, которое будет деплоится в контейнерах
- сборки пакетов могут сильно различаться и то, что легко запустится в одном образе не запустится в другом
- после добавления всех необходимых зависимостей в некий “минимальный” образ он по итогу может стать сопоставимым или даже больше по объему в сравнении со “стандартным – пример для python
- Напр. если на выходе после сборки софта получается bin/jar или другой execution файл, а сборка его требует множества зависимостей, которые при работе софта не требуются – рекомендуется использовать multi build stage инструкцию в dockerfile для создания образа, когда сборка исполняемого файла (напр. утилиты C/Go) требует множества разных зависимостей и происходит в отдельном контейнере, который со всеми зависимостями дропается после сборки, но из него извлекается нужный файл и он используется в новом контейнере, в котором отсутствует ворох зависимостей и он используется для создания образа. Раньше без этой инструкции поступали по сути так же (создавали отдельный контейнер в котором осуществляли сборку и перекладывали через какое то хранилище результат во второй контейнер), но сейчас более удобно/native.
- В том числе выбор базового дистрибутива для образа может влиять на объем конечного образа – напр. использовать Alpine вместо Debian, при этом нужно учитывать:
- Слои – следование по best practice `ниже позволяет ускорить создание image и сделать их менее тяжеловесными.
- По умолчанию докер создается с включенным кешированием (без –no-cache) и многие инструкции в dockerfile создают слои, которые кешируются и их можно переиспользовать. Напр. если ты запустил yum update, следующая такая же инструкция не будет исполнена, а результат будет взят из предыдущей (из кеша исполнения предыдущей). Можно отключить кеширование полностью (–no-cache), но чаще кеш нужен, чем нет.
- Для alpine image можно использовать особые инструкции – напр. -virtual, которая позволит счистить все зависимости в финале инструкций dockerfile
The --virtual tag allows you to add the following packages as a virtual group, for managing as a single package. For example, after that operation you could do apk del build-dependencies and it would delete all of those packages
-
- От ненужной информации в слое лучше избавляться в том же самом run, чтобы не сохранять ненужную информацию (напр. удалить все лишнее после apt install/update); помимо занятия лишнего места этот кеш так же может приводить к конфликтам.
- По сути слой несет за собой определенное состояние контейнера по результату исполнения инструкции.
- В среднем чем больше инструкций, тем больше слоев.
- Рекомендуется использовать как можно меньше слоев, чтобы не занимать место на диске.
- Слои рекомендуется объединять – напр. в рамках одной команды run (вместо нескольких отдельных) и соответственно слоя (создаем один слой вместо нескольких) через tab делать загрузку всех необходимых пакетов, но на разных строках для удобства сравнения в VCS
- От лишних слоев нужно избавляться
- Часто изменяемые слои располагать ниже по уровню т.к. при их изменении не будет сбрасываться кеш вышестоящих слоев, которые не изменились – напр. копирование часто обновляемого файла выносить вниз, в итоге вышестоящие слои (напр. с apt update/apt get) при отсутствии изменения будут взяты из кеша и по сути исполнены только команды начиная с обновления файла
- Watchtower крутой контейнер, который позволяет автоматизировать обновление других контейнеров (при создании watchtower ты ему разрешаешь доступ к docker api, через которые он и производит апдейты). При апдейте безусловно будет кратковременный даунтайм. Его использовали google для авто-апдейта outline VPN сервера. По умолчанию апдейты включаются для всех контейнеров, но можно указать конкретные (в случае с outline так и работает) или исключения. Так же можно настроить удаление старых image (cleanup) и расписание обновления других контейров (schedule в формата cron; по умолчанию апдейт раз в сутки).
With watchtower you can update the running version of your containerized app simply by pushing a new image to the Docker Hub or your own image registry. Watchtower will pull down your new image, gracefully shut down your existing container and restart it with the same options that were used when it was deployed initially.
- Изоляция и выделение ресурсов в docker реализуется за счет возможности задания ограничений на каждый контейнер выделяемых ресурсов (resource constraints)
- cgroups – ресурсы (очень кратко):
- CPU (–cpus / —gpuz)
- RAM (—memory)
- HDD
- namespaces – изоляция/видимость, контейнер по сути запускается в отдельном namespace process/net/file:
- списка/id процессов/имен
- межпроцессное взаимодействие не через сеть
- сетевое пространство
- пространство файловой системы
- chroot раньше использовался вместо namespaces + cgroups. Chroot реализует разграничение видимости на уровне файловой системы. Из названия понятно как он это делает – меняет корневую директорию (change root = chroot), а т.к. все в linux по сути есть файл и привязано к структуре всего файлового дерева – происходит подмена системы. Из недостатка chroot в сравнении с namespaces + cgroups chroot не ограничивает видимость пространства процессов и не позволяет ограничить выделяемые ресурсы; при этом chroot используется до сих пор – например для разбора проблем с загрузкой системы:
- загружаешься с live USB
- делаешь chroot в поломанную систему
- траблшутинг
- cgroups – ресурсы (очень кратко):
- Взаимодействие container/image – контейнер это запущенный экземпляр образа, образ по сути это неизменяемый набор/шаблон инструкций для создания контейнера в виде файла, некий аналог VM snapshot.
Docker images act as a set of instructions to build a Docker container, like a template. Docker images also act as the starting point when using Docker. An image is comparable to a snapshot in virtual machine (VM) environments.
- Для своей работы docker использует Union File System, который по сути представляет из себя наложение (union, объединение) файловых систем, позволяющее переиспользовать исходные файлы и создавать новые/модифицировать исходные, при этом никак не затрагивая исходные файлы. Именно Union FS обеспечивает сохранение новых/модицированных файлов в контейнере даже в случае перезапуска контейнера.
- Portainer – удобный web UI к docker, я не использовал.
- Рекомендуется запускать только один сервис/корневой процесс/демон в контейнере, остановка этот процесса (переднего плана, запускаемого ENTRYPOINT/CMD) приводит к остановке контейнера
- Различие entrypoint и cmd в dockerfile; чаще всего на практике задается CMD, а entrypoint используется default (/bin/sh -c)
The ENTRYPOINT
specifies a command ((process/scripts)) that will always be executed when the container starts.
TheCMD
specifies arguments that will be fed to theENTRYPOINT ((process/script arguments))
.
FROM debian:wheezy ENTRYPOINT ["/bin/ping"] CMD ["localhost"]
$ docker run -it test PING localhost (127.0.0.1)
$ docker run -it test google.com PING google.com (173.194.45.70)
Docker has a default entrypoint which is
/bin/sh -c
but does not have a default command. When you run docker like this:docker run -i -t ubuntu bash
the entrypoint is the default/bin/sh -c
, the image isubuntu
and the command isbash
. The command is run via the entrypoint. i.e., the actual thing that gets executed is/bin/sh -c bash
.
- Docker уже есть даже на сетевых устройствах, таких как Cisco Catalyst, Cisco ASR. В контейнере можно запустить много всего – например, Minecraft 🙂 Если серьезно – потенциально такой подход позволяет реализовывать CDN.
As an example, why not host your computer game servers directly on the switch instead of connecting to any dedicated server far away – it’s closer locally.
- Docker изначально появился как система разворачивания ПО – заворачиваем все ПО для нашего приложения (сайт состоит из веб сервера, субд, настроек, библиотек и проч) в docker образ. Создаем докер файл с конфигом – командами которые необходимо выполнить для разворачивания приложения. Теперь мы можем его развернуть где угодно в любом количестве.
- Есть централизованное хранилище образов – dockerhub. Выполнив команду сразу происходит скачивание образа и разворачивание. Доверять образам в dockerhub сильно не стоит.
20 Million Miners: Finding Malicious Cryptojacking Images in Docker Hub
- Созданный сервис запускается в контейнере. Контейнер представляет собой процесс в изолированном пространстве со своей ФС, деревом процессов, может быть ограничение по ОП и процессору, виртуализированная сеть – почти полноценная VM.
- Docker облегчает процедуру backup/recovery/migration сервисов до простого сохранения образа контейнера и разворачивания (по аналогии с VM) или даже операции pull контейнера из hub.docker.com (docker pull <image_name>)
- Docker в отличии от VM более Lightweight за счет использования Kernel ОС контейнерами. Недостаток от этого – не получится запустить контейнер если хост машина имеет отличную ОС от контейнера. т.е. Linux контейнеры на Linux хостах, Windows контейнеры на Windows хостах.
- На каждый Linux до ~500 докеров. Дальше разные косяки вылазят. Далее используя какой нибудь гипервизор (kvm, esxi, etc) можно наплодить штук 10-20 таких операционок и получить довольно легко 10к докеров.
- Linux docker для контейнеров использует код ядра хостовой ОС
- Обновление версии докер на CentOS 7.4. После обновления столкнулся с проблемой, что контейнеры перестали запускаться – они перестали быть совместимы с версией docker. Решилось подменой Runtime”:”docker-runc“ на Runtime”:”runc“ в файлах hostconfig.json всех старых контейнеров + перезапуском docker (чтобы он обновил конфигурацию), по идее так же должно было решаться созданием symlink, но по непонятной мне не помогло.
# DOCKER UPGRADE sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine sudo yum install -y yum-utils device-mapper-persistent-data lvm2 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce sudo systemctl start docker sudo systemctl enable docker sudo systemctl status docker # ERROR Error response from daemon: Unknown runtime specified docker-runc my solution was to simply re-run and start new containers using docker-ce because apparently the existing containers were not compatible with docker-ce -bash-4.2$ sudo vi /var/lib/docker/containers/7bd56f46428be06a2a41b74a137d582cac003513c108f766f58b3fc62c13013f/hostconfig.json {"Binds":null,"ContainerIDFile":"","LogConfig":{"Type":"journald","Config":{}},"NetworkMode":"bridge","PortBindings":{"443/tcp":[{"HostIp":"","HostPort":"443"}],"80/tcp":[{"HostIp":"","HostPort":"80"}]},"RestartPolicy":{"Name":"unless-stopped","MaximumRetryCount":0},"AutoRemove":false,"VolumeDriver":"","VolumesFrom":null,"ConsoleSize":[0,0],"CapAdd":null,"CapDrop":null,"CgroupnsMode":"","Dns":[],"DnsOptions":[],"DnsSearch":[],"ExtraHosts":null,"GroupAdd":null,"IpcMode":"","Cgroup":"","Links":null,"OomScoreAdj":0,"PidMode":"","Privileged":false,"PublishAllPorts":false,"ReadonlyRootfs":false,"SecurityOpt":null,"UTSMode":"","UsernsMode":"","ShmSize":67108864,"Runtime":"docker-runc","Isolation":"","CpuShares":0,"Memory":0,"NanoCpus":0,"CgroupParent":"","BlkioWeight":0,"BlkioWeightDevice":null,"BlkioDeviceReadBps":null,"BlkioDeviceWriteBps":null,"BlkioDeviceReadIOps":null,"BlkioDeviceWriteIOps":null,"CpuPeriod":0,"CpuQuota":0,"CpuRealtimePeriod":0,"CpuRealtimeRuntime":0,"CpusetCpus":"","CpusetMems":"","Devices":[],"DeviceCgroupRules":null,"DeviceRequests":null,"MemoryReservat
Usage
Частично на основе видео
INSTALL/START
Устанавливаем docker
sudo yum install docker curl -sSL https://get.docker.com/ | sh # ubuntu/debian best way
Запускаем docker как daemon, иначе будет ошибка
systemctl start docker
[root@weril ~]# sudo docker pull centos Using default tag: latest Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Добавляем в автозагрузку.
systemctl enable docker
[root@weril ~]# sudo systemctl enable docker Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
Добавляем в автозагрузку/autostart контейнеры.
-
- Для существующих контейнеров используем docker update. Чаще всего нужен unless-stopped, а не always, чтобы можно было остановить контейнер без его перезапуска сразу после остановки.
sudo docker update --restart=unless-stopped -d vpn sudo docker update --restart=unless-stopped -d wp # sudo docker update --restart=always -d sql # для примера always
-
- Для новых контейнеров используем docker run, обычно опция автостарта не единственная – она указывается в сопокупности с другими опциями создания и старта контейнера.
sudo docker run --restart=unless-stopped -d vpn sudo docker run --restart=unless-stopped -d wp # sudo docker run --restart=always -d sql # для примера always
Посмотреть политику перезагрузок (RestartPolicy) можно через docker inspect.
-bash-4.2$ sudo docker inspect -f "{{ .HostConfig.RestartPolicy }}" 556fab2e4417 {unless-stopped 0} -bash-4.2$ -bash-4.2$ sudo docker inspect -f "{{ .HostConfig.RestartPolicy }}" b1d1599ea129 {always 0}
Там же можно посмотреть точное время старта контейнера.
-bash-4.2$ sudo docker inspect -f "{{ .State.StartedAt }}" e9928acf61e4 2024-01-03T11:09:39.09654541Z -bash-4.2$ sudo docker inspect -f "{{ .State.StartedAt }}" 556fab2e4417 2024-01-03T11:09:39.380037969Z
IMAGES
- Есть ipsec/l2tp vpn server как docker image, очень удобен по настройке и использованию, работает с клиентами iOS/MAC OS (проверял и Cisco IPsec и L2TP). Подробнее в отдельной статье.
- Через docker images организуется backup как самих image, так и контейнеров
- В докер images зачастую нехватает даже самых базовых утилит – nano/vi, ifconfig/ip, ping
yum install iproute apt install iputils-ping
Получаем image для последнего centos с dockerhub (по умолчанию применяется флаг latest как в примере с mysql). Этой же командой обновляется старый image.
sudo docker pull <image_name>
[root@weril ~]# docker pull mysql/mysql-server:latest [root@weril ~]# docker pull centos Using default tag: latest Trying to pull repository docker.io/library/centos ... latest: Pulling from docker.io/library/centos 8a29a15cefae: Pull complete Digest: sha256:fe8d824220415eed5477b63addf40fb06c3b049404242b31982106ac204f6700 Status: Downloaded newer image for docker.io/centos:latest
Просмотр images.
docker images
-bash-4.2$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/mysql latest 30f937e841c8 3 days ago 541 MB docker.io/hwdsl2/ipsec-vpn-server latest b9823de3dae3 4 days ago 167 MB docker.io/httpd latest d4e60c8eb27a 9 days ago 166 MB docker.io/wordpress latest 675af3ca3193 9 days ago 540 MB docker.io/mysql/mysql-server latest 716286be47c6 3 weeks ago 381 MB docker.io/centos latest 470671670cac 4 months ago 237 MB
Удаление image по name/ID. Не должно быть контейнеров, использующих image.
sudo docker rmi <cont_name/ID> sudo docker image rm docker.io/httpd sudo docker image rm mysql-backup sudo docker image rm 675af3ca3193 # по ID (при совпадении имен)
[root@weril ~]# docker rmi 470671670cac Error response from daemon: conflict: unable to delete 470671670cac (cannot be forced) - image is being used by running container 38f35fcf081d
Через docker images организуется backup как самих image, так и контейнеров.
Резервное копирование/восстановление image возможно через tar архив или repo с docker images (приватный или публичный).
Важно: постоянные диски с динамически изменяемыми данными, т.к. файлы СУБД бекапиться не должны. Резервное копирование данных ваших СУБД необходимо выполнять штатными средствами самой СУБД, т.к. при простом копировании данных такого диска очень легко получить их неконсистентность и невозможность восстановить работоспособность и данные СУБД в будущем.
sudo docker save -o wordpress.tar wordpress # в файл sudo docker push busybox # на репо sudo docker load -i wordpress.tar # (образа не должно быть в системе)
Резервное копирование/восстановление контейнеров простое – создаем образ из контейнера и далее используем бекап схему выше для образов.
sudo docker commit -p wordpress wordpress:container_backup
CONTAINERS
Создаем и запускаем докер контейнер. -d позволяет сделать detatch (запуск в режиме демона), -t назначает процессу tty, что позволяет к нему подключатся в последующем.
docker run -d -t --name <cont_name> <image_name>
[root@weril ~]# docker run -d -t --name mysql mysql/mysql-server [root@weril ~]# docker run -d -t --name wordpress centos
При старте можно сделать mapping порта хоста к докер контейнеру. Первый порт – порт хоста, второй порт – порт контейнера. Так же можно добавлять опции (переменные среды) используя -e/–env или файл с переменными, необходимые для настройки поднимаемого сервиса во время создания. Изменение этих опций не предусмотрено.
docker run -d -t -p <host_port>:<cont_port> --name <cont_name> <image_name>
[root@weril ~]# docker run -d -t -p 80:80 -p 443:443 --name wordpress2 centos [root@weril ~]# docker run -d -t -p 80:80 -p 443:443 --name wordpress -e WORDPRESS_DB_HOST=172.17.0.4:3306 -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=wordpress -d wordpress [root@weril ~]# docker run -d -t --name ipsec-vpn-server --env-file ./vpn.env --restart=always -p 500:500/udp -p 4500:4500/udp --privileged hwdsl2/ipsec-vpn-server
Для проброса хостового девайса в контейнер можно использовать keyword “device”.
docker run -it --device --device Add a host device to the container
Нужно учитывать, что изменение environment variables можно сделать только через переразворачивание контейнера т.к. изменение переменных для уже созданных контейнеров не предусмотрено. И это идеологически неправильно. Если же поменять просто конфиг необходимого сервиса, без изменения env, то каждый перезапуск контейнера будет приводить к возврату изначально указанных в env настроек.
Destroy your container and start a new one up with the new environment variable using docker run -e .... It's identical to changing an environment variable on a running process, you stop it and restart with a new value passed in. Replace the concept of restarting a process with destroying and recreating a new container. If your container contains files that cannot be lost, then you should be using volumes. The other contents of the container filesystem should be either disposable or immutable.
Можно переименовать контейнер.
docker rename <old_cont> <new_cont>
Смотрим какие контейнеры запущены.
docker ps
[root@weril ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 38f35fcf081d centos "/bin/bash" 29 seconds ago Up 28 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp wordpress2 c9b035ee107f centos "/bin/bash" 12 minutes ago Up 12 minutes wordpress
Смотрим все контейнеры.
docker ps -a
[root@weril ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4056df8ab3cb centos "/bin/bash" 5 hours ago Exited (0) 5 hours ago wordpress2 41f09d3974a5 centos "/bin/bash" 5 hours ago Exited (0) 24 seconds ago wordpress
Подключаемся к контейнеру по имени. Или исполняем команду/скрипт.
docker exec -it <cont_name> <command>
[root@weril ~]# docker exec -it wordpress bash [root@c9b035ee107f /]# [root@weril ~]# sudo docker exec -it mysql mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g.
Отключаем/запускаем созданный контейнер.
docker stop <cont_name/ID> docker start <cont_name/ID>
[root@weril ~]# docker stop wordpress wordpress [root@weril ~]# docker start wordpress wordpress
Удаляем container (можно по name/id). Перед удалением нужно отключить.
docker stop <cont_name/ID> docker rm <cont_name/ID>
[root@weril ~]# docker rm c9b035ee107f Error response from daemon: You cannot remove a running container c9b035ee107ffb7ac120bbac474fdf4a65fc04cd1bcd93498064231a8b55b43a. Stop the container before attempting removal or use -f [root@weril ~]# docker stop c9b035ee107f c9b035ee107f [root@weril ~]# docker rm c9b035ee107f c9b035ee107f
docker cp foo.txt mycontainer:/foo.txt docker cp /home/user/. mycontainer:/
Копируем файл(ы) в контейнер с хоста. В общем случае лучше (принцип наименьших привилегий) использовать copy, а не add (ниже подробнее).
docker cp mycontainer:/foo.txt foo.txt docker cp mycontainer:/. /home/user/
Простейший бекап контейнера, сохраняется и image.
docker rename wordpress wordpress-old
копирование с исключениями (docker copy exclude)
.dockerignore – файл по аналогии с gitignore, позволяет при копировании игноировать какие то файлы. При этом наилучшей практикой является указание конкретных файлов, при этом в реальной эксплуатации зачастую неявное копирование может быть необходимо.
COPY . . Create file .dockerignore in your docker build context directory (so in this case, most likely a directory that is a parent to node_modules) with one line in it: node_modules
regexp exclusions
COPY [^n]* # All files that don't start with 'n' COPY n[^o]* # All files that start with 'n', but not 'no' COPY no[^d]* # All files that start with 'no', but not 'nod'
Чем отличает COPY от ADD
Более кратко
Инструкция COPY работает с локальными файлами и позволяет копировать их в контейнер.
Инструкция ADD работает с ссылками, архивами и локальными файлами: позволяет скачивать файлы из интернета аналогично команде wget, распаковывать архивы формата .tar и копировать локальные файлы в контейнер.
Подробно
Команды COPY
и ADD
– это две инструкции, используемые в Dockerfile для копирования файлов и папок из исходной директории хост-системы в образ Docker. Они выполняют схожую функцию, но есть несколько ключевых различий:
- Функциональность:
COPY
: КомандаCOPY
просто копирует файлы и папки из исходной директории на хост-системе в указанное место в образе Docker. Она предназначена для базовой операции копирования файлов.ADD
: КомандаADD
также копирует файлы и папки из исходной директории, но также может выполнять дополнительные действия, такие как извлечение файлов из архивов (например,.tar
) и загрузку файлов из URL. Это более мощная команда, которая может выполнять дополнительные операции.
- Инструкции в Dockerfile:
COPY
: ИнструкцияCOPY
имеет простой синтаксис и обычно выглядит следующим образом:COPY <source> <destination>
, где<source>
– это исходный файл или директория на хост-системе, а<destination>
– место, куда файлы или папки будут скопированы в образе.ADD
: ИнструкцияADD
также имеет синтаксисADD <source> <destination>
, но она добавляет дополнительную функциональность для обработки URL и архивов. Это может быть полезным, например, для автоматической загрузки и разархивирования файлов из интернета.
- Кеширование:
COPY
: КомандаCOPY
более подходит для случаев, когда вы хотите убедиться, что слои образа могут быть легко кешированы, поскольку она выполняет простую операцию копирования файлов. Это может способствовать более быстрой сборке образов при использовании кеширования.ADD
: ИнструкцияADD
, особенно при использовании для извлечения файлов из архивов, может создавать слои образа, которые сложнее кешировать, так как она включает в себя дополнительные шаги, которые могут изменяться со временем (например, URL-загрузки). Это может ухудшить эффективность кеширования.
В большинстве случаев для простых операций копирования файлов предпочтительно использовать COPY
, так как она более предсказуема и эффективна. ADD
лучше подходит, если вам нужна дополнительная функциональность, такая как извлечение архивов или загрузка файлов из интернета.
Network
Просмотр состояния сети. По умолчанию host и все контейнеры в одном bridge domain и им выдается адрес из динамического пула.
docker network ls docker network inspect <network_type>
-bash-4.2$ sudo docker network inspect bridge [ { "Name": "bridge", "Id": "6c5f8759faa9987533829b17424dec82d5d9477e0f68b6a454a3e090087d9f7a", "Created": "2020-05-23T22:04:26.659842657+02:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ] }, "Internal": false, "Attachable": false, "Containers": { "40fac485362f7d453377e22be0d3083ca5ef148f34749b67d75df24fc86d671c": { "Name": "wordpress2", "EndpointID": "fc7f2a768e75b7a7c90afca49fcf5e4c4a4fd685d9f5afa0af5780755f544964", "MacAddress": "02:42:ac:11:00:03", "IPv4Address": "172.17.0.3/16", "IPv6Address": "" }, "5ef3fcc80f21823726fab2d23783e726261ef7096fbb8875147fb395606dcf42": { "Name": "wordpress", "EndpointID": "80084e8ea4ba8bf4831254452da84bde5d06e5018bc8ff4bf37e577db7c2a432", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.default_bridge": "true", "com.docker.network.bridge.enable_icc": "true", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "docker0", "com.docker.network.driver.mtu": "1500" }, "Labels": {} } ]
Если нужно сделать адрес статическим (напр. адрес внутренней БД между контейнерами) – создаем отдельную сеть и переводим все контейнеры в нее (между сетями по умолчанию взаимодействие запрещено). Необходимым контейнерам назначаем статический адрес. Нужно учитывать, что если после этого придется изменить конфиг environment variables для каких-то контейнеров, то нужно будет переразворачивать контейнер (подробнее в описании docker run).
-bash-4.2$ sudo docker network connect --ip 172.17.0.2 bridge mysql Error response from daemon: User specified IP address is supported on user defined networks only sudo docker network create --subnet=172.18.0.0/16 mynet sudo docker network disconnect bridge mysql sudo docker network connect --ip 172.18.0.55 mynet mysql sudo docker network disconnect bridge wordpress sudo docker network connect mynet wordpress
MTU в контейнере судя по всему из-за ИБ нельзя поменять (даже из под root/sudo).
STATISTICS
Статистика использования ресурсов хоста каждым контейнером.
docker stats
[root@weril ~]# docker stats CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 38f35fcf081d 0.00% 524 KiB / 3.764 GiB 0.01% 1.1 kB / 656 B 0 B / 0 B 1 c9b035ee107f 0.00% 528 KiB / 3.764 GiB 0.01% 446 B / 656 B 0 B / 0 B 1
docker logs <cont_name>
-bash-4.2$ sudo docker logs mysql -bash-4.2$ sudo docker logs ipsec-vpn-server -bash-4.2$ sudo docker logs --tail 10 wordpress -bash-4.2$ sudo docker logs --tail 10 --follow wordpress
-bash-4.2$ sudo docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 7 6 2.849 GB 667.5 MB (23%) Containers 6 6 1.108 GB 0 B (0%) Local Volumes 46 3 10.05 GB 5.25 GB (52%) -bash-4.2$ sudo docker volume prune WARNING! This will remove all volumes not used by at least one container. Are you sure you want to continue? [y/N] y Deleted Volumes: ... -bash-4.2$ sudo docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 7 6 2.849 GB 667.5 MB (23%) Containers 6 6 1.108 GB 0 B (0%) Local Volumes 3 3 4.798 GB 0 B (0%)
dockerfile – разворачиваем образ на основе конфига
FROM ubuntu:22.04 COPY . /app RUN make /app CMD python /app/app.py
docker build – <Dockerfile. Пример.
# docker build - <Dockerfile FROM centos:centos7 MAINTAINER wireplay RUN yum -y update RUN yum -y install epel-release RUN yum -y install httpd RUN yum -y install git gcc RUN yum -y install libpcap gcc libpcap-devel libnet libnet-devel # startup script #COPY run-wireplay-client.sh /run-wireplay-client.sh #COPY bittorrent.stream36.pcap /wireplay/bittorrent.stream36.pcap #RUN chmod -v +x /run-wireplay-client.sh RUN rm -rf /wireplay RUN mkdir -p /wireplay RUN git clone https://github.com/pmcgleenon/wireplay.git RUN yum -y install make RUN cd wireplay && make all
Docker Swarm
Ниже на основе ролика.
Docker Swarm – позволяет объединить несколько контейнеров, в том числе на удаленных узлах, между собой в рой/кластер (swarm). Можно создавать кластерное ПО – один докер будет manager (proxy), остальные worker. Manager’ов и worker’ов может быть множество и в случае отказа одного worker происходит исключение worker’а из обработки запросов, но только один Manager в один момент времени является leader.
- Очень просто разворачивать – одной кнопкой разворачиваешь любое количество контейнеров на большом количестве хост машин (хоть всех которые есть в рое).
- Очень просто апдейтить – можно апдейтить последовательно контейнеры worker’ов не влияя на сервис. Останавливаем контейнеры, обновляем, возвращаем их в строй. Обновляем следующие.
docker swarm init --advertise-addr 1.1.1.1 - создается swarm, анонсируется адрес 1.1.1.1 как manager. В результате генерируются команды ниже по добавлению worker и manager в swarm. docker swarm join --token .... 1.1.1.1:xxxx. Ее вводим для присоединения worker'ов к swarm. docker swarm join-token manager. Ее вводим для присоединения manager к swarm. docker node ls - смотрим состав swarm.
Внутри нодов можно создавать независимые друг от друга контейнеры, а можно создавать сервисы на swarm. Например, можно создать сервис с веб оболочкой для докера – portainer. В нем можно через gui управлять кластером.
В docker по умолчанию встроена функция балансировки по созданным сервисам в swarm – при получении запроса на любой (не только manager) ноде происходит его перенаправление на рабочие ноды внутри swarm.
В целом штуки в идее крутые, но по факту пока сыро – могут не стартовать сервисы по непонятным причинам или запускаться с задержкой овер 5 минут, не распределяться контейнеры равномерно по кластеру, не распределяться нагрузка.
Ошибки
При загрузке нового image появляется ошибка ERROR: missing signature key. Решения:
-
- export DOCKER_CONTENT_TRUST=0 – мне не помогло
- обновление версии docker – мне помогло