安装了1.13或者更高版本的Dockerhtml
阅读了Part1中的定位(我没写)python
是时候用Docker构建一个app了。咱们会从构建这样一个app的最底层开始,容器——咱们这节所介绍的内容。在这层之上是服务,服务定义了容器们的在生产中的行为,在第3章介绍。最上层的是堆,定义了服务的交互行为,在第5章介绍。web
堆(Stack)redis
服务(Services)docker
容器(container)shell
使用docker,您能够直接获取一个可移植的Python运行时做为映像。而后,您的构建能够在应用程序代码旁边包含基本的Python映像,确保应用程序、它的依赖项和运行时环境一块儿运行。flask
Dockerfile
定义一个容器Dockerfile将定义容器内环境的内容。访问像网络接口和磁盘驱动器之类的资源在这个环境中是虚拟化的,这与系统的其余部分是隔离的,所以您必须将端口映射到外部世界,并具体地说明您想要“复制”到该环境中的文件。然而,在作了这些以后,您能够指望在这个Dockerfile中定义的应用程序的构建在运行的任何地方都是彻底相同的。小程序
建立一个空目录。将目录(cd)更改成新目录,建立一个名为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"]
您是否使用了代理服务器?
代理服务器一旦启动并运行,就能够阻塞链接到您的web应用程序。若是您在代理服务器后面,请在Dockerfile中添加如下几行,使用ENV命令为您的代理服务器指定主机和端口:服务器
# Set proxy server, replace host:port with values for your servers ENV http_proxy host:port ENV https_proxy host:port
这个Dockerfile和咱们尚未建立的两个文件有联系,即app.py和requirements.txt。接下来让咱们建立这些。
建立两个更多的文件,requirements.txt和app.py,并将它们放在与Dockerfile相同的文件夹中。这就完成了咱们的应用程序,正如您所看到的很是简单。当上述Dockerfile被构建成一个Image,因为Dockerfile的ADD命令会添加requirements.txt和app.py,感谢EXPOSE使app.py的输出能够经过HTTP访问。
Flask Redis
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)
如今咱们看到pip install -r requirements.txt
为Python安装Flask和Redis库,应用程序打印环境变量NAME
,以及调用socket.gethostname()的输出。最后,由于Redis没有运行(由于咱们只安装了Python库,而不是Redis自己),咱们应该指望在这里使用它的尝试会失败并产生错误消息。
注意:当容器内检索容器ID时访问主机名,这就像运行可执行文件的进程ID
就是这样!您不须要Python或requirements.txt
中的任何东西在你的系统上,也不会在你的系统上安装或运行这个映像。看起来你并无真正创建一个包含Python和Flask的环境,可是你有。
咱们已经准备好构建应用程序,确保您仍然处于新目录的顶层。下面是ls应该展现的:
$ ls Dockerfile app.py requirements.txt
如今运行build命令。这建立了一个Docker映像,咱们将使用- t标记它,所以它有一个友好的名称。
docker build -t friendlyhello .
你的构建的Image在哪里?就在你的机器的本地Docker图像注册表中:
$ docker images REPOSITORY TAG IMAGE ID friendlyhello latest 326387cea398
提示:您可使用命令
docker images
或较新的docker image ls
查看全部的镜像。它们给出相同的输出。
运行应用程序,使用-p
,将您的机器的端口4000映射到容器已经发布的端口80:
docker run -p 4000:80 friendlyhello
您应该看到一条消息,Python正在以http://0.0.0.0:80为您的应用服务。可是这个消息来自容器内部,它不知道您将该容器的80端口映射到4000,从而使正确的URL http://localhost:4000。
在web浏览器中访问该URL,能够看到web页面上显示的显示内容,包括“Hello World”文本、容器ID和Redis错误消息。
注意:若是您在Windows 7上使用Docker工具,使用Docker机器IP而不是localhost。例如,http://192.168.99.100:4000。要查看IP地址,请使用命令docker -machine IP。
您还能够在shell中使用curl命令来查看相同的内容。
$ curl http://localhost:4000 <h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
这个端口映射4000:80是为了演示在Dockerfile中公开的内容和使用docker run - p发布的内容之间的区别。在后面的步骤中,咱们将把端口80映射到容器中的80端口,并使用http://localhost。
在你的终端按CTRL + C退出。
如今让咱们在后台运行应用程序,在分离的模式下:
docker run -d -p 4000:80 friendlyhello
你获得了你的应用程序的容器长ID,而后被离开你的终端。您的容器将在后台运行。您还能够看到缩写容器ID经过docker container ls
(在运行命令时,这两个工做均可以互换):
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED 1fa4ab2cf395 friendlyhello "python app.py" 28 seconds ago
您将看到匹配http://localhost:4000上的CONTAINER ID
如今使用docker容器中止进程,使用容器ID,例如:
docker container stop 1fa4ab2cf395
为了演示咱们刚刚建立的可移植性,让咱们上传咱们构建的映像并在其余地方运行它。毕竟,当您想要将容器部署到生产时,您须要学习如何推进注册。
注册表是存储库的集合,而存储库是镜像的集合——有点像GitHub库,只是代码已经构建好了。注册表上的账户能够建立许多存储库。docker CLI在默认状况下使用docker的公共注册表。
注意:咱们将在这里使用Docker的公共注册表,由于它是免费和预配置的,可是有许多公共注册中心可供选择,并且您甚至可使用Docker可信注册表创建您本身的私有注册表。
若是你没有Docker账户,在cloud.docker.com注册一个。记下你的用户名。
登陆到本地机器上的Docker公共注册表。
$ docker login
将本地映像与注册表中的存储库关联的符号是username/repository:tag
。标签是可选的,但推荐,由于它是注册中心用来给Docker映像提供一个版本的机制。提供存储库并为上下文标记有意义的名称,例如get - started:part2。这将把镜像放到get-started存储库中,并将其标记为part2。
docker tag image username/repository:tag
例如:
docker tag image username/repository:tag
运行docker image
以查看新标记的图像。(您也可使用docker image ls
)
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE friendlyhello latest d9e555c53008 3 minutes ago 195MB john/get-started part2 d9e555c53008 3 minutes ago 195MB python 2.7-slim 1c7128a655f6 5 days ago 183MB ...
将您的标记镜像上载到存储库
docker push username/repository:tag
一旦完成,这个上传的结果是公开的。若是您登陆到Docker Hub,您将在那里看到新的图像,使用它的pull命令。
从如今开始,你可使用docker run
在任何机器上运行你的应用程序:
docker run -p 4000:80 username/repository:tag
若是映像在机器上没有本地可用,Docker将从存储库中提取该映像。
$ docker run -p 4000:80 john/get-started:part2 Unable to find image 'john/get-started:part2' locally part2: Pulling from john/get-started 10a267c67f42: Already exists f68a39a6a5e4: Already exists 9beaffc0cf19: Already exists 3c1fe835fb6b: Already exists 4c9f1fa8fcb8: Already exists ee7d8f576a14: Already exists fbccdcced46e: Already exists Digest: sha256:0601c866aab2adcc6498200efd0f754037e909e5fd42069adeff72d1e2439068 Status: Downloaded newer image for john/get-started:part2 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
注意:若是您没有指定这些命令的标记部分
:tag
,那么将会假定您在构建和运行映像时都将使用最新的标记:latest
。Docker将使用没有指定标记的图像的最后一个版本(不必定是最近的图像)。
不管docker运行在哪里,它都会拉出您的映像,以及Python以及来自需求的全部依赖项requirements.txt
,并运行您的代码。它在一个整洁的小程序包中一块儿运行,而主机不须要安装任何东西,而是安装Docker来运行它。