三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Docker容器化实战:从环境配置到应用部署的完整指南

Docker容器化实战:从环境配置到应用部署的完整指南

如果你是一名程序员,或者对软件开发、系统架构稍有了解,你肯定遇到过这样的场景:一个项目,代码写得飞快,功能模块也一个个堆叠起来,但当你试图把它部署到服务器上,或者交给另一个同事运行时,问题就来了——“在我本地是好的啊!”

“环境依赖缺失”、“配置文件路径不对”、“端口被占用”、“数据库连接失败”……这些问题像幽灵一样,消耗着开发者大量的时间和精力。它们不直接产生业务价值,却实实在在地拖慢了整个团队的交付节奏和协作效率。这就是软件开发中经典的“环境一致性”和“依赖管理”难题。

今天要讨论的,并不是一个具体的编程框架或算法,而是一个看似简单、实则深刻影响现代软件工程实践的核心方法论。它源于一个看似不相关的领域——容器化与Docker,但其思想内核,已经渗透到从本地开发、持续集成到云端部署的每一个环节。很多人以为Docker只是“轻量级虚拟机”或者“部署工具”,但实际上,它真正解决的是从“代码”到“可运行服务”这一链条中,最不可控、最依赖人力的“环境”环节。

本文将从一个资深开发者的视角,为你彻底拆解“环境即代码”这一理念。我们不止步于教你如何写Dockerfile和运行docker run,而是要深入理解:为什么容器技术能成为现代云原生架构的基石?它如何将混乱的环境配置转变为可版本化、可重复、可移植的“资产”?以及,作为一个开发者或团队,你应该如何从零开始,将这套方法论落地到你的日常工作中,真正告别“它在我电脑上能跑”的魔咒。

文章将包含完整的概念解析、实战操作、最佳实践和避坑指南。无论你是刚听说Docker的新手,还是已经用过但知其然不知其所以然的开发者,都能从中获得构建可靠、高效研发流程的实用知识。

1. 这篇文章真正要解决的问题:从“人肉运维”到“环境自描述”

在传统开发模式中,项目的运行环境严重依赖于个人的电脑状态。你需要手动安装特定版本的Java、Python、Node.js,配置复杂的环境变量,安装系统依赖库,甚至可能还需要调整操作系统的某些设置。这份“环境清单”可能存在于某个README文件里,也可能只存在于资深同事的脑子里。

这就导致了几个核心痛点:

  1. ** onboarding成本高**:新成员加入项目,第一周可能都在配环境,而且极易出错。
  2. ** 环境差异导致bug**:开发、测试、生产环境的不一致,是许多“灵异bug”的根源。
  3. ** 部署风险大**:运维人员需要根据文档手动在服务器上配置环境,步骤繁琐且容易遗漏。
  4. ** 难以回滚**:当新版本出现问题,想回滚到旧版本时,环境可能已经发生了变化,导致旧版本无法正常运行。

容器技术,特别是Docker,提供的解决方案本质上是:将应用程序及其完整的运行环境(包括代码、运行时、系统工具、系统库、设置)一起打包成一个标准化的、轻量级的、可执行的“镜像”。这个镜像可以在任何安装了Docker引擎的环境中,以完全一致的方式运行起来,这个运行起来的实例就是“容器”。

所以,本文要解决的核心问题是:如何将你项目中隐性的、脆弱的、手工维护的“运行环境”,转变为显性的、坚固的、可自动化管理的“基础设施代码”。这不仅关乎一个工具的使用,更关乎团队协作效率和软件交付质量的根本性提升。

2. 基础概念与核心原理:容器、镜像与Docker引擎

在深入实操前,必须厘清几个核心概念,这是理解后续所有操作的基础。

2.1 容器 vs. 虚拟机

这是最常见的误解。很多人把Docker容器看作轻量级虚拟机,虽然类比有助于理解,但本质不同。

特性虚拟机 (VM)Docker 容器
虚拟化层级硬件虚拟化。通过Hypervisor虚拟出一套完整的硬件,在上面安装完整的客户机操作系统(Guest OS)。操作系统级虚拟化。所有容器共享主机(Host)的操作系统内核,但拥有独立的用户空间(文件系统、进程、网络等)。
启动速度慢(分钟级)。需要启动完整的操作系统。极快(秒级)。直接启动应用进程。
性能损耗高。需要模拟硬件,并运行完整的OS。低。直接调用主机内核,接近原生性能。
磁盘占用大(通常GB级)。每个VM包含完整的OS。小(通常MB级)。容器镜像分层共享基础层。
隔离性强。完全的OS级别隔离,更安全。较弱。进程级别隔离,共享内核,存在潜在安全风险(可通过配置增强)。
典型代表VMware, VirtualBox, KVMDocker, Containerd, Podman

简单来说:虚拟机是“房子里的房子”,而容器是“房间里的独立公寓”。虚拟机提供了完整的隔离,但笨重;容器轻便高效,但隔离性依赖于主机内核的能力。

2.2 Docker 核心三要素

  1. 镜像 (Image):一个只读的模板。它包含了运行应用所需的文件系统、库、环境变量和配置。镜像可以看作面向Docker引擎的“软件安装包”。镜像是分层的,每一层代表Dockerfile中的一条指令,这种设计使得镜像可以高效复用和传输。
  2. 容器 (Container):镜像的一个运行实例。你可以创建、启动、停止、移动或删除容器。容器是隔离的、资源受限的进程沙箱。容器运行时,会在镜像的只读层之上创建一个可写的“容器层”,所有修改都发生在此层,容器停止后,此层默认不保留(除非使用数据卷)。
  3. 仓库 (Registry):集中存放镜像的地方。最著名的是Docker Hub,类似于代码的GitHub。你可以拉取(pull)公共镜像,也可以推送(push)自己的私有镜像到公共或私有仓库。

2.3 Docker 引擎 (Docker Engine)

这是Docker的核心,一个客户端-服务器架构的应用。

  • Docker 守护进程 (Docker Daemon):一个长期运行的后台服务(dockerd),负责管理镜像、容器、网络、存储卷等。
  • Docker 客户端 (Docker Client):命令行工具docker。用户通过它与守护进程交互,发送指令(如docker run)。
  • REST API:守护进程暴露API,客户端通过API与之通信。

理解了这些,你就明白了Docker的工作流:通过Dockerfile定义如何构建镜像 -> 使用docker build命令构建镜像 -> 将镜像推送到仓库 -> 在任何地方使用docker run从仓库拉取并运行容器。

3. 环境准备与前置条件

我们将在一个最通用的环境——Ubuntu 22.04 LTS上进行演示。其他Linux发行版、macOS或Windows的安装步骤类似,请参考官方文档。

核心前提:你需要一台具有sudo权限的Linux机器,或者本地安装的虚拟机/WSL2(Windows用户)。

3.1 卸载旧版本(如有)

为避免冲突,先清理系统上可能存在的旧版本Docker。

sudo apt-get remove docker docker-engine docker.io containerd runc

3.2 安装必要工具

sudo apt-get update sudo apt-get install \ ca-certificates \ curl \ gnupg \ lsb-release

3.3 添加Docker官方GPG密钥和仓库

这是为了确保从官方源安全地下载软件包。

# 创建密钥环目录 sudo mkdir -p /etc/apt/keyrings # 下载并添加GPG密钥 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 添加稳定版仓库 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

3.4 安装Docker引擎

更新包索引并安装最新版本的Docker Engine、containerd和Docker Compose插件。

sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

3.5 验证安装

安装完成后,运行经典的“Hello World”镜像来验证Docker引擎是否正常工作。

sudo docker run hello-world

如果看到“Hello from Docker!”等欢迎信息,说明安装成功。注意,第一次运行会从Docker Hub拉取hello-world镜像。

3.6 (可选但推荐)以非root用户身份管理Docker

默认情况下,docker命令需要sudo权限。为了避免每次输入sudo,可以将当前用户加入docker用户组。

sudo usermod -aG docker $USER

重要:执行此命令后,你需要完全退出当前终端会话并重新登录,或者重启系统,才能使组权限生效。之后,你就可以直接使用docker命令了。

4. 核心流程拆解:从零构建一个Python Web应用镜像

理论说再多,不如亲手实践。我们以一个简单的Python Flask Web应用为例,完整走一遍“编写应用 -> 编写Dockerfile -> 构建镜像 -> 运行容器 -> 访问服务”的流程。

4.1 第一步:创建项目目录和应用代码

首先,创建一个工作目录并编写最简单的Flask应用。

mkdir my-flask-app && cd my-flask-app

创建一个名为app.py的文件,内容如下:

# app.py from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, Docker! This is my first containerized Flask app.' if __name__ == '__main__': # 注意:在生产环境中,不应使用Flask自带的开发服务器。 # 这里为了演示,监听所有公开IP(0.0.0.0)的5000端口。 app.run(debug=True, host='0.0.0.0', port=5000)

4.2 第二步:创建依赖文件requirements.txt

Flask是一个第三方库,我们需要在镜像中安装它。requirements.txt是Python项目声明依赖的标准文件。

# requirements.txt Flask==2.3.3

这里我们固定了Flask的版本,这是保证环境一致性的关键一步。

4.3 第三步:编写Dockerfile——构建镜像的“蓝图”

这是整个流程的灵魂。Dockerfile是一个文本文件,包含了一系列指令,告诉Docker如何一步步构建我们的镜像。

在项目根目录创建名为Dockerfile的文件(注意没有后缀):

# Dockerfile # 第一阶段:使用官方Python轻量级镜像作为基础 FROM python:3.11-slim AS builder # 设置工作目录,后续命令都在此目录下执行 WORKDIR /app # 将依赖文件复制到工作目录 COPY requirements.txt . # 安装项目依赖到系统路径(使用清华PyPI镜像加速,国内环境推荐) RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt # 第二阶段:创建更小的运行时镜像 FROM python:3.11-slim # 设置环境变量,确保Python输出直接显示在终端,不缓冲 ENV PYTHONUNBUFFERED=1 # 从builder阶段复制已安装的Python包 COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY --from=builder /usr/local/bin /usr/local/bin WORKDIR /app # 将应用代码复制到容器内的/app目录 COPY app.py . # 声明容器运行时监听的端口(仅具文档性,实际映射在运行命令中) EXPOSE 5000 # 定义容器启动时执行的命令 CMD ["python", "app.py"]

关键指令解析

  • FROM: 指定基础镜像。我们选择官方的python:3.11-slim,它比完整版更小。
  • WORKDIR: 设置工作目录,相当于cd
  • COPY: 将主机文件复制到镜像内。
  • RUN: 在构建镜像时执行命令,这里用于安装依赖。
  • ENV: 设置环境变量。
  • EXPOSE: 声明容器打算使用的端口,这是一个元数据,方便他人理解。
  • CMD: 指定容器启动时默认运行的命令。一个Dockerfile中只能有一个CMD

我们使用了多阶段构建:第一阶段(builder)专门用于安装依赖,第二阶段只复制安装好的依赖包,舍弃了构建工具等中间文件,最终得到的镜像体积更小,安全性也更高。

4.4 第四步:构建Docker镜像

在包含Dockerfile的目录下,执行构建命令。-t参数用于给镜像打标签(命名),格式通常是仓库名/镜像名:标签,不指定仓库名则默认为本地。

docker build -t my-flask-app:1.0 .

命令最后的.表示构建上下文是当前目录。Docker引擎会将当前目录的所有文件发送给守护进程进行构建。构建过程会逐条执行Dockerfile中的指令,并生成镜像层。

4.5 第五步:运行容器

镜像构建成功后,就可以运行它了。

docker run -d -p 8080:5000 --name my-flask-container my-flask-app:1.0
  • -d: 后台运行(detached mode)。
  • -p 8080:5000: 端口映射。将主机的8080端口映射到容器的5000端口(我们在app.py中监听的端口)。
  • --name: 给容器起一个名字,便于后续管理。
  • my-flask-app:1.0: 要运行的镜像名和标签。

4.6 第六步:验证应用

容器运行后,你可以在主机上打开浏览器,访问http://localhost:8080,或者使用curl命令:

curl http://localhost:8080

你应该能看到返回的信息:Hello, Docker! This is my first containerized Flask app.

5. 完整示例与代码实现:一个更贴近实战的Node.js应用

为了加深理解,我们再来看一个Node.js Express应用的例子,并引入环境变量、数据卷等更实用的功能。

5.1 项目结构

my-node-app/ ├── Dockerfile ├── package.json ├── server.js └── .dockerignore

5.2 应用代码

server.js:

// server.js const express = require('express'); const app = express(); const port = process.env.PORT || 3000; // 从环境变量读取端口,默认3000 const message = process.env.APP_MESSAGE || 'Hello from Node.js in Docker!'; app.get('/', (req, res) => { res.send(`<h1>${message}</h1><p>Server is running on port ${port}</p>`); }); app.get('/health', (req, res) => { res.status(200).json({ status: 'UP' }); }); app.listen(port, () => { console.log(`App listening at http://localhost:${port}`); });

package.json:

{ "name": "my-node-app", "version": "1.0.0", "description": "A simple Node.js Docker demo", "main": "server.js", "scripts": { "start": "node server.js" }, "dependencies": { "express": "^4.18.2" } }

.dockerignore:

node_modules npm-debug.log .git .gitignore README.md

这个文件非常重要,它告诉Docker在构建镜像时忽略哪些文件和目录,避免将本地的node_modules或日志文件等不必要的、庞大的内容复制进镜像,从而减小镜像体积并避免潜在冲突。

5.3 Dockerfile

# Dockerfile # 使用官方Node.js LTS版本作为基础镜像 FROM node:18-alpine # 设置工作目录 WORKDIR /usr/src/app # 复制package.json和package-lock.json(如果存在) COPY package*.json ./ # 安装生产依赖(使用npm ci可以确保精确安装lock文件中的版本,适合CI/CD环境) RUN npm ci --only=production # 复制应用源代码 COPY . . # 应用运行时需要的非root用户(安全最佳实践) RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 -G nodejs USER nodejs # 声明容器监听的端口 EXPOSE 3000 # 定义启动命令,使用npm start脚本 CMD [ "npm", "start" ]

5.4 构建与运行(带环境变量和数据卷)

  1. 构建镜像

    docker build -t my-node-app:1.0 .
  2. 运行容器(传递环境变量)

    docker run -d \ -p 3000:3000 \ --name node-app \ -e PORT=3000 \ -e APP_MESSAGE="Welcome to our Containerized Service!" \ my-node-app:1.0

    访问http://localhost:3000将看到自定义的消息。

  3. 使用数据卷(Volume)持久化数据: 假设应用会产生日志文件app.log。我们不希望日志随着容器销毁而丢失。

    # 创建一个命名数据卷 docker volume create node-app-logs # 运行容器,将数据卷挂载到容器内的日志目录 docker run -d \ -p 3000:3000 \ --name node-app-with-logs \ -v node-app-logs:/usr/src/app/logs \ my-node-app:1.0

    这样,即使容器被删除,node-app-logs这个数据卷里的日志文件依然存在,可以被新的容器挂载使用。

6. 运行结果与效果验证

运行上述命令后,如何确认一切正常?

6.1 查看容器状态

docker ps

这个命令列出正在运行的容器。你应该能看到my-flask-containernode-app的状态是Up

6.2 查看容器日志

日志是排查问题的第一现场。

# 查看最后N行日志 docker logs --tail 50 my-flask-container # 实时查看日志(类似 tail -f) docker logs -f node-app

对于我们的Node.js应用,你应该能在日志中看到App listening at http://localhost:3000的输出。

6.3 进入容器内部(调试用)

有时需要进入容器内部检查文件、运行命令。

docker exec -it my-flask-container /bin/bash # 或对于Alpine基础镜像的容器 # docker exec -it node-app /bin/sh

进入后,你可以ls查看文件,cat app.py查看代码,或者pip list/npm list查看安装的包。

6.4 健康检查端点

我们为Node.js应用设计了/health端点。这是一个良好的实践,便于监控系统(如Kubernetes)检查应用是否存活。

curl http://localhost:3000/health

预期返回:{"status":"UP"}

6.5 停止和清理容器

# 停止容器 docker stop my-flask-container # 启动已停止的容器 docker start my-flask-container # 删除已停止的容器 docker rm my-flask-container # 强制删除运行中的容器 docker rm -f node-app # 删除不再使用的镜像 docker rmi my-flask-app:1.0

7. 常见问题与排查思路

在实际使用中,你一定会遇到各种问题。下表列出了典型问题及其排查路径。

问题现象可能原因排查方式解决方案
docker run报错Unable to find image1. 镜像名拼写错误。
2. 镜像不存在于本地或Docker Hub(对于私有镜像)。
1.docker images查看本地镜像列表。
2.docker pull <image_name>尝试拉取。
1. 检查镜像名和标签。
2. 确认网络可访问Docker Hub或私有仓库。
3. 确保已正确构建或拉取镜像。
docker build失败,如npm ERR!pip ERR!1. 网络问题,无法下载依赖。
2.Dockerfile中依赖版本冲突或不存。
3. 构建上下文缺少必要文件(如package.json)。
1. 查看构建失败日志的最后几行错误信息。
2. 检查Dockerfile中的COPY指令路径是否正确。
3. 在DockerfileRUN命令前加RUN cat package.json调试。
1. 使用国内镜像源(如示例中的清华源)。
2. 确保package.jsonrequirements.txt文件存在且语法正确。
3. 使用.dockerignore排除无关文件,确保构建上下文精简。
容器启动后立即退出 (Exited (0))1. 容器内主进程执行完毕退出(常见于一次性任务)。
2.CMDENTRYPOINT指定的命令不存在或执行失败。
docker logs <container_id>查看退出前的日志。1. 对于Web服务,确保CMD是前台持久运行命令(如npm start,python app.py),而不是后台命令。
2. 检查命令路径和权限。
访问localhost:port连接被拒绝1. 容器没有运行。
2. 端口映射错误(主机端口:容器端口)。
3. 应用在容器内未监听0.0.0.0
1.docker ps确认容器状态。
2.docker port <container_id>查看端口映射。
3.docker exec进入容器,curl localhost:<container_port>测试内部连通性。
1. 确保容器在运行 (Up状态)。
2. 检查-p参数,如-p 8080:5000意为主机8080映射容器5000。
3.关键:确保应用代码监听0.0.0.0(如Flask的host='0.0.0.0'),而不是127.0.0.1
容器内应用无法连接外部服务(如数据库)1. 容器网络模式问题。
2. 使用localhost127.0.0.1指代主机服务。
1.docker network lsdocker network inspect
2. 在容器内pingcurl外部服务地址测试。
1. 对于宿主机上的服务,在容器内应使用宿主机的IP地址或特殊DNS名host.docker.internal(Docker Desktop支持)。
2. 对于其他容器,使用Docker网络,通过容器名通信。
镜像体积过大1. 基础镜像选择太庞大(如ubuntu)。
2. 构建过程中产生了大量缓存或中间文件未清理。
docker images查看镜像大小。docker history <image_name>查看各层大小。1. 使用轻量级基础镜像(如alpine,slim变体)。
2. 使用多阶段构建(如本文示例)。
3. 在RUN命令中合并清理操作,如apt-get update && apt-get install -y package && rm -rf /var/lib/apt/lists/*
权限错误,如Permission denied1. 容器内进程以root运行,但挂载的宿主机目录权限不足。
2. 镜像中文件权限设置过严。
1.docker exec进入容器,ls -l查看文件权限。
2. 检查宿主机挂载目录的权限。
1. 在Dockerfile中使用USER指令指定非root用户运行(安全最佳实践)。
2. 调整宿主机目录权限,或使用-u参数指定运行用户ID。

8. 最佳实践与工程建议

掌握了基础操作和排错后,遵循以下最佳实践能让你的容器化之路走得更稳、更远。

8.1 镜像构建最佳实践

  1. 使用官方、特定版本的基础镜像:避免使用latest标签,明确指定稳定版本(如python:3.11-slim),保证构建的可重复性。
  2. 利用构建缓存:Dockerfile指令按顺序执行,每条指令都会生成一层镜像。将变化频率低的指令(如安装系统包、依赖)放在前面,变化频率高的指令(如复制源代码)放在后面,可以最大化利用缓存,加速构建。
  3. 使用多阶段构建:如示例所示,可以显著减小最终镜像体积,并提高安全性(最终镜像不包含构建工具)。
  4. 一个容器一个进程:这是微服务架构下的核心原则。每个容器只运行一个主进程(如一个Web服务器、一个数据库)。这使得容器更易于管理、扩展和故障隔离。
  5. 使用.dockerignore文件:排除node_modules,.git, 日志文件等,避免它们被发送到Docker守护进程,影响构建速度和镜像大小。
  6. 设置非root用户:在Dockerfile中通过USER指令指定一个非root用户来运行应用,这是重要的安全措施。

8.2 容器运行与管理最佳实践

  1. 限制容器资源:使用-m限制内存,--cpus限制CPU,防止单个容器耗尽主机资源。
    docker run -d -m 512m --cpus="1.5" my-app
  2. 使用命名卷进行数据持久化:对于数据库文件、上传目录、日志等需要持久化的数据,务必使用Docker数据卷(docker volume create)或绑定挂载,而不是存储在容器内部的可写层。
  3. 配置重启策略:使用--restart参数定义容器退出时的行为。对于生产服务,--restart=unless-stopped--restart=always是常见选择。
    docker run -d --restart=unless-stopped my-app
  4. 使用Docker Compose管理多容器应用:当应用包含多个服务(如Web应用+数据库+缓存)时,使用docker-compose.yml文件来定义和运行整个应用栈,比手动运行多个docker run命令要清晰和高效得多。
  5. 日志驱动配置:默认的json-file日志驱动可能会占满磁盘。对于生产环境,考虑配置日志轮转或使用其他日志驱动(如journald,syslog)。

8.3 安全最佳实践

  1. 定期更新基础镜像:基础镜像中的系统软件可能存在安全漏洞。定期(如每月)重建镜像以获取最新的安全更新。
  2. 扫描镜像漏洞:使用docker scan命令(或集成到CI/CD中的工具如Trivy、Clair)扫描镜像中的已知漏洞。
  3. 避免在镜像中存储机密:永远不要将密码、API密钥等硬编码在Dockerfile或应用代码中。应通过环境变量(-e)、Docker Secrets(Swarm模式)或外部配置中心(如Vault)在运行时注入。
  4. 最小权限原则:如前所述,使用非root用户运行容器。

9. 总结与后续学习方向

通过本文,我们从“环境不一致”这一经典痛点出发,深入探讨了Docker容器技术如何通过“镜像”这一核心概念,将应用与环境打包成一个不可变的、可移植的交付单元。我们从概念辨析到环境搭建,再到两个完整的技术栈(Python Flask和Node.js)实战,一步步演示了如何编写Dockerfile、构建镜像、运行容器,并解决了其中常见的坑。

本文的核心价值在于:它不仅仅是一份操作手册,更是一份思维转换的指南。它希望你理解,Docker的本质是将环境配置代码化、版本化。你的Dockerfile和docker-compose.yml文件,应该像你的业务源代码一样,被纳入版本控制系统(如Git)进行管理。任何环境的变更,都首先体现在这些配置文件的修改、代码评审和CI/CD流水线的构建中。

下一步,你可以沿着这些方向深入

  1. Docker Compose:学习如何用单个YAML文件定义和运行多容器应用,这是开发现代微服务应用的必备技能。
  2. Docker网络:深入理解桥接网络、主机网络、覆盖网络,掌握容器间如何安全、高效地通信。
  3. CI/CD集成:将Docker镜像构建和推送集成到Jenkins、GitLab CI、GitHub Actions等持续集成流水线中,实现自动化部署。
  4. 容器编排:当你需要管理成百上千个容器时,就需要Kubernetes或Docker Swarm这样的编排工具来处理调度、服务发现、扩缩容和自愈。
  5. 镜像仓库:搭建私有的Docker Registry(如Harbor),用于安全地存储和管理团队内部的镜像。

将本文中的示例代码和命令在你的环境中亲手运行一遍,遇到问题对照第7节的排查思路解决,是掌握这门技术最快的方式。当你成功将第一个自己的项目容器化并运行起来时,你就已经迈出了构建现代化、可重复、高效软件交付流程的关键一步。建议收藏本文,在未来的容器化实践中作为参考。

← 返回列表