LoginSignup
0
1

単一マシン内でネットワークテスト用の半仮想環境を用意する

Posted at

ネットワークテストをするためにはポートを分けることが一般的に行われますが、IPアドレス自体を分離したいこともあると思います。unshareを使えば比較的(?)容易に行えます。

worker

masterからNICを受け取るまで待ち、IPを設定してupするだけ。
ただしNIC名は15文字以下でないといけないらしい。

#!/usr/bin/python
from subprocess import call
from subprocess import check_call
from os.path import isdir
from time import sleep

check_call(['mount', '-t', 'sysfs', '/sys', '/sys'])
while not isdir('/sys/devices/virtual/net/wrk-container'):
    sleep(1)

try:
    check_call(['ip', 'link', 'set', 'lo', 'up'])
    check_call(['mount', '--rbind', '/tmp/wrk-container/log', '/var/log'])
    check_call(['mount', '--rbind', '/tmp/wrk-container/run', '/run'])
    # todo: add other binds if required
    check_call(['ip', 'addr', 'add', '172.127.0.2/24', 'dev', 'wrk-container'])
    check_call(['ip', 'link', 'set', 'wrk-container', 'up'])

    # todo: use proper daemon program such as https://pypi.org/project/supervisor/
    check_call(['python','-c','''
import signal
import sys
import socket
from time import sleep

def on_signal(signum, frame):
    print('[worker] signal %d' % signum)
    sys.exit(0)

signal.signal(signal.SIGINT, on_signal)
signal.signal(signal.SIGTERM, on_signal)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', 12345))
sock.listen(1)
sleep(9999)
    '''])  
finally:
    call(['umount','-R','/run'])
    call(['umount','-R','/var/log'])
    call(['umount','/sys'])

master

  • workerをunshareを用いて起動する
  • ip link add wrk-host type veth peer wrk-containerで仮想NICを作成する
  • ホスト側のIPを設定
  • ip link set wrk-container netns str(proc.pid)でワーカー端のNICをワーカーのnet namespace内に移動する
  • unshareをkillするだけだとworker daemonをkillできないようなので、worker daemonをプロセスツリーをたどってkillしています。詳細不明。
#!/usr/bin/python

from os import kill
from os.path import join
from os.path import dirname
from os.path import realpath
from shutil import rmtree
from shutil import copytree
from subprocess import check_call
from signal import SIGTERM
from time import sleep

try:
    from pathlib import Path
except ImportError:
    from pathlib2 import Path

from psutil import Process
from checkpopen import CheckPopen  # https://pypi.org/project/checkpopen

def main():
    with CheckPopen(['unshare','-m','-i','-n','-u','-p','--mount-proc','--kill-child=SIGTERM','--fork','--root=/',join(dirname(realpath(__file__)), 'worker.py')]) as proc:
        try:
            # rmtree('/tmp/worker-container/log', ignore_errors=True)
            Path('/tmp/wrk-container/log').mkdir(parents=True, exist_ok=True)
            rmtree('/tmp/worker-container/run', ignore_errors=True)
            Path('/tmp/wrk-container/run').mkdir(parents=True, exist_ok=True)
            # todo: transfer files to /tmp/wrk-container/foo
            check_call(['ip','link','add','wrk-host','type','veth','peer','wrk-container'])
            check_call(['ip','addr','add','172.127.0.1/24','dev','wrk-host'])
            check_call(['ip','link','set','up','wrk-host'])
            check_call(['ip','link','set','wrk-container','netns',str(proc.pid)])
            try:
                import socket
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.connect(('172.127.0.2', 12345))  # check connection
            finally:
                kill(Process(proc.pid).children()[0].children()[0].pid, SIGTERM)
                print('sent sigterm to worker daemon')
        finally:
            rmtree('/var/log/wrk-container', ignore_errors=True)
            copytree('/tmp/wrk-container/log', '/var/log/wrk-container', symlinks=True)
            try:
                kill(Process(proc.pid).children()[0].pid, SIGTERM)
                print('sent sigterm to worker launcher')
            except IndexError:
                # probably worker daemon terminated quickly
                pass
            proc.terminate()
            print('sent sigterm to unshare')

if __name__ == '__main__':
    main()
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1