最近云服务器内存不足导致部分服务挂了,这时候可以通过虚拟内存来对内存进行扩容。所谓虚拟内存将部分硬盘当成内存使用,性能会有所下降。虚拟内存=物理内存(RAM)+Swap分区/文件

swap分区/文件

在开启虚拟内存之前,先介绍下swap。

通常linux都会为硬盘分配一个分区用于内存拓展,这个分区就是swap分区。也可以不分区直接创建一个文件来充当swap文件,但是分区的效率会更高一些。

swap分区的作用如下

  • 扩展可用内存:当物理内存(RAM)不足时,系统会将部分不活跃的内存数据临时转移到Swap分区(硬盘上的特定空间),腾出RAM供当前运行的程序使用。
  • 支持休眠:没有Swap分区则无法启用休眠功能。
  • 优化内存管理:即使物理内存充足,Linux内核也会将部分长时间未使用的内存页移至Swap,以提高RAM的利用效率。

开启虚拟内存

首先查看一下系统是否开启了虚拟内存

1
free -h

显示结果如下,Swap部分为0

1
2
3
4
[root@VM-24-9-centos ~]# free -h
total used free shared buff/cache available
Mem: 15G 12G 175M 84M 2.7G 2.4G
Swap: 0G 0G 0G

创建swap文件

1
2
mkdir /swap
dd if=/dev/zero of=/swap/swapfile bs=1024 count=2024288

命令解释:

  • dd:这是一个用于复制和转换文件的命令行工具,常用于磁盘操作。
  • if=/dev/zero:if 表示输入文件(input file)。/dev/zero 是一个特殊的设备文件,读取时会提供无限的空字符(ASCII NULL,即二进制的0)。
  • of=/swap/swapfile:of 表示输出文件(output file)。/swap/swapfile 是目标文件的路径,这里表示在 /swap 目录下创建一个名为 swapfile 的文件。
  • bs=1024:bs 表示块大小(block size),这里设置为1024字节(即1KB)。
  • count=8388608:count 表示要复制的块数。总文件大小 = 块大小 × 块数 = 1024 × 8388608 = 8589934592 字节(约8GB)。

为什么是8GB

Linux 官方推荐的 Swap 空间大小

物理内存 (RAM) 推荐 Swap 大小
≤ 2GB 2 × RAM
2GB – 8GB = RAM
8GB – 64GB 0.5 × RAM(最小 4GB)
≥ 64GB 4GB(仅用于休眠或特殊情况)

格式化swap文件

1
2
3
sudo chmod 600 /swap/swapfile       # 设置权限,防止被篡改
sudo mkswap /swap/swapfile # 格式化为 Swap 文件
sudo swapon /swap/swapfile # 临时启用 Swap

这时候swap文件就已经生效了,使用命令再次查看

1
free -h

显示结果如下

1
2
3
4
[root@VM-24-9-centos ~]# free -h
total used free shared buff/cache available
Mem: 15G 12G 175M 84M 2.7G 2.4G
Swap: 8.0G 1.5G 6.5G

上述操作只是临时生效,重启之后就会失效,如果需要永久生效则需要修改/etc/fstab
在文件末尾添加

1
/swap/swapfile none swap sw 0 0

添加了之后系统启动时,会读取 /etc/fstab,发现这一行配置后,自动执行swapon /swap/swapfile,使该文件成为可用的 Swap 空间。
无需手动运行 swapon 命令,实现永久生效。

各字段解析(6 个字段)

字段 作用
设备/文件 /swap/swapfile 指定 Swap 文件的路径
挂载点 none Swap 不需要挂载点,设为 none
文件系统类型 swap 声明这是一个 Swap 文件
挂载选项 sw 等价于 sw(Swap 专用选项)
dump 备份标志 0 禁用 dump 备份(Swap 无需备份)
fsck 检查顺序 0 禁止 fsck 检查(Swap 不参与文件系统检查)

安装

1
2
3
yum -y update
yum -y install epel-release
yum -y install openvpn easy-rsa firewalld
  • easy-rsa:用于生成证书
  • openvpn:openvpn服务端
  • firewalld:防火墙管理工具

生成相关文件

生成pki文件夹,后续生成的证书等文件在里面

1
2
cd ~
/usr/share/easy-rsa/3/easyrsa init-pki

生成根证书,根证书服务端和客户端都要用到,其作用由两个

  1. 由根证书派生出服务端证书和客户端证书
  2. 对客户端来说,当服务端发送服务端证书过来,可以校验其证书是否合法。
    生成文件ca.crtprivate/ca.key
    1
    /usr/share/easy-rsa/3/easyrsa build-ca nopass

生成交互密钥,生成文件dh.pem

1
/usr/share/easy-rsa/3/easyrsa gen-dh

生成服务端证书以及密钥,生成文件private/vpn-server.keyissued/vpn-server.crt

1
/usr/share/easy-rsa/3/easyrsa build-server-full vpn-server nopass

生成客户端证书以及密钥,生成文件private/vpn-client-01.keyissued/vpn-client-01.crt

1
/usr/share/easy-rsa/3/easyrsa build-client-full vpn-client-01 nopass

生成证书交互列表,生成文件crl.pem

1
/usr/share/easy-rsa/3/easyrsa gen-crl

生成共享密钥,生成文件ta.key

1
openvpn --genkey --secret pki/ta.key

配置服务端

复制文件到/etc/openvpn目录

1
2
3
4
5
6
cp pki/ca.crt /etc/openvpn/ca.crt
cp pki/dh.pem /etc/openvpn/dh.pem
cp pki/issued/vpn-server.crt /etc/openvpn/server.crt
cp pki/private/vpn-server.key /etc/openvpn/server.key
cp pki/ta.key /etc/openvpn/ta.key
cp pki/crl.pem /etc/openvpn/crl.pem

修改配置文件/etc/openvpn/server.conf

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
# Secure OpenVPN Server Config
# Basic Connection Config
dev tun
proto tcp
port 1194
keepalive 10 120
max-clients 5
# Certs,文件名称对应好
ca ca.crt
cert server.crt
key server.key
dh dh.pem
tls-auth ta.key 0
# Ciphers and Hardening
reneg-sec 0
remote-cert-tls client
crl-verify crl.pem
tls-version-min 1.2
cipher AES-256-CBC
auth SHA512
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256
# Drop Privs
user nobody
group nobody
# IP pool,配置vpn网关
server xxx.xxx.100.0 255.255.255.0
topology subnet
ifconfig-pool-persist ipp.txt
client-config-dir client
# Misc
persist-key
persist-tun
comp-lzo
# DHCP Push options force all traffic through VPN and sets DNS servers
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
# Logging
log-append /var/log/openvpn.log
verb 4
duplicate-cn

启动服务端

启动

1
2
systemctl start openvpn@server
systemctl enable openvpn@server

放行 OpenVPN 入网流量与开启 IP 伪装

1
2
3
firewall-cmd --permanent --add-service openvpn
firewall-cmd --permanent --add-masquerade
firewall-cmd --reload

检查是否开启流量转发

1
sysctl -a | grep net.ipv4.ip_forward

确保net.ipv4.ip_forward 等于 1,如果不是,则需要修改一下

1
2
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p

安装客户端

客户端社区版

配置客户端

在安装目录的config文件夹下添加文件

1
2
3
4
cp pki/ca.crt config/ca.crt
cp pki/issued/vpn-client-01.crt config/client.crt
cp pki/private/vpn-client-01.key config/client.key
cp pki/ta.key config/ta.key

修改配置文件config/client.ovpn

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
# Secure OpenVPN Client Config
#viscosity dns full
#viscosity usepeerdns true
#viscosity dhcp true
tls-client
pull
client
dev tun
proto udp
remote 123.123.123.123 1194
redirect-gateway def1
nobind
persist-key
persist-tun
comp-lzo
verb 3
ca ca.crt
cert client.crt
key client.key
tls-auth ta.key 1
remote-cert-tls server
ns-cert-type server
key-direction 1
cipher AES-256-CBC
tls-version-min 1.2
auth SHA512
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256

remote后面填写服务端IP地址和使用的端口号,然后将 vpn-client-01-config 的所有文件拷贝到需要链接的电脑上,即可开始使用。

原理

vpn通信需要加密,对称加密的效率较高,所以服务端和客户端需要持有一把对称加密的密钥来加密消息。那么如何让服务端和客户端持有相同的密钥呢?那就是通过非对称加密的方式发送密钥给对方。流程如下

  1. 服务端和客户端都持有根证书,用于对服务端证书和客户端证书验签。
  2. 客户端请求连接vpn,服务端发送服务端证书给客户端。
  3. 客户端收到服务端证书后使用根证书验签,检验是否有效证书。
  4. 检验通过后,客户端生成对称加密密钥,并使用服务端证书加密,加密过后发送给服务端。
  5. 服务端收到客户端使用服务端证书加密的对称加密密钥,服务端使用服务端密钥对其解密,这样服务端就有了客户端生成的对称加密密钥。
  6. 服务端和客户端使用对称加密密钥加密消息。

其实和https一样,使用tls通信,浏览器内置了根证书,而vpn需要自行生成根证书。

在微服务中,不同的业务使用不同的数据库,然而用户表的使用频率是非常高的,查询的数据通常会带上用户姓名。

所以我尝试定义一个通用的拼接用户姓名的方法,如下

1
<T> void appendUsername(List<T> entities, Function<T, Long> userIdGetter, BiConsumer<T, String> userNameSetter);

这里犯了一个错,appendUsername的实现是在用户表所在的服务,就称系统服务吧,发送给系统服务后,系统服务拼接用户名称后的数据也是存在于远程服务中,这时候还应该将数据返回过来,这就造成了不必要的dubbo通信。还有一个问题是,dubbo不能传递lamda参数,因为lamda接口没有实现序列化。

建议的做法是在,为dubbo包装一层,dubbo只负责查询,包装层负责拼接

  • 系统服务:负责查询用户表
  • 调用服务:引入api包,api包定义了dubbo查询用户的接口,同时还包装了一层,用于对dubbo返回的数据进行处理。

解决

此时需要修改或重新生成grub的配置文件,但是此时无法进入系统,所以需要通过刻录有arch镜像的u盘进入系统修改文件。

准备材料

一个有刻录有archlinux镜像的u盘

进入镜像系统

挂载系统根目录

查看系统分区分配情况,找到根目录的分区

1
fdisk -l

挂载根目录分区到/mnt目录

1
mount /dev/sda3 /mnt

arch-chroot 进入根目录,挂载boot目录

1
arch-chroot /mnt

挂载boot目录,grub相关文件放在boot目录中

1
mount /dev/sda2 /boot

安装并更新grub

1
sudo grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=mygrub

执行以上操作后,会在生成efi-directory/bootloader-id目录,grubx64.efi就安装在该目录中

重新生成grub.cfg文件

1
2
3
4
5
6
sudo vim /etc/default/grub

# 删除导致grub失败的几行
GRUB_DISABLE_SUBMENU=y
GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true
1
grub-mkconfig -o /boot/grub/grub.cfg

扩展

boot的目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.
├── EFI
│   └── mygrub
├── grub
│   ├── fonts
│   ├── grub.cfg
│   ├── grubenv
│   ├── locale
│   ├── themes
│   └── x86_64-efi
├── initramfs-linux-fallback.img
├── initramfs-linux.img
├── initramfs-linux-zen-fallback.img
├── initramfs-linux-zen.img
├── System.map
├── vmlinuz-linux
└── vmlinuz-linux-zen

以上文件/文件夹分类好如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# EFI相关文件
EFI(folder)
grub(folder)

# Kernel Symbol Table
System.map(file)

# linux kernel files
initramfs-linux-fallback.img(file)
initramfs-linux.img(file)
vmlinuz-linux(file)

# linux-zen kernel files
initramfs-linux-zen-fallback.img(file)
initramfs-linux-zen.img(file)
vmlinuz-linux-zen(file)

grub-install

1
sudo grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=mygrub
  • target:efi类型,可使用命令’uname -m’查看,因为我电脑是x86_64所以类型为x86_64-efi
  • efi-directory:EFI系统分区根目录
  • bootloader-id: grub名称
1
2
3
# 执行成功输出
Installing for x86_64-efi platform.
Installation finished. No error reported.
1
2
3
4
5
6
7
8
9
10
# 在--efi-directory目录生成如下文件
EFI
└── mygrub
└── grubx64.efi
grub
├── fonts
├── grubenv
├── locale
├── themes
└── x86_64-efi

grub-mkconfig

1
2
# 自动检测系统的启动分区,如果有安装os-prober,那么还会检测其他可启动的分区,比如windows启动分区,将检测到的信息保存到/boot/grub/grub.cfg
grub-mkconfig -o /boot/grub/grub.cfg
1
2
3
4
5
6
7
8
9
10
11
12
13
# 执行输出
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-linux-zen
Found initrd image: /boot/initramfs-linux-zen.img
Found fallback initrd image(s) in /boot: initramfs-linux-zen-fallback.img
Found linux image: /boot/vmlinuz-linux
Found initrd image: /boot/initramfs-linux.img
Found fallback initrd image(s) in /boot: initramfs-linux-fallback.img
Warning: os-prober will be executed to detect other bootable partitions.
Its output will be used to detect bootable binaries on them and create new boot entries.
Found Windows Boot Manager on /dev/nvme0n1p1@/efi/Microsoft/Boot/bootmgfw.efi
Adding boot menu entry for UEFI Firmware Settings ...
done

ESP(EFI System Partition)是在/boot还是/boot/efi?

EFI分区的格式是fat32,在archlinux中是不关心EFI在/boot或者/boot/efi的

注意:如果grub-install的–efi-directory参数为/boot/efi,那么/boot/efi必须是一个分区根目录,而不是/boot中的一个目录,否则grub引导不生效。

为什么要挂载根目录

既然修改内容只涉及到了/boot目录,为什么还要挂载/目录?

因为/目录有linux的基本环境,比如ls,cd等基础命令,如果/mnt直接挂载boot目录,那么arch-chroot /mnt将会失败。

参考

Grub启动Linux引导到BIOS问题的解决

​ 使用archlinux+i3wm一年有余,总的来说体验不错。主要有以下几点良好的体验。

  • 习惯了使用快捷键在不同的应用之间切换
  • docker的原生态
  • 极低的内存占用率
  • 风扇极少狂转

​ 主要的缺点就是软件生态不行,比如剪辑,聊天,游戏等等。虽然通过一些手段也能将就,但是操作复杂,极其不便。就是你goolge半天,找到了解决方案,然后折腾半天在电脑可以运行了,然后这个方案的操作还很不方便。

​ 刚好最近常看到win11的消息,于是有了体验win11的想法。因为我本身就是win10 + arch双系统,所以我将win10升级为了win11。一开始我以为我能和archlinux系统告别了,体验了几天过后,我又回到了archlinux。

​ 以下是我这几天折腾的一些感受。

想换系统更多是新鲜感作祟

​ 在体验了新系统过后,其实也就那样,但是在体验的前期,总会有“这系统牛逼啊,我以后就用这个了“的想法。

就普通使用来说,windows比linux好,因为软件比系统重要

​ windows就像综合格斗选手,而linux就像一个拳击手。windows是普普通通的水泥路,路上有些坑坑洼洼,但是交通工具完善,大巴、轿车、自行车,linux就像一个赛道级别的柏油路,上面只有赛车,如果你想送货,赛车都没有给你装货的地方。

pacman是Archlinux的软件包管理工具,这篇文章将介绍初次使用pacman需要做什么。

添加国内软件源

默认的软件源在国内使用网速非常慢,这时候就需要配置pacman的软件源。

编辑/etc/pacman.conf文件

1
vim /etc/pacman.conf

添加如下内容

1
2
3
[archlinuxcn]
# 清华源
Server = https://mirrors.sjtug.sjtu.edu.cn/archlinux-cn/$arch

还可以通过refector软件自动获取网速更高的源,参考链接 Arch Linux 更换国内镜像源

初始化

不管软件源是否切换,初次使用pacman需要对pacman进行初始化。

从pacman.conf的配置中同步最新数据库到本地

1
pacman -Syy

安装官方密钥

1
pacman -S --noconfirm archlinux-keyring

下面这行代码用于解决后续步骤可能出现could not be locally signed错误

1
rm -fr /etc/pacman.d/gnupg

生成密钥环

1
pacman-key --init

导入密钥环,访问ArchLinux的官方GPG服务器来下载最新的公钥,
将下载的公钥导入到本地密钥环中

1
pacman-key --populate archlinux

如果添加了国内镜像源还要导入国内镜像源的公钥

1
pacman-key --populate archlinuxcn

GPG(GNU Privacy Guard)密钥环的作用

存储和管理加密密钥

  • 公钥:用于验证软件包签名的公钥。Arch Linux 开发者和包维护者使用他们的私钥签名软件包,用户系统上的 pacman 使用对应的公钥来验证这些签名。
  • 私钥:如果用户需要自己签名软件包或文档,则需要一个私钥。这个私钥需要安全地存储在密钥环中。

常见问题

安装 archlinuxcn-keyring 时报错 “签名是勉强信任的”

GnuPG-2.1 与 pacman 密钥环

key could not locally signed 问题

GnuPG-2.1 与 pacman 密钥环

参考链接

Arch Linux 更换国内镜像源

1
docker-compose --context production --env-file .env.prod up -d --force-recreate

不建议使用docker context对生产环境发布,主要有以下两点的考量。

  • 如果docker-compose包含了构建命令,那么将会在context指定的服务器进行构建,如果服务器是生产环境的话将可能影响业务。
  • 使用context远程构建的时候服务器并不存在docker-compose.yml文件,这意味着生产环境的发布需要依赖docker-compose.yml所在的服务器。

一个Docker镜像就是服务与环境的结合,将我们的服务放到docker镜像提供的环境运行。

这时候有两种方式可以实现。

  • 方式1:编写Dockerfile,在基础镜像上叠加一层形成新的镜像,例如以tomcat为基础镜像,将war复制到镜像内形成一个新的镜像
  • 方式2:不构建新的镜像而是将服务以挂载的形式放到容器内,例如直接运行tomcat镜像,并将war以挂载的形式放到容器内。

两者都能实现相同的功能,那么应该怎么选择呢?以下是我的一些想法

如果你构建的是一个服务,应该使用Dockerfile创建一个新的镜像,这有以下几点考量

逻辑上的新主体

逻辑上,这是一个新的服务,已经发生了质的改变,例如tomcat服务添加war后生成了一个新的服务,这时候不能称为tomcat服务,而是xxx业务服务。

Docker设计理念

从Docker的设计理念来说,Docker就是为了将服务和服务所依赖的环境一起打包,从而避免环境的影响。将服务通过挂载的方式运行显然违背了这个理念,使得环境和服务割裂开来。

功能

从功能来说,挂载是容器和宿主机数据交互的接口,提供配置输入数据,挂载目录输出数据,而不是提供服务。并且Dockerfile可定义服务所需要的环境变量,脚本等,可操作性更强。

docker启动gitlab后的默认密码是什么

不知道,可进入容器执行以下命令,重置密码

1
gitlab-rake "gitlab:password:reset[root]"

gitlab建议放公网吗

不建议,其实任何接口,能不放就不放。

v2ex上的讨论

为何要配置外部访问地址external_url

  • 生成链接,例如仓库克隆地址,不配置的话就是“容器id/仓库.git”,该地址只能容器内部使用。
  • cicd:cicd的runner可能和gitlab不在一台服务器上,runner需要知道gitlab外部访问的地址

编辑配置文件

1
vim /etc/gitlab/gitlab.rb

修改地址

1
external_url 'http://宿主机的ip:7002'
0%