Move userland proxies out of daemon's process

This PR moves the userland proxies for TCP and UDP traffic out of the
main docker daemon's process ( from goroutines per proxy ) to be a
separate reexec of the docker binary.  This reduces the cpu and memory
needed by the daemon and if the proxy processes crash for some reason
the daemon is unaffected.  This also displays in the standard process
tree so that a user can clearly see if there is a userland proxy that is
bound to a certain ip and port.

```bash
CONTAINER ID        IMAGE                       COMMAND             CREATED             STATUS              PORTS                                          NAMES
5d349506feb6        busybox:buildroot-2014.02   "sh"                13 minutes ago      Up 1 seconds        0.0.0.0:49153->81/tcp, 0.0.0.0:49154->90/tcp   hungry_pike
root@1cbfdcedc5a7:/go/src/github.com/docker/docker# ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  18168  3100 ?        Ss   21:09   0:00 bash
root      8328  0.7  0.6 329072 13420 ?        Sl   22:03   0:00 docker -d -s vfs
root      8373  1.0  0.5 196500 10548 ?        Sl   22:03   0:00 userland-proxy -proto tcp -host-ip 0.0.0.0 -host-port 49153 -container-ip 10.0.0.2 -container-port 81
root      8382  1.0  0.5 270232 10576 ?        Sl   22:03   0:00 userland-proxy -proto tcp -host-ip 0.0.0.0 -host-port 49154 -container-ip 10.0.0.2 -container-port 90
root      8385  1.2  0.0   3168   184 pts/0    Ss+  22:03   0:00 sh
root      8408  0.0  0.1  15568  2112 ?        R+   22:03   0:00 ps aux
```

This also helps us to cleanly cleanup the proxy processes by stopping
these commands instead of trying to terminate a goroutine.

Signed-off-by: Michael Crosby <michael@docker.com>
This commit is contained in:
Michael Crosby
2014-08-13 11:54:47 -07:00
parent 283ee10886
commit b4e2f5ed96
5 changed files with 163 additions and 59 deletions
+6 -44
View File
@@ -1,14 +1,19 @@
package bridge
import (
"fmt"
"net"
"strconv"
"testing"
"github.com/docker/docker/daemon/networkdriver/portmapper"
"github.com/docker/docker/engine"
)
func init() {
// reset the new proxy command for mocking out the userland proxy in tests
portmapper.NewProxy = portmapper.NewMockProxyCommand
}
func findFreePort(t *testing.T) int {
l, err := net.Listen("tcp", ":0")
if err != nil {
@@ -61,46 +66,3 @@ func TestAllocatePortDetection(t *testing.T) {
t.Fatal("Duplicate port allocation granted by AllocatePort")
}
}
func TestAllocatePortReclaim(t *testing.T) {
eng := engine.New()
eng.Logging = false
freePort := findFreePort(t)
// Init driver
job := eng.Job("initdriver")
if res := InitDriver(job); res != engine.StatusOK {
t.Fatal("Failed to initialize network driver")
}
// Allocate interface
job = eng.Job("allocate_interface", "container_id")
if res := Allocate(job); res != engine.StatusOK {
t.Fatal("Failed to allocate network interface")
}
// Occupy port
listenAddr := fmt.Sprintf(":%d", freePort)
tcpListenAddr, err := net.ResolveTCPAddr("tcp", listenAddr)
if err != nil {
t.Fatalf("Failed to resolve TCP address '%s'", listenAddr)
}
l, err := net.ListenTCP("tcp", tcpListenAddr)
if err != nil {
t.Fatalf("Fail to listen on port %d", freePort)
}
// Allocate port, expect failure
job = newPortAllocationJob(eng, freePort)
if res := AllocatePort(job); res == engine.StatusOK {
t.Fatal("Successfully allocated currently used port")
}
// Reclaim port, retry allocation
l.Close()
if res := AllocatePort(job); res != engine.StatusOK {
t.Fatal("Failed to allocate previously reclaimed port")
}
}