2025-05-01 13:00:00
前些天出门拍了很多照片,回来从 SD 卡导出之后就想有一个可以快速一键分享出去的方法,通过微信传,虽然可以通过笔记一次发送超过 9 张图片,但是还是需要将照片上传到微信的笔记里面,虽然我之前也有使用自己的在线图库,但依然避免不了上传等待的过程,并且如果照片比较大时,等待的时间会很长。所以这几天我一直再想能不能有一个方法可以直接将本地的文件夹共享出来,或者直接让别人将图片一次性发送到我本地。
刚好今天就发现了这样一款使用 Go 语言编写的文件服务器 HomeShare,可以直接将本地文件夹共享出来作为一个私人存储空间。
HomeShare 是一个使用 Go 语言 TypeScript 编写的家用文件服务器,可以直接在本地电脑上运行起来,并通过 Cloudflare 提供的 DDNS,暴露一个域名,即使没有公网 IP,也可以直接通过域名来访问自己的磁盘空间。HomeShare 还支持用户名和密码,来确保数据安全。还可以为文件或者文件夹设置临时分享链接,方便和朋友家人共享。
推荐使用 Docker Compose 来一键部署。
获取项目源码
git clone https://github.com/jugeekuz/HomeShare.git
cd HomeShare
在根目录创建 .env
文件,然后配置 Cloudflare,数据库,存储路径,管理员信息等。
然后打开 traefik/traefik.yml
文件,将邮箱修改为自己的邮箱。
然后构建并运行服务
docker compose up -d --build
等待服务启动,之后就可以在浏览器输入域名进行访问。
2025-04-29 13:00:00
我个人一直使用 Google Calendar 作为日程管理,很久以前也购买了一个叫做 Checker Plus 的 Chrome 插件,可以直接在 Chrome 的插件栏查看日程,并且还可以直接通过自然语言来创建日程,以及在日程之前弹窗提醒,实话说这款插件已经能满足我 90% 的日程安排了。
但是前段时间很偶然发现一款集成 AI 的日历助手,果然在 AI 的时代,什么样的应用都可以利用 AI 重写再写一遍,或者集成 AI 再现荣光。
Dola AI 是一款集成了大语言模型的智能 AI 日历和任务管理助手,希望通过自然语言,为个人和团队提供更智能,更便捷的日程管理体验。
用户可以通过文字,语音,图片等多种方式和 Dola 进行对话沟通,AI 会结合上下文的理解添加,编辑,取消用户日程。
Dola 也可以通过 API 连接 Google Calendar ,Apple Calendar,CalDAV 等来管理日程。
另外用户也可以直接使用 Dola 提供的 Bot 或账号,在自己经常使用的平台上连接使用 Dola,比如 Telegram,WhatsApp,Apple Message,Line 等等 IM,用户并不需要下载单独的 App 来使用。
Dola 还内置了 AI 搜索功能,可以查询天气,娱乐咨询等等。
下面以 Telegram 中使用 Dola 为例,介绍一下如何使用 Dola。
可以在 Dola 官方,点击与 Dola 聊天,然后选择 Telegram ,可以跳转到 Telegram 页面,并添加 Dola 机器人为好友,或者直接在 Telegram 中添加 Dola 机器人为好友。
Dola 支持文字,图片,语音等多模态输入,Dola 会根据内容自动识别添加日程。
可以在 Dola 连天窗口中「设置」或点击菜单,找到日历同步选项。可以选择同步 Google 日历,苹果日历,CalDAV 等,实现多平台日程统一管理。
在添加完日程之后,可以直接使用自然语言查询日程
也可以通过自然语言修改日程,比如
可以将 Dola 添加到群组中,这样就可以非常轻松地实现多人协作管理。
Dola 可以设置智能提醒,比如
支持自定义提醒时间,重复提醒频率,重要性等等。
2025-04-25 13:00:00
我之前一直是在一台机器上使用 Docker compose 安装了 Bitwarden,但是这样存在一个隐患,那就是如果这一台机器宕机了,或者发生任何意外,那么我可能有一段时间无法访问我的所有密码仓库,所以为了避免这样的问题,尤其是在我已经稳定运行 K3s 一段时间之后,我就想着将 Bitwarden 迁移到 K3s 上,并且我希望直接使用 Bitwarden 历史的数据,并且也直接使用原来的域名,密码等等,这样就不需要让我所有的设备重新再登录一遍。那么本文就记录一下我在 K3s 上搭建 Bitwarden ,以及将历史数据迁移到 K3s 中的过程。
Bitwarden 我就不多说明了,是一个开源的密码管理器,而我使用的 vaultwarden/server 则是一个开源的 Rust 编写的 Bitwarden 兼容的后端,Bitwarden 提供了跨平台的客户端,以及浏览器插件,所以可以在任何的平台上非常方便地使用。
Longhorn 更详细的安装步骤可以参考这篇文章,本文简略地再提一下。
节点准备 (如果需要): 根据 Longhorn 的要求,可能需要在每个节点上安装 nfs-common、open-iscsi,并准备用于存储的磁盘或目录。
安装 Longhorn,使用 Helm 安装到 longhorn-system 命名空间。
helm repo add longhorn https://charts.longhorn.io
helm repo update
ubectl create namespace longhorn-system
helm upgrade -i longhorn longhorn/longhorn --namespace longhorn-system
验证安装: 检查 longhorn-system 命名空间中的 Pod 是否都正常运行,并确认 longhorn StorageClass 是否已创建
kubectl -n longhorn-system get pod
kubectl get storageclass
K3s 默认已经安装了 Traefik 作为反向代理和负载均衡器,可以将外部流量路由到集群内部的服务中。
在域名服务提供商那边,为域名 bitwarden.einverne.info
创建一个 A 记录,指向 Traefik 的外部 IP 地址,或者指向集群中任意一台机器 IP。
可以使用 Helm 来安装 cert-manager ,cert-manager 会自动从证书发行商,比如 Let’s Encrypt 等获取以及续订 TLS 证书。
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set installCRDs=true
kubectl create namespace vaultwarden
vaultwarden/server
容器,并挂载 PVC vaultwarden-data
vaultwarden-tls-secret
的 Secret 中。apiVersion: v1
kind: Namespace
metadata:
name: vaultwarden
---
apiVersion: v1
kind: Secret
metadata:
name: vaultwarden-secrets
namespace: vaultwarden
stringData: # 使用 stringData 更易读,Kubernetes 会自动 base64 编码
ADMIN_TOKEN: "" # 替换为你生成的 Admin Token openssl
SMTP_HOST: "smtp.gmail.com" # 替换为你的 SMTP 服务器地址
SMTP_FROM: "@gmail.com" # 替换为发件人邮箱
SMTP_PORT: "587" # 替换为 SMTP 端口 (e.g., 587 for TLS)
SMTP_SSL: "true" # 或 "false", 根据你的 SMTP 服务器设置 TLS/SSL
SMTP_USERNAME: "" # 替换为 SMTP 用户名
SMTP_PASSWORD: "" # 替换为 SMTP 密码
# 注意:DOMAIN, WEBSOCKET_ENABLED, SIGNUPS_ALLOWED 将直接在 Deployment 中设置,因为它们不敏感或依赖于部署本身
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vaultwarden-data
namespace: vaultwarden # 假设部署在 vaultwarden 命名空间
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 512Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: vaultwarden
namespace: vaultwarden
labels:
app: vaultwarden
spec:
replicas: 1
selector:
matchLabels:
app: vaultwarden
template:
metadata:
labels:
app: vaultwarden
spec:
containers:
- name: vaultwarden
image: vaultwarden/server:1.32.7 # 使用官方镜像
ports:
- name: http
containerPort: 80
- name: websocket
containerPort: 3012
env:
- name: DOMAIN # 可选,配置域名
value: "https://bitwarden.einverne.info"
- name: WEBSOCKET_ENABLED
value: "true"
- name: SIGNUPS_ALLOWED
value: "false"
envFrom: # 从 Secret 加载敏感环境变量
- secretRef:
name: vaultwarden-secrets
volumeMounts:
- name: data
mountPath: /data # 挂载持久化存储
- name: localtime
mountPath: /etc/localtime
readOnly: true # 挂载宿主机时区文件
volumes:
- name: data
persistentVolumeClaim:
claimName: vaultwarden-data # 引用之前创建的 PVC
- name: localtime # 定义宿主机路径卷
hostPath:
path: /etc/localtime
type: File # 确保挂载的是文件
---
apiVersion: v1
kind: Service
metadata:
name: vaultwarden-service
namespace: vaultwarden
spec:
selector:
app: vaultwarden
ports:
- name: http # 命名端口以便 IngressRoute 引用
protocol: TCP
port: 80
targetPort: http # 对应 Deployment 中的 containerPort name: http
- name: websocket # 暴露 websocket 端口
protocol: TCP
port: 3012
targetPort: websocket # 对应 Deployment 中的 containerPort name: websocket
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: vaultwarden-certificate
namespace: vaultwarden
spec:
secretName: vaultwarden-tls-secret # K8s Secret 名称,用于存储证书
dnsNames:
- bitwarden.einverne.info # 需要签发证书的域名
issuerRef:
name: letsencrypt-prod # 引用之前创建的 ClusterIssuer
kind: ClusterIssuer
---
apiVersion: traefik.io/v1alpha1 # 使用 Traefik CRD
kind: IngressRoute
metadata:
name: vaultwarden-ingressroute
namespace: vaultwarden
spec:
entryPoints:
- websecure # 指定入口点,通常是 443/HTTPS
routes:
- match: Host(`bitwarden.einverne.info`) && PathPrefix(`/`) # 匹配域名
kind: Rule
services:
- name: vaultwarden-service # 指向 Vaultwarden Service
port: http
# Websocket 流量路由规则
- match: Host(`bitwarden.einverne.info`) && PathPrefix(`/notifications/hub`)
kind: Rule
services:
- name: vaultwarden-service # 指向 Vaultwarden Service
port: websocket # 指向 Service 的 websocket 端口 (3012)
tls:
secretName: vaultwarden-tls-secret # 引用包含证书的 Secret
可以将 YAML 配置文件保存成 vaultwarden-deployment.yaml
文件,然后应用
kubectl apply -f vaultwarden-deployment.yaml
K3s 会根据定义自动创建命名空间,资源等等。
运行如下的命令检查 vaultwarden 命名空间中的 Pod
kubectl get pods -n vaultwarden -w
检查其他资源
kubectl get pods,svc,certificate,ingressroute -n vaultwarden
当 Pod 在运行状态的时候,就可以在浏览器访问定义好的域名。完成这些步骤之后,Bitwarden 就已经在 K3s 集群中搭建好了,通过 Longhorn 进行持久化存储,通过 Traefik 和 cert-manager 来实现了自动化的 HTTPS 访问,如果某个节点发生故障,K3s 会尝试在其他可用节点上重新调度 Vaultwarden Pod,Longhorn 确保数据在节点之间共享。
因为我之前已经安装好了 Bitwarden,所以下面我会讲述一下如何将历史数据通过临时 Pod 恢复到 K3s 集群中由 Longhorn 管理的 vaultwarden-data PersistentVolumeClaim(PVC)中。
核心思路就是先停止 K3s 中的 vaultwarden 应用,然后通过一个临时的 Pod 访问 Longhorn 卷,将本地数据复制进去,然后重启 Vaultwarden 应用。
bw-data
已经备份好虽然目标是为了恢复旧的数据,但是最好还是先用 Longhorn UI 或者 kubectl 创建当前 vaultwarden-data PV 的快照或者备份,防止恢复过程中的问题。
为了安全地修改 PV 内容,停止当前正在使用 PV 的 Pod,将 Vaultwarden Deployment 缩容到 0 个副本。
kubectl scale deployment vaultwarden --replicas=0 -n vaultwarden
确认 Pod 已经被终止
kubectl get pods -n vaultwarden
此时应该没有 Pod 在运行。
创建一个简单的 Pod,例如使用 busybox 或者 alpine 镜像,并挂载 vaultwarden-data
PVC,将这个 Pod 作为数据传输的中转站。
apiVersion: v1
kind: Pod
metadata:
name: restore-helper
namespace: vaultwarden
spec:
volumes:
- name: vaultwarden-storage
persistentVolumeClaim:
claimName: vaultwarden-data # 确保这个名称与你的 PVC 名称一致
containers:
- name: helper
image: busybox:latest # 使用一个包含基本工具的轻量级镜像
command: ["/bin/sh", "-c", "sleep 3600"] # 让 Pod 持续运行一段时间
volumeMounts:
- name: vaultwarden-storage
mountPath: /restore-data # 将 PVC 挂载到 Pod 内的 /restore-data 目录
# 可选:如果 Vaultwarden 需要特定用户 ID 运行,可以在这里设置 securityContext
# securityContext:
# runAsUser: 1000 # 示例 User ID,根据 Vaultwarden 容器实际情况调整
# runAsGroup: 1000 # 示例 Group ID
# fsGroup: 1000 # 确保挂载卷的文件系统组权限正确
将上述配置保存成 restore-helper-pod.yaml
,然后应用这个 YAML 创建 Pod
kubectl apply -f restore-helper-pod.yaml -n vaultwarden
等待 Pod 编程 Running 状态
kubectl get pods -n vaultwarden
首先找到本地通过 Docker Compose 部署的 Bitwarden 挂在的路径,我一般会放在 HOME 目录下,假设是 ~/bitwarden
目录
sudo tar czvf - /home/einverne/bitwarden | ssh [email protected] "cat > /home/einverne/bitwarden-2025.tgz"
然后解压文件,假设 Bitwarden 的数据在 ~/bitwarden/bw-data
下。
# 首先检查一下当前数据
kubectl exec -n vaultwarden restore-helper -- ls -al /restore-data/
# 清空目标目录
kubectl exec -n vaultwarden restore-helper -- rm -rf /restore-data/*
# 复制本地数据到 Pod 内的卷挂载点
# 注意:源路径末尾的 "." 表示复制目录内容,而不是目录本身
kubectl cp ~/bitwarden/bw-data/. restore-helper:/restore-data/ -n vaultwarden
最后再运行验证一下数据是否已经复制,应该可以看到 db.sqlite3
, config.json
, attachments
, sends
, rsa_key.*
等文件。
根据迁移指南,恢复数据后需要移除旧的 RSA 密钥对,让 Vaultwarden 在启动时重新生成。在临时 Pod 中执行此操作:
kubectl exec -n vaultwarden restore-helper -- rm -f /restore-data/rsa_key.pem /restore-data/rsa_key.pub.pem
数据复制和清理完成后,删除 restore-helper
Pod:
kubectl delete pod restore-helper -n vaultwarden
将 Vaultwarden Deployment 扩容到 1 个副本
kubectl scale deployment vaultwarden --replicas=1 -n vaultwarden
观察 Pod 启动情况
kubectl get pods -n vaultwarden -w
kubectl logs -n vaultwarden -l app=vaultwarden -f # 检查日志是否有错误
最后,验证数据恢复,通过域名访问实例,使用之前的用户名和密码访问,检查密码库,设置,组织等等是否恢复。
# 获取 secret
kubectl get secret vaultwarden-secrets -n vaultwarden -o yaml
# 手动重启
kubectl rollout restart deployment vaultwarden -n vaultwarden
如果访问没有问题,可以在 Cloudflare 上(如果使用的话),配置多个 A 记录分别指向集群中的公网 IP,完成 Bitwarden 高可用的配置。
2025-04-24 13:00:00
给 K3s 中的节点添加标签并实现 Pod 调度是一个非常常见的需求,特别是当你希望某些 Pod 只在特定地理位置的节点,比如美国,日本,上运行的时候。
可以使用 kubectl
命令来为节点添加标签。
kubectl label nodes <node-name> <label-key>=<label-value>
kubectl label nodes <node-name> <label-key>=<label-value> <label2>=<value2>
例如,给节点添加地理位置标签
kubectl label nodes k3s-node-1 location=jp
可以通过如下的命令来验证标签是否添加成功
kubectl get nodes --show-labels
输出的结果,将显示节点信息和对应的标签内容。
如果希望在节点首次加入集群时就添加标签,可以在启动 K3s Agent 的时候使用 --node-label
参数。
k3s agent --node-label location=jp
注意,这种方式只能在节点首次注册的时候添加,之后不能通过重新运行 K3s 来修改。
有几种方法可以确保 Pod 只在特定标签的节点上运行。
在 Pod 配置文件中添加 nodeSelector
字段
apiVersion: v1
kind: Pod
metadata:
name: nginx-jp
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
location: jp
这个 Pod 将只会被调度到带有 location=jp 标签的节点上。同样 nodeSelector 还可以指定多个标签。
apiVersion: v1
kind: Pod
metadata:
name: nginx-jp
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
location: jp
label2: value2
Node Affinity 提供了比 nodeSelector 更强大的语法:
apiVersion: v1
kind: Pod
metadata:
name: nginx-jp
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: location
operator: In
values:
- jp
containers:
- name: nginx
image: nginx
这个配置使用 requiredDuringSchedulingIgnoredDuringExecution 确保 Pod 只会被调度到带有 location=jp 标签的节点上。
如果你想设置优先级而不是强制要求,可以使用preferredDuringSchedulingIgnoredDuringExecution
apiVersion: v1
kind: Pod
metadata:
name: nginx-jp-preferred
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: location
operator: In
values:
- jp
containers:
- name: nginx
image: nginx
调度器会尝试将 Pod 放在日本的节点上,但如果不可能,它也会考虑其他节点。
如果需要删除节点上的标签,可以使用如下的命令,在标签键后面加上减号”-“:
kubectl label nodes <node-name> <label-key>-
比如
kubectl label nodes k3s-node-1 location-
修改现有的标签,可以使用 --overwirte
参数
kubectl label nodes <ndoe-name> <label-key>=<label-value> --overwrite
比如
kubectl label nodes k3s-node-1 location=other --overwrite
通过以上方法,可以有效地管理 K3s 集群中的节点标签,并确保特定的 Pod 只在特定地理位置的节点上运行,从而满足地理位置相关的业务需求。
2025-04-24 13:00:00
前两天介绍过永久免费的 Claw Cloud Run,本文将介绍一下如何使用 Claw Cloud Run 来免费部署一个 Gemini API 反向代理服务。
配置应用信息
ghcr.io/wyeeeee/hajimi:latest
#基础部分
#设置一个你自己的访问密码
PASSWORD=123
#配置时区
TZ=Asia/Shanghai
#ai studio部分
#将key1,key2,key3等替换为你真正拥有的gemini api key
GEMINI_API_KEYS=key1,key2,key3
#每分钟最大请求数
MAX_REQUESTS_PER_MINUTE=30
#每天每个 IP 最大请求数
MAX_REQUESTS_PER_DAY_PER_IP=600
#是否启用假流式传输
FAKE_STREAMING=true
#单api 24小时最大使用次数
API_KEY_DAILY_LIMIT=100
#空响应重试次数
MAX_EMPTY_RESPONSES=5
#是否启用伪装信息
RANDOM_STRING=true
#伪装信息长度
RANDOM_STRING_LENGTH=5
#默认的并发请求数
CONCURRENT_REQUESTS=1
#当请求失败时增加的并发请求数
INCREASE_CONCURRENT_ON_FAILURE=0
允许的最大并发请求数
MAX_CONCURRENT_REQUESTS=3
#是否启用联网模式(联网模式有严格的审核)
SEARCH_MODE=false
#联网模式提示词(用英文单引号包裹提示词)
SEARCH_PROMPT='(使用搜索工具联网搜索,需要在content中结合搜索内容)'
#vertex部分(如果您不需要vertex或不知道vertex是什么,无需配置这些内容)
#是否启用vertex
ENABLE_VERTEX=false
#vertex ai 凭证
GOOGLE_CREDENTIALS_JSON=''
大部分设置都拥有默认值,只需要填写拥有的 gemini key 到对应位置即可
点击 claw 的 Add environment variable将 txt 文件内容复制并粘贴进去
等待部署完成 等待应用状态 (Status
) 变为 Running,这表示部署已成功。
获取反代地址并使用**
访问前端界面
把刚刚复制的链接地址在浏览器中打开,就能看到如图所示的前端界面。
2025-04-22 13:00:00
IT Tools 是一个开源的工具集,包含了非常多好用的工具,Token 生成,Hash 生成,UUID 生成,加密解密,BIP39 passphrase 生成,Hmac 生成,RSA 密钥生成,Password 生成,PDF 签名检查,日期转换,Base64 转换,Unicode,ASCII,YAML,JSON 等等非常多有用的工具。
今天这篇文章就以 IT Tools 为例来介绍一下在 K3s 上部署这样一个无状态的服务,并且通过 Traefik 配置域名访问 K3s 内部服务。
在安装之前,需要确保
获取第三方的 Chart
helm repo add jeffresc https://charts.jeffresc.dev
helm repo update
helm install it-tools jeffresc/it-tools
直接安装
helm install it-tools jeffresc/it-tools -n it-tools
卸载命令
helm uninstall it-tools -n it-tools
如果为了测试,可以直接执行下面的命令,通过临时端口转发来测试服务是否正常。
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=it-tools,app.kubernetes.io/instance=it-tools" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
如果您只是临时需要从其他集群外部访问,可以在port-forward
命令中添加--address
参数:
kubectl --namespace it-tools port-forward $POD_NAME 8080:$CONTAINER_PORT --address 0.0.0.0
这样可以让 port-forward 监听所有网络接口,而不仅仅是 localhost。但请注意,这种方法不是生产环境的推荐做法,因为它依赖于保持 kubectl 命令运行。
为了让服务可以向集群外部提供,这里有几个方法
[[Kubernetes NodePort]] 可以创建 NodePort 类型的 Service
❯ cat it-tools-service.yml
apiVersion: v1
kind: Service
metadata:
name: it-tools-nodeport
namespace: default
spec:
type: NodePort
selector:
app.kubernetes.io/name: it-tools
app.kubernetes.io/instance: it-tools
ports:
- port: 8080
targetPort: 8080
nodePort: 30080 # 可选,不指定会随机分配30000-32767之间的端口
应用此配置之后,可以通过 http://节点:30080 来访问应用。K3s 会在每个节点上开放 30080 端口,访问任一一个节点的 IP 加上端口都可以正常访问该服务。
[[Kubernetes Ingress]]
如果想通过域名来访问应用,可以配置 Ingress,K3s 默认安装了 Traefik 作为 Ingress 控制器。
❯ cat it-tools-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: it-tools-ingress
namespace: default
annotations:
# 可选:添加Traefik特定的注解
traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
rules:
- host: tools.einverne.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: it-tools
port:
number: 8080
tls:
- hosts:
- it-tools.einverne.info
secretName: it-tools-tls-secret
这样,当配置了 DNS A 记录指向 K3s 节点的 IP 地址,就可以通过 http://tools.einverne.info 来访问应用。Traefik 会自动申请证书,配置证书。
对于生产环境,建议使用 LoadBalancer 或 Ingress 方式,这样可以确保服务的稳定性和可扩展性。如果您的应用需要特定的 TCP/UDP 端口(不仅仅是 HTTP/HTTPS),LoadBalancer 是更好的选择。
检查证书是否成功颁发
kubectl get certificates -n default
当证书状态显示为Ready: True
时,表示证书已成功颁发并可以使用。