news 2026/10/6 16:53:21

Docker容器化部署实战:镜像构建、Compose编排与故障排查全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker容器化部署实战:镜像构建、Compose编排与故障排查全指南

这几年做后端和运维,几乎绕不开Docker。我自己的服务器上跑着MySQL、Redis、Nginx,再加上几个内部项目,全是用容器化的方式在管理。从最早“装个Docker跑个镜像”,到后来逐渐把部署流程沉淀成一套还算稳定的规范,中间踩过的坑确实不少。所以这篇文章不打算讲那些大而全的概念,而是把我实际验证过的容器化部署思路、常用配置和排查方法整理出来,重点放在镜像构建、Compose编排、网络与数据卷、故障排查这几个环节上,适合正在从“能用Docker”走向“用好Docker”的开发者。

1. 部署前先想明白:容器化到底解决了什么

1.1 容器化部署的核心价值:环境一致性与快速交付

先聊一个最朴素的问题:为什么非要用Docker?很多同学刚接触容器化时,只觉得它是“一个轻量级虚拟机”,这个理解不能说错,但会忽略掉真正关键的价值。我印象最深的一次事故是,一个Java应用在开发机上跑得好好的,部署到生产服务器后频繁报错,折腾了半天才发现是生产环境缺少某个系统库,而开发机上恰好装过。这类问题在传统部署方式下几乎无法根治,因为每台机器的操作系统版本、系统依赖、运行时环境都可能有细微差异。

Docker用镜像把应用和它的运行环境打包成一个标准制品,这个制品在任何装有Docker的机器上表现一致。也就是说,开发环境、测试环境、生产环境之间不再是“各自闯关”,而是同一个镜像在跑,这从根本上消除了“在我电脑上能跑”的问题。实际落地后,团队的发布速度明显提升,以前手工部署一套服务要写部署文档、确认依赖、担心漏配环境变量,现在只需要执行镜像构建和容器启动,半小时的活缩短到几分钟。

1.2 规划阶段就要确定的四件事

容器化不是“打个镜像就行”,部署前最好先做一轮整体规划,否则后面改起来成本很高。我一般会先确认四件事。

第一是镜像版本策略。所有环境必须使用同一个镜像,绝不能出现“生产还在用旧镜像、测试已经换新版本”的情况。镜像Tag建议带上版本号或者Git提交号,不要长期依赖latest,否则哪天拉取到的新版本不兼容,排查起来非常痛苦。

第二是端口规划。宿主机端口是有限的,容器端口可以内部统一,但宿主机的映射端口要提前规划,避免多个项目之间冲突。我一般会在项目文档里维护一张端口分配表,哪个服务占了哪个端口一目了然。

第三是数据目录规划。容器本身是无状态的,任何重要数据都必须落到数据卷或宿主机挂载目录中。提前确定数据目录的存放位置,后续备份、迁移、扩容都会省很多事。

第四是日志输出方案。容器内应用最好把日志写到标准输出,不要写到容器内的某个文件里,因为容器一销毁日志就丢了。写到stdout之后,可以通过docker logs查看,也可以交给日志采集组件统一收集,这个习惯越早养成越好。

1.3 哪些项目不适合一上来就容器化

容器化虽然好,但也不是银弹。我见过有人硬把一些不合适的应用往容器里塞,最后维护成本反而更高。比如强依赖特定GPU驱动和底层硬件特性的服务,容器里虽然可以用GPU,但宿主机驱动版本不匹配时照样跑不起来。再比如有状态的数据集群,像某些数据库的主从同步、集群节点发现,不是不能容器化,而是对网络、存储、运维能力的要求明显更高,新手直接上手容易翻车。

还有一个容易被忽略的场景:桌面级应用,特别是依赖图形界面的软件。容器里跑GUI不是不可以,但需要处理显示服务器、输入设备、音频等一堆问题,性价比很低。遇到这类需求,老老实实用虚拟机或者直接宿主机部署反而更省心。我的判断标准很简单:如果应用是长期运行的、无状态的、依赖特定运行时环境的,容器化收益最大;如果是短交互、强硬件依赖、重度GUI的场景,优先考虑其他方案。

2. 环境准备:从新机器到能跑第一个容器

2.1 Linux 安装 Docker Engine 的实际操作

Linux服务器上的Docker安装算是最基础的操作,但我在帮助朋友排查时发现,很多人会装到一个半新不旧的版本,甚至装完忘了配置开机自启,服务器一重启Docker就没了。这边把完整的操作步骤列一下,以Ubuntu为例。

# 1. 卸载可能存在的旧版本 sudo apt-get remove docker docker-engine docker.io containerd runc # 2. 更新apt源并安装依赖 sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg lsb-release # 3. 添加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 # 4. 添加仓库源 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 # 5. 安装Docker Engine sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin # 6. 启动并设置开机自启 sudo systemctl enable docker --now

装完之后建议执行docker version确认客户端和服务端版本。这里有个小细节,很多教程只装了docker.io这个包,它虽然能跑,但版本往往偏旧,还是用Docker官方仓库安装docker-ce更靠谱。另外,当前用户如果要免sudo执行docker命令,需要把用户加入docker用户组,执行sudo usermod -aG docker $USER,然后重新登录终端才能生效。

2.2 Windows上Docker Desktop的虚拟化坑

Windows机器上部署Docker,最常见的做法是安装Docker Desktop,但热词里那个报错“Virtualization support not detected”我见过太多次了,这里详细说一下。

这个报错的意思很直白:系统检测不到虚拟化支持。Docker Desktop在Windows上不是直接跑Linux容器的,它底层依赖WSL2或者Hyper-V来运行一个轻量级Linux虚拟机。如果系统没开启虚拟化功能,或者BIOS里关闭了硬件虚拟化,Docker Desktop就起不来。

排查路径按顺序走一遍。先打开任务管理器,性能页签里看“虚拟化”这一行,如果显示“已禁用”,必须重启机器进BIOS,找到Intel VT-x或者AMD SVM的选项打开。如果BIOS里已开启,但Windows里还没启用相关功能,就到“控制面板—程序—启用或关闭Windows功能”里勾选“虚拟机平台”和“适用于Linux的Windows子系统”,然后重启。装好之后,在PowerShell里执行wsl --status,确认WSL版本是2。如果版本是1,需要执行wsl --set-default-version 2。

我把这个排查过程整理成了一张表,照着做基本都能解决:

现象原因处理方式
任务管理器显示虚拟化“已禁用”BIOS未开启硬件虚拟化进入BIOS开启Intel VT-x或AMD SVM
Windows功能缺少虚拟机平台系统组件未启用启用“虚拟机平台”和“Windows虚拟机监控程序平台”后重启
wsl --status显示Version 1WSL版本过旧执行wsl --set-default-version 2,必要时升级内核包
提示未安装WSL2内核缺少内核组件下载wsl_update_x64.msi安装后重启
Docker Desktop启动后反复重启系统资源不足或版本冲突升级到最新版、清理残留文件、关闭占用内存过多的程序

这里再多提一句,Windows上跑Docker的底层是虚拟机,所以性能和纯Linux环境存在一定差距,特别是大量磁盘读写和网络转发场景。如果只是本地开发调试,Docker Desktop完全够用;但如果是生产环境或对性能敏感的服务,还是建议用Linux服务器。

2.3 daemon.json 基础调优

Docker装好后,有一个被我视为“必做项”的配置,就是修改/etc/docker/daemon.json。这个文件虽然大部分情况下可以不碰,但提前做好配置能省掉很多后患。

我常用的配置如下:

{ "registry-mirrors": ["https://docker.m.daocloud.io"], "data-root": "/data/docker", "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }

这个配置里做了三件事。第一,配置registry-mirrors镜像加速源,拉取公共镜像时会快很多,这里用哪个镜像站没有定论,选择自己实际访问稳定的地址就可以。第二,把Docker的数据目录从默认的/var/lib/docker迁移到/data/docker,这样做的好处是,Docker的数据不会和系统盘竞争空间,特别是容器日志和镜像缓存很容易把根目录塞满,数据目录独立之后清理和扩容都方便。第三,限制容器日志的大小,这是防止“日志把磁盘撑爆”的关键手段。

改完daemon.json后记得执行sudo systemctl restart docker,并用docker info确认配置是否生效。还有一个容易忽略的点,如果服务器上启用了SELinux或者防火墙,可能会影响容器的网络访问,这一点放到后面的故障排查部分细讲。

3. 镜像构建:从“能跑”到“可交付”

3.1 多阶段构建,一个坑一个坑填出来的经验

早期写Dockerfile,我犯过一个很典型的错误:把整个项目构建环境和运行环境塞进同一个镜像里。比如用Java项目时,基础镜像直接用maven:3.8-jdk-11,然后在一个Dockerfile里既编译代码又启动应用,结果镜像体积轻松超过1GB,推送到镜像仓库慢、拉取也慢,还附带了一堆用不到的编译工具链,增大了被攻击的面。

后来改用多阶段构建,思路就清晰多了。第一阶段用来编译打包,第二阶段只保留运行环境。以Java项目为例:

# 第一阶段:编译阶段 FROM maven:3.8-jdk-11 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests # 第二阶段:运行阶段 FROM openjdk:11-jre-slim WORKDIR /app COPY --from=builder /app/target/myapp.jar . EXPOSE 8080 CMD ["java", "-jar", "myapp.jar"]

这样最终镜像只有JRE和打好的jar包,体积能缩小到一百多MB。这里有个小技巧:把pom.xml单独COPY并执行依赖下载,再COPY源码,这样依赖层可以被Docker缓存复用,只要不修改pom文件,后续构建就可以直接命中缓存层,构建速度提升很明显。Go项目同理,第一阶段用golang镜像编译,第二阶段用alpine或者scratch跑二进制文件。

3.2 镜像瘦身与基础镜像选择

镜像瘦身不只是为了“好看”,它还直接影响部署速度、存储成本和生命周期管理。我见过不少项目,源文件只有几十MB,构建出来的镜像却高达三四个GB,一问基本都是基础镜像选得太大,或者往容器里装了一堆调试工具。

基础镜像的选择需要结合实际场景来看,给一张对比表会更直观:

基础镜像体积glibc/musl适用场景
alpine5MB左右musl追求极简体积的小型应用
debian:slim50MB左右glibc兼容性好,适合大多数动态语言
distroless20MB左右glibc安全要求高、不需要shell的场景
centos/ubuntu200MB+glibc依赖系统库较多的传统应用

这里特别提醒一点,alpine虽然小,但用的是musl而不是常见的glibc,某些Python扩展包、二进制从glibc编译的动态库放到alpine里可能跑不起来。我实际遇到过pandas、numpy这类科学计算库在alpine里装上后运行报错的情况,最后换成python:3.11-slim就正常了。所以做技术选型时,不能只盯着“体积小”,还要关注基础镜像和项目依赖之间是否兼容。

3.3 .dockerignore 和构建上下文

这个坑属于典型的“不踩不知道,一踩吓一跳”。Docker构建时,默认会把整个上下文目录发送给Docker daemon,如果项目里有node_modules、target、.git这类大目录,构建过程会变得极其缓慢,一次构建要传几百MB甚至几个G的文件。

解决办法就是写.dockerignore文件,作用和.gitignore类似,用于排除不需要进入镜像的文件。

.git node_modules target dist *.log Dockerfile .dockerignore

这里再细说一下为什么.dockerignore能影响镜像分层的效率。我们把COPY . .这一步单独拆出来看,假设项目目录下有一个200MB的node_modules目录,即使最终运行阶段根本不需要它,但构建时这200MB文件还是会被打包进构建上下文并发送给daemon,一旦这些文件被复制进了某一层,后续所有层都会带着这个体积。.dockerignore把这些目录排除后,构建上下文小了几百MB,整个流程会快很多。这是一种免费的性能优化,动动手就能做。

4. Compose 编排:多容器部署的标配玩法

4.1 Compose文件的核心结构

当系统里只有一个应用容器时,用docker run就能管住,但一旦出现多个服务,比如一个Web应用要搭配MySQL、Redis、Nginx,再用一长串命令分别启动并维护它们之间的网络关系,很快就会失控。Docker Compose的出现就是为了解决这个问题,把多个容器的启动方式、网络、数据卷集中写到一个docker-compose.yml里。

一个最基本的Compose结构长这样:

version: '3.8' services: app: image: myapp:1.2.3 ports: - "8080:8080" environment: - DB_HOST=mysql - REDIS_HOST=redis depends_on: - mysql - redis networks: - app_net mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: myapp volumes: - mysql_data:/var/lib/mysql networks: - app_net redis: image: redis:7 volumes: - redis_data:/data networks: - app_net volumes: mysql_data: redis_data: networks: app_net:

这里有几个值得细说的点。version字段在目前Docker Compose V2中已不再强制要求,但保留它并无坏处。depends_on只表示启动顺序先后,不代表上游服务已完全就绪,如果MySQL初始化需要较长时间,应用容器可能会在MySQL就绪前就尝试连接并报错,这种情况下需要在应用容器里加入重试逻辑,或者使用健康检查来控制依赖。networks这一段也很关键,它让所有服务处于同一个自定义网络上,容器之间可以直接用服务名互相访问,比如应用连接数据库时填的地址是mysql而不是localhost,这一点我会在下一部分展开讲。

4.2 MySQL 8.0、Redis 主从等中间件的 Compose 部署

服务端部署中最常见的中间件就是MySQL和Redis,我把这两个的Compose部署方式单独拿出来说,因为它们身上的坑非常典型。

先看MySQL 8.0的Compose配置。热词里频繁出现“docker安装mysql失败”,我排查过不少案例,绝大部分问题出在字符集、时区和密码复杂度上。下面这份配置可以作为一个相对稳定的模板:

services: mysql: image: mysql:8.0 container_name: mysql8 restart: always ports: - "3306:3306" environment: TZ: Asia/Shanghai MYSQL_ROOT_PASSWORD: "Str@ngP@ssw0rd" MYSQL_DATABASE: business MYSQL_USER: appuser MYSQL_PASSWORD: "App@12345" command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --default-time-zone=+08:00 volumes: - mysql_data:/var/lib/mysql - ./init:/docker-entrypoint-initdb.d healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot123"] interval: 10s timeout: 5s retries: 5 start_period: 30s volumes: mysql_data:

这里有个很有用的目录/docker-entrypoint-initdb.d,MySQL容器第一次启动时,如果数据库数据目录为空,它会自动执行这个目录下的.sql脚本,可以用来建表、导入初始化数据。注意只有首次启动时才会执行,数据卷里已经存在数据库文件后就不会再跑了,所以规划初始化数据脚本最好在第一次启动前就准备好。字符集参数同样很关键,MySQL 8.0默认字符集虽然是utf8mb4,但还是建议通过command显式指定,避免应用连接时出现中文乱码。

再看Redis主从的部署方式。如果只是单机Redis,Compose配置很简单;但有热词提到“redis主从”,这个场景我多说几句。用Compose编排主从时,最简单可靠的方式是启动两个Redis容器,主节点正常映射端口,从节点使用命令指定主节点地址。

services: redis-master: image: redis:7 container_name: redis-master ports: - "6379:6379" command: ["redis-server", "--appendonly", "yes"] redis-slave: image: redis:7 container_name: redis-slave ports: - "6380:6379" command: ["redis-server", "--replicaof", "redis-master", "6379"] depends_on: - redis-master

从节点的--replicaof参数里填的是redis-master,这正是Compose自定义网络的功劳,容器之间通过服务名解析到彼此的IP,不用关心底层IP会不会变。启动后可以用redis-cli -h localhost -p 6380 info replication验证主从状态,看到role:slave且连接正常就算成功。

4.3 健康检查与启动顺序控制

新手在编排多容器应用时最容易碰到的经典场景:应用容器启动后,疯狂报“连接数据库失败”,但仔细一看MySQL容器明明已经起来了。问题就在于MySQL虽然进程起来了,但还没准备好接受连接。MySQL初始化是需要时间的,而depends_on只负责“启动顺序”,并不关心服务是否真的就绪。

解法是加healthcheck。上面MySQL的Compose配置里已经写了一个,基本原理是让容器周期性地执行某个命令,Docker根据命令的退出码判断健康状态,健康状态变成healthy后,其他容器才能安全启动。

要让依赖方真正等待健康状态,需要在Compose文件里使用depends_on的条件语法,而不是仅写服务名列表:

services: app: image: myapp:1.2.3 depends_on: mysql: condition: service_healthy redis: condition: service_started

这样写之后,Compose会等MySQL的健康检查通过后才启动app应用容器。配合应用内部的重试机制,多容器部署的启动稳定性会有明显提升。这块是我在实际项目中反复调试过的经验,不少线上偶发的“启动时连接失败”问题,其实根源就在这里。

5. 网络、数据卷与本地化部署实战场景

5.1 网络模式选型:bridge、host还是none

Docker的网络是容器化部署中最乱、也最影响实际业务的一环。先说结论:绝大多数场景使用自定义bridge网络即可,极少数对网络性能有极致要求的场景可以考虑host模式,而none模式几乎只在自制网络插件时才用到。

默认的bridge网络能解决容器间通信,但有个问题:不同服务默认bridge网络时,服务名解析不可靠,容器重启后IP还会变化。自定义bridge网络则提供了内置的DNS解析能力,容器之间可以通过服务名互相通信,这才是Compose默认工作的基础。

host模式把容器直接放在宿主机网络中,没有端口映射,性能损耗最低,但代价是灵活性很差,不能动态映射端口,多个容器也不能同时监听同一个端口。如果某个应用对网络延迟极度敏感、又不介意这种限制,host模式可以试一下,但用它做正式编排前,最好先做一轮性能测试,确认收益真的存在。

还有一点需要提醒的是,不要随便用--network host的方式去避免端口映射,如果宿主机上已经有一个服务占用了该端口,容器间就会发生冲突。我自己一般只在部署一些特殊的监控组件时才会考虑host模式,常规业务一定会走自定义bridge。

5.2 数据卷与备份恢复

容器是“用完即走”的,但数据必须留下。Docker的数据持久化方案有三种,很多新手分不清,这里梳理一下。

第一种是bind mount,把宿主机目录直接挂载进容器,比如./conf:/app/conf。好处是修改宿主机文件容器里立刻生效,适合放配置文件、开发调试;缺点是目录权限和SELinux设置容易引发问题。

第二种是volume,由Docker管理的数据卷,比如mysql_data:/var/lib/mysql。好处是跨环境迁移方便、权限控制交给Docker,适合存放数据库这类关键数据。第三种是tmpfs,数据只存在内存中,容器停止数据就消失,适合存放临时文件。

我强烈建议数据库数据使用volume,不要图方便直接bind mount到宿主机普通目录,因为volume的权限处理更安全、迁移更友好。备份恢复也很关键,这里给出一个MySQL容器备份的示例命令:

# 备份:在宿主机对mysql容器执行mysqldump docker exec mysql8 sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases' > /data/backup/all_$(date +%Y%m%d).sql # 恢复:把备份文件导入容器 docker exec -i mysql8 sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD"' < /data/backup/all_20240101.sql

注意恢复时并不是直接复制文件到数据目录,而是通过mysql客户端导入逻辑备份,这样才能保证数据的一致性和兼容性。想在宿主机上写脚本做每日备份,把第一条命令封装成cron任务就可以。

5.3 本地大模型的容器化部署实践

最近热搜里“本地部署大语言模型”“ollama部署大模型”“dify本地部署”这类话题非常火,而这些场景正好非常适合用Docker编排来处理。究其原因,大模型依赖的Python运行环境、依赖库版本、CUDA版本非常容易互相冲突,用容器把环境隔离是天然的选择。

以Ollama部署本地模型为例,用Compose编排可以这样写:

services: ollama: image: ollama/ollama:latest container_name: ollama ports: - "11434:11434" volumes: - ollama_data:/root/.ollama restart: unless-stopped # 如有NVIDIA GPU,可增加下面的设备访问配置 deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] volumes: ollama_data:

这里有个关键的挂载点:ollama_data:/root/.ollama。模型文件动辄几个GB甚至十几GB,如果容器不挂载数据卷,每次重建容器都要重新下载模型,那体验会非常痛苦。挂载后,模型文件会固化在数据卷中,容器可以随意销毁重建。

把大模型相关的工具链,比如Dify、本地知识库这类应用,用Compose整体编排起来后,你会发现整个部署过程几乎不需要碰命令行里的“玄学配置”,只需改好Compose文件,执行docker compose up -d,环境自动就位。这个模式也适合内网离线部署:先把需要的镜像导出为tar文件,拷贝到目标机器再docker load导入,配合Compose文件,整个环境分钟级就能复现。

6. 高频故障与排查技巧

6.1 启动失败:从日志找线索

“Docker部署失败”是一个既定事实,排查失败才是真正的核心能力。我处理过最多的启动失败原因无非三类:端口占用、权限不足、配置错误。

端口占用最经典。执行docker run -p 8080:8080时如果提示端口已被绑定,先用lsof -i :8080或ss -tlnp | grep 8080查一下是哪个进程占用了端口。有时候是之前跑过的残留容器还在监听,用docker ps -a看一下,确认无用后docker rm清理掉。

权限不足也常见,特别是挂载宿主机目录后,容器内进程没有权限访问宿主机目录的情况。典型报错是Permission denied,这时候不要急着加--privileged,优先尝试调整宿主机目录的属主和权限,让目录的UID/GID与容器内进程匹配。我见过不少把容器开成privileged模式的案例,以为能解决所有权限问题,结果安全风险大幅增高。容器毕竟不是虚拟机,能不开特权模式就不开。

每次启动失败后第一件事永远是查日志,命令很简单:

docker logs --tail 500 -f <container_name>

日志里可能直接输出异常栈、SQL连接错误或配置文件缺失等具体原因,大多数排查都从这个命令开始。

6.2 网络不通:容器间通信与宿主机访问

“docker网络不通”在热词里也是一个高频问题。我把它拆成两类:容器间不通、宿主机与容器不通。

容器间不通,先检查是否在同一个自定义bridge网络中。如果在同一个网络,服务名应该能直接互通;如果发现两个容器分别在不同的网络中,可以执行docker network connect <网络名> <容器名>把它们加入同一个网络。曾经遇到过的情况是,Compose文件里写了很多服务,但因为某个服务忘了加入networks段,导致它跟其他服务不在同一个网段,连不上数据库。这种问题从Compose文件本身很容易看出来,所以写编排时养成“所有服务都属于同一个网络”的习惯,能省掉很多麻烦。

宿主机与容器不通,先查端口映射是否配置正确。执行docker port <容器名>查看实际映射关系。如果映射没问题,再考虑宿主机防火墙,常见错误是把容器端口映射到宿主机后,外部依然访问不了,这时要检查防火墙策略是否放行了对应端口。有时候还会遇到Docker容器内访问外网超时的情况,大概率是DNS解析问题,可以通过修改daemon.json中的DNS配置来解决。

6.3 日志膨胀与资源限制

容器运行久了,最容易爆的事故就是磁盘被日志塞满。默认的json-file日志驱动如果不限制大小,日志文件会无限增长,时间一长直接写满磁盘,整台服务器的服务全部瘫痪。我在daemon配置里就提前设置了max-size和max-file,这是第一道防线。

如果服务已经跑起来了,当时没配日志限制,也可以对运行中的容器进行配置修改,不过更快的办法是定期清理。归档日志时使用:

# 查看所有容器日志占用 du -sh /var/lib/docker/containers/*/*-json.log # 清理单个容器的日志 truncate -s 0 /var/lib/docker/containers/<container-id>/*-json.log

注意不要直接rm日志文件,因为Docker持有这个文件句柄,删除后空间不会立即释放,用truncate把文件清空是更稳妥的做法。

资源限制同样值得关注。一个容器吃满宿主机所有CPU和内存的案例我见过不少,因此生产环境的容器必须设置资源上限,尤其当一个宿主机上跑多个容器时。Compose文件里可以这样配置:

services: app: image: myapp:1.2.3 deploy: resources: limits: cpus: "2.0" memory: 2048M reservations: cpus: "0.5" memory: 256M

这表示该容器最多使用2个CPU核和2GB内存,低于这些资源时会至少保留0.5核和256MB。设置资源限制后,即使某个容器出现内存泄漏或死循环,也不会拖垮同机器上的其他应用。这一步把“局部故障”控制在一个容器内,才是容器隔离的真正意义所在,也是我在正式环境中必做的一项配置。

我个人在实际操作中最深的体会是,容器化部署的难点从来不是“启动一个容器”,而是“在多容器、多环境、多版本的情况下保持稳定”。镜像构建规范、Compose编排清晰、数据卷规划合理、日志监控到位,这些看似基础的事情,才是让Docker真正发挥价值的所在。如果你现在正在折腾某次容器部署,不妨从这几个维度重新审视一下自己的配置,哪怕只改掉一个不合理的地方,后面踩坑的概率也会降低很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 16:53:14

STM32无外部晶振启动模板:HSI内部时钟方案详解

1. 项目概述&#xff1a;为什么我会去做一个内晶振启动模板工程做嵌入式开发这些年&#xff0c;我接手过不少基于STM32的项目&#xff0c;发现一个问题反复出现&#xff1a;很多工程师默认拿到板子先焊外部晶振&#xff0c;然后按照标准库或者HAL库的默认配置把HSE&#xff08;…

作者头像 李华
网站建设 2026/10/6 16:51:26

企业网站h5源码从选型到部署:避坑指南与实战要点

简介&#xff1a;这套企业网站源码基于HTML5技术构建&#xff0c;定位为中小企业及个人开发者提供简洁大气的门户模板&#xff0c;可用于快速搭建形象展示、产品宣传与信息发布类站点&#xff0c;也适合前端学习者分析布局与交互实现。压缩包共2648个文件&#xff0c;大小34.15…

作者头像 李华
网站建设 2026/10/6 16:49:59

Agent-Reach实战指南:从工具调用到大模型触达能力全面解析

接手一个内部 AI 助手重构项目后&#xff0c;我彻底被一个词折磨到失眠——Agent-Reach。项目进度表上的功能卡片贴了一整墙&#xff0c;模型也能把业务问题回答得头头是道&#xff0c;可真让它"去把某个配置改掉""查一下库存再给出采购建议"时&#xff0c…

作者头像 李华
网站建设 2026/10/6 16:49:55

为什么数据库不用红黑树?B树定义与磁盘I/O底层逻辑深度解析

接触B树的时机通常有两种。一种是还在读书时背数据结构教材的定义&#xff0c;背完应付考试&#xff0c;考完就忘&#xff1b;另一种是工作之后因为索引优化、慢查询排查&#xff0c;被数据库底层结构逼着回来补课。我属于第二种。记得有一次线上范围查询慢得离谱&#xff0c;E…

作者头像 李华
网站建设 2026/10/6 16:47:06

Context-Mode实战:大模型对话上下文管理策略与工程实现

从刚接触大模型应用那会儿开始&#xff0c;我一直被一个问题反复折磨&#xff1a;聊天机器人聊着聊着就“失忆”&#xff0c;可一旦我把所有历史记录全部塞给模型&#xff0c;它又变得又慢又贵&#xff0c;甚至会翻出早该过期的信息来自作聪明。这个“给多少上下文、怎么给、什…

作者头像 李华