用 Docker & Nginx 搭建自己的账本(Firefly)

Preface

“我辛辛苦苦挣的钱都去哪儿了”,我可不希望以后什么什么时候开始说这种话,于是 搭建个记账软件来记录自己的收支吧。
该软件优点如下:

  • 资金流向:比如钱都花在哪些类目上了,钱都流向哪些商家了。
  • 资金预算:比如打算每个月花多少钱在饮食上。
  • 分类 & 标签:比如钱都花在那些类目上了。
  • 交易记录:除了收入和支出记录外,如有多个银行账户的话 还能记录不同账户之间的转帐交易
  • 存钱罐(Piggy Banks):比如想买个手机但是目前没有那么多预算,可以先弄个存钱罐,然后一点一点的往里面存。相当于一个长远的资金预算。
  • 账单:每月水电费之类的。
  • 规则:比如填写支出描述时自动填写预设的支出金额。
  • 报表(Reports):按月、季、年生成报表,账户余额、资金流向、短期/长期预算、账单 等等。
  • 多货币:软妹币, 美刀, 英镑, 比特币,想用什么就用什么(不支持货币换算,在创建收支记录时候可以手动填写换算前后的金额)。
  • 数据导入导出:导入导出 csv 格式的文件,进一步对资金流向做分析什么的。

目前我能想到的唯一缺点就是:没能和各种银行/支付机构(比如支付婊)进行接口对接,实现自动记录收入支出。
另外,该软件作者表示 “不支持自动支付重复性交易”,原因是:

I believe that if you are serious about changing your financial habits, you should be aware of what happens on your accounts. The money you spend and the money you earn. By entering each transaction manually, you will feel what you spend.
我认为如果你真的想改变你的财务习惯的话,你应该更关心你的账户,你花的钱还有你挣的钱。通过手动地录入每次交易,让你切实体会到钱都花在哪里了。

安装 & 更新 Docker (docker-ce)

近些天 Docker 更新了,连软件包名字也变了。docker-ce 对应 Docker 社区版(Community Edition),docker-ee 对应 Docker 企业版(Enterprise Edition)。

  1. 如果原来安装过 Docker 的话,先卸载旧版

    1
    sudo apt-get remove docker docker-engine
  2. 通过安装下面的软件包使 apt 通过 HTTPS 下载 Docker 安装包

    1
    2
    3
    4
    5
    sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common
  3. 添加 Docker 的 GPG key

    1
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

    执行命令 sudo apt-key fingerprint 0EBFCD88 后的输出应该大致是这样:

    1
    2
    3
    4
    pub   4096R/0EBFCD88 2017-02-22
    Key fingerprint = 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
    uid Docker Release (CE deb) <docker@docker.com>
    sub 4096R/F273FCD8 2017-02-22
  4. 添加 Docker 软件源

    1
    2
    3
    4
    sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
  5. 安装 Docker

    1
    apt-get update && apt-get install docker-ce

    然后等一段时间就安装好了。

构建和部署 Docker Image

  1. 更新 docker-compose 到最新版,可以参考 这里 的说明

    1
    2
    curl -L https://github.com/docker/compose/releases/download/1.12.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose
  2. 从 GitHub clone Firefly 下来

    1
    2
    3
    4
    git clone https://github.com/firefly-iii/firefly-iii.git
    cd ./firefly-iii/
    # 使用 git checkout tags/<版本号 tag> -b <新的分支名> 来 checkout 指定版本的 Firefly
    git checkout tags/4.3.8 -b v4.3.8
  3. 在当前目录下创建并编辑 docker-compose.my.ymlenvironment 里配置项的具体说明可以参考 这里

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    version: '2'

    services:
    firefly-app:
    image: firefly-iii
    build: .
    environment:
    FF_DB_HOST: "localhost" # 这里暂时用 localhost,等 Firefly 部署后再进入 docker 容器查看 IP 地址
    FF_DB_NAME: "firefly" # 数据库名称
    FF_DB_USER: "firefly" # 数据库用户名
    FF_DB_PASSWORD: "连接数据库的密码"
    FF_APP_KEY: "32个随机英文和数字字符"
    FF_APP_ENV: "production"
    SITE_OWNER: "填写自己的邮箱"
    APP_FORCE_SSL: "true" # 强制使用 HTTPS 连接
    APP_FORCE_ROOT: "https://qian.example.com" # 改成自己网站的 URL, 下同
    APP_URL: "https://qian.example.com"
    ports:
    - "127.0.0.1:8082:80/tcp" # 将 docker 容器的 80 端口映射到本机的 8082 端口上
    volumes:
    # 将 docker 容器内的 /var/www/firefly-iii/storage 映射到本机的 /somewhere/storage 目录下
    # storage 目录下将存放 Firefly 所需的文件(比如创建收支记录时候上传的附件)
    - "/var/www/firefly-iii/storage:/somewhere/storage"
  4. 创建 Docker 容器,这一步会花费较长的时间

    1
    docker-compose -f docker-compose.my.yml create
  5. 对刚创建出来的 Docker 容器改名,改名前的名字大概会像下面这样,改成 firefly 省得后续再写命令时候写那么大一长串:

    1
    docker rename fireflyiii_firefly-app_1 firefly
  6. 启动 Firefly Docker image

    1
    docker start firefly

    运行 curl http://127.0.0.1:8082,如果看到有 HTML 输出的话就行了。

  7. 查看 Docker 容器的 IP 和 Gateway

    1
    2
    3
    4
    # 进入正在运行的 Docker 容器内
    docker exec -i firefly /bin/bash -il
    # 查看 IP 和 Gateway
    ip route show default

    输出的内容大致是这样,将 Gateway(也就是下面的 127.17.0.1)和 IP(也就是 127.17.0.2)记录下来备用:

    1
    2
    default via 172.17.0.1 dev eth0 # 127.17.0.1 就是 Gateway
    172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2 # 127.17.0.2 就是 Docker 容器的 IP
  8. 编辑 docker-compose.my.yml,修改内容如下:

    1
    2
    # 将 FF_DB_HOST 由 localhost 改为上面的 Gateway
    FF_DB_HOST: "172.17.0.1"
  9. 执行下面命令来创建新的 Docker 容器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # kill 掉正在运行的 Docker 容器
    docker kill firefly
    # 删掉容器和旧的 Docker Image
    docker rm firefly
    docker rmi firefly-iii
    # 创建新的 Docker 容器,这将应用前面对 docker-compose.my.yml 的修改
    docker-compose -f docker-compose.my.yml create
    # 改名
    docker rename fireflyiii_firefly-app_1 firefly
    # 启动 Docker 容器
    docker start firefly
  10. 执行命令 curl http://127.0.0.1:8082,应该会有 HTML 输出

接下来开始弄 Nginx。

安装 & 配置 Nginx

安装 Nginx

1
apt-get install nginx

装完了就 curl http://127.0.0.1 看看效果。

生成 Let’s Encrypt SSL 证书

  1. 先为自己的域名添加一个 CNAME 记录,本文将以 qian.example.com 为例。

  2. 在路径 /var/www/ 下创建一个文件夹名为 qian.example.com

  3. 创建并编辑文件 /etc/nginx/sites-available/qian.example.com,文件内容如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    server {
    listen 80;
    listen [::]:80;
    server_name qian.example.com;
    root /var/www/qian.example.com;
    location / {
    try_files $uri $uri/ =404;
    }
    }
  4. 重启 Nginx systemctl restart nginx

  5. 生成 SSL 证书

    1
    letsencrypt certonly --webroot -w /var/www/qian.example.com --domain "qian.example.com"

配置 Nginx 使用 SSL

编辑文件 /etc/nginx/sites-available/qian.example.com,文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server {
server_name qian.example.com;

listen 443 default_server;
listen [::]:443 default_server;
access_log /var/log/nginx/${server_name}_access.log;

# SSL 证书的配置,把 qian.example.com 改成自己的就好了
ssl on;
ssl_certificate /etc/letsencrypt/live/qian.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/qian.example.com/privkey.pem;

index index.html index.php;

location / {
proxy_redirect off;
# proxy_pass:将 HTTP 请求转发到指定端口
proxy_pass http://127.0.0.1:8082;
proxy_set_header Host $http_host;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" always;
client_max_body_size 16M;
}
location = /.htaccess {
return 403;
}
}

重启 Nginx systemctl restart nginx,然后执行 curl https://qian.example.com,输出的 HTML 应该和刚启动完 Firefly 时的输出是一样的。

初始化 Firefly

初始化数据库

  1. 在 MariaDB 里为 Firefly 创建 firefly 数据库

    1
    2
    3
    4
    5
    6
    7
    -- 创建 firefly 数据库
    CREATE DATABASE firefly CHARACTER SET = utf8 COLLATE = utf8_general_ci;
    -- 创建 firefly 用户
    CREATE USER firefly IDENTIFIED BY 'Passw0rd';
    -- 让用户 firefly 拥有 firefly 数据库的完整权限
    GRANT ALL ON firefly.* TO firefly;
    flush privileges;
  2. 执行下面的命令创建数据库表

    1
    2
    3
    4
    5
    6
    # 进入正在运行的 firefly Docker 容器
    docker exec -i firefly /bin/bash -il
    # 如果当前目录不是 /var/www/firefly-iii/ 下面的话则 cd 过去
    cd /var/www/firefly-iii/
    # 执行命令来初始化数据库表
    php artisan migrate:refresh --seed --force

注册并登录

打开浏览器访问 https://qian.example.com,注册一个帐号即可。注册完成后 注册功能会自动关闭,默认情况下只允许单个用户使用这个网站,如果想多人使用的话 可以在设置里取消勾选 “Single user mode”。

更新 Firefly

如果 Firefly 更新了,大致可以按照下面的步骤做,一共3步。

1. 备份数据库

执行下面的命令,将整个 firefly 数据库全部备份出来。

1
sudo mysqldump --databases firefly --triggers --routines --events --single-transaction | gzip -c > mysql_db_firefly_$(date +%Y%m%d_%H%M_%s).sql.gz

2. 更新 docker 镜像

cd 到 firefly-iii 的文件的目录下,执行下面的命令

1
2
3
4
5
6
7
8
9
10
11
12
# 从 GitHub 拉取更新
git pull
# 构建新的 docker 镜像
docker-compose -f docker-compose.my.yml build
# 为 firefly docker 镜像改 tag
docker tag firefly-iii:latest firefly-iii:v4.4.3
# 使用 firefly docker 镜像创建 firefly docker 容器
docker-compose -f docker-compose.my.yml create
# 将新的 firefly docker 容器的名字改为 firefly
docker rename fireflyiii_firefly-app_1 firefly
# 启动 docker 容器
docker start firefly

3. 收尾工作

1
2
3
4
5
6
# 进入并运行 firefly docker 容器内的 /bin/bash
docker exec -i firefly /bin/bash -il
# 执行下面3条命令就行了,具体它们做的啥 我也不知道,也不用知道.
php artisan migrate --env=production --force
php artisan cache:clear
php artisan firefly:upgrade-database

总结

有了之前搭建 Nextcloud 的经验,这次搭建起来省了不少时间。

更新历史

18 Apr 2017: 首次发布
19 Apr 2017:

  • 在 “构建和部署 Docker Image” 中增加 curl 命令来验证 Docker 容器是否启动
  • 小修小改

13 May 2017:

  • 添加章节:更新 Firefly

References

  1. Get Docker for Ubuntu - docs.docker.com
  2. Releases · docker/compose - github.com
  3. Docker - Firefly III
  4. Installation guide - Firefly III
  5. Upgrade guide - Firefly III