如果想要开发一个基于python的应用,传统的开发方式中,首先要在本地操作系统中安装python。但Docker不用,只需要找到已经安装了python运行时环境的image即可,不需要本地操作系统中安装phthon,不只如此,image中可以包含任何应用运行时所需的构件。image本身是一种分层的特殊格式文件,在取得安装了python运行时环境的基础层以后,按下来就是把我们自己开发的东西逐层打包进去,构建新image。新image的构建通过定义Dockerfile文件实现,首先它有一个基础image,就是包含python运行时环境的镜像,接下来就是一系列的命令,将我们应用需要的库、文件、代码逐层加进去,最后打包。
首先创建一个新目录并cd到此目录下,在此目录下创建名为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
COPY . /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"]
接下来创建Dockerfile文件中需要的两个文件,requirements.txt与app.py。requirements.txt包含两行内容,指出需要额外安装的python库,pip通过此文件安装我们需要依赖的库,内容如下:
Flask
Redis
app.py文件为python代码,是需要执行的程序,内容如下:
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 = "cannot connect to Redis, counter disabled"
html = "Hello {name}!
" \
"Hostname: {hostname}
" \
"Visits: {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)
将以上两个文件放在与Dockerfile文件同级的目录下,整个开发工作已经结束。在用Dockerfile构建新的image之前,先解释一下文件内容。
FROM python:2.7-slim
本条命令的意思是把python:2.7-slim当成本次构建的基础镜像,如果python:2.7-slim在本地不存在,则Docker自动从默认的镜像仓库中拉取,当然也可以通过设置修改Docker自带的默认image仓库。
WORKDIR /app
本条命令的意思是设置image的工作目录。
COPY . /app
本条命令会将刚才创建的两个文件复制到app目录下。
RUN pip install --trusted-host pypi.python.org -r requirements.txt
利用requirements.txt中的内容安装依赖库
EXPOSE 80
镜像在运行时对外暴露 80号端口。
ENV NAME World
设置镜像运行时的环境变量。
CMD ["python", "app.py"]
启动app.py程序。
Docker在用Dockerfile文件构建新镜像时,每运行一条命令就在原始镜像的基础上新增加一层。
首先确认当前目录下的文件:
$ ls
Dockerfile app.py requirements.txt
执行如下命令构建image:
docker build -t friendlyhello .
构建好的image目前被保存在本地文件系统中,可以为其添加tag,并上传到镜像仓库中。执行如下命令确认:
$ docker image ls
REPOSITORY TAG IMAGE ID
friendlyhello latest 326387cea398
docker run -p 4000:80 friendlyhello
命令中的80端口号是容器网络内部的一个端口号,不能与外界通信,-p命令的意思是将80端口号映射成宿主机的4000端口号,这样就可以从外部访问此应用了。
可以在浏览器中打开http://localhost:4000试一下,注意网络问题如防火墙、DNS、代理等。
以上命令运行的容器会占用一个前端,通过如下命令使容器运行在后台:
docker run -d -p 4000:80 friendlyhello
通过如下命令确认容器是否在运行:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED
1fa4ab2cf395 friendlyhello "python app.py" 28 seconds ago
其中的CONTAINER ID由Docker自动生成,如果运行容器时没有特别指明主机名称,则此ID就是容器运行时的主机名称。
结束容器运行:
docker container stop 1fa4ab2cf395
需要注意的是,结束容器运行不代表此容器从系统中删除了。每个容器在运行时,在原来image的基础上会再添加一个可读写层,在容器运行过程中涉及到的修改操作如增加了某个文件、修改了某个文件的内容等,实际上会添加到这个可读写层,其它的层是不可修改的。当窗口停止运行时,其读写层仍然存在,在下次启动时继续使用。因此想要将容器删除还需要调用另外一个专门删除的命令。