本章内容包括:
- 如何制作一个镜像
- Docker基础操作
- 加入一个容器的本质
- 数据卷原理
在前面三次分享中,已经介绍了Linux Namespace的隔离能力,Linux Cgroups的限制能力,以及基于rootfs的文件系统三个角度,剖析了一个Linux容器的核心实现原理。
之所以要强调Linux容器,是因为比如Docker on Mac,以及Windows Docker(Hyper-V实现),实际上是基于虚拟化技术实现的
而在今天的分享中,会通过一个实际案例,对前面的所有内容做一次深入的总结和扩展。希望通过这次的讲解,能够让你更透彻地理解Docker容器的本质。
1 镜像制作
这一次,我要用Docker部署一个用Python编写的Web应用。这个应用的代码部分(app.py)非常简单:
from flask import Flask
import socket
import os
app = Flask(__name__)
@app.route('/')
def hello():
html = "<h3>Hello {name}!</h3>" \ "<b>Hostname:</b> {hostname}<br/>" return html.format(name=os.getenv("NAME", "world"),hostname=socket.gethostname())
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
在这段代码中,我使用Flask框架启动了一个Web服务器,而它唯一的功能是:如果当前环境中有“NAME”这个环境变量,就把它打印在“Hello”之后,否则就打印“Hello world”,最后再打印出当前环境的hostname。
这个应用的依赖,则被定义在了同目录下的requirements.txt文件里,内容如下所示:
$ cat requirements.txt
Flask
而将这样一个应用容器化的第一步,是制作容器镜像。
Docker为你提供了一种便捷的方式制作镜像,叫作Dockerfile,如下所示。
# 使用官方提供的Python开发镜像作为基础镜像
FROM python:2.7-slim
# 将工作目录切换为/app
WORKDIR /app
# 将当前目录下的所有内容复制到/app下
ADD . /app
# 使用pip命令安装这个应用所需要的依赖
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 允许外界访问容器的80端口
EXPOSE 80
# 设置环境变量
ENV NAME World
# 设置容器进程为:python app.py,即:这个Python应用的启动命令
CMD ["python", "app.py"]
通过这个文件的内容,你可以看到Dockerfile的设计思想,是使用一些标准的原语(即大写高亮的词语),描述我们所要构建的Docker镜像。并且这些原语,都是按顺序处理的。
比如FROM原语,指定了“python:2.7-slim”这个官方维护的基础镜像,从而免去了安装Python等语言环境的操作。
另外,在使用Dockerfile时,你可能还会看到一个叫作ENTRYPOINT的原语。实际上,它和CMD都是Docker容器进程启动所必需的参数,完整执行格式是:“ENTRYPOINT CMD”。
但是,默认情况下,