Docker容器化部署实战:Dockerfile编写与docker-compose多服务编排


一、Docker 究竟解决了什么问题

传统部署模式下,环境不一致是头号痛点:开发机跑得好好的,一到服务器就"依赖缺失、端口冲突、版本对不上"。Docker 用镜像(Image)+ 容器(Container)把应用及其所有依赖打包成一个可移植的单元,在任何装了 Docker 的机器上都以同样方式运行。镜像分层、容器共享内核,比虚拟机更轻量,秒级启动。对 VPS 用户来说,最大的价值是"一键部署、快速隔离、回滚方便",你不再需要手工折腾某服务的依赖和配置文件。

二、核心概念:镜像、容器、仓库与 Dockerfile

四个词理清楚,Docker 就懂了大半:

  • 镜像 Image:只读的模板,类似"安装包",如 nginx:latest、mysql:8.0。
  • 容器 Container:镜像的运行时实例,可启动、停止、删除,彼此隔离。
  • 仓库 Repository:存放和分发镜像的地方,默认 Docker Hub,也可自建私有仓库。
  • Dockerfile:用指令描述的镜像构建脚本,决定镜像内容。
# 拉取nginx镜像并运行一个命名为 web 的容器,映射端口 80->8080,后台运行
docker pull nginx:latest
docker run -d --name web -p 8080:80 nginx:latest

# 查看运行中的容器、停止、删除、进入容器执行命令
docker ps
docker stop web
docker rm web
docker exec -it web bash

三、Dockerfile 编写:从 0 构建一个 Web 应用镜像

以下是一个 Python Flask 应用的 Dockerfile,演示常见指令的用法:

# 基于官方 python 3.11 基础镜像
FROM python:3.11-slim

# 设置工作目录
WORKDIR /app

# 先复制依赖清单,利用镜像层缓存加快二次构建
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 再复制源码(比依赖改动更频繁)
COPY . .

# 暴露端口
EXPOSE 5000

# 指定容器启动时执行的命令
CMD ["gunicorn", "-b", "0.0.0.0:5000", "app:app"]

构建并运行:

docker build -t my-flask-app .
docker run -d --name myapp -p 5000:5000 my-flask-app

写 Dockerfile 的三大要点:把 COPY 依赖清单放在源码 COPY 之前以利用层缓存;用 .dockerignore 排除 node_modules、.git、__pycache__ 等垃圾;生产镜像尽量用精简基础镜像(slim/alpine)缩小体积、减少攻击面。不要用 sudo docker 之外随意把整个目录 COPY 进去。

四、docker-compose:多服务一键编排

单容器只解决了单个进程,真实 Web 应用往往有 Web + 数据库 + 缓存多个服务。docker-compose 用一份 YAML 把多容器组织起来,一条 up -d 全部拉起。以 Nginx + PHP + MySQL 的 LNMP 栈为例:

# docker-compose.yml
version: "3.8"
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./www:/usr/share/nginx/html:ro   # 挂载站点目录
    depends_on:
      - php

  php:
    image: php:8.2-fpm
    volumes:
      - ./www:/var/www/html

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: "strong-pass"
      MYSQL_DATABASE: myapp
    volumes:
      - dbdata:/var/lib/mysql           # 数据卷持久化
volumes:
  dbdata:                              # 声明数据卷,容器删除数据仍在

启动:

docker-compose up -d        # 后台启动全部服务
docker-compose ps           # 查看状态
docker-compose logs -f      # 聚合日志
docker-compose down         # 停止并移除(-v 同时删数据卷,慎用)

这里 volumes 是关键——容器本身是无状态的,数据必须通过"数据卷/挂载目录"持久化,否则容器一删就数据全丢。生产环境务必把数据库目录挂到宿主机并纳入备份。

五、常用运维命令与镜像管理

docker images                # 查看本地镜像
docker rmi nginx:latest      # 删除镜像
docker logs -f myapp         # 查看容器日志
docker stats                 # 实时查看容器资源占用
docker inspect myapp         # 查看容器详细信息
docker system prune          # 清理悬空镜像/停用容器/孤儿数据卷
docker exec -it myapp bash   # 进入运行中的容器

磁盘紧张时,docker system df 可以看清镜像、容器、数据卷各占多少空间,结合 prune 回收。

六、常见坑与安全建议

新手最容易踩的坑和高频安全点:

  • 端口映射冲突:-p 80:80 报端口被占用,先 docker ps 查是否已有容器占用,或改用其他宿主机端口。
  • 容器重启后配置丢失:关键数据必须走 volumes 持久化,容器本身可随时重建。
  • 不要以 root 运行容器内进程:Dockerfile 用 USER 指定非 root 用户,降低提权风险。
  • 规避 docker run -p 0.0.0.0 裸奔:数据库等服务只监听内网或仅绑定 127.0.0.1。
  • 镜像来源要可信:尽量用官方镜像或官方变体,慎用来路不明的第三方镜像。
# 只允许本机访问 mysql 端口(默认 -p 会绑到所有网卡)
docker run -d -p 127.0.0.1:3306:3306 mysql:8.0

七、container 日志与排查流程

服务异常先看状态再逐层排查:

docker ps -a                    # 查看所有容器含已退出的
docker logs --tail 100 myapp    # 最近 100 行日志
docker inspect myapp | grep -i exit  # 查退出码
docker restart myapp

容器一直重启(Restarting 状态)通常是进程崩溃,配合 docker logs 找到报错;连接不上数据库多半是 compose 里服务名/网络地址写错,容器内用服务名而非 localhost 互相访问(compose 自动建隔离网络和 DNS)。

写在最后

Docker 把繁琐的"装环境、配依赖、防冲突"压缩成"拉镜像、写 Dockerfile、一键 up"。对个人 VPS 而言,它带来的是可复现的部署和干净的回滚体验。上手路径建议:先用 docker run 跑通单个服务 → 手写 Dockerfile 打好自己应用 → 再用 docker-compose 编排多服务。记住容器无状态、数据走卷、端口别裸奔这三条铁律,你就能安全地从零把 LNMP 或任意多服务架在 Docker 之上。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

MySQL数据库优化与运维实战:慢查询、缓冲池调优与备份恢复

fail2ban防暴力破解实战:SSH与Web后台自动化封禁配置指南

评 论
更换验证码