Docker学习笔记 #1 基本概念与原理
本文最后更新于:2024年8月7日 中午
软件开发最大的麻烦事之一就是环境配置、操作系统设置、各种库和组件的安装。目前最流行的 Linux 容器解决方案之一就是 Docker,它极大地缓解了上述问题。
Docker 是一个基于 Go 语言实现的开源项目,由 dotCloud 公司在 2013 年初发起。其目标是「Build, Ship and Run Any App, Anywhere」——构建、传输并运行任意应用程序,随处可运行。
Docker 主要的概念包括镜像、容器和仓库。其核心技术基于 Linux 容器技术,能够有效地将操作系统资源划分为多个独立的组,以更好地管理资源使用。
什么是 Docker?
虚拟机与 Linux 容器
虚拟机(Virtual Machine,VM)是一种运行完整操作系统的虚拟化解决方案。它允许在一个操作系统中运行另一个操作系统,例如在 Windows 上运行 Linux。对于应用程序来说,虚拟机与真实系统无异,而对于底层系统来说,虚拟机只是一个文件,便于创建和删除。尽管虚拟机能够还原软件的原始环境,但也存在以下缺点:
- 资源占用多:虚拟机会独占一部分内存和硬盘空间,甚至在虚拟机内的应用程序仅占用少量资源时,虚拟机本身仍然需要大量的内存和存储来运行。
- 冗余步骤多:作为一个完整的操作系统,虚拟机需要执行一系列系统级操作,例如用户登录,这些步骤往往无法省略。
- 启动慢:启动虚拟机的时间与启动操作系统相当,通常需要数分钟才能开始运行应用程序。
为了克服虚拟机的这些不足,Linux 发展出了一种更轻量级的虚拟化技术——Linux 容器(Linux Containers,LXC)。Linux 容器并不模拟一个完整的操作系统,而是通过隔离进程来实现。对于容器内部的进程来说,它们接触到的资源都是虚拟化的,从而实现与底层系统的隔离。
相较于虚拟机,Linux 容器有以下优势:
- 启动快:容器内的应用程序直接作为底层系统的一个进程运行,启动速度快,类似于启动一个本地进程。
- 资源占用少:容器仅使用所需的资源,不会占用未使用的资源。而虚拟机作为完整的操作系统,需要占用所有资源。此外,多个容器可以共享资源,而虚拟机则是资源独享的。
- 体积小:容器只包含所需的组件,因此文件大小远小于包含整个操作系统的虚拟机文件。
通俗解释 Docker
Docker 的理念类似于集装箱。在运输业中,集装箱标准化了货物的存储和运输方式,使得各种不同的货物可以安全、整齐地放置在同一艘船上,而不会相互影响。
Docker 也是类似的思想:它将应用程序及其运行环境打包成标准化的单元,确保在不同的系统和环境中能够一致地运行。它具有以下优势:
- 隔离不同的应用环境:不同的应用程序可能依赖不同的环境配置,如 .NET 和 PHP 可能需要不同的库和依赖。在传统环境下,这可能需要在同一服务器上安装不同的依赖,导致配置冲突。使用 Docker,可以在同一物理机上运行「多个隔离的容器」,每个容器都有自己的环境配置,类似于虚拟机,但资源开销更小。
- 一致的开发和生产环境:开发人员可能使用 Ubuntu,而运维团队使用 CentOS。传统情况下,从开发环境到生产环境的迁移可能会遇到兼容性问题。使用 Docker,开发人员可以将开发环境封装在容器中,运维团队只需部署该容器即可,无需担心「底层系统的差异」。
- 高效的资源利用:虚拟机会占用大量的系统资源,即使虚拟机内部的应用程序负载较轻。Docker 通过更高效的资源共享机制,使得多容器在同一物理机上能够更好地利用系统资源。
Docker 中的三个概念
镜像 | Image
镜像(Image)是 Docker 的基础概念之一,可以类比为虚拟机中的镜像。它包含应用程序运行所需的所有内容,包括操作系统、应用程序代码、库和依赖项。镜像是不可变的、只读的文件,类似于容器的模板。通过镜像,可以创建一个或多个 Docker 容器。例如,一个 Ubuntu 镜像包含了 Ubuntu 操作系统的环境,而一个 Apache 镜像则是在 Ubuntu 镜像的基础上加入了 Apache 服务器。
镜像是以二进制文件的形式存在的,可以从已有的镜像基础上定制新的镜像。一般情况下,推荐使用已有的镜像文件,而不是从零开始创建。通常,我们会从 Docker 仓库拉取已经配置好的镜像。
此外,镜像可以通过 Dockerfile 文件进行构建,这是一种脚本文件,用于定义如何从基础镜像生成新的镜像。以下是一个示例,用于创建一个基于 Nginx 的 Web 服务器:
1 |
|
之后就可以通过 docker build -t my-nginx-image:1.0 .
来创建镜像了。
容器 | Container
容器(Container)是镜像的运行实例。它是一个轻量级的、独立的执行环境,可以看作是一个极简的 Linux 系统,包括独立的文件系统、网络和进程空间。容器利用 Docker 引擎来运行和隔离应用程序,各个容器之间是相互独立的。
容器的特点是启动快、资源消耗少。它们由镜像创建而来,当启动一个容器时,Docker 会在镜像的基础上添加一个可写层。这意味着镜像本身不变,而容器的任何修改都记录在这个可写层中。
容器可以创建、启动、停止和删除。即使容器停止运行,其对应的容器文件依然存在,直到显式删除为止。删除容器时,可以使用 docker container rm
命令,同时可以使用 docker container ls --all
查看所有容器,包括已经停止的容器。
仓库 | Repository
仓库(Repository)是存放镜像的地方,可以类比为代码仓库。它是 Docker 镜像文件的集中存放地。一个仓库可以包含多个版本的镜像,通过标签(tag)来区分。Docker 的官方公共仓库是 Docker Hub,它提供了大量的基础镜像,如 CentOS、Ubuntu 等,以及由社区或个人维护的镜像。
仓库可以分为公共仓库和私有仓库。公共仓库是开放的,任何人都可以访问和下载镜像。而私有仓库则是用户自己搭建的镜像存储环境,常用于企业内部。Docker 提供了一个名为 registry
的官方镜像,用于搭建私有仓库。
在实际使用中,镜像可以从仓库中下载,也可以上传到仓库进行共享。对于企业和开发者来说,合理地使用和管理仓库,可以大大提高应用的开发和部署效率。
Docker 原理
Docker 的核心原理在于通过操作系统级别的虚拟化技术提供独立的运行环境。Docker 通过利用 Linux 内核中的多项功能,如命名空间(Namespaces)和控制组(Cgroups),来实现容器的隔离、资源管理和安全性。接下来介绍 Docker 工作原理的主要组成部分。
命名空间 | Namespaces
命名空间是 Linux 内核的一项功能,它提供了一种隔离机制,使得不同的进程看起来像是在独立的操作系统中运行。Docker 使用多种类型的命名空间来隔离容器之间的环境,包括:
- PID 命名空间:隔离进程 ID,使得容器内的进程 ID 不与宿主机或其他容器的进程 ID 冲突。
- 网络命名空间:隔离网络接口和网络栈,确保容器有自己的虚拟网络接口和 IP 地址,互相隔离。
- 挂载命名空间:提供独立的文件系统视图,使得容器只能看到其自身的文件系统,不会影响宿主机的文件系统。
- UTS 命名空间:隔离主机名和域名,允许容器拥有独立的主机名。
- IPC 命名空间:隔离系统 V 共享内存、信号量和消息队列,使得这些资源在容器之间不共享。
- 用户命名空间:隔离用户和组 ID,允许在容器中运行的进程以不同于宿主机的用户身份运行,提高安全性。
控制组 | Cgroups
控制组是一种 Linux 内核功能,用于限制、计量和隔离进程组的资源使用,如 CPU、内存、磁盘 I/O 和网络带宽。Docker 利用控制组来管理和限制容器的资源使用,确保容器之间不会相互干扰,并且能够有效利用系统资源。
联合文件系统 | UnionFS
Docker 使用联合文件系统(UnionFS)来构建镜像和容器。联合文件系统允许将多个文件系统层叠加在一起,并提供一个统一的视图。Docker 镜像由多层只读文件系统组成,最上层是可写的。这种分层设计使得镜像可以复用已有层,减少存储空间的占用并加速构建速度。
当创建一个容器时,Docker 会在镜像的最上层添加一个可写层。容器运行时的所有变化都只发生在这个可写层上,而基础镜像层保持不变。这种设计不仅提高了效率,还简化了容器的版本管理和部署。
容器引擎 | Docker Engine
Docker Engine 是 Docker 的核心组件,是一个基于客户端-服务器(Client-Server)架构的应用程序,负责构建、运行和管理 Docker 容器。
Docker Engine 由以下几个主要部分组成:
- Docker 守护进程(Docker Daemon):运行在宿主机上,负责处理 Docker 客户端发送的请求。它是客户端-服务器模型中的服务器部分。Docker Daemon 管理所有的 Docker 对象,包括容器、镜像、网络和数据卷。它还负责构建和分发容器镜像。使用 REST API 提供服务,客户端通过这个 API 与 Daemon 进行通信。
- Docker REST API:Docker Daemon 暴露的编程接口,允许外部工具与 Docker 进行交互。它提供了一组标准化的 HTTP 请求,可以执行所有 Docker 操作,例如创建容器、管理镜像和设置网络等。通过 REST API,可以使用编程语言创建自定义的 Docker 工具和自动化脚本。任何支持 HTTP 请求的工具都可以通过 API 与 Docker 交互,例如发送
curl
请求。 - Docker CLI:Docker 提供的命令行工具,是与 Docker Daemon 进行交互的主要方式。通过 Docker CLI,用户可以执行各种 Docker 操作,如启动容器(
docker run
)、查看正在运行的容器(docker ps
)、构建镜像(docker build
)等。Docker CLI 可以运行在任何操作系统上,并能够通过网络与远程的 Docker Daemon 通信。 - Docker Machine:用于在各种主机上安装和管理 Docker Engine。它简化了在不同平台上(如本地开发机、云服务器等)配置 Docker 环境的过程。通过 Docker Machine,可以自动创建主机、安装 Docker Engine,并配置 Docker 客户端连接到这些主机。
Docker Registry
Docker Registry 是一个集中存储和分发 Docker 镜像的系统。Docker Hub 是最流行的公共 Docker Registry,提供了大量的公共镜像供用户使用。用户也可以搭建私有 Docker Registry,用于企业内部的镜像管理。Docker 客户端通过 docker push
和 docker pull
命令与 Registry 进行交互,上传和下载镜像。