用下面的命令快速测试你的环境是否完备:html
docker run hello-world
如今开始用 Docker 的方式构建应用。咱们从这个应用的层次结构底部开始,也就是这里讲的容器。在容器层上面有第三部分讲的 service 层,定义了生产中的容器的行为方式。最顶层的是第五部分讲的 stack 层,定义了全部 service 的交互。python
过去,若是要写个 Python 应用,首先要在机器上安装 Python 运行时。这就带来了一个问题:要使应用按照预期运行,就须要机器上的环境完美适合应用程序,同时生产环境须要与开发环境彻底一致。 redis
经过 Docker,能够将一个可移植的 Python 运行时做为一个 image 镜像获取,无需安装。 而后,构建时能够将基础 Python 镜像与应用程序代码一块儿包括在内,确保应用程序,依赖项和运行时都一块儿发布。 经过 Dockerfile
定义可移植的镜像。docker
Dockerfile
定义了容器中的环境包含哪些东西。对网络接口、磁盘等资源的访问被虚拟化到了这个环境内部,从而与系统的其余部分隔离,所以必须映射端口到外部,而且指明须要把哪些文件复制到容器内部。这些完成后,经过这个 Dockerfile 对应用的构建在任何地方运行时都会有相同的表现。shell
Dockerfile
json
在本地计算机上建立空目录。改变目录 (cd
) 进入到新目录,建立名为 Dockerfile
的文件,将如下内容复制并粘贴到该文件中,而后将其保存。请注意解释新 Dockerfile
中每一个语句的注释。flask
# 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
是指一些还没有建立的文件,即app.py
和requirements.txt
。接下来,让咱们建立这些。浏览器
再建立两个文件,requirements.txt
和app.py
,并将它们与 Dockerfile
放在同一文件夹中。这完成了咱们的应用程序,你能够看到这是很简单的。当上述 Dockerfile
内置到映像中时,因为 Dockerfile
的 COPY
命令,app.py
和requirements.txt
存在,而且因为 EXPOSE
命令,能够经过 HTTP
访问来自app.py
的输出。服务器
requirements.txt
网络
Flask
Redis
app.py
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 相似于正在运行的可执行文件的进程 ID。
就是这样!您不须要 Python
或任何requirements.txt
在您的系统上,也不构建或运行此映像安装它们在您的系统上。看起来您并无真正使用 Python 和 Flask 创建了一个环境,但您已经设置了环境。
咱们准备构建应用程序。请确保您仍在新目录的顶层。如下是 ls 应显示的内容:
$ ls Dockerfile app.py requirements.txt
如今运行生成命令。这将建立一个 Docker 镜像,咱们将使用 --tag 选项命名该镜像。若是要使用较短的选项,请使用 -t。
docker build --tag=friendlyhello .
你的构建镜像在哪里?它位于计算机的本地 Docker 镜像注册表中:
$ docker image ls REPOSITORY TAG IMAGE ID friendlyhello latest 326387cea398
请注意标记如何默认为最新标记。标记选项的完整语法相似于
--tag=friendlyhello:v0.0.1.
Linux 用户的故障排除
代理服务器设置
代理服务器能够在 Web 应用启动并运行后阻止其链接到该站点。若是位于代理服务器后面,请向 Dockerfile 添加如下行,使用 ENV 命令指定代理服务器的主机和端口:
# Set proxy server, replace host:port with values for your servers ENV http_proxy host:port ENV https_proxy host:port
DNS 设置
DNS 配置错误可能会生成
pip
问题。您须要设置本身的 DNS 服务器地址,以使pip
正常工做。您可能须要更改 Docker 守护进程程序的 DNS 设置。您可使用dns
密钥在/etc/docker/daeon.json
上编辑(或建立)配置文件,以下所示:{ "dns": ["your_dns_address", "8.8.8.8"] }
在上面的示例中,列表的第一个元素是 DNS 服务器的地址。第二个项目是谷歌的DNS,可使用时,第一个不可用。
在继续以前,请保存
daemon.json
并从新启动 docker 服务。sudo service docker restart
修复后,重试以运行
build
命令。
运行应用,使用 -p
将计算机的端口 4000 映射到容器的已发布端口 80:
docker run -p 4000:80 friendlyhello
您应该会看到一条消息,指出 Python 正在http://0.0.0.0:80
为你的应用提供服务。可是,该消息来自容器内部,该容器不知道您将该容器的端口 80 映射到 4000,从而正确的 URL 是 http://localhost:4000
。
转到 Web 浏览器中的该 URL 以查看网页上提供的显示内容。
注意:若是您在 Windows 7 上使用 Docker Toolbox,请使用 Docker machine 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
中的 EXPOSE
与运行 docker run-p
时设置的发布值之间的差别。在后面的步骤中,将主机上的端口 4000 映射到容器中的端口 80 并使用http://localhost
。
在终端中点击 CTRL+C
以退出。
在 Windows 上,显式中止容器
在 Windows 系统上,CTRL+C 不会中止容器。所以,首先键入 CTRL_C 以返回提示(或打开另外一个 shell),而后键入 docker 容器 ls 以列出正在运行的容器,而后键入 docker 容器中止 [容器名称或 ID] 以中止容器。不然,当您尝试在下一步中从新运行容器时,您将从守护进程收到错误响应。
如今,让咱们在后台以分离模式运行应用:
docker run -d -p 4000:80 friendlyhello
您将得到应用的长容器 ID,而后被踢回终端。容器在后台运行。您还能够看到带有 docker container ls
的缩写容器 ID(在运行命令时,二者可互换):
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED 1fa4ab2cf395 friendlyhello "python app.py" 28 seconds ago
请注意,CONTAINER ID
与http://localhost:4000
上的内容相匹配。
如今使用 docker container stop
来结束进程,使用 CONTAINER ID
,以下所示:
docker container stop 1fa4ab2cf395
为了演示咱们刚刚建立的内容的可移植性,让咱们上传构建的镜像并将其运行到其余地方。毕竟,您须要知道如何在将容器部署到生产时推送到registries。
一个registry是repositories的集合, repository是镜像的集合— 相似于 GitHub repository,但代码已经生成。一个registry上的账户能够建立许多repositories。默认状况下,Docker
CLI 使用 Docker 的公共registry。
注意:咱们在这里使用 Docker 的公共registry,只是由于它是免费的,而且预先配置了,但有许多公共registry可供选择,您甚至可使用 Docker 可信registry设置您本身的专用registry。
若是您没有 Docker 账户,请hub.docker.com注册。记下您的用户名。
在本地机器上登陆 Docker 的公共 registry:
$ docker login
将一个本地镜像关联到注册处 registry 中的一个仓库的符号是 username/repository:tag。标签是可选的,可是建议使用,由于这是 registry 用来给 Docker 镜像指定版本的机制。给仓库和标签起有意义的名字,例如 get-started:part2。这会把镜像放入 get-started 仓库,而且添加标签 part2。
运行 docker tag image 命令,用本身的 username,repository 和 标签名,这样镜像能够上传到指定的位置。命令的语法是:
docker tag image username/repository:tag
举个例子:
docker tag friendlyhello john/get-started:part2
运行 docker image ls 来查看新打标签的镜像:
$ docker image ls 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 ...
将标记的镜像上载到repository:
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)
无论 docker run
在哪里运行,Docker 会获取你的镜像并运行(这里的镜像安装了 Python 和从 requirements.txt
文件指定的依赖,并会运行应用代码)。全部的东西都在一个包里,获取到就能够运行,不须要安装其余东西。
本页就到此。在下一节中,咱们将学习如何经过在service
中运行此容器来扩展应用程序。
下面是此页面中的基本 Docker 命令的列表,若是您想在继续以前先浏览一下,则提供一些相关命令。
docker build -t friendlyhello . # Create image using this directory's Dockerfile docker run -p 4000:80 friendlyhello # Run "friendlyhello" mapping port 4000 to 80 docker run -d -p 4000:80 friendlyhello # Same thing, but in detached mode docker container ls # List all running containers docker container ls -a # List all containers, even those not running docker container stop <hash> # Gracefully stop the specified container docker container kill <hash> # Force shutdown of the specified container docker container rm <hash> # Remove specified container from this machine docker container rm $(docker container ls -a -q) # Remove all containers docker image ls -a # List all images on this machine docker image rm <image id> # Remove specified image from this machine docker image rm $(docker image ls -a -q) # Remove all images from this machine docker login # Log in this CLI session using your Docker credentials docker tag <image> username/repository:tag # Tag <image> for upload to registry docker push username/repository:tag # Upload tagged image to registry docker run username/repository:tag # Run image from a registry