介绍
Docker使您可以轻松地将应用程序和服务包装在容器中,以便您可以在任何位置运行它们。不幸的是,当构建你的镜像和集成你的应用程序需要的所有层,特别是如果你是新的Docker镜像和容器,可能会出现问题。在与其他容器通信时,可能会遇到打印错误,运行时库和模块的问题,命名冲突或问题。 在针对Docker新手的故障排除指南中,您将在构建Docker镜像时解决问题,在运行容器时解决命名冲突,以及解决容器之间通信时出现的问题。先决条件
要完成本教程,您需要- Docker安装在服务器或本地机器上。
第1步 - 解决Dockerfile的问题
当你从构建多克的镜像,你可能会碰到问题最常见的地方是Dockerfile
。在我们介绍之前,让我们澄清镜像和容器之间的区别。
- 镜像是您创建使用一个被称为配置文件的只读资源
Dockerfile
。 这是你什么船,并通过分享Docker Hub或私有注册。 - 容器 ,是一个读写实例您创建了您构建的镜像。
Dockerfile
,你可以清楚地看到一步一步的过程Docker采用构建映像,因为在每一行
Dockerfile
对应于过程的一个步骤。这通常意味着如果你到某个步骤,则所有以前的步骤成功完成。 让我们创建一个小项目,探索你可能会和一个遇到的一些问题
Dockerfile
。 创建
docker_image
在你的home目录目录,并使用
nano
或你喜欢的编辑器创建一个
Dockerfile
该文件夹中
mkdir ~/docker_image
nano ~/docker_image/Dockerfile
将以下内容添加到此新文件:
〜/ docker_image / Dockerfile
# base image
FROM debian:latest
# install basic apps
RUN aapt-get install -qy nano
在这段代码中有一个有意的拼写错误。你能找到吗?尝试从这个文件构建一个镜像,看看Docker如何处理一个坏的命令。使用以下命令创建映像:
docker build -t my_image ~/docker_image
您将在您的终端中看到此消息,指示错误:
OutputStep 2 : RUN aapt-get install -qy nano
---> Running in 085fa10ffcc2
/bin/sh: 1: aapt-get: not found
The command '/bin/sh -c aapt-get install -qy nano' returned a non-zero code: 127
在最后的错误消息意味着,有与第2步在这种情况下它是我们故意输入错误的命令一个问题:我们已经
aapt-get
,而不是
apt-get
。但这也意味着上一步骤正确执行。 修改
Dockerfile
然后改正:
Dockerfile
# install basic apps
RUN apt-get install -qy nano
现在运行的
docker build
再次命令:
docker build -t my_image ~/docker_image
现在,您将看到以下输出:
OutputSending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:latest
---> ddf73f48a05d
Step 2 : RUN apt-get install -qy nano
---> Running in 9679323b942f
Reading package lists...
Building dependency tree...
E: Unable to locate package nano
The command '/bin/sh -c apt-get install -qy nano' returned a non-zero code: 100
修正错误后,进程移动了一点点,因为Docker缓存了第一步,而不是重新下载基础镜像。但正如你可以从输出中看到的,我们有一个新的错误。 我们作为我们的形象基础的Debian发行找不到文本编辑器
nano
,即使我们知道它是可用在Debian软件包库。基本映像带有缓存的元数据,例如存储库和可用软件包列表。当您从中提取数据的活仓库已更改时,您可能偶尔会遇到一些缓存问题。 为了解决这个问题,修改Dockerfile做的来源清理和更新你安装任何新的软件包
之前 。再次打开配置文件:
nano ~/docker_image/Dockerfile
以下突出显示的行添加到该文件,安装命令
上述
nano
:
〜/ docker_image / Dockerfile
# base image
FROM debian:latest
# clean and update sources
RUN apt-get clean && apt-get update
# install basic apps
RUN apt-get install -qy nano
保存文件并运行
docker build
再次命令:
docker build -t my_image ~/docker_image
此过程成功完成。
OutputSending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:latest
---> a24c3183e910
Step 2 : RUN apt-get install -qy nano
---> Running in 2237d254f172
Reading package lists...
Building dependency tree...
Reading state information...
Suggested packages:
spell
The following NEW packages will be installed:
nano
...
---> 64ff1d3d71d6
Removing intermediate container 2237d254f172
Successfully built 64ff1d3d71d6
让我们看看当我们将Python 3和PostgreSQL驱动程序添加到我们的映像时会发生什么。打开
Dockerfile
一次。
nano ~/docker_image/Dockerfile
并添加两个新步骤来安装Python 3和Python PostgreSQL驱动程序:
〜/ docker_image / Dockerfile
# base image
FROM debian:latest
# clean and update sources
RUN apt-get clean && apt-get update
# install basic apps
RUN apt-get install -qy nano
# install Python and modules
RUN apt-get install -qy python3
RUN apt-get install -qy python3-psycopg2
保存文件,退出编辑器,然后再次构建镜像:
docker build -t my_image ~/docker_image
从输出中可以看出,程序包安装正确。该过程还完成得更快,因为先前的步骤被缓存。
OutputSending build context to Docker daemon 2.048 kB
Step 1 : FROM debian:latest
---> ddf73f48a05d
Step 2 : RUN apt-get clean && apt-get update
---> Using cache
---> 2c5013476fbf
Step 3 : RUN apt-get install -qy nano
---> Using cache
---> 4b77ac535cca
Step 4 : RUN apt-get install -qy python3
---> Running in 93f2d795fefc
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
krb5-locales libgmp10 libgnutls-deb0-28 libgssapi-krb5-2 libhogweed2
libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.4-2 libnettle4
libp11-kit0 libpq5 libsasl2-2 libsasl2-modules libsasl2-modules-db
libtasn1-6
Suggested packages:
gnutls-bin krb5-doc krb5-user libsasl2-modules-otp libsasl2-modules-ldap
libsasl2-modules-sql libsasl2-modules-gssapi-mit
libsasl2-modules-gssapi-heimdal python-psycopg2-doc
The following NEW packages will be installed:
krb5-locales libgmp10 libgnutls-deb0-28 libgssapi-krb5-2 libhogweed2
libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libldap-2.4-2 libnettle4
libp11-kit0 libpq5 libsasl2-2 libsasl2-modules libsasl2-modules-db
libtasn1-6 python3-psycopg2
0 upgraded, 18 newly installed, 0 to remove and 0 not upgraded.
Need to get 5416 kB of archives.
After this operation, 10.4 MB of additional disk space will be used.
...
Processing triggers for libc-bin (2.19-18+deb8u6) ...
---> 978e0fa7afa7
Removing intermediate container d7d4376c9f0d
Successfully built 978e0fa7afa7
注 :Docker缓存生成过程中,所以你可能会遇到在那里你在构建运行更新的情况下,Docker缓存此更新,一段时间后,你的基地分布再次更新其来源,让你使用过时的消息人士透露,尽管做了清理和更新在你Dockerfile
。 如果您在安装或更新容器,内运行包问题apt-get clean && apt-get update
的容器内。 密切注意Docker输出以识别打字错误,并在构建时和容器内运行更新,以确保缓存的包列表不会阻碍。 语法错误和缓存问题是在Docker中构建映像时可能遇到的最常见的问题。现在让我们来看看从这些镜像运行容器时可能出现的问题。
第2步 - 解决容器命名问题
当你启动更多的容器,你最终会碰到名称冲突。命名冲突是您尝试创建与系统上已存在的容器具有相同名称的容器的位置。让我们探讨如何正确处理命名,重命名和删除容器,以避免冲突。 让我们从上一节中创建的映像启动一个容器。我们将在这个容器中运行一个交互式bash解释器来测试东西。执行以下命令:docker run -ti my_image bash
容器启动时,您会看到一个根提示符等待指示:
现在你有一个正在运行的容器,让我们看看你可能遇到什么样的问题。 当你以你刚才所做的方式运行容器时,没有显式地设置一个名字,Docker为容器分配一个随机名字。您可以通过运行在看到所有正在运行的容器及其相应的名称的
docker ps
Docker主机上的命令,正在运行的容器外。 在Docker主机上打开一个新终端并运行以下命令:
docker ps
此命令输出正在运行的容器的列表,其名称如下例所示:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
80a0ca58d6ec my_image "bash" 22 seconds ago Up 28 seconds loving_brahmagupta
名称
loving_brahmagupta
在前述输出是Docker自动分配给前面的实施例中的容器中的名称;您的名称将不同。让Docker为你的容器分配一个名字在很简单的情况下是好的,但是可以出现重大的问题;当我们部署时,我们需要一致地命名容器,以便我们可以引用它们并轻松地自动化它们。 要为容器指定一个名字,我们既可以使用
--name
参数,当我们启动容器,或者我们可以运行的容器重命名为更具描述性的。 从Docker主机的终端运行以下命令:
docker rename your_container_name python_box
然后列出您的容器:
docker ps
你会看到
python_box
在输出容器中,确认您已成功更名为容器:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
80a0ca58d6ec my_image "bash" 24 minutes ago Up 24 minutes python_box
要关闭容器,类型
exit
在包含容器运行在终端的提示:
exit
如果这不是一个选项,您可以使用以下命令从Docker主机上的另一个终端杀死容器:
docker kill python_box
当你以这种方式杀死容器时,Docker返回刚刚被杀死的容器的名称:
Outputpython_box
为了确保
python_box
不存在了,再次列出所有正在运行的容器:
docker ps
如预期,容器不再列出:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
现在,你可能会认为你可以启动另一个名为容器
python_box
,但让我们看到,当我们尝试会发生什么。 我们将使用
--name
参数这段时间设置容器的名称:
docker run --name python_box -ti my_image bash
Outputdocker: 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..
See 'docker run --help'.
当您构建映像并重用现有映像的名称时,现有映像将被覆盖,如您所见。容器有点复杂,因为你不能覆盖已经存在的容器。 Docker说
python_box
已经存在,尽管我们刚刚杀了它,它甚至没有与上市
docker ps
。 它不是运行,但它仍然可用,以防你想要重新启动它。 我们停止了,但我们没有删除它。 该
docker ps
命令仅显示了
运行容器,不是
所有的容器。 要列出
所有 Docker容器中,运行,否则,传递
-a
标志(别名
--all
),以
docker ps
:
docker ps -a
现在,我们的
python_box
容器中出现的输出:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
80a0ca58d6ec my_image "bash" 12 minutes ago Exited (137) 6 minutes ago python_box
容器与存在
Exited (137)
的状态,这就是为什么我们碰到了命名问题,当我们试图创建具有相同名称的新容器。 当你想彻底删除一个容器,可以使用
docker rm
命令。在终端中执行此命令:
docker rm python_box
再次,Docker输出刚删除的容器的名称:
Outputpython_box
警告 :此命令将失败,并输出错误信息,如果容器仍在运行,所以一定要停止或先杀死它。 让我们创建一个新的容器名为
python_box
现在我们去掉了前一个:
docker run --name python_box -ti my_image bash
该过程完成,我们再次呈现一个根shell:
现在让我们杀死和删除容器,以便将来避免出现问题。从Docker主机上的另一个Terminal会话中,删除容器并使用以下命令删除它:
docker kill python_box && docker rm python_box
我们将两个命令链接在一起,所以输出显示容器名称两次。第一个输出验证我们已经杀死了容器,另一个确认我们已删除它。
Outputpython_box
python_box
保持
docker ps -a
记住遇到的名称问题时,并确保你的容器停止,您尝试使用相同的名称来重新创建之前删除。 命名容器使您更容易管理您的基础设施。名称还使得容器之间的通信变得容易,正如你将看到的。
第3步 - 解决容器通信问题
Docker使得容易实例化几个容器,所以你可以在每个容器运行不同的或甚至冗余的服务。如果服务失败或遭到破坏,您只需更换一个新的服务器,同时保持基础设施的其余部分不变。但是你可能遇到了使这些容器相互通信的问题。 让我们创建两个通信的容器,以便我们可以探索潜在的通信问题。我们将使用我们现有的映像创建一个运行Python的容器,以及另一个运行PostgreSQL实例的容器。我们将使用现有的PostgreSQL的官方图片,从 Docker Hub的容器中。 让我们先创建PostgreSQL容器。我们将通过给这个容器的名称--name
标志,以便我们能与其他容器链接时,很容易识别。 我们叫它
postgres_box
。 以前,当我们推出一个容器时,它运行在前台,接管我们的终端。我们要在后台启动,我们可以用做PostgreSQL数据库容器
--detach
标志。 最后,而不是运行
bash
,我们将运行
postgres
命令,该命令将启动容器内的PostgreSQL数据库服务器。 执行以下命令启动容器:
docker run --name postgres_box --detach postgres
Docker将从Docker Hub下载映像并创建容器。然后它将返回在后台运行的容器的完整ID:
OutputUnable to find image 'postgres:latest' locally
latest: Pulling from library/postgres
6a5a5368e0c2: Already exists
193f770cec44: Pull complete
...
484ac0d6f901: Pull complete
Digest: sha256:924650288891ce2e603c4bbe8491e7fa28d43a3fc792e302222a938ff4e6a349
Status: Downloaded newer image for postgres:latest
f6609b9e96cc874be0852e400381db76a19ebfa4bd94fe326477b70b8f0aff65
列出容器以确保此新容器正在运行:
docker ps
输出证实
postgres_box
容器在后台运行,端口
5432
,PostgreSQL数据库端口:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7a230b56cd64 postgres_box "/docker-entrypoint.s" Less than a second ago Up 2 seconds 5432/tcp postgres
现在让我们启动Python容器。为了使Python的容器中,以“看到”服务的内部运行的程序
postgres_box
容器,需要我们手动的Python我们容器链接到
postgres_box
使用容器
--link
参数。 要创建链接,我们指定容器的名称,后跟链接的名称。 我们将使用链接的名称来指代
postgres_box
从Python容器内的容器。 发出以下命令启动Python容器:
docker run --name python_box --link postgres_box:postgres -ti my_image bash
现在,让我们尝试从里面连接到PostgreSQL
python_box
容器。 我们以前安装
nano
的内部
python_box
容器,以便让我们用它来创建一个简单的Python脚本来测试到PostgreSQL的连接。 在终端为
python_box
容器,执行以下命令:
nano pg_test.py
然后将以下Python脚本添加到该文件:
pg_test.py
"""Test PostgreSQL connection."""
import psycopg2
conn = psycopg2.connect(user='postgres')
print(conn)
保存文件并退出编辑器。让我们看看当我们尝试从我们的脚本连接到数据库时会发生什么。在容器中执行脚本:
python3 pg_test.py
我们看到的输出表明连接到数据库时出现问题:
OutputTraceback (most recent call last):
File "pg_test.py", line 5, in <module>
conn = psycopg2.connect(database="test", user="postgres", password="secret")
File "/usr/lib/python3/dist-packages/psycopg2/__init__.py", line 164, in connect
conn = _connect(dsn, connection_factory=connection_factory, async=async)
psycopg2.OperationalError: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
我们已经确保了
postgres_box
容器运行,我们将它连接到
python_box
容器,所以后来发生了什么?嗯,当我们尝试连接时,我们从未指定数据库主机,所以Python尝试连接到本地运行的数据库,这将不工作,因为服务不是本地运行,它运行在不同的容器,它在不同的计算机上。 您可以使用创建链接时设置的名称访问链接的容器。在我们的例子中,我们使用
postgres
引用
postgres_box
是的运行我们的数据库服务器容器。 您可以通过查看验证这个
/etc/hosts
的内提交
python_box
容器:
cat /etc/hosts
您将看到所有可用的主机及其名称和IP地址。我们
postgres
服务器是清晰可见。
Output127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 postgres f6609b9e96cc postgres_box
172.17.0.3 3053f74c8c13
所以让我们修改我们的Python脚本并添加主机名。打开文件。
nano pg_test.py
然后在连接字符串中指定主机:
/pg_test.py
"""Test PostgreSQL connection."""
import psycopg2
conn = psycopg2.connect(host='postgres', user='postgres')
print(conn)
保存文件,然后再次运行脚本。
python3 pg_test.py
此时脚本完成,没有任何错误:
Output<connection object at 0x7f64caec69d8; dsn: 'user=postgres host=7a230b56cd64', closed: 0>
在尝试连接到其他容器中的服务时,请记住容器名称,并编辑应用程序凭据以引用这些容器的链接名称。
结论
我们刚刚介绍了在使用Docker容器时可能遇到的最常见的问题,从构建映像到部署容器网络。 Docker有一个--debug
它主要用于Docker开发商标志。但是,如果想了解更多关于Docker内部结构,请尝试在调试模式下运行Docker命令以获得更详细的输出:
docker -D [command] [arguments]
虽然软件中的容器已经存在了一段时间,但Docker本身只存在了三年,可能相当复杂。把你的时间来熟悉条款和
生态系统 ,你将看到如何都有点外国起初一些概念将很快做出了很大的意义。