html

我抵制为我的家庭实验室设置自动化很长一段时间,部分原因是我想手动重复这个过程,以便加深记忆,帮助学习。这意味着我错过了那些方便的命令行工具来进行一行修复,但我专注于设置的错误方面。如果我能重新来过,我会从一开始就学习像Terraform、Ansible和Packer这样的自动化工具,因为将基础设施视为代码(IaC)的范式转变,如果你已经陷入某种工作方式,会花费更长的时间。

现在我正在深入学习自动化所需的符文(抱歉,YAML和Jinja),并设置文本文件的文件夹,将它们串联成一个最终命令,只需几次按键就能重现我的家庭实验室。老实说,这种力量令人陶醉,但也有点令人畏惧,这就是为什么我一直在逐步进行。我离这个目标越来越近,使用Ansible,但还没有完全实现。

为什么Ansible是正确的答案

为什么Ansible是正确的答案

也许这些年我问错了问题

也许这些年我问错了问题

我老实说。我的家庭实验室在最好的时候几乎是无序的混乱。目前,它分布在两台运行Proxmox的迷你PC上,彼此差异巨大:一台有大量存储和低功耗CPU,另一台则配备Strix Halo、128GB内存和2TB存储。它还有一个NAS,里面仍然有我曾经使用但尚未完全替换(还在等待替换)的程序残余,以及一台Threadripper机器,由于我在等待一些部件到货,尚未完全配置。

在网络方面,它也是一个混合的组合,主要是Zyxel的设备,但有一些Firewalla设备在轮换使用。Firewalla路由器和接入点将很快成为未被触动的家庭网络,而家庭实验室即将进行一次重大升级,增加一些Unifi设备的机架。

关键不在于硬件;而是我需要一个多功能的解决方案,能够处理每个设备,平台无关且易于管理。我本可以选择Terraform(也许在玩了一段时间后我应该选择它),但我不喜欢中途放弃,所以Ansible有更多的时间来证明自己,而我在努力克服困难。

我的最终目标是用一行命令重现我的家庭实验室,ansible-playbook site.yml --vault-password-file ~/.vault_password

. 这个过程进展缓慢,但我希望只需做一次,因为我将最终拥有一个完全记录的家庭实验室,包含虚拟机、秘密管理、网络及其他一切。

这不是Proxmox管理的最佳解决方案

这不是Proxmox管理的最佳解决方案

虽然Ansible可以用于许多任务,但它需要一些调整才能与Proxmox配合使用。 Proxmox主机需要安装一些工具,以便能够利用Proxmox Rest API,主要是Proxmoxer及其所需的Python依赖。

apt install build-essential python3-dev libguestfs-tools -y

apt install python3 virtualenv

apt install python3 proxmoxer

与任何其他外部工具一样,我还需要一个API令牌,以便Ansible能够在我的主机上运行剧本。我决定不使用root用户,因为我想正确地做事,所以ansible@pam应运而生,我取消选中特权分离框,以避免后续的ACL问题。

这其实不是最难的部分,因为我需要在Ansible中使用community.general.proxmox_kvm模块,而不是更新的community.proxmox.proxmox.lxc模块。这意味着每个虚拟机剧本看起来像这样(但我的秘密和其他信息已被替换):

- name: 从定义中配置虚拟机

community.general.proxmox_kvm:

proxmox_default_behavior: "{{ proxmox_behavior | default('no_defaults') }}"

node: "{{ item.node }}"

vmid: "{{ item.vmid }}"

name: "{{ item.name }}"

memory: "{{ item.memory | default(2048) }}"

cores: "{{ item.cores | default(2) }}"

sockets: "{{ item.sockets | default(1) }}"

disk: "{{ item.disk | default('100') }}"

storage: "{{ item.storage | default('local-lvm') }}"

ostype: l26 # Linux 2.6+

osimage: "{{ item.osimage | default('ubuntu-20.04-standard') }}" # 操作系统镜像

netif: # 网络接口

net0: "virtio,bridge=vmbr{{ item.vlan | default('0') }}" # 网络配置

ipconfig0: "{{ item.ip_config | default('dhcp') }}" # IP配置

sshkeys: "{{ lookup('file', '~/.ssh/ansible_homelab.pub') }}" # SSH密钥

state: present # 状态

proxmox_default_behavior: no_defaults # Proxmox默认行为

loop: "{{ vms_to_create | default([]) }}" # 循环创建虚拟机

when: vms_to_create is defined # 当虚拟机列表已定义时

tags: vm_creation # 标签: 虚拟机创建

- name: 启动虚拟机 (Start VMs)

command: "qm start {{ item.vmid }}" # 启动指定的虚拟机

changed_when: false # 状态未改变

loop: "{{ vms_to_create | default([]) }}" # 循环创建虚拟机

when:

- vms_to_create is defined # 当虚拟机列表已定义时

- item.state | default('started') == 'started' # 如果状态为启动

- name: 等待虚拟机启动并获取IP地址

uri:

url: "{{ proxmox_url }}/api2/json/nodes/{{ item.node }}/qemu/{{ item.vmid }}/status/current" # API请求的URL

user: "{{ proxmox_user }}" # 用户名

password: "{{ proxmox_password }}" # 密码

validate_certs: no # 不验证证书

register: vm_status # 注册虚拟机状态

until: vm_status.json.data.status == 'running' # 直到虚拟机状态为运行中

重试次数:30

延迟:2

循环: "{{ vms_to_create | default([]) }}"

条件:vms_to_create 已定义

我们的 CMS 经常会提取 Ansible playbook 所需的缩进格式。大多数 IDE 都知道缩进的规则,而我现在很高兴使用 Cursor 进行开发。

记录所有内容是需要时间的

记录所有内容是需要时间的

尤其是因为在设置过程中我什么都没记录

尤其是因为在设置过程中我什么都没记录

最终的目标是建立一个遵循最佳实践的完整项目结构,每个方面都细分为更小的角色:

homelab-infra/

├── ansible.cfg # 全局 Ansible 配置

├── site.yml # 主要入口 playbook

├── requirements.yml # Ansible 集合和角色

├── inventory/

│ ├── 00-static.yml # 手动定义主机和组

│ ├── 01-proxmox-dynamic.yml # 动态 Proxmox 插件配置

│ └── 02-constructed.yml # 组的后处理

├── group_vars/

│ ├── all.yml # 全局变量

│ ├── proxmox.yml # Proxmox 特定配置

│ ├── networking.yml # VLAN、DNS、NTP 配置

│ └── vault.yml # 加密的秘密(Ansible Vault)

├── host_vars/ # 主机特定的变量(如有需要)

├── roles/

│ ├── bootstrap/ # SSH 密钥的设置和初始访问

│ ├── proxmox_baseline/ # Proxmox 节点配置

│ ├── networking/ # VLAN、路由、防火墙

│ ├── vm_provisioning/ # 虚拟机的创建与销毁

│ ├── services/ # Docker, K3s, 监控

│ └── security/ # 安全加固, 备份

├── playbooks/

│ ├── bootstrap.yml # Ansible 初始化设置

│ └── configure.yml # 主要配置

├── roles/common/

│ ├── tasks/

│ │ └── main.yml

│ ├── handlers/

│ │ └── main.yml

│ ├── templates/

│ ├── vars/

│ │ └── main.yml

│ └── defaults/

│ └── main.yml

└── .gitignore # 排除 vault.yml, secrets 等

目前,我已经将网络部分规划并处理完毕,并使从 Proxmox 主机获取动态库存的功能正常运作。这个计划中的一些内容目前在我的实验室中还没有实现,比如 K3s,但它们随时可能成为功能,所以趁着我脑子里还在想着 Ansible 语法,干脆把这项工作做了。

我可不能忘了秘密管理

我可不能忘了秘密管理

我最近开始使用 Bitwarden 来存储我在 Docker 文件中使用的秘密,我也想为我的 Ansible 剧本做同样的事。Ansible Vault 在使用时会创建加密字符串或文本文件,然后你可以在剧本中引用这些加密文件,让你的秘密,嗯……保持秘密。

ansible-vault create group_vars/vault.yml

这会让你创建一个 vault 密码,Ansible 会为你打开一个窗口,以便你添加 API 密钥、密码和其他秘密。我的文件里只有几个项目,因为我的家庭实验室比较简单,但随着我的需求增加,我可以在这个文件中添加更多内容。

# Proxmox API 凭证

vault_proxmox_user: automation@pve

vault_proxmox_password: "

# 服务凭证

vault_docker_registry_username: 我的用户名

vault_docker_registry_password: 我的密码

# 数据库凭据

vault_postgres_root_password: "(请填写密码)"

vault_postgres_replication_password: "(请填写密码)"

# TLS 证书

vault_tailscale_auth_key: "tskey-XXXXXXX"

然后,每个使用秘密的剧本都需要引用这个保管库,具体如下:

- name: 配置 Proxmox API 访问

set_fact:

proxmox_user: "{{ vault_proxmox_user }}"

proxmox_password: "{{ vault_proxmox_password }}"

- name: 用秘密来部署服务

docker_container:

name: postgres

image: postgres:14

env:

POSTGRES 密码: "{{ vault_postgres_root_password }}"

POSTGRES 复制密码: "{{ vault_postgres_replication_password }}"

老实说,这确实有点麻烦,但我更希望我的密码和 API 密钥能够被加密,尤其是因为我打算把剧本存储在自托管的 Git 实例里进行版本控制。而且我可不能忘了在那些任务里加上no_log: true,因为没人想在日志文件里泄露自己的秘密。

Ansible 是我脑子里需要的活文档工具

Ansible 是我脑子里需要的活文档工具

我很享受学习 Ansible 和它的语法。我知道我不会主动做文档,除非被迫,而把自动化工具当作文档,这种脑力黑客我很能接受。我期待有一天能用一行代码重建我的家庭实验室,无论我把它搞得多糟糕。当然,前提是我弄坏的东西不是实物,这可是完全不同的事情要处理。