从 0 到上线、长期运行、后续更新的**全流程**(适配 CentOS 服务器)

0. 前提:安装 Docker(若已安装可跳过)

bash 复制代码
# CentOS 7/8/Stream 通用
sudo yum -y install yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum -y install docker-ce docker-ce-cli containerd.io
sudo systemctl enable --now docker
docker version

如果服务器启用了防火墙或 SELinux,本文下面会给出对应处理方式。


1. 准备项目文件

在服务器新建目录并进入:

bash 复制代码
mkdir -p ~/fastapi-upload && cd ~/fastapi-upload

1.1 main.py

python 复制代码
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
import os
from datetime import datetime

app = FastAPI(title="FastAPI Upload Service")

UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)
app.mount("/files", StaticFiles(directory=UPLOAD_DIR), name="files")

@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
    ts = datetime.now().strftime("%Y%m%d%H%M%S")
    safe_name = file.filename.replace("/", "_").replace("\\", "_")
    filename = f"{ts}_{safe_name}"
    path = os.path.join(UPLOAD_DIR, filename)
    with open(path, "wb") as f:
        f.write(await file.read())
    # 返回相对路径,前端/调用方拼上域名即可访问下载
    return JSONResponse({"filename": filename, "url": f"/files/{filename}"})

1.2 requirements.txt

复制代码
fastapi>=0.110
uvicorn[standard]>=0.29

1.3 Dockerfile

dockerfile 复制代码
FROM python:3.11-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt && \
    pip install --no-cache-dir gunicorn

COPY main.py ./main.py

# 非 root 运行更安全
RUN useradd -m appuser && mkdir -p /app/uploads && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

# 健康检查(可选)
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD python - <<'PY'\nimport urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/docs').read()\nPY

# 生产推荐:Gunicorn 托管 Uvicorn Worker
CMD ["gunicorn", "-w", "2", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000", "main:app"]

2. 构建镜像

bash 复制代码
cd ~/fastapi-upload
docker build -t fastapi-upload:latest .

3. 准备持久化目录

bash 复制代码
sudo mkdir -p /data/fastapi-uploads
sudo chown -R 1000:1000 /data/fastapi-uploads   # 1000 是 appuser 的常见 uid,如需可改

SELinux 开启时 挂载建议使用 :Z 选项(下文已包含)。


4. 启动容器(长期运行)

bash 复制代码
docker run -d \
  --name fastapi-upload \
  -p 8000:8000 \
  -v /data/fastapi-uploads:/app/uploads:Z \
  --restart=always \
  fastapi-upload:latest
  • -d:后台运行
  • --restart=always:宕机/重启均自动拉起
  • -v:将上传目录持久化在宿主机(数据安全)

查看状态与日志:

bash 复制代码
docker ps -a
docker logs -f fastapi-upload

5. 防火墙与 SELinux

5.1 防火墙放行端口(例:8000)

bash 复制代码
sudo firewall-cmd --add-port=8000/tcp --permanent
sudo firewall-cmd --reload

5.2 SELinux(如开启)

  • 我们在挂载时用了 :Z,这会自动做上下文标记,通常即可。
  • 如仍遇到 Nginx/HTTP 无法转发到容器的 SELinux 限制,再针对具体服务设置布尔值。

6. 验证与调用

6.1 打开文档

浏览器访问:http://<服务器IP>:8000/docs

6.2 Postman(或 curl)上传

  • POST http://<服务器IP>:8000/upload
  • Body → form-data:键名 file,类型 File,选一个本地文件

curl 示例:

bash 复制代码
curl -X POST "http://<服务器IP>:8000/upload" \
  -F "file=@/path/to/local/test.png"

返回示例:

json 复制代码
{"filename":"20251009_test.png","url":"/files/20251009_test.png"}

6.3 下载

直接访问:http://<服务器IP>:8000/files/20251009_test.png


7. 一键脚本(可选,复制即用)

保存为 deploy.sh

bash 复制代码
#!/usr/bin/env bash
set -e

APP_DIR=~/fastapi-upload
DATA_DIR=/data/fastapi-uploads
IMAGE=fastapi-upload:latest
NAME=fastapi-upload
PORT=8000

# 准备目录
mkdir -p "$APP_DIR"
sudo mkdir -p "$DATA_DIR"
sudo chown -R 1000:1000 "$DATA_DIR"

# 构建镜像
cd "$APP_DIR"
docker build -t "$IMAGE" .

# 运行容器(若已存在则替换)
if docker ps -a --format '{{.Names}}' | grep -w "$NAME" >/dev/null; then
  docker rm -f "$NAME"
fi

docker run -d \
  --name "$NAME" \
  -p ${PORT}:8000 \
  -v ${DATA_DIR}:/app/uploads:Z \
  --restart=always \
  "$IMAGE"

echo "==> Service is up: http://<SERVER-IP>:${PORT}/docs"

执行:

bash 复制代码
chmod +x deploy.sh
./deploy.sh

8. 更新/升级(零停机思路 & 简单思路)

简单思路(短暂中断几秒):

bash 复制代码
# 修改代码后重新构建镜像
docker build -t fastapi-upload:latest .

# 重启容器
docker rm -f fastapi-upload
docker run -d --name fastapi-upload \
  -p 8000:8000 \
  -v /data/fastapi-uploads:/app/uploads:Z \
  --restart=always \
  fastapi-upload:latest

近似零停机(平滑切换端口):

  1. 启新容器占用备用端口(如 8001):

    bash 复制代码
    docker run -d --name fastapi-upload-v2 \
      -p 8001:8000 \
      -v /data/fastapi-uploads:/app/uploads:Z \
      --restart=always \
      fastapi-upload:latest
  2. (若有 Nginx 反代)把 Nginx 的 upstream 从 8000 切到 8001,nginx -s reload

  3. 确认新容器稳定后,删除旧容器:

    bash 复制代码
    docker rm -f fastapi-upload
    docker rename fastapi-upload-v2 fastapi-upload

9. 常见问题

  • 上传失败/下载 404 :确认 app.mount("/files", StaticFiles(...)) 存在;确认文件已落盘到 /app/uploads;相对路径 url 需要拼域名/IP。
  • "Attribute 'app' not found in module 'main'"main.py 内必须有 app = FastAPI()Dockerfile 的启动命令是 main:app
  • 上传大文件 413 :此方案不经 Nginx,默认无严格限制;如接入 Nginx,需调大 client_max_body_size
  • 权限问题 :确保宿主 /data/fastapi-uploads 对容器用户可写(本文用 1000:1000 和 :Z)。

10. 后续增强(可选)

  • Nginx/HTTPS:加一层反向代理提供 80/443、证书、限流、缓存等(对应"方案 B")。
  • 监控与告警 :Prometheus + cAdvisor 或简单基于 docker logs/HEALTHCHECK
  • 自动化更新:Watchtower 定时拉取新镜像并重启。
  • 备份 :定期备份 /data/fastapi-uploads 到对象存储(MinIO/S3/OSS)或异地。

相关推荐
A小辣椒2 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334663 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux