硬核|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 拉取镜像pullbash# 从Docker Hub拉取nginx最新版 docker pull nginx # 拉取指定版本 docker pull nginx:1.25 # 从私有仓库拉取需先登录 docker pull myregistry.com/myapp:1.0踩坑提醒不指定tag默认latest但在生产环境中永远不要用latest——你不知道它指向哪个具体版本下次拉取可能变了导致行为不一致。2.2 查看本地镜像bashdocker images # 或 docker image ls2.3 运行容器runbash# 最简单的运行前台运行CtrlC退出 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_PASSWORD123--restart重启策略--restartalways--network指定网络--network mynet2.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.jsdockerfile# 1. 指定基础镜像尽量用alpine瘦身版 FROM node:18-alpine AS builder # 2. 设置工作目录 WORKDIR /app # 3. 复制依赖文件利用缓存层 COPY package*.json ./ RUN npm ci --onlyproduction npm cache clean --force # 4. 复制源代码 COPY . . # 5. 多阶段构建最终镜像只复制产物 FROM node:18-alpine WORKDIR /app COPY --frombuilder /app/node_modules ./node_modules COPY --frombuilder /app/dist ./dist COPY --frombuilder /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最佳实践利用构建缓存把不常变的指令如COPY package.json放在前面源代码放在后面。多阶段构建最终镜像只包含运行必需文件体积可减少80%以上。指定具体基础镜像tag不要用node:latest用node:18.17.0-alpine。最小权限原则不要用root运行应用创建普通用户。合并RUN命令减少镜像层数如RUN apt update apt install -y curl rm -rf /var/lib/apt/lists/*。3.3 构建镜像buildbash# 基本构建 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 查看镜像层历史bashdocker history myapp:1.0四、镜像仓库推送与拉取4.1 登录仓库bash# Docker Hub docker login # 私有仓库如阿里云/自建Harbor docker login myregistry.com -u username -p password4.2 标记镜像tagbash# 语法docker tag 源镜像 目标仓库地址/命名空间/镜像名:tag docker tag myapp:1.0 myregistry.com/dev/myapp:1.04.3 推送镜像bashdocker push myregistry.com/dev/myapp:1.04.4 拉取私有镜像bashdocker pull myregistry.com/dev/myapp:1.04.5 仓库管理常用命令bash# 搜索镜像Docker Hub docker search nginx # 删除本地镜像 docker rmi myapp:1.0 # 清理未使用的镜像 docker image prune -a五、数据持久化Volume与Bind Mount5.1 两种挂载方式对比类型命令示例特点使用场景Volume卷-v myvolume:/dataDocker管理存于/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 prune5.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示例LNMPyamlversion: 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_HOSTmysql - MYSQL_DBmydb 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: bridge6.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 php3 # 执行一次性命令如数据库迁移 docker-compose run php php artisan migrate6.3 Compose生产配置要点指定镜像tag不要用latest写具体版本。资源限制避免容器耗尽主机资源。yamlservices: mysql: deploy: resources: limits: cpus: 2 memory: 2G reservations: memory: 1G健康检查yamlservices: php: healthcheck: test: [CMD, curl, -f, http://localhost/health] interval: 30s timeout: 10s retries: 3配置外部网络让多个compose项目互通bashdocker network create shared_networkyamlnetworks: default: external: name: shared_network七、进阶编排docker swarm vs kubernetes简要虽然Compose适合单机但在生产环境往往需要集群编排。这里简要对比特性Docker SwarmKubernetes复杂度低集成在Docker中高需独立部署服务发现内置DNS内置DNSService负载均衡内置多种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_php5八、生产部署实战一个完整的CI/CD视角8.1 流程概览开发者推送代码到GitCI服务器拉取代码执行测试构建Docker镜像推送私有仓库部署服务器拉取新镜像滚动更新容器8.2 镜像版本规范建议使用registry/project:git-commit-hash或:build-build-number示例myregistry.com/order-service:a1f9e3d8.3 部署脚本示例bashbash#!/bin/bash IMAGE_TAG$(git rev-parse --short HEAD) REGISTRYmyregistry.com PROJECTorder-api # 构建 docker build -t $REGISTRY/$PROJECT:$IMAGE_TAG . # 推送 docker push $REGISTRY/$PROJECT:$IMAGE_TAG # 在生产服务器上拉取并更新假设使用docker-compose ssh userprod-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 $PROJECT3 $PROJECT 8.4 镜像清理策略bash# 定期删除旧镜像保留最近5个 docker image prune -a --filter until168h -f九、常见问题与排障9.1 容器无法启动bash# 查看日志 docker logs container # 检查退出码 docker ps -a # 进入停止状态的容器需要先启动一个调试容器 docker run -it --entrypoint /bin/sh myapp:1.09.2 端口冲突bash# 查看宿主机端口占用 netstat -tulnp | grep :8080 # 或者改docker映射端口 docker run -p 8081:80 ...9.3 磁盘占满bash# 查看docker占用空间 docker system df # 清理未使用资源 docker system prune -a --volumes9.4 容器内时区不对yaml# compose中挂载时区文件 volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro或Dockerfile中设置环境变量dockerfileENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime echo $TZ /etc/timezone十、总结Docker命令脑图与学习路线textdocker ├── 镜像管理 │ ├── 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学习建议先敲熟run、exec、logs、ps这四个最常用命令。自己写一个简单的 Dockerfile比如一个 Python Flask 应用构建并运行。学会用 Compose 启动 LNMP/MEAN 完整环境。最后再接触 Swarm/K8s。掌握 Docker 并不难难的是形成“任何应用都应该容器化”的思维习惯。当你看到一个新项目脑子里自动浮现出 Dockerfile 和 compose 的结构时你就真正入门了。