Linux: Docker, conrainerd, container security (hardening)

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 от технологий упаковки приложений, которые решают проблемы зависимостей пустем сохранения этих зависимостей в пакете с приложением (
    Snap, Flatpak, AppImage).
  • 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, при этом нужно учитывать:
    • Напр. если на выходе после сборки софта получается bin/jar или другой execution файл, а сборка его требует множества зависимостей, которые при работе софта не требуются – рекомендуется использовать multi build stage инструкцию в dockerfile для создания образа, когда сборка исполняемого файла (напр. утилиты C/Go) требует множества разных зависимостей и происходит в отдельном контейнере, который со всеми зависимостями дропается после сборки, но из него извлекается нужный файл и он используется в новом контейнере, в котором отсутствует ворох зависимостей и он используется для создания образа. Раньше без этой инструкции поступали по сути так же (создавали отдельный контейнер в котором осуществляли сборку и перекладывали через какое то хранилище результат во второй контейнер), но сейчас более удобно/native.
  • Слои – следование по 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 в поломанную систему
      • траблшутинг
  • Взаимодействие 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.
The ENTRYPOINT specifies a command ((process/scripts)) that will always be executed when the container starts.
The CMD specifies arguments that will be fed to the ENTRYPOINT ((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 is ubuntu and the command is bash. 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.

IOX app hosting platform minecraft blog

  • 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. Они выполняют схожую функцию, но есть несколько ключевых различий:

  1. Функциональность:
    • COPY: Команда COPY просто копирует файлы и папки из исходной директории на хост-системе в указанное место в образе Docker. Она предназначена для базовой операции копирования файлов.
    • ADD: Команда ADD также копирует файлы и папки из исходной директории, но также может выполнять дополнительные действия, такие как извлечение файлов из архивов (например, .tar) и загрузку файлов из URL. Это более мощная команда, которая может выполнять дополнительные операции.
  2. Инструкции в Dockerfile:
    • COPY: Инструкция COPY имеет простой синтаксис и обычно выглядит следующим образом: COPY <source> <destination>, где <source> – это исходный файл или директория на хост-системе, а <destination> – место, куда файлы или папки будут скопированы в образе.
    • ADD: Инструкция ADD также имеет синтаксис ADD <source> <destination>, но она добавляет дополнительную функциональность для обработки URL и архивов. Это может быть полезным, например, для автоматической загрузки и разархивирования файлов из интернета.
  3. Кеширование:
    • 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 (делается через перенаправление логов сервиса в stderr/stdout docker’а). Последние 10 логов можно посмотреть с флагом –tail. С флагом –follow можно смотреть логи в реальном времени (аналог tail -f). Флаги можно использовать одновременно.
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
Использование памяти, мне было полезно т.к. в системе накопилось много (более 5 GB) unused volumes (Unused local volumes are those which are not referenced by any containers.)
-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 – мне помогло

 

 

Leave a Reply