Docker学习——基础知识(二):容器

先决条件

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.pyrequirements.txt。我们接下来创建这些文件。

应用程序(The app itself)

创建两个文件,requirements.txtapp.py,并将它们与Dockerfile放在同一个文件夹中。这就完成了我们的应用程序,步骤非常简单。当上面的Dockerfile内置到映像中时,由于DockerfileCOPY命令,app.pyrequirements.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

相关章节

上一节

基础知识(一):概述与环境配置

下一节

基础知识(三):服务

声明:此文章为个人根据官方文档翻译搬运,若有理解及翻译错误,欢迎大佬们批评指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值