sharex和s3的配合

事情起因

最近尝试多写一点blog,写的文字多了自然要追求一下效率。
之前写blog时,图片的相对路径问题一直困扰我。
每次想贴一点图片都要为图片存在哪个目录下,如何写而困扰。

解决方案搜寻

最根本的方法就是使用图床,众所周知图床主要靠绝对文件名和标签来组织图片路径的,所以可以完美避开相对路径的问题。

上传图床

上传图床挺麻烦的,很多图床都要收费。搜索相关讨论时发现了sharex这个神器,截图+上传一体的。下载下来简单试用了一下非常的不错。
看了一下里面支持amazon的S3,想到学校里面提供了s3服务,遂申请了一个桶试试。

sharex配置

直接看图吧(ps:这张图片就是使用这个配置上传的)
sharex配置

展示最终效果

截图之后,url是自动进入剪切板的,然后需要查看这张图片的url也非常方便。
截图之后如何拿到url

使用油猴改变令你不爽的后台管理界面

管理系统改造

下图是我的一个后端管理系统的例子
管理端截图
个人觉得会话标题不太有用,又占用了我宝贵的视野。
除了期待后端系统自己变好以外,用油猴改造后端到自己想要的样子更直接一些。改造好了还能展示给管理系统维护方,直观的看到自己的想法。

油猴代码

用大模型写的,本身不具备啥参考价值。
比较需要关注的字段主要是match的用法,这个代表了作用域。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
// ==UserScript==
// @name 隐藏“会话标题”列(同济后台)
// @namespace http://tampermonkey.net/
// @version 1.2
// @description 在 https://tjtx.tongji.edu.cn/tjtx/log 页面隐藏表格中的“会话标题”列
// @match https://tjtx.tongji.edu.cn/tjtx/*
// @grant none
// ==/UserScript==

(function () {
'use strict';

let targetColumnIndex = -1;

function hideColumnByHeaderText(headerText) {
const tables = document.querySelectorAll('.ant-table table');
tables.forEach(table => {
const headerCells = table.querySelectorAll('thead th');
headerCells.forEach((th, index) => {
if (th.textContent.trim() === headerText) {
targetColumnIndex = index;
th.style.display = 'none';
}
});

if (targetColumnIndex !== -1) {
const rows = table.querySelectorAll('tbody tr');
rows.forEach(row => {
const cells = row.querySelectorAll('td');
if (cells.length > targetColumnIndex) {
cells[targetColumnIndex].style.display = 'none';
}
});
}
});
}

function observeTableChanges() {
const observer = new MutationObserver(() => {
hideColumnByHeaderText('会话标题');
});

observer.observe(document.body, {
childList: true,
subtree: true
});

// 初始执行一次
hideColumnByHeaderText('会话标题');
}

window.addEventListener('load', observeTableChanges);
})();

效果展示

可以看到下图中会话标题不见了
改造后

使用校级问卷平台和企业微信机器人联动

目的

使用校级的问卷平台,因为是一个持续收集的问卷,肉眼可见填的人比较少,希望填报完了能给工作群一个提醒。
工作群里谁有空就处理下。

效果

1、用户填写问卷

填写问卷

2、企业微信群收到提醒

收到提醒

实现步骤

step1 建个机器人

  • 新建或者找到一个企业微信的群聊
  • 右上角···点击之后,选择添加群机器人
  • 点新创建一个机器人
  • 按照引导填写名字等等
  • 拿到Webhook地址,类似于https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=b42a4410-a3e4-xxxx-98cd-8d27c07xxxxxx,把这个地址存好。

step2 建个问卷

使用学校的校级问卷平台问卷平台新建一个问卷。

step3 设置自动化

创建完问卷之后,跳转到问卷设置,找到子菜单投放与分享,点开里面的自动化设置。
点击新增自动化任务,其他按照直觉填写,触发动作写推送到企业微信。
《企业微信机器人Webhook地址》一栏填写上面保存好的地址。其他按照直觉填写。

step4 测试吧

填写一个问卷,如果顺利就能在企业微信群里像我截图的那样看到这个消息啦。

使用vscode编辑远程服务器上容器中的文件

目的

见标题

实现方式

安装vscode插件

推荐安装Remote Development,里面直接包含了远程工作常用的四个组件。
其中Remote-SSH和Dev Containers是我们这次要使用的插件。

Remote-SSH到远程服务器上

Ctrl + Shift + P 打开命令面板,搜索Remote-SSH:Connect to Host....
运行它,第一次运行会提示使用密码来登录。建议使用证书登录免得每次都要输入密码。

ssh配置文件的地址一般是C:\Users\用户\.ssh,填写配置文件,配置文件建议参考下面格式。

1
2
3
4
5
Host 192.168.165.86
HostName 192.168.165.1
Port 2333
User root
IdentityFile ~/.ssh/id_rsa_sde

然后就会新开一个窗口,登录远程服务器上,在vscode左下角应该有如下ui代表链接到远程服务器上。
img.png
在终端中也可以确认自己直接连在远程服务器上。

在远程服务器上查看docker日志

docker ps可以查看到所有的container列表
使用命令docker logs -f sql-backend来实时查看产生的日志

登录到容器中

Ctrl + Shift + P打开命令面板,搜索Dev Containers:Attach to Running Containner...
会出现正在运行的容器列表,选择你要进入的容器。
会打开一个新的窗口来尝试进入容器,顺利的话应该会左下角有一个UI代表链接成功。
img2.png
这时在终端中可以看到已经进入了容器,并且通过打开文件夹或者文件,都可以直接操作容器中的内容了。

SSH配置文件的位置

一般在~/.ssh/config中。可以在vscode中查看。

从零学一套新技术栈

背景

从零学一套新技术栈,记录下需要的时间
先记录自己已经有的技术栈

前端相关

  • html,js,css懂得基本原理,大概能手撸一个单页面
  • vue2原理不太懂,能跟着教程或者照葫芦画瓢撸一个单页面
  • internet相关的基本原理都懂
  • 版本控制熟练
  • 包管理只会最普通的npm
  • 构建工具完全不懂
  • 鉴权协议熟练
  • Web安全原理略懂

这次想学以React为核心的,如何建立一个简单的单页面应用

1、调研什么是React,什么是Next.js,什么是TailwindCSS

  • 时常:2小时
  • 内容:基本上就看看各种论坛,各种博客

2、学习React基础

3、学习Next.js基础

4、暂时搁置

golang时区问题

现象

一个更新程序,打上的时间tag跟当前时间不一致,相差8小时。

原因

golang使用了UTC时间。
我的golang代码运行容器,容器中没有设置时区,所以使用的是UTC时间。

解决

尝试在golang中加入全局的时区设置,但是没有成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
// 加载东八区时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
fmt.Println("Error loading location:", err)
return
}

// 获取当前时间并转换为东八区时区
currentTime := time.Now().In(loc)

// 格式化时间
fmt.Println(currentTime.Format(time.DateTime))
}

会报错 Error loading location: unknown time zone Asia/Shanghai

经过排查,是依赖的镜像alpine:3.18中没有时区文件。
如果需要使用golang中的loadLocation,需要更换镜像。

最后使用debian:bullseye-slim镜像,并在init中,设置时区为Asia/Shanghai,问题解决。

总结

如果不想在dockerfile中安装tzdata设置默认时区,只能在程序启动时手工指定时区。

java时区问题

现象

程序进行迁移时,发现输出的时间差了8个小时

分析

1
2
3
4
5
FROM openjdk:8-alpine

COPY ./target/retire-manage-0.0.1-SNAPSHOT.jar app/retire-manage.jar

ENTRYPOINT ["java", "-jar" , "/app/retire-manage.jar", "-Duser.timezone=GMT+8"]

其实dockerfile中已经指定了”-Duser.timezone=GMT+8”,但是没有生效。
在程序中尝试打印如下日志,发现打印的是GMT的时间

1
2
LocalDateTime now = LocalDateTime.now();
System.out.println("Current time in GMT+8: " + now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

解决

在 Java 代码中显式设置时区

你可以通过以下代码来设置 JVM 的默认时区为日本时区。

在启动类中设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.TimeZone;

@SpringBootApplication
public class Application implements CommandLineRunner {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@Override
public void run(String... args) throws Exception {
// 设置默认时区为 Asia/Tokyo
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
}
}

联通云无法访问自己ip的问题现象和总结

现象 无法访问虚拟机自己的校园网IP

联通云因为是云架构,自己有一套自己的内网IP,可以额外绑定一个校园网IP。
对于虚拟主机自己,如果观察ifconfig返回的网卡结果,可以发现,虚拟主机自己只知道自己的内网IP,是不清楚自己的校园网IP的。
ifconfig的结果
图上是ifconfig的结果,其中红圈为联通云内网ip

校园网ip
上图可以看到我这台虚拟机主机的校园网IP

展示问题复现方式

1
2
3
4
5
6
7
8
9
#启动一个最小化的http服务
docker run -d -p 80:80 nginx:alpine
#如果顺利会展示nginx的welcome界面
curl -m 3 192.168.165.86
#这里因为配置了一个-m 3,3s之内无结果会报一个访问错误
curl -m 3 192.168.165.86
curl: (28) Connection timed out after 3001 milliseconds
#这里因为使用了联通云自己的内网IP,所以也能顺利的拿到nginx的welcome界面
curl -m 3 172.28.1.61

根因归属:我没有那么懂网络,如果按照我个人理解,这一层NAT转换应该联通云来做。

什么场景会触发这个问题

如果因为是用了外部的服务注册与发现框架导致的,那么因为你拿到的ip是校园网ip,自己内部部署了两个以上的微服务,这两个微服务互相访问就会出现问题。
如果同一台机器内部署了多个应用,其中还绑定了不同的域名,就很容易出现这个问题。

解决方法

利用iptables来增加一层本机的NAT,先用下面的命令确认自己现在的转发情况
如果安装过docker会有一个docker的转发,剩下没特殊设置应该是空的

1
iptables -t nat -L OUTPUT

增加一条NAT路由规则

1
2
3
iptables -t nat -A OUTPUT -d 192.168.165.86 -j DNAT --to-destination 127.0.0.1
#再次确认是否成功添加
iptables -t nat -L OUTPUT

操作完之后,测试,应该就可以了

1
2
#这个命令现在会正常返回nginx首页了
curl -m 3 192.168.165.86

把上面的iptable的操作配置为开机自启动

1
2
3
4
5
6
#保存当前的iptable到配置中
iptables-save > /etc/sysconfig/iptables
#然后在系统的启动文件中,附加下面一行
echo "iptables-restore < /etc/sysconfig/iptables" | sudo tee -a /etc/rc.local > /dev/null
#没加过的话,加个可执行权限
chmod +x /etc/rc.local

搭建redis服务器的记录.part1

背景

因为迁移上云的原因,所以需要把老虚拟机关掉,发现有一个老虚拟机上就一个redis什么都没有。
故决定搭建一个公用的redis

搭建公用redis的思路

  • 多租户
  • 集群稳定
  • 配置够顶

创建命令

1
docker run --name redis-main -d -p 6379:6379   -v /root/redis-data:/data   -v /root/redis-conf/redis.acl:/usr/local/etc/redis/redis.acl   redis:7.4.1 redis-server   --aclfile /usr/local/etc/redis/redis.acl   --appendonly yes   --appendfsync everysec   --save "60 1000"   --bind 0.0.0.0```

每个部分的详细解释:

  • -p 6379:6379:这将容器内部的6379端口映射到宿主机的6379端口,使得外部可以通过宿主机的6379端口访问到容器中的Redis服务。
  • -v /root/redis-data:/data:这将宿主机的/root/redis-data目录挂载到容器的/data目录,通常用于持久化存储Redis的数据。
  • -v /root/redis-conf/redis.acl:/usr/local/etc/redis/redis.acl:这将宿主机的/root/redis-conf/redis.acl文件挂载到容器的/usr/local/etc/redis/redis.acl路径,用于配置Redis的ACL(访问控制列表)。
  • redis:7.4.1:指定要使用的Redis镜像的名称和版本,这里是redis镜像的7.4.1版本。
  • redis-server:这是在容器内启动Redis服务的命令。
  • --aclfile /usr/local/etc/redis/redis.acl:指定Redis的ACL配置文件路径。
  • --appendonly yes:启用Redis的AOF(Append Only File)持久化模式,即所有的写操作都会被记录到AOF文件中。
  • --appendfsync everysec:设置AOF持久化的同步策略为每秒同步一次到磁盘。
  • --save "60 1000":设置RDB(Redis Database)持久化的触发条件,即在60秒内至少有1000个键被修改时,自动触发RDB持久化。
  • --bind 0.0.0.0:将Redis服务绑定到所有网络接口,使得容器内的Redis服务可以从任何IP地址访问。

redis.acl格式

1
2
3
user default on >passwordforuser ~* +@all
user user1 on >password123 ~user1:* +@all -@admin
user user2 on >password345 ~user2:* +@all -@admin

以上命令创建了默认用户和user1,user2两个租户。其中默认用户拥有全部的权限,主要包含了@admin系列权限。
@admin权限包含了redis-cli里运行的acl命令系列,可以更改其他人权限。
需要注意的点是租户一定不能赋予@admin权限。

redis实例的查看工具

我是用redis insight,就官方提供的gui界面。里面连上对应的实例之后,左下角有cli窗口,可以进行各种测试。

redis多租户的效果

使用key的时候需要加入账户名同名前缀
用管理员账号查看大概就是下面的效果
img.png

增加一个新的租户的操作

  • 编辑redis.acl文件,增加一行,指定用户名和password
  • docker restart redis-main

后续要做的工作

  • 集群化等后续再说
  • 等需要做集群了再申请多的机器

记录一次docker文件夹占满文件系统的排查过程

故障现象

早晨起来发现gitlab上的一些CICD流程运行失败了。因为未改动CICD脚本,故怀疑是runner机器故障。

关键步骤

登录机器检查磁盘占用

-h``` 直接确认根目录已经100%占用了,故确定是磁盘满了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

#### 检查docker情况
因为对docker的磁盘占用有一些预期,所以直接检查docker是否镜像占用过多。
```docker image prune```
先尝试用上面最温柔的方式清理一些数据,然后```df -h```检查磁盘空间占用变少,重新尝试在gitlab上运行流水线成功,确认磁盘问题。

#### 磁盘占用情况
```bash
[root@TJnetwork ~]# df -h
文件系统 容量 已用 可用 已用% 挂载点
devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs 7.6G 0 7.6G 0% /dev/shm
tmpfs 3.1G 297M 2.8G 10% /run
tmpfs 4.0M 0 4.0M 0% /sys/fs/cgroup
/dev/mapper/openeuler-root 63G 36G 24G 61% /
tmpfs 7.6G 0 7.6G 0% /tmp
/dev/sda2 974M 138M 770M 16% /boot
/dev/sda1 599M 6.2M 593M 2% /boot/efi
/dev/mapper/openeuler-home 31G 414M 29G 2% /home
/dev/sdb 1007G 124G 832G 13% /data

上面是当时的截图。根目录占用很高,data目录占用很低,所以尝试把docker的根目录迁移到/data目录下。

docker配置的更改

保证目标文件夹有权限,我直接使用777权限。 如果文件夹权限不对docker进程会启动失败。
docker的配置文件一般在/etc/docker/daemon.json

1
2
3
4
5
[root@TJnetwork lib]# cat /etc/docker/daemon.json
{
"data-root": "/data/docker-data",
"registry-mirrors" : [ "https://dockerpull.com", "https://register.liberx.info", "https://huecker.io", "https://dockerhub.timeweb.cloud", "https://dockerhub1.beget.com", "https://noohub.ru" ]
}

核心就是data-root的目录,重启之后数据都存在/data/docker-data上了。

后续核心命令

现在虽然硬盘加到1T,但是早晚也会满,弄一个计划任务,每天运行一次docker的清理命令。

1
docker image prune -a --filter "until=720h"

使用linux下常用的定时命令
crontab -e
加入如下规则,每天运行一次docker镜像清理命令
0 0 * * * docker image prune -a --filter "until=720h"