github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/docs/articles/ambassador_pattern_linking.md (about) 1 <!--[metadata]> 2 +++ 3 title = "Link via an ambassador container" 4 description = "Using the Ambassador pattern to abstract (network) services" 5 keywords = ["Examples, Usage, links, docker, documentation, examples, names, name, container naming"] 6 [menu.main] 7 parent = "smn_administrate" 8 weight = 6 9 +++ 10 <![end-metadata]--> 11 12 # Link via an ambassador container 13 14 ## Introduction 15 16 Rather than hardcoding network links between a service consumer and 17 provider, Docker encourages service portability, for example instead of: 18 19 (consumer) --> (redis) 20 21 Requiring you to restart the `consumer` to attach it to a different 22 `redis` service, you can add ambassadors: 23 24 (consumer) --> (redis-ambassador) --> (redis) 25 26 Or 27 28 (consumer) --> (redis-ambassador) ---network---> (redis-ambassador) --> (redis) 29 30 When you need to rewire your consumer to talk to a different Redis 31 server, you can just restart the `redis-ambassador` container that the 32 consumer is connected to. 33 34 This pattern also allows you to transparently move the Redis server to a 35 different docker host from the consumer. 36 37 Using the `svendowideit/ambassador` container, the link wiring is 38 controlled entirely from the `docker run` parameters. 39 40 ## Two host example 41 42 Start actual Redis server on one Docker host 43 44 big-server $ docker run -d --name redis crosbymichael/redis 45 46 Then add an ambassador linked to the Redis server, mapping a port to the 47 outside world 48 49 big-server $ docker run -d --link redis:redis --name redis_ambassador -p 6379:6379 svendowideit/ambassador 50 51 On the other host, you can set up another ambassador setting environment 52 variables for each remote port we want to proxy to the `big-server` 53 54 client-server $ docker run -d --name redis_ambassador --expose 6379 -e REDIS_PORT_6379_TCP=tcp://192.168.1.52:6379 svendowideit/ambassador 55 56 Then on the `client-server` host, you can use a Redis client container 57 to talk to the remote Redis server, just by linking to the local Redis 58 ambassador. 59 60 client-server $ docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli 61 redis 172.17.0.160:6379> ping 62 PONG 63 64 ## How it works 65 66 The following example shows what the `svendowideit/ambassador` container 67 does automatically (with a tiny amount of `sed`) 68 69 On the Docker host (192.168.1.52) that Redis will run on: 70 71 # start actual redis server 72 $ docker run -d --name redis crosbymichael/redis 73 74 # get a redis-cli container for connection testing 75 $ docker pull relateiq/redis-cli 76 77 # test the redis server by talking to it directly 78 $ docker run -t -i --rm --link redis:redis relateiq/redis-cli 79 redis 172.17.0.136:6379> ping 80 PONG 81 ^D 82 83 # add redis ambassador 84 $ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 alpine:3.2 sh 85 86 In the `redis_ambassador` container, you can see the linked Redis 87 containers `env`: 88 89 / # env 90 REDIS_PORT=tcp://172.17.0.136:6379 91 REDIS_PORT_6379_TCP_ADDR=172.17.0.136 92 REDIS_NAME=/redis_ambassador/redis 93 HOSTNAME=19d7adf4705e 94 SHLVL=1 95 HOME=/root 96 REDIS_PORT_6379_TCP_PORT=6379 97 REDIS_PORT_6379_TCP_PROTO=tcp 98 REDIS_PORT_6379_TCP=tcp://172.17.0.136:6379 99 TERM=xterm 100 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 101 PWD=/ 102 / # exit 103 104 This environment is used by the ambassador `socat` script to expose Redis 105 to the world (via the `-p 6379:6379` port mapping): 106 107 $ docker rm redis_ambassador 108 $ CMD="apk update && apk add socat && sh" 109 $ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 alpine:3.2 sh -c "$CMD" 110 [...] 111 / # socat -t 100000000 TCP4-LISTEN:6379,fork,reuseaddr TCP4:172.17.0.136:6379 112 113 Now ping the Redis server via the ambassador: 114 115 Now go to a different server: 116 117 $ CMD="apk update && apk add socat && sh" 118 $ docker run -t -i --expose 6379 --name redis_ambassador alpine:3.2 sh -c "$CMD" 119 [...] 120 / # socat -t 100000000 TCP4-LISTEN:6379,fork,reuseaddr TCP4:192.168.1.52:6379 121 122 And get the `redis-cli` image so we can talk over the ambassador bridge. 123 124 $ docker pull relateiq/redis-cli 125 $ docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli 126 redis 172.17.0.160:6379> ping 127 PONG 128 129 ## The svendowideit/ambassador Dockerfile 130 131 The `svendowideit/ambassador` image is based on the `alpine:3.2` image with 132 `socat` installed. When you start the container, it uses a small `sed` 133 script to parse out the (possibly multiple) link environment variables 134 to set up the port forwarding. On the remote host, you need to set the 135 variable using the `-e` command line option. 136 137 --expose 1234 -e REDIS_PORT_1234_TCP=tcp://192.168.1.52:6379 138 139 Will forward the local `1234` port to the remote IP and port, in this 140 case `192.168.1.52:6379`. 141 142 # 143 # do 144 # docker build -t svendowideit/ambassador . 145 # then to run it (on the host that has the real backend on it) 146 # docker run -t -i -link redis:redis -name redis_ambassador -p 6379:6379 svendowideit/ambassador 147 # on the remote host, you can set up another ambassador 148 # docker run -t -i -name redis_ambassador -expose 6379 -e REDIS_PORT_6379_TCP=tcp://192.168.1.52:6379 svendowideit/ambassador sh 149 # you can read more about this process at https://docs.docker.com/articles/ambassador_pattern_linking/ 150 151 # use alpine because its a minimal image with a package manager. 152 # prettymuch all that is needed is a container that has a functioning env and socat (or equivalent) 153 FROM alpine:3.2 154 MAINTAINER SvenDowideit@home.org.au 155 156 RUN apk update && \ 157 apk add socat && \ 158 rm -r /var/cache/ 159 160 CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh