HBB's Blog

Ordinary road, record every bit

在群晖 NAS 上部署 Fava,实现实时访问

引言

Fava 是一款基于 Beancount 的文本记账可视化工具,支持实时查看和管理财务数据。对于使用 Beancount 进行财务管理的用户来说,将 Fava 部署到 NAS 上,可以实现随时随地访问账本,并保持数据的最新状态。

本篇博客将介绍如何利用群晖 NAS 的 Container Manager(即 Docker)来部署 Fava,并通过 GitHub 私有仓库管理 Beancount 账本数据,最终实现自动化账单更新和远程访问。

准备工作

在开始部署之前,请确保你的群晖 NAS 具备以下条件:

  • 已安装 Container Manager(可在 DSM 套件中心安装)。
  • 能够访问 GitHub(或其他 Git 托管服务,如 Gitee)。
  • 熟悉基本的 SSH 访问、Git 操作Docker 命令(如果不熟悉,可以参考相关文档)。

此外,你需要准备好 Beancount 账本文件,存储在远程 Git 仓库中。

建立 Beancount Git数据仓库

Beancount 账本我个人存储在 GitHub 平台上,这样可以方便备份和多设备同步。

设置账单仓库为私有仓库并使用git-crypt进行加密,后续的步骤将涉及到如何 克隆私有仓库 以及 解密账本

部署所需要准备的步骤

在通过Docker部署前,需要先考虑如何把fava跑起来。

我这边的思路就是: 克隆仓库 -> 解密文件 -> 安装依赖 -> 启动服务 -> 定时更新。

从私有仓库克隆账本

由于 GitHub 私有仓库需要身份验证,我们可以使用 Token的方式 进行访问:

  1. 在群晖 NAS 终端中生成 SSH Key(如果还没有)

    访问Personal Access Tokens (Classic),点击右上角Generate new token。

  2. 保存好你的token

    将上面的token保存到文件./secrets/github_token.txt中(文件名可任取)

  3. 克隆仓库数据到当前目录:

    git clone -b master https://${GITHUB_TOKEN}@github.com/<your user name>/<your-repositories.git> .

解密账本数据

因为我们的仓库经过git-crypt加密,所以在Docker环境中拉取的时候就需要解密。

  1. 在已经解密的仓库中 导出密钥:

    git-crypt export-key ./secrets/git_crypt_key
  2. 通过export的key进行解密,在Fava启动前解密账本:

    git-crypt unlock ./secrets/git_crypt_key

配置定时任务自动更新账单

为了保证账单数据是最新的,我们可以在通过cron来定时拉取最新的仓库代码。

考虑到目前没有对公网开放访问,所以没有接入 Github Hook

构建Docker镜像

现在,我们可以将上面涉及到的点都打包成一个Docker镜像,使用 Container Manager 来运行 Fava 并在局域网进行 访问。

目录结构

.
├── Dockerfile
├── docker-compose.yml
└── secrets
├── git_crypt_key
└── github_token.txt

Docker Compose

我们通过 Docker Compose 的方式进行部署,先定义compose的配置文件。

version: '3.8'

services:
my_beancount:
container_name: beancount_server
build:
context: .
secrets:
# 3.x 的 secrets写法
# - source: github_token
# target: github_token
# - source: git_crypt_key
# target: git_crypt_key

# 2.9.x version
- github_token
- git_crypt_key
secrets:
# - source: github_token
# target: github_token
# - source: git_crypt_key
# target: git_crypt_key
# 2.9.x version
- github_token
- git_crypt_key
ports:
- "宿主机的端口:5000"
# 主要定义安全信息
secrets:
github_token:
file: ./secrets/github_token.txt
git_crypt_key:
file: ./secrets/git_crypt_key

Dockerfile

# 默认的镜像拉取可能也会遇到问题,如果这个镜像拉不下来,可以尝试修改其他镜像地址。
# [目前国内可用Docker镜像源汇总(截至2025年1月) - CoderJia](https://www.coderjia.cn/archives/dba3f94c-a021-468a-8ac6-e840f85867ea)
FROM docker-0.unsee.tech/python:3

WORKDIR /usr/src/app

# 安装必要的软件包,包括 cron 和 git
RUN apt-get update && apt-get install -y git git-crypt cron

# 可能你的环境拉取git仓库会遇到网络问题,这里你就可以设置代理来解决它。
# ENV http_proxy="http://<proxy-host>:<port>"
# ENV https_proxy="http://<proxy-host>:<port>"

# 克隆仓库并安装依赖
RUN --mount=type=secret,id=github_token \
--mount=type=secret,id=git_crypt_key \
export GITHUB_TOKEN=$(cat /run/secrets/github_token) && \
# clone 仓库
git clone -b master https://${GITHUB_TOKEN}@github.com/<your user name>/<your-repositories.git> . && \
# 解密 git-crypt 加密的文件
git-crypt unlock /run/secrets/git_crypt_key && \
# 安装依赖
pip install --no-cache-dir -r requirements.txt

# 创建一个脚本用于执行 git pull
RUN echo "#!/bin/bash\n\
# 如果无法访问可以考虑增加代理 \n\
# http_proxy=\"http://<proxy-host>:<port>\" \n\
# https_proxy=\"http://<proxy-host>:<port>\" \n\
cd /usr/src/app && \
echo 'git pull' && \
git pull" > /usr/src/app/git_pull.sh && \
chmod +x /usr/src/app/git_pull.sh

# 设置 cron 任务,每间 6h 执行 git_pull.sh
RUN echo "0 */6 * * * root /usr/src/app/git_pull.sh >> /var/log/cron.log 2>&1" > /etc/cron.d/git-pull-cron && \
chmod 0644 /etc/cron.d/git-pull-cron && \
touch /var/log/cron.log

# 暴露端口
EXPOSE 5000

# 启动 cron 服务并运行 fava
# 注意这里要 --host 0.0.0.0 才可以被外部访问到
CMD cron && tail -f /var/log/cron.log & fava --host 0.0.0.0 --port 5000 ./ledger/main.bean

群晖 Container Manager 中创建项目

  1. 将上面目录结构中的文件传到NAS File Station的任意位置
  2. 打开 Container Manager 新建一个项目
  3. 点击设置路径,选择步骤1中上传的目录
  4. 弹出的对话框中确定选择使用现有的docker-compose.yml来创建项目
  5. 输入项目名称后下一步,门户不用设置,直至完成。
  6. 构建中有错误的根据错误情况分别处理,一般情况都是网络问题。

至此,如果构建成功的话,应该就可以通过你本地nas地址加上你指定的宿主机的端口进行访问了。

完善

目前还只能在局域网中进行访问,若要在公网进行访问,还需要完成以下几个部分

  • fava服务的鉴权: 通过部署群晖的SSO验证

  • ipv6的防火墙: 电信只给了公网ipv6,目前家里的主路由防火墙不能单独配置开放端口,还要增加设备。

  • 反向代理: 最后就是使用自己的域名访问了,quick connect无法用来访问自定义的服务

用DDNS将公网ipv6绑定到阿里云的域名上了,然后路由器开了防火墙,公网无法访问但是内网可以访问,而且用下来没有什么公网需要访问的需求。

6. 结语

通过本文,你可以在 群晖 NAS 上成功部署 Fava,实现 账本的自动更新远程访问

这种方案不仅适用于 Fava,也适用于其他基于 Git + Docker 部署的个人应用。如果你对 NAS、自动化账单管理或者 Docker 容器部署有更深入的想法,欢迎交流!

后面折腾部署到公网后会再更新教程,但其实直接公开有点风险,也可以结合VPN回家的方式来在公网访问,后续再更新吧。

祝大家2025,新年快乐🎉,万事顺意🎉。