服务端6月5日 22:29
Docker Desktop 怎么用?安装配置和常见问题Docker Desktop 是在 Mac 和 Windows 上用 Docker 最省事的方式——装一个应用就拥有完整的 Docker 环境,不需要折腾虚拟机或 Linux 双系统。但它不只是个安装包,里面的 WSL2 集成、Kubernetes 支持、资源管理有些门道值得了解。
## Docker Desktop 里装了什么
Docker Desktop 不是 Docker Engine 的 GUI 包装——它是一个完整的开发环境:
| 组件 | 作用 |
|------|------|
| Docker Engine | 容器运行时 |
| Docker CLI | 命令行工具 |
| Docker Compose | 多容器编排 |
| Docker BuildKit | 高性能构建引擎 |
| Kubernetes (可选) | 单节点 K8s 集群 |
| Docker Scout | 镜像漏洞扫描 |
| Docker Extensions | 扩展市场 |
Mac 上 Docker Desktop 通过一个轻量级 Linux 虚拟机运行 Docker Engine(因为 Docker 本质上需要 Linux 内核)。Windows 上通过 WSL2 运行。
## 安装后的关键配置
### 资源分配
默认配置经常不够用——4GB 内存跑不了几个容器:
```
Settings → Resources
CPUs: 4-6(建议宿主机的 50%)
Memory: 8-12GB(建议宿主机的 50%)
Swap: 2GB
Disk image size: 60GB+
```
调完后 Docker Desktop 会重启虚拟机。分配太多会导致宿主机卡顿,太少容器 OOM。建议 CPU 和内存各分一半给 Docker。
### WSL2 集成(Windows)
Docker Desktop 在 Windows 上跑在 WSL2 里。开启后可以在 WSL2 的 Linux 发行版中直接使用 `docker` 命令:
```
Settings → Resources → WSL Integration
✅ Enable integration with my default WSL distro
✅ Ubuntu (或其他发行版)
```
这样在 Windows Terminal 的 Ubuntu 标签页里直接 `docker run`,不需要额外安装 Docker。
### 文件共享性能
Mac 上 Docker 挂载目录特别慢——因为文件要在 macOS 和 Linux 虚拟机之间同步。VirtioFS 是 Docker Desktop 4.x 以后的新方案,比之前的 gRPC FUSE 快很多:
```
Settings → General
✅ Choose file sharing implementation for your containers: VirtioFS
```
另外 `node_modules` 这种大量小文件的目录不要挂载——用匿名 volume 代替:
```yaml
services:
app:
volumes:
- .:/app # 代码目录挂载
- /app/node_modules # node_modules 用容器内的,不走文件共享
```
## 日常使用的核心功能
### 镜像管理
```
Docker Desktop → Images
```
可以搜索、拉取、删除镜像,查看镜像层信息。比命令行更直观——特别是看哪个镜像占了多少磁盘。
### 容器管理
```
Docker Desktop → Containers
```
启动、停止、删除容器,查看日志,进入容器终端。小技巧:点击容器的端口号可以直接在浏览器打开。
### Volume 管理
```
Docker Desktop → Volumes
```
查看所有 Docker Volume 占用的磁盘空间。容器删了 Volume 不会自动删——时间久了会积累大量废弃数据。定期清理:
```bash
docker volume prune # 删除所有未被容器引用的 volume
```
### 构建缓存清理
```bash
docker builder prune # 清理构建缓存
docker system prune -a # 一键清理所有未使用的资源(镜像、容器、网络、缓存)
```
Docker Desktop 的 Troubleshoot 页面也有 "Clean / Purge data" 按钮——重置整个 Docker 环境。
## Kubernetes 支持
Docker Desktop 内置了单节点 Kubernetes 集群,一键开启:
```
Settings → Kubernetes
✅ Enable Kubernetes
```
开启后 `kubectl` 直接可用:
```bash
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# docker-desktop Ready control-plane 1m v1.28.0
```
适合本地开发测试 K8s manifest,不需要装 minikube 或 kind。
**注意**:开启 K8s 会额外占用 2-3GB 内存。不用时建议关掉。
## Docker Extensions
Docker Desktop 支持扩展,常用的几个:
| 扩展 | 功能 |
|------|------|
| Disk Usage | 可视化磁盘占用分析 |
| Trivy | 镜像安全扫描 |
| DDEV | 本地开发环境管理 |
| Tilt | 实时开发工作流 |
安装方式:Extensions → Browse → Install
## Docker Desktop 的替代方案
Docker Desktop 对个人免费,但大企业(250+ 员工或 1000 万+ 美元年收入)需要付费订阅。如果不想付费:
| 替代方案 | 平台 | 说明 |
|---------|------|------|
| **OrbStack** | Mac | 比 Docker Desktop 快 3-5 倍启动,内存占用少 |
| **Rancher Desktop** | Mac/Win/Linux | 开源免费,支持 containerd 和 dockerd |
| **Colima** | Mac | 命令行工具,轻量,基于 Lima |
| **Podman Desktop** | Mac/Win/Linux | Red Hat 出品,无守护进程 |
**推荐**:Mac 用户优先试 OrbStack——启动快、内存省、文件共享性能好,个人免费。
## 常见问题
### Docker Desktop 启动慢
Mac 上首次启动需要 30-60 秒。加快方法:不要关 Docker Desktop,用 `docker stop` 停容器即可。Docker Desktop 自身在后台几乎不占 CPU。
### 磁盘空间持续增长
Docker 的虚拟磁盘(`Docker.raw` / `data.vhdx`)只会增大不会自动缩小。即使删了镜像,虚拟磁盘文件也不会缩小。解决:
```bash
# Mac:压缩虚拟磁盘
docker system prune -a
# 然后重启 Docker Desktop → Troubleshoot → Clean / Purge data
```
### 容器网络访问宿主机服务
容器里访问宿主机(比如宿主机上的数据库):
```bash
# 从容器内访问宿主机
curl http://host.docker.internal:5432
```
`host.docker.internal` 是 Docker Desktop 提供的特殊 DNS 名,自动解析为宿主机 IP。
标签
Docker
Docker 是一个开源的容器化平台,允许开发者打包应用及其全部依赖到一个可移植的容器中,然后这个容器可以在任何机器上运行,确保应用在不同环境之间运行的一致性。Docker 使用了 Linux 容器(LXC)的技术,但进行了扩展,使其更为易用、功能更全面。

服务端6月5日 22:29
Docker 反向代理该用 Nginx 还是 Traefik?部署和自动发现对比Docker 部署反向代理的核心问题是:容器 IP 每次重启都会变,手动配 upstream 不现实。Nginx 需要手动更新配置,Traefik 能自动发现容器。选哪个取决于你的场景。
## Nginx:手动配置但性能最强
### 基本反向代理
```yaml
# docker-compose.yml
services:
nginx:
image: nginx:1.25
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./certs:/etc/nginx/certs:ro
networks:
- frontend
app1:
image: myapp:latest
networks:
- frontend
app2:
image: another-app:latest
networks:
- frontend
networks:
frontend:
```
```nginx
# nginx/conf.d/default.conf
upstream app1 {
server app1:3000;
}
upstream app2 {
server app2:8080;
}
server {
listen 80;
server_name app1.example.com;
location / {
proxy_pass http://app1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 80;
server_name app2.example.com;
location / {
proxy_pass http://app2;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
Docker Compose 的服务名(`app1`、`app2`)会自动解析为容器 IP——不需要硬编码 IP。但容器重启后如果 upstream 数量变了(比如扩容),Nginx 不会自动感知。
### SSL 配置(Let's Encrypt)
手动获取证书:
```bash
certbot certonly --standalone -d example.com
```
```nginx
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
location / {
proxy_pass http://app1;
}
}
```
**问题**:证书 90 天过期,需要 cron 定时续期 + `docker exec nginx nginx -s reload`。
### Nginx 什么时候合适
- 流量非常大,需要极致性能
- 配置不频繁变动
- 团队熟悉 Nginx
## Traefik:自动发现的反向代理
Traefik 的核心卖点:容器启动/停止时自动更新路由,不需要改配置文件或重启代理。
### 基本配置
```yaml
services:
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
ports:
- "80:80"
- "443:443"
- "8080:8080" # Dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
app1:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.app1.rule=Host(`app1.example.com`)"
- "traefik.http.routers.app1.entrypoints=web"
- "traefik.http.services.app1.loadbalancer.server.port=3000"
```
关键点:
- `--providers.docker=true` 启用 Docker Provider,自动监听容器事件
- `exposedbydefault=false` 只代理有 `traefik.enable=true` 标签的容器
- 容器的路由规则通过 labels 配置,不需要单独的配置文件
### 自动 SSL(Let's Encrypt)
```yaml
services:
traefik:
command:
- "--certificatesresolvers.le.acme.email=you@example.com"
- "--certificatesresolvers.le.acme.storage=/acme.json"
- "--certificatesresolvers.le.acme.tlschallenge=true"
volumes:
- ./acme.json:/acme.json
app1:
labels:
- "traefik.http.routers.app1.tls=true"
- "traefik.http.routers.app1.tls.certresolver=le"
```
Traefik 自动申请和续期证书——不需要 cron,不需要手动 reload。这是 Traefik 相比 Nginx 最大的优势。
### 负载均衡
同一个服务多个实例,Traefik 自动负载均衡:
```yaml
app1:
deploy:
replicas: 3
labels:
- "traefik.http.services.app1.loadbalancer.server.port=3000"
```
3 个副本自动分摊流量,扩容缩容 Traefik 实时感知。
## Caddy:最简配置
Caddy 的卖点是一个 Caddyfile 搞定反向代理 + 自动 HTTPS:
```yaml
services:
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
volumes:
caddy_data:
```
```
# Caddyfile
app1.example.com {
reverse_proxy app1:3000
}
app2.example.com {
reverse_proxy app2:8080
}
```
就这样——Caddy 自动申请 Let's Encrypt 证书、自动续期、自动 HTTP→HTTPS 重定向。比 Nginx 少 90% 的配置。
**局限**:不如 Nginx 灵活(复杂的 rewrite/条件判断不好写),不如 Traefik 自动化(不自动发现容器),适合简单的反向代理场景。
## 选择决策
| 场景 | 推荐 | 原因 |
|------|------|------|
| 简单反向代理 + HTTPS | Caddy | 配置最少,自动 HTTPS |
| 容器频繁变动 | Traefik | 自动发现、自动配置 |
| 流量极大 | Nginx | 性能最强 |
| 需要 Let's Encrypt | Traefik 或 Caddy | Nginx 需要手动续期 |
| 已有 Nginx 运维经验 | Nginx | 熟悉的工具不容易出问题 |
| Docker Compose 项目 | Traefik | labels 配置和 compose 一体 |
| Kubernetes 环境 | Nginx Ingress | K8s 生态标配 |
**起步建议**:Docker Compose 项目用 Traefik,省心省力。需要极致性能或复杂路由时切换 Nginx。
服务端6月5日 22:29
Docker 容器怎么监控?Prometheus + Grafana 完整方案容器出问题了,`docker stats` 只能看到 CPU 和内存——磁盘 IO、网络、进程状态全不知道。完整的监控系统需要指标采集、存储、可视化、告警四层。
## Docker 监控的四层架构
```
容器 → 采集器 → 存储后端 → 可视化
cAdvisor Prometheus Grafana
Node Loki
Exporter
```
- **采集层**:从容器和宿主机收集指标数据
- **存储层**:时序数据库存指标,日志库存日志
- **可视化层**:Dashboard 展示趋势、图表
- **告警层**:超过阈值自动通知
## 指标采集:cAdvisor + Node Exporter
### cAdvisor——容器指标
Google 出品,自动发现所有容器,采集 CPU、内存、网络、磁盘 IO:
```yaml
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
```
cAdvisor 自带一个简单的 Web UI(`http://localhost:8080`),可以看每个容器的实时指标。
### Node Exporter——宿主机指标
cAdvisor 只管容器,宿主机本身的 CPU、内存、磁盘、网络由 Node Exporter 采集:
```yaml
services:
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
```
## Prometheus——指标存储和查询
Prometheus 定时从 cAdvisor 和 Node Exporter 拉取指标,存到自己的时序数据库:
```yaml
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
volumes:
prometheus_data:
```
```yaml
# prometheus/prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'app'
static_configs:
- targets: ['app:8080']
```
`scrape_interval: 15s` 表示每 15 秒采集一次。容器数量多时可以调大到 30s-60s 减少负载。
### 常用 PromQL 查询
```promql
# 所有容器的 CPU 使用率
rate(container_cpu_usage_seconds_total{name!=""}[5m]) * 100
# 容器内存使用量(MB)
container_memory_usage_bytes{name!=""} / 1024 / 1024
# 宿主机磁盘使用率
(1 - node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100
# 容器网络流入速率
rate(container_network_receive_bytes_total[5m])
```
## Grafana——可视化 Dashboard
Prometheus 的 UI 只适合临时查询。正式的监控面板用 Grafana:
```yaml
services:
grafana:
image: grafana/grafana:10.3.0
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
```
配置步骤:
1. 打开 `http://localhost:3000`(默认 admin/admin)
2. 添加数据源 → Prometheus → URL 填 `http://prometheus:9090`
3. 导入 Dashboard:推荐 ID **11600**(Docker 监控)和 **1860**(Node Exporter 全量)
导入方式:Dashboard → Import → 输入 ID → Load → 选择 Prometheus 数据源
## 告警:Alertmanager
Prometheus 本身只负责判断规则,告警通知由 Alertmanager 发送:
```yaml
# prometheus/alert_rules.yml
groups:
- name: docker_alerts
rules:
- alert: ContainerCpuHigh
expr: rate(container_cpu_usage_seconds_total{name!=""}[5m]) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "容器 {{ $labels.name }} CPU 超过 80%"
- alert: ContainerMemoryHigh
expr: container_memory_usage_bytes / container_spec_memory_limit_bytes * 100 > 90
for: 5m
labels:
severity: critical
- alert: DiskSpaceLow
expr: (1 - node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 > 85
for: 10m
```
`for: 5m` 表示持续 5 分钟才告警——避免短暂波动误报。
## 完整的 docker-compose 监控栈
```yaml
version: "3.8"
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus:/etc/prometheus
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
grafana:
image: grafana/grafana:10.3.0
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
node-exporter:
image: prom/node-exporter:latest
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
alertmanager:
image: prom/alertmanager:latest
volumes:
- ./alertmanager:/etc/alertmanager
volumes:
prometheus_data:
grafana_data:
```
这套组合约需 2-3GB 内存,覆盖了指标采集、存储、可视化、告警四层。
## 监控方案选择
| 规模 | 推荐方案 | 内存需求 |
|------|---------|---------|
| 单机开发 | `docker stats` + cAdvisor Web | < 500MB |
| 小团队(<20 容器) | Prometheus + Grafana + cAdvisor | 1-2GB |
| 中等规模 | 完整监控栈 + Alertmanager | 2-4GB |
| 大规模/生产 | Kubernetes + Prometheus Operator | 按需扩展 |
**起步建议**:先跑 cAdvisor + Prometheus + Grafana 三件套,够用了再加告警。
服务端6月5日 22:29
Docker 容器日志怎么聚合?ELK、Loki 和 EFK 怎么选容器一多,日志分散在各处——`docker logs` 只能看单个容器的输出,排查问题要一个一个翻。日志聚合把所有容器的日志集中到一个地方,搜索、过滤、告警一站搞定。
## Docker 日志驱动:日志的入口
Docker 支持多种日志驱动,决定容器日志的去向:
```bash
# 查看当前日志驱动
docker info --format '{{.LoggingDriver}}'
# 默认是 json-file
```
```yaml
# docker-compose.yml 全局配置
services:
app:
logging:
driver: json-file
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 最多保留 3 个文件
```
`json-file` 默认不限制大小——跑久了磁盘会被日志撑满。务必配 `max-size` 和 `max-file`。
### 其他日志驱动
| 驱动 | 去向 | 适用场景 |
|------|------|---------|
| `json-file` | 本地文件 | 默认,简单场景 |
| `local` | 本地文件(压缩) | 省磁盘 |
| `journald` | systemd journal | CentOS/Ubuntu 系统日志 |
| `fluentd` | Fluentd | 接入日志聚合栈 |
| `syslog` | syslog 服务 | 传统运维 |
生产环境推荐用 `fluentd` 或 `local` 驱动——前者直接接入日志聚合,后者比 json-file 省磁盘。
## ELK Stack:经典方案
Elasticsearch + Logstash + Kibana,功能最全但最重:
```yaml
# docker-compose.yml
services:
elasticsearch:
image: elasticsearch:8.12.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
logstash:
image: logstash:8.12.0
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
ports:
- "5044:5044"
kibana:
image: kibana:8.12.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
app:
image: myapp:latest
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: myapp
volumes:
es_data:
```
**问题**:ELK 最少需要 4GB 内存才能跑起来。小团队或开发环境用太重了。
## EFK Stack:轻量替代
用 Fluentd 替代 Logstash,更省资源:
```yaml
services:
fluentd:
image: fluent/fluentd:v1.16
volumes:
- ./fluentd/conf:/fluentd/etc
ports:
- "24224:24224"
environment:
- FLUENTD_CONF=fluent.conf
```
```conf
# fluentd/conf/fluent.conf
<source>
@type forward
port 24224
</source>
<match **>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix fluentd
<buffer>
@type file
path /var/log/fluentd/buffer
flush_interval 5s
</buffer>
</match>
```
容器配置 `logging.driver: fluentd` 后,所有 stdout/stderr 输出自动发到 Fluentd,Fluentd 转存到 Elasticsearch。
## Grafana Loki:最轻量的选择
Loki 只索引标签不索引日志内容,存储成本比 Elasticsearch 低 10 倍以上:
```yaml
# docker-compose.yml
services:
loki:
image: grafana/loki:2.9.0
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:2.9.0
volumes:
- /var/log:/var/log
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail/config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:10.3.0
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
```
Promtail 从 Docker 容器日志目录读取日志,推送到 Loki。Grafana 查询和可视化。
**Loki 的查询语法**(LogQL)比 Kibana 的 KQL 简单:
```logql
{container_name="myapp"} |= "error" | json | line_format "{{.message}}"
```
意思是:从 myapp 容器日志中过滤包含 "error" 的行,解析 JSON,只显示 message 字段。
## 日志结构化:让聚合更有效
非结构化日志在聚合平台里只能做全文搜索。结构化日志可以按字段过滤、聚合统计:
```python
# Python 结构化日志(JSON 格式)
import logging
import json
class JSONFormatter(logging.Formatter):
def format(self, record):
return json.dumps({
"timestamp": self.formatTime(record),
"level": record.levelname,
"message": record.getMessage(),
"service": "user-service",
"trace_id": getattr(record, "trace_id", None),
})
```
```javascript
// Node.js 用 pino 直接输出 JSON
const logger = require('pino')({
level: 'info',
formatters: { level: (label) => ({ level: label }) },
})
logger.info({ userId: 123, action: 'login' }, 'User logged in')
```
## 选择决策
| 场景 | 推荐方案 | 理由 |
|------|---------|------|
| 开发/测试 | `docker logs` + json-file | 够用 |
| 小团队(<10 服务) | Grafana Loki + Promtail | 轻量,1GB 内存够 |
| 中大型团队 | EFK Stack | 功能全、社区大 |
| 需要全文搜索 | ELK Stack | Elasticsearch 全文检索最强 |
| 已有 Grafana | 直接加 Loki | 不用额外装 Kibana |
| 日志量巨大 | Loki(只索引标签) | 存储成本最低 |
**起步建议**:先用 Loki + Grafana,轻量够用。等日志量大到需要全文搜索时再迁移到 ELK。
服务端6月5日 22:29
Docker 容器配置怎么管理?环境变量、挂载和配置中心怎么选Docker 容器的配置管理不是把配置写死在镜像里——那样每次改配置都得重新构建。正确做法是配置与镜像分离,容器启动时注入配置,运行时能动态更新。这篇讲清楚 Docker 环境下四种配置管理方式的适用场景和实现方法。
## 环境变量:最简单的配置注入
适合少量、扁平的配置项(数据库地址、端口、开关):
```yaml
# docker-compose.yml
services:
app:
image: myapp:latest
environment:
- DB_HOST=postgres
- DB_PORT=5432
- LOG_LEVEL=info
- FEATURE_FLAG=true
```
或者用 `.env` 文件:
```bash
# .env
DB_HOST=postgres
DB_PORT=5432
LOG_LEVEL=info
```
```yaml
services:
app:
env_file: .env
```
**优势**:简单、Docker 原生支持、docker-compose 直接读取
**局限**:只有字符串值、不能表示嵌套结构、修改需要重启容器
### 敏感信息别放环境变量
环境变量会被 `docker inspect` 暴露,也会出现在进程列表里。密码、密钥等用 Docker Secret 或文件挂载:
```bash
# Docker Swarm Secret
echo "my_password" | docker secret create db_password -
```
```yaml
services:
app:
secrets:
- db_password
secrets:
db_password:
external: true
```
## 配置文件挂载:复杂配置的首选
当配置是结构化的(YAML、JSON、TOML),用 Volume 挂载比环境变量更合适:
```yaml
services:
app:
image: myapp:latest
volumes:
- ./config/app.yml:/app/config/app.yml:ro
- ./config/nginx.conf:/etc/nginx/conf.d/default.conf:ro
```
`:ro` 表示只读挂载——容器不能修改配置文件,只能读取。防止容器内进程意外修改配置。
### 只挂载需要的文件,别挂载整个目录
```yaml
# 好:只挂载需要的文件
volumes:
- ./config/app.yml:/app/config/app.yml:ro
# 差:挂载整个目录,可能暴露无关文件
volumes:
- ./config:/app/config:ro
```
### 配置热更新:不重启容器更新配置
挂载的文件修改后,容器内立即可见——但应用是否自动重新加载取决于应用本身:
- **Nginx**:`docker exec nginx nginx -s reload`
- **Spring Boot**:配合 Spring Cloud Config 自动刷新
- **Node.js**:用 `chokidar` 监听文件变化
如果应用不支持热加载,可以配合 `inotifywait` 检测文件变化后发送信号:
```bash
#!/bin/bash
inotifywait -m -e modify /app/config/app.yml | while read event; do
kill -SIGHUP 1 # 发送 HUP 信号给主进程
done
```
## 配置中心:分布式系统的统一配置
多服务、多实例的场景,配置文件挂载管理成本太高——改一个配置要同步到所有机器。配置中心解决的就是这个问题。
### Consul + Consul-Template
```yaml
# docker-compose.yml
services:
consul:
image: consul:1.15
ports:
- "8500:8500"
command: agent -dev -client=0.0.0.0
app:
image: myapp:latest
volumes:
- ./templates:/templates
command: >
consul-template
-consul-addr=consul:8500
-template="/templates/app.ctmpl:/app/config/app.yml:docker restart app"
```
`consul-template` 监听 Consul KV 变化,自动重新生成配置文件并触发容器重启。
### Etcd + Confd
和 Consul-Template 类似的模式,Etcd 做存储,Confd 做模板渲染:
```bash
# 写入配置
etcdctl set /myapp/db_host "postgres.prod"
# confd 读取 etcd 并渲染模板
confd -onetime -backend etcd -node http://etcd:2379
```
### Spring Cloud Config Server
Java 生态的标准方案:
```yaml
services:
config-server:
image: springcloud/configserver
ports:
- "8888:8888"
environment:
- SPRING_CLOUD_CONFIG_SERVER_GIT_URI=https://github.com/org/config-repo
app:
image: my-spring-app
environment:
- SPRING_CLOUD_CONFIG_URI=http://config-server:8888
```
配置存在 Git 仓库里,有版本历史。应用启动时从 Config Server 拉取配置,配合 `/actuator/refresh` 端点实现热更新。
## Kubernetes ConfigMap 和 Secret
如果跑在 K8s 上,环境变量和文件挂载都由 ConfigMap/Secret 管理:
```yaml
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DB_HOST: postgres
app.yml: |
server:
port: 8080
logging:
level: info
---
# Pod 使用 ConfigMap
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: app-config
volumeMounts:
- name: config
mountPath: /app/config
volumes:
- name: config
configMap:
name: app-config
```
Secret 和 ConfigMap 用法一样,但值是 base64 编码的,且访问可以加 RBAC 控制:
```bash
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=s3cret
```
**ConfigMap/Secret 更新后 Pod 不会自动重启**——需要手动 `kubectl rollout restart` 或用 Reloader 之类的工具自动触发。
## 选择决策
| 场景 | 推荐方案 | 原因 |
|------|---------|------|
| 单容器、少量配置 | 环境变量 + .env | 最简单 |
| 单容器、复杂配置 | Volume 挂载配置文件 | 支持结构化配置 |
| 多容器、同一台主机 | docker-compose + 挂载 | compose 管理方便 |
| 多主机、多服务 | Consul/Etcd 配置中心 | 统一管理、动态更新 |
| Kubernetes 环境 | ConfigMap + Secret | K8s 原生方案 |
| 敏感信息 | Docker Secret / K8s Secret | 不暴露在环境变量里 |
**原则**:配置与代码分离,敏感信息加密,变更可追溯。能用简单的就不用复杂的——别为了用 Consul 而用 Consul。
服务端6月3日 00:04
Docker Compose 怎么用?多容器编排和常用命令速查Docker Compose 用一个 YAML 文件定义和运行多容器应用。一条命令启动所有服务,不用逐个 docker run。
## docker-compose.yml 基本结构
关键配置:
- build: . — 用当前目录的 Dockerfile 构建镜像
- ports: 宿主机端口:容器端口
- environment: 环境变量(同一网络内用服务名互访,如 DB_HOST: db)
- depends_on: 启动依赖(只控制启动顺序,不等服务就绪)
- volumes: 数据持久化(.:/app 挂载源码方便开发热更新)
## 常用命令
## 开发 vs 生产
开发时加 volume 挂载源码,代码修改实时生效:
生产时去掉 volume 挂载,用构建好的镜像:
用多个 compose 文件覆盖配置:
## 常见问题
**端口冲突**:某个端口已被占用。改宿主机端口(3001:3000)或停掉占用进程。
**容器启动后立即退出**:docker compose logs web 查看日志定位原因。
**Volume 数据残留**:docker compose down 不删除 Volume。要清空数据用 docker compose down -v。服务端6月3日 00:04
Docker 网络模式有哪些?bridge、host 和 overlay 怎么选?Docker 有四种网络模式:bridge(默认)、host、none、overlay。日常开发用 bridge,性能敏感用 host,集群通信用 overlay。
## bridge:默认模式
每个容器有独立网络栈,通过虚拟网桥(docker0)和宿主机通信。容器之间用服务名互访(同网络内),外部通过端口映射访问容器。
bridge 模式的端口映射(-p 8080:80)有一层 NAT 转换,理论上比 host 慢一点,但隔离性好。
自定义网络让容器间可以通过服务名通信:
## host:直接用宿主机网络
容器不隔离网络,直接用宿主机的网络栈。没有 NAT、没有端口映射,性能最好。
host 模式的限制:
- 端口冲突——多个容器不能监听同一个端口
- 没有网络隔离——容器能看到宿主机所有网络接口
- macOS/Windows 上不支持 host 模式(只有 Linux 支持)
适合:网络密集型应用(代理、负载均衡)、需要极低延迟的场景。
## none:无网络
容器没有网络接口,只有 loopback。适合纯计算任务(批处理、数据处理),不需要网络的场景。
## overlay:跨主机网络
Docker Swarm 模式下,overlay 网络让不同主机上的容器互相通信:
Kubernetes 有自己的网络方案(Flannel、Calico),不用 Docker overlay。
## 怎么选
- 开发/通用场景:bridge + 自定义网络
- 性能优先(Linux):host
- 纯计算/安全隔离:none
- Swarm 集群:overlay
- 生产环境:K8s 管网络,不需要选 Docker 网络模式服务端6月3日 00:04
Docker 镜像和 Dockerfile 是什么?从零构建镜像详解Docker 镜像是容器的模板——只读的文件包,包含运行应用所需的一切(代码、运行时、依赖、配置)。Dockerfile 是构建镜像的脚本——一行一个指令,告诉 Docker 怎么组装镜像。
## 镜像是什么
镜像是一个分层的文件系统。每个指令(RUN、COPY、ADD)创建一层,层层叠加。好处:多个镜像共享相同的基础层,节省磁盘和拉取时间。
镜像本身不可变。运行容器时,Docker 在镜像顶部加一个可写层——容器修改文件只影响这个可写层,不修改镜像。
## Dockerfile 基本结构
逐行解读:
- FROM:基础镜像,你的镜像在此之上构建
- WORKDIR:设置工作目录,后续指令都在这个目录下执行
- COPY:拷贝文件到镜像里
- RUN:构建时执行命令(安装依赖等)
- EXPOSE:声明端口(文档作用,不实际映射)
- CMD:容器启动时执行的命令
## 构建镜像
-t myapp:v1 给镜像打标签,. 表示 Dockerfile 在当前目录。
## Dockerfile 优化技巧
**1. 利用缓存**:Docker 按层缓存。package.json 没变时 npm ci 用缓存,不用重新安装。所以先 COPY package.json 再 COPY 源码——源码变了不影响依赖缓存。
**2. .dockerignore**:排除不需要的文件:
不加 .dockerignore 会把 node_modules 也 COPY 进去,既慢又大。
**3. 多阶段构建**:编译和运行分开,运行镜像不需要编译工具。详见多阶段构建专题。
## 镜像标签管理
不要只用 latest 标签——无法回滚。用版本号(v1.2.3)或 git commit hash 标记每个镜像。服务端6月3日 00:04
Docker 和虚拟机有什么区别?该用哪个?Docker 和虚拟机都是隔离运行环境的技术,但原理完全不同:虚拟机虚拟整套硬件(含操作系统),Docker 共享宿主机内核只隔离进程。结果:Docker 启动快 10 倍、内存省 5 倍,但隔离性不如虚拟机。
## 核心区别
| 维度 | Docker 容器 | 虚拟机 |
|------|------------|--------|
| 虚拟层级 | 进程级隔离(共享内核) | 硬件级虚拟化(独立内核) |
| 启动时间 | 秒级 | 分钟级 |
| 内存占用 | MB 级 | GB 级 |
| 镜像大小 | MB-Tens of MB | GB 级 |
| 性能损耗 | 接近原生 | 5-15% |
| 隔离性 | 弱(共享内核) | 强(独立内核) |
| 密度 | 单机跑几十个 | 单机跑几个 |
## 为什么 Docker 这么轻
虚拟机需要给每个实例装一套完整的操作系统(Linux 内核 + 用户空间),至少 1GB 内存。Docker 容器只是宿主机上的一个进程——用 Linux namespace 隔离进程、网络、文件系统,用 cgroup 限制资源。所有容器共享同一个内核,不需要重复运行操作系统。
打个比方:虚拟机是每家自建一栋房子(独立地基、管道),Docker 是同一栋楼里的不同公寓(共享地基和管道,各自有门锁)。
## Docker 做不到什么
因为共享内核,容器不能:
- 运行不同内核版本的系统(Linux 容器不能跑在 Windows 上,反之亦然)
- 完全隔离内核漏洞(一个容器的内核漏洞可能影响宿主机)
- 运行需要特定硬件驱动的应用
虚拟机可以——它有独立的内核,可以跑 Windows on Linux、Linux on macOS。
## 什么时候用虚拟机
- 需要运行不同操作系统(Windows + Linux 混合环境)
- 安全要求极高(金融、政务),需要内核级隔离
- 需要直接访问硬件(GPU 直通、特定网卡)
- 合规要求规定必须用虚拟机
## 什么时候用 Docker
- 微服务部署、CI/CD 流水线
- 开发环境统一(所有人跑相同的容器)
- 快速扩缩容
- 90% 的后端应用场景
## 混合使用
Docker 跑在虚拟机里是常见架构——云厂商的虚拟机(EC2/ECS)上跑 Docker 容器。虚拟机提供硬件级隔离(多租户安全),Docker 提供应用级隔离(部署灵活性)。服务端6月3日 00:02
Docker 健康检查怎么配?HEALTHCHECK 和 Compose 健康检查详解Docker 健康检查自动检测容器内应用是否正常——容器运行中不代表应用可用。MySQL 可能在做崩溃恢复,Nginx 可能配置错误 502,但容器状态都是 Up。
## Dockerfile 里配置
参数:
- --interval=30s:每 30 秒检查一次
- --timeout=5s:5 秒内没响应算失败
- --retries=3:连续 3 次失败才标记为 unhealthy
健康状态:starting(启动中)→ healthy(健康)→ unhealthy(不健康)
## docker-compose.yml 里配置
start_period 很重要——应用启动需要时间(Spring Boot 可能要 30 秒),启动期间的健康检查失败不应该算数。
## 不同应用的健康检查命令
## depends_on 配合健康检查
Compose 的 depends_on 默认只等容器启动,不等应用就绪:
condition: service_healthy 确保 db 真正可用后 web 才启动。比 depends_on: db(只等容器启动)更可靠。
## 没有 curl 的镜像
Alpine 镜像没有 curl。用 wget 替代:
或者安装 curl:RUN apk add --no-cache curl服务端6月3日 00:02
Docker 多阶段构建怎么用?减小镜像体积的最佳方法多阶段构建让 Dockerfile 分多个阶段——编译阶段用完整环境构建产物,运行阶段只拷贝最终产物。结果:镜像从 1GB+ 缩小到 50MB 以下。
## 问题:单阶段构建镜像太大
node:20 镜像 1.1GB,加上 node_modules 几百 MB,最终镜像可能 1.5GB。但运行时只需要 dist/ 目录和 node 生产依赖。
## 多阶段构建
--from=builder 从第一阶段拷贝指定目录。node:20-slim 只有 200MB,最终镜像约 300MB——比单阶段小 5 倍。
## Go 应用:极致压缩
Go 编译出单个二进制文件,运行时不需要 Go 环境:
scratch 是空镜像——里面只有你拷贝的二进制文件。最终镜像可能只有 10-20MB。
## 前端应用:Nginx 托管静态文件
前端只需要构建后的 HTML/CSS/JS,不需要 node_modules。nginx:alpine 只有 25MB。
## COPY --from 的其他用法
不限于同一 Dockerfile 的阶段,可以从其他镜像拷贝:
从 Caddy 官方镜像里只拷贝二进制文件,不用自己安装。
## 关键要点
- 每个 FROM 开始一个新阶段,只有最后一个阶段的产物进入最终镜像
- 用 AS 命名阶段,COPY --from=名称 引用
- 运行阶段尽量用 slim/alpine 变体
- 不要把源码、编译工具、dev 依赖带进运行镜像服务端6月2日 23:47
Docker 容器数据怎么备份和恢复?Volume 备份和数据库导出实战Docker 容器的数据在 Volume 里——备份 Volume 就是备份数据。两种方式:直接备份 Volume 文件,或从容器内导出。
## 方法一:备份 Volume 目录
简单粗暴,但要求停掉容器或确保数据一致性(数据库正在写入时备份可能损坏)。
## 方法二:用临时容器备份
不停容器,用 --volumes-from 挂载同一个 Volume:
临时容器挂载 pg_data(只读)和宿主机 /backup 目录,把数据打包到宿主机。
## 数据库导出(推荐)
数据库不适合直接拷文件——文件可能处于不一致状态。用数据库的导出工具:
在容器里执行:
导出的是 SQL 文本,保证逻辑一致性,可以跨版本恢复。
## 恢复
数据库恢复用 psql/mysql 命令,不用拷文件。
## 自动化备份
备份文件要推到远程存储(S3、OSS),不要只存在本机——本机挂了备份也没了。
## Docker Volume 备份 vs 数据库导出
- Volume 备份:快,但不保证一致性,适合非数据库文件
- 数据库导出:慢,但保证一致性,适合数据库
- 两者配合:Volume 备份应用配置/上传文件,数据库导出业务数据服务端6月2日 23:47
Docker 容器怎么监控?Prometheus + Grafana 和告警配置实战Docker 监控分两层:容器级(CPU/内存/网络)和应用级(QPS/延迟/错误率)。容器级用 cAdvisor + Prometheus,应用级用代码埋点 + Prometheus。统一在 Grafana 看板和告警。
## 最快上手:docker stats
只适合临时查看,没有历史数据、没有告警、没有可视化。
## Prometheus + cAdvisor:容器级监控
cAdvisor 采集容器的 CPU、内存、网络、磁盘 IO 指标,Prometheus 存储和查询,Grafana 可视化。
cAdvisor 暴露 /metrics 端点,Prometheus 定时拉取。Grafana 导入 Docker dashboard 模板(ID 893)即可看到容器资源看板。
## 关键监控指标
| 指标 | 含义 | 告警阈值 |
|------|------|----------|
| container_cpu_usage_seconds_total | CPU 使用率 | > 80% 持续 5 分钟 |
| container_memory_usage_bytes | 内存使用 | > 90% 限制值 |
| container_network_receive_bytes_total | 网络接收 | 异常突增 |
| container_oom_events | OOM 次数 | > 0 立即告警 |
OOM 事件是最严重的——容器被杀意味着应用中断,必须立即处理。
## 告警配置
Prometheus Alertmanager 配置告警规则和通知渠道:
通知渠道支持邮件、Slack、钉钉、企业微信。
## 日志监控
监控 + 日志配合:告警触发后用 docker logs 查看对应容器的日志,定位问题。如果用了 Loki,直接在 Grafana 里查日志,不用跳到终端。服务端6月2日 23:47
Docker 编排工具怎么选?Docker Compose、Swarm 和 Kubernetes 对比Docker 编排工具解决的是多容器管理问题——手动 docker run 管几个容器还行,几十个就力不从心了。三个主流方案:Compose(开发)、Swarm(小团队)、Kubernetes(生产)。
## Docker Compose:开发环境首选
一条 docker compose up -d 启动所有服务。适合本地开发、CI 测试、小型项目部署。
局限:单机运行,不支持自动扩缩容,没有滚动更新,没有服务发现。服务挂了需要手动重启。
## Docker Swarm:轻量级集群
Swarm 内置在 Docker 里,不需要额外安装。支持多节点集群、滚动更新、服务发现、内置负载均衡。
局限:功能比 K8s 少很多——没有自动扩缩容(HPA)、没有自定义调度、没有 CRD 扩展。社区在萎缩,新项目不建议选 Swarm。
## Kubernetes:生产标准
K8s 是容器编排的事实标准。功能完整:自动扩缩容、滚动更新、服务发现、配置管理、密钥管理、持久卷、网络策略、审计日志。
K8s 的代价:学习曲线陡、运维复杂、需要专门的平台团队。小项目用 K8s 是杀鸡用牛刀。
## 怎么选
- 1-5 个服务:Docker Compose,简单够用
- 5-20 个服务,单集群:Swarm 或 K8s(建议直接 K8s,Swarm 没有未来)
- 20+ 个服务,多环境:Kubernetes
- 云上部署:直接用云厂商的 K8s 托管服务(EKS/GKE/AKS),别自己搭
一句话:开发用 Compose,生产用 K8s。Swarm 跳过。服务端6月2日 23:45
Docker 容器权限怎么管?rootless 模式、用户映射和安全加固Docker 容器默认以 root 运行——如果容器被攻破,攻击者获得容器内的 root 权限,可能逃逸到宿主机。安全的第一步:不要用 root 跑应用。
## 不要用 root 跑应用
USER appuser 之后的所有操作(CMD、ENTRYPOINT)都以 appuser 身份执行。即使应用有漏洞,攻击者只有普通用户权限。
## docker run 指定用户
如果 Dockerfile 里没有 USER,运行时指定:
覆盖 Dockerfile 里的 USER 指令。UID 1000 通常是宿主机的第一个普通用户。
## 只读文件系统
把容器文件系统挂载为只读,攻击者无法写入恶意文件。 给 /tmp 临时写入空间(很多应用需要)。
## 限制能力(Capabilities)
Linux capabilities 是细粒度的权限控制。Docker 默认给容器少量 capability,但还可以更严格:
删掉所有能力, 只加回绑定 1024 以下端口的能力。按需添加,不给多余的权限。
## Rootless Docker
Docker 默认以 root 运行 daemon——即使容器里不是 root,daemon 本身是 root。Rootless 模式让整个 Docker daemon 以普通用户运行:
Rootless 模式的限制:不能绑定 80/443 端口(需要 1024 以上),没有 cgroup 资源限制,网络功能受限。适合 CI/CD 和开发环境,生产环境不成熟。
## 资源限制
防止恶意或失控的容器吃光宿主机资源:
限制容器最多 100 个进程,防止 fork bomb。
## 安全检查清单
- 容器不以 root 运行(USER 指令或 --user 参数)
- 文件系统只读(--read-only)
- 最小 capability(--cap-drop ALL + 按需 cap-add)
- 资源限制(memory、cpus、pids)
- 不挂载 Docker socket(-v /var/run/docker.sock 是最危险的操作)
- 镜像来自可信源(不用来历不明的镜像)服务端6月2日 23:45
Docker 容器怎么更新?滚动更新、蓝绿部署和回滚策略Docker 容器是不可变的——更新不是在容器里改代码,而是用新镜像替换旧容器。关键是怎么替换才能不停服。
## 最简单的更新:停旧启新
有停机时间。适合内部工具、非关键服务。停机时间取决于镜像拉取速度和启动时间。
## 滚动更新:Docker Swarm
Swarm 的滚动更新逐个替换容器,始终保持部分实例在线:
更新过程中部分容器跑 v1,部分跑 v2,用户无感知。如果新版本有问题,回滚:
## 蓝绿部署
准备两套环境(蓝和绿),切换流量:
蓝绿部署零停机,但需要双倍资源。适合关键服务的版本更新。
## Docker Compose 更新
Compose 会检测镜像是否变化,只重建有变更的容器。但不是滚动更新——旧容器先停再启新容器,有短暂停机。
## 健康检查确保更新安全
更新后容器必须通过健康检查才算成功。连续失败 3 次,标记为 unhealthy。配合滚动更新,unhealthy 的新容器不会继续替换旧容器。
## 回滚策略
不管用什么更新方式,都要有回滚能力:
- 保留旧版本镜像(不要 docker rmi 删掉)
- 数据库变更要向后兼容(新版本能读旧 schema)
- 配置文件版本化管理(.env 用 git 追踪)
回滚就是 docker run 旧版本镜像。如果数据库迁移了且不兼容旧版本,回滚也救不了——所以数据库变更要分步做。服务端6月2日 23:43
Docker 容器日志怎么查看和分析?日志驱动和集中化方案Docker 日志分两类:容器标准输出(docker logs)和应用自己的日志文件。
## docker logs
应用必须把日志输出到 stdout/stderr 才能用 docker logs 查看。
## 日志轮转(防止磁盘爆满)
每个容器最多 3 个文件,每个 10MB,超过自动轮转。
## 集中化日志
多容器环境下逐个 docker logs 不现实。用日志聚合:
- Loki + Grafana:轻量,推荐
- ELK Stack:功能全但重
- Fluentd:日志收集器
## 排查技巧
- grep 过滤:docker logs my-container 2>&1 | grep ERROR
- 多容器:docker compose logs -f
- 启动失败:docker logs $(docker ps -lq)服务端6月2日 23:43
Docker 容器网络不通怎么排查?DNS、端口和防火墙问题定位容器网络问题分三类:容器间不通、容器访问不了外网、外部访问不了容器。按层级排查:DNS → 端口映射 → 防火墙。
## 容器间通信问题
不在同一个网络:docker network connect my-network container-b。
Docker 内置 DNS 只在同网络内生效。用服务名(ping redis)而不是 IP。ping redis 失败但 IP 能通是 DNS 问题。
## 端口映射问题
常见错误:忘了 -p 参数、端口冲突、只绑定了 127.0.0.1。
## 容器访问不了外网
DNS 失败时手动指定:docker run --dns 8.8.8.8。
## 防火墙拦截
Docker 修改 iptables 实现端口映射。ufw 开启但没放行 Docker 链会拦截流量。确保 DEFAULT_FORWARD_POLICY="ACCEPT"。
## 网络模式
- bridge(默认):需要端口映射和 DNS
- host:直接用宿主机网络,性能好但隔离差
- none:无网络
生产环境推荐 bridge + 自定义网络。服务端6月2日 23:43
Docker 容器内存泄漏怎么排查?OOM 和内存增长定位实战容器内存持续增长最终被 OOM Kill 是 Docker 最常见的问题之一。排查步骤:确认是不是真的泄漏 → 定位哪个容器 → 找到代码里的泄漏点。
## 确认问题
内存持续增长不回落大概率是泄漏。短暂增长后稳定不一定是泄漏——可能是 JVM/Node.js 堆还没触发 GC。
## 检查 OOM 事件
## 容器内定位
## 语言级分析
Node.js:用 v8.writeHeapSnapshot() 生成堆快照
Java:用 jmap -dump 生成堆 dump
Python:用 tracemalloc 或 objgraph
用 Chrome DevTools 或 MAT 分析堆快照,找 Retained Size 最大的对象。
## 常见原因
- 全局列表/缓存只加不删(最常见)
- 事件监听器没有 removeEventListener
- 数据库连接没有释放
## 临时缓解
只治标不治本,排查期间防止 OOM Kill。