您的位置:首页 > 运维架构 > Docker

02docker简单使用和配置(网络、存储和Hub)

2016-03-19 19:20 821 查看
四:网络
1:命名容器
在各种docker命令中,可以通过名字中找到对应的容器。之前创建的容器都是由docker自动命名的,可以在docker run中,通过--name参数指定容器的名字。比如:
$ docker run -d -P --name web training/webapp python app.py
$ docker ps -l
CONTAINER ID  IMAGE                  COMMAND        CREATED       STATUS       PORTS                    NAMES
aed84ee21bde  training/webapp:latest python app.py  12 hours ago  Up 2 seconds 0.0.0.0:49154->5000/tcp  web


容器的名字必须是唯一的。因此只能有一个容器叫做web,如果想再次使用这个名字,必须删除之前的容器。

2:在默认网络上运行容器
docker默认提供两种网络驱动,分别是bridge和overlay。docker允许自己写网路驱动插件,不过这属于高级话题了,暂不讨论。
安装docker引擎后自动包含了三种默认的网络:
[hh_93_197 ~]# docker network ls
NETWORK ID          NAME                DRIVER
57f0a920665a        none                null
e8fad866218e        host                host
4948e842e58f        bridge              bridge


除非在docker run中特意指定,否则docker总是将容器运行在bridge网络上,比如:
[hh_93_197 ~]# docker run -itd --name=networktest ubuntu
df38005e240bcd0a2502f1c47b8b2d1b31059f1b7c75f0522006cd0b986e3916


使用docker network inspect命令看一下默认的bridge网络:
[hh_93_197 ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "4948e842e58f3fedd25afe97bf292d53d7023ddeb90fefb7825103b56c5251b8",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16"
}
]
},
"Containers": {
"df38005e240bcd0a2502f1c47b8b2d1b31059f1b7c75f0522006cd0b986e3916": {
"Name": "networktest",
"EndpointID": "ee6b49e5c03ca2bd61f2d2b46c45c9067ae5ccf098bb4c14beaeeb64fde8bb5f",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
}
}
]


可见该网络使用的网段是172.17.0.0/16,容器networktest是attach到该网络上的唯一容器,其IP地址为172.17.0.2。主机上的docker0就是该网络上的网桥。
下面,进入容器networktest,用ifconfig看一下它的IP地址,确实是172.17.0.2。
[hh_93_197 ~]# docker attach networktest
root@df38005e240b:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:02
inet addr:172.17.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
inet addr:127.0.0.1  Mask:255.0.0.0
UP LOOPBACK RUNNING  MTU:65536  Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


可以将容器从一个网络中移除(断开),比如在另一个终端上执行下面的命令:
[hh_93_197 ~]# docker network disconnect bridge networktest


此时,在容器中查看网络配置:
root@df38005e240b:/# ifconfig
lo        Link encap:Local Loopback
inet addr:127.0.0.1  Mask:255.0.0.0
UP LOOPBACK RUNNING  MTU:65536  Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


网络是隔离容器最合适的方法。可以创建自己的网络:

3:创建自己的网络
bridge网络将容器限制在运行dokcer引擎的主机上,而overlay网络则可以跨越多个主机(高级话题)。
可以创建自己的bridge网络:
[hh_93_197 ~]# docker network create -d bridge my-bridge-network
c6198f9bb78a4275088d7f06116ef839871ca7aa1dbbd7b120f1187df4224cb4
[hh_93_197 ~]# docker network ls NETWORK ID NAME DRIVER 57f0a920665a none null e8fad866218e host host 4948e842e58f bridge bridgec6198f9bb78a my-bridge-network bridge


使用-d选项,表明新创建的网络使用bridge驱动。分别使用docker network inspect 和ifconfig看一下:
[hh_93_197 ~]# docker network inspect my-bridge-network
[
{
"Name": "my-bridge-network",
"Id": "c6198f9bb78a4275088d7f06116ef839871ca7aa1dbbd7b120f1187df4224cb4",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1/16"
}
]
},
...
[hh_93_197 ~]# ifconfig
...

br-c6198f9bb78a: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
inet 172.18.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
ether 02:42:81:54:91:a3  txqueuelen 0  (Ethernet)
RX packets 0  bytes 0 (0.0 B)
RX errors 0  dropped 0  overruns 0  frame 0
TX packets 0  bytes 0 (0.0 B)
TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
ether 02:42:c6:77:db:9e  txqueuelen 0  (Ethernet)
RX packets 107  bytes 7532 (7.3 KiB)
RX errors 0  dropped 0  overruns 0  frame 0
TX packets 135  bytes 11825 (11.5 KiB)
TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

...

可见br-c6198f9bb78a就是新创建的my-bridge-network中的网桥。

4:网络隔离
下面分别将两个容器加入到两个独立的网络中查看一下,首先将一个基于ubuntu镜像的容器加入到新创建的网络中:
[hh_93_197 ~]# docker run -itd --net=my-bridge-network --name testnetwork ubuntu
559048438ae5972c8c4e578f63195d14d4c7a155e9306630f3eff6d9d18fcb2c
[hh_93_197 ~]# docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' testnetwork
172.18.0.2

可见该容器的IP地址为172.18.0.2。接下来再创建一个web容器,不指定任何网络参数,因此该容器会attach到默认的bridge网络上:

[hh_93_197 ~]# docker run -d --name web training/webapp python app.py
226dc5d3a98a9d2b009e870462c7f072180c98ba5a1f6144b5cff4e15b0a1638
[hh_93_197 ~]# docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
172.17.0.2


可见该容器的IP地址为172.17.0.2。两个容器附加到不同的网络上,相当于已经隔离了。进入testnetwork容器,在该容器中ping一下172.17.0.2,ping不通:
[hh_93_197 ~]# docker exec -it testnetwork bash
root@559048438ae5:/# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
^C
--- 172.17.0.2 ping statistics ---
21 packets transmitted, 0 received, 100% packet loss, time 19999ms


docker允许将一个容器attach到任意多的网络上。下面,在另一个终端上,将web容器attach到my-bridge-network上:
[hh_93_197 ~]# docker network connect my-bridge-network web


此时,在testnetwork容器中ping一下web容器:
root@559048438ae5:/# ping web
PING web (172.18.0.3) 56(84) bytes of data.
64 bytes from web.my-bridge-network (172.18.0.3): icmp_seq=1 ttl=64 time=0.105 ms
64 bytes from web.my-bridge-network (172.18.0.3): icmp_seq=2 ttl=64 time=0.046 ms
...


说明web已经和testnetwork处于同一个网络上了。其实,web容器目前是分别attach到my-bridge-network和bridge两个网络上,具有两个IP地址,分别是172.17.0.2和172.18.0.3。

五:存储
1:数据卷(Data Volumes)
数据卷用于持久化数据以及共享数据,它具有以下功能:
a:当容器创建时会初始化卷。如果容器的基础镜像在特定挂载点上包含数据,则这些数据会在卷初始化时复制到新卷中(当挂载宿主机目录时不会这么做)。
b:数据卷可以在多个容器间共享和重用;
c:数据卷上的改变,是立即可见的;
d:当升级镜像时,不会包含数据卷中修改的内容;
e:容器被删除时,数据卷依然存在。
数据卷用于持久化数据,并且独立于容器的生命周期。因此即使删除容器时,Docker也不会自动的删除卷,也不会在没有容器引用一个卷时,将其清理。

2:增加一个数据卷
在docker create和docker run中使用-v选项可以在创建容器时增加数据卷,也可以使用多个"-v"选项挂载多个数据卷。
例子如下:
[hh_93_197 ~]# docker run -it --name testvolume2 -v /dev ubuntu /bin/bash
root@aa644c48392b:/# ls /dev
agpgart   core  kmem   loop6   midi03  mixer3      ram    ram14  ram6    rmidi2     smpte3   tty1  tty8
audio     dsp   loop0  loop7   midi1   mpu401data  ram0   ram15  ram7    rmidi3     sndstat  tty2  tty9
audio1    dsp1  loop1  mem     midi2   mpu401stat  ram1   ram16  ram8    sequencer  stderr   tty3  urandom
audio2    dsp2  loop2  midi0   midi3   null        ram10  ram2   ram9    shm        stdin    tty4  zero
audio3    dsp3  loop3  midi00  mixer   port        ram11  ram3   random  smpte0     stdout   tty5
audioctl  fd    loop4  midi01  mixer1  ptmx        ram12  ram4   rmidi0  smpte1     tty      tty6
console   full  loop5  midi02  mixer2  pts         ram13  ram5   rmidi1  smpte2     tty0     tty7


可见,直接在容器中创建了/dev这个数据卷,而且/dev是ubuntu镜像原有的挂载点,其中的内容也已经复制到这个数据卷了。
在容器中,进入/dev目录下,新建readme.txt文件,内容是:”thisis data volume in container”。

在宿主机上,使用docker inspect命令查看该容器,得到信息如下:
…
"Mounts": [
{
"Name": "35aa73994cc70828d861620dca03fd1e91699ff46d1b560b6e3ded6128b1e429",
"Source": "/var/lib/docker/volumes/35aa73994cc70828d861620dca03fd1e91699ff46d1b560b6e3ded6128b1e429/_data",
"Destination": "/dev",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
…


因此,容器中的数据卷,对应着宿主机上的目录是/var/lib/docker/volumes/35aa73994cc70828d861620dca03fd1e91699ff46d1b560b6e3ded6128b1e429/_data,查看该目录的内容:
[hh_93_197 /var/lib/docker/volumes/35aa73994cc70828d861620dca03fd1e91699ff46d1b560b6e3ded6128b1e429/_data]# ls
agpgart   core  kmem   loop6   midi03  mixer3      ram    ram14  ram6        rmidi1     smpte2   tty0  tty7
audio     dsp   loop0  loop7   midi1   mpu401data  ram0   ram15  ram7        rmidi2     smpte3   tty1  tty8
audio1    dsp1  loop1  mem     midi2   mpu401stat  ram1   ram16  ram8        rmidi3     sndstat  tty2  tty9
audio2    dsp2  loop2  midi0   midi3   null        ram10  ram2   ram9        sequencer  stderr   tty3  urandom
audio3    dsp3  loop3  midi00  mixer   port        ram11  ram3   random      shm        stdin    tty4  zero
audioctl  fd    loop4  midi01  mixer1  ptmx        ram12  ram4   readme.txt  smpte0     stdout   tty5
console   full  loop5  midi02  mixer2  pts         ram13  ram5   rmidi0      smpte1     tty      tty6
[hh_93_197 /var/lib/docker/volumes/35aa73994cc70828d861620dca03fd1e91699ff46d1b560b6e3ded6128b1e429/_data]# cat readme.txt
this is data volume in container


可见,readme.txt文件已经在宿主机上可见了。反之也成立。在宿主机上的该目录下创建文件readme2.txt,在容器中也是立即可见的。

使用该容器制作新镜像,不会包含数据卷中的新增内容:
[hh_93_197 ~]# docker commit -m "test data volume" -a "hh" testvolume2  hh/ubuntu:volume2
sha256:1c4275b3ac33ee0c4e64ea533d836ee407369de7a1c604e9933b997e22027ff1
[hh_93_197 ~]# docker run -ti hh/ubuntu:volume2 /bin/bash
root@f6987d519b9a:/# ls /dev
agpgart   core  kmem   loop6   midi03  mixer3      ram    ram14  ram6    rmidi2     smpte3   tty1  tty8
audio     dsp   loop0  loop7   midi1   mpu401data  ram0   ram15  ram7    rmidi3     sndstat  tty2  tty9
audio1    dsp1  loop1  mem     midi2   mpu401stat  ram1   ram16  ram8    sequencer  stderr   tty3  urandom
audio2    dsp2  loop2  midi0   midi3   null        ram10  ram2   ram9    shm        stdin    tty4  zero
audio3    dsp3  loop3  midi00  mixer   port        ram11  ram3   random  smpte0     stdout   tty5
audioctl  fd    loop4  midi01  mixer1  ptmx        ram12  ram4   rmidi0  smpte1     tty      tty6
console   full  loop5  midi02  mixer2  pts         ram13  ram5   rmidi1  smpte2     tty0     tty7


当删除上面的容器后,宿主机上的数据卷目录依然存在:
[hh_93_197 /var/lib/docker/volumes/35aa73994cc70828d861620dca03fd1e91699ff46d1b560b6e3ded6128b1e429/_data]# ls
agpgart   core  kmem   loop6   midi03  mixer3      ram    ram14  ram6         rmidi0     smpte1   tty   tty6
audio     dsp   loop0  loop7   midi1   mpu401data  ram0   ram15  ram7         rmidi1     smpte2   tty0  tty7
audio1    dsp1  loop1  mem     midi2   mpu401stat  ram1   ram16  ram8         rmidi2     smpte3   tty1  tty8
audio2    dsp2  loop2  midi0   midi3   null        ram10  ram2   ram9         rmidi3     sndstat  tty2  tty9
audio3    dsp3  loop3  midi00  mixer   port        ram11  ram3   random       sequencer  stderr   tty3  urandom
audioctl  fd    loop4  midi01  mixer1  ptmx        ram12  ram4   readme2.txt  shm        stdin    tty4  zero
console   full  loop5  midi02  mixer2  pts         ram13  ram5   readme.txt   smpte0     stdout   tty5


3:挂载宿主机目录到容器中
使用-v选项,还可以将宿主机上的目录,挂载到容器中:
[hh_93_197 ~]# docker run -it -v /host/foo:/opt/foo ubuntu /bin/bash
root@0235be2f412d:/# ls /opt
foo


以上的命令,就将宿主机上的/host/foo目录,挂载到容器中的/opt/foo中了。
在宿主机中/host/foo创建文件,容器中/opt/foo会立即可见;容器中/opt/foo创建的文件,宿主机中/host/foo也是立即可见的。

如果宿主机目录/host/foo不存在,则docker会自动创建。如果容器中的目录/opt/foo中原先已存在且包含文件,则挂载宿主机目录后,/opt/foo中的内容不会被删除,只是不可见了,一旦解除mount,则/opt/foo中的内容会恢复(这事mount的特点)。
在-v选项中,容器目录必须是绝对路径的形式,宿主机目录可以是一个绝对路径,也可以是一个名字,使用名字时,docker会使用改名字创建一个命名卷。

默认情况下,docker使用的是读写挂载,可以指定只读挂载,这种情况下,容器中就不能修改相应目录的内容了:
[hh_93_197 ~]# docker run -it -v /host/foo:/opt/foo:ro ubuntu /bin/bash
root@b1ba14479947:/# cd /opt/foo/
root@b1ba14479947:/opt/foo# touch readme2.txt
touch: cannot touch 'readme2.txt': Read-only file system


注意,因为挂载主机目录是依赖于特定主机的,因此,不允许在Dockerfile中挂载主机目录,因为创建的镜像必须是可移植的,包含主机目录挂载的镜像不能应用到其他主机上。

除了可以挂载目录之外,也可以将主机的文件挂载到容器中:
[hh_93_197 ~]# docker run --rm -it -v ~/proxy_on:/root/proxy_on ubuntu /bin/bash
root@2305c0b5e1b4:/# cat /root/proxy_on
export https_proxy=http://19.28.20.22:1888
export http_proxy=http://12.28.20.22:8000


4:创建并挂载一个数据卷容器(数据卷共享)
可以将数据卷在多个容器中共享。比如下面先创建第一个容器sd1,其中挂载了数据卷/sharedata:
[hh_93_197 ~]# docker run -it -v /sharedata --name sd1 ubuntu


然后在docker run中,使用--volumes-from参数,创建新容器,并把sd1中的/sharedata挂载到新容器中:
[hh_93_197 ~]# docker run -it --volumes-from sd1 --name sd2 ubuntu
[hh_93_197 ~]# docker run -it --volumes-from sd2 --name sd3 ubuntu


此时,sd1、sd2和sd3这三个容器都具有数据卷/sharedata,而且是共享的。在其中任意一个容器中的/sharedata中修改内容,都可以在其他容器中立即可见。
删除容器sd1、sd2或者sd3时,该数据卷并不会被删除。可以在docker rm命令中使用-v参数,当最后一个关联数据卷的容器删除时,相应的数据卷也会被删除。
[hh_93_197 ~]# docker rm -v sd1
sd1
[hh_93_197 ~]# docker rm -v sd2
sd2
[hh_93_197 ~]# docker rm -v sd3
sd3


这样删除sd3之后,数据卷也就相应的被删除了。
如果不指定-v选项,则当一个数据卷的所有关联容器都被删除之后,该数据卷就成了” dangling”卷。可以使用命令docker volume ls -f dangling=true列出所有” dangling”卷,并使用命令dockervolume rm <volume name>删除即可。

5:数据备份、迁移和恢复
使用--volumes-from参数,可以实现数据备份、迁移和恢复的功能。例子如下:
首先创建一个挂载数据卷/sharedata的容器,然后在/sharedata中创建新文件,当做需要备份的数据,然后退出:
[hh_93_197 ~]# docker run -it -v /sharedata --name sd1 ubuntu
root@c62d94cb3c91:/# cd /sharedata/
root@c62d94cb3c91:/sharedata# echo "hello world" >> readme.txt
root@c62d94cb3c91:/sharedata# cat readme.txt
hello world
root@c62d94cb3c91:/sharedata# exit
exit


然后使用--rm选项,创建一个临时容器。--rm选项可以在容器停止运行后立即将其删除。
该临时容器通过--volumes-from参数,与sd1共享/sharedata目录,并且挂载宿主机目录,运行tar命令压缩备份/sharedata内容,将备份数据存放到宿主机目录中:
[hh_93_197 ~]# docker run --rm --volumes-from sd1 -v /root/testbackup:/backup ubuntu tar cvf /backup/backup.tar /sharedata
tar: Removing leading `/' from member names
/sharedata/
/sharedata/readme.txt


接下来,创建需要恢复数据的容器sd2:
[hh_93_197 ~]# docker run -ti -v /sharedata --name sd2 ubuntu /bin/bash
root@0d44aee764fa:/# ls /sharedata/


目前其/sharedata目录是空的,然后再创建一个临时容器,将数据恢复到sd2中:
[hh_93_197 /host/foo]# docker run --rm --volumes-from sd2 -v /root/testbackup:/backup ubuntu bash -c "cd /sharedata && tar xvf /backup/backup.tar --strip 1"
sharedata/readme.txt


此时,在sd2中查看/sharedata,数据已经恢复了:
root@0d44aee764fa:/# cat /sharedata/readme.txt
hello world


注意,使用共享数据卷时,当多个容器同时修改共享卷中的同一个文件时,有可能会造成数据损坏。
而且,数据卷在宿主机上也是可见的,如果宿主机和容器同时修改同一个文件,也可能会造成数据损坏。

六:Docker镜像库
DockerHub(hub.docker.com)是由Docker公司维护的镜像库。可以通过docker search,pull,login和push命令与Docker Hub交互。
dockerlogin用于向Docker Hub注册用户;
dockersearch用于在Docker Hub搜索镜像;
dockerpull用于下载镜像;
dockerpush用于上传镜像;

除了Docker Hub之外,还可以从其他仓库下载镜像,从其它仓库下载时需要指定完整的仓库注册服务器地址。比如:
[hh_93_197 ~]# docker pull registry.aliyuncs.com/ddbmh/redis
Using default tag: latest
latest: Pulling from ddbmh/redis
80ab95908a2b: Pull complete
a3ed95caeb02: Pull complete
47a0d79f89b9: Pull complete
7190081b1686: Pull complete
fe09c22d81ac: Pull complete
a5eae2bcc645: Pull complete
662723161f77: Pull complete
b568670a8ccd: Pull complete
a1a961e320bc: Pull complete
Digest: sha256:68a1aa9675f25e77d97fe9436dececd29be5ba6eae4ce04169288da5bfe9096b
Status: Downloaded newer image for registry.aliyuncs.com/ddbmh/redis:latest
[hh_93_197 ~]# docker images
REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
...
registry.aliyuncs.com/ddbmh/redis   latest              a70b69767606        7 months ago        109.3 MB


https://docs.docker.com/engine/userguide/containers/networkingcontainers/
https://docs.docker.com/engine/userguide/containers/dockervolumes/
https://docs.docker.com/engine/userguide/containers/dockerrepos/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: