0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Docker容器化部署完全指南

马哥Linux运维 来源:马哥Linux运维 2026-02-09 14:09 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

Docker 容器化部署完全指南:从安装到生产环境最佳实践

一、概述

1.1 背景介绍

Docker 解决的核心问题就一个:"在我机器上能跑,到你那就不行了"。通过将应用及其依赖打包成镜像,保证开发、测试、生产环境完全一致。容器秒级启动(实测冷启动 0.5-2 秒),镜像分层复用节省存储和传输成本——一个 200MB 的基础镜像被 50 个应用共享,每个应用只需额外存储自己的差异层。

我们团队从 2019 年开始全面容器化,目前线上跑着 800+ 个容器,覆盖 Java、Go、Node.jsPython 四种技术栈。这篇文章把从安装到生产环境踩过的坑全部整理出来。

1.2 技术特点

环境一致性:镜像即环境,开发用的镜像和生产用的完全一样,彻底消除环境差异导致的问题

秒级启动:容器不需要启动完整操作系统,直接运行应用进程,启动速度比虚拟机快 10-100 倍

分层存储:镜像由多层只读层组成,相同的层在多个镜像间共享,节省磁盘空间和网络传输

资源隔离:基于 Linux cgroup 和 namespace 实现 CPU、内存、网络、文件系统隔离,互不干扰

声明式编排:通过 Dockerfile 和 docker-compose.yml 声明式定义应用环境,版本可控、可复现

1.3 适用场景

应用容器化部署:将传统应用打包成容器,统一部署和管理方式,降低运维复杂度

微服务架构:每个微服务独立容器化,独立部署、独立扩缩容,服务间通过网络通信

CI/CD 流水线:构建环境容器化,保证每次构建环境一致;测试环境秒级创建和销毁

开发测试环境标准化:新人入职docker compose up一条命令拉起整套开发环境,不用花半天装依赖

1.4 环境要求

组件 版本要求 说明
操作系统 CentOS 7+ / Ubuntu 20.04+ 推荐 Ubuntu 22.04 LTS,内核 5.15+ 对 overlay2 和 cgroup v2 支持更好
Linux 内核 4.x+(推荐 5.x+) 内核 5.x 的 overlay2 性能更好,cgroup v2 支持更完善
Docker CE 24.0+(推荐 27.x) 24.0 开始默认使用 BuildKit,27.x 是当前最新稳定版
Docker Compose V2(docker compose) V1(docker-compose)已停止维护,V2 作为 Docker CLI 插件集成
硬件配置 2C4G 起步 跑 10 个以内容器 2C4G 够用,20+ 容器建议 4C8G 起步
存储 SSD 推荐 overlay2 存储驱动在 SSD 上性能明显优于 HDD

二、详细步骤

2.1 准备工作

2.1.1 系统检查

# 检查系统版本
cat /etc/os-release

# 检查内核版本(建议 5.x+)
uname -r

# 检查是否支持 overlay2(生产环境必须用 overlay2)
cat /proc/filesystems | grep overlay

# 检查可用磁盘空间(Docker 镜像和容器数据会占用大量空间)
df -h

# 检查是否已安装旧版本 Docker
docker --version 2>/dev/null &&echo"Docker 已安装"||echo"Docker 未安装"

2.1.2 卸载旧版本

旧版本的包名可能是docker、docker.io、docker-engine,必须先卸载干净:

# Ubuntu/Debian
sudo apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null
sudo apt autoremove -y

# CentOS/Rocky Linux
sudo yum remove -y docker docker-client docker-client-latest 
  docker-common docker-latest docker-latest-logrotate 
  docker-logrotate docker-engine 2>/dev/null

注意:卸载旧版本不会删除/var/lib/docker/下的镜像、容器和卷数据。如果要全新安装,手动删除:

# 这个操作会删除所有镜像和容器数据,执行前确认不需要保留
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

2.2 安装 Docker CE

2.2.1 Ubuntu/Debian 安装

# 安装必要工具
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release

# 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 添加 Docker 官方源
echo
"deb [arch=$(dpkg --print-architecture)signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu 
$(. /etc/os-release && echo "$VERSION_CODENAME")stable"| 
 sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker CE + CLI + containerd + Compose 插件 + BuildKit
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io 
  docker-buildx-plugin docker-compose-plugin

# 验证安装
docker --version
docker compose version

2.2.2 CentOS/Rocky Linux 安装

# 安装 yum-utils(提供 yum-config-manager)
sudo yum install -y yum-utils

# 添加 Docker 官方源
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装
sudo yum install -y docker-ce docker-ce-cli containerd.io 
  docker-buildx-plugin docker-compose-plugin

# 启动并设置开机自启
sudo systemctl start docker
sudo systemctlenabledocker

# 验证
docker --version
docker compose version

2.2.3 配置非 root 用户运行 Docker

生产环境不建议用 root 跑 Docker 命令,把用户加到 docker 组即可:

# 创建 docker 组(安装时通常已自动创建)
sudo groupadd docker 2>/dev/null

# 将当前用户加入 docker 组
sudo usermod -aG docker$USER

# 重新登录使组权限生效(或执行以下命令临时生效)
newgrp docker

# 验证非 root 用户可以运行 docker
docker run hello-world

注意:docker 组的用户等同于拥有 root 权限(可以挂载宿主机任意目录),只把可信用户加入该组。

2.3 daemon.json 生产环境配置

这是我们线上所有 Docker 主机统一使用的 daemon.json,每个参数都有明确的理由:

{
"storage-driver":"overlay2",
"log-driver":"json-file",
"log-opts": {
 "max-size":"100m",
 "max-file":"3"
 },
"registry-mirrors": [
 "https://mirror.ccs.tencentyun.com",
 "https://docker.mirrors.ustc.edu.cn"
 ],
"insecure-registries": [
 "harbor.internal.example.com:5000"
 ],
"live-restore":true,
"default-ulimits": {
 "nofile": {
  "Name":"nofile",
  "Hard":65535,
  "Soft":65535
  }
 },
"bip":"172.17.0.1/16",
"default-address-pools": [
  {
  "base":"172.18.0.0/16",
  "size":24
  }
 ],
"max-concurrent-downloads":10,
"max-concurrent-uploads":5,
"features": {
 "buildkit":true
 }
}

参数说明

storage-driver: overlay2:生产环境唯一推荐的存储驱动,性能和稳定性最好。旧版本的 aufs/devicemapper 已废弃

log-driver: json-file+max-size/max-file:限制每个容器日志最大 100MB,保留 3 个文件。这个必须配,否则日志会撑爆磁盘。线上出过好几次磁盘 100% 的事故,都是没限制日志大小导致的

registry-mirrors:镜像加速器,国内拉 Docker Hub 镜像很慢,配了加速器后拉取速度从几十 KB/s 提升到几 MB/s

insecure-registries:内网 Harbor 私有仓库如果没配 HTTPS,必须加到这里,否则 push/pull 会报证书错误

live-restore: true:Docker daemon 重启时不杀容器。升级 Docker 版本或重启 dockerd 时,正在运行的容器不受影响。生产环境必开

bip:自定义 docker0 网桥的网段。默认 172.17.0.0/16 可能和公司内网冲突,提前规划好

default-address-pools:自定义容器网络的地址池,避免docker network create时分配到已被占用的网段

max-concurrent-downloads: 10:并行拉取镜像层数,默认 3 太少,设为 10 加快拉取速度

# 写入配置后重启 Docker
sudo mkdir -p /etc/docker
sudo vim /etc/docker/daemon.json # 写入上面的配置
sudo systemctl daemon-reload
sudo systemctl restart docker

# 验证配置生效
docker info | grep -E"Storage Driver|Logging Driver|Registry Mirrors|Live Restore"

2.4 Dockerfile 最佳实践

2.4.1 多阶段构建

多阶段构建是减小镜像体积最有效的手段。编译阶段用完整的 SDK 镜像,运行阶段只用最小的 runtime 镜像。

Go 应用示例(从 1.2GB 压缩到 15MB):

# 文件名:Dockerfile
# 阶段一:编译
FROMgolang:1.22-alpine AS builder

WORKDIR/app

# 先复制 go.mod/go.sum,利用缓存(依赖不变时不重新下载)
COPYgo.mod go.sum ./
RUNgo mod download

# 再复制源码
COPY. .

# 编译为静态二进制(CGO_ENABLED=0 不依赖 C 库)
RUNCGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w"-o /app/server ./cmd/server

# 阶段二:运行
FROMalpine:3.19

# 安装必要的 CA 证书(HTTPS 请求需要)和时区数据
RUNapk --no-cache add ca-certificates tzdata

# 非 root 用户运行
RUNaddgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR/app
COPY--from=builder /app/server .
COPY--from=builder /app/configs ./configs

# 设置时区
ENVTZ=Asia/Shanghai

USERappuser

EXPOSE8080

HEALTHCHECK--interval=30s --timeout=3s --start-period=5s --retries=3 
  CMD wget -qO- http://localhost:8080/health ||exit1

ENTRYPOINT["./server"]

实测结果:

不用多阶段构建:golang:1.22 基础镜像 1.2GB + 应用 = 约 1.3GB

多阶段构建用 alpine:15MB

Java Spring Boot 应用示例:

# 文件名:Dockerfile
# 阶段一:构建
FROMmaven:3.9-eclipse-temurin-21AS builder

WORKDIR/app
COPYpom.xml .
# 先下载依赖(利用缓存层)
RUNmvn dependency:go-offline -B

COPYsrc ./src
RUNmvn package -DskipTests -B

# 解压 Spring Boot fat jar(分层优化)
RUNjava -Djarmode=layertools -jar target/*.jar extract --destination /extracted

# 阶段二:运行
FROMeclipse-temurin:21-jre-alpine

RUNaddgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR/app

# 按变化频率从低到高复制(充分利用缓存)
COPY--from=builder /extracted/dependencies/ ./
COPY--from=builder /extracted/spring-boot-loader/ ./
COPY--from=builder /extracted/snapshot-dependencies/ ./
COPY--from=builder /extracted/application/ ./

ENVTZ=Asia/Shanghai
ENVJAVA_OPTS="-Xms512m -Xmx512m -XX:+UseG1GC"

USERappuser

EXPOSE8080

HEALTHCHECK--interval=30s --timeout=5s --start-period=30s --retries=3 
  CMD wget -qO- http://localhost:8080/actuator/health ||exit1

ENTRYPOINT["sh","-c","java$JAVA_OPTSorg.springframework.boot.loader.launch.JarLauncher"]

实测结果:

不用多阶段构建:maven 基础镜像 800MB + 应用 = 约 900MB

多阶段构建用 JRE alpine:约 180MB

2.4.2 .dockerignore 文件

构建上下文会把当前目录所有文件发送给 Docker daemon,不配 .dockerignore 会把 node_modules、.git 等无关文件也发过去,构建慢且镜像臃肿。

# 文件名:.dockerignore
.git
.gitignore
.dockerignore
Dockerfile
docker-compose*.yml
README.md
LICENSE
docs/

# Node.js
node_modules
npm-debug.log

# Go
vendor/

# Java
target/
*.jar
*.class

# Python
__pycache__
*.pyc
.venv
venv

# IDE
.idea
.vscode
*.swp
*.swo

# 环境变量文件(绝对不能打进镜像)
.env
.env.*

这个文件必须有,特别是.env文件绝对不能打进镜像,里面有数据库密码等敏感信息。

2.4.3 Dockerfile 编写要点

COPY vs ADD:优先用 COPY,语义明确。ADD 会自动解压 tar 包和支持 URL 下载,容易产生意外行为

RUN 合并减少层数:每条 RUN 指令生成一层,合并可以减小镜像体积

# 错误写法:3 层,且第一层的 apt cache 无法被清理掉
RUNapt update
RUNapt install -y curl wget
RUNrm -rf /var/lib/apt/lists/*

# 正确写法:1 层,安装完立即清理缓存
RUNapt update && apt install -y --no-install-recommends curl wget 
  && rm -rf /var/lib/apt/lists/*

非 root USER:容器默认以 root 运行,安全风险大。生产环境必须创建专用用户

HEALTHCHECK:定义健康检查,Docker 和编排工具(Compose/Swarm/K8s)据此判断容器是否正常

ARG vs ENV:ARG 只在构建阶段可用(如版本号),ENV 在运行时也可用(如配置参数)

2.5 Docker Compose 编排

2.5.1 完整的多服务编排示例

以下是一个典型的 Web 应用编排:Nginx 反向代理 + Node.js API + MySQL + Redis,包含健康检查、启动顺序控制、资源限制。

# 文件名:docker-compose.yml

services:
# Nginx 反向代理
nginx:
 image:nginx:1.24-alpine
 container_name:app-nginx
 ports:
  -"80:80"
  -"443:443"
 volumes:
  -./nginx/conf.d:/etc/nginx/conf.d:ro
  -./nginx/ssl:/etc/nginx/ssl:ro
  -./frontend/dist:/var/www/html:ro
  -nginx-logs:/var/log/nginx
 depends_on:
  api:
   condition:service_healthy
 restart:unless-stopped
 deploy:
  resources:
   limits:
    cpus:'1.0'
    memory:256M
 networks:
  -frontend

# Node.js API 服务
api:
 build:
  context:./api
  dockerfile:Dockerfile
 container_name:app-api
 env_file:
  -.env
 environment:
  -NODE_ENV=production
  -DB_HOST=mysql
  -DB_PORT=3306
  -REDIS_HOST=redis
  -REDIS_PORT=6379
 depends_on:
  mysql:
   condition:service_healthy
  redis:
   condition:service_healthy
 healthcheck:
  test:["CMD","wget","-qO-","http://localhost:3000/health"]
  interval:15s
  timeout:5s
  retries:3
  start_period:10s
 restart:unless-stopped
 deploy:
  resources:
   limits:
    cpus:'2.0'
    memory:1G
   reservations:
    cpus:'0.5'
    memory:256M
 networks:
  -frontend
  -backend

# MySQL 数据库
mysql:
 image:mysql:8.0
 container_name:app-mysql
 environment:
  MYSQL_ROOT_PASSWORD:${MYSQL_ROOT_PASSWORD}
  MYSQL_DATABASE:${MYSQL_DATABASE}
  MYSQL_USER:${MYSQL_USER}
  MYSQL_PASSWORD:${MYSQL_PASSWORD}
 volumes:
  -mysql-data:/var/lib/mysql
  -./mysql/conf.d:/etc/mysql/conf.d:ro
  -./mysql/init:/docker-entrypoint-initdb.d:ro
 healthcheck:
  test:["CMD","mysqladmin","ping","-h","localhost","-u","root","-p${MYSQL_ROOT_PASSWORD}"]
  interval:10s
  timeout:5s
  retries:5
  start_period:30s
 restart:unless-stopped
 deploy:
  resources:
   limits:
    cpus:'2.0'
    memory:2G
 command:>
   --character-set-server=utf8mb4
   --collation-server=utf8mb4_unicode_ci
   --max-connections=500
   --innodb-buffer-pool-size=1G
 networks:
  -backend

# Redis 缓存
redis:
 image:redis:7-alpine
 container_name:app-redis
 command:>
   redis-server
   --requirepass ${REDIS_PASSWORD}
   --maxmemory 512mb
   --maxmemory-policy allkeys-lru
   --appendonly yes
 volumes:
  -redis-data:/data
 healthcheck:
  test:["CMD","redis-cli","-a","${REDIS_PASSWORD}","ping"]
  interval:10s
  timeout:3s
  retries:3
 restart:unless-stopped
 deploy:
  resources:
   limits:
    cpus:'1.0'
    memory:768M
 networks:
  -backend

networks:
frontend:
 driver:bridge
backend:
 driver:bridge
 internal:true # 后端网络不暴露到外部

volumes:
mysql-data:
redis-data:
nginx-logs:

配套 .env 文件:

# 文件名:.env(不要提交到 Git)
MYSQL_ROOT_PASSWORD=your_root_password_here
MYSQL_DATABASE=app_production
MYSQL_USER=app_user
MYSQL_PASSWORD=your_app_password_here
REDIS_PASSWORD=your_redis_password_here

常用操作命令:

# 启动所有服务(后台运行)
docker compose up -d

# 查看服务状态
docker compose ps

# 查看某个服务的日志
docker compose logs -f api

# 重新构建并启动
docker compose up -d --build

# 停止所有服务(保留数据卷)
docker compose down

# 停止并删除数据卷(慎用,会丢数据)
docker compose down -v

2.6 网络模式

2.6.1 网络模式对比

模式 说明 适用场景
bridge 默认模式,容器通过虚拟网桥通信 大部分场景
host 容器直接使用宿主机网络栈 对网络性能要求极高的场景(如监控采集)
none 无网络,完全隔离 安全敏感的离线计算任务
overlay 跨主机容器通信(Swarm 模式) Docker Swarm 集群
macvlan 容器拥有独立 MAC 地址,直接接入物理网络 需要容器有独立 IP 的传统网络环境

2.6.2 自定义网络

生产环境不要用默认的 bridge 网络,自定义网络有 DNS 解析功能(容器间可以用容器名互相访问):

# 创建自定义网络(指定网段,避免和内网冲突)
docker network create 
  --driver bridge 
  --subnet 172.20.0.0/24 
  --gateway 172.20.0.1 
  app-network

# 查看网络详情
docker network inspect app-network

# 将已运行的容器接入网络
docker network connect app-network my-container

# 断开网络
docker network disconnect app-network my-container

注意:默认 bridge 网络(docker0)不支持容器名 DNS 解析,只有自定义网络才支持。这是很多人踩的坑——在默认网络里用容器名访问另一个容器,永远连不上。

2.7 存储卷

2.7.1 三种挂载方式对比

类型 命令示例 数据位置 适用场景
Named Volume -v mydata:/data Docker 管理(/var/lib/docker/volumes/) 数据库、持久化数据
Bind Mount -v /host/path:/container/path 宿主机指定目录 配置文件、代码热更新
tmpfs --tmpfs /tmp 内存 临时文件、敏感数据(不落盘)

2.7.2 Named Volume 数据持久化

# 创建命名卷
docker volume create mysql-data

# 查看卷详情
docker volume inspect mysql-data

# 使用命名卷启动 MySQL
docker run -d 
  --name mysql 
  -v mysql-data:/var/lib/mysql 
  -e MYSQL_ROOT_PASSWORD=secret 
  mysql:8.0

# 备份卷数据
docker run --rm 
  -v mysql-data:/source:ro 
  -v $(pwd):/backup 
  alpine tar czf /backup/mysql-data-backup.tar.gz -C /source.

# 恢复卷数据
docker run --rm 
  -v mysql-data:/target 
  -v $(pwd):/backup 
  alpine tar xzf /backup/mysql-data-backup.tar.gz -C /target

注意:docker compose down -v会删除所有命名卷,数据库数据会丢失。生产环境执行前务必确认,或者养成习惯只用docker compose down(不加 -v)。

三、示例代码和配置

3.1 完整配置示例

3.1.1 daemon.json 生产环境完整配置(带注释版)

// 文件路径:/etc/docker/daemon.json
// 注意:实际 JSON 文件不支持注释,这里仅作说明
{
 // 存储驱动,生产环境只用 overlay2
 "storage-driver": "overlay2",

 // 日志驱动和限制(防止日志撑爆磁盘)
 "log-driver": "json-file",
 "log-opts": {
  "max-size": "100m",
  "max-file": "3"
 },

 // 镜像加速器(按需配置)
 "registry-mirrors": [
  "https://mirror.ccs.tencentyun.com"
 ],

 // 内网私有仓库(HTTP 协议)
 "insecure-registries": [
  "harbor.internal.example.com:5000"
 ],

 // Docker daemon 重启时保持容器运行
 "live-restore": true,

 // 容器默认 ulimit
 "default-ulimits": {
  "nofile": { "Name": "nofile", "Hard": 65535, "Soft": 65535 },
  "nproc": { "Name": "nproc", "Hard": 65535, "Soft": 65535 }
 },

 // 自定义 docker0 网桥网段
 "bip": "172.17.0.1/16",

 // 自定义容器网络地址池
 "default-address-pools": [
  { "base": "172.18.0.0/16", "size": 24 }
 ],

 // 并行下载/上传层数
 "max-concurrent-downloads": 10,
 "max-concurrent-uploads": 5,

 // 启用 BuildKit(构建速度更快,支持缓存挂载)
 "features": { "buildkit": true },

 // 数据根目录(默认 /var/lib/docker,磁盘不够时可改到大盘)
 "data-root": "/var/lib/docker",

 // DNS 配置(容器内 DNS 解析)
 "dns": ["8.8.8.8", "114.114.114.114"]
}

3.1.2 Makefile 封装常用 Docker 操作

团队协作时,用 Makefile 封装常用命令,新人不用记一堆 docker 参数:

# 文件名:Makefile

APP_NAME := myapp
VERSION :=$(shellgit describe --tags --always --dirty 2>/dev/null || echo "dev")
REGISTRY := harbor.internal.example.com:5000
IMAGE  :=$(REGISTRY)/$(APP_NAME):$(VERSION)

.PHONY: build push run stop logs clean

# 构建镜像
build:
docker build -t$(IMAGE)-t$(REGISTRY)/$(APP_NAME):latest .

# 推送到私有仓库
push: build
docker push$(IMAGE)
docker push$(REGISTRY)/$(APP_NAME):latest

# 本地运行
run:
docker compose up -d

# 停止服务
stop:
docker compose down

# 查看日志
logs:
docker compose logs -f --tail=100

# 清理悬空镜像和停止的容器
clean:
docker system prune -f
docker image prune -f

# 进入容器 shell
shell:
docker compose exec api sh

# 数据库备份
db-backup:
docker compose exec mysql mysqldump -u root -p$(MYSQL_ROOT_PASSWORD)$(MYSQL_DATABASE)> backup_$(shelldate +%Y%m%d_%H%M%S).sql

# 查看资源使用
stats:
docker stats --no-stream

3.2 实际应用案例

案例一:Node.js 应用容器化部署

场景描述:将一个 Express.js API 服务容器化,配合 PM2 进程管理,实现生产级部署。

Dockerfile:

FROMnode:20-alpine AS builder

WORKDIR/app
COPYpackage*.json ./
RUNnpm ci --only=production && npm cache clean --force

FROMnode:20-alpine

RUNapk --no-cache add tini
RUNaddgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR/app
COPY--from=builder /app/node_modules ./node_modules
COPY. .

ENVNODE_ENV=production
USERappuser
EXPOSE3000

HEALTHCHECK--interval=30s --timeout=3s --retries=3 
  CMD wget -qO- http://localhost:3000/health ||exit1

# 用 tini 作为 PID 1(正确处理信号,避免僵尸进程)
ENTRYPOINT["/sbin/tini","--"]
CMD["node","src/index.js"]

运行结果

$ docker build -t myapi:1.0 .
$ docker images myapi
REPOSITORY  TAG  IMAGE ID    SIZE
myapi    1.0  a1b2c3d4e5f6  95MB

# 对比不用多阶段构建:约 350MB

案例二:零停机滚动更新

场景描述:生产环境更新应用版本,不中断服务。通过 docker compose 配合 Nginx upstream 实现。

实现步骤

构建新版本镜像并推送到仓库

拉取新镜像,启动新容器

健康检查通过后,切换 Nginx 流量到新容器

停止旧容器

#!/bin/bash
# 文件名:rolling-update.sh
# 零停机滚动更新脚本

set-e

APP_NAME="app-api"
NEW_IMAGE="$1"

if[ -z"$NEW_IMAGE"];then
 echo"用法:$0"
 exit1
fi

echo"[1/5] 拉取新镜像:$NEW_IMAGE"
docker pull"$NEW_IMAGE"

echo"[2/5] 启动新容器"
docker compose up -d --no-deps --scale api=2 api

echo"[3/5] 等待新容器健康检查通过..."
sleep 10
NEW_CONTAINER=$(docker compose ps -q api | tail -1)
foriin$(seq 1 30);do
  STATUS=$(docker inspect --format='{{.State.Health.Status}}'"$NEW_CONTAINER"2>/dev/null)
 if["$STATUS"="healthy"];then
   echo"新容器健康检查通过"
   break
 fi
 echo"等待中... ($i/30)"
  sleep 2
done

echo"[4/5] 缩容,移除旧容器"
docker compose up -d --no-deps --scale api=1 api

echo"[5/5] 清理旧镜像"
docker image prune -f

echo"更新完成!"

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 镜像瘦身

选择合适的基础镜像

基础镜像 大小 适用场景
ubuntu:22.04 77MB 需要完整包管理器的场景
debian:bookworm-slim 74MB 需要 glibc 但不需要完整 Debian
alpine:3.19 7MB Go/Rust 等静态编译语言,体积最小
distroless 2-20MB 安全要求最高的场景,无 shell 无包管理器
scratch 0MB 纯静态二进制,如 Go 应用

我们团队的做法:Go 应用用 alpine,Java 应用用 eclipse-temurin JRE alpine,Node.js 用 node alpine。

用 dive 分析镜像层

# 安装 dive
wget https://github.com/wagoodman/dive/releases/download/v0.12.0/dive_0.12.0_linux_amd64.tar.gz
tar -zxvf dive_0.12.0_linux_amd64.tar.gz
sudo mv dive /usr/local/bin/

# 分析镜像每一层的内容和大小
dive myapp:latest

dive 会显示每一层新增了哪些文件、占了多少空间,能快速定位镜像臃肿的原因。实测用 dive 分析后,我们把一个 Java 镜像从 600MB 优化到 180MB。

4.1.2 安全加固

非 root 运行:容器默认以 root 运行,一旦容器逃逸攻击者就是宿主机 root。Dockerfile 中必须创建专用用户:

RUNaddgroup -S appgroup && adduser -S appuser -G appgroup
USERappuser

只读文件系统:容器运行时文件系统设为只读,防止恶意写入:

docker run --read-only --tmpfs /tmp --tmpfs /var/run myapp:latest

需要写入的目录用 tmpfs 或 volume 挂载。

Drop 不需要的 Linux Capabilities

# 移除所有 capabilities,只保留需要的
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp:latest

默认容器有 14 个 capabilities,大部分应用只需要 1-2 个。全部 drop 再按需 add 是最安全的做法。

镜像安全扫描

# 用 Trivy 扫描镜像漏洞(推荐,速度快、数据库全)
trivy image myapp:latest

# 只显示高危和严重漏洞
trivy image --severity HIGH,CRITICAL myapp:latest

# 集成到 CI/CD:发现高危漏洞时构建失败
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest

我们团队在 CI 流水线中强制扫描,HIGH 以上漏洞必须修复才能上线。

4.1.3 日志管理

json-file 驱动限制大小(daemon.json 中已配置):

{
"log-driver":"json-file",
"log-opts": {
 "max-size":"100m",
 "max-file":"3"
 }
}

每个容器最多 3 个日志文件,每个 100MB,总共 300MB 上限。不配这个,跑几天日志就能把磁盘撑爆。

单个容器覆盖全局日志配置

# docker-compose.yml 中
services:
api:
 logging:
  driver:json-file
  options:
   max-size:"200m"
   max-file:"5"

集中日志收集方案

# 用 fluentd 驱动将日志发送到集中式日志平台
services:
api:
 logging:
  driver:fluentd
  options:
   fluentd-address:"10.0.0.50:24224"
   tag:"docker.{{.Name}}"
   fluentd-async:"true"

4.1.4 资源限制

容器不限制资源的话,一个容器内存泄漏就能把整台宿主机拖垮。生产环境必须设资源限制。

# 命令行方式限制资源
docker run -d 
  --name myapp 
  --memory 1g 
  --memory-swap 1g 
  --cpus 2.0 
  --pids-limit 200 
  --oom-score-adj 500 
  myapp:latest

参数说明

--memory 1g:内存上限 1GB,超过会触发 OOM Killer

--memory-swap 1g:设为和 memory 相同值表示禁用 swap(生产环境建议禁用,swap 会导致性能急剧下降)

--cpus 2.0:最多使用 2 个 CPU 核心

--pids-limit 200:限制容器内最大进程数,防止 fork 炸弹

--oom-score-adj 500:OOM 优先级,值越大越容易被 kill(保护宿主机上更重要的进程)

4.1.5 CI/CD 集成

BuildKit 缓存加速构建

# 启用 BuildKit(daemon.json 中已配置,也可以通过环境变量临时启用)
exportDOCKER_BUILDKIT=1

# 使用缓存挂载加速依赖下载(Go 示例)
# Dockerfile 中:
# RUN --mount=type=cache,target=/go/pkg/mod go mod download
# RUN --mount=type=cache,target=/root/.cache/go-build go build -o /app/server

# 使用内联缓存(CI/CD 跨构建复用缓存)
docker build 
  --build-arg BUILDKIT_INLINE_CACHE=1 
  --cache-from harbor.example.com/myapp:latest 
  -t harbor.example.com/myapp:v1.2.3 
  .

多平台构建(buildx)

# 创建多平台构建器
docker buildx create --name multiarch --use

# 同时构建 amd64 和 arm64 镜像并推送
docker buildx build 
  --platform linux/amd64,linux/arm64 
  -t harbor.example.com/myapp:v1.2.3 
  --push 
  .

ARM 服务器(如 AWS Graviton)越来越多,提前做好多平台支持能省不少麻烦。

Harbor 私有仓库

# 登录私有仓库
docker login harbor.internal.example.com:5000

# 打标签并推送
docker tag myapp:v1.2.3 harbor.internal.example.com:5000/project/myapp:v1.2.3
docker push harbor.internal.example.com:5000/project/myapp:v1.2.3

4.2 注意事项

4.2.1 配置注意事项

daemon.json 改错了会导致 Docker 无法启动,改之前先备份。

修改 daemon.json 后必须systemctl daemon-reload && systemctl restart docker

live-restore: true时重启 dockerd 不会杀容器,但升级大版本(如 24.x 到 27.x)时可能不兼容,建议先在测试环境验证

bip网段规划要提前和网络团队确认,避免和公司内网、VPN 网段冲突。我们踩过的坑:默认 172.17.0.0/16 和办公网 VPN 冲突,导致开发机连不上容器

4.2.2 常见错误

错误现象 原因分析 解决方案
Cannot connect to the Docker daemon dockerd 未启动或权限不足 systemctl start docker 或将用户加入 docker 组
no space left on device 磁盘被镜像/容器/日志占满 docker system prune -a 清理;配置日志大小限制
OCI runtime create failed 内核版本太低或 seccomp 配置问题 升级内核到 5.x+;检查 seccomp profile
port is already allocated 端口被其他进程占用 ss -tlnp | grep 找到占用进程并处理
network has active endpoints 删除网络时还有容器在用 先停止并删除使用该网络的容器
image pull timeout DNS 解析失败或网络不通 检查 DNS 配置;配置镜像加速器

4.2.3 兼容性问题

cgroup v1 vs v2:Ubuntu 22.04 默认用 cgroup v2,部分老版本应用(如 Java 8u191 以下)不识别 cgroup v2 的内存限制,会导致 JVM 看到的是宿主机全部内存而非容器限制。解决办法:升级 JDK 版本或在 GRUB 中加systemd.unified_cgroup_hierarchy=0回退到 v1

Alpine 与 glibc:Alpine 用 musl libc 而非 glibc,部分依赖 glibc 的应用(如某些 Python C 扩展、Oracle JDK)在 Alpine 上会报错。解决办法:换用 debian-slim 基础镜像,或安装 gcompat 兼容层

Docker Compose V1 vs V2:V1(docker-compose,Python 实现)已停止维护,V2(docker compose,Go 实现)是 Docker CLI 插件。两者配置文件基本兼容,但部分边缘行为有差异。新项目直接用 V2

五、故障排查和监控

5.1 故障排查

5.1.1 日志查看

# 查看容器日志(最近 100 行,实时跟踪)
docker logs --tail 100 -f 

# 查看指定时间段的日志
docker logs --since"2026-02-07T0000"--until"2026-02-07T1200"

# 查看 Docker daemon 日志
sudo journalctl -u docker -f

# 查看容器内进程
docker top 

# 查看容器详细信息(启动参数、网络、挂载等)
docker inspect 

# 查看容器事件流(启动、停止、OOM 等事件)
docker events --since"1h"

5.1.2 常见问题排查

问题一:容器启动后立即退出

# 查看退出码
docker inspect --format='{{.State.ExitCode}}'

# 常见退出码含义:
# 0  - 正常退出(CMD/ENTRYPOINT 执行完毕)
# 1  - 应用错误
# 137 - 被 SIGKILL 杀死(通常是 OOM)
# 139 - 段错误(Segmentation Fault)
# 143 - 被 SIGTERM 正常终止

# 查看最后的日志
docker logs --tail 50 

解决方案

退出码 0:CMD 命令执行完就退出了,需要一个前台进程保持运行。常见错误是用了CMD service nginx start(后台启动后进程退出),应该用CMD ["nginx", "-g", "daemon off;"]

退出码 137:容器被 OOM Killer 杀了,增大内存限制或优化应用内存使用

退出码 1:应用报错,看日志找具体原因

问题二:容器网络不通

# 检查容器网络配置
docker inspect --format='{{json .NetworkSettings.Networks}}' | jq

# 进入容器排查
dockerexec-it  sh

# 容器内检查 DNS
cat /etc/resolv.conf
nslookup mysql

# 容器内检查连通性
ping 
wget -qO- http://:/health

# 宿主机检查 iptables 规则(Docker 通过 iptables 实现端口映射和网络隔离)
sudo iptables -t nat -L -n | grep 
sudo iptables -L DOCKER -n

解决方案

容器间互通但用容器名访问不了:检查是否在同一个自定义网络中,默认 bridge 网络不支持 DNS

容器访问外网不通:检查宿主机的 IP 转发sysctl net.ipv4.ip_forward,值必须为 1

端口映射不生效:检查宿主机防火墙规则,Docker 的 iptables 规则可能被其他防火墙工具覆盖

问题三:存储空间耗尽

# 查看 Docker 磁盘使用情况
docker system df

# 详细查看每个镜像/容器/卷的占用
docker system df -v

# 查看 overlay2 存储占用
du -sh /var/lib/docker/overlay2/* | sort -rh | head -20

解决方案

# 清理已停止的容器、未使用的网络、悬空镜像和构建缓存
docker system prune -f

# 更激进的清理:同时删除未被任何容器使用的镜像
docker system prune -a -f

# 只清理超过 24 小时的资源
docker system prune -a -f --filter"until=24h"

# 清理未使用的卷(数据库卷要小心)
docker volume prune -f

预防措施:定期清理 + 日志大小限制 + 监控磁盘使用率告警

问题四:容器内存 OOM

# 查看容器资源使用情况
docker stats --no-stream 

# 查看容器内存限制
docker inspect --format='{{.HostConfig.Memory}}'

# 查看是否发生过 OOM
docker inspect --format='{{.State.OOMKilled}}'

# 查看系统 OOM 日志
dmesg | grep -i"oom|killed"

解决方案

确认内存限制是否合理:docker stats观察实际内存使用峰值,限制值设为峰值的 1.5 倍

Java 应用特别注意:JVM 堆内存(-Xmx)+ 非堆内存(Metaspace、线程栈、NIO Buffer)总和不能超过容器内存限制。经验公式:容器内存 = Xmx * 1.5

禁用 swap:--memory-swap设为和--memory相同值

5.2 性能监控

5.2.1 docker stats 实时监控

# 查看所有容器资源使用(实时刷新)
docker stats

# 查看指定容器(不刷新,输出一次)
docker stats --no-stream app-api app-mysql app-redis

# 输出示例:
# CONTAINER ID  NAME    CPU %  MEM USAGE / LIMIT  MEM %  NET I/O     BLOCK I/O    PIDS
# a1b2c3d4e5f6  app-api   2.35%  256MiB / 1GiB    25.00% 1.2GB / 800MB  50MB / 10MB   35
# b2c3d4e5f6a1  app-mysql  5.12%  1.1GiB / 2GiB    55.00% 500MB / 1.5GB  2GB / 500MB   42
# c3d4e5f6a1b2  app-redis  0.50%  50MiB / 768MiB   6.51%  200MB / 300MB  1MB / 5MB    5

5.2.2 cAdvisor + Prometheus + Grafana 监控方案

生产环境推荐用 cAdvisor 采集容器指标,Prometheus 存储,Grafana 展示。

# 文件名:docker-compose.monitoring.yml

services:
cadvisor:
 image:gcr.io/cadvisor/cadvisor:v0.49.1
 container_name:cadvisor
 ports:
  -"8080:8080"
 volumes:
  -/:/rootfs:ro
  -/var/run:/var/run:ro
  -/sys:/sys:ro
  -/var/lib/docker/:/var/lib/docker:ro
  -/dev/disk/:/dev/disk:ro
 privileged:true
 restart:unless-stopped

prometheus:
 image:prom/prometheus:v2.51.0
 container_name:prometheus
 ports:
  -"9090:9090"
 volumes:
  -./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
  -prometheus-data:/prometheus
 command:
  -'--config.file=/etc/prometheus/prometheus.yml'
  -'--storage.tsdb.retention.time=30d'
 restart:unless-stopped

grafana:
 image:grafana/grafana:10.4.0
 container_name:grafana
 ports:
  -"3000:3000"
 volumes:
  -grafana-data:/var/lib/grafana
 environment:
  -GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
 restart:unless-stopped

volumes:
prometheus-data:
grafana-data:

Prometheus 采集配置:

# 文件路径:prometheus/prometheus.yml
global:
scrape_interval:15s

scrape_configs:
-job_name:'cadvisor'
 static_configs:
  -targets:['cadvisor:8080']

5.2.3 监控指标说明

指标名称 正常范围 告警阈值 说明
容器 CPU 使用率 < 70% of limit > 85% 持续高于 85% 需要扩容或优化
容器内存使用率 < 80% of limit > 90% 接近限制值会触发 OOM
容器重启次数 0 > 3 次/小时 频繁重启说明应用有问题
磁盘使用率 < 70% > 85% Docker 数据目录磁盘使用率
容器网络错误 0 > 0 网络丢包或错误需要排查

5.3 备份与恢复

5.3.1 备份策略

#!/bin/bash
# 文件名:docker-backup.sh
# Docker 数据备份脚本

BACKUP_DIR="/data/backups/docker"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7

mkdir -p"$BACKUP_DIR"

echo"=== Docker 数据备份开始:$DATE==="

# 备份所有命名卷
forvolin$(docker volume ls -q);do
 echo"备份卷:$vol"
  docker run --rm 
    -v"$vol":/source:ro 
    -v"$BACKUP_DIR":/backup 
    alpine tar czf"/backup/${vol}_${DATE}.tar.gz"-C /source.
done

# 备份 docker-compose 配置
echo"备份 Compose 配置..."
tar czf"$BACKUP_DIR/compose_configs_${DATE}.tar.gz"
  /opt/apps/*/docker-compose.yml 
  /opt/apps/*/.env 2>/dev/null

# 清理过期备份
find"$BACKUP_DIR"-name"*.tar.gz"-mtime +$RETENTION_DAYS-delete

echo"=== 备份完成 ==="

5.3.2 恢复流程

停止服务:docker compose down

恢复卷数据

docker run --rm 
  -v mysql-data:/target 
  -v /data/backups/docker:/backup 
  alpine tar xzf /backup/mysql-data_20260207_030000.tar.gz -C /target

验证完整性:docker compose up -d && docker compose ps

检查数据:连接数据库确认数据完整

六、总结

6.1 技术要点回顾

安装与配置:daemon.json 是 Docker 生产环境的核心配置,日志大小限制、live-restore、网段规划这三项必须配置,否则迟早出事故

Dockerfile 编写:多阶段构建是镜像瘦身的关键手段,Go 应用从 1.3GB 压缩到 15MB,Java 应用从 900MB 压缩到 180MB。.dockerignore 必须有,防止敏感文件打进镜像

Compose 编排:healthcheck + depends_on condition 控制启动顺序,deploy.resources 限制资源,networks 隔离前后端流量

安全加固:非 root 运行、只读文件系统、drop capabilities、Trivy 镜像扫描,四道防线缺一不可

监控与运维:docker stats 日常巡检,cAdvisor + Prometheus + Grafana 长期监控,定期 docker system prune 清理空间

6.2 进阶学习方向

Kubernetes 容器编排:Docker 解决了单机容器化问题,K8s 解决多机编排问题。掌握 Docker 后学 K8s 是自然的进阶路径

学习资源:Kubernetes 官方文档

实践建议:先用 kind 或 minikube 搭建本地集群练手,再上生产

镜像构建优化:深入 BuildKit 缓存机制、多平台构建、镜像签名和供应链安全

学习资源:BuildKit 官方文档

实践建议:在 CI/CD 中实践 BuildKit 缓存挂载,构建速度能提升 50% 以上

容器运行时:了解 containerd、CRI-O 等底层运行时,理解 Docker 只是容器生态的上层工具

6.3 参考资料

Docker 官方文档- 最权威的 Docker 使用参考

Dockerfile 最佳实践- 官方推荐的 Dockerfile 编写规范

Docker Compose 规范- Compose 文件格式完整参考

Docker 安全基准- CIS Docker Benchmark,安全加固检查清单

Awesome Docker- Docker 生态工具和资源汇总

附录

A. Docker 命令速查表

# 镜像操作
docker images               # 列出本地镜像
docker pull :         # 拉取镜像
docker build -t : .      # 构建镜像
docker push :         # 推送镜像
docker rmi             # 删除镜像
docker image prune -f           # 清理悬空镜像
docker save -o backup.tar      # 导出镜像为 tar
docker load -i backup.tar         # 从 tar 导入镜像
docker tag         # 给镜像打标签
dockerhistory          # 查看镜像构建历史
# 容器操作
docker run -d --name      # 后台运行容器
docker ps                 # 查看运行中的容器
docker ps -a               # 查看所有容器(含已停止)
docker stop           # 停止容器
docker start          # 启动已停止的容器
docker restart         # 重启容器
docker rm            # 删除容器
docker rm -f          # 强制删除运行中的容器
dockerexec-it  sh      # 进入容器 shell
docker logs --tail 100 -f    # 查看容器日志
docker inspect         # 查看容器详细信息
docker cp :/path /host/path  # 从容器复制文件到宿主机
docker stats               # 查看容器资源使用
docker top           # 查看容器内进程

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 测试
    +关注

    关注

    9

    文章

    6374

    浏览量

    131639
  • JAVA
    +关注

    关注

    20

    文章

    3005

    浏览量

    116813
  • 容器
    +关注

    关注

    0

    文章

    535

    浏览量

    23024
  • Docker
    +关注

    关注

    0

    文章

    537

    浏览量

    14393

原文标题:Docker 容器化部署完全指南:从安装到生产环境最佳实践

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    如何使用 Docker容器技术

    对于开发人员来说,Docker肯定都不陌生,今天小编带大家重新学习一下Docker。 什么是 Docker 官话: Docker 是一种开源的容器
    的头像 发表于 09-30 11:24 1.7w次阅读

    RK3568-Docker容器部署方法说明

    RK3568-Docker容器部署方法说明
    的头像 发表于 01-22 10:12 2406次阅读
    RK3568-<b class='flag-5'>Docker</b><b class='flag-5'>容器</b><b class='flag-5'>部署</b>方法说明

    TLT507-Docker容器部署方法说明

    TLT507-Docker容器部署方法说明
    的头像 发表于 01-26 09:49 1177次阅读
    TLT507-<b class='flag-5'>Docker</b><b class='flag-5'>容器</b><b class='flag-5'>部署</b>方法说明

    干货分享 | RK3588 Ubuntu系统Docker容器使用指南

    前言:在瑞芯微RK3588高性能AIoT平台上运行Ubuntu系统时,Docker容器技术能极大提升开发部署效率。通过轻量级虚拟实现环境隔离与快速迁移,本文将从零开始详解RK3588
    的头像 发表于 06-27 12:01 4161次阅读
    干货分享 | RK3588 Ubuntu系统<b class='flag-5'>Docker</b><b class='flag-5'>容器使用指南</b>

    ARM平台实现Docker容器技术

    及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows机器上,亦可实现虚拟容器完全使用沙箱机制,相互之间不会有任何接口。使用Docker,可像管理应用程序一
    发表于 07-17 11:05

    ARM平台实现Docker容器技术

    及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows机器上,亦可实现虚拟容器完全使用沙箱机制,相互之间不会有任何接口。使用Docker,可像管理应用程序一
    发表于 07-25 14:36

    理解Docker容器并畅玩docker

    完全不影响其他容器的正常运作)。这样描述,还是不大明白,我们可以实际操作一下。先打开两个命令行,在其中一个命令行执行以下命令:docker run -it --name a1_rm alpine命令解析
    发表于 11-05 09:54

    Docker入门指南

    •简化Arm硬件的应用程序开发•在开始之前在云中工作并在边缘部署指南假设您熟悉容器概念。如果你不熟悉容器的概念,你可以在Docker入门中
    发表于 08-02 06:09

    Docker容器部署-以TI AM335x平台为例

    前言:Docker是一个开源的应用容器引擎,让开发者可打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows机器上,亦可实现虚拟容器
    的头像 发表于 12-20 15:16 2275次阅读
    <b class='flag-5'>Docker</b><b class='flag-5'>容器</b><b class='flag-5'>部署</b>-以TI AM335x平台为例

    docker部署mysql的坏处

    Docker 是一种虚拟技术,它允许开发人员在容器内打包应用程序及其所有依赖项,从而实现在不同环境中运行相同的应用程序的能力。然而,在使用 Docker
    的头像 发表于 11-23 09:29 2394次阅读

    docker部署对性能的影响

    Docker 是一个流行的容器平台,它提供了一种轻量级的虚拟技术,使得应用程序可以在独立的容器中运行。然而,
    的头像 发表于 11-23 09:31 3054次阅读

    docker容器有几种状态

    Docker 是一种流行的容器平台,它能够帮助开发人员将应用程序和其依赖打包成一个独立的容器,并且能够在不同的环境中进行部署和运行。在
    的头像 发表于 11-23 09:50 3933次阅读

    ARM平台实现Docker容器技术

    ,亦可实现虚拟容器完全使用沙箱机制,相互之间不会有任何接口。使用Docker,可像管理应用程序一样管理基础结构。通过利用Docker
    的头像 发表于 03-07 13:48 1749次阅读
    ARM平台实现<b class='flag-5'>Docker</b><b class='flag-5'>容器</b>技术

    基于 Docker 与 Jenkins 实现自动部署

    优化,为 Docker 容器应用与 Jenkins 自动流水线提供了理想的运行环境。无论是快速构建、测试还是部署,Flexus X 都能
    的头像 发表于 01-07 17:25 1130次阅读
    基于 <b class='flag-5'>Docker</b> 与 Jenkins 实现自动<b class='flag-5'>化</b><b class='flag-5'>部署</b>

    如何使用Docker部署大模型

    随着深度学习和大模型的快速发展,如何高效地部署这些模型成为了一个重要的挑战。Docker 作为一种轻量级的容器技术,能够将模型及其依赖环境打包成一个可移植的
    的头像 发表于 05-24 16:39 1331次阅读