网络同步盘Syncthing介绍

背景

在现在网盘到处限速的情况下,而且还时不时的删资源,搞不好还能关停(@360 云盘),最重要的还是隐私问题,你把数据交给云盘,它就真的不会看看里面是啥内容吗?
之前尝试过 Nextcloud,可惜这个体验总感觉差这么一点,它用的是传递的用户名密码方案,总感觉安全差点意思。
功夫不负有心人,最近发现了一个工具 Syncthing,这个工具非常的优秀
Syncthing 有很多的优点

  1. 同步速度快,支持文件系统监听,一有修改立刻同步,并且速度可以直接打满带宽。
  2. 数据加密传输,传输数据前需要各个节点确认,安全性很高
  3. 分布式技术,支持部分节点故障的情况下,剩余节点正常同步
  4. 支持文件版本管理,找回误删除的数据
  5. 支持配置忽略模式,不想同步的文件不同步
  6. 部署方便,因为是用 Go 编写的,只有一个二进制,并支持的多个平台部分(Windows,Linux,Mac,Android)

不过也有一些缺点

  1. 当对一个文件夹重命名时,同步端会先把原文件夹删除再下载新重命名的文件夹,效率有点慢
  2. iPhone 没有对应的免费客户端
  3. 同步使用 NAS 下 SMB 挂载的目录时,如果直接通过 NAS 添加文件,syncthing 会感知不到文件变化就不同步了。另外也没有带同步端没有强制同步的按钮,不能手动同步。
  4. 不支持懒加载模式,就是需要的时候再下载同步。

使用

下面就介绍一下下面的这个使用场景怎么部署。
小明在家中有一台华为家庭存储,一台在家中长期运行的 Linux 主机,并且还有一台 Mac 笔记本电脑。
他有两个使用场景

  1. Mac 修改的内容能够随时备份到家中的家庭存储中,并且当误删除文件的时候,能通过家庭存储找回数据。
  2. 将家庭存储的部分资源(如通过迅雷下载的电影)同步到 Mac 中

华为家庭存储设置

华为 NAS 先开启 SMB,设置用户名密码,这里假设用户名 kitty 密码 12345 家庭存储的 IP 是192.168.1.2

Linux 主机设置

这里我假设你是通过你的 Mac 电脑通过 SSH 远程访问的 Linux 主机,毕竟长期运行的 Linux 一般也用不着屏幕。

  1. 使用 Mount 命令挂载 SMB 到本地的一个目录
1
2
$ sudo mount -t cifs -o rw,file_mode=0644,dir_mode=0755,username=kitty //192.168.1.2/<这里换成华为用户名> ~/MyNAS
# 提示输入密码的时候输入密码
  1. 在~/MyNAS 下面创建一个叫 Sync 的目录
  2. https://syncthing.net/网站上将 Linux 版本的 syncthing 下载下来,二进制复制到/usr/local/bin
  3. 直接运行./syncthing
  4. Syncthing 会默认监听本地的 8384 端口,这里使用 ssh 命令将这个服务转发到 Mac 上来
1
$ ssh -L 18384:localhost:8384 用户名@Linux的IP

然后就可以在电脑上的http://localhost:18384访问 syncthing 的控制界面了。

  1. 不过这里的 Default Folder 对应的目录还是 /Sync, 我们先直接将这个移除掉,改成保存路径/MyNAS/Sync

点击
然后按照下面的截图中这样写就行了,注意文件夹 ID 最好写成 default,不然跟其他设备同步的时候会麻烦一点。

  1. 因为 Syncthing 对于 NAS 通过 SMB 挂载的文件监听不敏感,所以将扫描间隔从 3600s 调整为 60s

Mac 电脑配置

  1. https://syncthing.net/网站上将 Mac 版的 dmg 文件,直接安装就可以了。安装完成后会在顶部的状态栏出现一个小图标。
  2. 点击设置,勾选一下开机启动
  3. 本地浏览器打开http://localhost:8384,点击右下角的添加远程设备

  4. http://localhost:18384 中通过操作->显示设备 ID 拿到的 ID 复制到上一步的弹窗中
  5. 两个浏览器都要点击确认一下。之后在界面 Default Folder 设置中的共享中,将要共享的设备勾选一下,两个浏览器再确认一下就完成了。

文件版本控制

这是 Syncthing 非常好用的一个功能,如果再 A 机器配置了版本控制,A 机器删除了文件,这个版本控制是不记录的,只有 B 机器删除了文件同步到 A 的时候,A 机器的版本控制才会工作。
说一下怎么配置吧,在 Linux Syncthing 的控制界面,打开版本控制标签。有很多可以选的。这里就选了一个稍微简单一点的建议版本控制,60 天后清除。最多 5 个版本。

想恢复的时候从这个历史版本里面
找到要恢复的文件,恢复就好了。

效果

实际效果啊那是非常的好,比那个 Nextcloud 好用太多了。

申请ChatGPT账号方法

  1. 国外邮箱
  2. 收码平台

邮箱注册

谷歌邮箱是支持的,不过我更推荐用临时邮箱 https://temp-mail.org/
点击邮箱中的链接,就会进入到下面的界面

收码平台

推荐这个 https://sms-activate.org/ 里面选择 OpenAI 服务,然后随便挑一个国家

然后慢慢的等待验证码收到就行了,如果号码不能用就点击刷新再换一个号

有时候会出现点意外,比如这种,号码都关联上去的,还不给用

Python peewee时区处理

背景

MYSQL 中存储的 DATETIME 数据是不包含时区的,这就需要自己来约定一个时区,最好是跟 MYSQL 的配置一致,不然麻烦多多,数据库默认是 CST 也就是+8 时区
数据库字段如果设置了 DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,默认插入的时间就是用的 mysql 时区
peewee 中的时间字段是 DateTimeField

1
2
3
4
class Base(flask_db.Model):
id = IntegerField(primary_key=True)
gmt_create = DateTimeField(default=datetime.now)
gmt_modified = DateTimeField(default=datetime.now)

插入一个数据试试,查数据库看看(接近中午搞的,存储的是北京时间)时间差不多

1
2
3
4
5
6
7
8
9
# 进入 mysql:
mysql -u root -p
# 查看当前时区
mysql> show variables like '%time_zone%';
# 设置为北京时间:
mysql> set time_zone='+8:00';
mysql> Query OK, 0 rows affected (0.00 sec)
# 通过 select now () 来验证时区:
mysql> select now();

Peewee 读数据库

我们在看 peewee 读到的数据,全部都变成了 UTC 的时间,这个显然是不对的。

谷歌一下也没看到太好的办法,没办法自己定义一个 DateTimeTzField 吧,在代码里面修正一下时区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#
# pip install python-dateutil

from datetime import datetime
from dateutil import tz

# 这里没有用pytz,因为pytz修改时区是多出来6分钟,说是因为用的绝对地点的时差
# 这里用的是python-dateutil,据说说官方开发的。反正挺好用的
CHINA_TZ = tz.gettz("Asia/Shanghai")

class DateTimeTzField(Field):
field_type = 'DATETIME'

def db_value(self, value: datetime):
return value.strftime("%Y-%m-%d %H:%M:%S")

def python_value(self, value: datetime):
# 因为field_type是DATETIME,所以value是datetime.datetime类型
if value is None:
return None
return value.replace(tzinfo=CHINA_TZ) # 修正时区

数据库的定义改为

1
2
3
4
class Base(flask_db.Model):
id = IntegerField(primary_key=True)
gmt_create = DateTimeTzField(default=datetime.now)
gmt_modified = DateTimeTzField(default=datetime.now)

改完之后总算正常了

北海游玩规划

预算:住宿与交通大约花费 3000

需个人购买部分

  • 往返船票(提前一周购买)
  • 一嗨租车(最好提前一周预定)
  • 酒店(记得开发票)

行程规划

Day 1

交通

07:55 ~ 10:35 杭州萧山机场-> 南宁吴?机场 四川航空 3U3183

11:00 ~ 14:00 一嗨租车(需要提前定,自行组队)取车自驾到北海(自驾时长约 2.5h)

一嗨租车没有异地还车费用

15:20~16:40 下午坐船到涠洲岛(船票需提前购买)
17:00 入住涠洲岛客栈 客栈名:沐光海岸客栈

Day 2

涠洲岛上游玩,攻略待定(可以租小电驴)

Day 3

交通

13:3014:50 坐船回北海住银滩附近酒店。
17:00
19:00 可以去银滩赶海

Day 4

10:00 ~ 12:00 租车(需提前定)自驾返回南宁
13:30 ~ 15:45 做飞机返回杭州

参考

  • 机票购买 携程
  • 船票购买 微信小程序(来游吧)
  • 一嗨租车

Linux虚拟内存创建

写了一个脚本方便一键扩容虚拟分区
简单版本

1
2
3
4
5
6
swapoff -a
dd if=/dev/zero of=/swapfile bs=1M count=2000
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
grep SwapTotal /proc/meminfo

复杂版本

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
#!/bin/bash
#

if test $(whoami) != root
then
echo "require root privilege"
exit 1
fi

SWAPFILE_SIZE_GB=8 # 8GB

SWAPFILE_SIZE=$(stat --format="%s" /swapfile)
EXPECT_SIZE=$(($SWAPFILE_SIZE_GB << 30))
if [[ $SWAPFILE_SIZE == $EXPECT_SIZE ]]
then
echo "Swapfile is no need to expand."
exit 0
fi

swapoff -a
dd if=/dev/zero of=/swapfile bs=1G count=${SWAPFILE_SIZE_GB:-8}
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
grep SwapTotal /proc/meminfo

快速配置网络加速

安装必要软件

1
2
3
4
sudo apt install -y autossh tmux tsocks
# autossh用于代替ssh保持会话不中断
# tmux用于持久化会话
# tsocks用于快速设计命令行的socks5代理

修改 tsocks 配置

1
2
3
server = 127.0.0.1
server_type = 5
server_port = 1080

启动 ssh socks5 代理

1
2
3
# -N: do not execute remote commands
# -D 1080: open a socks proxy on local port 1080
autossh -D 0.0.0.0:1080 -N username@remote_host

测试一下

1
2
$ tsocks curl ipinfo.io
# 如果返回的city是目标城市的话就OK

socks5 转 http 代理

1
2
3
4
5
pip3 install "pproxy[accelerated]"

# -R: remote address
# -L: 监听地址,默认是http+socks5://:8080
pproxy -R socks5://localhost:1080

Flask Prometheus监控

简介

就算是在稳定的应用也有一天会崩掉,而且如果一个服务不被监控就很难保证其稳定性。本文介绍了如何使用使用业界最流行的 Prometheus、Grafana 来实现 Flask 应用的监控

  • 应用程序通过 HTTP /metrics 透出数据
  • Prometheus 根据配置文件 prometheus.yml 中获取到的应用节点定时拉数据
  • Grafana 负责做前端展示
  • Alertmanager 负责做通知

其中 Prometheus 由前谷歌工程师在 Soundcloud 上以开源软件的形式进行研发。2012 年创立,目前已经发展到了 2.0

Flask 应用配置

一般 Flask 的部署基本上都是 Gunicorn,这里就不讲别的方案了

  1. 代码修改

app.py 文件中修改为下面的内容

1
2
3
4
5
6
7
8
9
10
11
import flask
from prometheus_flask_exporter.multiprocess import GunicornInternalPrometheusMetrics

app = flask.Flask(__name__)
# 重要是下面这两行
metrics = GunicornInternalPrometheusMetrics(app)
metrics.info('app_info', 'Application info', version='0.1.0')

@app.route("/ruok")
def ruok():
return "imok"

添加 python 库 prometheus_flask_exporter 到 requirements.txt 中

  1. 运行脚本修改

因为 gunicorn 会有多个 worker,为了在多进程环境下通过/metrics 获取到的数据是一致的,需要将数据保存在本地共享

1
2
3
4
5
6
7
#!/bin/bash -ex
#

export PROMETHEUS_MULTIPROC_DIR=$PWD/tmp/prometheus
mkdir -p "$PROMETHEUS_MULTIPROC_DIR"

gunicorn -b 0.0.0.0:7001 -w 2 app:app
  1. 测试
1
2
3
4
5
$curl localhost:7001/ruok # 接口请求一下
imok
$curl localhost:7001/metrics # 查询统计到的数据
# 在请求的最后应该可以看到一句
flask_http_request_total{method="GET",status="200"} 1.0

Prometheus 和 Grafana 安装

就写 Docker 的使用方式吧,比较比较方便

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
version: "3"

volumes:
prometheus-data:
driver: local
grafana-data:
driver: local

services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- /etc/prometheus:/etc/prometheus
- prometheus-data:/prometheus
restart: unless-stopped
command:
- "--config.file=/etc/prometheus/prometheus.yml"

grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
restart: unless-stopped

# 下面这个可选,加上的话需要在prometheus.yml中同步修改
node_exporter:
image: quay.io/prometheus/node-exporter:latest
container_name: node_exporter
command:
- "--path.rootfs=/host"
pid: host
restart: unless-stopped
volumes:
- "/:/host:ro,rslave"

准备一下 prometheus 的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.

# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
# external_labels:
# monitor: 'codelab-monitor'

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
static_configs:
- targets: ["localhost:9090"]

先写到这里吧,等我配置完,再继续写

参考

Miniconda

简介

miniconda 是一款小巧的 python 环境管理工具,安装包 50 多 MB,它能够创建独立的 Python 虚拟环境,并支持特定的 Python 版本创建。并且支持在各种平台安装。
大约相当于 virtualenv + pyenv

安装

直接去官网下载对应的文件 https://docs.conda.io/en/latest/miniconda.html
比如我的电脑是 Mac X86 芯片的

1
2
3
4
5
6
7
8
9
10
11
12
# I like python 3.9
wget https://repo.anaconda.com/miniconda/Miniconda3-py39_23.3.1-0-Linux-x86_64.sh

# -b run install in batch mode (without manual intervention),
# it is expected the license terms (if any) are agreed upon
# -f no error if install prefix already exists
# -h print this help message and exit
# -p PREFIX install prefix, defaults to /home/ubuntu/miniconda3, must not contain spaces.
# -s skip running pre/post-link/install scripts
# -u update an existing installation
# -t run package tests after installation (may install conda-build)
bash Miniconda3-py39_23.3.1-0-Linux-x86_64.sh -b

这个 sh 脚本大的很,既有可读脚本,又附加有二进制
默认安装到 ~/miniconda3 目录,想换别的路径使用-p 指定,比如-p /opt/miniconda3

conda 安装完之后,会默认在当前 shell 的 rc 配置文件中写入 conda 的初始化脚本,并启用 conda 的 base 环境。这样你运行 python 的时候,默认就变成了 conda 的 python。如果希望默认不启用的话,就执行下面的命令

1
2
3
# 默认不开启base环境(可选)
# 影响~/.condarc文件
conda config --set auto_activate_base false

设置软件源

由于 conda 在国外(M 国), 软件速度不是这么快。可以通过设置软件源来加速

1
2
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --set show_channel_urls yes

使用

下面试试创建一个虚拟环境试试,环境名就叫 foo

1
2
3
4
5
6
7
8
9
conda create -n foo python=3.5
# 激活环境
conda activate foo
# 检查python版本
python -V
# 使用pip安装一个库
pip install requests
# 离开当前环境
conda deactivate foo

Docker 上如何安装

1
2
bash Miniconda3-latest-Linux-x86_64.sh -b
$HOME/miniconda3/bin/conda create python=3.7 -n modelscope -y

参考文档

Vue+Flask学习

创建项目

https://cn.vuejs.org/guide/scaling-up/tooling.html

1
npm init vue@latest

建议增加的插件

  • TypeScript
  • Pina
  • Router

创建玩之后,修改一下 vite.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// https://vitejs.dev/config/
export default defineConfig({
// 指定一下输出目录,方便flask使用
build: {
outDir: "../static",
emptyOutDir: true,
},
// proxy一般也要配置一下
server: {
proxy: {
"/api": {
target: "http://localhost:7001",
changeOrigin: true,
},
},
},
//...
});

Flask 需要加上两个路由

1
2
3
4
5
6
7
8
9
10
11
from flask import send_from_directory

app = flask.Flask(__name__)

@app.route("/")
def index():
return send_from_directory("static", "index.html")

@app.route("/<path:path>")
def static_file(path: str):
return send_from_directory("static", path)

这样子访问 Index 主页就可以直接加载 vue 编译出来的文件了

数据库

一般推荐的都是 SQLAlchemy

1
2
sqlalchemy
pymysql
下面是个简单的例子通过DB_URL环境变量传递要连接的地址
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
27
28
from sqlalchemy import create_engine, Column, Integer, String, Boolean, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import os

db_url = os.getenv("DB_URL")
if not db_url:
raise RuntimeError("DB_URL not set")
engine = create_engine(db_url)

Base = declarative_base()
Session = sessionmaker(bind=engine)

def get_session():
session = Session()
return session


class User(Base):
__tablename__ = 'user'

id = Column(Integer, primary_key=True)
gmt_create = Column(DateTime, default=datetime.now)
gmt_modified = Column(DateTime, default=datetime.now, onupdate=datetime.now)
email = Column(String(50), unique=True, nullable=False)
password = Column(String(50))
admin = Column(Boolean)

常用链接