Docker学习——基础知识(二):容器
先决条件
- 安装Docker 1.13或更高版本。
- 阅读第1部分中的概述。
- 运行一个快速测试,以确保您完成所有设置:
docker run hello-world
介绍
现在是时候开始使用Docker方式构建应用程序了。我们从这个应用程序层次结构的底部——容器——开始,容器是这篇文章主要讲述的知识。在容器之上是一个服务,它定义了容器在生产中的行为方式,在第3部分中介绍。最后,在顶层是堆栈,定义了第5部分中介绍的所有服务的交互。
- 堆栈
- 服务
- 容器(现在学习的知识)
新的开发环境
以前,如果我们要开始编写Python应用程序,那么我们的首要任务就是在计算机上安装Python运行时环境。但是,这样会导致我们计算机上的环境需要确保开发的App如期运行,又要匹配生产环境。
使用Docker,我们可以将可移植的Python运行时作为映像获取,无需安装。在编译时,将基本Python映像和应用程序代码一起打包,确保我们的的应用程序、其依赖项和运行时都一起运行。
这些可移植映像由Dockerfile
来定义。
用Dockerfile
来定义容器
Dockerfile
定义容器内环境中处理的任务。对网络接口和磁盘驱动器等资源的访问在此环境中进行虚拟化,该环境与系统的其余部分隔离。因此,我们需要将端口映射到外部世界,并具体说明要“复制”到该环境的文件。执行此操作后,我们可以预期此Dockerfile
中定义的应用程序的编译在其运行的任何位置都会完全相同。
Dockerfile
创建一个空目录。将目录(cd
)定位到这个新目录,创建名为Dockerfile
的文件,将以下内容复制并粘贴到该文件中,然后保存。注释中对Dockerfile中每个语句进行了解释:
# 使用官方Python运行时作为父镜像
FROM python:2.7-slim
# 将工作目录设置为/ app
WORKDIR /app
# 将当前目录内容复制到/ app的容器中
COPY . /app
# 安装requirements.txt中指定的任何所需包
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 使端口80可用于容器外部访问
EXPOSE 80
# 定义环境变量
ENV NAME World
# 在容器启动后运行app.py
CMD ["python", "app.py"]
这个Dockerfile
引用了我们尚未创建的几个文件,即app.py
和requirements.txt
。我们接下来创建这些文件。
应用程序(The app itself)
创建两个文件,requirements.txt
和app.py
,并将它们与Dockerfile
放在同一个文件夹中。这就完成了我们的应用程序,步骤非常简单。当上面的Dockerfile
内置到映像中时,由于Dockerfile
的COPY
命令,app.py
和requirements.txt
存在,并且由于EXPOSE
命令,app.py
的输出可通过HTTP访问。
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
现在运行build命令。这将创建一个Docker镜像,我们将使用-t标记它,因此它具有友好名称。
docker build -t friendlyhello .
编译镜像位于您机器的本地Docker镜像注册表中:
$ docker image ls
REPOSITORY TAG IMAGE ID
friendlyhello latest 326387cea398
Linux用户的故障排除
代理服务器设置
代理服务器在启动并运行后会阻止与Web应用程序的连接。如果您在使用代理服务器,请使用ENV
命令将以下行添加到Dockerfile中,以指定代理服务器的主机和端口:
# 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设置。可以在/etc/docker/daemon.json
中编辑(或创建)配置文件,配置dns
键值,如下所示:
{
"dns": ["your_dns_address", "8.8.8.8"]
}
在上面的示例中,列表的第一个元素是DNS服务器的地址。第二项是Google的DNS,可在第一项无法使用时使用。
在继续之前,请保存daemon.json并重新启动docker服务。
sudo service docker restart
修复后,重试运行build
命令。
运行应用程序
运行应用程序,使用-p
将计算机的端口4000映射到容器的已发布端口80:
docker run -p 4000:80 friendlyhello
您应该在http://0.0.0.0:80
看到Python正在为您的应用程序提供服务的消息。但是该消息来自容器内部,它不知道您将该容器的端口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 container ls
以列出正在运行的容器,然后键入docker container stop <Container NAME或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
共享你的镜像
为了演示我们刚刚创建的内容的可移植性,让我们上传我们构建的镜像并在其他地方运行它。毕竟,当您想要将容器部署到生产环境时,您需要知道如何推送到注册表。注册表是存储库的集合,存储库是镜像的集合——类似于GitHub存储库,不过代码是编译好了的。注册表上的帐户可以创建许多存储库。docker
CLI默认使用Docker的公共注册表。
**注意:**我们在这里使用Docker的公共注册表只是因为它是免费和预先配置的,但有许多公共注册表可供选择,您甚至可以使用Docker Trusted Registry设置自己的私有注册表。
使用Docker ID登录
如果您没有Docker帐户,请在hub.docker.com上注册一个帐户。记下您的用户名。
登录本地计算机上的Docker公共注册表。
$ docker login
标记镜像
将本地映像与注册表上的存储库相关联:username/repository:tag
。标签是可选的,但建议使用,因为它是用来为Docker镜像提供版本的机制。为存储库提供存储库和标记有意义的名称,例如get-started:part2
。这会将镜像放入get-started
存储库并将其标记为part2
。
使用您的用户名,存储库和标记名称运行docker tag image
,以便将镜像上载到所需的目标位置。 该命令的语法是:
docker tag image username/repository:tag
例如:
docker tag friendlyhello gordon/get-started:part2
运行docker image ls以查看新标记的图像。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello latest d9e555c53008 3 minutes ago 195MB
gordon/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 gordon/get-started:part2
Unable to find image 'gordon/get-started:part2' locally
part2: Pulling from gordon/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 gordon/get-started:part2
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
无论docker run
在哪里执行,它都会提取你的图像,以及来自requirements.txt
的Python和所有依赖项,并运行你的代码。它们都在一个整洁的小包中一起传送,你不需要在主机上安装任何东西,Docker就可以运行它。
第二部分的结论
这就是这个页面的全部内容。在下一节中,我们将学习如何通过在服务中运行此容器来扩展应用程序。
回顾和备忘(可选)
这是本页所涵盖内容的终端录像:
以下是此页面中基本Docker命令的列表,以及一些相关的命令:
docker build -t friendlyhello . # Create image using this directory's Dockerfile
docker run -p 4000:80 friendlyhello # Run "friendlyname" 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
相关章节
上一节
下一节
声明:此文章为个人根据官方文档翻译搬运,若有理解及翻译错误,欢迎大佬们批评指正。