Featured image of post 私有道理余音乐部署到云服务器

私有道理余音乐部署到云服务器

在京东云 CentOS 7 上从零部署一套 FastAPI 音乐流媒体后端,并保留已有的老版本服务。记录环境准备、systemd 双服务共存、ffmpeg 静态版安装,以及实际踩过的 8 个坑。

字数: 2704

从零部署后端余哥音乐到京东云 :一次完整的踩坑记录

一个自建无损音乐流媒体服务的部署实录。包含环境准备、systemd 配置、双服务共存、以及我实际踩过的 8 个坑。

背景

「道理鱼音乐」是一套自建的音乐流媒体服务:

  • 后端:FastAPI + SQLite,负责扫描本地音乐库、提供 API
  • 前端:单文件 HTML 播放器(player.html),浏览器直接打开
  • 特色功能:音质降级转码(原档 / 320k / 128k)、文件夹自动歌单、歌词逐行滚动、歌单封面

需求是把这套服务部署到京东云的一台 CentOS 机器上,同时保留机器上已有的老版本服务(端口 42129),新版本跑在 42126。

听起来不难,但实际踩了一路坑。记录下来,给后面的人省点时间。


一、环境清单

组件版本备注
系统CentOS 7.x京东云轻量云主机
Python3.12手动装的,系统自带的 3.6 太老
ffmpeg7.0.2-static静态编译版,免依赖
项目路径/opt/mydaoliyu_music
音乐库/usr/local/music_player/music
数据目录/usr/local/music_player/data
服务端口42126老版本占 42129

二、部署流程

2.1 装依赖

# Python 环境
yum install -y python3 python3-pip python3-devel gcc

# 编译依赖(bcrypt、Pillow 需要)
yum install -y libffi-devel openssl-devel

2.2 装 ffmpeg(重点)

yum install ffmpeg 在 CentOS 7 上一定失败——EPEL 里没有 ffmpeg,因为专利许可问题。三种方案:

方案 A:Nux Dextop 仓库

rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
yum install -y ffmpeg

方案 B:RPM Fusion 仓库

yum install -y https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm
yum install -y ffmpeg

方案 C:静态编译版(我用的这个,最省事)

cd /tmp
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar xvf ffmpeg-release-amd64-static.tar.xz
cp ffmpeg-*-static/ffmpeg /usr/local/bin/
cp ffmpeg-*-static/ffprobe /usr/local/bin/
chmod +x /usr/local/bin/ffmpeg /usr/local/bin/ffprobe

# 验证(任何目录都能跑)
ffmpeg -version | head -1

静态版最大好处:不污染系统包管理,卸载就是删两个文件。

2.3 上传代码 + 建虚拟环境

cd /opt/mydaoliyu_music
python3.12 -m venv venvmusic3.12
source venvmusic3.12/bin/activate

pip install --upgrade pip
pip install -r requirements.txt

# 验证
python3 -c "import fastapi, uvicorn, multipart, mutagen, PIL, jwt, bcrypt; print('all ok')"

2.4 目录规划

mkdir -p /usr/local/music_player/music   # 音乐库
mkdir -p /usr/local/music_player/data    # SQLite + 封面缓存 + 转码缓存

2.5 配置 .env

重点:nano 在最小化安装的 CentOS 上默认没有。要么 yum install -y nano,要么用 cat 一次性写入:

cat > /opt/mydaoliyu_music/.env << 'EOF'
MUSIC_ROOT=/usr/local/music_player/music
DATA_DIR=/usr/local/music_player/data

JWT_SECRET=用 python -c "import secrets; print(secrets.token_urlsafe(48))" 生成
TICKET_SECRET=再生成一次

ADMIN_USERNAME=admin
ADMIN_PASSWORD=改成强密码

HTTP_HOST=0.0.0.0
HTTP_PORT=42126

AUTO_SCAN_ENABLED=1
SCAN_INTERVAL_SECONDS=1800
SCAN_ON_STARTUP=1
AUTO_FOLDER_PLAYLISTS=1

FFMPEG_BIN=ffmpeg
EOF

chmod 600 /opt/mydaoliyu_music/.env

<< 'EOF' 里的单引号很重要,避免 $ 被 bash 提前替换。

2.6 systemd 服务(CentOS 7 有坑,见第三节)

cat > /etc/systemd/system/mydaoliyu-music.service << 'EOF'
[Unit]
Description=My Daoliyu Music API
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/mydaoliyu_music
EnvironmentFile=/opt/mydaoliyu_music/.env
ExecStart=/bin/bash -c 'exec /opt/mydaoliyu_music/venvmusic3.12/bin/uvicorn main:app --host 0.0.0.0 --port 42126 --workers 1 >> /var/log/mydaoliyu-music.log 2>&1'
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now mydaoliyu-music
systemctl status mydaoliyu-music

2.7 放行端口

京东云控制台 → 安全组 → 加入站规则:TCP 42126,来源 0.0.0.0/0。

firewalld(如果开着):

firewall-cmd --permanent --add-port=42126/tcp
firewall-cmd --reload

SELinux(如果 Enforcing):

semanage port -a -t http_port_t -p tcp 42126

三、踩坑记录(8 个)

坑 1:CentOS 7 没预装 nano

最小化安装的 CentOS 只有 vi。或者用 cat > file << 'EOF' 直接写入,比 vi 还快。

yum install -y nano

坑 2:EPEL 里没有 ffmpeg

yum install ffmpeg 一定报 No package ffmpeg available.。用静态版最省心。

坑 3:Python 3.12 的 venv 与老 pip 冲突

系统 pip 是 3.6 时代的,直接 pip install 会装错地方。必须先激活 venv:

source /opt/mydaoliyu_music/venvmusic3.12/bin/activate
which python
# /opt/mydaoliyu_music/venvmusic3.12/bin/python

坑 4:StandardOutput=append: 在 CentOS 7 报错

CentOS 7 的 systemd 是 219 版本,StandardOutput=append: 语法是 240+ 才支持的。

我在服务文件里写了:

StandardOutput=append:/var/log/daoliyu-music.log

结果 systemd 直接报:

Failed to parse output specifier...
Current command vanished from the unit file

服务起不来,日志也不生成。

解决方案:用 bash 包装重定向:

ExecStart=/bin/bash -c 'exec /path/to/uvicorn ... >> /var/log/xxx.log 2>&1'

exec 关键字很关键——它让 uvicorn 直接替换 bash 进程,systemd 才能正确管理 PID 和信号。

坑 5:服务名冲突

本来我把服务起名叫 daoliyu-music.service,结果 systemctl enable 时报:

Failed to execute operation: File exists

查了一下才发现,机器上已经有一个老版本服务用这个名字了,而且文件在 /usr/lib/systemd/system/daoliyu-music.service(系统级),我写的是 /etc/systemd/system/daoliyu-music.service(用户级),两者同名,systemd 加载时以 /etc 优先,但 enable 操作会冲突。

解决:服务名改成 mydaoliyu-music,服务文件、日志文件名都加前缀,彻底隔离。

坑 6:systemctl status 显示的还是老进程

改完文件、systemctl daemon-reload 后,status 里 Main PID 还是老的 daoliyu-server。

原因:systemd 缓存了老的服务定义。执行:

systemctl stop daoliyu-music
systemctl daemon-reload
systemctl reset-failed daoliyu-music
systemctl start daoliyu-music

坑 7:drop-in 目录里的遗留配置

有些老部署会在 /etc/systemd/system/xxx.service.d/ 下放 drop-in 文件(比如 after-mysql.conf),它们会自动合并到同名服务里,导致你不认识的行为。

排查:

ls -l /etc/systemd/system/daoliyu-music.service.d/
systemctl cat daoliyu-music

清理:

rm -rf /etc/systemd/system/daoliyu-music.service.d
systemctl daemon-reload

坑 8:端口被占,但 netstat 查不出来

netstat -tlnp | grep 42126 没输出,但启动还是报 Address already in use。

原因:进程可能绑的是 IPv6(:::42126),要用:

ss -tlnp | grep 42126

ss 比 netstat 更现代、信息更全,优先用 ss。


四、双服务共存方案

老服务保留,新服务并存,关键原则:名字不同、端口不同、路径不同、日志不同。

项目老服务新服务
服务名daoliyu-musicmydaoliyu-music
端口4212942126
项目路径/opt/daoliyu-music/opt/mydaoliyu_music
数据目录/var/lib/daoliyu-music/usr/local/music_player/data
用户daoliyu-musicroot
日志/var/log/daoliyu-music.log/var/log/mydaoliyu-music.log
启动方式官方 serverctl自己写的 uvicorn

两条服务 systemctl list-units 里同时 running,互不影响:

daoliyu-music.service      active running   Daoliyu Music Server
mydaoliyu-music.service    active running   My Daoliyu Music API

端口占用:

LISTEN  *:42126  uvicorn
LISTEN  [::]:42129  daoliyu-server

五、验证

本机健康检查:

curl http://127.0.0.1:42126/api/health
# {"ok":true,"time":...,"autoScan":true,"scanInterval":1800}

启动日志(应有完整路径回显):

服务启动完成
BASE_DIR           = /opt/mydaoliyu_music
MUSIC_ROOT         = /usr/local/music_player/music  (exists=True)
DATA_DIR           = /usr/local/music_player/data   (exists=True)
DB_PATH            = /usr/local/music_player/data/app.db
TRANSCODE_DIR      = /usr/local/music_player/data/transcoded
FFMPEG_BIN         = ffmpeg
HTTP               = 0.0.0.0:42126
AUTO_FOLDER_PL     = True
自动扫描已开启,间隔 1800 秒
开始扫描:/usr/local/music_player/music
文件夹歌单同步完成:新增 0,更新 1,删除 0
扫描完成 用时 0.23s:新增 0,更新 0,跳过 689,移除 0,错误 0

浏览器访问:http://<公网IP>:42126/web

默认账号 admin / admin(登录后立刻改密码)。


六、部署后的效果

  • 689 首歌入库,扫描 0.23 秒完成
  • 文件夹歌单自动生成:music/ 下每个一级目录变成一个歌单,名字就是文件夹名
  • 音质切换:底部播放条有「原 / 320 / 128」按钮,实时切
  • 转码缓存:切到 320 或 128 后,data/transcoded/ 下会生成对应 mp3,下次秒播

转码缓存看一下:

ls -lh /usr/local/music_player/data/transcoded/
# -rw-r--r-- 1 root root 7.2M  xxxx_mid.mp3
# -rw-r--r-- 1 root root 2.9M  xxxx_low.mp3

七、运维命令速查

# 新服务
systemctl restart mydaoliyu-music
systemctl stop mydaoliyu-music
systemctl status mydaoliyu-music
tail -f /var/log/mydaoliyu-music.log

# 老服务(不动)
systemctl status daoliyu-music

# 手动触发扫描
TOKEN=$(curl -s -X POST http://127.0.0.1:42126/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"xxx"}' | \
  python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")
curl -X POST http://127.0.0.1:42126/api/library/scan \
  -H "Authorization: Bearer $TOKEN"

# 数据库备份(SQLite WAL 模式要用 .backup)
sqlite3 /usr/local/music_player/data/app.db \
  ".backup /backup/app.db.$(date +%F)"

八、给后来者的 5 条建议

  1. 静态 ffmpeg 是自建音乐服务的最优解:不用折腾仓库、不污染系统、删两个文件就干净。
  2. CentOS 7 的 systemd 是 219,很多新语法(StandardOutput=append:、LogNamespace= 等)不支持。用 bash -c 'exec ... >> log' 万能。
  3. 同名服务会打架。部署新版本前先 systemctl list-units | grep xxx 看有没有老服务;部署时改名字。
  4. ss -tlnp 比 netstat 靠谱。IPv6 监听、unix socket 都能列出来。
  5. 服务文件写完后必做三连:daemon-reload → reset-failed → enable --now。少一个都可能加载到老配置。

结语

整个部署从零到跑起来,如果不踩坑,大概 10 分钟。踩了坑大概 30小时。把过程写下来,希望下一个部署的人一路顺风。

发布了 13 篇文章 | 共 43.6K 字 | 总访客数: Loading | 总访问量: Loading
Hugo theme Stack designed by Jimmy | 黔ICP备2025049100号-1