硬核|Docker从入门到精通:镜像构建、仓库推送、Compose编排、生产部署全攻略

上周帮一个朋友排查线上问题,他一脸茫然地问我:"为什么我本地跑得好好的容器,到服务器上就起不来了?"我进服务器一看------镜像tag是latest,启动命令写的是npm start,连Dockerfile都没有打版本号。这是一个典型"会用但没真正理解Docker"的状态。

本文不讲虚的,从镜像拉取→容器运行→Dockerfile定制→镜像仓库推送→多容器编排Compose→生产环境部署,一条龙把Docker的所有基础但核心的操作讲透。全文每个命令都有场景说明和踩坑提示,让你从"会敲命令"变成"真懂容器"。

一、Docker核心概念(一句话建立认知模型)

  • 镜像(Image) :一个只读的模板,包含运行环境和代码,类比"类"。

  • 容器(Container) :镜像的运行实例,类比"对象"。

  • 仓库(Registry) :存放镜像的地方,Docker Hub是官方仓库。

  • Dockerfile:描述如何构建镜像的脚本。

  • docker-compose:定义和运行多容器的工具(YAML配置)。

理解这5个词,你已经掌握了50%的Docker思想。

二、镜像拉取与容器基础操作

2.1 拉取镜像(pull)

bash

复制代码
# 从Docker Hub拉取nginx最新版
docker pull nginx

# 拉取指定版本
docker pull nginx:1.25

# 从私有仓库拉取(需先登录)
docker pull myregistry.com/myapp:1.0

踩坑提醒 :不指定tag默认latest,但在生产环境中永远不要用latest------你不知道它指向哪个具体版本,下次拉取可能变了导致行为不一致。

2.2 查看本地镜像

bash

复制代码
docker images
# 或
docker image ls

2.3 运行容器(run)

bash

复制代码
# 最简单的运行(前台运行,Ctrl+C退出)
docker run nginx

# 后台运行 -d,映射端口 -p 主机端口:容器端口,命名 --name
docker run -d --name mynginx -p 8080:80 nginx

# 进入容器内部交互(常用调试)
docker exec -it mynginx /bin/bash

# 运行一次性命令(如查看环境变量)
docker exec mynginx env

常用run参数速查

参数 说明 示例
-d 后台运行 -d
-p 端口映射 -p 8080:80
-v 挂载卷(持久化) -v /host/data:/app/data
-e 设置环境变量 -e MYSQL_ROOT_PASSWORD=123
--restart 重启策略 --restart=always
--network 指定网络 --network mynet

2.4 管理容器

bash

复制代码
# 查看运行中容器
docker ps

# 查看所有容器(含已停止)
docker ps -a

# 停止/启动/重启
docker stop mynginx
docker start mynginx
docker restart mynginx

# 删除容器(需先停止)
docker rm mynginx

# 强制删除运行中容器
docker rm -f mynginx

# 查看容器日志
docker logs mynginx
docker logs -f mynginx   # 实时跟踪

三、Dockerfile:定制自己的镜像

3.1 一个生产级Dockerfile示例(Node.js)

dockerfile

复制代码
# 1. 指定基础镜像(尽量用alpine瘦身版)
FROM node:18-alpine AS builder

# 2. 设置工作目录
WORKDIR /app

# 3. 复制依赖文件(利用缓存层)
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 4. 复制源代码
COPY . .

# 5. 多阶段构建:最终镜像只复制产物
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./

# 6. 声明运行时用户(非root安全)
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
USER nodejs

# 7. 暴露端口
EXPOSE 3000

# 8. 启动命令(建议用JSON数组形式)
CMD ["node", "dist/index.js"]

3.2 Dockerfile最佳实践

  1. 利用构建缓存:把不常变的指令(如COPY package.json)放在前面,源代码放在后面。

  2. 多阶段构建:最终镜像只包含运行必需文件,体积可减少80%以上。

  3. 指定具体基础镜像tag :不要用node:latest,用node:18.17.0-alpine

  4. 最小权限原则:不要用root运行应用,创建普通用户。

  5. 合并RUN命令 :减少镜像层数,如RUN apt update && apt install -y curl && rm -rf /var/lib/apt/lists/*

3.3 构建镜像(build)

bash

复制代码
# 基本构建
docker build -t myapp:1.0 .

# 指定Dockerfile路径
docker build -t myapp:1.0 -f Dockerfile.prod .

# 构建时不使用缓存
docker build --no-cache -t myapp:1.0 .

3.4 查看镜像层历史

bash

复制代码
docker history myapp:1.0

四、镜像仓库:推送与拉取

4.1 登录仓库

bash

复制代码
# Docker Hub
docker login

# 私有仓库(如阿里云/自建Harbor)
docker login myregistry.com -u username -p password

4.2 标记镜像(tag)

bash

复制代码
# 语法:docker tag 源镜像 目标仓库地址/命名空间/镜像名:tag
docker tag myapp:1.0 myregistry.com/dev/myapp:1.0

4.3 推送镜像

bash

复制代码
docker push myregistry.com/dev/myapp:1.0

4.4 拉取私有镜像

bash

复制代码
docker pull myregistry.com/dev/myapp:1.0

4.5 仓库管理常用命令

bash

复制代码
# 搜索镜像(Docker Hub)
docker search nginx

# 删除本地镜像
docker rmi myapp:1.0

# 清理未使用的镜像
docker image prune -a

五、数据持久化:Volume与Bind Mount

5.1 两种挂载方式对比

类型 命令示例 特点 使用场景
Volume(卷) -v myvolume:/data Docker管理,存于/var/lib/docker/volumes/ 数据库、配置文件持久化
Bind Mount(绑定挂载) -v /host/path:/container/path 直接挂载宿主机目录 开发热加载、日志输出

5.2 Volume操作

bash

复制代码
# 创建卷
docker volume create mydata

# 查看卷列表
docker volume ls

# 挂载卷运行容器
docker run -d -v mydata:/var/lib/mysql --name mysql mysql

# 查看卷详情
docker volume inspect mydata

# 删除无用卷
docker volume prune

5.3 容器间共享卷

bash

复制代码
# 创建一个共享卷
docker volume create shared

# 容器A挂载
docker run -d --name app1 -v shared:/app/data busybox

# 容器B挂载同一个卷
docker run -d --name app2 -v shared:/app/data busybox

六、docker-compose:多容器编排

6.1 一个完整的docker-compose.yml示例(LNMP)

yaml

复制代码
version: '3.8'

services:
  nginx:
    image: nginx:1.25-alpine
    container_name: my-nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./html:/usr/share/nginx/html
    depends_on:
      - php
      - mysql
    networks:
      - appnet

  php:
    build:
      context: ./php
      dockerfile: Dockerfile
    container_name: my-php
    volumes:
      - ./html:/var/www/html
    environment:
      - MYSQL_HOST=mysql
      - MYSQL_DB=mydb
    depends_on:
      - mysql
    networks:
      - appnet

  mysql:
    image: mysql:8.0
    container_name: my-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: mydb
      MYSQL_USER: app
      MYSQL_PASSWORD: app123
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"
    networks:
      - appnet

volumes:
  mysql-data:

networks:
  appnet:
    driver: bridge

6.2 常用compose命令

bash

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

# 查看服务状态
docker-compose ps

# 查看日志
docker-compose logs -f

# 停止并删除容器、网络(默认不删volumes)
docker-compose down

# 停止并删除volumes
docker-compose down -v

# 重新构建镜像后启动
docker-compose up -d --build

# 扩容某个服务(比如php启动3个实例)
docker-compose up -d --scale php=3

# 执行一次性命令(如数据库迁移)
docker-compose run php php artisan migrate

6.3 Compose生产配置要点

  1. 指定镜像tag :不要用latest,写具体版本。

  2. 资源限制:避免容器耗尽主机资源。

yaml

复制代码
services:
  mysql:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          memory: 1G
  1. 健康检查

yaml

复制代码
services:
  php:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
  1. 配置外部网络(让多个compose项目互通):

bash

复制代码
docker network create shared_network

yaml

复制代码
networks:
  default:
    external:
      name: shared_network

七、进阶编排:docker swarm vs kubernetes(简要)

虽然Compose适合单机,但在生产环境往往需要集群编排。这里简要对比:

特性 Docker Swarm Kubernetes
复杂度 低,集成在Docker中 高,需独立部署
服务发现 内置DNS 内置DNS+Service
负载均衡 内置 多种Ingress
滚动更新 支持 高级策略
学习曲线 平缓 陡峭

Swarm快速上手

bash

复制代码
# 初始化集群
docker swarm init --advertise-addr 192.168.1.10

# 加入工作节点(在其他机器执行)
docker swarm join --token <token> 192.168.1.10:2377

# 部署stack(类似compose但支持集群)
docker stack deploy -c docker-compose.yml myapp

# 查看服务
docker service ls

# 扩容
docker service scale myapp_php=5

八、生产部署实战:一个完整的CI/CD视角

8.1 流程概览

  1. 开发者推送代码到Git

  2. CI服务器拉取代码,执行测试

  3. 构建Docker镜像,推送私有仓库

  4. 部署服务器拉取新镜像,滚动更新容器

8.2 镜像版本规范

建议使用:<registry>/<project>:<git-commit-hash>:build-<build-number>

示例:myregistry.com/order-service:a1f9e3d

8.3 部署脚本示例(bash)

bash

复制代码
#!/bin/bash
IMAGE_TAG=$(git rev-parse --short HEAD)
REGISTRY="myregistry.com"
PROJECT="order-api"

# 构建
docker build -t $REGISTRY/$PROJECT:$IMAGE_TAG .

# 推送
docker push $REGISTRY/$PROJECT:$IMAGE_TAG

# 在生产服务器上拉取并更新(假设使用docker-compose)
ssh user@prod-server "
  docker pull $REGISTRY/$PROJECT:$IMAGE_TAG
  cd /app/$PROJECT
  sed -i 's|image:.*|image: $REGISTRY/$PROJECT:$IMAGE_TAG|' docker-compose.yml
  docker-compose up -d --no-deps --scale $PROJECT=3 $PROJECT
"

8.4 镜像清理策略

bash

复制代码
# 定期删除旧镜像(保留最近5个)
docker image prune -a --filter "until=168h" -f

九、常见问题与排障

9.1 容器无法启动

bash

复制代码
# 查看日志
docker logs <container>

# 检查退出码
docker ps -a

# 进入停止状态的容器(需要先启动一个调试容器)
docker run -it --entrypoint /bin/sh myapp:1.0

9.2 端口冲突

bash

复制代码
# 查看宿主机端口占用
netstat -tulnp | grep :8080

# 或者改docker映射端口
docker run -p 8081:80 ...

9.3 磁盘占满

bash

复制代码
# 查看docker占用空间
docker system df

# 清理未使用资源
docker system prune -a --volumes

9.4 容器内时区不对

yaml

复制代码
# compose中挂载时区文件
volumes:
  - /etc/localtime:/etc/localtime:ro
  - /etc/timezone:/etc/timezone:ro

或Dockerfile中设置环境变量:

dockerfile

复制代码
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

十、总结:Docker命令脑图与学习路线

text

复制代码
docker
├── 镜像管理
│   ├── pull / push
│   ├── build (Dockerfile)
│   ├── tag / rmi
│   └── history / inspect
├── 容器生命周期
│   ├── run ( -d -p -v -e --name --restart )
│   ├── start / stop / restart
│   ├── rm ( -f )
│   └── exec / logs
├── 存储
│   ├── volume (create / ls / inspect / prune)
│   └── bind mount
├── 网络
│   ├── network create / ls / inspect
│   └── bridge / overlay / host
├── 编排
│   ├── docker-compose (up / down / logs / exec)
│   └── docker stack (swarm)
└── 系统
    ├── system df / prune
    └── info / version

学习建议

  1. 先敲熟 runexeclogsps 这四个最常用命令。

  2. 自己写一个简单的 Dockerfile(比如一个 Python Flask 应用),构建并运行。

  3. 学会用 Compose 启动 LNMP/MEAN 完整环境。

  4. 最后再接触 Swarm/K8s。

掌握 Docker 并不难,难的是形成"任何应用都应该容器化"的思维习惯。当你看到一个新项目,脑子里自动浮现出 Dockerfile 和 compose 的结构时,你就真正入门了。

相关推荐
乘云数字DATABUFF4 天前
5分钟部署开源APM Databuff:OpenTelemetry全链路追踪入门实战
运维·后端
Patrick_Wilson4 天前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
Suroy5 天前
DockerView-Go:用 Go 写一个终端 Docker 监控工具,顺便做了个 Web 仪表盘
docker
云恒要逆袭5 天前
运行你的第一个Docker容器
后端·docker·容器
荣--6 天前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森6 天前
动手实战学 Docker — 从零到集群编排完全指南
运维
宋均浩6 天前
# Docker 镜像瘦身实战:从 1.2G 到 80MB 的五个优化步骤
ci/cd·docker
Avan_菜菜6 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
程序员老赵6 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程
WangMingHua1116 天前
LM Studio Docker 部署——本地大模型一键启动
docker