Browse code

add linked containers to hosts file

Docker-DCO-1.1-Signed-off-by: Bryan Murphy <bmurphy1976@gmail.com> (github: bmurphy1976)

Docker-DCO-1.1-Signed-off-by: Solomon Hykes <solomon@docker.com> (github: shykes)
Tested-by: Solomon Hykes <solomon@docker.com> (github: shykes)

Bryan Murphy authored on 2014/04/08 03:34:07
Showing 5 changed files
... ...
@@ -357,7 +357,20 @@ func (container *Container) buildHostnameAndHostsFiles(IP string) error {
357 357
 	}
358 358
 
359 359
 	container.HostsPath = path.Join(container.root, "hosts")
360
-	return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname)
360
+
361
+	extraContent := make(map[string]string)
362
+
363
+	children, err := container.daemon.Children(container.Name)
364
+	if err != nil {
365
+		return err
366
+	}
367
+
368
+	for linkAlias, child := range children {
369
+		_, alias := path.Split(linkAlias)
370
+		extraContent[alias] = child.NetworkSettings.IPAddress
371
+	}
372
+
373
+	return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, &extraContent)
361 374
 }
362 375
 
363 376
 func (container *Container) allocateNetwork() error {
... ...
@@ -1,4 +1,4 @@
1
-page_title: Docker Run Reference 
1
+page_title: Docker Run Reference
2 2
 page_description: Configure containers at runtime
3 3
 page_keywords: docker, run, configure, runtime
4 4
 
... ...
@@ -407,6 +407,13 @@ And we can use that information to connect from another container as a client:
407 407
     $ docker run -i -t --rm --link redis-name:redis_alias --entrypoint /bin/bash dockerfiles/redis -c '/redis-stable/src/redis-cli -h $REDIS_ALIAS_PORT_6379_TCP_ADDR -p $REDIS_ALIAS_PORT_6379_TCP_PORT'
408 408
     172.17.0.32:6379>
409 409
 
410
+Docker will also map the private IP address to the alias of a linked
411
+container by inserting an entry into `/etc/hosts`.  You can use this
412
+mechanism to communicate with a linked container by its alias:
413
+
414
+    $ docker run -d --name servicename busybox sleep 30
415
+    $ docker run -i -t --link servicename:servicealias busybox ping -c 1 servicealias
416
+
410 417
 ## VOLUME (Shared Filesystems)
411 418
 
412 419
     -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro].
... ...
@@ -109,3 +109,32 @@ the Redis container.
109 109
     CONTAINER ID        IMAGE                        COMMAND                CREATED              STATUS              PORTS               NAMES
110 110
     4c01db0b339c        ubuntu:12.04                 bash                   17 seconds ago       Up 16 seconds                           webapp
111 111
     d7886598dbe2        crosbymichael/redis:latest   /redis-server --dir    33 minutes ago       Up 33 minutes       6379/tcp            redis,webapp/db
112
+
113
+## Resolving Links by Name
114
+
115
+New in version v0.11.
116
+
117
+Linked containers can be accessed by hostname.  Hostnames are mapped by
118
+appending entries to '/etc/hosts' using the linked container's alias.
119
+
120
+For example, linking a container using '--link redis:db' will generate the
121
+following '/etc/hosts' file:
122
+
123
+    root@6541a75d44a0:/# cat /etc/hosts
124
+    172.17.0.3  6541a75d44a0
125
+    172.17.0.2  db
126
+
127
+    127.0.0.1   localhost
128
+    ::1     localhost ip6-localhost ip6-loopback
129
+    fe00::0     ip6-localnet
130
+    ff00::0     ip6-mcastprefix
131
+    ff02::1     ip6-allnodes
132
+    ff02::2     ip6-allrouters
133
+    root@6541a75d44a0:/#
134
+
135
+Using this mechanism, you can communicate with the linked container by
136
+name:
137
+
138
+    root@6541a75d44a0:/# echo PING | redis-cli -h db
139
+    PONG
140
+    root@6541a75d44a0:/#
112 141
new file mode 100644
... ...
@@ -0,0 +1,45 @@
0
+package main
1
+
2
+import (
3
+	"fmt"
4
+	"os/exec"
5
+	"testing"
6
+)
7
+
8
+func TestPingUnlinkedContainers(t *testing.T) {
9
+	runCmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "sh", "-c", "ping -c 1 alias1 -W 1 && ping -c 1 alias2 -W 1")
10
+	exitCode, err := runCommand(runCmd)
11
+
12
+	if exitCode == 0 {
13
+		t.Fatal("run ping did not fail")
14
+	} else if exitCode != 1 {
15
+		errorOut(err, t, fmt.Sprintf("run ping failed with errors: %v", err))
16
+	}
17
+}
18
+
19
+func TestPingLinkedContainers(t *testing.T) {
20
+	cmd := exec.Command(dockerBinary, "run", "-d", "--name", "container1", "busybox", "sleep", "10")
21
+	out, _, err := runCommandWithOutput(cmd)
22
+	errorOut(err, t, fmt.Sprintf("run container1 failed with errors: %v", err))
23
+	idA := stripTrailingCharacters(out)
24
+
25
+	cmd = exec.Command(dockerBinary, "run", "-d", "--name", "container2", "busybox", "sleep", "10")
26
+	out, _, err = runCommandWithOutput(cmd)
27
+	errorOut(err, t, fmt.Sprintf("run container2 failed with errors: %v", err))
28
+	idB := stripTrailingCharacters(out)
29
+
30
+	cmd = exec.Command(dockerBinary, "run", "--rm", "--link", "container1:alias1", "--link", "container2:alias2", "busybox", "sh", "-c", "ping -c 1 alias1 -W 1 && ping -c 1 alias2 -W 1")
31
+	out, _, err = runCommandWithOutput(cmd)
32
+	fmt.Printf("OUT: %s", out)
33
+	errorOut(err, t, fmt.Sprintf("run ping failed with errors: %v", err))
34
+
35
+	cmd = exec.Command(dockerBinary, "kill", idA)
36
+	_, err = runCommand(cmd)
37
+	errorOut(err, t, fmt.Sprintf("failed to kill container1: %v", err))
38
+
39
+	cmd = exec.Command(dockerBinary, "kill", idB)
40
+	_, err = runCommand(cmd)
41
+	errorOut(err, t, fmt.Sprintf("failed to kill container2: %v", err))
42
+
43
+	deleteAllContainers()
44
+}
... ...
@@ -15,7 +15,7 @@ var defaultContent = map[string]string{
15 15
 	"ip6-allrouters":                       "ff02::2",
16 16
 }
17 17
 
18
-func Build(path, IP, hostname, domainname string) error {
18
+func Build(path, IP, hostname, domainname string, extraContent *map[string]string) error {
19 19
 	content := bytes.NewBuffer(nil)
20 20
 	if IP != "" {
21 21
 		if domainname != "" {
... ...
@@ -30,5 +30,14 @@ func Build(path, IP, hostname, domainname string) error {
30 30
 			return err
31 31
 		}
32 32
 	}
33
+
34
+	if extraContent != nil {
35
+		for hosts, ip := range *extraContent {
36
+			if _, err := content.WriteString(fmt.Sprintf("%s\t%s\n", ip, hosts)); err != nil {
37
+				return err
38
+			}
39
+		}
40
+	}
41
+
33 42
 	return ioutil.WriteFile(path, content.Bytes(), 0644)
34 43
 }