如何利用Dockerfile,从零开始制作一个docker镜像?
发布于 作者:苏南大叔 来源:程序如此灵动~如何从零开始,制作一个docker
镜像呢?如果可以自定义一个docker
镜像,就可以封装自己想要的功能和版本。这个是一件令人振奋的事情。那么,本文中,苏南大叔介绍的就是:如何定义并使用Dockerfile
文件,并最终封装一个docker镜像。
本文中,所使用的具体代码例子,来源自网络。镜像的具体的功用是:安装一个基于python
的框架flask
,对外提供www
服务。这个例子,在网络上还是很流行的。苏南大叔就根据自己的理解,对这个例子进行一下解读。
整个套路是这样的:新建一个Dockerfile
文件。在文件里,定义编写特定的功能脚本。最后在当前目录下面,执行命令docker build -t <imgname> .
,就可以得到一个自定义的docker
镜像了。
本文的测试环境是:centos7.5
,docker 18.06.1-ce, build e68fc7a
。
Dockerfile
内容
首先,苏南大叔在测试机,新建一个文件夹test
,作为本次镜像制作的工作目录。test
目录里面,放置一个名为Dockerfile
的文件,注意:不能改名,注意大小写。
该Dockerfile
内容如下:
# Use an official Python runtime as a parent image
FROM python:2.7-slim
# Set the working directory to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
ADD . /app
# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# Make port 80 available to the world outside this container
EXPOSE 80
# Define environment variable
ENV NAME World
# Run app.py when the container launches
CMD ["python", "app.py"]
参数说明From
在上述范例中,需要特别注意的就是:From
。From
,指的是:基础镜像。一般来说设置为centos:latest
。
我们要设置的基础镜像,我们可以使用docker search
找到基本镜像名称。然后,通过选中的镜像名称
,查找相关的tags
。查找到合适的tag
,就可以继续继续了。
具体可以参见这篇文章:
- 《如何查找特定功能的docker镜像?docker镜像hub地址大全》
https://newsn.net/say/docker-search.html
参数说明WORKDIR
WORKDIR
,苏南大叔这边理解着就是:类似于大家常见的cd
命令。在执行后续的构建命令的时候,需要先切换到某个目录中,那么这个WORKDIR
,就起到了cd
的作用。那么因此,路径中的斜线之类的都会是有特殊意义的,多次WORKDIR
,路径间也是有相互作用的。anyway,大家把它理解为cd
,就很好说明一切问题了。
同时,这个参数,还可以影响容器命令行的默认目录。正常情况下来说,通过docker exec <name> /bin/bash
进入容器内部的时候,默认所处的目录,就是这个WORKDIR
目录。
WORKDIR
目录,存在于镜像(容器)内部,与宿主机无关。
参数说明ADD
ADD
命令两个参数,ADD <source> <dest>
。苏南大叔把这个ADD
命令,就理解为cp
命令。就是复制文件的意思,把制作镜像时的宿主机上的某些文件或目录,复制到镜像内部的某个位置上去。这些文件,一般都是未来可能会在镜像里面,用到的特殊文件。
在本例中,在执行docker build
命令之前,宿主机的test
目录下面,所有的文件,都会被无差别的复制到镜像的/app
目录下面。所以,那些文件,需要被复制到镜像里面呢?大家可以先想好,再执行build
。比如Dockerfile
文件,其实就并不需要复制到镜像里面。
参数说明RUN
RUN
命令,就是常理上的执行某条命令。本例子中,就是利用pip
安装了一些依赖包,特殊的地方,就是依赖包列表是放在了requirements.txt
文件里面了。这里不做详细解释。更多详细内容,大家可以参见苏南大叔的pip
相关经验链接。
在本例中,requirement.txt
对于Dockerfile
并不是必须的,只是本次实验的一个普通文件。内容如下:
Flask
Redis
RUN
就是镜像构建时的CMD
命令,但是RUN
和CMD
还是有比较重大的区别的,这里做个伏笔。作为小白入门教程,这里的RUN
就简单理解为执行某条命令即可。
参数说明CMD
和RUN
基本上一致,只是CMD
是构建好镜像之后,运行镜像的时候,才执行的。在写法上,CMD
也可以不使用这种数组的形式,具体请参见未来的经验文章。在功用上,CMD
和ENTRYPOINT
是很类似的。这里,苏南大叔就简要的说一句,CMD
在docker run -it
的时候,可以被覆盖的。而ENTRYPOINT
则不会被覆盖。就是说,ENTRYPOINT
,恒定会执行的。
参数说明EXPOSE
EXPOSE
英文单词,就是暴露
的意思,就是说,对外提供一个什么样的端口号。就相当于docker run
时,-p <port1>:<port2>
中的port2
,这个比较好理解。需要暴露多个端口号的时候,就写很多行的EXPOSE
语句即可。
EXPOSE 80
EXPOSE 81
EXPOSE 82
# ...
入口文件app.py
这个app.py
,就是基于python
的www
脚本。这里涉及的python
脚本的用法,苏南大叔就不具体解释了。因为,和本文的主要议题docker
的镜像制作,关系不大。
至是需要说明的是:这个入口文件,可以用于接收环境变量。也就是docker run
时的-e
参数。这个环境变量,还可以在Dockerfile
里面使用ENV
命令,设置默认值。
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
构建docker镜像
苏南大叔使用docker build
命令,来构建最终的docker
镜像。构建的依据就是:Dockerfile
文件。使用-t
来定义镜像名称,.
表示要操作的目录。
docker build -t test .
那么,上述命令,就把上述Dockerfile
文件构建成了,一个带有www
功能的镜像,名字为test
。
运行镜像为容器
得到了test
镜像之后,就可以docker run
了。这里,苏南大叔也不做详细解释了。下面是个docker run
的命令范例。
docker run --name test2 -p 4000:80 -e NAME=sunan -d test
大家,可以仔细想想-p
和-e
参数,是怎么来的。对比一下Dockerfile
里面的相关参数,来加深一下印象。
总结
本文中,苏南大叔所叙述的Dockerfile
,仅仅是冰山一角。里面还有很多不同的参数命令。更多Dockerfile
的应用经验文字,请点击下面的链接查看。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。