通过先前的几篇博客, 我应该将Docker的启动关闭以及运行调试的各种操作介绍完了,
从本节开始的两节, 会着重介绍Dockerfile的编写. 此小节会介绍一些基础内容.
基础镜像的选择问题
有关基础镜像的一些说明, 请查看我先前的博客Docker基础镜像tag含义及选择.
个人看法:
如果一个项目已经使用了Docker, 并且依赖于基础镜像, 那么还是保持现状比较好
新的项目, 我觉得考虑一下alpine也是可以的, 你可以按需安装软件, 可以用最少的
依赖让项目跑起来, 而且如果你去Docker hub上翻看基础镜像, 几乎每个基础镜像
下面的tag都会有几个是用alpine构建的.
我在业余的时候也会写写golang, 其实我觉得这个语言本身就可以跑在不同的发行版上
使用Docker的时候完全是为了隔离宿主机环境, 使用alpine也是合适的.
暂时没有考虑使用slim.
官方文档 - Dockerfile编写最佳实践
建议大家至少也去看看官方文档中的Best practices for writing Dockerfiles.
Docker的build操作, 是以层为概念的, Dockerfile中的每个语句会成为镜像中一个只读的层.
一个好消息就是, 合理的组织层结构可以有效复用先前的层, 以下面的语句为例.
演示构建时的缓存
1 2 3 4 5 6
| FROM python:3.7-alpine
COPY requirements.txt requirements.txt RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ COPY main.py main.py ENTRYPOINT ["python", "main.py"]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import tornado.ioloop import tornado.web
class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world")
def make_app(): return tornado.web.Application([ (r"/", MainHandler), ])
if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
|
第一次构建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| Sending build context to Docker daemon 4.096kB Step 1/5 : FROM python:3.7-alpine ---> b11d2a09763f Step 2/5 : COPY requirements.txt requirements.txt ---> 2936fbbc42a0 Step 3/5 : RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ ---> Running in ac062d0f6a21 Looking in indexes: https://mirrors.aliyun.com/pypi/simple/ Collecting tornado==6.0.3 Downloading https://mirrors.aliyun.com/pypi/packages/30/78/2d2823598496127b21423baffaa186b668f73cd91887fcef78b6eade136b/tornado-6.0.3.tar.gz (482kB) Building wheels for collected packages: tornado Building wheel for tornado (setup.py): started Building wheel for tornado (setup.py): finished with status 'done' Created wheel for tornado: filename=tornado-6.0.3-cp37-cp37m-linux_x86_64.whl size=410711 sha256=80949481cc31a3ea333993961965fb109ad338102d14e8dd7715c55e31714cbc Stored in directory: /root/.cache/pip/wheels/df/a7/02/7a4c8f28d662723d5f5cc490138d18000b059e197ada8851b3 Successfully built tornado Installing collected packages: tornado Successfully installed tornado-6.0.3 Removing intermediate container ac062d0f6a21 ---> 21428e5cf880 Step 4/5 : COPY main.py main.py ---> 400b8bb0856f Step 5/5 : ENTRYPOINT ["python", "main.py"] ---> Running in f7bb65ee7c0d Removing intermediate container f7bb65ee7c0d ---> 04441e7b5051 Successfully built 04441e7b5051 Successfully tagged test:latest
|
假如我们修改了main.py
再次构建时, 可以发现Step 3/5以及之前的语句显示的是Using cache
, 说明缓存被复用了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Sending build context to Docker daemon 4.096kB Step 1/5 : FROM python:3.7-alpine ---> b11d2a09763f Step 2/5 : COPY requirements.txt requirements.txt ---> Using cache ---> 2936fbbc42a0 Step 3/5 : RUN pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ ---> Using cache ---> 21428e5cf880 Step 4/5 : COPY main.py main.py ---> 2e9bc4b4f879 Step 5/5 : ENTRYPOINT ["python", "main.py"] ---> Running in 40991f958270 Removing intermediate container 40991f958270 ---> a38a8b6cb396 Successfully built a38a8b6cb396 Successfully tagged test:latest
|
FAQ
如果大家对Dockerfile有任何问题, 可以在评论中写, 我会继续更新这部分的内容.
apt语句为什么要合并
官方一直推荐的apt-get书写形式是类似下面这样的, 为什么apt-get update
要和后面的语句写到一起.
1 2 3 4 5 6
| RUN apt-get update && apt-get install -y \ bzr \ cvs \ git \ mercurial \ subversion
|
我简要说明一下:
apt-get update的主要作用是去源上爬取数据库, 这份数据库存储了apt-get可以安装的软件,
并且包含了下载地址. 那么, 这份数据库其实是会随时间变化的, 那么也就是说,
如果apt-get update
与install
语句分开了, 类似下面这样:
1 2 3 4 5 6 7
| RUN apt-get update RUN apt-get install -y \ bzr \ cvs \ git \ mercurial \ subversion
|
本身这样写是没有关系的, 你仍然可以正常构建. 但是, 当你想要安装新的软件时,
例如增加了vim:
1 2 3 4 5 6 7 8
| RUN apt-get update RUN apt-get install -y \ bzr \ cvs \ git \ mercurial \ subversion \ vim
|
那么apt-get update
会因为这一层的语句没有变化, 而不会执行. 因此install
语句就有可能使用了旧的软件数据,
甚至下载地址都已经失效了, 这就会造成构建时的失败. 请一定要将install
与update
语句合并在一起写.