Docker容器基础 —— 把应用装进"集装箱"
一、为什么需要容器?——从"搬家噩梦"说起
问题场景
小明在服务器配好了一套Web开发环境(Python 3.11 + Flask + MySQL 8.0),兴冲冲地把代码拷到家里的电脑上——结果折腾了整整一个下午,要么是Python版本不对,要么是MySQL配置冲突,程序就是跑不起来。
互动提问:你有没有遇到过"在我电脑上明明能跑"的尴尬?想想看,问题的根源是什么?
根源:应用程序和它所在的运行环境(操作系统、依赖库、配置文件)是"绑定"在一起的。换一台机器,环境变了,程序就可能水土不服。
解决方案:容器化
生活类比:想象国际贸易——如果把散装货物(汽车零件、香蕉、电子产品)直接堆在船舱里,到港后卸货慢、容易损坏、还互相影响。于是人类发明了集装箱:不管里面装什么,外面的尺寸是统一的,可以吊到卡车、火车、轮船上无缝转运。
Docker容器就是软件世界的"集装箱":
| 对比维度 | 传统虚拟机(独立房屋) | 容器(集装箱) |
|---|---|---|
| 隔离方式 | 硬件级虚拟化,完整OS | 进程级隔离,共享宿主机内核 |
| 启动速度 | 分钟级(要启动整个操作系统) | 秒级(直接启动进程) |
| 资源占用 | GB级别(每个VM几个G内存) | MB级别(只占应用本身大小) |
| 镜像大小 | 几十GB | 几十MB到几百MB |
| 性能损耗 | 较大(Hypervisor层开销) | 几乎为零 |
| 适用场景 | 需要不同操作系统 | 需要快速部署、弹性伸缩 |
一句话总结:虚拟机是"盖一栋独立房子"(自带水电煤),容器是"在公寓里隔出一间标准房间"(共用水电煤,但各自独立生活)。
二、Docker架构:三大角色
Docker采用客户端-服务端(C/S)架构,主要由三个组件协作:
┌──────────────┐ ┌─────────────────────┐ ┌──────────────┐
│ Docker │ REST │ Docker Daemon │ pull │ Registry │
│ Client │ ──────> │ (dockerd) │ ─────> │ (镜像仓库) │
│ (docker命令) │ <────── │ 负责构建/运行容器 │ <───── │ Docker Hub │
└──────────────┘ └─────────────────────┘ └──────────────┘
| 组件 | 类比 | 职责 |
|---|---|---|
| Docker Client | 餐厅里的顾客 | 发送指令(docker run、docker build等) |
| Docker Daemon | 餐厅里的厨师 | 接收指令,实际执行镜像拉取、容器创建等工作 |
| Docker Registry | 食材供应商 | 存储和分发镜像(Docker Hub是官方公共仓库) |
互动提问:如果你在离线环境的学校机房使用Docker,没有网络访问Docker Hub怎么办?(提示:想想"食材供应商"能不能换成自己的小仓库?)
答案是可以搭建私有Registry或使用Harbor,后面会详细介绍。
三、Docker安装:各发行版全覆盖
3.1 RHEL系(CentOS 7/8、Fedora、麒麟Kylin)
CentOS 7/8
# 第一步:卸载旧版本(问题:旧版docker可能与新版冲突)
sudo yum remove -y docker docker-common docker-selinux docker-engine
# 第二步:安装依赖工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 第三步:添加Docker官方仓库(用阿里云镜像加速)
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 第四步:安装Docker CE(Community Edition 社区版)
sudo yum install -y docker-ce docker-ce-cli containerd.io
# 第五步:启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
# 第六步:验证安装
docker --version
docker run hello-world
Fedora
# Fedora使用dnf包管理器,命令略有不同
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/fedora/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io
sudo systemctl start docker && sudo systemctl enable docker
银河麒麟(Kylin V10)
# 麒麟V10基于CentOS/RHEL,安装方式与CentOS基本相同
# 但需注意架构差异:鲲鹏/飞腾CPU为aarch64,需要下载对应架构的Docker
uname -m # 确认架构:x86_64 或 aarch64
# 如果是aarch64架构,使用对应的镜像源
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
3.2 Debian系(Ubuntu、Deepin)
Ubuntu 20.04/22.04/24.04
# 第一步:卸载旧版本
sudo apt-get remove -y docker docker-engine docker.io containerd runc
# 第二步:更新包索引并安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 第三步:添加Docker官方GPG密钥(用阿里云镜像)
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 第四步:添加仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 第五步:安装Docker CE
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# 第六步:启动并验证
sudo systemctl start docker
sudo systemctl enable docker
docker --version
3.3 配置国内镜像加速(所有发行版通用)
# 问题:从Docker Hub拉取镜像太慢怎么办?
# 解决:配置镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://registry.docker-cn.com"
]
}
EOF
# 重启Docker使配置生效
sudo systemctl daemon-reload
sudo systemctl restart docker
3.4 让普通用户也能使用docker(免sudo)
# 问题:每次执行docker命令都要加sudo,太麻烦
# 解决:将用户加入docker组
sudo usermod -aG docker $USER
# 重新登录后生效
互动提问:
yum和apt-get分别是哪个系列的包管理器?你还知道哪些Linux包管理器?(提示:Arch Linux用pacman,openSUSE用zypper)
四、镜像操作:管理你的"集装箱蓝图"
4.1 核心概念
镜像(Image) 是一个只读的模板,包含了运行应用所需的一切:代码、运行时、库、环境变量和配置文件。可以类比为"集装箱的设计图纸+内部装修方案"。
镜像是分层(Layer)存储的,每一层是一条Dockerfile指令的执行结果。层与层之间叠加,相同的层可以复用,节省存储空间。
┌─────────────────────┐
│ Layer 4: CMD/应用代码 │ ← 最上层,可写(容器运行时)
├─────────────────────┤
│ Layer 3: 安装nginx │ ← 只读
├─────────────────────┤
│ Layer 2: apt-get更新 │ ← 只读
├─────────────────────┤
│ Layer 1: Ubuntu基础 │ ← 只读
└─────────────────────┘
4.2 常用镜像命令
# ===== 搜索镜像 =====
docker search nginx # 在Docker Hub搜索
docker search nginx --filter "stars=100" # 筛选高星镜像
# ===== 拉取镜像 =====
docker pull nginx # 拉取最新版(latest标签)
docker pull nginx:1.25 # 拉取指定版本
docker pull registry.cn-hangzhou.aliyuncs.com/library/nginx:latest # 从指定仓库拉取
# ===== 查看本地镜像 =====
docker images # 列出所有镜像
docker images -q # 只显示镜像ID
docker image ls --filter "dangling=true" # 查看悬空镜像(没有标签的)
# ===== 镜像标签 =====
docker tag nginx:latest myregistry.com/nginx:v1.0 # 给镜像打新标签
# 类比:就像给同一个集装箱贴上不同的编号标签,箱子本身没变
# ===== 删除镜像 =====
docker rmi nginx:1.25 # 按标签删除
docker rmi <镜像ID> # 按ID删除
docker image prune -f # 删除所有悬空镜像
docker image prune -a -f # 删除所有未被容器使用的镜像
# ===== 导出与导入(离线传输)=====
docker save -o nginx-latest.tar nginx:latest # 导出为tar文件
docker load -i nginx-latest.tar # 从tar文件导入
# 场景:学校机房没有外网,可以先在家下载好镜像,用U盘拷贝到学校机房导入
# ===== 查看镜像详情 =====
docker inspect nginx:latest # 查看镜像的完整元信息(JSON格式)
docker history nginx:latest # 查看镜像的构建历史(每一层做了什么)
# ===== 构建镜像 =====
docker build -t myapp:v1.0 . # 从当前目录的Dockerfile构建
docker build -t myapp:v1.0 -f docker/Dockerfile.prod . # 指定Dockerfile路径
互动提问:
docker save/load和docker export/import有什么区别?前者针对镜像,后者针对容器——想想"蓝图"和"实体房间"的区别。
五、容器操作:让你的"集装箱"跑起来
5.1 容器的生命周期
创建(create) → 启动(start) → 运行中(running) → 暂停(pause) → 停止(stop) → 删除(rm)
↑ ↓
└──────── 恢复(unpause) ────────┘
5.2 核心命令详解
运行容器 docker run
# 基本语法:docker run [选项] 镜像 [命令]
# 1. 前台运行(能看到输出,Ctrl+C停止)
docker run nginx
# 2. 后台运行(-d: detached,守护态)
docker run -d nginx
# 3. 指定容器名称
docker run -d --name my-web nginx
# 类比:给集装箱编个号,方便以后查找和管理
# 4. 端口映射(-p 宿主机端口:容器端口)
docker run -d -p 8080:80 nginx
# 解释:访问宿主机的8080端口,转发到容器内的80端口
# 类比:你住在学校宿舍301(容器端口80),但外面的人要通过学校大门收发室(宿主机端口8080)找你
# 5. 数据卷挂载(-v 宿主机路径:容器路径)
docker run -d -p 8080:80 -v /home/user/html:/usr/share/nginx/html nginx
# 解释:将宿主机的网页目录挂载到容器内,修改文件容器内立刻生效
# 6. 环境变量(-e)
docker run -d -e MYSQL_ROOT_PASSWORD=123456 --name my-mysql mysql:8.0
# 7. 交互式运行(-it:常用于调试)
docker run -it ubuntu:22.04 /bin/bash
# 进入容器的bash终端,像操作虚拟机一样操作容器
# 8. 综合示例:运行一个WordPress博客
docker run -d \
--name wordpress \
-p 8080:80 \
-e WORDPRESS_DB_HOST=mysql-server \
-e WORDPRESS_DB_PASSWORD=123456 \
-v wp-data:/var/www/html \
wordpress:latest
容器管理命令
# ===== 查看容器 =====
docker ps # 查看正在运行的容器
docker ps -a # 查看所有容器(包括已停止的)
docker ps -q # 只显示容器ID
# ===== 进入运行中的容器 =====
docker exec -it my-web /bin/bash # 在容器内打开一个终端(推荐)
docker exec my-web cat /etc/os-release # 在容器内执行单条命令
# 注意:docker attach也可以进入容器,但退出时容器会停止,所以推荐用exec
# ===== 查看容器日志 =====
docker logs my-web # 查看全部日志
docker logs -f my-web # 实时跟踪日志(类似tail -f)
docker logs --tail 50 my-web # 只看最后50行
docker logs --since 1h my-web # 只看最近1小时的日志
# ===== 容器状态 =====
docker inspect my-web # 查看容器详细信息(IP地址、挂载点等)
docker stats # 实时显示容器资源使用情况(CPU/内存/网络)
docker top my-web # 查看容器内的进程列表
# ===== 启停操作 =====
docker stop my-web # 优雅停止(发送SIGTERM,等10秒后SIGKILL)
docker kill my-web # 强制停止(直接SIGKILL)
docker start my-web # 启动已停止的容器
docker restart my-web # 重启容器
docker rm my-web # 删除已停止的容器
docker rm -f my-web # 强制删除(包括运行中的容器)
# ===== 批量操作 =====
docker stop $(docker ps -q) # 停止所有运行中的容器
docker rm $(docker ps -aq) # 删除所有已停止的容器
docker rm $(docker ps -aq -f status=exited) # 只删除已退出的容器
互动提问:
docker stop和docker kill有什么区别?就像"礼貌地请人离开"和"直接把人推出去"——前者给程序时间做清理工作(保存数据、关闭连接),后者直接终止。
5.3 容器信息速查表
| 命令 | 作用 | 输出示例 |
|---|---|---|
docker ps | 运行中的容器 | 容器ID、镜像名、命令、创建时间、状态、端口 |
docker inspect | 详细元数据 | JSON格式,包含网络、挂载、环境变量 |
docker logs | 标准输出日志 | 应用打印的所有日志 |
docker stats | 资源使用率 | CPU%、MEM%、NET I/O |
docker diff | 文件系统变更 | 容器内哪些文件被修改/新增 |
六、Dockerfile:编写你自己的"集装箱蓝图"
6.1 什么是Dockerfile?
Dockerfile是一个文本文件,里面写了一系列指令,告诉Docker"如何一步步构建镜像"。类比:如果镜像是一道菜,Dockerfile就是菜谱。
6.2 核心指令详解
# ============================================
# Dockerfile 指令速查
# ============================================
# FROM —— 基础镜像("这道菜用什么锅底")
FROM ubuntu:22.04
# 多阶段构建时会用到多个FROM
# RUN —— 构建时执行命令("往锅里加料")
RUN apt-get update && apt-get install -y nginx curl
# 建议:多条命令用 && 连接,减少镜像层数
# 反模式(会生成多层,增大镜像):
# RUN apt-get update
# RUN apt-get install -y nginx
# RUN apt-get install -y curl
# COPY —— 复制宿主机文件到镜像("把食材放进锅")
COPY index.html /usr/share/nginx/html/
COPY config/ /etc/nginx/conf.d/
# ADD —— 类似COPY,但支持URL和自动解压tar
ADD app.tar.gz /opt/ # 自动解压到/opt/目录
ADD https://example.com/file.zip /tmp/ # 从URL下载
# 最佳实践:优先用COPY,只在需要自动解压时才用ADD
# WORKDIR —— 设置工作目录("厨师的操作台")
WORKDIR /app
# 后续RUN/CMD/COPY等指令都在这个目录下执行
# ENV —— 设置环境变量("贴个标签提醒厨师")
ENV APP_VERSION=2.0
ENV PATH="/opt/bin:${PATH}"
# EXPOSE —— 声明容器监听的端口("告诉外面这个房间有窗户")
EXPOSE 80
EXPOSE 443
# 注意:EXPOSE只是声明,不会真正映射端口,运行时还需要 -p
# CMD —— 容器启动时执行的默认命令("上菜后的默认吃法")
CMD ["nginx", "-g", "daemon off;"]
# 格式:exec格式(推荐)或shell格式
# CMD会被docker run后面的命令覆盖
# 例如:docker run myimage echo "hello" 会覆盖CMD
# ENTRYPOINT —— 容器启动时执行的入口命令("这道菜必须用某种方式吃")
ENTRYPOINT ["python", "app.py"]
# 不会被docker run后面的命令覆盖,而是拼接在后面
# 例如:docker run myimage --help → python app.py --help
# CMD和ENTRYPOINT配合使用:
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run myimage → python app.py
# docker run myimage script.py → python script.py
6.3 CMD vs ENTRYPOINT 对比
| 特性 | CMD | ENTRYPOINT |
|---|---|---|
| 是否可被覆盖 | docker run后的命令直接覆盖 | 不会被覆盖,而是拼接参数 |
| 典型用途 | 提供默认命令,允许用户替换 | 设置固定的可执行程序 |
| 同时使用 | 作为ENTRYPOINT的默认参数 | 作为主命令 |
6.4 多阶段构建(Multi-stage Build)
问题:编译Go/Java项目需要完整的编译工具链,但运行只需要编译好的二进制文件,导致镜像很大。
解决:多阶段构建——先用一个"施工阶段"编译,再把成品搬到"入住阶段"。
# ========== 阶段1:编译阶段(施工)==========
FROM golang:1.21 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server ./cmd/server
# ========== 阶段2:运行阶段(入住)==========
FROM alpine:3.18
WORKDIR /app
# 只从builder阶段拷贝编译好的二进制文件
COPY --from=builder /app/server .
COPY --from=builder /src/config/ ./config/
EXPOSE 8080
CMD ["./server"]
# 效果:最终镜像只有十几MB(alpine+二进制),而不是几百MB(包含整个Go工具链)
互动提问:如果把构建镜像比作做蛋糕,多阶段构建就像——先在大厨房里烤好蛋糕胚(阶段1),然后只把蛋糕胚搬到展示柜里加装饰(阶段2),而不是把整个厨房都搬到展示柜里。
6.5 Dockerfile最佳实践
- 选择精简基础镜像:优先用
alpine(约5MB)而非完整的ubuntu - 合并RUN指令:用
&&连接,减少镜像层数 - 清理缓存:
apt-get install后加&& rm -rf /var/lib/apt/lists/* - 利用构建缓存:把不常变化的指令放前面(如安装系统包),常变的放后面(如拷贝代码)
- 使用
.dockerignore:排除不需要拷贝的文件(.git/、node_modules/、*.log) - 不要存储敏感信息:密码、密钥不要写在Dockerfile中
七、Docker仓库:镜像的"应用商店"
7.1 Docker Hub(官方公共仓库)
# 注册并登录
docker login
# 输入用户名和密码
# 推送镜像到自己的仓库
docker tag myapp:v1.0 username/myapp:v1.0
docker push username/myapp:v1.0
7.2 搭建私有Registry
# 最简单的私有仓库——用官方registry镜像
docker run -d \
--name registry \
-p 5000:5000 \
-v /opt/registry:/var/lib/registry \
registry:2
# 推送镜像到私有仓库
docker tag nginx:latest localhost:5000/nginx:latest
docker push localhost:5000/nginx:latest
# 从私有仓库拉取
docker pull localhost:5000/nginx:latest
# 查看仓库中的镜像列表
curl http://localhost:5000/v2/_catalog
7.3 Harbor(企业级私有仓库)
Harbor是VMware开源的企业级镜像仓库,相比简单的Registry,提供了:
- 图形化管理界面
- 基于角色的访问控制(RBAC)
- 镜像漏洞扫描
- 镜像签名与验证
- 跨数据中心镜像复制
# Harbor安装步骤概要
# 1. 下载离线安装包
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgz
# 2. 解压并配置
tar xzf harbor-offline-installer-v2.10.0.tgz
cd harbor
cp harbor.yml.tmpl harbor.yml
# 编辑harbor.yml,修改hostname、关闭https(测试环境)
# 3. 安装并启动
sudo ./install.sh
# 默认管理员密码在harbor.yml中配置
# 4. 访问 http://你的IP 即可看到Web界面
互动提问:学校机房要管理100台电脑的Docker镜像分发,你觉得用Docker Hub、简单Registry还是Harbor更合适?为什么?
八、Docker Compose:一键启动多个容器
8.1 为什么需要Compose?
问题:一个Web应用通常需要多个服务协作——Nginx(Web服务器)+ PHP(应用处理)+ MySQL(数据库)。手动一个个 docker run 太繁琐。
解决:Docker Compose用一个YAML文件描述所有服务及其依赖关系,一条命令全部启动。
类比:Compose就像一份"装修清单"——水电工、木工、油漆工按照清单上的顺序和协作关系进场施工。
8.2 安装Docker Compose
# 方式1:官方二进制安装(推荐)
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
# 方式2:通过pip安装
pip3 install docker-compose
# 新版Docker已内置 docker compose(无横杠),两者通用
8.3 docker-compose.yml 示例
version: "3.8"
services:
# Web服务器
nginx:
image: nginx:1.25
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./html:/usr/share/nginx/html
depends_on:
- php
networks:
- app-net
# PHP应用
php:
build: ./php
volumes:
- ./app:/var/www/html
depends_on:
- mysql
networks:
- app-net
# 数据库
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret123
MYSQL_DATABASE: myapp
volumes:
- mysql-data:/var/lib/mysql
networks:
- app-net
volumes:
mysql-data: # 声明数据卷
networks:
app-net: # 声明自定义网络
driver: bridge
8.4 Compose常用命令
docker-compose up -d # 后台启动所有服务
docker-compose down # 停止并删除所有服务及网络
docker-compose ps # 查看服务状态
docker-compose logs -f # 查看所有服务日志
docker-compose exec nginx bash # 进入某个服务的容器
docker-compose build # 重新构建有build指令的服务
docker-compose pull # 拉取所有服务的最新镜像
docker-compose restart # 重启所有服务
九、实战:用Dockerfile构建Nginx+PHP镜像
9.1 项目结构
lnmp-docker/
├── Dockerfile
├── nginx.conf
├── index.php
└── index.html
9.2 编写Dockerfile
# ============================================
# Nginx + PHP-FPM 一体化镜像
# 基础镜像选择Debian slim版(比完整版小,比alpine兼容性好)
# ============================================
FROM debian:bookworm-slim
# 设置环境变量,避免apt安装时的交互式提问
ENV DEBIAN_FRONTEND=noninteractive
# 安装Nginx和PHP-FPM(合并RUN减少层数)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
nginx \
php-fpm \
php-mysql \
php-curl \
php-json \
supervisor \
curl && \
rm -rf /var/lib/apt/lists/*
# 配置Nginx
COPY nginx.conf /etc/nginx/sites-available/default
# 拷贝网页文件
COPY index.php /var/www/html/
COPY index.html /var/www/html/
# 确保目录权限正确
RUN chown -R www-data:www-data /var/www/html && \
mkdir -p /run/php && \
mkdir -p /var/log/supervisord
# 使用supervisor同时管理Nginx和PHP-FPM
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# 声明端口
EXPOSE 80
# 使用supervisor启动两个服务
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
9.3 Nginx配置文件 nginx.conf
server {
listen 80 default_server;
server_name _;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
# PHP处理
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
9.4 Supervisor配置 supervisord.conf
[supervisord]
nodaemon=true
logfile=/var/log/supervisord/supervisord.log
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:php-fpm]
command=/usr/sbin/php-fpm8.2 -F
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
9.5 测试网页 index.php
<?php
echo "<h1>Docker LNMP 实战成功!</h1>";
echo "<p>PHP版本:" . phpversion() . "</p>";
echo "<p>服务器时间:" . date('Y-m-d H:i:s') . "</p>";
phpinfo();
?>
9.6 构建与运行
# 构建镜像
docker build -t lnmp-demo:v1.0 .
# 运行容器
docker run -d \
--name my-lnmp \
-p 8080:80 \
lnmp-demo:v1.0
# 验证
curl http://localhost:8080/index.php
# 或者浏览器访问 http://你的IP:8080/index.php
# 查看日志
docker logs my-lnmp
# 进入容器检查服务状态
docker exec -it my-lnmp bash
supervisorctl status
互动提问:为什么要用supervisor?因为一个容器通常只运行一个主进程(PID 1),而我们需要同时运行Nginx和PHP-FPM两个进程,supervisor就是帮我们管理多个进程的"监工"。
十、常见问题排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
docker: permission denied | 当前用户不在docker组 | sudo usermod -aG docker $USER 后重新登录 |
| 拉取镜像超时 | 网络问题/防火墙 | 配置镜像加速器或使用代理 |
| 容器启动后立即退出 | 主进程执行完毕退出 | 确保前台运行,如 nginx -g "daemon off;" |
| 端口被占用 | 宿主机端口冲突 | 换一个端口映射或停止占用端口的程序 |
| 构建缓存不更新 | 中间层缓存命中 | docker build --no-cache -t myapp . |
| 磁盘空间不足 | 废弃镜像/容器积累 | docker system prune -a 清理 |
运维常用命令精选
# === 镜像管理 ===
docker images # 列出所有镜像
docker rmi $(docker images -qf "dangling=true") # 删除悬空镜像
docker image prune -a # 删除所有未使用的镜像
docker save -o backup.tar myimage:v1 # 导出镜像
docker load -i backup.tar # 导入镜像
docker history myimage:v1 # 查看镜像构建历史
# === 容器管理 ===
docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}" # 格式化输出
docker exec -it <容器> /bin/bash # 进入容器
docker logs -f --tail 100 <容器> # 实时查看日志
docker stats --no-stream # 快照式查看资源使用
docker cp <容器>:/path/file ./local/ # 从容器拷贝文件到宿主机
docker cp ./local/file <容器>:/path/ # 从宿主机拷贝文件到容器
# === 系统清理 ===
docker system df # 查看Docker磁盘使用概况
docker system prune -a --volumes # 大扫除:清理所有未使用资源
docker volume prune # 清理未使用的数据卷
docker network prune # 清理未使用的网络
# === 构建相关 ===
docker build -t myapp:v1 --no-cache . # 不使用缓存构建
docker build --build-arg VERSION=2.0 -t myapp:v1 . # 传入构建参数
docker tag myapp:v1 registry.com/myapp:v1 # 打标签
趣味命令
# 1. 看看Docker里养了一条鲸鱼(ASCII艺术)
docker run --rm busybox sh -c 'echo " .\n / \\\n / \\\n / \\\n / . . \\\n / . \\\n /___________\\\n | DOCKER |\n |___________|"'
# 2. 在容器里看看"我是谁"
docker run --rm alpine cat /etc/os-release
# 3. 用容器跑一个动态时钟
docker run --rm -it busybox watch -n1 date
# 4. 看看Docker的彩蛋—— cowsay(牛说话)
docker run --rm debian:bookworm-slim sh -c "apt-get update && apt-get install -y cowsay && cowsay 'Hello Docker!'"
# 5. 一行命令看系统架构
docker run --rm alpine sh -c "uname -a && cat /proc/cpuinfo | grep 'model name' | head -1"
# 6. 容器里的"黑客帝国"数字雨
docker run --rm -it ubuntu:22.04 sh -c "apt-get update && apt-get install -y cmatrix && cmatrix"
# 7. 查看Docker引擎信息(像汽车仪表盘)
docker info
小结:Docker通过容器技术实现了"一次构建,到处运行"。镜像是蓝图,容器是实体,Dockerfile是编写蓝图的方式,Registry是存储蓝图的仓库,Compose是多容器编排的利器。掌握了这些基础,你就已经踏入了云原生世界的大门!