summaryrefslogtreecommitdiff
path: root/docs/sources/use/ambassador_pattern_linking.rst
blob: bbd5816768ee8520b29be22f33000d28e5a8bf2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
:title: Link via an Ambassador Container
:description: Using the Ambassador pattern to abstract (network) services
:keywords: Examples, Usage, links, docker, documentation, examples, names, name, container naming

.. _ambassador_pattern_linking:

Link via an Ambassador Container
================================

Rather than hardcoding network links between a service consumer and provider, Docker
encourages service portability.

eg, instead of

.. code-block:: bash

	(consumer) --> (redis)

requiring you to restart the ``consumer`` to attach it to a different ``redis`` service, 
you can add ambassadors

.. code-block:: bash

	(consumer) --> (redis-ambassador) --> (redis)

	or

	(consumer) --> (redis-ambassador) ---network---> (redis-ambassador) --> (redis)

When you need to rewire your consumer to talk to a different redis server, you 
can just restart the ``redis-ambassador`` container that the consumer is connected to.

This pattern also allows you to transparently move the redis server to a different
docker host from the consumer.

Using the ``svendowideit/ambassador`` container, the link wiring is controlled entirely 
from the ``docker run`` parameters.

Two host Example
----------------

Start actual redis server on one Docker host

.. code-block:: bash

	big-server $ docker run -d --name redis crosbymichael/redis

Then add an ambassador linked to the redis server, mapping a port to the outside world

.. code-block:: bash

	big-server $ docker run -d --link redis:redis --name redis_ambassador -p 6379:6379 svendowideit/ambassador

On the other host, you can set up another ambassador setting environment variables for each remote port we want to proxy to the ``big-server``

.. code-block:: bash

	client-server $ docker run -d --name redis_ambassador --expose 6379 -e REDIS_PORT_6379_TCP=tcp://192.168.1.52:6379 svendowideit/ambassador

Then on the ``client-server`` host, you can use a redis client container to talk 
to the remote redis server, just by linking to the local redis ambassador.

.. code-block:: bash

	client-server $ docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli
	redis 172.17.0.160:6379> ping
	PONG



How it works
------------

The following example shows what the ``svendowideit/ambassador`` container does 
automatically (with a tiny amount of ``sed``)

On the docker host (192.168.1.52) that redis will run on:

.. code-block:: bash

	# start actual redis server
	$ docker run -d --name redis crosbymichael/redis

	# get a redis-cli container for connection testing	
	$ docker pull relateiq/redis-cli

	# test the redis server by talking to it directly
	$ docker run -t -i --rm --link redis:redis relateiq/redis-cli
	redis 172.17.0.136:6379> ping
	PONG
	^D
	
	# add redis ambassador
	$ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 busybox sh
	
in the redis_ambassador container, you can see the linked redis containers's env

.. code-block:: bash

	$ env
	REDIS_PORT=tcp://172.17.0.136:6379
	REDIS_PORT_6379_TCP_ADDR=172.17.0.136
	REDIS_NAME=/redis_ambassador/redis
	HOSTNAME=19d7adf4705e
	REDIS_PORT_6379_TCP_PORT=6379
	HOME=/
	REDIS_PORT_6379_TCP_PROTO=tcp
	container=lxc
	REDIS_PORT_6379_TCP=tcp://172.17.0.136:6379
	TERM=xterm
	PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
	PWD=/
	
	
This environment is used by the ambassador socat script to expose redis to the world 
(via the -p 6379:6379 port mapping)

.. code-block:: bash

	$ docker rm redis_ambassador
	$ sudo ./contrib/mkimage-unittest.sh
	$ docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 docker-ut sh
	
	$ socat TCP4-LISTEN:6379,fork,reuseaddr TCP4:172.17.0.136:6379
	
then ping the redis server via the ambassador

.. code-block::bash

	$ docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli
	redis 172.17.0.160:6379> ping
	PONG

Now goto a different server

.. code-block:: bash

	$ sudo ./contrib/mkimage-unittest.sh
	$ docker run -t -i  --expose 6379 --name redis_ambassador docker-ut sh
	
	$ socat TCP4-LISTEN:6379,fork,reuseaddr TCP4:192.168.1.52:6379

and get the redis-cli image so we can talk over the ambassador bridge

.. code-block:: bash

	$ docker pull relateiq/redis-cli
	$ docker run -i -t --rm --link redis_ambassador:redis relateiq/redis-cli
	redis 172.17.0.160:6379> ping
	PONG

The svendowideit/ambassador Dockerfile
--------------------------------------

The ``svendowideit/ambassador`` image is a small busybox image with ``socat`` built in.
When you start the container, it uses a small ``sed`` script to parse out the (possibly multiple)
link environment variables to set up the port forwarding. On the remote host, you need to set the 
variable using the ``-e`` command line option.

``--expose 1234 -e REDIS_PORT_1234_TCP=tcp://192.168.1.52:6379`` will forward the 
local ``1234`` port to the remote IP and port - in this case ``192.168.1.52:6379``.


::

	#
	#
	# first you need to build the docker-ut image 
	# using ./contrib/mkimage-unittest.sh
	# then 
	#   docker build -t SvenDowideit/ambassador .
	#   docker tag SvenDowideit/ambassador ambassador
	# then to run it (on the host that has the real backend on it)
	#   docker run -t -i --link redis:redis --name redis_ambassador -p 6379:6379 ambassador
	# on the remote host, you can set up another ambassador
	#    docker run -t -i --name redis_ambassador --expose 6379 sh

	FROM	docker-ut
	MAINTAINER	SvenDowideit@home.org.au


	CMD	env | grep _TCP= | sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/'  | sh && top