基于Volume的互联

理解Docker Volume

/var/lib/docker/graph 存放本地Image里的分层信息

/var/lib/docker/devicemapper/devicemapper/data 存储了ImageContainer的二进制数据文件

/var/lib/docker/devicemapper/devicemapper/metadata 存储了相关元数据

 

 

Aufs driver是Docker最早支持的driver,但是aufs只是Linux内核的一个补丁集

Device mapper是Linux 2.6内核中提供的一种从逻辑设备到物理设备的映射框架机制,是LVM2的核心,支持块级别的copy on write特性目前,除少数版本如Ubuntu, Docker基本运行在Devicemapper基础上FS虚拟文件系统的最大缺陷是不支持copy on write特性,每层都是一个单独的目录,如果新增一个child层,则需要将父级层镜像文件一并复制到新目录

btrfs 非常快,采用btrfs的文件系统级的快照能力来实现layer分层功能,缺点是仍然在进化中,还不够成熟,特别是大量写操作的压力下

目前,除少数版本如UbuntuDocker基本运行在Devicemapper基础上

 

为什么需要Volume

[root@node1 ~]# docker run --rm=true -it -v /leader java /bin/bash    #容器删除目录不存在
root@476a8dc8ae5d:/# ls
bin  boot  dev  etc  home  leader  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@476a8dc8ae5d:/# ls /leader/
[root@node1 ~]# docker ps
CONTAINER ID  IMAGE  COMMAND                  CREATED       STATUS        PORTS                               NAMES
476a8dc8ae5d  java   "/bin/bash"              4 minutes ago Up 4 minutes                                      mystifying_heisenberg
3d48efbceb60  java   "/bin/bash"              7 days ago    Up 6 hours                                        myjava3
11f068716ba1  mysql  "docker-entrypoint.s…"   8 days ago    Up 6 hours    0.0.0.0:3306->3306/tcp, 33060/tcp   mysqlsrv1
[root@node1 ~]# docker inspect 476a8dc8ae5d
       "Mounts": [
            {
                "Type": "volume",
                "Name": "06d3f6ca6b5148fd7e33ed2ce0e32f924c722e9bfdd54488a869ff502246983a",
                "Source": "/var/lib/docker/volumes/06d3f6ca6b5148fd7e33ed2ce0e32f924c722e9bfdd54488a869ff502246983a/_data",
                "Destination": "/leader",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
[root@node1 ~]# mkdir /var/lib/docker/volumes/06d3f6ca6b5148fd7e33ed2ce0e32f924c722e9bfdd54488a869ff502246983a/_data/mybook
root@476a8dc8ae5d:/# ls leader/
mybook

docker run --rm=true -it -v /storage(本地目录) /leader(容器目录)java /bin/bash

[root@node1 ~]# mkdir /storage
[root@node1 ~]# docker run --rm=true -it -v /storage:/leader:ro java /bin/bash    #容器删除目录还在
root@9bb567abc803:/# mkdir /leader/mybook3
mkdir: cannot create directory ‘/leader/mybook3’: Read-only file system
[root@node1 ~]# docker run --rm=true -it -v /storage:/leader:rw java /bin/bash
root@502db16fb115:/# mkdir leader/mybook3

可以多个容器中的Volume指向同一个本机目录,实现基于文件的的共享访问

基于Volume的互联,也可以解决跨主机的共享问题

基于数据容器的单主机互联

[root@node1 ~]# docker run -it -v /leader java /bin/bash
[root@node1 ~]# docker ps
CONTAINER ID  IMAGE  COMMAND                  CREATED         STATUS        PORTS                               NAMES
2e1132cdd525  java   "/bin/bash"              17 seconds ago  Up 16 seconds                                     determined_lederberg
3d48efbceb60  java   "/bin/bash"              8 days ago      Up 7 hours                                        myjava3
11f068716ba1  mysql  "docker-entrypoint.s…"   8 days ago      Up 7 hours    0.0.0.0:3306->3306/tcp, 33060/tcp   mysqlsrv1
[root@node1 ~]# docker run --rm=true --privileged=true --volumes-from=2e1132cdd525 -it java /bin/bash
root@2302ce2ac287:/# ls /leader/
root@2302ce2ac287:/# ls /leader/
root@2302ce2ac287:/# mkdir /leader/mybook
root@2e1132cdd525:/# ls /leader/
mybook

基于link的互联

默认情况下是容器直接是互联的

[root@node1 ~]# docker run --rm=true --name=mysqlserver -e MYSQL_ROOT_PASSWORD=smoke520 mysql
[root@node1 ~]# docker ps
CONTAINER ID  IMAGE  COMMAND                  CREATED        STATUS       PORTS                               NAMES
a35457528ed2  mysql  "docker-entrypoint.s…"   2 minutes ago  Up 2 minutes 3306/tcp, 33060/tcp                 mysqlserver
3d48efbceb60  java   "/bin/bash"              8 days ago     Up 7 hours                                       myjava3
11f068716ba1  mysql  "docker-entrypoint.s…"   8 days ago     Up 7 hours   0.0.0.0:3306->3306/tcp, 33060/tcp   mysqlsrv1
[root@node1 ~]# docker exec -it a35457528ed2 bash
root@a35457528ed2:/# cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.4      a35457528ed2
[root@node1 ~]# docker inspect a35457528ed2
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "8c60f6322422f9f0b16ebf0bb956fce4c4c567dc489f7079e834b2e7ff02d56c",
                    "EndpointID": "eecb29d0af7e30f8c2daed9fac1fc00442dd53ee9e00d572a77cc1be8bb7847f",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.4",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:04",
                    "DriverOpts": null
                }
            }
[root@node1 ~]# docker run --rm=true -it java curl 172.17.0.4:3306    #默认情况下时容器直接是互联的
%N▒▒▒▒▒<u}S)|w1=caching_sha2_password▒Got packets out of order[root@node1 ~]#

link方式

docker默认是允许container互通,通过-icc=false关闭互通。一旦关闭了互通,只能通过-link name:alias命令连接指定container.

-- link redis:db的别名,会在/etc/hosts中生成对应的ip映射

--link=myjaveserver[目标容器(需要连接容器)]:serverM1[给一个主机名(DNS名称)用来代替IP地址进行访问]

[root@node1 ~]# docker run --rm=true --name=myjavaserver -it java /bin/bash
root@50b8886a5de8:/# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
165: eth0@if166: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:05 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.5/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@node1 ~]# docker run --rm=true --link=myjavaserver:serverM1 -it java /bin/bash
root@08e669748006:/# cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.5      serverM1 50b8886a5de8 myjavaserver
172.17.0.6      08e669748006
root@08e669748006:/# ping serverM1
PING serverM1 (172.17.0.5): 56 data bytes
64 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.210 ms
^C--- serverM1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.210/0.210/0.210/0.000 ms
root@08e669748006:/# ping myjavaserver
PING serverM1 (172.17.0.5): 56 data bytes
64 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.139 ms
^C--- serverM1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.139/0.139/0.139/0.000 ms

/usr/bin/docker daemon --icc=false --iptables=true    #关闭容器互通

[root@node1 ~]# vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --icc=false --iptables=true
[root@node1 ~]# systemctl daemon-reload
[root@node1 ~]# systemctl restart docker
[root@node1 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
2512029290fb        mysql               "docker-entrypoint.s…"   24 hours ago        Up 24 hours         3306/tcp, 33060/tcp   mysqlserver
[root@node1 ~]# docker inspect 2512029290fb
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "2bd1ee6d1375a84d109f5b8945ebf15d5ddd0c0aad69ef8005a32b5ef74d4b06",
                    "EndpointID": "c00fe9deded0c2ba4ce3c80841aaadd0381bfdf4e4c7fd55a9b481a3082c4251",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
[root@node1 ~]# docker run --rm=true -it java ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
^C--- 172.17.0.2 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
[root@node1 ~]# docker run --rm=true --link=mysqlserver:myserver -it java /bin/bash
root@fd90f24a4a59:/# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
189: eth0@if190: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@node1 ~]# iptables-save > /etc/sysconfig/iptables
[root@node1 ~]# vim /etc/sysconfig/iptables
-A DOCKER -s 172.17.0.3/32 -d 172.17.0.2/32 -i docker0 -o docker0 -p tcp -m tcp --dport 3306 -j ACCEPT
-A DOCKER -s 172.17.0.2/32 -d 172.17.0.3/32 -i docker0 -o docker0 -p tcp -m tcp --sport 3306 -j ACCEPT
root@fd90f24a4a59:/# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
^C^C92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
--- 172.17.0.2 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
root@fd90f24a4a59:/# curl 172.17.0.2:3306
8.0.19  Te1▒▒▒▒▒'QMB:vI`ycPcaching_sha2_password▒Got packets out of orderroot@fd90f24a4a59:/# xterm-256color
[root@node1 ~]# docker run --rm=true java curl 172.17.0.2:3306
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (7) Failed to connect to 172.17.0.2 port 3306: No route
 to host
[root@node1 ~]# docker run --rm=true --link=mysqlserver:myserver -it java ping myserver
PING myserver (172.17.0.2): 56 data bytes
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
92 bytes from 172.17.0.1: Dest Unreachable, Unknown Code: 10
^C--- myserver ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss

Docker远程代理(Ambassador) 模式

https://github.com/gliderlabs/connectable

socat是一个多功能的网络工具,名字来由是” SOcket CAT” 

基于网络的互联

Docker的网络模型

Bridge(桥)是 Linux 上用来做 TCP/IP 二层协议交换的设备,与现实世界中的交换机功能相似。

常用的互联方式:端口映射

docker run -p "8080:80" 宿主的0.0.0.0:8080 -> 容器80

[root@node1 ~]# docker run --rm=true --name=mysqlserver -p 8066:3306 -e MYSQL_ROOT_PASSWORD=smoke520 mysql
[root@node1 ~]# ps -elf | grep docker-proxy
4 S root      41390  94465  0  80   0 - 91097 futex_ 21:53 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port
 8066 -container-ip 172.17.0.2 -container-port 3306
0 S root      41726  88007  0  80   0 - 28180 pipe_w 21:54 pts/0    00:00:00 grep --color=auto docker-proxy
[root@node1 ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1024/sshd
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1444/master
tcp6       0      0 :::8066                 :::*                    LISTEN      41390/docker-proxy
tcp6       0      0 :::22                   :::*                    LISTEN      1024/sshd
tcp6       0      0 ::1:25                  :::*                    LISTEN      1444/master

Apparently there are some edge cases without a better workaround (for now):

•localhost<->localhost routing

•docker instance calling into itself via its published port

•and possibly more

[root@node1 ~]# iptables -t nat -L -n
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:3306
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8066 to:172.17.0.2:3306
[root@node1 ~]# iptables -L -n
ACCEPT     tcp  --  0.0.0.0/0            172.17.0.2           tcp dpt:3306

Do we really need the proxy to be using 11MB of ram... per port!

Even one of the docker-proxy processes is using more than my znc server,and there are 3 ports forwarded from it!

Docker default bridge network makes networking very slow #11911

The user land proxy is going to be removed.Since removing the user land proxy will fix this problem,I'm going to close this in flavor of #11185 because the solution to #11185 is the complete removal of the user land proxy.

直接使用宿主机网络

[root@node1 ~]# docker run --rm=true --net=host --name=mysqlserver -e MYSQL_ROOT_PASSWORD=smoke520 mysql
[root@node1 ~]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1024/sshd
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1444/master
tcp6       0      0 :::33060                :::*                    LISTEN      49883/mysqld
tcp6       0      0 :::3306                 :::*                    LISTEN      49883/mysqld
tcp6       0      0 :::22                   :::*                    LISTEN      1024/sshd
tcp6       0      0 ::1:25                  :::*                    LISTEN      1444/master
[root@node1 ~]# docker inspect mysqlserver
            "Networks": {
                "host": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "12977d1d7d74463b9d8a91ae746d30d051fbcfe77faf10b90711e40d67516bbe",
                    "EndpointID": "6d037354d2fc875619a7d660815b3ce365c3c641b69046a464c2214a50422d1f",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "",
                    "DriverOpts": null
                }
            }

容器公用一个IP网络

[root@node1 ~]# docker run --rm=true --name=mysqlserver -e MYSQL_ROOT_PASSWORD=smoke520 mysql
[root@node1 ~]# docker run --rm=true --net=container:mysqlserver java ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
209: eth0@if210: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@node1 ~]# docker inspect mysqlserver
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "2bd1ee6d1375a84d109f5b8945ebf15d5ddd0c0aad69ef8005a32b5ef74d4b06",
                    "EndpointID": "207dcd5d4018cf68e36754ca6dfafdbce3678b0d423fbedef9ab6f0c6054cf69",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }

同一个IP怎么访问

[root@node1 ~]# docker run --rm=true --net=container:mysqlserver java curl localhost:3306
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   104    0   104    0     0    815      0 --:--:-- --:--:-- --:--:--   825
7▒▒▒▒▒%t=m_&imGcaching_sha2_password▒Got packets out of order[root@node1 ~]#

 目前更为复杂的主流方向

docker容器的IP地址能够被另外主机所访问