Для создания образа будем использовать один из популярных инструментов старых админов – supermin. Те кто на ты с man могу сразу там почитать что к чему)
свободно от авторского текста
supermin [-o OUTPUTDIR] - Имена СПИСОК PKGS ... supermin [-o OUTPUTDIR] Имена файлов PKG ...
Supermin – это инструмент для создания “программных сущностей или устройств или наборов файлов” – читай как хочешь.
Они вообщем-то крошечные и похожи на виртуальные машины, обычно около 100 КБ в размере, которые создаются полностью на лету за доли секунды когда нужно. Начиная с версии 4 этот инструмент является независимым от дистрибутива и может создавать “супер-сущности” для нескольких популярных дистрибутивов Linux, а добавить поддержку других достаточно просто.
и пару слов о том как он работает ))
Есть два режима использования супермина:
С параметром –names supermin берет список имен пакетов и создает supermin сущность, содержащюю эти пакеты и все зависимости, которые требуются для этих пакетов. В этом режиме supermin обычно требуется доступ к сети потому что, возможно, потребуется проконсультироваться с репозиториями пакетов, чтобы определить зависимости и загрузить пакеты.
И без –names supermin принимает список самих пакетов (т.е. имена файлов уже локально доступных пакетов). Этот пакет должен быть полным и с отсутствием зависимостей за границей набора пакетов, которые вы предоставляете. В этом режиме супермин не требует доступа к сети. Он видит все файлы пакетов сами. Под «пакетом» мы понимаем пакет RPM, DEB и т. д. Имя пакета может быть полным именем (например, “coreutils-8.22-24.el7.x86_64”) или некоторое сокращение (например, “coreutils”). Точный формат имени и допустимые сокращения зависят от менеджера пакетов.
Файловая программная сущность supermin, которая создается в результате, состоит из двух файлов, называемых «hostfiles» и «base.img».
По умолчанию они записываются в текущий рабочий каталог , но если указать опцию -o OUTPUTDIR, то эти файлы будут записаны в этот каталог (обычно этот каталог “supermin.d“, но вы можете называть его как хотите).
Исходя из обобщенного выше, для примера, при создание своего образа с нуля нам уже будет нужна целевая ОС с установленными пакетами.
Итак, устанавливаем 5 версию (если его еще нет в ОС) и создаем рабочий каталог:
# yum install supermin5 $ cd ; mkdir supermin ; cd supermin
Далее готовим минимальное дерево среды с запросом явной установки только одного пакета с зависимостями – yum.
В говорливом режиме мы видим много интересного под капотом ))
$ supermin5 --verbose --prepare yum -o centos8s. supermin: version: 5.2.1 supermin: rpm: detected RPM version 4.14 supermin: rpm: detected RPM architecture x86_64 supermin: package handler: fedora/rpm supermin: prepare: yum supermin: packages specified on the command line: - yum-4.7.0-18.el8.noarch supermin: writing centos8s.supermin.d.ovrhqye7/packages supermin: after resolving dependencies there are 131 packages: ... Loaded plugins: builddep, changelog, config-manager, copr, debug, debuginfo-install, download, generate_completion_cache, groups-manager, needs-restarting, playground, repoclosure, repodiff, repograph, repomanage, reposync, system-upgrade DNF version: 4.7.0 ... supermin: renaming centos8s.supermin.d.ovrhqye7 to centos8s.supermin.d
в результате мы получили:
$ tree centos8s.supermin.d centos8s.supermin.d ├── base.tar.gz └── packages 0 directories, 2 files
А что же было сделано? Да очень просто! Это команда просто вызывает yum на хосте, получает все зависимости этого пакета из текущих репозиториев и сохраняет в кэш менеджера пакетов на хосте. Затем ищет конфигурационные файлы в пакетах и на основе этого создает дерево директорий с этими файлами конфигурации и упаковывает в архив в указанную директорию. Вот такой индийский стиль редхата для “базового набора” файлов. Файл packages – просто текст с именем упомянутого в выхове пакета.
Как ни странно, по логике, дальше нам нужно все это разархивировать в отдельный каталог в так называемом режиме “build в chroot” и туда все установить. Мы же помним как это) Тихий смех в зале среди старых админов из-за странных действий.
–verbose – просто выводить больше информации;
–build – собственно сам глагол действия;
–include-packagelist – включать ли текстовый файлик в корень файлового дерева установленных пакетов;
–format – в каком режиме или каким путем установить (определить /);
–centos8s.appliance.d – где это все будет установлено как финальная среда/дерево через chroot
$ supermin --verbose --build --include-packagelist --format chroot centos8s.supermin.d -o centos8s.appliance.d supermin: version: 5.2.1 supermin: rpm: detected RPM version 4.14 supermin: rpm: detected RPM architecture x86_64 supermin: package handler: fedora/rpm supermin: build: centos8s.supermin.d supermin: reading the supermin appliance supermin: build: visiting centos8s.supermin.d/base.tar.gz type gzip base image (tar) supermin: build: visiting centos8s.supermin.d/packages type uncompressed packages supermin: mapping package names to installed packages supermin: resolving full list of package dependencies supermin: build: 131 packages, including dependencies supermin: build: 29592 files supermin: build: 29592 files, after matching excludefiles supermin: build: 29592 files, after adding hostfiles supermin: build: 14005 files, after removing unreadable files supermin: build: 14037 files, after munging supermin: chroot: creating /packagelist supermin: renaming centos8s.appliance.d.8ncgbjln to centos8s.appliance.d
О! Теперь в новом каталоге все базовое дерево каталогов Linux в которые уже установлены все новые пакеты с зависимостью, а не только каркас с файлами конфигов. Обратим внимание на размер и идем дальше.
Кстати, для тех, кому не нужно всех локалей, установленных по умолчанию, могжно почистить и оставить только английскую локализацию, тем самым выиграв около 50 мегабайт
$ du -hs * 334M centos8s.appliance.d 176K centos8s.supermin.d $ ( cd centos8s.appliance.d/usr/share/locale ; find . -type d -not -regex 'en_US' -not -regex '.' -not -regex '..' -print | xargs /bin/rm -rf ) $ ( cd centos8s.appliance.d/usr/share/i18n/locales ; find . -type d -not -regex 'en_US' -not -regex '.' -not -regex '..' -print | xargs /bin/rm -rf )
И надо добавить переменную для yum (иначе потом просто не будет работать yum в контейнере)
$ mkdir -p centos8s.appliance.d/etc/yum/vars $ echo 8-stream > centos8s.appliance.d/etc/yum/vars/stream $ echo 8 > centos8s.appliance.d/etc/yum/vars/releasever
Теперь все готово упаковать созданное дерево каталогов в tar архив пригодный для экспорта в docker как базовый слой:
$ tar --numeric-owner -cpf centos8s_zero.tar -C centos8s.appliance.d . $ ls -l total 314788 drwxr-xr-x. 17 U4710S U4710S 4096 Aug 11 23:04 centos8s.appliance.d drwxr-xr-x. 2 U4710S U4710S 4096 Aug 11 20:38 centos8s.supermin.d -rw-rw-r--. 1 U4710S U4710S 322334720 Aug 11 23:40 centos8s_zero.tar
И теперь экспортируем tar архив как образ для контейнеров docker в локальной системе (пользователь должен быть в группе docker для доступа к сокету docker демона):
$ sudo cat centos8s_zero.tar | docker import - local/centos8s-zero sha256:4f3a0bfdbf5fed5193b9e7c8b51bdc9f495ae918381fa8ee9a8eb6b679b79191 $ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE local/centos8s-zero latest 9a0d60008825 14 seconds ago 312MB
В данный момент мы получили в локальной системе минимальный образ файловой сиcтемы для Centos 8 Stream.
Теперь можно запустить интерактивный контейнер на основе этого образа и выполнить остальную установку пакетов до минимального набора.
Мы помним, что контейнеры это не виртуальные системы, а просто изоляции отдельных процессов с изменными namespace для файлового и сетевого пространства. В остальном они равноправно живут под управлением одного ядра как и все остальные процессы не заключенные в изоляцию докером. Поэтому, даже этот контейнер можно не убивать при выходе, а просто отключить стандартные дескрипторы ввода/вывода Вашего терминала от изолированного процесса (Ctrl P+Q). Потом, при необходимости, можно снова “подцепиться” через docker container attach.
$ sudo docker run -it local/centos8s-zero /bin/bash bash-4.4# yum clean all 0 files removed bash-4.4# yum makecache CentOS Stream 8 - AppStream 7.9 MB/s | 31 MB 00:03 CentOS Stream 8 - BaseOS 3.3 MB/s | 41 MB 00:12 CentOS Stream 8 - Extras 64 kB/s | 18 kB 00:00 CentOS Stream 8 - Extras common packages 12 kB/s | 6.8 kB 00:00 Metadata cache created. bash-4.2# bash-4.2# yum -y install rpm rpm-build-libs rpm-libs python3-rpm bash-4.2# yum -y install cracklib cracklib-dicts binutils bash-4.2# yum -y install dbus-libs device-mapper device-mapper-libs file bash-4.2# yum -y install iproute iptables iptables-services iputils groff-base bash-4.2# yum -y install less libcroco libgomp libunistring bash-4.2# yum -y install vim rootfiles bash-4.2# yum -y install openssh-server bash-4.2#
Теперь когда я вышел из контейнера – он остановился или нет?
Так или иначе Вы можете снова запустить контейнер, если он остановился и подключиться.
На следующем шаге я создаю образ “обновленной” файловой системы в новом архиве для создания будущих образов на его основе. Я просто экспортирую текущее состоние файловой системы контейнера в tar архив.
$ docker ps --all CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6ea89e7d69c3 local/centos8s-zero "/bin/bash" 31 minutes ago Up 31 minutes adoring_jemison $ docker export 6ea89e7d69c3 > centos8s_minimal.tar
Сейчас я готов создавать новые образы на базе данного архива/образа и использовать их как базу для новых прикладных образов (с новыми слоями) для новых контейнеров на базе CentOS 8 Stream.
Наиболее правильным шагом будет создание контенейра с доступом из сети, так как это возможно основной смысл использования таких любых сетевых процессов/микросервисов в изоляции, как атомарные сущности, так и в рамках минимальной среды ОС.
Для этого подойдет официальный пример для получения командной оболочки в контейнере с sshd процессом НО! с нашими изменениями
Импортируем новый образ.
$ cat centos8s_minimal.tar | docker import - local/centos8s-minimal sha256:6dac80d36138f1176ce2f6232375dcdd1a69880a7e8b034e1abf75f9cddf79a3 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE local/centos8s-minimal latest 6dac80d36138 51 seconds ago 877MB local/centos8s-zero latest 9a0d60008825 4 days ago 312MB
Создаем докер файл для образа где будет запускаться ssh демон и берем за основу наш подготовленный minimal в отдельной директории
$ mkdir centos8s_with_ssh && cd centos8s_with_ssh $ cat dockerfile-centos8s-minimal-with-sshd FROM local/centos8s-minimal RUN mkdir /var/run/sshd RUN ssh-keygen -A RUN echo 'root:12345678' | chpasswd EXPOSE 22 CMD ["/sbin/sshd", "-D"]
$ docker build --force-rm=true --file=dockerfile-centos8s-minimal-with-sshd --tag=local/centos8s-minimal-with-sshd . [+] Building 9.1s (8/8) FINISHED => [internal] load build definition from dockerfile-centos8s-minimal-with-sshd 1.0s => => transferring dockerfile: 266B 0.0s => [internal] load .dockerignore 1.1s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/local/centos8s-minimal:latest 0.0s => [1/4] FROM docker.io/local/centos8s-minimal 0.3s => [2/4] RUN mkdir /var/run/sshd 1.8s => [3/4] RUN ssh-keygen -A 2.3s => [4/4] RUN echo 'root:12345678' | chpasswd 2.1s => exporting to image 1.1s => => exporting layers 1.1s => => writing image sha256:5927624d3cdf7db302f6cf770e12bec4bb5fbfd67514b3b2d4dc1a236b533078 0.0s => => naming to docker.io/local/centos8s-minimal-with-sshd
Смотрим что вышло
docker images -a REPOSITORY TAG IMAGE ID CREATED SIZE local/centos8s-minimal-with-sshd latest 5927624d3cdf 2 minutes ago 877MB local/centos8s-minimal latest 6dac80d36138 27 minutes ago 877MB <none> <none> 3dfb6c2979e5 27 minutes ago 0B local/centos8s-zero latest 9a0d60008825 4 days ago 312MB docker images -a --digests REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE local/centos8s-minimal-with-sshd latest <none> 5927624d3cdf 2 minutes ago 877MB local/centos8s-minimal latest <none> 6dac80d36138 27 minutes ago 877MB <none> <none> <none> 3dfb6c2979e5 27 minutes ago 0B local/centos8s-zero latest <none> 9a0d60008825 4 days ago 312MB
Запускаю контейнер на базе этого образа. Следует отметить, что docker вообще “не дружит с безопасностью” и без каких-либо ограничений может вмешаться в работу уже настроенного файервола и создать не мало проблем, если Вы запустите его на боевом сервере, пытаясь совместить эту реализацию контейнеров с уже работающей продуктовой системой.
И снова смех в зале у старых админов.
Поэтому если Вы еще не завербованы в юные убунтоиды, то стоит в приказном порядке указать демону о недопустимости такого поведения.
А еще лучше выделить для docker отдельный зоопарк виртуальных машин-песочниц для изоляции процессов, что и убережет Вас от сюрпризов.
Пока же просто добавим параметр
–iptables=false в файл конфигурации /etc/sysconfig/docker и перезапустим демона docker.
docker run -d --name centos8s-sshd-empty local/centos8s-minimal-with-sshd 3f14b88438ba34a4ab0a121a5f38465824ee38fe0e565598bf31f5e1c455848d
docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3f14b88438ba local/centos8s-minimal-with-sshd "/sbin/sshd -D" 35 seconds ago Up 32 seconds 22/tcp centos8s-sshd-empty
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 3f14b88438ba 172.17.0.2
Попробуем найти ssh порт на этом IP на мосту docker0 на узле где запущен контейнер с sshd
# nmap 172.17.0.2 Starting Nmap 7.92 ( https://nmap.org ) at 2023-08-16 01:45 +03 Nmap scan report for 172.17.0.2 Host is up (0.000027s latency). Not shown: 999 closed tcp ports (reset) PORT STATE SERVICE 22/tcp open ssh MAC Address: 02:42:AC:11:00:02 (Unknown) Nmap done: 1 IP address (1 host up) scanned in 0.39 seconds
И пробуем подключиться к этому демону sshd в его изоляции
# ssh 172.17.0.2 The authenticity of host '172.17.0.2 (172.17.0.2)' can't be established. ECDSA key fingerprint is SHA256:B7v5o7snKYMPseZMw55QX/L25S6PR3fHlo6gn6izi4M. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '172.17.0.2' (ECDSA) to the list of known hosts. root@172.17.0.2's password: [root@3f14b88438ba ~]#
О том как пробросить сетевые порты или добавить иные правила в файервол для сетевой доступности приложений в изолированных в контейнере namespace думаю уже обьяснять не надо старым админам. Дерзайте на работе!