从 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)或异地。

相关推荐
海阳宜家电脑6 小时前
SQL Server连接字符串
服务器·网络
努力学习的小廉6 小时前
深入了解linux网络—— 自定义协议(上)
linux·服务器·网络
bcgbsh7 小时前
Linux开机启动脚本(cron 的 @reboot 特性)
linux·cron
听风吹雨yu7 小时前
RK3588从数据集到训练到部署YoloV8
linux·yolo·开源·rk3588·rknn
野犬寒鸦7 小时前
从零起步学习Redis || 第十一章:主从切换时的哨兵机制如何实现及项目实战
java·服务器·数据库·redis·后端·缓存
iconball8 小时前
个人用云计算学习笔记 --19 (MariaDB服务器)
linux·运维·笔记·学习·云计算
Lynnxiaowen8 小时前
今天我们开始学习python3编程之python基础
linux·运维·python·学习
Chandler248 小时前
一图掌握 操作系统 核心要点
linux·windows·后端·系统
dragoooon349 小时前
[Linux系统编程——Lesson6.进程切换与调度]
linux·运维·服务器