以 IaC 管理 Incus 接入与 Ayatori 双环境基础容器
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
---
|
||||
- name: 接入 Incus Web UI 与 Authelia
|
||||
hosts: incus_hosts
|
||||
become: true
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: 增量协调 Authelia 的 Incus 客户端
|
||||
become: false
|
||||
ansible.builtin.command:
|
||||
argv: "{{ ['python3', playbook_dir ~ '/../scripts/reconcile_authelia.py', '--desired', playbook_dir ~ '/../../../apps/authelia/clients/incus.yaml'] + (['--check'] if ansible_check_mode else []) }}"
|
||||
register: incus_authelia
|
||||
changed_when: "'changed=true' in incus_authelia.stdout"
|
||||
check_mode: false
|
||||
|
||||
- name: 查询当前 Incus 服务配置
|
||||
ansible.builtin.command: incus query /1.0
|
||||
register: incus_server
|
||||
changed_when: false
|
||||
check_mode: false
|
||||
no_log: true
|
||||
|
||||
- name: 协调 OIDC 与 LAN HTTPS listener
|
||||
ansible.builtin.command:
|
||||
argv: [incus, config, set, "{{ item.key }}={{ item.value }}"]
|
||||
loop: "{{ incus_server_config | dict2items }}"
|
||||
when: (incus_server.stdout | from_json).config.get(item.key, '') != item.value
|
||||
changed_when: true
|
||||
|
||||
- name: 读取 Incus 公共证书用于网关后端验证
|
||||
ansible.builtin.slurp:
|
||||
src: /var/lib/incus/server.crt
|
||||
register: incus_backend_certificate
|
||||
|
||||
- name: 检查入口 namespace 是否已存在
|
||||
become: false
|
||||
ansible.builtin.command: kubectl get namespace incus --ignore-not-found -o name
|
||||
register: incus_namespace
|
||||
changed_when: false
|
||||
check_mode: false
|
||||
|
||||
- name: 渲染并预览或应用专属入口资源
|
||||
become: false
|
||||
ansible.builtin.command:
|
||||
argv: "{{ (['kubectl', 'apply', '--dry-run=client', '-f', '-'] if incus_namespace.stdout == '' else ['kubectl', 'diff', '-f', '-']) if ansible_check_mode else ['kubectl', 'apply', '-f', '-'] }}"
|
||||
stdin: "{{ lookup('template', 'gateway.yaml.j2') }}"
|
||||
register: incus_gateway
|
||||
check_mode: false
|
||||
changed_when: "(incus_gateway.rc == 1 or incus_namespace.stdout == '') if ansible_check_mode else ('created' in incus_gateway.stdout or 'configured' in incus_gateway.stdout)"
|
||||
failed_when: "incus_gateway.rc not in ([0, 1] if ansible_check_mode else [0])"
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
- name: 准备 Ayatori 基础容器的 SSH 入口
|
||||
hosts: incus_hosts
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: 按容器协调 SSH
|
||||
ansible.builtin.include_tasks: tasks/container-ssh.yml
|
||||
loop: [ayatori-dev, ayatori-prod]
|
||||
loop_control:
|
||||
loop_var: incus_container
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
- name: 仅协调 Incus 的 AD DNS 记录
|
||||
hosts: samba_dc
|
||||
become: true
|
||||
gather_facts: false
|
||||
vars_files:
|
||||
- ../../dns/records.yml
|
||||
- ../../samba-ad/ansible/group_vars/all/vars.yml
|
||||
tasks:
|
||||
- name: 从共享清单选择 Incus RRset
|
||||
ansible.builtin.set_fact:
|
||||
incus_dns_records: "{{ homelab_dns.samba.records | selectattr('name', 'equalto', 'incus') | selectattr('zone', 'equalto', 'ad.ddupan.top') | list }}"
|
||||
- name: 确认只有一个受管 A 记录
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- incus_dns_records | length == 1
|
||||
- incus_dns_records[0].type == 'A'
|
||||
- name: 协调 Incus A 记录
|
||||
ddupan.homelab.samba_dns_record:
|
||||
server: "{{ samba_ad_dc_ip }}"
|
||||
zone: "{{ item.zone }}"
|
||||
name: "{{ item.name }}"
|
||||
type: "{{ item.type }}"
|
||||
values: "{{ item['values'] }}"
|
||||
state: present
|
||||
exact: true
|
||||
loop: "{{ incus_dns_records }}"
|
||||
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOLvzIxZhVRd9wEFWR/uCOx7b4HQEdPDiZd8LCN7Hics panxiao81@laptop
|
||||
@@ -0,0 +1,41 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
=tHYp
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
# 显式升级版本,避免例行重跑意外升级虚拟化服务。
|
||||
incus_package_version: '1:7.5.1-ubuntu24.04-202609250207'
|
||||
incus_packages:
|
||||
- incus
|
||||
- incus-base
|
||||
- incus-client
|
||||
- incus-ui-canonical
|
||||
|
||||
# 先完成 IdP 准入限制,再配置 OIDC,最后开放 listener。
|
||||
incus_server_config:
|
||||
oidc.issuer: https://auth.ddupan.top
|
||||
oidc.client.id: incus
|
||||
oidc.audience: https://incus.ad.ddupan.top
|
||||
oidc.scopes: openid,offline_access
|
||||
core.https_address: 192.168.10.127:8443
|
||||
@@ -0,0 +1,7 @@
|
||||
all:
|
||||
children:
|
||||
incus_hosts:
|
||||
hosts:
|
||||
laptop:
|
||||
ansible_connection: local
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
- name: 安装 laptop 的 Incus 基础服务
|
||||
hosts: incus_hosts
|
||||
become: true
|
||||
gather_facts: true
|
||||
tasks:
|
||||
- name: 限定已验证的平台
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_facts['distribution'] == 'Ubuntu'
|
||||
- ansible_facts['distribution_release'] == 'noble'
|
||||
- ansible_facts['architecture'] == 'x86_64'
|
||||
fail_msg: 当前包版本只针对 Ubuntu 24.04 amd64 验证。
|
||||
|
||||
- name: 创建 APT 公钥目录
|
||||
ansible.builtin.file:
|
||||
path: /etc/apt/keyrings
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
|
||||
- name: 安装已核对指纹的 Zabbly 公钥
|
||||
ansible.builtin.copy:
|
||||
src: zabbly.asc
|
||||
dest: /etc/apt/keyrings/zabbly.asc
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
register: incus_key
|
||||
|
||||
- name: 声明 Zabbly stable 软件源
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
Enabled: yes
|
||||
Types: deb
|
||||
URIs: https://pkgs.zabbly.com/incus/stable
|
||||
Suites: noble
|
||||
Components: main
|
||||
Architectures: amd64
|
||||
Signed-By: /etc/apt/keyrings/zabbly.asc
|
||||
dest: /etc/apt/sources.list.d/zabbly-incus-stable.sources
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
register: incus_source
|
||||
|
||||
# 防止系统自动更新绕开版本声明;显式改版本后重跑才升级。
|
||||
- name: 固定 Incus 包版本
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
Package: {{ incus_packages | join(' ') }}
|
||||
Pin: version {{ incus_package_version }}
|
||||
Pin-Priority: 1000
|
||||
dest: /etc/apt/preferences.d/incus
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
# 只刷新本组件源,避免其他服务仓库故障阻塞安装。
|
||||
- name: 刷新 Incus 包索引
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- apt-get
|
||||
- update
|
||||
- -o
|
||||
- Dir::Etc::sourcelist=sources.list.d/zabbly-incus-stable.sources
|
||||
- -o
|
||||
- Dir::Etc::sourceparts=-
|
||||
- -o
|
||||
- APT::Get::List-Cleanup=0
|
||||
- -o
|
||||
- APT::Update::Error-Mode=any
|
||||
changed_when: false
|
||||
register: incus_refresh
|
||||
retries: 3
|
||||
delay: 5
|
||||
until: incus_refresh.rc == 0
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: 安装固定版本且禁止移除既有包
|
||||
ansible.builtin.apt:
|
||||
name: "{{ incus_packages | map('regex_replace', '$', '=' ~ incus_package_version) | list }}"
|
||||
state: present
|
||||
install_recommends: false
|
||||
fail_on_autoremove: true
|
||||
lock_timeout: 120
|
||||
environment:
|
||||
# 不让 needrestart 顺带重启 k3s、libvirt 等无关服务。
|
||||
NEEDRESTART_MODE: l
|
||||
register: incus_install
|
||||
retries: 3
|
||||
delay: 5
|
||||
until: incus_install is succeeded
|
||||
# check 模式不会创建新源,APT 无法解析只在新源存在的版本。
|
||||
when: not (ansible_check_mode and (incus_source.changed or incus_key.changed))
|
||||
|
||||
- name: 提示首次 check 的包验证边界
|
||||
ansible.builtin.debug:
|
||||
msg: 软件源或公钥尚待写入,本次仅预览仓库配置;安装后须重跑 check 和幂等验证。
|
||||
when: ansible_check_mode and (incus_source.changed or incus_key.changed)
|
||||
|
||||
- name: 启用 Incus 本地 socket
|
||||
ansible.builtin.systemd_service:
|
||||
name: incus.socket
|
||||
enabled: true
|
||||
state: started
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: 启动 Incus 服务
|
||||
ansible.builtin.systemd_service:
|
||||
name: incus.service
|
||||
state: started
|
||||
when: not ansible_check_mode
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
- name: 等待首次用户初始化
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, cloud-init, status, --wait]
|
||||
changed_when: false
|
||||
|
||||
- name: 检查 SSH server 是否安装
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, dpkg-query, -W, '-f=${Status}', openssh-server]
|
||||
register: container_ssh_package
|
||||
changed_when: false
|
||||
failed_when: container_ssh_package.rc not in [0, 1]
|
||||
|
||||
- name: 刷新容器包索引
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, apt-get, -o, Acquire::Retries=3, update]
|
||||
when: "'install ok installed' not in container_ssh_package.stdout"
|
||||
changed_when: true
|
||||
register: container_apt_update
|
||||
retries: 3
|
||||
delay: 5
|
||||
until: container_apt_update.rc == 0
|
||||
|
||||
- name: 安装 SSH server
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --env, DEBIAN_FRONTEND=noninteractive, --, apt-get, -o, Acquire::Retries=3, install, -y, --no-install-recommends, openssh-server]
|
||||
when: "'install ok installed' not in container_ssh_package.stdout"
|
||||
changed_when: true
|
||||
register: container_apt_install
|
||||
retries: 3
|
||||
delay: 5
|
||||
until: container_apt_install.rc == 0
|
||||
|
||||
# cloud-init 在未安装 sshd 时预先生成了只有 PasswordAuthentication 的配置;
|
||||
# OpenSSH 默认 UsePAM=no 会拒绝锁定密码的公钥账号,显式采用 Ubuntu 的 PAM 模式。
|
||||
- name: 读取 SSH 配置
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, cat, /etc/ssh/sshd_config]
|
||||
register: container_sshd_config
|
||||
changed_when: false
|
||||
|
||||
- name: 声明仅公钥 SSH 与 PAM 账号检查
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, tee, /etc/ssh/sshd_config]
|
||||
stdin: "{{ container_sshd_desired }}"
|
||||
vars:
|
||||
container_sshd_desired: |
|
||||
UsePAM yes
|
||||
PubkeyAuthentication yes
|
||||
PasswordAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
PermitRootLogin no
|
||||
Subsystem sftp internal-sftp
|
||||
when: container_sshd_config.stdout | trim != container_sshd_desired | trim
|
||||
register: container_sshd_write
|
||||
changed_when: true
|
||||
|
||||
- name: 校验 SSH 配置
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, /usr/sbin/sshd, -t]
|
||||
changed_when: false
|
||||
|
||||
- name: 更新运行中的 SSH 配置
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, systemctl, restart, ssh]
|
||||
when: container_sshd_write is changed
|
||||
changed_when: true
|
||||
|
||||
- name: 检查 SSH service
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, systemctl, is-active, ssh]
|
||||
register: container_ssh_active
|
||||
changed_when: false
|
||||
failed_when: container_ssh_active.rc not in [0, 3, 4]
|
||||
|
||||
- name: 检查 SSH 自启
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, systemctl, is-enabled, ssh]
|
||||
register: container_ssh_enabled
|
||||
changed_when: false
|
||||
failed_when: container_ssh_enabled.rc not in [0, 1, 3, 4]
|
||||
|
||||
- name: 启用 SSH
|
||||
ansible.builtin.command:
|
||||
argv: [incus, exec, "local:{{ incus_container }}", --, systemctl, enable, --now, ssh]
|
||||
when: container_ssh_active.rc != 0 or container_ssh_enabled.stdout != 'enabled'
|
||||
changed_when: true
|
||||
@@ -0,0 +1,109 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: incus
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: incus
|
||||
namespace: incus
|
||||
spec:
|
||||
ports:
|
||||
- name: https
|
||||
port: 8443
|
||||
targetPort: 8443
|
||||
---
|
||||
apiVersion: discovery.k8s.io/v1
|
||||
kind: EndpointSlice
|
||||
metadata:
|
||||
name: incus-laptop
|
||||
namespace: incus
|
||||
labels:
|
||||
kubernetes.io/service-name: incus
|
||||
endpointslice.kubernetes.io/managed-by: homelab-ansible
|
||||
addressType: IPv4
|
||||
ports:
|
||||
- name: https
|
||||
protocol: TCP
|
||||
port: 8443
|
||||
endpoints:
|
||||
- addresses: [192.168.10.127]
|
||||
conditions:
|
||||
ready: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: incus-backend-ca
|
||||
namespace: incus
|
||||
data:
|
||||
ca.crt: |
|
||||
{{ incus_backend_certificate.content | b64decode | indent(4, true) }}
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1alpha3
|
||||
kind: BackendTLSPolicy
|
||||
metadata:
|
||||
name: incus
|
||||
namespace: incus
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: ''
|
||||
kind: Service
|
||||
name: incus
|
||||
validation:
|
||||
hostname: laptop
|
||||
caCertificateRefs:
|
||||
- group: ''
|
||||
kind: ConfigMap
|
||||
name: incus-backend-ca
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: incus
|
||||
namespace: incus
|
||||
spec:
|
||||
parentRefs:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: Gateway
|
||||
name: eg
|
||||
namespace: envoy-gateway-system
|
||||
sectionName: https
|
||||
hostnames: [incus.ad.ddupan.top]
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- group: ''
|
||||
kind: Service
|
||||
name: incus
|
||||
port: 8443
|
||||
weight: 1
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: incus-http
|
||||
namespace: incus
|
||||
spec:
|
||||
parentRefs:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: Gateway
|
||||
name: eg
|
||||
namespace: envoy-gateway-system
|
||||
sectionName: http
|
||||
hostnames: [incus.ad.ddupan.top]
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
filters:
|
||||
- type: RequestRedirect
|
||||
requestRedirect:
|
||||
scheme: https
|
||||
port: 443
|
||||
statusCode: 301
|
||||
Reference in New Issue
Block a user