介绍
Docker 使您的应用程序和服务易于包装成容器,以便您可以在任何地方运行它们. 不幸的是,在构建您的图像和集成应用程序所需的所有层时可能会出现问题,特别是如果您对 Docker 图像和容器很新奇。
在此针对 Docker 的新用户的故障排除指南中,您将在构建 Docker 图像时解决问题,在运行集装箱时解决命名碰撞,并解决在集装箱之间通信时出现的问题。
前提条件
要完成本教程,您将需要
- Docker 安装在服务器上或本地机器上。
要在服务器上安装 Docker,您可以遵循方法指南(https://andsky.com/tech/tutorials/how-to-install-and-use-docker-on-centos-7)或(for Ubuntu 16.04)(https://andsky.com/tech/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04)。
您可以访问 Docker 网站或遵循 官方安装文件以在本地计算机上安装 Docker。
步骤 1 — 解决 Dockerfile 的问题
您可能遇到的问题最常见的地方是当您从Dockerfile
构建您的Docker图像时。
- 一个 image 是一个只读的资源,你创建的配置文件称为
Dockerfile
. 这是你发送和分享通过 Docker Hub或你的私人注册表。
您可以在教程中了解更多有关这些概念的信息(https://andsky.com/tech/tutorials/docker-explained-using-dockerfiles-to-automate-building-of-images)。
当您查看Dockerfile
,您可以清楚地看到Docker使用的步骤过程构建图像,因为Dockerfile
中的每个行都与过程中的一个步骤相匹配。
让我们在您的主目录中创建一个docker_image
目录,并使用nano
或您最喜欢的编辑器在该文件夹中创建一个Dockerfile
1mkdir ~/docker_image
2nano ~/docker_image/Dockerfile
将以下内容添加到新文件中:
1[label ~/docker_image/Dockerfile]
2# base image
3FROM debian:latest
4
5# install basic apps
6RUN aapt-get install -qy nano
这个代码中有故意的打字符,你能找到吗?试着从这个文件中构建一个图像,看看Docker如何处理一个错误的命令。
1docker build -t my_image ~/docker_image
您将在您的终端中看到此消息,表示错误:
1[secondary_label Output]
2Step 2 : RUN aapt-get install -qy nano
3 ---> Running in 085fa10ffcc2
4/bin/sh: 1: aapt-get: not found
5The command '/bin/sh -c aapt-get install -qy nano' returned a non-zero code: 127
最后的错误消息意味着在步骤 2 中出现了命令问题,在这种情况下,它是我们的故意打字:我们有aapt-get
而不是apt-get
。
更改Dockerfile
并进行更正:
1[label Dockerfile]
2
3# install basic apps
4RUN apt-get install -qy nano
现在,再次运行docker build
命令:
1docker build -t my_image ~/docker_image
现在你会看到以下的输出:
1[secondary_label Output]
2Sending build context to Docker daemon 2.048 kB
3Step 1 : FROM debian:latest
4---> ddf73f48a05d
5Step 2 : RUN apt-get install -qy nano
6---> Running in 9679323b942f
7Reading package lists...
8Building dependency tree...
9E: Unable to locate package nano
10The command '/bin/sh -c apt-get install -qy nano' returned a non-zero code: 100
通过修正字体,这个过程稍微加快了,因为Docker缓存了第一个步骤,而不是重新下载了基础图像,但正如您从输出中看到的那样,我们有一个新的错误。
我们使用的 Debian 发行版作为我们的图像的基础,无法找到文本编辑器nano
,尽管我们知道它可以在 Debian 包存储库中找到。
要修复此问题,请修改 Dockerfile 以清理和更新源 before 安装任何新包。
1nano ~/docker_image/Dockerfile
向文件添加以下突出的行, above 命令来安装 nano
:
1[label ~/docker_image/Dockerfile]
2# base image
3FROM debian:latest
4
5# clean and update sources
6RUN apt-get clean && apt-get update
7
8# install basic apps
9RUN apt-get install -qy nano
保存文件并再次运行docker build
命令:
1docker build -t my_image ~/docker_image
这一次,过程顺利完成。
1[secondary_label Output]
2Sending build context to Docker daemon 2.048 kB
3Step 1 : FROM debian:latest
4 ---> a24c3183e910
5Step 2 : RUN apt-get install -qy nano
6 ---> Running in 2237d254f172
7Reading package lists...
8Building dependency tree...
9Reading state information...
10Suggested packages:
11 spell
12The following NEW packages will be installed:
13 nano
14...
15
16 ---> 64ff1d3d71d6
17Removing intermediate container 2237d254f172
18Successfully built 64ff1d3d71d6
让我们看看当我们将Python 3和PostgreSQL驱动程序添加到我们的图像时会发生什么。
1nano ~/docker_image/Dockerfile
并添加两个新的步骤来安装 Python 3 和 Python PostgreSQL 驱动程序:
1[label ~/docker_image/Dockerfile]
2# base image
3FROM debian:latest
4
5# clean and update sources
6RUN apt-get clean && apt-get update
7
8# install basic apps
9RUN apt-get install -qy nano
10
11# install Python and modules
12RUN apt-get install -qy python3
13RUN apt-get install -qy python3-psycopg2
保存文件,离开编辑器,然后重新构建图像:
1docker build -t my_image ~/docker_image
正如你可以从输出中看到的那样,包安装正确,而且由于以前的步骤被缓存,这个过程也更快完成。
1[secondary_label Output]
2Sending build context to Docker daemon 2.048 kB
3Step 1 : FROM debian:latest
4 ---> ddf73f48a05d
5Step 2 : RUN apt-get clean && apt-get update
6 ---> Using cache
7 ---> 2c5013476fbf
8Step 3 : RUN apt-get install -qy nano
9 ---> Using cache
10 ---> 4b77ac535cca
11Step 4 : RUN apt-get install -qy python3
12 ---> Running in 93f2d795fefc
13Reading package lists...
14Building dependency tree...
15Reading state information...
16The following extra packages will be installed:
17 krb5-locales libgmp10 libgnutls-deb0-28 libgssapi-krb5-2 libhogweed2
18 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.4-2 libnettle4
19 libp11-kit0 libpq5 libsasl2-2 libsasl2-modules libsasl2-modules-db
20 libtasn1-6
21Suggested packages:
22 gnutls-bin krb5-doc krb5-user libsasl2-modules-otp libsasl2-modules-ldap
23 libsasl2-modules-sql libsasl2-modules-gssapi-mit
24 libsasl2-modules-gssapi-heimdal python-psycopg2-doc
25The following NEW packages will be installed:
26 krb5-locales libgmp10 libgnutls-deb0-28 libgssapi-krb5-2 libhogweed2
27 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.4-2 libnettle4
28 libp11-kit0 libpq5 libsasl2-2 libsasl2-modules libsasl2-modules-db
29 libtasn1-6 python3-psycopg2
300 upgraded, 18 newly installed, 0 to remove and 0 not upgraded.
31Need to get 5416 kB of archives.
32After this operation, 10.4 MB of additional disk space will be used.
33
34...
35
36Processing triggers for libc-bin (2.19-18+deb8u6) ...
37 ---> 978e0fa7afa7
38Removing intermediate container d7d4376c9f0d
39Successfully built 978e0fa7afa7
<$>[注] 注 :Docker缓存构建过程,因此您可能会遇到在构建中运行更新的情况,Docker缓存此更新,并在一段时间后您的基发行重新更新其源,留给您过时的源,尽管在您的Dockerfile
中进行清理和更新。
密切关注 Docker 输出,以识别打字位置,并在构建时间和容器内运行更新,以确保您不会受到缓存包列表的干扰。
语法错误和缓存问题是您在在 Docker 中构建图像时可能遇到的最常见问题。
第2步:解决集装箱命名问题
随着更多集装箱的启动,你最终会遇到名称碰撞. 命名碰撞是你试图创建一个与系统上已经存在的集装箱相同的名称的集装箱的地方。
让我们从我们在上一节中构建的图像中启动一个容器,我们将在这个容器内部运行一个交互式Bash解释器来测试事物。
1docker run -ti my_image bash
当容器启动时,你会看到一个等待指示的根提示:
1[environment second]
现在你有一个运行容器,让我们看看你可能会遇到什么样的问题。
当你运行一个容器,就像你刚刚做的,没有明确设置一个名字时,Docker 会随机分配一个名称给该容器. 您可以通过在Docker 主机上运行docker ps
命令来查看所有正在运行的容器及其相应的名称。
在 Docker 主机上打开新终端并运行以下命令:
1docker ps
此命令输出运行容器列表,其名称如下示例所示:
1[secondary_label Output]
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
380a0ca58d6ec my_image "bash" 22 seconds ago Up 28 seconds loving_brahmagupta
在前面的输出中,loving_brahmagupta 这个名字是 Docker 在前面的例子中自动分配给容器的名称;你的名字将有不同的名称. 让 Docker 给你的容器分配一个名称在非常简单的情况下是好的,但可能会出现重大问题;当我们部署时,我们需要一致地命名容器,以便我们可以参考它们并轻松自动化它们。
要指定容器的名称,我们可以使用--name
参数在启动容器时,或者我们可以将运行容器重新命名为更为描述性的东西。
从 Docker 主机的终端运行以下命令:
1docker rename your_container_name python_box
然后列出您的容器:
1docker ps
您将在输出中看到python_box
容器,确认您已成功更名容器:
1[secondary_label Output]
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
380a0ca58d6ec my_image "bash" 24 minutes ago Up 24 minutes python_box
要关闭容器,请在包含正在运行的容器的终端的提示处键入出口
:
1[environment second]
2exit
如果这不是一个选项,您可以使用以下命令在 Docker 主机上的其他终端杀死容器:
1docker kill python_box
当您以这种方式杀死容器时,Docker 会返回刚刚被杀死的容器的名称:
1[secondary_label Output]
2python_box
要确保python_box
不再存在,请重新列出所有正在运行的容器:
1docker ps
正如预期的那样,容器不再列出:
1[secondary_label Output]
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
现在你可能认为你可以推出另一个名为python_box
的容器,但让我们看看当我们尝试时会发生什么。
我们这次将使用--name
参数来设置容器的名称:
1docker run --name python_box -ti my_image bash
1[secondary_label Output]
2docker: Error response from daemon: Conflict. The name "/python_box" is already in use by container 80a0ca58d6ecc80b305463aff2a68c4cbe36f7bda15e680651830fc5f9dda772. You have to remove (or rename) that container to be able to reuse that name..
3See 'docker run --help'.
当您构建图像并重新使用现有图像的名称时,现有图像将被重写,正如您已经看到的那样。
Docker 說「python_box」已經存在,儘管我們剛剛殺掉了它,甚至沒有列出「docker ps」。
要列出 all 的 Docker 容器,运行和其他方式,将)传输到docker ps
:
1docker ps -a
现在我们的python_box
容器出现在输出中:
1[secondary_label Output]
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
380a0ca58d6ec my_image "bash" 12 minutes ago Exited (137) 6 minutes ago python_box
容器具有退出(137)
状态,这就是为什么我们在尝试创建一个具有相同名称的新容器时遇到命名问题。
当您想要完全删除一个容器时,您可以使用docker rm
命令。
1docker rm python_box
再次,Docker输出刚刚删除的容器的名称:
1[secondary_label Output]
2python_box
<$>[警告] 警告 :如果容器仍在运行,此命令将失败并输出错误消息,因此请确保先停止或杀死它。
让我们创建一个名为python_box
的新容器,现在我们已经删除了以前的容器:
1docker run --name python_box -ti my_image bash
这个过程完成了,我们再次呈现了一个根壳:
1[environment second]
现在让我们杀死并删除容器,以便在未来避免问题. 从 Docker 主机上的另一个终端会话中,杀死容器并用以下命令删除它:
1docker kill python_box && docker rm python_box
我们链接了两个命令,所以输出显示了容器名称两次,第一个输出验证了我们杀死了容器,另一个确认了我们删除了它。
1[secondary_label Output]
2python_box
3python_box
在遇到与名字有关的问题时,请记住docker ps -a
,并确保您的容器在尝试以相同名称重建之前被停止和删除。
命名容器使您更容易管理您的基础设施. 名称也使您更容易在容器之间进行通信,如下所示。
第3步:解决集装箱通信问题
Docker 可以轻松地实例多个集装箱,以便您可以在每个集装箱中运行不同的甚至多余的服务. 如果一个服务失败或受到损害,您可以简单地将其更换成一个新的服务,同时保持其余的基础设施完整。
让我们创建两个通信容器,以便我们可以探索潜在的通信问题. 我们将使用我们的现有图像创建一个运行Python的容器,另一个运行PostgreSQL的实例的容器。
让我们先创建 PostgreSQL 容器,我们将使用--name
旗帜命名此容器,以便在将其与其他容器链接时轻松识别。
以前,当我们推出一个集装箱时,它在前沿运行,接管了我们的终端。
最后,而不是运行bash
,我们将运行postgres
命令,该命令将启动容器内部的PostgreSQL数据库服务器。
执行以下命令启动容器:
1docker run --name postgres_box --detach postgres
Docker 将从 Docker Hub 下载图像并创建容器,然后返回在背景中运行的容器的完整 ID:
1[secondary_label Output]
2Unable to find image 'postgres:latest' locally
3latest: Pulling from library/postgres
46a5a5368e0c2: Already exists
5193f770cec44: Pull complete
6...
7484ac0d6f901: Pull complete
8Digest: sha256:924650288891ce2e603c4bbe8491e7fa28d43a3fc792e302222a938ff4e6a349
9Status: Downloaded newer image for postgres:latest
10f6609b9e96cc874be0852e400381db76a19ebfa4bd94fe326477b70b8f0aff65
列出容器,以确保这个新容器正在运行:
1docker ps
输出确认postgres_box
容器在背景中运行,暴露了5432
端口,即PostgreSQL数据库端口:
1[secondary_label Output]
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
37a230b56cd64 postgres_box "/docker-entrypoint.s" Less than a second ago Up 2 seconds 5432/tcp postgres
现在让我们启动 Python 容器,以便在 Python 容器内部运行的程序在postgres_box 中看到
服务,我们需要通过使用--link
论点将我们的 Python 容器与postgres_box 容器手动链接。
发出以下命令来启动 Python 容器:
1docker run --name python_box --link postgres_box:postgres -ti my_image bash
现在让我们尝试从python_box
容器内部连接到PostgreSQL。
我们之前在python_box
容器内部安装了nano
,所以让我们使用它来创建一个简单的Python脚本来测试与PostgreSQL的连接。
1[environment second]
2nano pg_test.py
然后将以下Python脚本添加到文件中:
1[label pg_test.py]
2"""Test PostgreSQL connection."""
3import psycopg2
4
5conn = psycopg2.connect(user='postgres')
6print(conn)
保存文件并离开编辑器 让我们看看当我们尝试从我们的脚本连接到数据库时会发生什么。
1[environment second]
2python3 pg_test.py
我们看到的输出表示连接到数据库存在问题:
1[secondary_label Output]
2Traceback (most recent call last):
3 File "pg_test.py", line 5, in <module>
4 conn = psycopg2.connect(database="test", user="postgres", password="secret")
5 File "/usr/lib/python3/dist-packages/psycopg2/__init__.py", line 164, in connect
6 conn = _connect(dsn, connection_factory=connection_factory, async=async)
7psycopg2.OperationalError: could not connect to server: No such file or directory
8 Is the server running locally and accepting
9 connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
我們確保「postgres_box」容器正在運行,我們已將其連接到「python_box」容器,那麼發生了什麼事?在嘗試連接時,我們從未指定過資料庫主機,所以Python 試圖連接到本地運行的資料庫,而這不會運作,因為該服務不在本地運行,它在不同的容器上運行,就像是在不同的電腦上一樣。
您可以使用您创建链接时设置的名称访问链接的容器. 在我们的情况下,我们使用postgres
来参考正在运行我们的数据库服务器的postgres_box
容器. 您可以通过查看python_box
容器中的/etc/hosts
文件来验证此情况:
1[environment second]
2cat /etc/hosts
您将看到所有可用的主机,他们的名字和IP地址. 我们的postgres
服务器是清晰可见的。
1[secondary_label Output]
2127.0.0.1 localhost
3::1 localhost ip6-localhost ip6-loopback
4fe00::0 ip6-localnet
5ff00::0 ip6-mcastprefix
6ff02::1 ip6-allnodes
7ff02::2 ip6-allrouters
8172.17.0.2 postgres f6609b9e96cc postgres_box
9172.17.0.3 3053f74c8c13
所以让我们修改我们的Python脚本并添加主机名。
1[environment second]
2nano pg_test.py
然后在连接字符串中指定主机:
1[label /pg_test.py]
2"""Test PostgreSQL connection."""
3import psycopg2
4
5conn = psycopg2.connect(host='postgres', user='postgres')
6print(conn)
保存文件,然后再次运行脚本。
1[environment second]
2python3 pg_test.py
这一次,脚本完成了,没有任何错误:
1[secondary_label Output]
2<connection object at 0x7f64caec69d8; dsn: 'user=postgres host=7a230b56cd64', closed: 0>
当您尝试连接到其他集装箱中的服务时,请记住集装箱名称,并编辑应用程序凭据以引用这些集装箱的链接名称。
结论
我们刚刚涵盖了您在使用 Docker 容器时可能遇到的最常见问题,从构建图像到部署容器网络。
Docker有一个 -debug
旗帜,主要是为Docker开发人员而设计的,但是,如果您想了解更多关于Docker内部的信息,请尝试在调试模式下运行Docker命令,以获得更简单的输出:
1docker -D [command] [arguments]
虽然软件中的容器已经存在了一段时间,但Docker本身仅存在了三年,并且可能相当复杂. 花时间熟悉这些术语和 生态系统,你会看到一些最初有点陌生的概念很快就会产生很多意义。