本文主要谈谈如何使用 Dockerfile 制作一个自己定制的镜像!
现有容器制作成镜像
我们可以将依据运行的容器,通过我们的自定义修改,然后制作成镜像,举个例子,我们运行一个 busybox 的镜像:
docker run -it --name b1 busybox /bin/sh
在内部运行一个死循环:
while true;do echo $(date) > /tmp/123.txt && sleep 5;done &
然后 ctrl + p + q 退出,结果如图:
将该容器目前的状态制作成为镜像:
docker container commit b1 busybox:v1.0
查看镜像:
此时我们使用这个镜像构建容器试试:
可以看到文件得以保留下来,但是进程却没有。
使用 Dockerfile 制作镜像
Dockerfile 其实就是一个配置文件,格式都差不多,主要就是几个关键字的使用,当然,本文这里还只是简单的 Dockerfile 的编写。
其主要的关键字包含以下一些:
关键字 | 说明 |
---|---|
FROM | 基于那个镜像,如:FROM nginx:latest。如果没有基于的镜像则 FROM scratch |
LABEL | 老版本中叫 MAINTAINER,脚本说明,也算是打标签,如:LABEL author="Dylan" email="[email protected]" |
RUN | 你想执行的操作,多个操作建议使用 && 连接,这样能够减少镜像分层,每个 RUN 一层,\ 可以换行 |
WORKDIR | 类似于 linux 的 cd 命令,进入该目录 |
ADD | 添加当前目录的指定文件或者目录到指定目录,如果文件是 tar 压缩文件会自动解压,如果文件是 url 则不会解压 |
COPY | 和 ADD 类似,只是少了解压功能 |
ENV | 设置环境变量,能够被文件调用,如 ENV MYSQL_VERSION 5.7 |
ENTRYPOINT | 容器启动时的命令,一定会执行 |
CMD | RUN 是在构建镜像时执行,CMD 是容器启动后默认的运行命令,但是如果 docker run 的时候后面有命令,如 /bin/sh,则 CMD 不会执行,多个 CMD 也只会执行最后一个。 |
EXPOSE | 服务的端口 |
至于更复杂的写法,可以参照 MySQL 5.7 的官方 Dockerfile:
https://github.com/docker-library/mysql/blob/master/5.7/Dockerfile
Dockerfile 实例
这里以一个 flask 项目代码实战为例:
1. 现在服务器上面新建一个目录用于存放该项目:
mkdir flask-demo cd flask-demo/ vim app.py
内容如下:
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello docker!" if __name__ == "__main__": app.run(port= 5000)
2. 在该目录下新建 Dockerfile 文件:
FROM python:2.7 LABEL author="Dylan" mail="[email protected]" RUN pip install flask COPY app.py /app/ WORKDIR /app/ EXPOSE 5000 CMD ["python", "app.py"]
值得注意的地方:
a. 将文件 COPY 到指定目录的时候,目录后面需要 /,否则文件是重命名为目录最后一级。
b. CMD 运行方式主要有两种,一种是直接完整命令,一种是像这里一样拆分成列表。
3. 构建镜像:
docker image build -t dylan/flask-demo:v1.0 .
这里可能会出现 pip 安装失败的情况,再执行一次就行了。
可以看到每一个 Step 就是一层,所以我们的 Dockerfile 尽量层少一些。
可以看到这个镜像,包括他基于 python 2.7 镜像。
4. 运行容器测试:
docker run -d --name flask-demo-1 dylan/flask-demo:v1.0
我们这里有个关键参数,-d,后台运行,否则该容器会前台命令行运行!
可以见到我们这个容器多了一个 Dockerfile 里面定义的端口选项。
5. 进入容器访问测试:
docker container exec -it flask-demo-1 /bin/sh
结果如图:
可以看到服务能够正常访问到!但是问题在于我们只能在容器内部访问到该地址,而无法外部访问,这肯定不是想要的结果。
后面会谈谈如何让外部也能访问到!