玩玩Docker

2017-05-17

偶然的一个机会,听说了Docker,由于没做过后端,一开始只是觉得听起来就很高格调,这要是拿来装装该有多好,尤其是听说,装一些centos和ubuntu镜像,不用下载很庞大的image,而且用docker运行起来,比用virtualbox运行起来快多了。
极客学院docker教程WIKI
于是参照上面的教程,就敲着玩,按着那个教程敲了一遍,但是不知道是我没做过后端的原因,还是docker教程对初学者不是那么友好,算了,还是算我没做过后端好了。总之,就是命令,比着葫芦画瓢,能明白些,但是对于docker的原理,和用来干什么,为什么那么多人追逐,并不是很了解。终于今天看了掘金上的,周楷文的一篇文章,周楷文在程序员界,是青年才俊,之前我知道他,是在大学的时候,通过他写ios开发教程认识的,当时对于iOS并不是很了解,周楷文,写的ios教程,将设计与开发融合成了一本书,而我当时只看了讲设计的部分。重新回来,周楷文的这篇docker教程让人一下就明白了docker是用来干什么的问题。不得不叹服,讲解时,类比思维的重要性。
周楷文docker第一课
我来写下我的理解,首先要理解的三个名词,docker、容器、镜像。
镜像是centos,ubuntu等,为了提供软件所运行的环境。
一个容器对应一个镜像,在一个镜像上,装一个软件,这一个整体,就叫一个容器,有了运行环境,有了软件,那一个容器是为了提供一个服务的。
docker可以有很多容器,有不同的容器,就提供不同的服务,同样,在一个docker中不同容器之间可以利用其他容器的结果。
还有一个概念,就是docker运行宿主机,就是我们平时在电脑上运行一个docker,docker是访问不了我们电脑上的文件的。但是我们电脑却可以访问docker中的文件。docker有自己的端口,我们的电脑也有自己的端口,我们可以把docker的端口和我们电脑上的端口绑定,如果别人用访问我们电脑的端口,就可以访问到我们的docker了。
还有就是文章的结尾处引用的这篇文章

十张图带你深入了解docker容器与镜像

首先贴张总结性的图

容器=镜像+读写层
一个容器运行时,内部是怎样一个过程呢?

top是列出正在运行的进程,一个容器运行一个进程,一个进程可以理解为一个软件。容器直接彼此隔离,这样就避免了运行环境的相互影响。容器可以运行多个进行,就像在ubuntu中装多个软件。

如图,我在内部,直接创建了个文件,你不用管我内部是怎样的了,不要打扰我,直接能给你那个结果就好了。当这个容器不用后,在我们的电脑上也能找到这个文件。这是只生成了一个txt文件,如果是生成一个需要特殊容器才能生成的文件呢?可以想下。

docker start是为容器创建了一个隔离空间。

docker commit命令将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像。
万丈高山起于垒土,一层一层堆基础啊

这个命令是在容器中,开启一个新的进程。
还有一个就是docker save和docker export。docker save是保存镜像,docker export是保存容器。
当看完上面两篇文章,在回看极客学院上的教程,就更好明白了。在补充下。

比较以上两个命令,第一个直接利用利用容器得到了结果,第二个是进入了容器,也就是我们平时理解的进入了虚拟机,只不过没有图形化界面罢了。
-t表示在新容器内指定一个伪终端或终端,-i表示允许我们对容器内的STDIN进行交互。
可以输入exit进行退出。但这样的结果,就是容器停止了,我们在手机上打开网易云音乐的时候退出,可能更多时候是想一边听歌,一边玩微信,让网易云音乐到后台去提供服务。这时候就需要类似

-d是告诉容器,让其后台运行。
还有就是另一个重要的命令

形象化一点,就是这个图

这个命令是查看正在运行的容器,已关闭的容器查看,还要其他命令。还有许多命令,但掌握了原理,就在脑中有了形象,在去了解其他命令,就好理解了很多。

Docker基础命令

我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:14.04 就包含了完整的一套 Ubuntu 14.04 最小系统的 root 文件系统。

Docker基础命令

查看Docker信息

1
docker info

下载镜像

1
docker pull busybox

BusyBox是一个最小的Linux系统
运行一个带标签镜像的容器

1
sudo docker run -t -i ubuntu:14.04 /bin/bash

跑个Hello World

1
docker run busybox /bin/echo Hello World

构建容器

1
sample_job=$(docker run -d busybox /bin/sh -c "while true;do echo Docker;sleep 1;done")

其中的-d是放在后台执行
查看日志

1
docker logs $sample_job

名为sample_job的容器,停止命令是

1
docker stop $sample_job

重启该容器

1
docker restart $sample_job

要将容器删除,需要先将容器停止,容器删除命令为:

1
docker rm $sample_job

将容器的状态保存为新的镜像的方法为:

1
docker commit

查看镜像

1
docker images

搜索镜像

1
docker search (image-name)

查看镜像的历史版本

1
docker history (image-name)

将镜像推送到registry

1
docker push (image-name)

查看我们正在运行的容器

1
docker ps -l

端口映射

1
docker run -d -p 5000:5000 training/webapp python app.py

移除镜像

1
docker rmi(镜像名称或者id前3位)
小例子

命令

1
docker run -d -p 80:80 --name webserver nginx

这条命令会用 nginx 镜像启动一个容器,命名为 webserver,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器。

现在,假设我们非常不喜欢这个欢迎页面,我们希望改成欢迎 Docker 的文字,我们可以使用 docker exec 命令进入容器,修改其内容。

1
2
3
4
$ docker exec -it webserver bash
root@3729b97e8226:/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit
exit

我们修改了容器的文件,也就是改动了容器的存储层。我们可以通过 docker diff 命令看到具体的改动。

1
$ docker diff webserver

现在我们定制好了变化,我们希望能将其保存下来形成镜像。

Docker commit

要知道,当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
我们可以用下面的命令将容器保存为镜像:

1
2
3
4
5
$ docker commit \
--author "Tao Wang <twang2218@gmail.com>" \
--message "修改了默认网页" \
webserver \
nginx:v2
1
docker run --name web2 -d -p 81:80 nginx:v2

这里我们命名为新的服务为 web2,并且映射到 81 端口。

慎用 docker commit

首先,如果仔细观察之前的 docker diff webserver 的结果,你会发现除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。

此外,使用 docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然 docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。

1
2
docker stop webserver
docker rm webserver
运行一个带标签镜像的容器
1
sudo docker run -t -i -rm ubuntu:14.04 /bin/bash

-it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
-rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 –rm 可以避免浪费空间。
ubuntu:14.04:这是指用 ubuntu:14.04 镜像为基础来启动容器。
bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。
要想列出已经下载下来的镜像,可以使用 docker images 命令。

Docker images的过滤
1
$ docker images ubuntu

镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个标签。

如果仔细观察,会注意到,这里标识的所占用空间和在 Docker Hub 上看到的镜像大小不同。比如,ubuntu:16.04
镜像大小,在这里是 127 MB
,但是在 Docker Hub 显示的却是 50 MB
。这是因为 Docker Hub 中显示的体积是压缩后的体积。

docker images 还支持强大的过滤器参数 –filter,或者简写 -f。我们希望看到在 mongo:3.2 之后建立的镜像,可以用下面的命令

1
$ docker images -f since=mongo:3.2

想查看某个位置之前的镜像也可以,只需要把 since 换成 before 即可。
文章参考:
Docker入门实战
极客学院
Docker从入门到实践