原文作者:Chase Seibert
原文地址:https://dzone.com/articles/dockerosx-quickstart-not
Docker(容器技术)直到2013年才开始受到关注,但现在在我的 Twitter 消息流和 RSS 阅读器中似乎到处都是有关 Docker 技术的文章。我曾经尝试学习过它的 “Hello World” 入门示例,但其实我却从未真正地理解 Docker 技术的核心价值和它到底是如何工作的。直到这一周,我才有一些时间好好坐下来将我的注意力放在它的身上。随后我发现,Docker 技术并没有我想象的那么神秘,那么复杂。
Docker 技术原本诞生于 Linux 平台上,并使用 Linux 原生的工具如 LXC Linux 容器工具 来展现它的精妙之处。在 Windows 平台上也有原生的 Windows 版本以供使用。但在被 Mac 系统占领主导地位的软件开发界,还是让我们来看一下如何在 OS X (现 macOS)平台上安装并运行 Docker。
首先,请不要尝试通过 brew
来进行安装,或者是任何其他类似的包管理工具。Docker 本身是使用 Go 语言编写的,有着在编译为二进制文件后,不需要外部依赖即可使用的优点。此外,Docker 项目的开发进展如此之快,以至于包管理工具中的 Docker 版本都已经过时了。因此,请忘掉这些工具,动手 下载二进制版本 进行安装吧!
如果你在 Terminal (终端)中能正确执行 docker --version
的话,那么你已经准备好进行接下来的步骤了。这篇教程中使用的 Docker 版本是 1.5.0
。
如果你现在就尝试启动一个 Docker 镜像的话,你会看到一条类似 docker max dial unix /var/run/docker.sock: no such file or directory
的神秘错误信息。这是因为 Docker 的守护进程还没有运行。但是实际上,它的守护进程没办法在 Mac 上运行!作为替代,你需要使用 boot2docker 这个程序,这是一个微型的虚拟机程序,运行在 VirtualBox 虚拟机软件中,并运行着 Docker 的守护进程。再一次的,请使用二进制安装包来安装 boot2docker 这个程序(我很抱歉你需要这么做!)。
为了启动 boot2docker,打开 Terminal 终端,并执行下述的几条指令。
boot2docker init
boot2docker up
eval "$(boot2docker shellinit)"
docker run ubuntu:14.04 /bin/echo 'Hello world'
上述的指令就是一个简单的 "Hello world" 程序。接下来让我们逐条分析一下这里究竟发生了什么。
boot2docker init
这条指令在 VirtualBox 中初始化创建了一个新的虚拟机。
下一步,boot2docker up
启动了刚刚创建的虚拟机。随后的 eval "$(boot2docker shellinit)"
这条指令的作用是设置好一些环境变量,这些环境变量用于告知 Docker 当前所属容器的上下文信息。如果你直接在 Terminal 内执行 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
前三行只是一些提示性的消息,只有后三行内容会被输出到标准输出 stdout。
最后一条指令 docker run ubuntu:14.04 /bin/echo 'Hello world'
实际上创建了一个新的 Docker 容器(运行 Ubuntu 14.04),并在容器内执行了一行命令 /bin/echo 'Hello world'
。
容器(Container)是小型的沙盒化的 Linux 实例。镜像文件(Image)是经序列化的定义文件,而容器是依附于这些镜像文件生成的。Docker 技术的精妙之处就在于镜像文件是非常容易转移的。而我在最初没有真正感受到这个概念的意义,而是想当然的认为,你需要现在你的 Mac 上制作好镜像文件并运行它,然后在你的亚马逊 EC2 云服务器上另外制作一个镜像文件并运行它。
实际上,你只需要在你的 Mac 上制作好一个镜像文件,然后本质上只需要一条 scp
指令将镜像文件传送到云服务器上运行就可以了。事实上,你甚至不需要手动的执行复制操作,这恰恰是 Docker Hub 容器中枢在行的事情。
此外,在你的 Docker 容器中运行的 Linux 发行版本甚至不需要和宿主机操作系统的版本一致。例如,你可以在一台运行着 CentOS 系统的宿主机上运行一个 Ubuntu 容器,反过来也同样可行。
最后,镜像文件自带有有一个 分层机制。从本质上来说,你可以在一个原始的镜像文件基础上,添加任意数量的对镜像文件的微小修改。这为 Docker 技术带来了强大的优化能力和抽象能力,我们会在稍后再次讲到这一点。
对于想要开始使用 Docker 技术的 Python 开发者来说,这是一个非常经典有实际意义的教程,但到目前为止我还未能通过我找到的文档资料来成功地完成这个例子。以下是我自己完成的一个特殊的 snowflake 版本。
首先,创建一个叫 flask
的文件夹,在这个文件夹内,你将会要创建以下3个文件。
第一个文件叫 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 python:2.7
告诉 Docker 程序从公共仓库里查找一个叫 python
的镜像文件,并使用名字标签为 2.7
的 pyhton
镜像文件(类似于版本号的含义)创建容器。
第二行的 ADD
指令将当前目录下 .
的所有文件,即你的代码,复制到 Docker 容器内的 Linux 实例中的 /code
路径下。第三行的 WORKDIR
同时将 /code
这个目录设置为工作路径。
RUN
这个关键词可以被使用若干次。它的含义是告诉 Docker 在第一次建立容器的时候执行这些代码。需要注意的是,多个 RUN
的步骤实际上是被缓存处理的,这意味着当你之后对一条 RUN
指令做出修改时,只有被修改的指令会被重新运行,这一特性很可能是由我们之前提到的容器分层机制导致的。
EXPOSE
指令告诉 Docker 这个容器向外部开放 5000 端口进行服务。我们稍后也会将 flask 应用运行在同一个端口上。
最后,CMD
指令指明了在容器的主守护进程运行的命令。如果你需要多个守护进程,请参阅 docker-compose 了解更多这方面的资料。
为了运行刚才的示例,你需要执行接下来的这些命令:
open "http://$(boot2docker ip):5000"
docker build -t flask-example .
docker run -it -p 5000:5000 -v $(pwd):/code:ro flask-example
执行完这些命令之后,一个浏览器标签会在 flask 运行之前打开。这时浏览器的内容很可能是一个 “网页无法访问” 的错误界面,但如果你刷新一下当前页面,你就应该能看到 “Hello World!” 这句话。
刚才运行的这些代码创建了一个名为 flask-example
的镜像文件,并随后运行了它。你甚至可以直接在你的本地文件系统内编辑这些代码,这些改动会同步到 Docker 内(参数 -v
起到的作用)并重新启动 flask 。
现在,让我们看一下如何在云服务器上运行之前我们使用的容器。首先,你需要在 Docker Hub 注册一个账号,这一步是免费的。
在这里我们假设你的 Docker Hub 用户名是 foobar
,首先,重新创建并发布你的镜像文件:
docker build -t foobar/flask-example .
docker login
docker push foobar/flask-example
然后,在亚马逊云服务控制台创建一个新的 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 抓取了你的镜像文件(备注:并不需要进行身份验证!),然后在一个交互式命令行内运行它,最后将宿主机 EC2 服务器的外部 8000 端口映射到容器的 5000 端口。
如果你的服务器安全组配置放行了 8000 端口,那么你应该能够用浏览器访问你的云服务器的 8000 端口了。
当我开始着手于学习 Docker 时,我马上就尝试着去阅读 docker-compose
和 docker-machine
的文档并尝试去使用这两个工具,但过早的去研究他们其实是我犯的一个错误。这两个官方插件的功能是能让你在配置 Docker 的多服务和多机器功能时轻松不少。我建议在你刚开始学习的时候不要去深入研究这些工具,直到你能把上面介绍的基础知识都弄清楚。我在初学的时候就因为执着于学习这些工具而导致我没能理解清楚 Docker 技术真正做到的事情是什么。