Управление контейнерами с runC (2024)

Управление контейнерами с runC (1)

Продолжаем цикл статей оконтейнеризации. Сегодня мыпоговорим оrunC— инструменте для запуска контейнеров, разрабатываемом врамках проекта Open Containers. Цель этого проекта заключается вразработке единого стандарта вобласти контейнерных технологий. Проект поддерживают такие компании, как Facebook, Google, Microsoft, Oracle, EMC, Docker. Летом 2015 года был опубликован черновой вариант спецификации под названием Open Container Initiative (OCI).

RunC уже активно используется всовременных инструментах контейнеризации. Так, последние версии Docker (начиная с1.11, вышедшей весной этого года) созданы всоответствии соспецификациями OCI иработают набазе runC. Абиблиотека libcontainer, которая посути является частью runc, используется вDocker вместо LXC начиная ещё сверсии 1.8.

Вэтой статье мыпокажем, как можно создавать контейнеры и управлять ими спомощью runC.

Установка

Мыбудем описывать установку runc для Ubuntu16.04. Вэтой операционной системе последняя натекущий момент стабильная версияGo (1.6) уже включена вофициальные репозитории и устанавливается стандартным способом:

$ sudo apt-get install golang-go

Runc тоже включён врепозитории Ubuntu16.04, нодалеко невсамой свежей версии. Последнюю насегодняшний день версию (1.0.0) нужно собирать изисходного кода. Для этого сначала потребуется установить необходимые зависимости:

sudo apt-get install build-essential make libseccomp-dev

Вот ивсё, можно приступать ксборке runC:

$ git clone https://github.com/opencontainers/runc$ cd runc$ make$ sudo make install 

Врезультате выполнения этих команд runc будет установлен вдиректорию /usr/local/bin/runc.

Создаём первый контейнер

Итак, ксозданию первого контейнера всё готово.

Первое, что нам нужно сделать— это создать отдельную директорию под новый контейнер, авнутри её— директорию rootfs:

$ mkdir /mycontainer$ cd /mycontainer$ mkdir rootfs

Начнём сcамого простого примера. Загрузим docker-образ memcached, преобразуем его вархив *.tar ираспакуем вдиректорию rootfs:

$ docker export $(docker create memcached) | tar -C rootfs -xvf -

В результате выполнения этой команды в директорию rootfs будут помещены системные файлы для будущего контейнера:

$ ls rootfsbin dev etc lib media opt root sbin sys usrboot entrypoint.sh home lib64 mnt proc run srv tmp var

После этого мыможем запускать контейнеры иуправлять ими, неприбегая кпомощи Docker. Создадим конфигурационный файл, вкотором будут прописаны настройки нового контейнера:

$ sudo runc spec

После этого вдиректории rootfs появится новый файл— config.json. Кзапуску нового контейнера всё готово. Выполним:

$ sudo runc run mycontainer

Внутри контейнера будет запущена командная оболочка.

Мырассмотрели самый элементарный пример, вкотором контейнер был запущен савтоматически сгенерированными настройками. Для более тонкой настройки контейнеров упомянутый выше файл config.json придётся редактировать вручную. Разберём его структуру более подробно.

Конфигурационный файл config.json

Впервой части конфигурационного файла описываются общие характеристики контейнера: версия OCI, операционная система иеёархитектура, параметры терминала:

 "ociVersion": "1.0.0-rc1", "platform": { "os": "linux", "arch": "amd64" },"process": { "terminal": true, "user": { "uid": 0, "gid": 0 }, "args": [ "sh" ], "env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM=xterm" ],

Вследующем разделе приводятся настройки для директории, вкоторой работает контейнер:

"cwd": "/", "capabilities": [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ],

Аббревиатура CWD означает current working directory. Внашем случае это директория «/». Далее указывается набор capabilities (нарусский язык этот термин несовсем удачно переводят как «возможности»)— разрешений для исполняемых файлов наиспользование определённых подсистем без прав root. CAP_AUDIT_WRITE разрешает делать записи вжурнал аудита, CAP_KILL— отправлять процессам сигналы, аCAP_NET_BIND_SERVICE— разрешает привязку сокетов кпривилегированным портам (т.е. портам сномерами меньше 1024).

Следующий раздел— rlimits:

"rlimits": [ { "type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024 } ], "noNewPrivileges": true },

Здесь мыустанавливаем для контейнера лимит ресурсов, аименно— максимальное количество одновременно открытых файлов (RLIMIT_NOFILE), которое составляет 1024.

Далее идёт описание настроек корневой файловой системы:

"root": { "path": "rootfs", "readonly": true}

Вразделе mounts описываются cмонтированные вконтейнер директории:

"mounts": [ { "destination": "/tmp", "type": "tmpfs", "source": "tmpfs", "options": ["nosuid","strictatime","mode=755","size=65536k"] }, { "destination": "/data", "type": "bind", "source": "/volumes/testing", "options": ["rbind","rw"] }]

Мы рассмотрели лишь самые основные разделы файла config.json. О некоторых других его разделах мы поговорим ниже. С подробным описанием этого файла можно ознакомиться здесь.

Хуки

Ещё одна интересная возможность runc — настройка хуков: мыможем прописать в конфигурационном файле конкретные действия, которые будут выполнены перед запуском пользовательского процесса вконтейнере (prestart), после запуска пользовательского процесса (poststart) ипосле его остановки (poststop).

Приведём несколько примеров, чтобы лучше понять, зачем нужны хуки икак прописываются соответствующие настройки вконфигурационном файле. Представим себе вполне такую ситуацию: прежде чем запускать некоторую программу вконтейнере, нам нужно настроить сеть. Для этого добавим вконфигурационный файл такой хук (пример взят изофициальной документации):

"hooks": { "prestart": [ { "path": "/path/to/script" }

В разделе path прописывается путь к программе, которая и выполнит настройку сети. Готовые программные инструменты подобного рода можно найти на Github — см., например, здесь.

Рассмотрим теперь пример poststart-хука:

"poststart": [ { "path": "/usr/bin/notify-start", "timeout": 5 }

Когда этот хук сработает, будет запущен скрипт (в нашем примере она называется notify-start), который запишет влоги информацию особытиях, связанных сзапуском контейнера.

Poststop-хуки инициируют действия, которые будут выполнены после завершения пользовательского процесса вконтейнере. Они могут понадобиться, например, вслучае, когда нам нужно удалить логи исессионные файлы, оставленные контейнером всистеме, азаодно исам контейнер. Вот простой пример:

"poststop": [ { "path": "/usr/sbin/cleanup.sh", "args": ["cleanup.sh", "-f"] }

При срабатывании этого хука будет запущен скрипт cleanup.sh, который ивыполнить все упомянутые выше действия.

Управление контейнерами: основные команды

Для управление контейнерами вrunc используются простые ипривычные команды. Вот ихкраткий список:

#просмотреть список контейнеров и информацию об их состоянииrunc list#запустить процесс в контейнереrunc start mycontainerid#остановить процесс в контейнерerunc stop mycontainerid#удалить контейнерrunc delete mycontainerid

Настройка сети

Сбазовыми операциями поуправлению контейнерами мыразобрались. Попробуем настроить в контейнере сеть. Это несамая простая задача. Все операции нужно осуществлять вручную.

Для начала выполним следующую последовательность команд (взято из этой статьи):

$ sudo brctl addbr runc0$ sudo ip link set runc0 up$ sudo ip addr add 192.168.10.1/24 dev runc0$ sudo ip link add name veth-host type veth peer name veth-guest$ sudo ip link set veth-host up$ sudo brctl addif runc0 veth-host$ sudo ip netns add runc$ sudo ip link set veth-guest netns runc$ sudo ip netns exec runc ip link set veth-guest name eth1$ sudo ip netns exec runc ip addr add 192.168.10.101/24 dev eth1$ sudo ip netns exec runc ip link set eth1 up$ sudo ip netns exec runc ip route add default via 192.168.10.1

Приведённые команды говорят сами за себя. Сначала мы создаём мост для связи между контейнером и интерфейсом на основном хосте. Затем «поднимаем» виртуальный интерфейс и добавляем его в мост. После этого мы создаём сетевое пространство имён (неймспейс) с именем runc и назначаем в нём IP-адрес интерфейсу eth1.

Чтобы настроить в контейнере сеть, мы должны ассоциировать с ним неймспейс runc. Для этого внесём небольшие изменения в файл config.json:

...... "root": { "path": "rootfs", "readonly": false }

В разделе namespaces укажем путь к пространству имён runc:

 { "type": "network", "path": "/var/run/netns/runc" },

Вот и всё: все необходимые настройки прописаны. Сохраняем внесённые изменения и перезапускаем контейнер. Выполним на основном хосте команду:

$ ping 192.168.10.101PING 192.168.10.101 (192.168.10.101) 56(84) bytes of data.64 bytes from 192.168.10.101: icmp_seq=2 ttl=64 time=0.070 ms64 bytes from 192.168.10.101: icmp_seq=3 ttl=64 time=0.090 ms64 bytes from 192.168.10.101: icmp_seq=4 ttl=64 time=0.106 ms64 bytes from 192.168.10.101: icmp_seq=5 ttl=64 time=0.091 ms64 bytes from 192.168.10.101: icmp_seq=6 ttl=64 time=0.097 ms

Её вывод свидетельствует о том, что контейнер принимает ping с основного хоста.

Заключение

Эта статья представляет собой лишь краткое введение в runC. Для желающих узнать больше приводим небольшую подборку полезных ссылок:

Если вы уже экспериментировали с runС — приглашаем поделиться опытом в комментариях.

Управление контейнерами с runC (2024)
Top Articles
Latest Posts
Article information

Author: Merrill Bechtelar CPA

Last Updated:

Views: 5528

Rating: 5 / 5 (70 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Merrill Bechtelar CPA

Birthday: 1996-05-19

Address: Apt. 114 873 White Lodge, Libbyfurt, CA 93006

Phone: +5983010455207

Job: Legacy Representative

Hobby: Blacksmithing, Urban exploration, Sudoku, Slacklining, Creative writing, Community, Letterboxing

Introduction: My name is Merrill Bechtelar CPA, I am a clean, agreeable, glorious, magnificent, witty, enchanting, comfortable person who loves writing and wants to share my knowledge and understanding with you.