Anki 有个开源的同步服务器 ankisyncd,支持 AnkiDroid 和 Anki PC 版同步数据,下面介绍一下部署方法:

Linux 系统下部署

Linux 系统下部署需要一台 Linux 的服务器,我这里以 Ubuntu 为例,Debian 和 Centos 的方法类似。

准备源码

1
2
3
4
5
# 下载源码
wget https://github.com/ankicommunity/anki-sync-server/archive/refs/tags/v2.3.0.zip
# 解压
unzip v2.3.0.zip
rm -rf v2.3.0.zip

这里使用了 2.3.0 版本,其它版本方法类似,具体的请参考 ankisyncd

安装依赖

由于 ankisyncd 使用了 python 编写,而且官方使用了 poetry 做为包管理,所以我这里也直接使用 poetry 来安装依赖。

1
2
3
4
5
6
7
8
9
# 更新源
apt update
# 安装必要的系统依赖
apt install -y wget unzip nginx python3 python3-pip
pip install poetry
# 进入源码目录
cd anki-sync-server-2.3.0
# 安装依赖,且不安装开发才需要的依赖
poetry install --no-dev

Read More

目前在个人的测试环境使用了 K3S,兼容 K8S 又比较轻量,打算将公司的某个小环境也使用 k3s 管理了。

Traefik 做为 ingress 资源占用比较低,而且自带 Dashboard 可以看实时状态,新版本还可以使用 Go 来写插件(应该用不上),对日常使用来说还是挺方便的。

在 K3S 上自带的是 Traefik 1.7 版本,但现在 2.X 分支也已经更新了好多个版本了,就打算升一把。以下为升级步骤和常用的路由定义方法。

升级 Traefik 2.X

删除 Traefik 1.7

先禁用 1.7 版本,只要在 k3s server 启动的时候添加 --disable traefik 参数就可以,我使用了 systemd 管理,所以在 /etc/systemd/system/k3s.service 里面的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Unit]
Description=Lightweight Kubernetes
Documentation=https://k3s.io
After=network-online.target

[Service]
Type=notify
EnvironmentFile=/etc/systemd/system/k3s.service.env
ExecStartPre=-/sbin/modprobe br_netfilter
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/bin/k3s server --docker --disable traefik --kube-apiserver-arg 'service-node-port-range=1-65535'
KillMode=process
Delegate=yes
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
Restart=always
RestartSec=5s

[Install]
WantedBy=multi-user.target

这里使用了 docker 做为容器引擎,因为我比较熟悉这个,有点会拿 docker 做点事情。并且我在这里增加了开放的端口范围,方便使用 80, 443 以及其它范围的端口。

由于我之前开启过 traefik 1.7 ,所以这里在 k3s 启动成功后还会遗留之前的,所以先删除 traefik。

1
2
3
4
# 如果没有权限就先将文件拷出来到,再增加文件权限
k3s kubectl delete -f /var/lib/rancher/k3s/server/manifests/traefik.yaml
sudo systemctl daemon-reload
sudo systemctl restart k3s

安装 traefik 2.

添加Helm仓库

1
helm repo add traefik https://helm.traefik.io/traefik

更新一下仓库

1
helm repo update

安装traefik

1
helm install traefik traefik/traefik

如果要安装的指定的 namespace 使用下面的命令

1
2
3
4
kubectl create ns traefik-v2
# Install in the namespace "traefik-v2"
helm install --namespace=traefik-v2 \
traefik traefik/traefik

配置的修改可以使用 helm show values traefik/traefik > traefik_values.yml 命令将 values 导出,然后修改后使用 helm upgrade -f traefik_values.yml traefik traefik/traefik 更新。

这里修改一下入口的端口

将 traefik_values.yml 里面的 ports.web.nodePort 改成 80,ports.websecure.nodePort 改成 443,再部署一下。

1
helm upgrade -f traefik_values.yml traefik traefik/traefik

查看 dashboard

默认的 dashboard 端口是 9000,并没有通过 service 开放出来,也没有添加 ingress,所以无法直接访问。管理员可以考虑使用 kubectl port-forward deploy/traefik 9000:9000 命令映射到本地的 9000 端口上临时访问。

http://localhost:9000/dashboard/

配置持久化并增加TLS

持久化

在 traefik_values.yml 文件中提供了一个 persistence 配置,如果将 enabled 设置为 true 会启用一个 pvc 来做为持久化存储。我这里先新建一个 pv 绑定到 hostPath,因为我使用的是单机环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: PersistentVolume
metadata:
name: traefik-pv
labels:
type: local
spec:
storageClassName: traefik-config
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/home/chronos/dev/k3s/data/traefik"

将 persistence 配置修改为如下值

1
2
3
4
5
6
persistence:
enabled: true
name: data
accessMode: ReadWriteOnce
storageClass: "traefik-config"
path: /data

这样就可以将 /data 绑定到指定的 hostPath 了。

配置 TLS

先配置 resolvers 通过增加命令行参数实现,我这里使用了 dnspod,所以 traefik_values.yml 中增加的参数配置如下:

1
2
3
4
5
6
additionalArguments:
- "--certificatesresolvers.dnspod.acme.email=[你的email]"
- "--certificatesresolvers.dnspod.acme.storage=/data/acme.json"
- "--certificatesresolvers.dnspod.acme.dnschallenge=true"
- "--certificatesresolvers.dnspod.acme.dnschallenge.provider=dnspod"
- "--certificatesresolvers.dnspod.acme.dnschallenge.delaybeforecheck=0"

挂载的 /data 目录需要给 65532:65532 设置写入权限,用于生成 acme.json,并且由于使用了 dnspod 来获取证书,所以还需要配置一个环境变量来设置 api_key

1
2
3
env: 
- name: DNSPOD_API_KEY
value: [API_KEY]

剩下的就是绑定 ingressRoute 并设置 tls 的参数指定 resolvers 就会自动获取证书并续约了。以下是 IngressRoute 示例,绑定了 dnspod 这个 Resolver 来获取证书。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: caddy-secure
namespace: default
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`[你的域名]`)
services:
- kind: Service
name: caddy
namespace: default
passHostHeader: true
port: 80
responseForwarding:
flushInterval: 1ms
scheme: http
strategy: RoundRobin
weight: 10
tls:
certResolver: dnspod

参考链接

目前使用了 docker build 和 docker buildx 来打包镜像,但这个管理镜像和打包镜像还不够灵活,依赖 docker daemon 不太方便。buildah 可以在不开 docker 的情况下打包镜像,非常适合在 ci/cd 的环境下使用,而且支持了多架构的打包,兼容 Dockerfile 以及 bash script 直接调用 buildah 命令的方式构建镜像。

安装 buildah

在 manjaro 上安装只要 yay -S buildah 就可以。

使用 Dockerfile 构建镜像

由于我的 wsl 环境使用非 root 用户构建镜像一直有些问题,暂时就用 root 用户直接玩了。以下所有操作都在 root 环境下,理论上可以在非 root 环境执行。

开始构建前先在 /etc/containers/registries.conf 里面增加 unqualified-search-registries 配置,兼容以前的 Dockerfile FROM 地址。

1
unqualified-search-registries = ['docker.io']

执行下面这条命令就会构建 Dockerfile

1
buildah bud -t nginx:local

使用 buildah 命令构建

本质上使用 buildah 的命令构建是写了一个 bash 脚本,所以里面的写法在调试的时候完全可以直接在命令行一条一条的尝试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 基于镜像生成一个容器
ctr1=$(buildah from "nginx")

# 在容器里面执行命令,生成一个文件
buildah run "$ctr1" -- touch somefile

# 设置容器的注解信息
buildah config --annotation "com.example.build.host=$(uname -n)" "$ctr1"

# 设置 cmd 参数
buildah config --cmd "/usr/sbin/nginx" "$ctr1"
# 设置开放的端口
buildah config --port 80 "$ctr1"

## 提交容器修改到新的镜像
buildah commit "$ctr1" "nginx:modify"

多架构镜像构建

多架构依赖 qemu-user-static ,目前方便的方法是使用 https://github.com/multiarch/qemu-user-static 来处理 binfmt。

Docker 方式

1
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

Podman 方式

1
sudo podman run --rm --privileged multiarch/qemu-user-static --reset -p yes

构建指定架构的镜像

我这里使用了 Dockerfile

1
2
buildah bud --arch=arm64 -t nginx:arm64
buildah bud --arch=amd64 -t nginx:arm64

建立 manifest

1
2
3
buildah manifest create nginx:multiarch
buildah manifest add nginx:multiarch nginx:arm64
buildah manifest add nginx:multiarch nginx:amd64

上传 manifest

1
buildah manifest push --all nginx:multiarch docker://localhost:5000/nginx:multiarch

注意要增加 --all ,以便同时上传关联的镜像

需要 Docker 19.03 以上版本,并开启实验功能,Daemon 和 Cli 都需要开启

1
2
3
4
# 在 ~/.docker/config.json 中加入 experimental 开启 Cli 端的实验功能
{
"experimental": "enabled"
}
1
2
3
4
# 在 /etc/docker/daemon.json 中加入 experimental 开启 daemon 的实验功能
{
"experimental": true
}

安装兼容 arm 的执行器 docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

添加一个 buildx docker buildx create --name multiarch

将刚添加的 buildx 设置为当前使用的 docker buildx use multiarch

初始化 buildx docker buildx inspect --bootstrap

1
2
3
4
5
6
7
8
9
10
11
12
[+] Building 12.2s (1/1) FINISHED                                                                                                                                                           
=> [internal] booting buildkit 12.2s
=> => pulling image moby/buildkit:buildx-stable-1 11.3s
=> => creating container buildx_buildkit_multiarch0 0.9s
Name: multiarch
Driver: docker-container

Nodes:
Name: multiarch0
Endpoint: unix:///var/run/docker.sock
Status: running
Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/386, linux/arm/v7, linux/arm/v6, linux/s390x
1
docker buildx build --platform linux/amd64,linux/arm64 -t harbor.yunyang.com.cn/library/citybrain-nginx:1.18.0-alpine --push .

之前一直想拿 nodemcu 做个远程控制电脑开关的东西,当时配件没买齐,实现到一半就扔在那边放了好久,用着小米路由器的远程唤醒功能。不过使用网卡的远程唤醒在停电后就无法再通过远程唤醒了,而且死机后也没办法强制关机。
前两天搬到新家后,因为小米路由器留给老家了,所以又把 nodemcu 拿起来做这个远程开关机的东西。

准备零件

  1. nodemcu 1个
  2. s9013 NPN三极管1个
  3. 杜邦线若干
  4. 1k 色环电阻1个

Read More

Beautify

美化代码支持 javascript, JSON, CSS, Sass, and HTML。

Bookmarks

提供书签功能,方便做代码跳转。

Bracket Pair Colorizer

用不同的色彩显示括号,在括号嵌套比较多的地方能很容易的看出每个括号对应的是那个。

Read More

之前买的 NodeMCU 做了一些试验之后忙着装修,一直没动它。这两天抽空研究了一下 MQTT 协议,用来实现自己家庭用的 IOT Server,接入多个 NodeMCU 设备。MQTT 协议开销很小,而且 NodeMCU 官方有现成的模块可以使用。

MQTT Server 的选择

决定使用 MQTT 协议后就去网上找各种 Brokers 的实现,因为要部署在 vps 上,而且只有自己一个人使用,不能使用那个占用资源非常多的方案。

项目名 开发语言 官网 备注 采用
emqttd erlang http://www.emqtt.io/ 官方提供一个 docker 镜像,运行后初始占用大约要 100 多 MB 内存 ×
mosquitto c https://mosquitto.org/ 资源占用非常小,初始内存只有几百KB
surgemq go https://github.com/influxdata/surgemq 这是一个库,需要自己写代码调用 ×
mqtt go https://github.com/jeffallen/mqtt 看介绍只支持 QOS 0 ×

目前看来还是使用 mosquitto 更符合我的要求一些,能实现身份验证和各种 QOS 级别的消息传递,而且资源占用非常小。使用 go 语言来开发控制端,连接 mosquitto 服务器,通过订阅和发布消息来控制 nodemcu 设备,然后开放一个 web 界面,这样手机也能方便的远程监控家里的情况。

最近想用 nodemcu 做点小玩意,然后上淘宝买了两只回来,昨晚抽空做了一下实验,成功点亮一颗 LED。

NodeMCU 简介

NodeMCU 是一个可以在嵌入式开发中使用 lua 的开源固件,提供了很多现成的模块,比如:mqtt, cjson, file, websocket, http等。

我买的 NodeMCU 是下面这种,在淘宝上买的,基于 ESP8266 芯片,自带 WIFI 做物联网好用。

果然很小,如果只买 ESP8266-12F 芯片,然后自己焊接可以做的更小,就是会比较麻烦。

刷固件

首先新买的 NodeMCU 拿到手的时候没有固件,需要先刷入固件。

驱动

我的买的这种便宜一点,USB 转串口的芯片是 CH340G,另外还有一种使用的是 CP2102,据说 CP2102 更好一点,不过我的 CH340G 也一样可以正常使用。

CH340G驱动 For Mac

Read More

前两天看到influxdb这个数据库,觉得拿来存储日志太适合了。数据库带有压缩功能,可以减少存储空间,并且支持一些基本的查询,语法类似SQL(有很多限制,后面会讲到)。配合下列组件可以很方便的查看服务器性能和应用日志分析:

  1. fluentd: 用于日志收集,类似于logstash,使用ruby写的,内存占用比logstash小,不过有gil,多核利用需要用插件多进程。
  2. telegraf: 采集服务器cpu,内存等负载数据,类似于collectd,使用go编写,配合influxdb使用。
  3. grafana: 图形化显示性能分析数据,跟influxdb配合。

influxdb

基本概念

  1. 跟mysql数据一样有数据库,语法也是 create database [dbname],不过多了一个可以设置数据保留规则的东西可以让旧的数据自动清除
  2. 有MEASUREMENTS类似于table,但是没有模式,数据结构可动态增加
  3. 每一个point数据有tag,field, timestamp
  4. tag可以用于group by,field不可以
  5. field可以进行 function aggregate,tag不行
  6. timestamp插入数据的时候可以指定也可以不指定,默认为当前时间
  7. 存储的时间都为utc时区

使用docker部署

用docker来部署比较简单,直接使用 docker run -p 8083:8083 -p 8086:8086 -v influxdb:/var/lib/influxdb influxdb:0.13.0-alpine,我喜欢用alpine版本的镜像,容量比较小。
运行成功后就可以使用浏览器访问8083来控制了, 8083是admin控制台,8086是http api端口。

influxdb管理界面

查询数据

语法类似于 SQL ,不过像group by 之类的语句对 tag 和 field 是有区别的,所以设置 schema 的时候需要注意一下。

SELECT * FROM "bbs" WHERE time > now() - 2h

查询最近两小时的日志数据

弃坑

原本想把整套环境配置起来玩玩,正好碰上迁移服务器,迁移后的服务器内存大小是8G,然后发现个非常严重的问题,这货会把内存吃光光,进程在收集日志的时候就占用1.2G内存,查询的时候更是超过了2G,甚至有上过6G。直接把服务器卡的无法动弹。
目前看来这个方案无法在小内存的服务器上使用,独立到单独的日志处理服务器上也许可行。后来试过0.12.2, 0.13.0, 1.0.0-beta3 都是一样的情况,这样就没办法用了。

首先在 /etc/sysctl.conf 里面加入

kern.maxfiles=1048600
kern.maxfilesperproc=1048500

执行下面的命令

sudo sysctl -w kern.maxfiles=1048600
sudo sysctl -w kern.maxfilesperproc=1048500

limit.maxfiles.plist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxfiles</string>
<string>1048600</string>
<string>1048600</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>

limit.maxproc.plist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxproc</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxproc</string>
<string>1048500</string>
<string>1048500</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>

/Library/LaunchDaemons/ 目录中加入这两个 plist 文件

执行下面的命令让其启用

sudo launchctl load /Library/LaunchDaemons/limit.maxfiles.plist
sudo launchctl load /Library/LaunchDaemons/limit.maxproc.plist

再重启计算机就可以了