跳到主要内容

Docker 101

Docker 从 2013 年发布至今一直广受瞩目,很快被业界接受并使用,本文主要用于给初识 docker 的新手扫扫盲,让新手更容易上手 Docker,并且也是作为自己知识储备积累的一个过程吧。

Docker 是什么,它为什么重要 #

在现代软件开发过程中,Docker 已经成为一种非常重要的技术。Docker 是一个开源的应用容器引擎,它允许开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器或 Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。

Docker 的优势 #

  • 首先,Docker 的重要性在于它解决了“在我电脑上可以运行,但不知为何在你电脑上不能运行”的问题。这是因为 Docker 可以将应用程序及其所有依赖打包在内的容器中。这意味着,不论你的运行环境是什么,应用程序总是在相同的、预定义的环境中运行,极大地增强了应用的可移植性。

  • 其次,Docker 的另一个主要优点是它的性能。与传统的虚拟机相比,Docker 容器共享主机的操作系统,而不是模拟一个完整的操作系统,因此 Docker 容器可以更轻量、更快速地启动和停止。

  • 最后,Docker 也对软件开发和运维工作流程带来了变革。由于 Docker 的一致性和可移植性,开发人员可以在本地开发和测试 Docker 容器,并将同一容器部署到生产环境中。同时,运维团队可以专注于如何最好地管理和调度容器,而不是在维护和升级各种环境中消耗时间。

安装 Docker #

得益于 Docker 的全球流行,它拥有庞大的社区和十分详尽的官方文档,关于各个平台如何安装 Docker 的内容可以快速的从官方安装文档中找到范例,此处就不再赘述了。

理解 Docker 基础概念 #

在开始使用 Docker 之前,我们首先要理解一些 Docker 的核心概念,包括 Docker 镜像(Images)、容器(Containers)以及 Docker Registry/Docker Hub等。

  • Docker 镜像(Images): Docker 镜像就像是应用程序的“蓝图”,它包含了运行应用程序所需的一切,包括代码、运行时、库、环境变量和配置文件。Docker 镜像是只读的,一旦创建就不能修改。你可以把它想象成一个轻量级的、独立的、可执行的软件包,包含了运行软件所需的一切。

  • 容器(Containers):如果说 Docker 镜像是应用的蓝图,那么容器就是这个蓝图的一个实例。你可以基于同一个镜像来启动多个相互隔离的容器,每个容器都有自己的文件系统,每个容器都在自己的环境中运行,互不干扰。在容器中做的任何修改(例如,修改文件、安装新软件)都只会影响当前容器,不会影响基于同一镜像启动的其他容器,也不会影响镜像本身。

  • Docker Registry/Docker Hub: Docker Registry 是用于存储和分发 Docker 镜像的服务。Docker Hub 是 Docker 官方维护的公开的 Registry,包含了数以百万计的公开 Docker 镜像。除了 Docker Hub 之外,用户还可以自建私有的 Docker Registry,或者使用其他一些第三方的 Docker Registry 服务,如 Google Container Registry, Amazon Elastic Container Registry 等。

运行你的第一个 Docker 容器 #

完成 Docker 的安装并理解了 Docker 的基本概念后,我们可以开始运行我们的第一个 Docker 容器了。作为一个经典的示例,我们将尝试运行一个 “hello-world” 的 Docker 容器。

首先,打开你的命令行界面(Terminal),输入以下命令:

docker run hello-world

这个命令告诉 Docker 运行一个名为 “hello-world” 的镜像。由于这是你第一次运行 “hello-world” 镜像,Docker 将会发现你的本地环境并没有这个镜像,所以它会从 Docker Hub(Docker 的官方镜像仓库)下载这个镜像到你的机器上。

下载完成后,Docker 将基于 “hello-world” 镜像创建一个新的容器,并运行这个容器。“hello-world” 容器运行后,它将会在你的命令行界面输出一条欢迎信息,然后结束运行并退出。

要验证 “hello-world” 容器已经运行并退出,你可以运行以下命令来查看你的 Docker 容器运行历史:

docker ps -a

在这个命令的输出中,你应该可以看到 “hello-world” 容器的状态是 “Exited”,这表示这个容器已经完成运行并已退出。

构建你的第一个 Docker 镜像 #

接下来我们尝试构建一个简单的 Docker 镜像,这个镜像将会运行一个基本的 Python 网页服务器。

首先,我们需要创建一个新的目录来保存我们的 Docker 项目,然后在这个目录中创建两个文件:一个是应用程序代码 app.py,另一个是 Dockerfile

我们的 Python 网页服务器代码 app.py 非常简单,只有几行:

from http.server import BaseHTTPRequestHandler, HTTPServer

class RequestHandler(BaseHTTPRequestHandler):
    def _send_response(self, message):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write(bytes(message, "utf8"))

    def do_GET(self):
        self._send_response("Hello, Docker!")

def run(server_class=HTTPServer, handler_class=RequestHandler):
    server_address = ('', 8000)
    httpd = server_class(server_address, handler_class)
    print('Starting httpd...')
    httpd.serve_forever()

if __name__ == "__main__":
    run()

接下来是 Dockerfile:

# 使用官方 Python 运行时作为父镜像
FROM python:3.9-slim-buster

# 将工作目录设置为 /app
WORKDIR /app

# 将当前目录内容复制到位于 /app 的容器中
COPY . /app

# 使得我们的服务能运行在容器中
CMD ["python", "app.py"]

一旦我们有了这两个文件,我们就可以通过运行 docker build 命令来创建我们的 Docker 镜像。在你的命令行界面中,切换到包含这两个文件的目录,然后运行以下命令:

docker build -t my-python-app .

这个命令会告诉 Docker 构建一个新的镜像,并且将这个镜像标记为 “my-python-app”。

构建完成后,你就可以像运行 “hello-world” 一样来运行你的 Python 应用了:

docker run -p 8000:8000 my-python-app

现在,如果你在浏览器中访问 http://localhost:8000,你应该能看到 “Hello, Docker!” 的欢迎信息。

理解 Docker 网络和数据卷 #

在深入使用 Docker 之前,了解 Docker 网络和数据卷的概念是非常重要的。这两个概念是你在使用 Docker 构建和部署复杂应用时不可或缺的工具。

Docker 网络 #

Docker 网络让你能够控制和定义容器间如何相互通信。默认情况下,Docker 提供了三种类型的网络模式:

  • Bridge:这是默认的网络模式。当你运行一个新的容器时,Docker 会自动为这个容器分配到一个内部的 IP 地址,并且这个容器可以和运行在同一个 Docker 主机上的其他容器进行通信。
  • Host:在这个网络模式下,容器直接使用主机的网络,无需进行任何的网络隔离或路由。
  • None:在这个网络模式下,容器会运行在自己的网络栈中,但是不会进行任何配置。

在一些复杂的应用场景中,你可能需要自定义网络来满足特定的网络需求。

Docker 数据卷 #

Docker 数据卷是一种持久化数据的机制。默认情况下,当一个容器被删除时,它里面的所有数据都会丢失。这当然不是我们想要的,特别是当你的容器需要处理的数据是持久化的,比如数据库。

数据卷允许你将容器中的数据持久化存储在 Docker 主机上,或者是在第三方服务或设备上。使用数据卷,你的应用可以无缝地读写数据,而无需关心数据如何持久化存储。

这两个概念可能一开始听起来有些复杂,但是随着你不断地使用 Docker,你会发现它们都是非常实用的功能。

Docker 最佳实践 #

在使用 Docker 的过程中,遵循一些最佳实践可以帮助我们更有效率、更安全地使用 Docker。以下是一些值得注意的 Docker 最佳实践:

  • 保持镜像尽可能小:大的 Docker 镜像不仅会消耗更多的存储空间,而且在构建和部署的过程中也会花费更多的时间。一般来说,我们应该只将运行应用所必需的文件和依赖添加到 Docker 镜像中,并使用 .dockerignore 文件来排除不需要的文件。
  • 使用专门的用户运行应用:出于安全考虑,我们应该避免以 root 用户的身份运行应用。相反,我们应该为每个应用创建一个专门的非权限用户,并以该用户的身份运行应用。
  • 使用多阶段构建:多阶段构建是一种可以帮助我们减小 Docker 镜像大小的技术。它允许我们在一个阶段构建我们的应用,并在另一个阶段复制构建结果,而不需要复制构建过程中产生的所有额外文件。
  • 使得容器易于配置:应用的配置应该可以通过环境变量或者命令行参数来指定,而不是硬编码在 Docker 镜像中。这使得容器在不同的环境中运行时能更灵活地进行配置。
  • 合理使用数据卷和 Docker 网络:理解和合理使用数据卷和 Docker 网络对于构建可扩展的 Docker 应用至关重要。尽可能地使用这两个功能,使你的应用在 Docker 中更好地运行。

以上这些最佳实践只是其中的一部分,而且每一个最佳实践都有其特定的使用场景。在使用 Docker 的过程中,随着经验的逐渐积累,你会慢慢根据你自己的需求和经验来决定哪些最佳实践适合你。

总结 #

Docker 是一项强大的技术,它为软件开发带来了许多便利。然而,学习 Docker 就像旅行一样,总是充满了新的发现和惊喜。希望这篇文章能作为你旅程的起点,帮助你打开 Docker 的大门,探索更多的可能性。我也鼓励你在阅读完这篇文章后,动手尝试安装 Docker,运行你的第一个 Docker 容器,甚至尝试构建你自己的 Docker 镜像。只有通过实践,你才能真正理解并掌握 Docker。

在你的 Docker 学习旅程中,如果遇到任何问题,记得查阅官方文档,或者参与到 Docker 社区中来。每一个 Docker 用户都是这个社区的一部分,我们都在互相学习,互相帮助。