浏览 AI使用教程
Docker 项目部署教程:先说明端口、卷、环境变量和健康检查
容器显示 Up,只说明进程还在。端口、卷、环境变量、依赖和健康检查要一起验证,才能确认服务可访问、数据也不会随重建消失。
按实际操作顺序推进
每完成一段就检查当前输出,确认结果符合预期后再继续。
Docker 容器已经 Up,浏览器为什么还是打不开
教学服务在容器内监听3000,宿主仅通过127.0.0.1:8080访问;写入/app/data/note.txt后重建容器,再确认命名卷中的文件仍可读。
本例专门解释端口、持久卷与就绪检查。真实数据库依赖、账号和外部代理需要另行接入验证,不能从此例推定生产可用。
- 端口与监听地址一致
- 持久卷经重建验证
- 健康、日志和停止行为正常
动手前,把材料和不能改的部分定下来
| 需要确认 | 本例内容 |
|---|---|
| 现有材料 | 已安装Docker Engine/Desktop及Compose插件;下方给出server.mjs、Dockerfile、compose.yaml三个完整文件;本练习没有数据库和真实密钥。 |
| 不能越过的边界 | 端口只绑定宿主回环地址;不使用生产数据。停止服务用docker compose down,练习数据由命名卷保留;不要附加-v删除卷。 |
| 要交付的结果 | 容器运行契约 |
Docker 项目部署教程,从“镜像与命令”开始做
镜像与命令
写基础镜像、目标架构、构建上下文、启动命令和非 root 用户要求。
端口与网络
区分容器监听端口、宿主映射、服务名和外部入口,说明是否经过代理。
卷与数据
标持久化目录、只读挂载、初始化和备份;明确删除容器是否应保留数据。
变量与密钥
列名称、来源、必填、默认和重启影响,不把生产值放进 Compose 或镜像层。
健康与依赖
健康检查使用真实就绪条件,依赖服务有超时和重试;启动顺序不替代可用性判断。
三份文件使用同一3000/8080约定
server.mjs
本机练习服务
import http from 'node:http';
import { access, readFile, writeFile } from 'node:fs/promises';
import { constants } from 'node:fs';
const file = '/app/data/note.txt';
const server = http.createServer(async (req, res) => {
try {
if (req.url === '/healthz') { await access('/app/data', constants.W_OK); res.end('ready'); return; }
if (req.url === '/note' && req.method === 'POST') {
let body = ''; for await (const part of req) body += part;
await writeFile(file, body); res.end('saved'); return;
}
if (req.url === '/note' && req.method === 'GET') { res.end(await readFile(file)); return; }
res.writeHead(404); res.end('not found');
} catch (error) { res.writeHead(error.code === 'ENOENT' ? 404 : 503); res.end(error.code || 'unavailable'); }
});
server.listen(3000, '0.0.0.0');
process.on('SIGTERM', () => server.close(() => process.exit(0)));
Dockerfile
镜像目录与用户
FROM node:22-alpine WORKDIR /app RUN mkdir -p /app/data && chown node:node /app/data COPY --chown=node:node server.mjs /app/server.mjs USER node EXPOSE 3000 CMD ["node", "server.mjs"]
compose.yaml
宿主8080映射容器3000
services:
app:
build: .
ports:
- "127.0.0.1:8080:3000"
volumes:
- lesson-data:/app/data
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 10s
timeout: 3s
retries: 3
volumes:
lesson-data:
healthz检查本例数据目录可写,不等于每个业务端点通过;镜像标签是本例选用版本,生产需自行固定经过验证的镜像摘要。
先检查解析,再启动和重建
PowerShell
在三份文件所在目录执行
docker compose config # 确認只有本例端口和命名卷后启动 docker compose up -d --build docker compose ps Invoke-WebRequest 'http://127.0.0.1:8080/healthz' Invoke-WebRequest 'http://127.0.0.1:8080/note' -Method Post -Body 'lesson-persist-1' (Invoke-WebRequest 'http://127.0.0.1:8080/note').Content docker compose up -d --force-recreate # 等到health为healthy后再次读取,预期仍为lesson-persist-1 (Invoke-WebRequest 'http://127.0.0.1:8080/note').Content docker compose logs --tail 50 app # 停止练习并保留卷: docker compose down
端口占用时改宿主左侧8080并同步请求地址,容器右侧3000和server监听保持一致;503先看数据目录权限,重建后404则检查挂载是否仍为同一命名卷。
完成后的容器运行契约
预期GET /healthz返回200;POST /note写入后GET /note能读回;docker compose up -d --force-recreate后内容保持一致。需以当前机器的请求、inspect和日志验证;本页不声称已在你的Docker环境实测。
为什么“健康检查通过,功能接口仍失败”还不能交付
健康检查通过,功能接口仍失败
- 原因
- 健康只返回固定 200,没有检查关键依赖
- 怎么改
- 区分存活与就绪检查,关键流程另做外部冒烟测试
