Pull to refresh

Простое развёртывание сетевой лабы на базе контейнеров

Reading time7 min
Views12K

Вступление

Часто сетевая лаба представляет собой довольно сложную конструкцию, состоящую из множества устройств, соединенных между собой. В этом помогает виртуализация, благо всякие маршрутизаторы и т.п. могут запускаться в виде контейнеров (скажем, Quagga) или виртуальных машин (здесь я рассмотрю Mikrotik).

Тем не менее развернуть лабу из нескольких устройств с множеством сетевых интерфейсов и подключений, к примеру, для проверки BGP, в том числе между устройствами разных производителей, задача нетривиальная.  Можно, конечно, писать плейбуки Ansible для развертывания виртуалок на ESXi и последующей конфигурации устройств, но это само по себе достойная задача. Может быть что-то с vagrant изобразить…

Но недавно я узнал о проекте ContainerLab.dev и весьма впечатлился. Попробовал, оценил, впечатлился еще больше. Полез на Хабр почитать тонкости и хитрости, но, к удивлению, не нашел ни единого поста на эту тему.

Решил исправить. Вдруг кому-то еще облегчит жизнь.

ContainerLab.dev

Как пишут создатели, Containerlab предоставляет простой интерфейс командной строки для оркестрации и управления сетевыми лабами на основе контейнеров. Запускает, строит между ними виртуальные линки. Причем все это богатство описывается в простом и понятном YAML файле.

«Containerlab provides a CLI for orchestrating and managing container-based networking labs. It starts the containers, builds a virtual wiring between them to create lab topologies of users choice and manages labs lifecycle».

Мой пример файла:

name: lab
prefix: ""

mgmt:
  network: statics
  ipv4_subnet: 172.20.20.0/24

topology:
  kinds:
    vr-ros:
    # https://hub.docker.com/r/iparchitechs/chr
      image: iparchitechs/chr:long-term # :7.5beta8 :long-term

  nodes:
    ubuntu:
      kind: linux
      image: ubuntu:latest
      memory: 256MB
      cpu: 0.5
      mgmt_ipv4: 172.20.20.21
      binds:
        - setup_ubuntu.sh:/tmp/setup.sh
      exec:
        - bash /tmp/setup.sh
        - ip addr add dev eth1 10.2.2.21/24
        - ip route add 10.2.0.0/16 via 10.2.2.1

    alpine1:
      kind: linux
      image: alpine:latest
      memory: 64MB
      cpu: 0.5
      mgmt_ipv4: 172.20.20.31
      binds:
        - setup_alpine.sh:/tmp/setup.sh
      exec:
        - sh /tmp/setup.sh
        - ip addr add dev eth1 10.2.3.31/24
        - ip route add 10.2.0.0/16 via 10.2.3.1

    ros1:
      kind: vr-ros
      mgmt_ipv4: 172.20.20.11
      memory: 512MB
      cpu: 1
      ports:
        - 10122:22
        - 10180:80
#      startup-config: ros1.cfg
      binds:
        - setup_mikrotik.sh:/tmp/setup.sh
      exec:
        - sh /tmp/setup.sh 192.168.122.1 10.2.2 10.2.3

  links:
    - endpoints: ["ubuntu:eth1","ros1:eth1"]
    - endpoints: ["ros1:eth2","alpine1:eth1"]

Фактически задействованные устройства описываются в секции nodes, а связи между ними в links. Минимальное описание:

    ubuntu:
      kind: linux
      image: ubuntu:latest

или

    ros1:
      kind: vr-ros
      image: iparchitechs/chr:long-term # :7.5beta8 :long-term

А как задаются связи между ними и так понятно.

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

На выходе получили три контейнера. Посмотреть их IP также можно с помощью команды containerlab inspect.

Для удобства я создал alias clab='sudo containerlab', так что на скриншотах будет clab.

С помощью clab graph можно построить топологию.  При этом поднимется веб-сервер. Скриншт ниже именно с него, я только IP адреса добавил для понятности.

(Нюанс: при использовании бриджа топология рисуется кривовато, не знаю пока, что с этим делать)

Теперь можно подключаться к развернутым устройствам (docker exec, ssh…) и проверять, что душе угодно.

Подробности

Сеть

Коли вы заинтересовались описанным, подробнее освещу важные моменты.

Развертывается элементарно (bash -c "$(curl -sL https://get.containerlab.dev)").

IP адреса для контейнеров можно не указывать, получат автоматическию. Хотя я предпочитаю  указывать явно (mgmt_ipv4, mgmt_ipv6). Из какой подсети выдавать адреса настраивается: ipv4_subnet.

Это речь об интерфейсе управления – eth0. По этому адресу вы можете обращаться к своим виртуальным устройствам. Через него же они могут выходить в интернет, получать апдейты…

Но для данных вы создадите дополнительные интерфейсы. И уже их будете соединять друг с другом. В моем случае

  links:
    - endpoints: ["ubuntu:eth1","ros1:eth1"]
    - endpoints: ["ros1:eth2","alpine1:eth1"]

Т.е. у Ubuntu помимо интерфейса управления (eth0) будет eth1, подключенный к Mikrotik (eth1). Другой интерфейс  Микротика (eth2) будет подключен к alpine (eth1). IP адреса для этих интерфейсов (eth1 и т.п.) задаются внутри виртуализированного устройства.

Подключить к одному интерфейсу (ros1:eth2) несколько контейнеров нельзя. Но можно создать бридж (сначала на хосте обычным образом, потом в YAML файле). Пример с дополнительным контейнером alpine2 также за eth2 Микротика есть в репозитории (ros-br.clab.yml).

Фактически описываем дополнительное устройство (бридж) br-clab и связываем его:

    br-clab:
      kind: bridge
  links:
    - endpoints: ["ubuntu:eth1","ros1:eth1"]
    - endpoints: ["ros1:eth2","br-clab:eth1"]
    - endpoints: ["alpine1:eth1","br-clab:eth2"]
    - endpoints: ["alpine2:eth1","br-clab:eth3"]

Виртуальные машины

С Ubuntu все понятно. Берешь контейнер и наслаждаешься. В том числе есть контейнеризиванные маршрутизаторы и т.п. Но что, если хочется поиграть виртуальной копией аппаратного устройства, для которого нет контейнера, но есть виртуальная машина?

К счастью, имеются проекты, позволяющие «обернуть» виртуалку так, чтобы сделать ее совместимой по сетевой части со средой Containerlab.

Здесь я в качестве примера взял Mikrotik. Другие готовые решения можно посмотреть в разделе kinds. Juniper, Cisco…

Микротик был адаптирован с помощью проекта vrnetlab. Для недавно добавленного Check Point CloudGuard использовался более новый boxen. Если будет интерес, могу отдельно описать, как из скаченного с официального сайта qcow2 (образа для KVM) собрать контейнер.

Сравните описания:

https://containerlab.dev/manual/kinds/linux/ позволяет использовать ваш любимый образ. Я взял Ubuntu и Alpine с традиционного Docker Hub.

https://containerlab.dev/manual/kinds/vr-ros/: MikroTik RouterOS cloud hosted router is identified with vr-ros or vr-mikrotik_ros kind in the topology file. It is built using vrnetlab project and essentially is a Qemu VM packaged in a docker container format.

https://containerlab.dev/manual/kinds/checkpoint_cloudguard/: Check Point Cloudguard virtualized security appliance is identified with checkpoint_cloudguard kind in the topology file. It is built using boxen project and essentially is a Qemu VM packaged in a docker container format.

Понимание отличия «настоящего» контейнера от «не настоящего» очень важно. Команда docker exec для настоящего контейнера работает привычным образом. А вот в случае «не настоящего» вы подключитесь в «контейнер-обертку», а не в свою виртуальную машину с желаемым устройством. Я тут поначалу оторопел, почему не та ОС и куда делись ожидаемые команды.

Я в «обертке» (обычно это не требуется): docker exec --it ros1 bash

А вот ssh казалось бы на IP контейнера пробросит действительно внутрь искомой виртуальной машины: ssh admin@ros1

Магия!

И обратите внимание на IP адрес интерфейса управления ether1: 172.31.255.30/30, хотя SSH мы делали на 172.20.20.11. Это тоже особенность «обертки», которую просто нужно знать (и не портить этот IP при настройке Микротика или другой системы).

Сразу приведу скриншоты, как все работает, как с Ubuntu я могу пингануть Alpine через Микротик (и обратно).

Понятно, что для этого внутри Ubuntu я прописал дополнительный статический маршрут в сторону 10.2.2.1 (mikrotik:eth1), а на alpine в сторону 10.2.3.1 (mikrotik:eth2).

Как задать IP для дополнительных интерфейсов, маршруты и прочие настройки? Да любым привычным способом. Хоть CLI, хоть Web-интерфейс. Хоть автоматизировать :)

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

Во-первых, поддерживаются традиционные для контейнеров параметры: image, memory, cpu… Доступны bind, exec. Я использую их для автоматизации первоначальной настройки.

В данном примере я монтирую в контейнер скрипты с «универсальными» настройками и запускаю их через exec. Также через exec я сразу же на этапе развертывания лабы задаю параметры, специфичные для конкретного контейнера (IP адреса, маршруты…)

Можно опубликовать порты. Скажем, для Микротика:

      ports:
        - 10122:22
        - 10180:80

Как в традиционном контейнерном мире: это позволит подключиться к основному хосту, на котором развернут Containerlab (http://CLab:10180) и увидеть web-интерфейс Микротика.

Хотя я предпочитаю прописать маршрут для сети управления (172.20.20.0 в моем случае) на CLab. Это позволит обращаться к Микротику напрямую, без публикации портов на хосте (http://172.20.20.11, как задано в YAML файле конфигурации лабы).

Настройки «не настоящих контейнеров»

Помните, docker exec пробрасывает не в виртуальную машину, а в «контейнер-обертку»? Так же и exec выполнит команды в контейнере vrnetlab или boxen, но не там, где хотелось бы. Так что придется действовать хитрее.

Итак,

    ros1:
      kind: vr-ros
      mgmt_ipv4: 172.20.20.11
#      startup-config: ros1.cfg
      binds:
        - setup_mikrotik.sh:/tmp/setup.sh
      exec:
        - sh /tmp/setup.sh 192.168.122.1 10.2.2 10.2.3

По идее Микротику можно «скормить» файл конфигурации, но я опишу более универсальный подход (строчка startup-config закомментирована).

Фактически я выполняю скрипт setup_mikrotik.sh в «обертке», и изнутри этого контейнера делаю ssh внутрь виртуальной машины с Микротиком.

#!/bin/bash
intIP=172.31.255.30

wait_ssh() {
    printf "Waiting for ssh: "
    SSH_UP=0
    while [ $SSH_UP -eq 0 ]
    do
        printf "."
        SSH_UP=$(wget --timeout=1 --tries=1 $intIP:22 2>&1 | grep -c Read);
    done
    printf " SSH UP"
}

apt-get update > /dev/null
apt-get install -y sshpass > /dev/null

wait_ssh

cat <<EOF | sshpass -p admin ssh -tt -o StrictHostKeyChecking=no admin@$intIP
/ip dns set servers=$1;
/ip address add address=$2.1/24 interface=ether2 network=$2.0;
/ip address add address=$3.1/24 interface=ether3 network=$3.0;
/ip route add distance=1 gateway=172.31.255.29;
EOF

Через ssh я выполню ряд команд Микротика (/ip dns set и т.п.). В качестве параметров передаю ему индивидуальные настройки (IP, DNS…). Для подстановки пароля я предварительно устанавливаю sshpass. (expect в «обертке» ожидаемо отсутствует). Потом можно и ключ ssh загрузить, но на первом этапе так.

Еще из интересного – после того, как контейнер стартовал, это не значит, что виртуальная машина с Микротиком сразу готова к работе, ей нужно немного времени. Поэтому я написал функцию wait_ssh, ожидающую готовности SSH в виртуальной машине. Проверку делаю через wget, поскольку он уже установлен. Во избежание зависания, я ограничиваю timeout. Ничего толкового я не получу, код возврата будет «Ошибка» в любом случае. Но с помощью grep я отличить Read в случае успешного подключения от полной ошибки подключения (не готовности виртуальной машины).

Заключение

Разумеется, так можно создавать весьма сложные топологии (скажем, много интерфейсов, BGP, VPN, переключения на резервные линки…).

Для Check Point CloudGuard вы получите традиционное:

В данном случае у меня Standalone конфигурация (Management + Gateway) и дополнительный шлюз.

Было бы очень интересно узнать, знакомы ли вы с ContainerLab, представляет ли это для вас интерес.

Упомянутые в статье и другие файлы можно найти на GitHub.

Tags:
Hubs:
Total votes 11: ↑11 and ↓0+11
Comments17

Articles