跳转到内容
Go back

搭建 Anki 自托管同步服务器

更新于:

搭建 Anki 自托管同步服务器

同步服务端部署

同步服务端部署用的是容器的部署方式,一开始想用 Windows 客户端带的同步服务功能来部署自定义同步服务的,但是 Windows 客户端的可执行文件和相关依赖占用的空间实在太大了,遂改用自编译 Rust 二进制可执行程序作为服务组件来封装 Docker 容器。

期间一开始是考虑封装 Python 版的同步服务环境的,无奈有个 Anki 包库依赖在部署后运行服务时,其内部调用提示缺失,就放弃了 Python 的方案。Python 环境编译部署安装比较省时,但是封装完的镜像体积上,Rust 更有优势,占用的空间更小。

相关目录及文件

# 相关目录结构
# tree .
.
├── Dockerfile
├── data.d
├── docker-compose.yml
└── envs
    ├── pub.env
    └── users.env

2 directories, 4 files

创建文件夹和配置文件

创建文件夹

mkdir anki-sync-server anki-sync-server/data.d anki-sync-server/envs

创建配置文件

新建 pub.env

tee anki-sync-server/envs/pub.env <<-'EOF'
TZ=Asia/Shanghai
SYNC_BASE=/opt/anki.d/sync.d
SYNC_HOST=0.0.0.0
SYNC_PORT=8080
EOF

新建 users.env

tee anki-sync-server/envs/users.env <<-'EOF'
SYNC_USER1=user1:user1_password
SYNC_USER2=user2:user2_password
SYNC_USER3=user3:user3_password
EOF

创建 Anki 同步服务器的 Dockerfile

[!note] 注意修改 rust 编译环境的版本为 anki 官方服务所支持的版本

# Dockerfile
FROM rust:1.75-alpine AS builder

RUN set -aeux && sed -i "s/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g" /etc/apk/repositories
RUN set -aeux && apk add --no-cache binutils git musl-dev protobuf-dev

RUN set -aeux \
    && cargo install --git https://mirror.ghproxy.com/https://github.com/ankitects/anki.git --tag 24.06 anki-sync-server \
    && rm -rf /tmp/cargo-install*

# RUN set -aeux \
RUN set -aeux \
    && mkdir /rootfs \
    && cp --parents $(which anki-sync-server) /rootfs/ \
    && cp --parents $(ldd "$(which anki-sync-server)" | awk '{if ( match($1,"/") ) {print $1}}') /rootfs/ \
    && strip $(find /rootfs/ -type f)

FROM alpine:latest

# Init system envs
ENV \
    PATH="${PATH}:/usr/local/cargo/bin" \
    LANG=C.UTF-8 \
    TZ=Asia/Shanghai \
    SYNC_BASE=/opt/anki.d/sync.d

# Set work directory
WORKDIR /opt/anki.d

# Set system environments
RUN set -aeux && sed -i "s/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g" /etc/apk/repositories
RUN set -aeux && apk add --no-cache ca-certificates tzdata

# Set the Timezone
RUN set -aeux \
    && ln -sf "/usr/share/zoneinfo/${TZ}" /etc/localtime

# Copy anki-sync-server
COPY --from=builder /rootfs/ /

CMD ["anki-sync-server"]

编译镜像

可以使用如下两种命令,根据 dockerfile 编译 images,其中,如使用 buildx 的话,需要单独安装 docker-builx

docker buildx build -t anki-sync-server:v24.04 -f Dockerfile .

docker build -t anki-sync-server:v24.04 -f Dockerfile .

使用 Docker-compose 部署服务

新建一个容器服务的编排文件: docker-compose.yml

[!note] 注意修改 Dockerfiledocker-compose.yml 中 image 的版本,并且两者的版本需要保持一致

version: '3.12'
services:
  anki:  # 服务名称
    container_name: anki  # 容器名称
    image: anki/syncd:v24.04
    restart: always

    env_file:
      - ./envs/pub.env
      - ./envs/users.env

    network_mode: bridge
    ports:
      - "8080:8080/tcp"

    sysctls:
      - net.ipv4.tcp_ecn=1
      - net.ipv4.tcp_ecn_fallback=1
      - net.ipv4.tcp_dsack=1
      - net.ipv4.tcp_fack=1
      - net.ipv4.tcp_sack=1
      - net.ipv4.conf.all.rp_filter=1
      - net.ipv4.conf.default.rp_filter=1
      # - net.ipv4.ip_forward=1
      - net.ipv4.tcp_keepalive_intvl=15
      - net.ipv4.tcp_keepalive_probes=5
      - net.ipv4.tcp_keepalive_time=75
      - net.ipv4.tcp_fastopen=3
      - net.ipv4.tcp_moderate_rcvbuf=1
      - net.ipv4.tcp_mtu_probing=2
      - net.ipv4.tcp_syncookies=1
      - net.ipv4.tcp_timestamps=1
      - net.ipv4.tcp_window_scaling=1

    volumes:
      - "./data.d/:/opt/anki.d/"
      - "/etc/localtime:/etc/localtime:ro"

# 配置参考资料:
# https://docs.docker.com/compose/compose-file/
# https://docs.ankiweb.net/sync-server.html#multiple-users
# https://docs.ankiweb.net/sync-server.html#with-cargo

根据配置文件部署

docker-compose up -d

使用 Bash 脚本一键式部署

# 创建配置文件夹
mkdir anki-sync-server anki-sync-server/data.d anki-sync-server/envs

# 新建 Dockerfile
tee anki-sync-server/Dockerfile <<-'EOF'
# Dockerfile
FROM rust:1.71-alpine AS builder
ENV \
    RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup \
    RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup

RUN set -aeux && sed -i "s/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g" /etc/apk/repositories
RUN set -aeux && apk add --no-cache binutils git musl-dev protobuf-dev

RUN set -aeux \
    && cargo install --git https://github.com/ankitects/anki.git --tag 24.04.1 anki-sync-server \
    && rm -rf /tmp/cargo-install*

RUN set -aeux \
    && mkdir -p /rootfs/ \
    && cp --parents $(which anki-sync-server) /rootfs/ \
    && cp --parents `ldd $(which anki-sync-server) | awk '{if (match($1,"/")){print $1}}')` /rootfs/ \
    && strip $(find /rootfs/ -type f -iname "*")

FROM alpine:latest

# Init system envs
ENV \
    LANG=C.UTF-8 \
    TZ=Asia/Shanghai \
    SYNC_BASE=/opt/anki.d/sync.d

# Set work directory
WORKDIR /opt/anki.d

# Copy anki-sync-server
COPY --from=builder /rootfs/ /

# Set system environments
RUN set -aeux && sed -i "s/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g" /etc/apk/repositories
RUN set -aeux && apk add --no-cache ca-certificates tzdata

# Set the Timezone
RUN set -aeux \
    && ln -sf $(echo /usr/share/zoneinfo/${TZ}) /etc/localtime \
    && echo ${TZ} > /etc/timezone \
    && ln -sf /usr/local/cargo/bin/anki-sync-server /usr/local/bin/anki-sync-server

CMD ["anki-sync-server"]
EOF

tee anki-sync-server/docker-compose.yml <<-'EOF'
version: '3.12'
services:
  anki:  # 服务名称
    container_name: anki  # 容器名称
    image: anki/syncd:v23.10
    restart: always

    env_file:
      - ./envs/pub.env
      - ./envs/users.env

    network_mode: bridge
    ports:
      - "8080:8080/tcp"

    sysctls:
      - net.ipv4.tcp_ecn=1
      - net.ipv4.tcp_ecn_fallback=1
      - net.ipv4.tcp_dsack=1
      - net.ipv4.tcp_fack=1
      - net.ipv4.tcp_sack=1
      - net.ipv4.conf.all.rp_filter=1
      - net.ipv4.conf.default.rp_filter=1
      # - net.ipv4.ip_forward=1
      - net.ipv4.tcp_keepalive_intvl=15
      - net.ipv4.tcp_keepalive_probes=5
      - net.ipv4.tcp_keepalive_time=75
      - net.ipv4.tcp_fastopen=3
      - net.ipv4.tcp_moderate_rcvbuf=1
      - net.ipv4.tcp_mtu_probing=2
      - net.ipv4.tcp_syncookies=1
      - net.ipv4.tcp_timestamps=1
      - net.ipv4.tcp_window_scaling=1

    volumes:
      - "./data.d/:/opt/anki.d/"
      - "/etc/localtime:/etc/localtime:ro"
EOF

# 新建 pub.env
tee anki-sync-server/envs/pub.env <<-'EOF'
TZ=Asia/Shanghai
SYNC_BASE=/opt/anki.d/sync.d
SYNC_HOST=0.0.0.0
SYNC_PORT=8080
EOF

# 新建 users.env
tee anki-sync-server/envs/users.env <<-'EOF'
SYNC_USER1=user1:user1_password
EOF

# build docker image
docker build -t anki/syncd:v24.04

# 部署
cd anki-sync-server && docker-compose up -d

客户端设置

桌面端

桌面客户端(macOS/Windows/Linux)配置方法如下:

  1. 先打开「首选项」

    图片描述:2023-06-26-12-24-QHYKZt.png

  2. 点击「网络」,往下看,可以看到标有 self-hosted sync server(自定义同步服务器) 的方框,在里面填写您的服务端的地址:

    图片描述:2023-06-26-12-26-HYOaBJ.png

  3. 重启 Anki,然后点击「同步」:

    图片描述:2023-06-26-12-28-ccnUOj.png

  4. 这时候会弹出一个输入框让你输入用户名和密码,你需要将你之前设置的用户名和密码输入进去:

    图片描述:2023-06-26-12-29-z5E9gi.png

  5. 点击确认后,就会开始同步了。

安卓端

安卓端也是直接配置即可,我的 AnkiDroid 版本是 2.15.6。你可以通过「设置 -> 高级设置 -> 自定义同步服务器」找到配置页面。

再填写用户名和密码:

设置 -> 常用设置 -> AnkiWeb 账户

这样就算配置完成了,所有的牌组都同步过来了。

官方的版本实在是太老了,如果你想使用更激进的社区版本,可以到这个页面下载最新的 Beta 版:

建议下载 arm64-v8a 版本。

安装完成后,可以通过「设置 -> 同步 -> 自定义同步服务器」找到配置页面:

再填写用户名和密码:

设置 -> 同步 -> AnkiWeb 账户

iOS 端

AnkiMobile 也已经支持和自建的同步服务器同步了。至少对于版本 Ankimobile 2.0.90(20090.2) 来说,似乎是可行的,这是一位 iOS 系统用户 在 Anki 论坛报告的

如果设置完成后发现不能同步可以参考下面的内容再试一次:

If you’re using AnkiMobile and are unable to connect to a server on your local network, please go into the iOS settings, locate Anki near the bottom, and toggle “Allow Anki to access local network” off and then on again.

上面的内容摘自 ANki tutorial

参考资料

1、https://docs.ankiweb.net/sync-server.html#multiple-users

2、https://docs.ankiweb.net/sync-server.html#with-cargo


分享文章至:

Previous Post
得年、享年、享寿
Next Post
有效笔记的三个原则