TL;DR
runtime.LockOSThread () '' '' lors de l'exécution d'opérations liées à l'espace de noms Linux qui sont fortement associées à OS Thread. y a-t-il. [^ 1]Si vous préparez une VM pour chaque locataire (200 ~), la gestion et le coût seront importants, j'ai donc décidé de créer un mécanisme pour fournir un proxy inverse HTTP (S) aux locataires avec des espaces d'adressage en conflit.
Proof of Concept
Essayez d'exécuter le code ci-dessous.
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)
}
}
Pour exécuter ce code, préparez un conteneur isolé sur le réseau comme indiqué ci-dessous.
# 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 &
Lorsque ce binaire est exécuté, il n'existe pas dans l'espace de noms réseau du conteneur (ci-après dénommé netns) lorsqu'il fonctionne en tant que serveur HTTP.
# ls -l /proc/1/ns/net #Informations réseau initiales pour l'hôte
lrwxrwxrwx 1 root root 0 Dec 24 21:42 /proc/1/ns/net -> 'net:[4026531984]'
# ls -l /proc/$(pgrep nsproxy)/task/*/ns/net #Le processus nsproxy est sur les réseaux hôtes
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 #informations netns pour le conteneur
lrwxrwxrwx 1 root root 0 Dec 24 21:50 /proc/3867/task/3867/ns/net -> 'net:[4026532117]'
Cependant, si vous utilisez nsenter pour entrer les réseaux du conteneur, vous pouvez voir que le serveur http fonctionne à
127.0.0.1: 8080```.
# 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
Voyons à quel point cette méthode évolue. Développez pour avoir plusieurs ports d'écoute.
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()
}
Préparez environ 100 conteneurs comme indiqué ci-dessous
#Créer 100 conteneurs
seq 1000 1999 | xargs -I '{}' -exec docker run -d --net none --name 'pause{}' k8s.gcr.io/pause:3.1
#Écoutez 100 conteneurs
sudo ./nsproxy 127.0.0.1:8080 $(docker inspect --format '{{.NetworkSettings.SandboxKey}}' pause{100..199} ) &
État immédiatement après le démarrage du processus
$ 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
Immédiatement après le démarrage, on constate que le RSS est assez léger, environ 10980 ko.
Ce n'est pas effrayant de toucher l'espace de noms du réseau, alors essayez-le. La bibliothèque CNI elle-même est légère, alors assurez-vous de jeter un œil à l'implémentation elle-même.
Recommended Posts