简单聊聊我全新的服务器基础架构

已经很久没有更新博客了,想来最近稍稍有了时间,有必要把之前一直没有时间写完的博客更新了

尽管之前我 基于 GitLab、Docker-Compose 和 Harbor 的 CI/CD 实现 中已经实现了一个 CI/CD 流程。但 Docker 本身并不够强大,CI/CD 实现中也十分不优雅。

因此,从 2022 年 2 月的寒假我就决定将我手上大多数服务器推成一个 Kubernetes 集群,并将服务进行迁移。这个工程量是相当巨大的,因此至今还有一小部分服务没有恢复(鸽精本精

然后整个上学期因为架构设计的一些问题,第一版系统架构有着比较严重的问题。因此我一直拖着没有更新博客。

关于 CI/CD 我们在这里不过多阐述,这篇博文主要聊聊新的基础架构。

虚拟化平台

Proxmox VE 7 的 VirtIO 虚拟网卡存在性能问题,对此我进行了一些尝试但没有解决这个问题。
在一位朋友的推荐下,我的第一版方案选择使用 VMware ESXi。并且通过 PCIe 直通将 RAID 控制器直通到 TrueNAS SCALE 虚拟机中实现 NAS。

但这个方案产生了比较严重的时序问题。我们不得不使用 NFSv4 来利用被直通的磁盘空间建立虚拟机。因此在新版 Proxmox VE 解决了虚拟网卡问题之后我决定转回 Proxmox VE,并基于 Proxmox VE 手动实现 NAS 和运行 Kubernetes。

K3S

为了将我的服务器都纳入管理体系,我们使用 WireGuard 将它们放在一起。

启动好 WireGuard 后,我们需要在 /etc/rancher/k3s/config.yaml 中添加:

node-ip: 这里填写 WireGuard 接口的 IP

这里放上 k3s.service 的启动参数:

[Unit]
Description=Lightweight Kubernetes
Documentation=https://k3s.io
Wants=network-online.target

[Install]
WantedBy=multi-user.target

[Service]
Type=notify
KillMode=process
Delegate=yes
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
Restart=always
RestartSec=5s
ExecStartPre=-/sbin/modprobe br_netfilter
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/k3s \
    server \
        '--flannel-backend=none' \
        '--disable=traefik,local-storage' \
        '--disable-network-policy' \
        '--kube-proxy-arg' 'proxy-mode=ipvs' 'masquerade-all=true' \
        '--snapshotter' 'native' \
        '--container-runtime-endpoint' '/run/containerd/containerd.sock' \
        '--node-name' 'ix-truenas'   # 这里的 node-name 是因为我先前使用 TrueNAS SCALE 中的 k3s

这里说明一下,我采用基于 eBPF 的 cilium 作为 CNI,因此 Flannel 和默认的 Network Policy 要进行禁用。同时我们也不希望 Traefik 被 k3s 自动安装。

在 Proxmox VE 中,我们可以直接使用自行安装的 containerd,因此需要设置 container-runtime-endpoint

同时我使用 ZFS 作为文件系统,因此 snapshotter 要选取 native。

存储后端我选择使用 OpenEBS 提供的 zfs-localpv。部署 YAML 如下:

allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    storageclass.kubernetes.io/is-default-class: 默认则 "true"
    storageclass.kubesphere.io/allow-clone: "true"
    storageclass.kubesphere.io/allow-snapshot: "true"
  name: 名称
parameters:
  fstype: zfs
  poolname: 【ZFS Pool 名】/【数据集】
  shared: "yes"
provisioner: zfs.csi.openebs.io
reclaimPolicy: Delete
volumeBindingMode: Immediate

Helm 可以用于安装 Traefik、Cilium、OpenEBS 等。

然后我们可以将更多节点加入 Kubernetes 集群。总之上一张 KubeSphere 面板图。
KubeSphere

NAS

作为 HomeLab 的部分,NAS 自然必不可少。先前我的方案也主要出于 NAS 考虑而采用了 TrueNAS SCALE,但其定制性实在不够好,且遇到系统组件有 bug 时很难处理。

因此,我决定在 Proxmox VE 上直接手动搭建所有 NAS 功能。

初始化 ZFS

尽管该部分先前我基本已经在 TrueNAS SCALE 上完成,但为了方便读者,还是简单介绍一下。

ZFS 使用之前我们首先要创建存储池,即 zpool,一个简单的存储池可以像这样创建:

zpool create -R /mnt/flash flash /dev/disk/by-partuuid/f656034c-bffa-462a-8534-db7fc71972a4

其中,-R 参数指定的是挂载点,flash 是 pool 名,后面可以跟数个块设备。这样我们创建的是一个简单的存储池。

然而 ZFS 自带软件 RAID,因此我们也可以创建 RAIDZ 存储池。例如,我们创建类似 RAID 6 的 RAIDZ-2 存储池,它可以允许两块磁盘离线。

zpool create panda raidz2 【vdevs】

这里我们建议使用 /dev/disk/by-partuuid 下映射的虚拟设备,因为这样由分区 UUID 决定路径可以确保设备路径不会因为一些原因发生变化。

Samba

首先是账号体系,为了实现良好的账号体系,我决定采用 Windows Server 来搭建 Active Directory 域。搭建成功后,我们就可以通过使用它来管理账号体系而不是在 Samba 内进行管理。这里略去 Active Directory 的搭建过程,仅介绍 Samba 加入 AD 域的相关配置。

首先我们安装 samba 和 winbindd 组件。

apt install samba-common winbind libnss-winbind libpam-winbind
pam-auth-update

然后配置 /etc/samba/smb.conf

[global]
        log level = 1
        read raw = Yes
        write raw = Yes
        strict locking = No
        oplocks = yes
        max xmit = 65535
        dead time = 15
        getwd cache = yes
        aio read size = 16384
        aio write size = 16384
        use sendfile = true
        bind interfaces only = Yes
        client ldap sasl wrapping = seal
        disable spoolss = Yes
        dns proxy = No
        domain master = No
        kerberos method = secrets and keytab
        load printers = No
        logging = file
        max log size = 5120
        obey pam restrictions = Yes
        passdb backend = tdbsam:/usr/local/samba/passdb.tdb
        preferred master = Yes
        local master = yes
        printcap name = /dev/null
        realm = 这里是域名
        registry shares = Yes
        map to guest = Bad User
        netbios name = 这里是NAS的NetBIOS名称
        security = ADS
        server min protocol = SMB2
        server multi channel support = No
        server role = member server
        server string = 你的NAS标识信息
        template homedir = 用户主目录,例如 /home/%U
        template shell = /sbin/nologin
        username map = /usr/local/samba/etc/user.map
        winbind cache time = 7200
        winbind enum groups = Yes
        winbind enum users = Yes
        winbind max domain connections = 10
        winbind use default domain = Yes
        workgroup = AD
        wins support = yes
        idmap config ad : backend = rid
        idmap config ad : range = 100000001 - 200000000
        idmap config * : range = 90000001 - 100000000
        idmap config * : backend = tdb
        create mask = 0775
        directory mask = 0775

配置完这个之后,我们就可以加入域了。

net ads join -U 【一个有权限加入域的用户】

加入后 wbinfo --ping-dc 返回包含 succeeded 即代表成功。此时使用 getent passwd 应当能看到域用户。

为了实现 Shadow Copy 功能,我们应当给每个用户一个数据集,然后安装 zfs-auto-snapshot 来自动化创建快照。

然后,我们给出一个 Home Share 的示例。

[Home]
        ea support = Yes
        kernel share modes = No
        mangled names = no
        path = 这里是家路径,例如 /home/%U/
        posix locking = No
        read only = No
        valid users = %U %D\%U
        vfs objects = catia fruit streams_xattr shadow_copy2 acl_xattr recycle io_uring
        map acl inherit = yes
        shadow:snapdir = .zfs/snapshot
        shadow:sort = desc
        shadow:format = zfs-auto-snap_frequent-%Y-%m-%d-%H%M
        fruit:aapl = yes
        fruit:model = Xserve
        recycle:subdir_mode = 0700
        recycle:directory_mode = 0777
        recycle:touch = True
        recycle:versions = True
        recycle:keeptree = True
        recycle:repository = .recycle/%U

但是,这里存在一个问题,就是如何确保 samba 在 AD 域服务启动后再启动。由于我本来就会运行一个脚本用来每分钟执行一些任务,所以:

if (ping -c 1 -w 1 AD域服务器IP 2> /dev/null); then
  systemctl start smbd.service nmbd.service winbind.service && sleep 4
  if [ -n "$(getent passwd 某个AD账户)" ]; then
    # 这里还可以用来写一些 smb 启动后的操作
fi

NFS 和 iSCSI

NFS 和 iSCSI 就比较简单了,只需要安装以下组件:

apt install nfs-common nfs-kernel-server tgt

NFS 相关的配置在 /etc/exports 中,只需要指定路径和参数即可,十分简单,我们主要说说 iSCSI。

iSCSI 的分享是块设备,正好可以很好地和我们的 ZFS 相适应。ZFS 的 zvol 功能可以提供良好的块设备支持。
若要创建一个 zvol,只需要像这样:

zfs create -V 5gb dataset/myzvol

像这样,即可在 /dev/zvol/dataset/myzvol 找到对应的 zvol 块设备。

对于 iSCSI,有两个重要的概念:target 和 LUN。其中一个 target 可以认为是一个虚拟的 SCSI 控制器,LUN 则是这个 SCSI 控制器下面挂接的磁盘。target 名称结构是这样的 iqn.<四位年份>-<两位月份>.<反转域名>:<标识符>

我们可以用 tgtadm 管理我们的 iSCSI,例如创建一个 target:

tgtadm --lld iscsi --op new --mode target --tid 1 --targetname iqn.2018-02.tech.imvictor.iscsi:tgt0

那么这个 target 就在系统中 ID 为 1,并且名称为 iqn.2018-02.tech.imvictor.iscsi:tgt0
要将 LUN 加入 target,只需:

tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 --backing-store /dev/zvol/dataset/myzvol

更多配置可以查看 tgtadm 的配置。


本文的内容大概就在这里了,只是一个简单的介绍,更多内容后面再写。

发表回复

您的电子邮箱地址不会被公开。