Docker是现在很流行的一种容器技术,可以控制资源利用、环境隔离等。对于个人电脑,可以做到很好的环境隔离,不污染本地环境。于是乎,装上Docker玩一玩吧……
First. 最初的镜像
第一步自然是先安装Docker,blabalabalabala……,OK,安装完成。查看docker信息(请忽略工作目录):
可见,我已经有9个容器了,其中8个在运行中。
那如何下载一个容器呢?比如,我要下载一个ubuntu镜像,先搜索一下有没有:
既然有,那就直接拉下来吧:
OK,看一下现在都有哪些镜像了:
不要关注其他的,重点是红线圈起来的ubuntu。
有了容器之后该当如何?进去参观一下呗:
版本还是挺新的嘛。-i表示重定向输入,-t表示使用哪个tag的镜像,后面/bin/bash表示运行哪个程序。
插播一条笑话:“百度和谷歌有什么区别?”“谷歌可以用来搜索,百度用来测试网是不是通的。”
那试一试网络是不是通的吧:
纳尼?没有ping命令?!算了,那就先生成一个有ping命令的镜像吧:
这个文件是什么意思先不用管,且继续往下看(下面这段中间部分可以跳过不看,只看命令和结果就好了):
$ docker build -t ubuntu:base .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu
---> f7b3f317ec73
Step 2 : RUN apt-get update
---> Running in ce5878848c01
Get:1 http://security.ubuntu.com/ubuntu xenial-security InRelease [102 kB]
Get:2 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
Get:3 http://security.ubuntu.com/ubuntu xenial-security/universe Sources [30.3 kB]
Get:4 http://security.ubuntu.com/ubuntu xenial-security/main amd64 Packages [321 kB]
Get:5 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
Get:6 http://security.ubuntu.com/ubuntu xenial-security/restricted amd64 Packages [12.8 kB]
Get:7 http://security.ubuntu.com/ubuntu xenial-security/universe amd64 Packages [135 kB]
Get:8 http://archive.ubuntu.com/ubuntu xenial-backports InRelease [102 kB]
Get:9 http://archive.ubuntu.com/ubuntu xenial/universe Sources [9802 kB]
Get:10 http://security.ubuntu.com/ubuntu xenial-security/multiverse amd64 Packages [2932 B]
Get:11 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages [1558 kB]
Get:12 http://archive.ubuntu.com/ubuntu xenial/restricted amd64 Packages [14.1 kB]
Get:13 http://archive.ubuntu.com/ubuntu xenial/universe amd64 Packages [9827 kB]
Get:14 http://archive.ubuntu.com/ubuntu xenial/multiverse amd64 Packages [176 kB]
Get:15 http://archive.ubuntu.com/ubuntu xenial-updates/universe Sources [190 kB]
Get:16 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages [676 kB]
Get:17 http://archive.ubuntu.com/ubuntu xenial-updates/restricted amd64 Packages [13.2 kB]
Get:18 http://archive.ubuntu.com/ubuntu xenial-updates/universe amd64 Packages [583 kB]
Get:19 http://archive.ubuntu.com/ubuntu xenial-updates/multiverse amd64 Packages [9816 B]
Get:20 http://archive.ubuntu.com/ubuntu xenial-backports/main amd64 Packages [4929 B]
Get:21 http://archive.ubuntu.com/ubuntu xenial-backports/universe amd64 Packages [2567 B]
Fetched 23.9 MB in 37s (638 kB/s)
Reading package lists...
---> 881ca0887b48
Removing intermediate container ce5878848c01
Step 3 : RUN apt-get install inetutils-ping -y
---> Running in ad887719330b
Reading package lists...
Building dependency tree...
Reading state information...
The following additional packages will be installed:
ifupdown iproute2 isc-dhcp-client isc-dhcp-common libatm1 libdns-export162
libisc-export160 libmnl0 libxtables11 netbase
Suggested packages:
ppp rdnssd iproute2-doc resolvconf avahi-autoipd isc-dhcp-client-ddns
apparmor
The following NEW packages will be installed:
ifupdown inetutils-ping iproute2 isc-dhcp-client isc-dhcp-common libatm1
libdns-export162 libisc-export160 libmnl0 libxtables11 netbase
0 upgraded, 11 newly installed, 0 to remove and 10 not upgraded.
Need to get 1858 kB of archives.
After this operation, 5616 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu xenial/main amd64 libatm1 amd64 1:2.5.1-1.5 [24.2 kB]
Get:2 http://archive.ubuntu.com/ubuntu xenial/main amd64 libmnl0 amd64 1.0.3-5 [12.0 kB]
Get:3 http://archive.ubuntu.com/ubuntu xenial/main amd64 iproute2 amd64 4.3.0-1ubuntu3 [522 kB]
Get:4 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 ifupdown amd64 0.8.10ubuntu1.2 [54.9 kB]
Get:5 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 libisc-export160 amd64 1:9.10.3.dfsg.P4-8ubuntu1.6 [153 kB]
Get:6 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 libdns-export162 amd64 1:9.10.3.dfsg.P4-8ubuntu1.6 [666 kB]
Get:7 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 isc-dhcp-client amd64 4.3.3-5ubuntu12.6 [223 kB]
Get:8 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 isc-dhcp-common amd64 4.3.3-5ubuntu12.6 [105 kB]
Get:9 http://archive.ubuntu.com/ubuntu xenial/main amd64 libxtables11 amd64 1.6.0-2ubuntu3 [27.2 kB]
Get:10 http://archive.ubuntu.com/ubuntu xenial/main amd64 netbase all 5.3 [12.9 kB]
Get:11 http://archive.ubuntu.com/ubuntu xenial/universe amd64 inetutils-ping amd64 2:1.9.4-1build1 [56.9 kB]
debconf: delaying package configuration, since apt-utils is not installed
Fetched 1858 kB in 11s (167 kB/s)
Selecting previously unselected package libatm1:amd64.
(Reading database ... 4764 files and directories currently installed.)
Preparing to unpack .../libatm1_1%3a2.5.1-1.5_amd64.deb ...
Unpacking libatm1:amd64 (1:2.5.1-1.5) ...
Selecting previously unselected package libmnl0:amd64.
Preparing to unpack .../libmnl0_1.0.3-5_amd64.deb ...
Unpacking libmnl0:amd64 (1.0.3-5) ...
Selecting previously unselected package iproute2.
Preparing to unpack .../iproute2_4.3.0-1ubuntu3_amd64.deb ...
Unpacking iproute2 (4.3.0-1ubuntu3) ...
Selecting previously unselected package ifupdown.
Preparing to unpack .../ifupdown_0.8.10ubuntu1.2_amd64.deb ...
Unpacking ifupdown (0.8.10ubuntu1.2) ...
Selecting previously unselected package libisc-export160.
Preparing to unpack .../libisc-export160_1%3a9.10.3.dfsg.P4-8ubuntu1.6_amd64.deb ...
Unpacking libisc-export160 (1:9.10.3.dfsg.P4-8ubuntu1.6) ...
Selecting previously unselected package libdns-export162.
Preparing to unpack .../libdns-export162_1%3a9.10.3.dfsg.P4-8ubuntu1.6_amd64.deb ...
Unpacking libdns-export162 (1:9.10.3.dfsg.P4-8ubuntu1.6) ...
Selecting previously unselected package isc-dhcp-client.
Preparing to unpack .../isc-dhcp-client_4.3.3-5ubuntu12.6_amd64.deb ...
Unpacking isc-dhcp-client (4.3.3-5ubuntu12.6) ...
Selecting previously unselected package isc-dhcp-common.
Preparing to unpack .../isc-dhcp-common_4.3.3-5ubuntu12.6_amd64.deb ...
Unpacking isc-dhcp-common (4.3.3-5ubuntu12.6) ...
Selecting previously unselected package libxtables11:amd64.
Preparing to unpack .../libxtables11_1.6.0-2ubuntu3_amd64.deb ...
Unpacking libxtables11:amd64 (1.6.0-2ubuntu3) ...
Selecting previously unselected package netbase.
Preparing to unpack .../archives/netbase_5.3_all.deb ...
Unpacking netbase (5.3) ...
Selecting previously unselected package inetutils-ping.
Preparing to unpack .../inetutils-ping_2%3a1.9.4-1build1_amd64.deb ...
Unpacking inetutils-ping (2:1.9.4-1build1) ...
Processing triggers for systemd (229-4ubuntu16) ...
Processing triggers for libc-bin (2.23-0ubuntu7) ...
Setting up libatm1:amd64 (1:2.5.1-1.5) ...
Setting up libmnl0:amd64 (1.0.3-5) ...
Setting up iproute2 (4.3.0-1ubuntu3) ...
Setting up ifupdown (0.8.10ubuntu1.2) ...
Creating /etc/network/interfaces.
Setting up libisc-export160 (1:9.10.3.dfsg.P4-8ubuntu1.6) ...
Setting up libdns-export162 (1:9.10.3.dfsg.P4-8ubuntu1.6) ...
Setting up isc-dhcp-client (4.3.3-5ubuntu12.6) ...
Setting up isc-dhcp-common (4.3.3-5ubuntu12.6) ...
Setting up libxtables11:amd64 (1.6.0-2ubuntu3) ...
Setting up netbase (5.3) ...
Setting up inetutils-ping (2:1.9.4-1build1) ...
Processing triggers for libc-bin (2.23-0ubuntu7) ...
Processing triggers for systemd (229-4ubuntu16) ...
---> 6685deaa1939
Removing intermediate container ad887719330b
Step 4 : RUN apt-get install net-tools
---> Running in 7f440dd530ac
Reading package lists...
Building dependency tree...
Reading state information...
The following NEW packages will be installed:
net-tools
0 upgraded, 1 newly installed, 0 to remove and 10 not upgraded.
Need to get 175 kB of archives.
After this operation, 725 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu xenial/main amd64 net-tools amd64 1.60-26ubuntu1 [175 kB]
debconf: delaying package configuration, since apt-utils is not installed
Fetched 175 kB in 1s (94.8 kB/s)
Selecting previously unselected package net-tools.
(Reading database ... 5001 files and directories currently installed.)
Preparing to unpack .../net-tools_1.60-26ubuntu1_amd64.deb ...
Unpacking net-tools (1.60-26ubuntu1) ...
Setting up net-tools (1.60-26ubuntu1) ...
---> ffb26e4fbd2c
Removing intermediate container 7f440dd530ac
Successfully built ffb26e4fbd2c
篇幅太长,只要看到最后的:
就表示成功了。现在看一下都有哪些镜像:
生成的ubuntu:base比ubuntu:latest大了44.4MB,没错,那就是装的ping、ifconfig那些工具占的空间。
书接前文,试一试网络是不是通的吧:
诶,通了不是?
Second. 后台运行容器
那现在问题来了:我如果要运行一个容器,总不能一直占着一个窗口,静静地看着它XX吧?安啦,docker是有解决办法的:
没错,-d,让容器去后台默默地运行吧,docker只相当于一个进程守护程序了。
看一下都有哪些正在运行的容器吧:
那我怎么知道它的运行状态呢?三种办法:
方案一,看日志:
运行docker logs时,后面可以跟sleepy_borg这个随机生成的名字,也可以跟dced458fb1f8这个Container ID,效果是一样的,关于这个名字,后面会提到,这里先立个flag。
方案二:看现在的状态:
但是很不幸,我一个ctrl+c就把这个容器进程给杀了,因为attach的时候把stdin重定向进去了……So,现在docker ps里面没有它了……
如何再把它运行起来呢?
同样,相反的命令结束它:
方案三:进入正在运行的容器:
通过ps,还是能看到正在执行ping命令,只是看不到输出。
这东西只是个Demo,我们看docker ps -a时,还是能看到它:
对,还是占着磁盘空间的,像你我等如此洁癖之人,能不能忍?不能忍!
故删之。
那有没有方便点的办法呢?
对咯,列出所有运行过的容器,全删之,看上面结果可知,正在运行中的容器是删不掉的,只成功删掉一个已经停止运行的ef991e424ea1,所以大可放心。
Third. 创建镜像、运行容器
好了,现在可以干点正事了吧?
所谓工欲善其事,必先利其器,先装个编译器:
是时候说一下Dockerfile里的指令了:(虽然没说指令一定要大写,但规范要求)指令要大写。
FROM <image:tag> 指定从哪个基础容器创建新容器
RUN <command> 运行一条系统命令(容器内系统的命令)
ADD <src> <dst> 从宿主机src复制文件(夹)到容器内dst目录
ENV <key> <value> 设置容器内环境变量
先说这么多,以后用到哪些再说,build一个go编译器的镜像:
本人就是这么个耿直男儿,能贴图,坚决不贴代码,哈哈……
现下,我们有三个ubuntu的镜像了,一个什么都没有的,一个带ping、ifconfig命令的,一个带ping、ifconfig、go编译器的,三个镜像。
编程第一步:Hello world来参见。
10行一个完整的web服务器,我想这牛逼的也是没谁了,没错,你没看错,跟我学golang吧。
EXPOSE <port> 定义端口8080
闲言少叙,build之:
运行起来:
-p <宿主机端口>:<容器端口> 表示端口映射到宿主机端口,让宿主机也可以访问。
访问一下:
光辉亮丽的Hello world出炉了!
Forth. 输入输出
我们知道,一个web程序有各种输入输出,主要分为文件、网络的读写请求,比如配置文件,浏览器访问,数据库等。
所以现下需要:
一、文件读,如配置文件,代码文件等
二、文件写,如日志等
三、外部网络访问,如 GET http://127.0.0.1等
四、内部网络访问,如RPC调用、数据库等
Talk is cheap, show me the example.
eg.1: 文件只读。
同样是Hello world,我们之前是把test.go打包进容器中了,如果我们改了test.go,是不是又要build一次?很麻烦呀,那把test.go挂载上去,每次启动拿到的都是最新代码,不就OK了?
先把Dockerfile改一下:
VOLUME 表示要挂载这个文件(夹)。
把上个程序停了:
启起新的demo2:
-v <src> <dst> 把宿主文件(夹)挂载到容器中文件(夹),注意一定要是绝对路径!后面加"ro"表示"read only",默认为读写权限。
看结果:
现在把代码改一下,在Hello world后面加个“!”(感叹号)以示区分。
重启容器:
刷新页面:
这样,每次改变源文件后,只要重启一下,都是最新的,不用再重复的build工作了。
eg.2: 文件写。
最常见的写文件非log莫属了,而且日志文件不能随意丢弃,以后分析线上问题全靠它呢。
先清理一下环境:
docker stop 4da4974cf8c3
docker rmi -f web:demo web:demo2
docker rm $(docker ps -aq)
改一下test.go,写一下日志输出:
再改一下Dockerfile,多挂载一个log目录:
再build一次:
run起来:
现在挂载了一个文件,一个文件夹,映射了一个端口,每次访问网站,都可以看到:
eg.3 外部网络访问。
现在一直都在用了,外网也可以访问到8080端口,这里不再赘述。
eg.4 内部网络访问。
现在假设有一个基础服务,一个对外网关。
服务程序(就是上面程序改了一下名字):
build and run:
用--name可以指定名称。
网关程序(一个简单的代理程序):
不管三七二十一,直接丢给backend就好了,now,build and run:
--link name:alias 其中name是要连接容器的名字,alias是连接的别名,故上面命令可以改写为--link web-proxy:web-proxy。而它所做的事情就是:
对,就是把要连接的容器名字加到hosts里面,在查找dns域名时可以优先查询到。(注:后面跟的CONTAINER ID本来应该是f28c0fa7d6e7—见上上图,由于本段是后面添加的,容器是重新生成的,所以会看到不同的CONTAINER ID。)
现在访问一下:
测试关停proxy:
同样,再次打开web-proxy便又可以访问了。
Fifth. 未解问题
一、如果我的程序分别部署在不同的机器上呢?
比如,web-backend部署在物理机192.168.1.100上,而web-proxy部署在物理机192.168.1.200上,那跨物理机的docker容器之间如何网络互通呢?
为解决这个问题,coreos提供了一种方案:
对,就是这东西,它的网络模型是这样的:
网上关于flannel的教程有很多,看客可自行搜索,小生这里机器有限,不太好试,只好先在这留个坑了。
这个图很好理解,flannel接管了docker的底层网络,不同物理间数据包可以通过不同flanneld之间传递(仅限局域网)。
->坑<-
二、如何进行服务编排治理呢
当容器多了之后,我总不能还一个一个去启动或替换什么的吧,所以在现在版本的Docker中有了Swarm组件,可以帮你管理众多容器,同样,这里只是占个坑。
->坑<-
Sixth. 总结
最后总结一下,关于用到的docker命令,和Dockerfile指令:
docker info -- 查看docker信息
docker search -- 搜索镜像
--no-trunc -- 不要截断描述信息
docker pull -- 拉取镜像
docker images -- 所有本地镜像
docker run -- 运行容器
-i -- 重定向标准输入
-d -- 以守护进程方式运行
-t -- 运行指定tag的镜像
-p -- 端口映射
-v -- 文件挂载
--name -- 指定名字
--link -- 容器间网络连接
docker build -- 创建新镜像
-t -- 指定镜像名称
-f -- 指定Dockerfile文件
docker logs -- 容器运行日志
docker attach -- 重定向运行容器输入输出
docker exec -- 在运行容器中运行命令
-i -- 重定向标准输入
-t -- 运行指定tag的容器
docker start -- 启动容器
docker stop -- 关闭容器
docker restart -- 重启容器
docker ps -- 所有运行中的容器
-a -- 所有运行中的容器
-q -- 只显示CONTAINER ID列
docker rm -- 删除容器
docker rmi -- 删除镜像
-f -- 强制删除
如果忘了命令,docker --help,如果忘了命令的用法,docker COMMAND --help。
Dockerfile指令:
FROM <image>:<tag> 从哪个镜像创建
ADD <src> <dst> 从宿主机src拷贝到镜像dst中去
ENV <key> <value> 设置镜像内环境变量
RUN command args... 容器内运行命令
EXPOSE port 要映射的端口
VOLUME ["/mount1", "/mount2"] 要挂载的文件/目录
CMD ["COMMAND", "args1", "args2"] 运行容器时运行的命令
容器作为当下时兴技术之一,提供了良好的环境隔离,方便运维部署,保证环境一致性,减少了程序出错机率,值得大家学一学,用一用。即便是个人电脑使用,也可以提供良好的环境隔离,不会污染本地环境,何乐而不为之呢?