diff --git a/.gitignore b/.gitignore index 0bb458a3..9f55d1c8 100644 --- a/.gitignore +++ b/.gitignore @@ -389,9 +389,8 @@ vite.config.ts.timestamp-* dockerfiles/Courses/DL/data/FashionMNIST/raw/ *.vsix -# Local config overrides (any file containing 'local') -*local* -*.local.* +# Local config overrides +runtime/values.local.yaml # Offline bundle artifacts auplc-bundle-*/ diff --git a/deploy/ansible/playbooks/pb-pxe-controller.yml b/deploy/ansible/playbooks/pb-pxe-controller.yml index 7bf4082d..951821ca 100644 --- a/deploy/ansible/playbooks/pb-pxe-controller.yml +++ b/deploy/ansible/playbooks/pb-pxe-controller.yml @@ -63,6 +63,11 @@ # - "ssh-ed25519 AAAA... you@workstation" # - "ssh-rsa AAAA... ci@runner" + # Apache HTTP port for serving the k3s token / kubeconfig to PXE agents. + # Defaults to 8080 (not 80) so the PXE controller can live on a k3s node + # without clashing with k3s Traefik / ServiceLB, which owns host port 80/443. + pxe_web_port: 8080 + # APT Mirror pxe_apt_mirror: "http://tw.archive.ubuntu.com/ubuntu" diff --git a/deploy/ansible/roles/pxe_controller/defaults/main.yml b/deploy/ansible/roles/pxe_controller/defaults/main.yml index 3c58119a..cc131711 100644 --- a/deploy/ansible/roles/pxe_controller/defaults/main.yml +++ b/deploy/ansible/roles/pxe_controller/defaults/main.yml @@ -93,6 +93,13 @@ pxe_nfs_root: "/srv/nfs/rootfs" pxe_tftp_root: "/srv/tftp" pxe_web_root: "/var/www/html" +# TCP port Apache listens on to serve the k3s token / kubeconfig to PXE agents. +# This is an internal-only endpoint (reachable from the node subnet), so the port +# number is arbitrary. It defaults to 8080 -- NOT 80 -- so the PXE controller can +# be co-located on a k3s node without colliding with k3s Traefik / ServiceLB, +# which binds host port 80 (and 443) for cluster ingress. +pxe_web_port: 8080 + # ============================================================ # Rootfs build settings # ============================================================ diff --git a/deploy/ansible/roles/pxe_controller/tasks/main.yml b/deploy/ansible/roles/pxe_controller/tasks/main.yml index f75e3b95..b16e2af0 100644 --- a/deploy/ansible/roles/pxe_controller/tasks/main.yml +++ b/deploy/ansible/roles/pxe_controller/tasks/main.yml @@ -431,12 +431,46 @@ changed_when: "'Enabling' in _a2enconf.stdout" notify: Restart apache2 +# Apache serves the token/kubeconfig on pxe_web_port (default 8080) instead of +# 80 so the PXE controller can be co-located on a k3s node without colliding with +# k3s Traefik / ServiceLB, which owns host port 80/443. +- name: Configure Apache listen port + ansible.builtin.template: + src: apache-ports.conf.j2 + dest: /etc/apache2/ports.conf + mode: "0644" + notify: Restart apache2 + +- name: Deploy PXE HTTP virtual host + ansible.builtin.template: + src: pxe-http-vhost.conf.j2 + dest: /etc/apache2/sites-available/pxe-http.conf + mode: "0644" + notify: Restart apache2 + +- name: Enable PXE HTTP virtual host + ansible.builtin.command: + cmd: a2ensite pxe-http + register: _a2ensite + changed_when: "'Enabling' in _a2ensite.stdout" + notify: Restart apache2 + +- name: Disable default Apache site (frees host port 80) + ansible.builtin.command: + cmd: a2dissite 000-default + register: _a2dissite + changed_when: "'Disabling' in _a2dissite.stdout" + notify: Restart apache2 + - name: Enable and start Apache ansible.builtin.systemd: name: apache2 state: started enabled: true +- name: Flush handlers for Apache + ansible.builtin.meta: flush_handlers + # ========================================================== # 12. Verify PXE server status # ========================================================== @@ -491,7 +525,7 @@ - name: Check HTTP token endpoint is reachable ansible.builtin.uri: - url: "http://127.0.0.1/k3s/" + url: "http://127.0.0.1:{{ pxe_web_port }}/k3s/" status_code: [200, 403] register: _verify_http @@ -503,7 +537,7 @@ ══════════════════════════════════════════ dnsmasq : Proxy DHCP + TFTP on {{ pxe_network_interface }} NFS : {{ pxe_nfs_root }} → {{ pxe_subnet }} - Apache : http://{{ pxe_controller_ip }}/k3s/ + Apache : http://{{ pxe_controller_ip }}:{{ pxe_web_port }}/k3s/ Kernel : {{ pxe_kernel_version | default('unknown') }} ────────────────────────────────────────── Next steps (see the Multi-AIPC PXE deployment guide): diff --git a/deploy/ansible/roles/pxe_controller/templates/apache-ports.conf.j2 b/deploy/ansible/roles/pxe_controller/templates/apache-ports.conf.j2 new file mode 100644 index 00000000..35080196 --- /dev/null +++ b/deploy/ansible/roles/pxe_controller/templates/apache-ports.conf.j2 @@ -0,0 +1,6 @@ +# Managed by Ansible (pxe_controller role) -- do not edit by hand. +# +# The PXE controller only needs plain HTTP to hand the k3s token / kubeconfig to +# netbooting agents. It listens on {{ pxe_web_port }} (not 80) so it can coexist +# with k3s Traefik / ServiceLB, which binds host port 80/443 for cluster ingress. +Listen {{ pxe_web_port }} diff --git a/deploy/ansible/roles/pxe_controller/templates/k3s-auto-join.sh.j2 b/deploy/ansible/roles/pxe_controller/templates/k3s-auto-join.sh.j2 index 64f4c8a2..53998b6a 100644 --- a/deploy/ansible/roles/pxe_controller/templates/k3s-auto-join.sh.j2 +++ b/deploy/ansible/roles/pxe_controller/templates/k3s-auto-join.sh.j2 @@ -3,8 +3,8 @@ set -euo pipefail PXE_SERVER="{{ pxe_controller_ip }}" K3S_SERVERS="{{ pxe_k3s_server_ips | join(' ') }}" -TOKEN_URL="http://${PXE_SERVER}/k3s/token" -KUBECONFIG_URL="http://${PXE_SERVER}/k3s/kubeconfig" +TOKEN_URL="http://${PXE_SERVER}:{{ pxe_web_port }}/k3s/token" +KUBECONFIG_URL="http://${PXE_SERVER}:{{ pxe_web_port }}/k3s/kubeconfig" K3S_DATA="{{ pxe_k3s_data_dir }}" HOSTNAME=$(hostname) diff --git a/deploy/ansible/roles/pxe_controller/templates/mount-local-disk.service.j2 b/deploy/ansible/roles/pxe_controller/templates/mount-local-disk.service.j2 new file mode 100644 index 00000000..dd5bb976 --- /dev/null +++ b/deploy/ansible/roles/pxe_controller/templates/mount-local-disk.service.j2 @@ -0,0 +1,12 @@ +[Unit] +Description=Mount local disk for k3s container image cache +Before=k3s-auto-join.service +After=local-fs.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/mount-local-disk.sh +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/deploy/ansible/roles/pxe_controller/templates/mount-local-disk.sh.j2 b/deploy/ansible/roles/pxe_controller/templates/mount-local-disk.sh.j2 new file mode 100644 index 00000000..15f104ba --- /dev/null +++ b/deploy/ansible/roles/pxe_controller/templates/mount-local-disk.sh.j2 @@ -0,0 +1,25 @@ +#!/bin/bash +set -euo pipefail + +K3S_DATA="{{ pxe_k3s_data_dir }}" + +DISK="" +for dev in /dev/sda /dev/vda /dev/nvme0n1; do + [ -b "$dev" ] && { DISK="$dev"; break; } +done + +if [ -z "$DISK" ]; then + echo "No local disk found, using tmpfs for k3s data" + mkdir -p "$K3S_DATA" + mount -t tmpfs -o size=4G tmpfs "$K3S_DATA" + exit 0 +fi + +if ! blkid "$DISK" | grep -q 'TYPE="ext4"'; then + echo "Formatting $DISK as ext4" + mkfs.ext4 -F -L k3s-cache "$DISK" +fi + +mkdir -p "$K3S_DATA" +mount "$DISK" "$K3S_DATA" +echo "Mounted $DISK to $K3S_DATA" diff --git a/deploy/ansible/roles/pxe_controller/templates/pxe-http-vhost.conf.j2 b/deploy/ansible/roles/pxe_controller/templates/pxe-http-vhost.conf.j2 new file mode 100644 index 00000000..5d40021a --- /dev/null +++ b/deploy/ansible/roles/pxe_controller/templates/pxe-http-vhost.conf.j2 @@ -0,0 +1,13 @@ +{# Managed by Ansible (pxe_controller role) -- do not edit by hand. #} + + DocumentRoot {{ pxe_web_root }} + + + Options -Indexes +FollowSymLinks + AllowOverride None + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/pxe-http-error.log + CustomLog ${APACHE_LOG_DIR}/pxe-http-access.log combined +