文章目录
- 一、总场景:公司内网业务上线(分角色/分模块)
- 二、任务设计
- 任务 1:镜像规范化(所有镜像通用)
- 任务 2:sshd 镜像“安全化”改造(不要把它当真实生产)
- 任务 3:systemd 镜像的“代价认知 + 正确运行姿势”
- 任务 4:nginx 生产化:反向代理 + 配置挂载 + 日志
- 任务 5:tomcat 生产化:部署应用 + 环境变量 + 日志
- 任务 6:MySQL 生产化:持久化 + 初始化脚本 + 权限最小化
- 三、综合联调关卡:三层网络 + 只暴露 Nginx
- 目标拓扑
- 四、排障训练(最像生产的部分)
- 五、交付物要求
下面我把这套sshd / systemd / nginx / tomcat / mysql镜像实验,整合成一套更像“生产环境”的实战场景(带任务、验收点、附加项、常见坑),按任务做完基本就能形成完整链路思维:镜像规范 → 运行参数 → 数据持久化 → 网络隔离 → 安全 → 健康检查 → 日志排障。
一、总场景:公司内网业务上线(分角色/分模块)
你们是运维小组,要在一台新服务器上用 Docker 快速部署一个“教学版业务系统”:
- Web 层:Nginx(反向代理 + 静态资源)
- App 层:Tomcat(跑一个 demo war)
- DB 层:MySQL(持久化 + 远程访问控制)
- 运维入口:SSH(仅用于排障演示,不建议真实生产这样用)
- 系统管理:systemd 容器(演示 systemctl 管理服务的方式与代价)
要求:可重复部署、可迁移、可排障、可回滚。
二、任务设计
任务 1:镜像规范化(所有镜像通用)
- 每个镜像必须:
- 有清晰 tag:
<service>:<version>-<base>(例nginx:1.12-centos7) - Dockerfile 里写注释、分层合理
- 有清晰 tag:
yum -y update改成可控(生产里不建议 build 时全量 update)- 清理缓存减小镜像:
yum clean all && rm -rf /var/cache/yum
验收点
docker images看 size 是否明显下降- Dockerfile 层数是否更少(例如合并 RUN)
附加项
- 给镜像加
LABEL(维护人、用途、版本、构建时间)
任务 2:sshd 镜像“安全化”改造(不要把它当真实生产)
- 禁止 root 密码登录(改用 key)
- 只允许某个普通用户(如
ops)登录 - SSH 端口改为非 22(容器内仍可 22,宿主映射不同)
- 使用更安全的 sshd_config 片段(最少做到:禁用密码、禁用 root、限制用户)
验收点
- root 不能登录
- 未授权用户不能登录
- 只能通过 key 登录
提示
PermitRootLogin noPasswordAuthentication noAllowUsers ops
步骤
- 创建Dokcerfile文件
mkidr /opt/mysshcd/mysshcp/etc/yum.repos.d/CentOS-Base.repo ./teeDockerfile<<-'EOF' FROM centos:7 LABEL maintainer="this is ssh image <jixuancheng@qq.com>" ENV ALLOWED_USER=ops # 添加yum源 RUN rm -rf /etc/yum.repos.d/* ADD CentOS-Base.repo /etc/yum.repos.d/ # 安装和配置 RUN yum clean all && \ yum makecache && \ yum -y install openssh-server openssh-clients sudo && \ yum clean all && \ # 创建用户 useradd -m -s /bin/bash ${ALLOWED_USER} && \ echo "${ALLOWED_USER}:ops@123" | chpasswd && \ echo "${ALLOWED_USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \ # 生成主机密钥 ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N '' && \ ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N '' && \ # SSH配置 sed -i \ -e 's/^#PermitRootLogin.*/PermitRootLogin no/' \ -e 's/^PasswordAuthentication.*/PasswordAuthentication no/' \ -e 's/^#PubkeyAuthentication.*/PubkeyAuthentication yes/' \ /etc/ssh/sshd_config && \ # 添加用户限制 echo "AllowUsers ${ALLOWED_USER}" >> /etc/ssh/sshd_config && \ # PAM配置 sed -ri '/^session\s+required\s+pam_loginuid.so/ s/^/#/' /etc/pam.d/sshd && \ sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config && \ sed -i 's/#UseDNS yes/UseDNS no/g' /etc/ssh/sshd_config && \ # 创建.ssh目录 mkdir -p /home/${ALLOWED_USER}/.ssh && \ chmod 700 /home/${ALLOWED_USER}/.ssh && \ chown -R ${ALLOWED_USER}:${ALLOWED_USER} /home/${ALLOWED_USER}/.ssh EXPOSE 22 CMD ["/usr/sbin/sshd", "-D"] EOF
UsePAM no:不使用PAM认证UseDNS no:取消DNS反向认证,加快访问速度。sed -ri '/^session\s+required\s+pam_loginuid.so/ s/^/#/' /etc/pam.d/sshd:#取消pam限制ssh-keygen -t rsa -A:生成密钥认证文件CMD ["/usr/sbin/sshd" , "-D"]:/usr/sbin/sshd -D 用于前台启动sshd服务
- 客户端连接
cd/opt/myssh# 1. 生成SSH密钥(如果没有的话)ssh-keygen -t rsa# 2. 构建镜像dockerbuild -t myssh-image.# 3. 创建authorized_keys文件。cat~/.ssh/id_rsa.pub>authorized_keys# 其他客户端需要连服务器上的myssh-server时,将客户端生成的公钥追加到authorized_keys即可。scp~/.ssh/id_ed25519.pub root@容器宿主机/opt/sshd/catid_ed25519.pub>>authorized_keys# 4. 运行容器dockerrun -d\-p2222:22\-v$(pwd)/authorized_keys:/home/ops/.ssh/authorized_keys\--name myssh-server\myssh-image# 5. 连接到容器ssh-p2222ops@容器宿主机- myssh-server宿主机
- 其他客户端登录
报错日志
- 重复创建容器会提示远程主机标识已更改,删掉
~/.ssh/known_hosts该条记录或直接删掉known_hosts文件即可。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@rm~/.ssh/known_hosts任务 3:systemd 镜像的“代价认知 + 正确运行姿势”
你已经写了 systemd 容器清理 wants 的做法,这是很典型的“能跑 systemctl”的实验。
- 必须解释清楚:为什么需要
--privileged+ cgroup 挂载(安全影响是什么) - 在 systemd 容器里通过
systemctl start/stop/status管理 sshd 或 mysqld - 写一份“什么时候不该用 systemd 容器”的说明
验收点
- systemctl 能正常查看服务状态
- 能重启服务后仍正常
附加项
- 对比:不用 systemd 的做法(容器前台启动 vs systemctl)优缺点
任务 4:nginx 生产化:反向代理 + 配置挂载 + 日志
- Nginx 镜像不再把配置写死在镜像里:用 volume 挂载
/usr/local/nginx/conf/nginx.conf/usr/local/nginx/conf/conf.d/
- Nginx 反向代理到 Tomcat(例如
/app→ tomcat:8080) - 日志持久化挂载:
/usr/local/nginx/logs/
验收点
- 修改宿主机配置后,reload 生效(容器不重建)
curl http://宿主IP:端口/app能访问到 tomcat- 日志在宿主机可见
附加项
- 做健康检查:用
curl检测 200
任务 5:tomcat 生产化:部署应用 + 环境变量 + 日志
- Tomcat 镜像做到“应用可插拔”:
- 通过 volume 挂载
webapps/或挂载 war 包
- 通过 volume 挂载
- 配置 JVM 参数通过环境变量传入(如
JAVA_OPTS) - 日志持久化(
logs/)
验收点
- 替换 war 包无需重建镜像
- 设置
JAVA_OPTS后ps可看到 - 日志落盘到宿主机
任务 6:MySQL 生产化:持久化 + 初始化脚本 + 权限最小化
- 数据必须持久化(volume)
- 初始化动作要可重复(首次初始化执行,已有数据不重复初始化)
- 权限控制:不要
grant all on *.* to root@'%'作为最终答案- 改为创建业务库、业务用户,只授权业务库
验收点
- 删除容器重建,数据不丢
- 业务用户只能访问指定库
- 远程能连,但 root 不允许随便远程(作为加分)
问题
- 为什么生产里更推荐官方 mysql 镜像(减少编译维护成本)
三、综合联调关卡:三层网络 + 只暴露 Nginx
目标拓扑
frontend_net:只放 nginx,对外暴露端口backend_net:tomcat + mysql 在内网,外部不可直接访问- nginx 同时加入两个网络做桥接(或只做反代到 backend)
任务
- 创建两个网络
- mysql 不映射宿主机端口(禁止外部直连)
- 只能通过 nginx 访问到业务页面
- tomcat 只能被 nginx 访问(外部不映射)
验收点
docker ps只有 nginx 有0.0.0.0:xxxx->80- 从宿主机直接连 mysql 失败
- 从 nginx 容器能连 mysql(或 tomcat 能连 mysql)
四、排障训练(最像生产的部分)
要求在限定时间内定位问题并修复:
- nginx 502
- tomcat 未启动 / upstream 写错 / 网络不通
验收:能解释原因 + 修复
- tomcat 未启动 / upstream 写错 / 网络不通
- tomcat 启动但访问 404
- war 未部署 / context 路径错
验收:能找到 webapps 内容并修复映射
- war 未部署 / context 路径错
- mysql 能启动但远程连不上
- 授权、bind-address、端口没暴露、网络不通
验收:能说明“该不该暴露端口”,并给出更安全的连接方式
- 授权、bind-address、端口没暴露、网络不通
- 数据丢失
- 忘了 volume
验收:能复现、能给出修复方案、能解释原理
- 忘了 volume
五、交付物要求
最后提交:
- 每个服务的 Dockerfile(或 compose 文件)
- 一份部署文档(含端口、网络、账号策略)
- 一份回滚方案(如何回到上一版镜像)
- 一份安全说明(哪些设置仅用于实验、生产要怎么改)