- PVSM.RU - https://www.pvsm.ru -

Время на месте не стоит, и у горячо любимого всеми Docker от версии к версии появляется новый функционал. Случается так, что когда читаешь Changelog для новой версии, видишь там то, что может пригодиться и сделать какие-то вещи лучше, чем есть на данный момент.
Так дело обстояло и в моем случае. Хочу заметить, что многие задачи, которые приходится делать, я делаю по принципу keep it simple. То есть почти всегда, если для решения задачи можно использовать простые инструменты и шаги, я выберу этот путь. Я понимаю, что простой или сложный шаг или инструмент — оценка субъективная, но т.к. работаем мы в команде, то вот такие критерии могут подходить при выборе инструментов:
В этой статье речь пойдет о сетевом аспекте Docker. Расскажу обо всем по порядку, но хочу заметить, что на этот раз я не буду говорить «мы используем сеть хоста, всячески избегая применения NAT».
О том, как Docker работает с сетью, можно ознакомиться по ссылке [1]. Выделим основные моменты:
Как я говорил ранее в некоторых своих публичных выступлениях, нам нужна максимальная производительность сети для наших контейнеров. Если говорить о production, то NAT для контейнеров мы не используем.
Долгое время (да чего уж скрывать — и по сей день) для запуска контейнеров мы используем параметр --net=host, получая тем самым «нативный» eth внутри контейнера. Да, в этом случае одного бенефита — изоляции — мы, конечно, лишаемся… Но посмотрев на плюсы и минусы в нашем конкретном случае, мы намеренно пришли к такому решению, т.к. задачи изолировать сеть между запущенными приложениями в рамках одного хоста не стояло. Хочу напомнить, что пишу я о конкретном месте применения Docker — в Badoo.
Что мы знаем про наши сервисы:
Исходя из вышесказанного, мы гарантируем следующее:
Все хорошо, тогда зачем мне пришлось что-то менять?
Дело было вечером, делать было нечего… Ранее говорилось о том, что мы продолжаем переносить наши сервисы в контейнеры. При следовании такому сценарию, как это обычно бывает, самые сложные остаются на потом. Причин для этого может быть масса:
Ну так вот. Был (и есть) у нас один такой сервис, который давно написан. Он и по сей день отлично работает, но есть у него несколько минусов:
Вот так сервис запускался ДО:
Как можно было решить задачу, если бы было лень изобретать новые методы:
Как к задаче решил подойти я? Изначально, конечно, это все выглядело как эксперимент, да чего скрывать — это и было экспериментом. Мне показалось, что для этого сервиса как нельзя кстати подойдет MACVLAN-технология, которая на тот момент была отмечена в Docker как Experimental (версия 1.11.2), а вот в версии 1.12 всё уже доступно в основном функционале.
MACVLAN — это, по сути, Linux switch, который основан на статичном соответствии MAC и VLAN. Здесь используется unicast-фильтрация, не promiscuous-режим. MACVLAN может работать в режиме private, VEPA, bridge, passthru. MACVLAN — это reverse VLAN в Linux. Данная технология позволяет взять один реальный интерфейс и сделать на его основе несколько виртуальных с разными MAC-адресами.
Также сравнительно недавно появилась технология IPVLAN(https://www.kernel.org/doc/Documentation/networking/ipvlan.txt [2]). Основное отличие от MACVLAN заключается в том, что IPVLAN может работать в L3 mode. В данной статье я буду рассматривать вариант использования MACVLAN (в режиме bridge), потому что:
Чуть подробнее про MACVLAN vs IPVLAN можно ознакомиться по ссылке http://hicu.be/macvlan-vs-ipvlan [3].
Вот здесь можно почитать теорию и как это работает в Docker: https://github.com/docker/docker/blob/master/experimental/vlan-networks.md [4].
Теория — это отлично, но даже там мы видим, что overhead имеет место быть. Можно и нужно посмотреть на сравнительные тесты пропускной способности MACVLAN в интернете (например, http://comp.photo777.org/docker-network-performance/ [5] и http://delaat.net/rp/2014-2015/p92/report.pdf [6]), но также неотъемлемой частью эксперимента является проведение теста в своих лабораторных условиях. Поверить на слово — хорошо, но «потрогать руками» и сделать выводы самому — интересно и необходимо.
Для того чтобы проверить, работает ли MACVLAN в Docker, нам нужно включить в последнем поддержку экспериментальных функций.
Если данный функционал при сборке не включен, то в логах можно будет видеть вот такие сообщения об ошибках:
# docker network create -d macvlan --subnet=1.1.1.0/24 --gateway=1.1.1.1 -o parent=eth0 cppbig_vlan
Error response from daemon: plugin not found
А в логах процесса будет следующее:
docker[2012]: time="2016-08-04T11:44:44.095241242Z" level=warning msg="Unable to locate plugin: macvlan, retrying in 1s"
docker[2012]: time="2016-08-04T11:44:45.095489283Z" level=warning msg="Unable to locate plugin: macvlan, retrying in 2s"
docker[2012]: time="2016-08-04T11:44:47.095750785Z" level=warning msg="Unable to locate plugin: macvlan, retrying in 4s"
docker[2012]: time="2016-08-04T11:44:51.095970433Z" level=warning msg="Unable to locate plugin: macvlan, retrying in 8s"
docker[2012]: time="2016-08-04T11:44:59.096197565Z" level=error msg="Handler for POST /v1.23/networks/create returned error: plugin not found"
Если вы видите такие сообщения — значит, поддержка MACVLAN в Docker не включена.
Тест был символическим, с использованием iperf. Для каждого варианта я запускал сначала 1 клиента, потом 8 в параллель. Вариантов было 2:
# docker run -it --net=host --name=iperf_w_host_net --entrypoint=/bin/bash dockerio.badoo.com/itops/sle_12_base:latest
# iperf3 -s -p 12345
-----------------------------------------------------------
Server listening on 12345
-----------------------------------------------------------
Запускаем клиента:
# iperf3 -c 1.1.1.2 -p 12345 -t 30
На сервере получаем результат:
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-30.04 sec 0.00 Bytes 0.00 bits/sec sender
[ 5] 0.00-30.04 sec 2.45 GBytes 702 Mbits/sec receiver
На клиенте:
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-30.00 sec 2.46 GBytes 703 Mbits/sec 0 sender
[ 4] 0.00-30.00 sec 2.45 GBytes 703 Mbits/sec receiver
Запускаем в параллель 8 клиентов:
# iperf3 -c 1.1.1.2 -p 12345 -t 30 -P 8
На сервере получаем результат:
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 5] 0.00-30.03 sec 314 MBytes 87.7 Mbits/sec receiver
[ 7] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 7] 0.00-30.03 sec 328 MBytes 91.5 Mbits/sec receiver
[ 9] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 9] 0.00-30.03 sec 305 MBytes 85.2 Mbits/sec receiver
[ 11] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 11] 0.00-30.03 sec 312 MBytes 87.3 Mbits/sec receiver
[ 13] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 13] 0.00-30.03 sec 316 MBytes 88.3 Mbits/sec receiver
[ 15] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 15] 0.00-30.03 sec 310 MBytes 86.7 Mbits/sec receiver
[ 17] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 17] 0.00-30.03 sec 313 MBytes 87.5 Mbits/sec receiver
[ 19] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 19] 0.00-30.03 sec 321 MBytes 89.7 Mbits/sec receiver
[SUM] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[SUM] 0.00-30.03 sec 2.46 GBytes 704 Mbits/sec receiver
На клиенте:
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-30.00 sec 315 MBytes 88.1 Mbits/sec 0 sender
[ 4] 0.00-30.00 sec 314 MBytes 87.8 Mbits/sec receiver
[ 6] 0.00-30.00 sec 330 MBytes 92.3 Mbits/sec 0 sender
[ 6] 0.00-30.00 sec 328 MBytes 91.6 Mbits/sec receiver
[ 8] 0.00-30.00 sec 306 MBytes 85.6 Mbits/sec 0 sender
[ 8] 0.00-30.00 sec 305 MBytes 85.3 Mbits/sec receiver
[ 10] 0.00-30.00 sec 313 MBytes 87.5 Mbits/sec 0 sender
[ 10] 0.00-30.00 sec 312 MBytes 87.4 Mbits/sec receiver
[ 12] 0.00-30.00 sec 317 MBytes 88.8 Mbits/sec 0 sender
[ 12] 0.00-30.00 sec 316 MBytes 88.4 Mbits/sec receiver
[ 14] 0.00-30.00 sec 312 MBytes 87.1 Mbits/sec 0 sender
[ 14] 0.00-30.00 sec 310 MBytes 86.8 Mbits/sec receiver
[ 16] 0.00-30.00 sec 314 MBytes 87.9 Mbits/sec 0 sender
[ 16] 0.00-30.00 sec 313 MBytes 87.6 Mbits/sec receiver
[ 18] 0.00-30.00 sec 322 MBytes 90.2 Mbits/sec 0 sender
[ 18] 0.00-30.00 sec 321 MBytes 89.8 Mbits/sec receiver
[SUM] 0.00-30.00 sec 2.47 GBytes 707 Mbits/sec 0 sender
[SUM] 0.00-30.00 sec 2.46 GBytes 705 Mbits/sec receiver
2. Запускам сервер, используя MACVLAN:
# docker run -it --net=cppbig_vlan --name=iperf_w_macvlan_net --ip=1.1.1.202 --entrypoint=/bin/bash dockerio.badoo.com/itops/sle_12_base:latest
# iperf3 -s -p 12345
-----------------------------------------------------------
Server listening on 12345
-----------------------------------------------------------
Запускаем клиента:
# iperf3 -c 1.1.1.202 -p 12345 -t 30
На сервере получаем результат:
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-30.04 sec 0.00 Bytes 0.00 bits/sec sender
[ 5] 0.00-30.04 sec 2.45 GBytes 701 Mbits/sec receiver
На клиенте:
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-30.00 sec 2.46 GBytes 703 Mbits/sec 0 sender
[ 4] 0.00-30.00 sec 2.45 GBytes 702 Mbits/sec receiver
Запускаем в параллель 8 клиентов:
# iperf3 -c 1.1.1.202 -p 12345 -t 30 -P 8
На сервере получаем результат:
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 5] 0.00-30.03 sec 306 MBytes 85.4 Mbits/sec receiver
[ 7] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 7] 0.00-30.03 sec 319 MBytes 89.1 Mbits/sec receiver
[ 9] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 9] 0.00-30.03 sec 307 MBytes 85.8 Mbits/sec receiver
[ 11] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 11] 0.00-30.03 sec 311 MBytes 87.0 Mbits/sec receiver
[ 13] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 13] 0.00-30.03 sec 317 MBytes 88.6 Mbits/sec receiver
[ 15] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 15] 0.00-30.03 sec 322 MBytes 90.1 Mbits/sec receiver
[ 17] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 17] 0.00-30.03 sec 313 MBytes 87.5 Mbits/sec receiver
[ 19] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[ 19] 0.00-30.03 sec 310 MBytes 86.7 Mbits/sec receiver
[SUM] 0.00-30.03 sec 0.00 Bytes 0.00 bits/sec sender
[SUM] 0.00-30.03 sec 2.45 GBytes 700 Mbits/sec receiver
На клиенте:
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Retr
[ 4] 0.00-30.00 sec 307 MBytes 85.8 Mbits/sec 0 sender
[ 4] 0.00-30.00 sec 306 MBytes 85.5 Mbits/sec receiver
[ 6] 0.00-30.00 sec 320 MBytes 89.6 Mbits/sec 0 sender
[ 6] 0.00-30.00 sec 319 MBytes 89.2 Mbits/sec receiver
[ 8] 0.00-30.00 sec 308 MBytes 86.2 Mbits/sec 0 sender
[ 8] 0.00-30.00 sec 307 MBytes 85.9 Mbits/sec receiver
[ 10] 0.00-30.00 sec 313 MBytes 87.5 Mbits/sec 0 sender
[ 10] 0.00-30.00 sec 311 MBytes 87.1 Mbits/sec receiver
[ 12] 0.00-30.00 sec 318 MBytes 89.0 Mbits/sec 0 sender
[ 12] 0.00-30.00 sec 317 MBytes 88.6 Mbits/sec receiver
[ 14] 0.00-30.00 sec 324 MBytes 90.5 Mbits/sec 0 sender
[ 14] 0.00-30.00 sec 322 MBytes 90.2 Mbits/sec receiver
[ 16] 0.00-30.00 sec 314 MBytes 87.9 Mbits/sec 0 sender
[ 16] 0.00-30.00 sec 313 MBytes 87.6 Mbits/sec receiver
[ 18] 0.00-30.00 sec 311 MBytes 87.1 Mbits/sec 0 sender
[ 18] 0.00-30.00 sec 310 MBytes 86.8 Mbits/sec receiver
[SUM] 0.00-30.00 sec 2.46 GBytes 704 Mbits/sec 0 sender
[SUM] 0.00-30.00 sec 2.45 GBytes 701 Mbits/sec receiver
Как видно из результатов, overhead есть, но в данном случае можно считать его не критичным.
Ограничения технологии by design: доступность контейнера с хоста и доступность хоста из контейнера отсутствует. Нам такой функционал необходим, потому что:
Варианты преодоления данного ограничения:
В данном случае получается, что нам нужно слишком много IP-адресов, которые, по большому счету, использоваться будут мало. Помимо излишнего расходования IP-адресов стоит также помнить, что нам нужно будет поддерживать статичные маршруты через этот самый виртуальный интерфейс до контейнера. Это не непреодолимая сложность, но усложнение системы в целом — факт.
Внимательный читатель задаст вопрос: «А зачем адрес на основной интерфейс и на MACVLAN-интерфейс, если можно адрес основного интерфейса отдать виртуальному?» В таком случае мы оставим нашу систему без адресов на реальных интерфейсах, а на такой шаг я пойти пока не готов.
В предыдущих двух вариантах предполагалось, что адреса всех интерфейсов принадлежат одной сети. Как несложно представить, даже при 100 серверах в такой подсети, если завести по три адреса, то в /24 мы уже не попадаем.
Рассматривать, как уже стало понятно, будем пункт три. Чтобы все у нас заработало, нужно проделать несколько действий:
# cat /etc/sysconfig/network/ifcfg-vlan8
BOOTPROTO='static'
STARTMODE='auto'
VLAN='yes'
ETHERDEVICE='eth0'
# ip -d link show vlan8
31: vlan8@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether e4:11:5b:ea:b6:30 brd ff:ff:ff:ff:ff:ff promiscuity 1
vlan protocol 802.1Q id 8 <REORDER_HDR>
# docker network create -d macvlan --subnet=1.1.2.0/24 --gateway=1.1.2.1 -o parent=vlan8 c_services
# docker network ls | grep c_services
a791089219e0 c_services macvlan
Все сделал, все хорошо. Тут я решил посмотреть на общие графики по хосту (а если быть точнее, то на это обратил мое внимание коллега). Вот такую картину мы там увидели:

Да, здесь видно использование conntrack на хосте.
Как так? Ну не нужен же conntrack для МACVLAN?! Так как дело было уже вечером, я решил проверить даже самые невероятные теории. В подтверждение моих теоретических знаний, connection tracking был на самом деле не нужен. Без него все продолжало работать. Выгрузка модулей, так или иначе завязанных на conntrack, была невозможной только в момент запуска моего контейнера. Идеи меня покинули, и пошел я домой, решив, что утро вечера мудренее.
На следующий день я в очередной раз убедился в точности данного высказывания. Итак, я решил «топорным» методом сделать так, чтобы Docker не мог подгружать nf_conntrack. Сначала я его просто переименовал (т.к. blacklist игнорируется при загрузке модуля через modprobe), после чего запустил свой контейнер снова. Контейнер, как и ожидалось, поднимался и отлично себя чувствовал, но в логе я увидел сообщения о том, что четыре правила не могут быть добавлены в iptables. Получается, что conntrack нужен? Вот правила, которые не хотели добавляться:
-t nat -A OUTPUT -d 127.0.0.11 -p udp --dport 53 -j DNAT --to-destination 127.0.0.11:35373
-t nat -A POSTROUTING -s 127.0.0.11 -p udp --sport 35373 -j SNAT --to-source :53
-t nat -A OUTPUT -d 127.0.0.11 -p tcp --dport 53 -j DNAT --to-destination 127.0.0.11:41214
-t nat -A POSTROUTING -s 127.0.0.11 -p tcp --sport 41214 -j SNAT --to-source :53
53 порт? Налицо работа, связанная с «резолвером». И тут я, к своему удивлению, узнаю про embedded DNS server. Ну хорошо, пусть и встроенный, но можно же как-то опциями выключить его? Нет, нельзя :)
Далее я попробовал вернуть модуль, запустить сервис, поправить правила из iptables и выгрузить модули… Но не тут то было. Путем ковыряния modinfo я выяснил, какой там модуль от какого зависит, и какой из них кого тянет за собой. При создании сети Docker принудительно делает modprobe xt_nat, который, в свою очередь, зависит от nf_conntrack, вот тому подтверждение:
# modinfo xt_nat
filename: /lib/modules/4.4.0-3.1-default/kernel/net/netfilter/xt_nat.ko
alias: ip6t_DNAT
alias: ip6t_SNAT
alias: ipt_DNAT
alias: ipt_SNAT
author: Patrick McHardy <kaber@trash.net>
license: GPL
srcversion: 9982FF46CE7467C8F2361B5
depends: x_tables,nf_nat
intree: Y
vermagic: 4.4.0-3.1-default SMP preempt mod_unload modversions
Как я уже говорил, все работает и без этих модулей. Соответственно, мы можем сделать вывод, что в нашем случае они не нужны. Остается вопрос: зачем, все же, они нужны? Я не поленился и заглянул в 2 места:
И что я там нашел? Верно: для любого user defined network Docker делает modprobe. Смотрим код и видим 2 интересных для нас пункта:
if out, err := exec.Command("modprobe", "-va", "nf_nat").CombinedOutput(); err != nil {
logrus.Warnf("Running modprobe nf_nat failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
}
if out, err := exec.Command("modprobe", "-va", "xt_conntrack").CombinedOutput(); err != nil {
logrus.Warnf("Running modprobe xt_conntrack failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
}
И вот такое еще:
if err := r.setupIPTable(); err != nil {
return fmt.Errorf("setting up IP table rules failed: %v", err)
}
Делаем патч, а точнее — выкидываем все ненужное :) Делаем новую сборку Docker.
Смотрим. Все ок, все работает.
На этом этапе можно считать, что вся наша схема в лабораторных условиях работает, осталось сделать самую малость — прицепить ее к нашему сервису. Хорошо, возвращаемся к сервису и смотрим на его общую архитектуру:

Пояснения о том, как оно работает:
В первом приближении нам ничто не мешает собрать образ с нашим сервисом и запустить его уже в контейнере, но есть одно НО. Так уж сложилось, что для тех сервисов, которым нужно взаимодействие с внешним балансировщиком, у нас присутствует определенный набор статических маршрутов, например, вот такой:
# ip r
default via 1.1.2.254 dev eth0
10.0.0.0/8 via 1.1.2.1 dev eth0
1.1.2.0/24 dev eth0 proto kernel scope link src 1.1.2.14
192.168.0.0/16 via 1.1.2.1 dev eth0
Т.е. все, что должно идти в или из наших внутренних сетей, идет через .1, а остальное — через .254.
Почему это проблема в нашем случае? Потому что при запуске контейнера в его маршрутах мы видим следующее:
# ip r
default via 1.1.2.1 dev eth0
1.1.2.0/24 dev eth0 proto kernel scope link src 1.1.2.14
Попытка поменять маршруты внутри контейнера ни к чему не приведет, т.к. он у нас не привилегированный (--priveleged). Остается менять маршруты руками после старта контейнера с хоста (тут кроется большое заблуждение, но про это — дальше). Здесь варианта два:
Скажу сразу, что с этим можно жить, но существуют опасности, как у студента: «можно забыть, забить или запить» :)
Стремясь к идеалу, мы сделали для этой сервисной сети все маршруты через default gw, а всю сложность маршрутизации переложили на сетевой отдел. Всё. Точнее, я думал, что всё…
Как мне показалось на тот момент — решение отличное. Если бы все работало именно так, как я рассчитывал, так бы оно и было, но на этом ничего не закончилось. Чуть позже стало понятно, что при такой схеме мы получаем асимметричный роутинг для тех сетей, в которых есть маршрут через наш LTM. Чтобы стало чуть более понятно, я попробую показать, какие подсети у нас могут быть.


Поговорив с сетевиками, мы сделали следующие выводы:
Получилось так, что при желании сделать что-то простое, мы получили проблему на ровном месте, и если бы не подумали сразу о возможных сложностях, это привело бы к довольно грустным последствиям.
Я всегда говорю, что не стоит забывать про идеи, которые приходили в голову раньше, но были отвергнуты. Мы вернулись к мысли использовать статические маршруты внутри контейнера.
Итак, вот те условия, которые гарантируют нам работу нашего сервиса в контейнере:
Запускать контейнеры в привилегированном режиме (--privileged) не хотелось и не хочется. Я изначально не подумал про Linux capabilities, которые можно добавлять и убирать при запуске контейнера. Подробнее про них можно почитать вот тут [8]. Для нашей задачи было достаточно добавить NET_ADMIN.
Вот теперь картинка сложилась, и мы можем добавить все нужные нам штуки, связанные с маршрутизацией, в автозапуск.
Давайте посмотрим, как выглядит наш Dockerfile в близком к финальному результату.
Dockerfile:
FROM dockerio.badoo.com/itops/sle_12_base:latest
MAINTAINER #MAINTEINER#
RUN /usr/bin/zypper -q -n in iproute2
RUN groupadd -g 1001 wwwaccess
RUN mkdir -p /local/SERVICE/{var,conf}
COPY get_configs.sh /local/SERVICE/
COPY config.cfg /local/SERVICE/
ADD SERVICE-CERTS/ /local/SERVICE-CERTS/
ADD SERVICE/bin/SERVICE-BINARY-${DVERSION} /local/SERVICE/bin/
ADD SERVICE/conf/ /local/SERVICE/conf/
COPY routes.sh /etc/cont-init.d/00-routes.sh
COPY env.sh /etc/cont-init.d/01-env.sh
COPY finish.sh /etc/cont-finish.d/00-finish.sh
COPY run /etc/services.d/SERVICE/
COPY finish /etc/services.d/SERVICE/
RUN touch /tmp/fresh_container
ENTRYPOINT ["/init"]
На что тут стоит обратить внимание:
Используемые скрипты:
#!/usr/bin/with-contenv sh
if [ ! -x /usr/sbin/ip ];then
echo -e "e[31mCan't execute /usr/sbin/ipe[0m";
[ $(pgrep s6-svscan) ] && s6-svscanctl -t /var/run/s6/services
exit 1;
else
LTMGW=$(/usr/sbin/ip r show | /usr/bin/grep default | /usr/bin/awk {'print $3'} | /usr/bin/awk -F . {'print $1"."$2"."$3".254"'})
DEFGW=$(/usr/sbin/ip r show | /usr/bin/grep default | /usr/bin/awk {'print $3'} | /usr/bin/awk -F . {'print $1"."$2"."$3".1"'})
/usr/sbin/ip r replace default via ${LTMGW}
/usr/sbin/ip r add 192.168.0.0/16 via 10.10.8.1 dev eth0
/usr/sbin/ip r add 10.0.0.0/8 via 10.10.8.1 dev eth0
echo -e "e[32mAll job with routes done:e[0mn$(/usr/sbin/ip r show)"
fi
#!/usr/bin/with-contenv sh
if [ ! -z "${ISTEST}" ];then exit 0;fi
if [ ! -n "${SERVICETYPE}" ];then
echo -e "e[31mPlease set SERVICE typee[0m";
[ $(pgrep s6-svscan) ] && s6-svscanctl -t /var/run/s6/services
exit 1;
fi
bash /local/SERVICE/get_configs.sh || exit 1
echo -e "e[32mSERVICE ${SERVICETYPE} is runninge[0m"
#!/usr/bin/with-contenv bash
exec /local/SERVICE/bin/SERVICE-${DVERSION} -l /local/SERVICE/var/mobile-${SERVICETYPE}.log -P /local/SERVICE/var/mobile-${SERVICETYPE}.pid -c /local/SERVICE/conf/SERVICE.conf -v ${VERBOSITY}
#!/bin/sh
[ $(pgrep s6-svscan) ] && s6-svscanctl -t /var/run/s6/services
Строка для запуска нашего сервиса будет выглядеть так:
docker run -d --net=c_services --ip=1.1.2.17 --name=SERVICE-INSTANCE16 -h SERVICE-INSTANCE16.local --cap-add=NET_ADMIN --add-host='nginx.localhost:1.1.1.17' -e SERVICETYPE=INSTANCE16_eu1 -e HOST_IP=1.1.1.17 --volumes-from=badoo_loop dockerio.badoo.com/cteam/SERVICE:2.30.0_994
На этом перенос нашего сервиса в контейнер можно считать успешным. Забегая вперед, хочу заметить, что MACVLAN/IPVLAN мы используем в других наших сервисах, но примером послужил именно этот эксперимент.
Антон banuchka [10] Турецкий
Site Reliability Engineer, Badoo
Автор: Badoo
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/linux/178246
Ссылки в тексте:
[1] ссылке: https://docs.docker.com/engine/userguide/networking/dockernetworks/
[2] https://www.kernel.org/doc/Documentation/networking/ipvlan.txt: https://www.kernel.org/doc/Documentation/networking/ipvlan.txt
[3] http://hicu.be/macvlan-vs-ipvlan: http://hicu.be/macvlan-vs-ipvlan
[4] https://github.com/docker/docker/blob/master/experimental/vlan-networks.md: https://github.com/docker/docker/blob/master/experimental/vlan-networks.md
[5] http://comp.photo777.org/docker-network-performance/: http://comp.photo777.org/docker-network-performance/
[6] http://delaat.net/rp/2014-2015/p92/report.pdf: http://delaat.net/rp/2014-2015/p92/report.pdf
[7] https://github.com/jpetazzo/pipework: https://github.com/jpetazzo/pipework
[8] тут: https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities
[9] Docker Meetup: https://tech.badoo.com/ru/presentation/193/monitor-avtomatiziruj-docker/
[10] banuchka: https://habrahabr.ru/users/banuchka/
[11] Источник: https://habrahabr.ru/post/308402/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.