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