标签

Docker

Docker 是一个开源的容器化平台,允许开发者打包应用及其全部依赖到一个可移植的容器中,然后这个容器可以在任何机器上运行,确保应用在不同环境之间运行的一致性。Docker 使用了 Linux 容器(LXC)的技术,但进行了扩展,使其更为易用、功能更全面。

Docker
查看更多相关内容
服务端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。
服务端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 网络模式有哪些?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月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 旧版本镜像。如果数据库迁移了且不兼容旧版本,回滚也救不了——所以数据库变更要分步做。