使用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.pngimg.png
在终端中也可以确认自己直接连在远程服务器上。

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

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

登录到容器中

Ctrl + Shift + P打开命令面板,搜索Dev Containers:Attach to Running Containner...
会出现正在运行的容器列表,选择你要进入的容器。
会打开一个新的窗口来尝试进入容器,顺利的话应该会左下角有一个UI代表链接成功。
img2.pngimg2.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的结果
图上是ifconfig的结果,其中红圈为联通云内网ip

校园网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.pngimg.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"

在服务器上方便的跑一次性golang脚本

场景描述

有一个跑的时间非常长的任务,大概需要连续跑20h,这个任务是一次性的,不值得去配置什么CICD

思路

golang手熟所以使用golang去完成了,曾经思考是不是用其他脚本语言会更好?
但是对于这种一次性的需求,显然哪个语言熟练度高就用哪个语言了。
最好的方式应该是使用交叉编译,打包一个可执行文件,扔到服务器上去运行即可。
或者也可以在一个有运行环境的服务器上。

最后手头有一个没有golang环境,但是有docker环境的,不清楚linux版本的测试机器。决定使用docker的方式来运行这个脚本。

配置

  • 任何ssh工具(我是用mobaXterm)
  • 一台具有docker环境的机器

具体过程

登录到运行机器上,先通过ssh工具把代码传到机器上。
然后cd到代码所在的文件夹,直接运行以下语句:

1
docker run -it --rm -v $(pwd):/app -w /app golang:1.20 bash

你将获得一个一次性的,以当前目录挂载到app文件夹下的golang容器,需要注意的是,因为用了rm的选项,一旦退出当前ssh会话,容器就被销毁了。

如果需要一个真正的,后台一直运行的任务,需要用如下命令

1
docker run -d --rm -v $(pwd):/app -w /app golang:1.20 bash -c 'while true; do echo "The script is running at: $(date)"; sleep 1; done'

其中单引号里的语句替换成真正的生产实际语句。

golang的google源没办法使用,记得先换成使用命令换成国内源

1
export GOPROXY=https://goproxy.cn,direct

最后的命令应该组成类似于

1
docker run -d --rm -v $(pwd):/app -w /app golang:1.20 bash -c 'export GOPROXY=https://goproxy.cn,direct;go run main.go'

附上一段我自己用的main.go测试文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
"time"
)

func main() {
for {
// 获取当前时间
currentTime := time.Now().Format(time.RFC3339)
// 打印当前时间
fmt.Printf("The script is running at: %s\n", currentTime)
// 等待 1 秒
time.Sleep(1 * time.Second)
}
}

CSP基础

业务场景

希望能集成一个weda开发出来的页面到开放平台上

实现方式

1
<iframe src="https://lowcode-7gcbyr6cba0c6868-1312402421.tcloudbaseapp.com/app-v9spncjv/production/index" width="600" height="720" frameborder="0" allowfullscreen></iframe>

通过iframe实现,虽然对iframe也不算很了解,但是简单的本地测试没问题

遇到问题

push到生产环境之后,浏览器直接报无法加载,console中类似报错,例子中用的script,实际是iframe

1
Refused to load the script 'http://xxxxx' because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-eval' 'unsafe-inline'

定位问题

之前做等保认证时,好像配置过csp(Content Security Policy),当时不知道具体作用根据需要就配置了下,现在重新梳理下

CSP作用

我们都应该听说过XSS(跨站脚本攻击);它可能是最常见、危害最大的网络安全漏斗。大约十年前吧,W3C 网络应用安全工作组为防御 XSS、点击劫持等代码注入攻击,推荐 CSP 成为计算机安全标准,以阻止恶意内容在受信网页环境中执行;之后几乎所有的现代浏览器都支持了这一策略。

实现方式

配置在响应的标头里或者html网页的meta里的一串字符串,如下所示

1
Content-Security-Policy: style-src 'unsafe-inline' 'self'; script-src blob: 'unsafe-inline' 'self';

我因为要引入https://lowcode-7gcbyr6cba0c6868-1312402421.tcloudbaseapp.com/这个网页作为iframe的源头,
就应该在iframe-src或者default-src中配置这个地址

遇到一些不明确的问题

一开始偷懒不想改nginx里的配置,在meta中直接添加了https://lowcode-7gcbyr6cba0c6868-1312402421.tcloudbaseapp.com/,
但是效果跟预期不一致,不清楚响应的标头和meta里都配置出现了什么问题

最后的解决方式

nginx里的csp直接删掉,下次等保再说吧

参考的网络资源

CSP基础学习

git迁移项目到新命名空间

迁移一个项目到新的命名空间

工作需要迁移了一个项目到新的命名空间,因为项目用到了Gitlab自带的镜像仓库,所以在迁移的时候报错了。
删除镜像仓库中的所有镜像之后,就能够正常转移项目到新的命名空间了。

本地代码仓库指向新的远程仓库

以下为示例操作步骤:

假设当前远程仓库地址为 https://github.com/old-user/old-repo.git,新仓库地址为 https://github.com/new-user/new-repo.git

  1. 查看当前远程仓库地址:

    1
    git remote -v

    输出:

    1
    2
    origin  https://github.com/old-user/old-repo.git (fetch)
    origin https://github.com/old-user/old-repo.git (push)
  2. 修改远程仓库地址:

    1
    git remote set-url origin https://github.com/new-user/new-repo.git
  3. 验证更改:

    1
    git remote -v

    输出应当为:

    1
    2
    origin  https://github.com/new-user/new-repo.git (fetch)
    origin https://github.com/new-user/new-repo.git (push)

完成这些步骤后,你的本地 Git 仓库将会与新的远程仓库地址关联。