jinmialuo’s blog

I am what I am

在 Windows 上实现 Vim 远程剪切板

前言 我本地是一台 Windows 11 的 ThinkPad T480,开发环境运行在一台 Linux 服务器上,基于 SSH 的方式进行访问和控制。我希望在 Linux 服务器的 Vim 中,通过 y 复制代码到本地 Windows 11 的剪切板内,通过 p 将本地 Windows 11 剪切板中最新的内容黏贴到服务器的 vim 内。本文将提供一种低成本的解决方案。 思路 Windows 的 WSL 支持直接运行 Linux X11 GUI 程序,并且在 Linux X11 GUI 中,是可以直接访问 Windows 本地的剪切板的。这意味着在 WSL 内,我们可以通过 SSH 的 X11Forward 将远端的 X11 转发到 WSL 内,这样 Linux 服务器上的 X11 GUI 程序就可以经 WSL 中转,访问 Windows 11 本地的剪切板了。 因此,我们只需: 基于 WSL 来登录 Linux 服务器 开启 SSH 的 X11Forward 将 Linux 服务器的 Vim 包用 GVim 包替换 即可。...

十一月 28, 2023

源码阅读:Miniflux

前言 我从 9 月的 27 号开始阅读 Miniflux 项目的代码,阅读成熟的开源项目就像在阅读一本适合自己的书,节奏虽慢但充实。因为快接近阅读这个项目代码的尾声,故整理这篇博文作为过程的记录和分享。 开始之前简单介绍一下 Miniflux。Miniflux 是一个开源的 RSS 订阅服务实现,类似 Tiny Tiny RSS,使用过 RSS 订阅服务的朋友应该不陌生,部署 Miniflux 服务相当于拥有了自己的 Feedly 服务。该项目是基于 Go 和 Vanilla JS (没有使用任何框架的原生 JavaScript API) 的 SaaS APP 实现,提倡尽可能少的外部依赖,简单可维护易扩展,所以这个项目会有很多基础功能的实现(这些细节在参与 Web 框架本身的开发时才会涉及,平时基于 Web 框架的项目开发,比如 Django/Rails 会自带这些功能,直接用就可以)。 本文会通过少量的代码,记录阅读过程中发现的有趣的点,如果你对这些点感兴趣,再去阅读代码即可。我更希望这篇博客像在介绍一本书,让你知道这本书哪里有趣,然后你再自己去判断适不适合你,或者有哪些点适合你。读书不一定要整本通读,发现感兴趣的点,只读感兴趣的部分,也是一种不错的选择。 软件工程应该是件有趣的事,或者说,作为从业者我们应该去发现那些有趣的点,穿透高度抽象带来的认知迷雾。 内容 不使用 ORM 如何实现数据库抽象 如果你想在项目内使用 Golang 标准库的 database/sql 来封装 SQL 操作,可以参考。 Miniflux 中的数据库表的管理,代码分成两类,第一类是对这个表的 SQL 操作的封装,在 storage 包内,不同的表对应不同的 storage 文件夹内的文件,比如 users 表对应 user.go 文件,user.go 中一个操作: // SetLastLogin updates the last login date of a user....

十月 22, 2023

Python 枚举模块的实现

前言 为什么看 Python 枚举模块实现?Python 是面向对象(OOP)的语言,标准库中的枚举模块的实现涉及很多 OOP 的概念,比如:mixin、鸭子类型、元类、魔法函数等。阅读枚举模块的源码是了解面向对象编程一个比较有效的方式。本文基于 CPython 的 3.11 分支,对应的 Python 版本是 3.11.4 版本。 会包含如下内容: 枚举类 鸭子类型 魔法函数 协议 装饰器 描述符类 元类 概念 枚举类 枚举类的成员是枚举类的特殊实例。也就是说枚举类的属性名不变,值变成了枚举类的一个特殊实例。在枚举类中,原先定义的属性名和属性值被存储到该特殊实例的 name 属性和 value 属性内。 from enum import Enum class Role(Enum): ADMIN = 'admin' DEVELOPER = 'developer' GUEST = 'guest' print(type(Role.ADMIN)) # 输出: <enum 'Role'> # 原本的属性名被更新为一个 Role 的实例 print(Role.ADMIN.name) # 'ADMIN' print(Role.ADMIN.value) # 'admin' # 原先属性的名和值被保存到特殊实例的 name 属性和 value 属性内 枚举类的用法 在 Python Web 开发中,用户权限的管理和判断会通枚举类来实现。...

七月 13, 2023

IAM 服务的部署和整合

前言 这篇文章是阅读 Arch Linux 团队部署、管理、整合身份与访问管理( Identity and Access Management)服务相关代码的笔记,会包含: 如何部署和管理 Keycloak 服务:Ansible 相关 如何管理 Keycloak 上的用户和权限:Terraform 相关 如何将 Web 站点接入通过 Keycloak:OAuth2.0、OIDC、Python、Authlib 相关 如何基于 Keycloak 中的信息,实现权限管理:装饰器和枚举类相关 IAM 服务 有什么用 先说说没有 IAM 服务会有什么问题。 一般团队内会有多种服务,比如:GitLab、Jira、Confluence、MatterMost、Grafana、Kibana、Argo 等,会产生的问题: 每个团队成员入职/离职时,需在每个服务上添加/删除账号 每个团队成员使用服务时,需在每个服务上输入一遍账号密码 每个团队成员需要管理多个服务的密码,如果密码泄漏且相同时,每个服务需要手动修改一遍 IAM 服务的作用: 统一的权限管理。授权服务器可以管理用户对各个应用的访问权限,例如,哪些用户可以访问哪些应用,用户在各个应用中的角色和权限是什么,另外用户账号的增删也只需要一遍。 单点登录。用户只需要在授权服务器上进行一次登录,就可以访问所有连接到该授权服务器的应用。提高用户体验,减少了用户需要记住多个凭证的负担。 降低业务复杂度。授权码流程可以将认证功能分离出业务代码,业务代码只需根据 OAuth2.0、OIDC 获取到的用户信息,更新和管理用户权限相关的用户表,而无需再每个业务内实现2FA、验证码等验证逻辑。 微服务改造的基础。微服务旨在将业务拆分成独立小服务(独立的数据库)从而降低复杂度、提高容错率和扩展性。IAM 在微服务架构中为各个微服务提供了集中、统一的身份验证和权限管理,从而实现安全性,减少重复验证的开销,并提高系统的可维护性和扩展性。 Keycloak 是什么 Keycloak 是 RedHat 开源的身份认证和授权服务实现。通过部署 Keycloak,我们就可以拥有自己的 IAM 服务,Arch Linux 团队的 IAM 服务就是基于 Keycloak。 Keycloak 的部署 Arch Linux 团队的 Keycloak 通过 Ansible 进行部署和管理,相关的 Playbook 和 Role 在这里:...

七月 9, 2023

备份系统的实践和思考

前言 最近借鉴 Arch Linux 的 infrastructure 仓库中的 Ansible 代码,部署了本地基础设施用的备份系统,本文是对这个过程中的记录和思考。会包含实施前的考量,具体如何实施,以及备份系统本身如何监控。 备份系统 采用什么方案 采用了 borg 作为备份的解决方案。borg 是一个支持去重、压缩、加密的备份程序。 关于加密的意义: 备份系统本质上是将本地的数据拷贝一份存储到其他位置。备份加密指的是将备份的数据,在发送到其他位置前做一次加密,因为备份一般被存储在不可信的第三方,比如 Aliyun、AWS 的对象存储,所以需要加密。 关于去重和压缩的意义: 第三方存储一般是按照使用空间的大小收费的,占用更少的空间意味着更低的成本。 borg 创建备份的时候,会将大文件分为小的分块(chunk),chunk 的大小根据内容动态调整,每个 chunk 有一个哈希值用于去重,哈希值相同的块只存储一份,最后压缩得到的数据大小才是最终的备份大小。 如图所示,Original size 即原始大小 226.77G 的数据,Compressed size 压缩后的大小为 138.41G,Deduplicated size 去重后的大小为 56.10 GB,实际上每月只需支付 56.10 GB 数据的存储费用。 采用什么备份规则 备份是针对整个系统的,第一次是整个系统的全量备份,后续的备份是 borg chunking 机制提供的增量备份。 具体的备份规则见 Arch Linux infrastructure 仓库的 borg-backup.sh.j2 文件 大概的内容如下: 判断需要备份的服务器上是否有数据库类的服务,如果有,基于官方的 dump 工具创建每个数据库的 dump 文件(确保数据库数据的一致性、版本兼容性) 判断 filesystem 是否是 btrfs 如果:服务器根目录是基于 btrfs 文件系统。由于 btrfs 制作快照的时候,子卷的快照是不会包含所有内嵌子卷的内容的,因此: 遍历 / 及其下面所有的子卷 在所有子卷内制作快照。比如:/ 的快照就是 /backup-snapshot,子卷 /home/jinmiaoluo 的快照就是 /home/jinmiaoluo/backup-snapshot 把所有子卷的快照通过 mount -o bind 映射到 /backup 内,比如: 子卷 / 的快照 /backup-snapshot 对应的路径就是 /backup 子卷 /home/jinmiaoluo 的快照 /home/jinmiaoluo/backup-snapshot 对应的路径就是 /backup/home/jinmiaoluo 对 /backup 创建备份,即实现了对整个系统进行备份的目的 否则:服务器根目录不是基于 btrfs 文件系统。直接对 / 创建备份 基于 systemd timer 每天发起 1 次备份操作 需要指出的是:快照可以快速的实现本地的状态留存,就像拍了一张相片一样。但在创建远端的系统备份的时候,则是对整个系统的一次比对和拷贝。好在通过 borg chunking 机制,实际上从第二次开始,会被压缩、加密、发送并存储到远端的内容只是修改的内容,因此,实际的内网带宽压力并不大。比对、压缩的操作是在发起备份的服务器上进行的,这也在一定程度上把计算的负载很好的分摊了下去,即使是对一定规模的服务器集群进行备份,也不会对 borg 的服务端产生计算压力,borg 服务端只要准备好足够的存储空间即可。...

六月 27, 2023

Terraform 和云资源管理

前言 过去一年在前公司(电信)写了很多基础设施的自动化的实现,主要是通过 Ansible 将以往手动部署的中间件代码化,实现中间件的高效部署和持续运维。但像云服务器,域名等云资源,还是通过人工在控制台点点点来完成的。过去新机房的建设,近万台服务器,全是靠人工点出来的。 业界已经有成熟的工具:Terraform,国内的云公司比如:阿里云和腾讯云,也都有完善的支持了。这里记录一下 Arch Linux DevOps 团队的实现和我本地的实践。 本文会包含: 云资源如何代码化 Terraform 代码如何实现本地 AK、SK 等加密信息的加密存储 IaC & Terraform Terraform 介绍 Terraform 是声明式的(类似 K8s),基于自己的描述语言:HCL。比如有 10 台服务器,那么在存放 HCL 的基础设施代码仓库里,就会有 10 台服务器对应的安全组/规格/镜像的声明(当然,为了避免重复代码,会使用模块进行抽象)。 Terraform HCL 只声明必要的项,一些非必要的项,比如实例的 ID,镜像的 ID,会作为状态,存放在一个叫 state backend 的地方,通过本地的 HCL 代码配合 state backend 中的数据一起,将云资源所有信息声明出来。 默认 state backend 是执行者本地的一份 tfstate 结尾的文件。本地文件会导致一个问题,如果其他人想要获得当前所有云设施的最新状态,就需要跟之前的执行者索取他本地最新的 state 文件。因此,团队内一般会使用 remote state backend,这可以是一个数据库或者 S3(AWS 对象存储服务)。比如我本地就是用 PostgreSQL 来作为 remote state backend。所有基础设施代码的贡献者,确保能安全的访问到 PG 和对象存储即可。 即使使用了 remote state backend,本地还是会有 tfstate 文件,但将只包含 remote state backend 的连接信息,不包含云资源的补充信息。本地 tfstate 文件的信息在初始化 Terraform 时写入,比如我本地的 Terraform 首次初始化的命令如下:...

二月 15, 2023

基础设施代码化与加密

前言 Arch Linux 和 Rust 这两个开源组织的基础设施代码是开源的,那么,在实现基础设施代码开源的时候,如何保证私密信息安全呢? 实现在这里: Arch Linux infrastructure rust simpleinfra Arch Linux 使用 ansible-vault 和 gnupg 来管理私密信息,包括 Terraform 的 ak/sk/token。 Rust Team 使用 aws sts 实现权限管理。使用 aws ssm 来管理 Ansible 和 Terraform 中的私密信息。 Terraform 的 state 保存在 S3 上。 Arch Linux 团队的实践 关于 Ansible 的部分 Arch Linux 是通过 ansible.cfg 中的: vault_identity_list 来实现。 vault_identity_list 是一个逗号分隔的数组,可以传入多份包含密码的文件, 也可以传入多份可执行文件(该文件需要具备可执行权限),会是这样的形式: default@misc/vault-keyring-client.sh,super@misc/vault-keyring-client.sh 实际含义是,依次用如下的形式调用可执行文件: vault-keyring-client.sh --vault-id default vault-keyring-client.sh --vault-id super 只要可执行的脚本将密钥打印到标准输出即可。因此,我们就可以: 将密钥通过 gnupg 加密到一份文件内,存储到 gitlab 上,通过 bash 封装 gnupg 进行解密 将密钥存放在 1password,通过 bash 脚本封装 1password 命令行解密 Arch Linux 团队用的就是第一种方案,见:...

二月 7, 2023

自建虚拟化中的 virsh 基本用法和技巧

我的基础设施环境主要靠我家局域网内一台 7x24 小时运行的工作站支撑,我平时使用的基础设施,比如:gitlab、gitlab-runner、jira、password-store 甚至是现在你正在访问的博客站点,都是运行在这台工作站上的。这台工作站基于 Arch Linux,部署了 libvirt 虚拟化,不同的基础设施运行在彼此独立的虚拟机内。 下面简单介绍一下我平时是如何使用这个虚拟化的。 关于网络 我的虚拟化使用的是 bridge 网络模式,所以任何一台虚拟机都可以直接拿到局域网 IP,如何配置可参考:archwiki systemd-networkd#bridge-interface 关于系统和镜像 我的所有虚拟机也是 Arch Linux 系统。在虚拟化的宿主机上准备了一个 Arch Linux 虚拟机作为模板(模板镜像的准备使用了 virt-sysprep 这个工具),其他的虚拟机的镜像都是从模板镜像克隆出来的。虚拟机的镜像文件来自 Arch 官方,下载地址如下:Arch-Linux-x86_64-basic.qcow2 操作如下: 列出所有的虚拟机 virsh list --all 模板机关机备用 virsh shutdown template 基于模板机(我这里的模板机名字是:template)克隆出所需的主机 virt-clone --auto-clone --original template --name demo1 virt-clone --auto-clone --original template --name demo2 virt-clone --auto-clone --original template --name demo3 镜像准备,避免 machine-id、ssh-host-key 等冲突问题(这步是必须的) sudo virt-sysprep -d demo1 sudo virt-sysprep -d demo2 sudo virt-sysprep -d demo3 [可选] 为了后面能够通过 SSH 免密登录,所以在准备镜像的时候,我会顺便注入我的公钥,命令如下:...

一月 27, 2023

可持续运维:质量、安全、效率

前言 过去手动部署 1 个高可用 apollo 集群并验证往往需要 1 ~ 2 个工作日,现在1 个工作日(7小时)可以部署 24 套,部署一套只需 18 分钟,并且所有集群都是生产就绪的,如何实现的? TL;DR 基于 vscode 的统一开发环境 基于 libvirtd 的配套测试环境 基于 wireguard 的私网 gitlab 和生产服务器集群加密通信虚拟局域网 基于 gitlab 的多分支开发模式 基于 gitlab ci 的运维代码自动同步 统一的开发环境 & 可重复的测试环境 在我司办公区机房有一台 Linux 物理服务器,我使用这台服务器作为统一开发环境,平时所有的 ansible 代码编写工作都是通过 vscode remote development 登录这台物理服务器完成。下面是常见的 2 种访问方式: 每一个开发者会有自己的一个 Linux 普通用户,开发者只需在自己的电脑上安装好 vscode,通过 vscode remote development 插件和自己在服务器的普通账号 ssh 连接物理服务器即可, vscode 会在各自的开发者用户下运行 vscode server 进程,因为 vscode 实际上都跑在同一台物理服务器上,所以其他开发者就有了跟我一模一样的 ansible 开发环境。 这篇博客也是通过 vscode remote development 方式,编写出来的,如下图:...

十二月 3, 2022

可持续运维:运维代码化中的数据与操作

这是上一篇的展开说说。 可持续运维解决的几个问题: 一,运维代码化后,运维操作相关的代码可复用。比如:同一套运维操作代码,在不同的服务器上都可以使用。只需拷贝一份运维操作相关的代码,执行一遍,就可以得到完全相同的 2 套服务; 二,运维代码化后,运维操作相关的代码可叠加使用。比如:安装 kafka 集群为例。部署 kafka 需要先安装 zookeeper,部署 codis 集群也需要安装 zookeeper。zookeeper 的运维操作代码化后,我们就可以在 kafka 和 codis 的运维操作代码中直接调用 zookeeper 的运维操作代码,类似拼积木一般,无需重复实现 zookeeper 的运维操作代码; 三、运维代码化后,运维数据可按需传入。比如:在不同的机房部署的 codis 集群,无论是密码还是集群名字都是不同的。代码化运维,运维数据会通过 jinja2 等模板语言抽象成变量;部署时,按需传入不同的变量值,就实现了不同 codis 集群的部署,从而实现了运维代码的自定义,提高了运维代码化的兼容性; 四、运维代码化后,运维数据的加密存储。比如:上一步提到了 codis 集群的数据,其中数据分为普通数据和私密数据,比如前面,codis 集群的名字就是普通数据,codis 集群的密码就是私密数据。运维代码化无论是普通数据还是加密数据,都会直接存放在 Git 等版本控制系统中,这样可以全面、简单的记录所有运维的操作,但私密数据是经过加密的,如下图: 普通数据 私密数据(未解密) 私密数据(已解密) 可以看到,私密数据,在没有解密的时候,是一串密文。无论是普通数据还是私密数据(解密后的),其实都是变量。运维数据加密存储可以实现人员权限的管理。比如,我们可以控制哪些人的公钥(一个人或多个人)可以解开私密数据。有权限的人(内部人员)才能成功的执行运维代码(实际情况中还会通过 SSH 公私钥实现更精细的服务器权限管理,管理谁可以操作哪些服务器,而运维数据的加密存储则是对内部数据的保护)。 运维数据加密存储也为运维协作带来可能,因为运维仓库内,实际上就只有运维操作和安全的运维数据。另外,可以把开源看作是大规模协作的一种形式,这种形式,不是走的最快,但会走得最远。 五、运维代码化后,运维操作的持续迭代。我喜欢使用开源软件,但维护几个、几十个开源软件的运行环境是痛苦的。举个例子:我本地的 Nginx 都是 HTTPS 加密的,这里就离不开 lego 和 dns-01 实现的自动证书申请和续期。我的内网 nginx、外网 nginx、gitlab、jira 都有一份证书,都需要创建和配置自动证书刷新,这是很消耗精力的。几个原因,一个是开源软件的持续迭代往往是 github、gitlab,国内网络不友好,下载更新体验差;其次,几个,几十个开源软件的运行环境持续更新,运维操作没有代码化的时候,手动运维时间和精力成本太大了,并且还有不少重复操作可能导致犯错。因此,将运维操作代码化,在可控人力成本(我一个人)条件下,实现稳定、高效、持续的更新非常重要,而这离不开 Git 和 CI 的支持。 我现在只需要在安装 nginx、gitlab、jira 的 playbook 内,调用这块的运维操作代码,如下图: 即可自动完成证书的申请(泛域名证书),并配置自动刷新所需的所有定时任务(如果你也尝试使用 dns-01 实现证书自动刷新,记得在自动刷新的时间点上加上一点随机性哦,避免刷新时间点冲突)。 另外,如果我需要更新 lego 这个开源组件,我只需更新 lego 版本对应的变量,提交改动到 Git,我的 CI 会自动下载最新的 lego,并更新所有服务器上的 lego 版本,如下图: 这一篇文章,主要还是解释一下,运维代码化中,操作和数据分离的必要性。代码化运维可以实现持续的优化、安全的权限控制和广泛的协同、高效的自动化。这也是可持续运维所需要的。

十一月 25, 2022