仅仅在2013年左右才发布的Docker,却已经遍布了我的Twitter消息和RSS阅读器。在之前我已经运行过“Hello World”的示例,但我自觉没有能真正理解其内涵,准确地说,我并不了解Docker自身是如何运行的。这一周,我花了很多功夫坐下来集中注意力来思考这个问题。我发现其实Docker既没有那么神秘也没有我所想象的那么复杂。
Docker起源于Linux,并且使用像LXC(即:Linux Container 是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。)的Linux内部组件来实现它的魔法。Docker同样有一个Windows的原生版本(但没有任何人关心)。考虑到旧金山湾区的软件工程师大都使用Mac,那就开始让我们看看Docker是如何安装并运行在OSX上的吧。
一开始的时候,先不要通过brew或是其他包管理工具来安装它。Docker由Go语言编写,这种语言具有编译为无依赖的二进制文件的优点。除此之外这个项目迭代地十分频繁,包管理工具总会出现过时的情况。因此忍一忍,下载二进制文件来手动安装它。
如果你打开终端输入docker --version
,那就说明成功安装了。此时的教程是针对1.5.0版本的。
如果你正试着运行Docker镜像,你也许会得到一个含糊的错误提示:
docker max dial unix /var/run/docker.sock: no such file or directory.
这是因为Docker守护进程并没有在运行。事实上,它没有办法运行在Mac系统上!作为替代,你必须使用boot2docker,它是一个含有Docker守护进程的轻量级虚拟机。然后重新运行二进制安装器(抱歉!)。打开终端运行如下命令:
boot2docker init
boot2docker up
eval "$(boot2docker shellinit)"
docker run ubuntu:14.04 /bin/echo 'Hello world'
这就是你的hello world。让我们一步步来看这到底发生了什么。boot2docker init
在VirtualBox(一款开源虚拟机软件。由德国 Innotek 公司开发,由Sun Microsystems公司出品)创建了一个新的虚拟机。
下一步,boot2docker up
命令会启动这个虚拟机。随后eval "$(boot2docker shellinit)
这一步设置一些环境变量来告诉Docker目前容器的上下文环境。如果你仅仅运行boot2docker shellinit
你会看见有如下输出:
Writing /Users/chase/.boot2docker/certs/boot2docker-vm/ca.pem
Writing /Users/chase/.boot2docker/certs/boot2docker-vm/cert.pem
Writing /Users/chase/.boot2docker/certs/boot2docker-vm/key.pem
export DOCKER_HOST=tcp://192.168.59.104:2376
export DOCKER_CERT_PATH=/Users/chase/.boot2docker/certs/boot2docker-vm
export DOCKER_TLS_VERIFY=1
其中,前三行是运行信息,后三行是打印出的标准输出。
最后一步,docker run ubuntu:14.04 /bin/echo 'Hello world'
实际上是实例化一个新的Docker容器(使用Ubuntu 14.04)同时在里面单独执行一个命令。
容器是Linux沙盒的实例,镜像是由我们定义的序列化文件,容器正是由他们编排而来。Docker的奇妙之处在于镜像是完全可编排的。这个概念一开始我没有能理解,我以为你需要在你的Mac电脑上构建并运行一个镜像,然后才可以在Amazon EC2上运行相同的东西。
其实,你可以在你的Mac上构建镜像,然后使用scp
命令上传到AWS去运行它。你也未必需要手动的去复制他,因为Docker Hub正是为此而存在的。
另外,在Docker容器中使用的Linux发行版并不需要与主机操作系统相匹配。你可以在CentOS的主机里运行Ubuntu,反之亦然。
最后,镜像有内置的分层机制。本质上,你可以利用一个基础镜像在此之上扩展任意多的分层。这是一个强大的优化和抽象,稍后我们将讨论。
这是Python开发人员开始使用Docker的规范教程,但是我无法找到任何相应的文档。以下这是我自己的雪花版本。
首先创建一个叫作flask
的文件夹。我们将在里面创建三个文件。
第一个文件命名为app.py
,这是一个简单的hello world示例Flask应用。
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello World!'
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
然后创建一个requirements.txt
文件来列出Flask的依赖:
Flask==0.10.1
最后,创建你自己的Dockerfile:
FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
EXPOSE 5000
CMD python app.py
让我们花点时间来看看最后这个文件。
FROM
这一行告诉Docker使用的是在公共库中叫做python
的基础镜像,同时使用的标签(类似版本号的分类)是2.7。
ADD
这行语句把我们的代码从当前的目录.
拷贝到Docker容器的Linux实例/code
目录下,并由WORKDIR
设置/code
为工作目录。
RUN
可以被使用多次,它告知Docker在容器第一次启动时需要运行这些命令。同时运行的这些命令会被缓存起来;每次变更只会重新运行被改变的部分。这可能就是我们之前提到的容器分层的缘故。
EXPOSE
告知Docker服务的对外端口号是5000。我们将运行的flask应用会以这个端口号启动。
最后CMD
这一行指定在容器内部作为主守护进程处理的命令。如果你需要多个守护进程,可以查看docker-compose。
如要运行这个例子,执行如下命令:
docker build -t flask-example .
docker run -it -p 5000:5000 -v $(pwd):/code:ro flask-example</pre>
在启动flask应用之前我们可以先打开浏览器页面(输入:localhost:5000
)会弹出“该页无法显示”的错误页,但在启动应用后刷新页面,你就可以看见“Hello World”的文字了。
你需要做的就是创建一个叫做flask-example
的镜像并运行它。你甚至可以在本地的文件系统中编辑你的代码并同步到Docker中运行(多亏了-v
),然后flask会重新启动。
现在让我看看如何在AWS中运行相同容器。首先需要去注册Docker Hub账号。这是免费的。
假如说你的Docker Hub账户名是foobar
,你首先需要重新构建并发布你的镜像:
docker build -t foobar/flask-example .
docker login
docker push foobar/flask-example</pre>
现在创建一个新的EC2实例。确认使用的是“Amazon Linux”的基础镜像,它可以让安装Docker变得更轻松。使用SSH登陆你的实例然后启动docker容器:
sudo yum install -y docker; sudo service docker start
sudo docker run -it -p 8000:5000 foobar/flask-example
第一行很简单地创建了一个Docker并启动了它。第二行从Docker Hub拉取你的镜像(不需要登陆鉴权),使它运行在交互式shell下,然后将内部端口号5000映射到外部8000端口上。
如果你有安全组设置来开放8000端口,你需要在浏览器上开放EC2在当前用户名下的8000端口。
开始时,我犯了尝试使用docker-compose
和docker-machine
这些各自便于配置多服务和多主机的官方插件的错误。由于我发现在开始时对其原理理解的十分模糊,所以我不建议你在有上述基础前就使用这些插件。