Skip to content

Commit 1d8bca7

Browse files
Merge pull request #1 from qnib/gpu
GPU enabled proxy
2 parents c727afd + 31dd73f commit 1d8bca7

File tree

349 files changed

+156942
-15
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

349 files changed

+156942
-15
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.1.2
2+
current_version = 0.2.0
33
files = main.go
44
commit = True
55
tag = True

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@
1414
.glide/
1515

1616
# binaries
17-
doxy_*
17+
**/doxy_*
18+
.idea/

Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
ARG DOCKER_REG=docker.io
2+
FROM ${DOCKER_REG}/qnib/alplain-golang AS build
3+
4+
WORKDIR /usr/local/src/github.com/qnib/doxy
5+
COPY main.go ./main.go
6+
COPY proxy ./proxy
7+
COPY vendor/ vendor/
8+
RUN govendor install
9+
10+
## Build final image
11+
FROM alpine:3.5
12+
13+
COPY --from=build /usr/local/bin/doxy /usr/local/bin/
14+
CMD ["/usr/local/bin/doxy"]

Dockerfile.ubuntu

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
ARG DOCKER_REG=docker.io
2+
FROM ${DOCKER_REG}/qnib/uplain-golang:1.9.2 AS build
3+
4+
WORKDIR /usr/local/src/github.com/qnib/doxy
5+
COPY main.go ./main.go
6+
COPY proxy ./proxy
7+
COPY vendor/ vendor/
8+
RUN govendor install
9+
10+
## Build final image
11+
FROM ${DOCKER_REG}/qnib/uplain-init
12+
13+
COPY --from=build /usr/local/bin/doxy /usr/local/bin/
14+
CMD ["/usr/local/bin/doxy"]

GPU.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# GPU Support
2+
3+
As a Proof-of-Concept how to implement GPU (and InfiniBand) support, `doxy` was extended to allow for injection of payload to the `docker create` call.
4+
5+
On an `p2.xlarge` instance with CUDA support, `doxy:gpu` was started like that.
6+
7+
```bash
8+
$ docker run -v /var/run:/var/run/ -ti --rm qnib/doxy:gpu doxy --pattern-key=hpc --debug --proxy-socket=/var/run/hpc.sock --gpu
9+
```
10+
11+
The pattern `hpc` allows for read/write endpoints to be used and `--gpu` injects bind-mounts, device-mappings and environment variables to make it work.
12+
13+
```bash
14+
root@ip-172-31-28-27:~# docker -H unix:///var/run/hpc.sock create nvidia/cuda nvidia-smi -L
15+
6b708d7cda36b9c37e325893108839f3b02f172e40ab97182fa77d770cc219fb
16+
root@ip-172-31-28-27:~# docker start -a 6b708d7cda36b9c37e325893108839f3b02f172e40ab97182fa77d770cc219fb
17+
GPU 0: Tesla K80 (UUID: GPU-234d5537-ea27-68cd-7337-ee21b2f34bf1)
18+
root@ip-172-31-28-27:~#
19+
```
20+
21+
When `start`ing the container the default socket is used; as I had issue to pass `stdin`/`stdout` through.
22+
The command hangs with the `doxy`-socket.
23+
I suspect issues with `stdin`/`stdout` passthrough when proxying the two unix-sockets. :/

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ GLOBAL OPTIONS:
1616
$ ./doxy_darwin
1717
2017/08/18 11:37:43 [II] Start Version: 0.1.0
1818
2017/08/18 11:37:43 Error reading patterns file (open /etc/doxy.pattern: no such file or directory), using default patterns
19-
2017/08/18 11:37:43 [gk-soxy] Listening on /tmp/doxy.sock
19+
2017/08/18 11:37:43 [doxy] Listening on /tmp/doxy.sock
2020
```
2121
2222
## Filter mechanism
@@ -79,7 +79,7 @@ $ ./doxy_darwin -debug
7979
2017/08/18 11:44:50 4 : ^/(v\d\.\d+/)?nodes(/\w+)?$
8080
2017/08/18 11:44:50 5 : ^/(v\d\.\d+/)?info$
8181
2017/08/18 11:44:50 6 : ^/_ping$
82-
2017/08/18 11:44:50 [gk-soxy] Listening on /tmp/doxy.sock
82+
2017/08/18 11:44:50 [doxy] Listening on /tmp/doxy.sock
8383
[negroni] 2017-08-18T11:45:00+02:00 | 200 | 3.800713ms | docker | GET /_ping
8484
[negroni] 2017-08-18T11:45:00+02:00 | 403 | 34.067µs | docker | GET /v1.31/containers/a62250e0890a/export
8585
[negroni] 2017-08-18T11:45:04+02:00 | 200 | 1.800044ms | docker | GET /_ping

build.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
3+
COMMIT_TAG=$(git describe --tags)
4+
TEMP_TAG=$(base64 /dev/urandom | tr -d '/+' | dd bs=32 count=1 2>/dev/null)
5+
mkdir -p bin/
6+
7+
echo ">> Build temp-image 'qnib/$(basename $(pwd)):${TEMP_TAG}'"
8+
docker build -t qnib/$(basename $(pwd)):${TEMP_TAG} -f Dockerfile.ubuntu .
9+
echo ">> Start image as ${TEMP_TAG} to copy binary"
10+
ID=$(docker run -d --name ${TEMP_TAG} qnib/$(basename $(pwd)):${TEMP_TAG} tail -f /dev/null)
11+
echo ">> CONTAINER_ID=${ID}"
12+
docker cp ${ID}:/usr/local/bin/doxy bin/doxy_x86_${COMMIT_TAG}
13+
echo ">> Remove container and image"
14+
docker rm -f ${ID}
15+
docker rmi qnib/$(basename $(pwd)):${TEMP_TAG}

main.go

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/zpatrick/go-config"
77
"github.com/codegangsta/cli"
88
"github.com/qnib/doxy/proxy"
9+
"strings"
910
)
1011

1112
var (
@@ -21,11 +22,37 @@ var (
2122
Usage: "Proxy socket to be created",
2223
EnvVar: "DOXY_PROXY_SOCKET",
2324
}
25+
proxyPatternKey = cli.StringFlag{
26+
Name: "pattern-key",
27+
Value: "default",
28+
Usage: "pattern key predefined",
29+
EnvVar: "DOXY_PATTERN_KEY",
30+
}
31+
gpuEnabled = cli.BoolFlag{
32+
Name: "gpu",
33+
Usage: "Map devices, bind-mounts and environment into each container to allow GPU usage",
34+
EnvVar: "DOXY_GPU_ENABLED",
35+
}
36+
constrainUser = cli.BoolFlag{
37+
Name: "user-pinning",
38+
Usage: "Pin user within container to the UID calling the command",
39+
EnvVar: "DOXY_USER_PINNING_ENABLED",
40+
}
2441
debugFlag = cli.BoolFlag{
2542
Name: "debug",
2643
Usage: "Print proxy requests",
2744
EnvVar: "DOXY_DEBUG",
2845
}
46+
bindAddFlag = cli.StringFlag{
47+
Name: "add-binds",
48+
Usage: "Comma separated list of bind-mounts to add",
49+
EnvVar: "DOXY_ADDITIONAL_BINDS",
50+
}
51+
devMapFlag = cli.StringFlag{
52+
Name: "device-mappings",
53+
Usage: "Comma separated list of device mappings",
54+
EnvVar: "DOXY_DEVICE_MAPPINGS",
55+
}
2956
patternFileFlag = cli.StringFlag{
3057
Name: "pattern-file",
3158
Value: proxy.PATTERN_FILE,
@@ -41,6 +68,10 @@ func EvalOptions(cfg *config.Config) (po []proxy.ProxyOption) {
4168
po = append(po, proxy.WithDockerSocket(dockerSock))
4269
debug, _ := cfg.Bool("debug")
4370
po = append(po, proxy.WithDebugValue(debug))
71+
devMaps, _ := cfg.String("device-mappings")
72+
gpu, _ := cfg.Bool("gpu")
73+
po = append(po, proxy.WithGpuValue(gpu))
74+
po = append(po, proxy.WithDevMappings(strings.Split(devMaps,",")))
4475
return
4576
}
4677

@@ -50,18 +81,32 @@ func EvalPatternOpts(cfg *config.Config) (proxy.ProxyOption) {
5081
defer reader.Close()
5182
patterns := []string{}
5283
if err != nil {
53-
log.Printf("Error reading patterns file (%s), using default patterns\n", err.Error())
54-
return proxy.WithPatterns(proxy.DEFAULT_PATTERNS)
84+
patternsKey, _ := cfg.String("pattern-key")
85+
if patterns, ok := proxy.PATTERNS[patternsKey]; ok {
86+
log.Printf("Error reading patterns file '%s', using %s patterns\n", err.Error(), patternsKey)
87+
return proxy.WithPatterns(patterns)
88+
}
89+
log.Printf("Could not find pattern-key '%s'\n", patternsKey)
90+
os.Exit(1)
91+
5592
}
5693
patterns, err = proxy.ReadPatterns(reader)
5794
return proxy.WithPatterns(patterns)
5895
}
5996

97+
func EvalBindMountOpts(cfg *config.Config) (proxy.ProxyOption) {
98+
bindStr, _ := cfg.String("add-binds")
99+
bindMounts := strings.Split(bindStr,",")
100+
return proxy.WithBindMounts(bindMounts)
101+
}
102+
103+
60104
func RunApp(ctx *cli.Context) {
61105
log.Printf("[II] Start Version: %s", ctx.App.Version)
62106
cfg := config.NewConfig([]config.Provider{config.NewCLI(ctx, true)})
63107
po := EvalOptions(cfg)
64108
po = append(po, EvalPatternOpts(cfg))
109+
po = append(po, EvalBindMountOpts(cfg))
65110
p := proxy.NewProxy(po...)
66111
p.Run()
67112
}
@@ -70,12 +115,15 @@ func main() {
70115
app := cli.NewApp()
71116
app.Name = "Proxy Docker unix socket to filter out insecure, harmful requests."
72117
app.Usage = "doxy [options]"
73-
app.Version = "0.1.2"
118+
app.Version = "0.2.0"
74119
app.Flags = []cli.Flag{
75120
dockerSocketFlag,
76121
proxySocketFlag,
77122
debugFlag,
123+
gpuEnabled,
78124
patternFileFlag,
125+
proxyPatternKey,
126+
bindAddFlag,
79127
}
80128
app.Action = RunApp
81129
app.Run(os.Args)

proxy/helper.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package proxy
2+
3+
import (
4+
"github.com/docker/docker/api/types/container"
5+
"github.com/docker/docker/api/types/network"
6+
"io"
7+
"bytes"
8+
"encoding/json"
9+
"net/http"
10+
"strings"
11+
"fmt"
12+
)
13+
14+
type Headers map[string][]string
15+
16+
17+
18+
type configWrapper struct {
19+
*container.Config
20+
HostConfig *container.HostConfig
21+
NetworkingConfig *network.NetworkingConfig
22+
}
23+
24+
func createDevMapping(dev string) (dm container.DeviceMapping, err error) {
25+
spl := strings.Split(dev, ":")
26+
switch len(spl) {
27+
case 2:
28+
dm = container.DeviceMapping{
29+
PathOnHost: spl[0],
30+
PathInContainer: spl[1],
31+
}
32+
case 3:
33+
dm = container.DeviceMapping{
34+
PathOnHost: spl[0],
35+
PathInContainer: spl[1],
36+
CgroupPermissions: spl[2],
37+
}
38+
default:
39+
return dm, fmt.Errorf("string needs to specify <src>:<dst>[:permission]")
40+
}
41+
return
42+
}
43+
44+
func encodeBody(obj interface{}, header http.Header) (io.Reader, http.Header, error) {
45+
if obj == nil {
46+
return nil, header, nil
47+
}
48+
49+
body, err := encodeData(obj)
50+
if err != nil {
51+
return nil, header, err
52+
}
53+
return body, header, nil
54+
}
55+
56+
func encodeData(data interface{}) (*bytes.Buffer, error) {
57+
params := bytes.NewBuffer(nil)
58+
if data != nil {
59+
if err := json.NewEncoder(params).Encode(data); err != nil {
60+
return nil, err
61+
}
62+
}
63+
return params, nil
64+
}

proxy/main.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const (
1616
)
1717

1818
var (
19-
DEFAULT_PATTERNS = []string{
19+
DEF_PAT = []string{
2020
`^/(v\d\.\d+/)?containers(/\w+)?/(json|stats|top)$`,
2121
`^/(v\d\.\d+/)?services(/[0-9a-f]+)?$`,
2222
`^/(v\d\.\d+/)?tasks(/\w+)?$`,
@@ -27,12 +27,28 @@ var (
2727
`^/(v\d\.\d+/)?version$`,
2828
"^/_ping$",
2929
}
30+
HPC_PAT = []string{
31+
`^/(v\d\.\d+/)?containers(/\w+)?/(json|stats|top|create|start|run|kill)$`,
32+
`^/(v\d\.\d+/)?services(/[0-9a-f]+)?$`,
33+
`^/(v\d\.\d+/)?tasks(/\w+)?$`,
34+
`^/(v\d\.\d+/)?networks(/\w+)?$`,
35+
`^/(v\d\.\d+/)?volumes(/\w+)?$`,
36+
`^/(v\d\.\d+/)?nodes(/\w+)?$`,
37+
`^/(v\d\.\d+/)?info$`,
38+
`^/(v\d\.\d+/)?version$`,
39+
"^/_ping$",
40+
}
41+
PATTERNS = map[string][]string{
42+
"default": DEF_PAT,
43+
"hpc": HPC_PAT,
44+
}
3045
)
3146

3247
type Proxy struct {
3348
dockerSocket, newSocket string
34-
debug bool
49+
debug, gpu bool
3550
patterns []string
51+
bindMounts,devMappings []string
3652
}
3753

3854
func NewProxy(opts ...ProxyOption) Proxy {
@@ -44,7 +60,10 @@ func NewProxy(opts ...ProxyOption) Proxy {
4460
dockerSocket: options.DockerSocket,
4561
newSocket: options.ProxySocket,
4662
debug: options.Debug,
63+
gpu: options.Gpu,
4764
patterns: options.Patterns,
65+
bindMounts: options.BindMounts,
66+
devMappings: options.DevMappings,
4867
}
4968
}
5069

@@ -59,7 +78,7 @@ func (p *Proxy) GetOptions() map[string]interface{} {
5978
}
6079

6180
func (p *Proxy) Run() {
62-
upstream := NewUpstream(p.dockerSocket, p.patterns)
81+
upstream := NewUpstream(p.dockerSocket, p.patterns, p.bindMounts, p.devMappings, p.gpu)
6382
sigc := make(chan os.Signal, 1)
6483
signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
6584
l, err := ListenToNewSock(p.newSocket, sigc)

0 commit comments

Comments
 (0)