Недавно случайно нашел на гитхабе очень хороший проект.bocker, был реализован простой докер с сотнями строк кода.Потом я его посмотрел и нашел очень интересным.Я просто поиграл в него и внес некоторые изменения(проект давно не обновлялся и есть места которые не поддерживается), Просто проанализируйте и поделитесь.
предисловие
я посмотрел на100Написать докер в одну строку точно невозможно, раньше это выглядело как самый простой питон плюс зависимости требовали бы сотен строк кода, напримерmoker, и есть тысячи строк для идеальной реализации gomydocker, но, взглянув на этот проект, в нем действительно всего более 100 строк, но он используетshell, Но помните, что более 100 строк должны быть сделаны только с оболочкой.Если вы не знакомы с оболочкой, вы можете прочитать некоторые базовые знания о оболочке.
В настоящее время этот проект в основном реализуется вИзвлечение изображения, просмотр изображения, запуск контейнера, удаление контейнера, представление контейнера, ограничение ресурсов контейнера, удаление изображения,функции одни из самых основных,а недоделок много.Здесь я грубо разберу принципы их реализации,разберу каждый процесс,и разберу их нормально по порядку операций.В первую очередь обсуждаемая здесь ситуация это linux рекомендуется использовать centos7 и для систем выше ubuntu14 процесс на самом деле относительно прост.базовая реализация опирается на некоторые основные компоненты linux, iptables, cgroup и пространство имен linux для полной сети, ограничения ресурсов, изоляции ресурсов и используйте оболочку для управления этими ресурсами.
Начать!!
Среда конфигурации
предпочтительноvagrant(Если это mac и windows, то рекомендуется использовать эту среду, если linux, если ядро системы выше, можно напрямую управлять),vagrantЭто может помочь нам реализовать облегченную среду разработки.Очень нравится.Он работает и управляет vm,и с более тяжелыми средами иметь дело удобнее.Здесь среду нужно настроить заранее.Официальный адрес я прикрепил в ссылке.
Есть проблема с источником данных epel официального Vagrantfile, и сеть зависима.Весь процесс автоматизирован, но для отладки не удобен.Для облегчения личной отладки я написал процесс пошагово, которым будет удобнее работать.
Загрузите виртуальную среду (бродячий файл конфигурации)
Создать файл конфигурации Vagrant
Запуск бродячей конфигурации
$script = <<SCRIPT
(
echo "echo start---config"
) 2>&1
SCRIPT
Vagrant.configure(2) do |config|
config.vm.box = 'puppetlabs/centos-7.0-64-nocm'
config.ssh.username = 'root'
config.ssh.password = 'puppet'
config.ssh.insert_key = 'true'
config.vm.provision 'shell', inline: $script
end
拷贝上边的文件Vim为保存到一个文件中Vagrantfile中
vagrant up (直接启动,这里会去源拉去centos的镜像,时长主要根据个人网络)
vagrant ssh (直接进入)
Установить зависимости
- Установите источник rpm:
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm(官方用的eprl源不存在了)
- Тогда соответствующие зависимости:
Ядро cgourp, btrfs-progs
yum install -y -q autoconf automake btrfs-progs docker gettext-devel git libcgroup-tools libtool python-pip
jq
- Создайте смонтированную файловую систему: (файловая структура, поддерживаемая образом докера) Подробнее см. по ссылке btrfs wiki
fallocate -l 10G ~/btrfs.img
mkdir /var/bocker
mkfs.btrfs ~/btrfs.img
mount -o loop ~/btrfs.img /var/bocker
- Установить базу:
pip install git+https://github.com/larsks/undocker
systemctl start docker.service
docker pull centos
docker save centos | undocker -o base-image
- Установите linux-utils инструмент для Linux
git clone https://github.com/karelzak/util-linux.git
cd util-linux
git checkout tags/v2.25.2
./autogen.sh
./configure --without-ncurses --without-python
make
mv unshare /usr/bin/unshare
- Настройка сетевой карты и сетевой переадресации
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables --flush
iptables -t nat -A POSTROUTING -o bridge0 -j MASQUERADE
iptables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADE
ip link add bridge0 type bridge
ip addr add 10.0.0.1/24 dev bridge0
ip link set bridge0 up
Позвольте мне кратко объяснить описанный выше процесс.Поскольку базовая сеть докеров будет реализована с использованием iptables и пространства имен linux, это необходимо для нормальной работы сети контейнеров, которая в основном разделена на две части.
1 Сначала нужно создать виртуальную сетевую картуbridge0, а затем настройте преобразование nat-адреса сетевой карты bridge0, где мост эквивалентен адресу в докере.docker0, bridge0 эквивалентен устройству коммутатора уровня 2 в сети.Он может подключаться к различным сетевым устройствам.Когда запрос достигает устройства моста, он может транслировать и пересылать сообщение через MAC-адрес сообщения, поэтому весь контейнер виртуальный сетевые карты должны быть под мостом., который также является соединениемnamespaceсетевое оборудование ихост-сетьметод, здесь будет объяснение. (при необходимости реализацииoverlayи т. д., вам нужно переключиться на более продвинутый инструмент преобразования, такой как использование ovs для классовvxlan, greпреобразование протокола)
2 Включите переадресацию ядра и настройте iptablesMASQUERADE, это использование правила MASQUERADE для преобразования ip контейнера в ip выходной сетевой карты хоста.В пространстве имен linux при запросе внешнего адреса хоста замените исходный адрес в пространстве имен хостом как исходный адрес, так что вы можете адрес конвертируется нормально в пространстве имен.
Подготовка среды завершена, и можно приступить к анализу конкретной реализации.
Прежде всего, подумайте о самых важных частях докера.зеркало, вторая независимаяокружающая обстановка, ip, сеть, третий естьОграничения в ресурсах.
Здесь я добавил некоторые китайские комментарии к коду для простоты понимания.Этот проект называется bocker, и меня также называют bocker.
- порт входа в программу
[[ -z "${1-}" ]] && bocker_help "$0"
# @1 执行与help
case $1 in
pull|init|rm|images|ps|run|exec|logs|commit|cleanup) bocker_"$1" "${@:2}" ;;
*) bocker_help "$0" ;;
esac
Справка относительно проста, вход в программу, логика аналогична основной функции в нашей программе, которая выполняет различные функции в соответствии с входящими параметрами.
- Рабочая среда?Зеркальная тяга Бокер тяга ()
function bocker_pull() { #HELP Pull an image from Docker Hub:\nBOCKER pull <name> <tag>
# @1 获取对应镜像进行拉去, 源代码老版本是v1的docker registry是无效的, 我更新为了v2版本
token=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/$1:pull" | jq '.token'| sed 's/\"//g')
registry_base='https://registry-1.docker.io/v2'
tmp_uuid="$(uuidgen)" && mkdir /tmp/"$tmp_uuid"
# @2 获取docker镜像每一层的layter,保存到数组中
manifest=$(curl -sL -H "Authorization: Bearer $token" "$registry_base/library/$1/manifests/$2" | jq -r '.fsLayers' | jq -r '.[].blobSum' )
[[ "${#manifest[@]}" -lt 1 ]] && echo "No image named '$1:$2' exists" && exit 1
# @3 依次获取镜像每一层, 然后init
for id in ${manifest[@]}; do
curl -#L -H "Authorization: Bearer $token" "$registry_base/library/$1/blobs/$id" -o /tmp/"$tmp_uuid"/layer.tar
tar xf /tmp/"$tmp_uuid"/layer.tar -C /tmp/"$tmp_uuid"
done
echo "$1:$2" > /tmp/"$tmp_uuid"/img.source
bocker_init /tmp/"$tmp_uuid" && rm -rf /tmp/"$tmp_uuid"
}
В этом проекте просто реализован докер, поэтому репозиторий образов докеров точно не реализован.Репозиторий образов по-прежнему использует официальный исходник.Если вам нужно использовать свой приватный исходник, то нужно внести изменения в исходники и код образа.Логика здесь заключается в загрузке соответствующего изображения.Слоистый,Потомсохранитьна собственное файловое зеркало,Здесь я изменил его логику и использовал версию API реестра докеров v2., (т.к. у автора истек код версии исходников v1, корректные данные получить у официалов невозможно, автор три года не подавал, да и скорость разработки докеров слишком быстрая, это понятно), процесс первый для авторизации, для получения соответствующих разрешений для соответствующего образа Сделайте токен, а затем используйте токен для получения каждого слоя зеркала.Здесь я использую плагин jq json parsing, будет удобнее работать с Джейсоном, конвертировать его в переменные, связанные с оболочкой, а затем загрузите все слои и выгрузите их в их собственный уникальный зеркальный каталог, и одновременно сохраните изображение с именем файла.
- бокер сохранить изображение
function bocker_init() { #HELP Create an image from a directory:\nBOCKER init
# @1 生成随机数镜像,就像生成docker images 唯一id
uuid="img_$(shuf -i 42002-42254 -n 1)"
if [[ -d "$1" ]]; then
[[ "$(bocker_check "$uuid")" == 0 ]] && bocker_run "$@"
# @2 创建对应image文件 btrfs volume
btrfs subvolume create "$btrfs_path/$uuid" > /dev/null
cp -rf --reflink=auto "$1"/* "$btrfs_path/$uuid" > /dev/null
[[ ! -f "$btrfs_path/$uuid"/img.source ]] && echo "$1" > "$btrfs_path/$uuid"/img.source
echo "Created: $uuid"
else
echo "No directory named '$1' exists"
fi
}
Вот собственно сохранено и вытащено с зеркального складаlayer, а затем создайте каталог.Здесь следует подчеркнуть, что каталог изображений, используемый докером, должен бытьbtrfsфайловую структуру, а затем сохранить соответствующее имя образа в файл img.source, где среда была создана с помощью команды btrfs при подготовкефайловая система 10g,Docker поддерживает различные системы хранения, подробности можно найти здесь.
.
- С изображением вы можете выполнить важный прогон бокера (Часть 1)
function bocker_run() { #HELP Create a container:\nBOCKER run <image_id> <command>
# @1 环境准备,生成唯一id,检查相关镜像,ip, mac地址
uuid="ps_$(shuf -i 42002-42254 -n 1)"
[[ "$(bocker_check "$1")" == 1 ]] && echo "No image named '$1' exists" && exit 1
[[ "$(bocker_check "$uuid")" == 0 ]] && echo "UUID conflict, retrying..." && bocker_run "$@" && return
cmd="${@:2}" && ip="$(echo "${uuid: -3}" | sed 's/0//g')" && mac="${uuid: -3:1}:${uuid: -2}"
# @2 通过ip link && ip netns 实现隔离的网络namespace与网络通信
ip link add dev veth0_"$uuid" type veth peer name veth1_"$uuid"
ip link set dev veth0_"$uuid" up
ip link set veth0_"$uuid" master bridge0
ip netns add netns_"$uuid"
ip link set veth1_"$uuid" netns netns_"$uuid"
ip netns exec netns_"$uuid" ip link set dev lo up
ip netns exec netns_"$uuid" ip link set veth1_"$uuid" address 02:42:ac:11:00"$mac"
ip netns exec netns_"$uuid" ip addr add 10.0.0."$ip"/24 dev veth1_"$uuid"
ip netns exec netns_"$uuid" ip link set dev veth1_"$uuid" up
ip netns exec netns_"$uuid" ip route add default via 10.0.0.1
btrfs subvolume snapshot "$btrfs_path/$1" "$btrfs_path/$uuid" > /dev/null
Разобрать:
При запуске bocker run будут выполняться некоторые конфигурации столбцов. Я также добавил и прокомментировал. Первая часть сначала сгенерирует соответствующую конфигурацию. Во-первых, уникальный идентификатор каждого bocker будет сгенерирован с помощью функции shuf, и соответствующая легальность будет проверяется, а затем в соответствии с сгенерированным случайным числом id перехватывается, и некоторые поля перехватываются для формирования ip-адреса и mac-адреса (Обратите внимание, что здесь могут быть вероятностные конфликты ip, которые следует оптимизировать позже).
Вторая часть, создающаяLinux vethПравильно (Veth появляется парами на виртуальных сетевых устройствах, и запрос на отправку виртуального устройства Veth будет отправлен с виртуального устройства на другом конце. В сценарии контейнерной виртуализации Veth часто используется для подключения к разным пространствам имен), используйте команда ip Создать пару veth0_xx, veth1_xx, создать уникальныйuuid namespace, привяжите veth1 к пространству имен, чтобысвяжи меняp, mac адрес, затем привязать маршрут, запустить сетевую карту, сетевой интерфейс, пара veth, используемая здесь, вы можете просто понять это как подключение к сетевому кабелю и проиллюстрировать это.
Тогда этот проводОба конца здесь являются устройствами в пространстве имен,Другой конец - хост, Давайте проанализируем структурную схему здесь.Вы можете видеть, что у докера есть eth0, а у хоста есть veth.Они являются парой veth.
Это позволит бокеру в контейнере нормально выходить в интернет.
- Ограничения ресурсов bocker run (Часть 2)
# @3 更改nameserver, 保存cmd
echo 'nameserver 8.8.8.8' > "$btrfs_path/$uuid"/etc/resolv.conf
echo "$cmd" > "$btrfs_path/$uuid/$uuid.cmd"
# @4 通过cgroup-tools工具配置cgroup资源组与调整资源限制
cgcreate -g "$cgroups:/$uuid"
: "${BOCKER_CPU_SHARE:=512}" && cgset -r cpu.shares="$BOCKER_CPU_SHARE" "$uuid"
: "${BOCKER_MEM_LIMIT:=512}" && cgset -r memory.limit_in_bytes="$((BOCKER_MEM_LIMIT * 1000000))" "$uuid"
# @5 执行
cgexec -g "$cgroups:$uuid" \
ip netns exec netns_"$uuid" \
unshare -fmuip --mount-proc \
chroot "$btrfs_path/$uuid" \
/bin/sh -c "/bin/mount -t proc proc /proc && $cmd" \
2>&1 | tee "$btrfs_path/$uuid/$uuid.log" || true
ip link del dev veth0_"$uuid"
ip netns del netns_"$uuid"
Здесь для удобства работы мы используемcgroupинструменты для ограничения ресурсов,cgroupЭто инструмент ограничения ресурсов процесса, который поставляется с linux, соответствующие подробности есть в ссылке. используется здесьcgroup-toolsИнструментальная операция cgroup будет относительно простой.Здесь cgcreate используется для увеличения CPU, set и mem для лимита, а группа cgroup создается случайно созданной библиотекой id.package)
Как видно из рисунка ниже, данные, соответствующие контрольным группам, хранятся в файлах и хранятся в каталогах.
Наконец, используйте cgroup exec для запуска исполнителя запуска и вывода вывода в каталог журнала через tee.
Когда выполнение программы завершится, удалите соответствующий сетевой интерфейс и пространство имен, и сделайте так, чтобы сетевой интерфейс был для удобства привязки к хосту.удаление виртуальной сетевой карты
Здесь можно добиться бокерского пробега, ниже приведены некоторые детали.
- чистый сетевой интерфейс
function bocker_cleanup() { #HELP Delete leftovers of improperly shutdown containers:\nBOCKER cleanup
# @1 清楚所有的相关网络接口
for ns in $(ip netns show | grep netns_ps_); do [[ ! -d "$btrfs_path/${ns#netns_}" ]] && ip netns del "$ns"; done
for iface in $(ifconfig | grep veth0_ps_ | awk '{ print $1 }'); do [[ ! -d "$btrfs_path/${iface#veth0_}" ]] && ip link del dev "$iface"; done
}
ps извлеките соответствующую сетевую карту и удалите соответствующий сетевой интерфейс
- Просмотр журналов контейнеров
function bocker_logs() { #HELP View logs from a container:\nBOCKER logs <container_id>
# @1 查看日志
[[ "$(bocker_check "$1")" == 1 ]] && echo "No container named '$1' exists" && exit 1
cat "$btrfs_path/$1/$1.log"
}
Все журналы сохраняются в подкаталоге, соответствующем файловой системе BTRFS.$btrfs_path/$uuid, это соответствует btrfs_path, так что вам нужно только получить правильный каталог и указать файл.
Есть еще несколько простых команд, которые я не буду разбирать, они относительно просты, вы можете сами перейти по ссылке, указанной в начале, и скачать исходный код, соответствующий изменениям кода в моей статье.
Суммировать:
В целом, этот проект использует шелл и реализует небольшую часть основных функций докера, фреймворк есть, а 99% функций не реализованы, такие как межхостовое взаимодействие, переадресация портов, сопоставление портов, исключение обработка и т. д. Подождите, но как учебный проект, он может заставить людей блестеть глаза.Также вы можете реализовать простой докер по идеям этого проекта, я думаю, это не будет сложно.