Betreiben Sie den Linux-Netzwerk-Namespace mit Go

TL;DR

  1. Go Language Goroutine wechselt den OS-Thread, der standardmäßig präventiv ausgeführt wird. Daher ist es erforderlich, "runtime.LockOSThread ()" auszuführen, wenn Linux-Namespace-bezogene Vorgänge ausgeführt werden, die stark mit dem OS-Thread verbunden sind. Gibt es. [^ 1]
  2. Wenn Sie den Linux-Netzwerk-Namespace in der Sprache Go betreiben möchten, können Sie bequem CNI Library verwenden.

Warum tust du das?

Wenn Sie für jeden Mandanten eine VM vorbereiten (200 ~), sind Verwaltung und Kosten hoch. Daher habe ich beschlossen, einen Mechanismus zu erstellen, um Mandanten mit widersprüchlichen Adressräumen einen HTTP (S) -Reverse-Proxy bereitzustellen.

Proof of Concept

Versuchen Sie, den folgenden Code auszuführen.

package main

import (
        "log"
        "net"
        "net/http"
        "os"
        "runtime"

        "github.com/containernetworking/plugins/pkg/ns"
)

func main() {
        nspath := os.Args[1]
        addr := os.Args[2]
        var err error
        var l net.Listener
        ns.WithNetNSPath(nspath, func(_ ns.NetNS) error {
                l, err = net.Listen("tcp", addr)
                return nil
        })
        runtime.UnlockOSThread()
        if err != nil {
                log.Fatal(err)
        }
        if err := http.Serve(l, nil); err != nil {
                log.Fatal(err)
        }
}

Um diesen Code auszuführen, bereiten Sie einen im Netzwerk isolierten Container vor, wie unten gezeigt.

# build binary
go build -o nsproxy nsproxy.go
# setup environment
docker run -d --net none --name pause k8s.gcr.io/pause:3.1
ns=$(docker inspect --format '{{ .NetworkSettings.SandboxKey }}' pause)
# run program
sudo ./nsproxy "$ns" 127.0.0.1:8080 &

Wenn diese Binärdatei ausgeführt wird, ist sie im Netzwerk-Namaspace des Containers (im Folgenden als netns bezeichnet) nicht vorhanden, wenn er als HTTP-Server betrieben wird.

# ls -l /proc/1/ns/net #Anfängliche Netzinformationen für den Host
lrwxrwxrwx 1 root root 0 Dec 24 21:42 /proc/1/ns/net -> 'net:[4026531984]'
# ls -l /proc/$(pgrep nsproxy)/task/*/ns/net #Der nsproxy-Prozess befindet sich auf den Host-Netzen
lrwxrwxrwx 1 root root 0 Dec 24 21:42 /proc/4377/task/4377/ns/net -> 'net:[4026531984]'
lrwxrwxrwx 1 root root 0 Dec 24 21:47 /proc/4377/task/4378/ns/net -> 'net:[4026531984]'
lrwxrwxrwx 1 root root 0 Dec 24 21:47 /proc/4377/task/4379/ns/net -> 'net:[4026531984]'
lrwxrwxrwx 1 root root 0 Dec 24 21:47 /proc/4377/task/4380/ns/net -> 'net:[4026531984]'
lrwxrwxrwx 1 root root 0 Dec 24 21:47 /proc/4377/task/4381/ns/net -> 'net:[4026531984]'
lrwxrwxrwx 1 root root 0 Dec 24 21:47 /proc/4377/task/4382/ns/net -> 'net:[4026531984]'
lrwxrwxrwx 1 root root 0 Dec 24 21:47 /proc/4377/task/4393/ns/net -> 'net:[4026531984]'
# ls -l /proc/$(docker inspect --format '{{.State.Pid}}' pause)/task/*/ns/net #Netzinformationen für Container
lrwxrwxrwx 1 root root 0 Dec 24 21:50 /proc/3867/task/3867/ns/net -> 'net:[4026532117]'

Wenn Sie jedoch nsenter verwenden, um die Netze des Containers einzugeben, können Sie sehen, dass der http-Server unter `` `127.0.0.1: 8080``` ausgeführt wird.

# nsenter --net=$(docker inspect --format '{{ .NetworkSettings.SandboxKey }}' pause) bash
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
# ss -ltn
State           Recv-Q          Send-Q                   Local Address:Port                     Peer Address:Port
LISTEN          0               128                          127.0.0.1:8080                          0.0.0.0:*
# curl http://127.0.0.1:8080 -v
* Expire in 0 ms for 6 (transfer 0x5627619e7f50)
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x5627619e7f50)
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Tue, 24 Dec 2019 12:58:10 GMT
< Content-Length: 19
<
404 page not found
* Connection #0 to host 127.0.0.1 left intact

Versuchen Sie, es von vielen Containern aus zugänglich zu machen

Mal sehen, wie sehr diese Methode skaliert. Erweitern Sie, um mehrere Abhörports zu haben.

package main

import (
        "log"
        "net"
        "net/http"
        "os"
        "runtime"
        "sync"

        "github.com/containernetworking/plugins/pkg/ns"
)

func main() {
        addr := os.Args[1]
        var ls []net.Listener
        for _, nspath := range os.Args[2:] {
                ns.WithNetNSPath(nspath, func(_ ns.NetNS) error {
                        l, err := net.Listen("tcp", addr)
                        if err != nil {
                                log.Fatal(err)
                        }
                        ls = append(ls, l)
                        return nil
                })
        }
        runtime.UnlockOSThread()
        var wg sync.WaitGroup
        for _, l := range ls {
                wg.Add(1)
                go func(l net.Listener){
                        err := http.Serve(l, nil)
                        if err != nil {
                                log.Print(err)
                        }
                        wg.Done()
                }(l)
        }
        wg.Wait()
}

Bereiten Sie ungefähr 100 Behälter wie unten gezeigt vor

#Erstellen Sie 100 Container
seq 1000 1999 | xargs -I '{}' -exec docker run -d --net none --name 'pause{}' k8s.gcr.io/pause:3.1
#Hören Sie auf 100 Container
sudo ./nsproxy 127.0.0.1:8080 $(docker inspect --format '{{.NetworkSettings.SandboxKey}}' pause{100..199} ) &

Zustand unmittelbar nach Inbetriebnahme des Prozesses

$ sudo cat /proc/$(pgrep nsproxy)/status
Name:   nsproxy
Umask:  0022
State:  S (sleeping)
Tgid:   17082
Ngid:   0
Pid:    17082
PPid:   17068
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
FDSize: 128
Groups: 0
NStgid: 17082
NSpid:  17082
NSpgid: 17068
NSsid:  3567
VmPeak:   618548 kB
VmSize:   561720 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:     10980 kB
VmRSS:     10980 kB
RssAnon:            6608 kB
RssFile:            4372 kB
RssShmem:              0 kB
VmData:   161968 kB
VmStk:       140 kB
VmExe:      2444 kB
VmLib:      1500 kB
VmPTE:       140 kB
VmSwap:        0 kB
HugetlbPages:          0 kB
CoreDumping:    0
Threads:        7
SigQ:   0/15453
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: ffffffffffc1feff
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
NoNewPrivs:     0
Seccomp:        0
Speculation_Store_Bypass:       thread vulnerable
Cpus_allowed:   ffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff
Cpus_allowed_list:      0-239
Mems_allowed:   00000000,00000001
Mems_allowed_list:      0
voluntary_ctxt_switches:        6
nonvoluntary_ctxt_switches:     0

Unmittelbar nach dem Start ist zu erkennen, dass das RSS mit rund 10980 kB recht leicht ist.

Zusammenfassung

Es ist nicht beängstigend, den Netzwerk-Namespace zu berühren. Versuchen Sie es also bitte. Die CNI-Bibliothek selbst ist leichtgewichtig. Schauen Sie sich also unbedingt die Implementierung selbst an.

Recommended Posts

Betreiben Sie den Linux-Netzwerk-Namespace mit Go
Linux-Netzwerk-Namespace
Betreiben Sie den Db2-Container mit Go
Netzwerk-Linux-Befehle
Netzwerk-Namespace-Routing
Ich habe versucht, Linux mit Discord Bot zu betreiben
Linux (Lubuntu) mit OneMix3S
Betreiben Sie Blender mit Python
Betreiben Sie Excel mit Python (1)
Netzwerk-Memo (hauptsächlich Linux)
Betreiben Sie Excel mit Python (2)
Betreiben Sie Excel mit Python Open Pyxl
Zeichnen Sie die Bezier-Kurve mit Go
Betreiben Sie TwitterBot mit Lambda, Python
Erste Schritte mit Go Assembly
Selbst erstellter Linux-Kernel mit Clang
Neuronales Netzwerk mit Python (Scikit-Learn)
Spielen Sie mit Linux-Partitionen herum
3. Normalverteilung mit neuronalem Netz!
Vollbit-Suche mit Go
Stellen Sie mit GO eine Verbindung zu Postgresql her
[Hinweis] Betreiben Sie MongoDB mit Python
Arbeiten Sie mit Websites mit Python_Webbrowser
Linux am schnellsten mit AWS lernen
Versuchen Sie, Parfüm mit Go zu implementieren
Verwenden Sie WDC-433SU2M2 mit Manjaro Linux
[Python] [SQLite3] Betreiben Sie SQLite mit Python (Basic)
4. Kreisparameter mit einem neuronalen Netzwerk!
ROS Kurs 105 Betriebstio mit ROS
Netzwerkprogrammierung mit Python Scapy
Messung der Netzwerkleistung mit iperf