Настраиваем сетевую загрузку с помощью mikrotik, ipxe, bios, uefi

Настраиваем сетевую загрузку с помощью mikrotik, ipxe для bios, uefi.

update

2026-01-21: Добавил ansible-playbook.

Intro

Купил себе два HP Proliant Microserver Gen8, один рабочий, другой не рабочий. Обновил bios, iLO. До последних версии. В принципе покупал сервера только из-за наличия iLO4, с надеждой, что можно эти сервера настроить и отправить в сельскую местность, в случае неисправности через iLO4 можно решить вопросы.

Так же есть слот MicroSD. Куда можно разместить emmc и установить систему, но их у меня пока нет.

Но сервера старые, там нет возможности загрузить с UEFI (и это для 2012 года, вроде тогда уже везде было).

Помучался несколько дней, для себя нашел верный вариант, загружаемся по сети, загружаем ipxe, затем уже в ipxe загружаемся с 0 диска. Ну еще для себя написал небольшое меню с возможностью установить Rocky Linux 8 версии. Другие версии и дистрибутивы в принципе тоже можно добавить. Просто нет такого желания.

Что используем?

  • mikrotik router - как dhcp-server и откуда указываем где tftp-сервер и какие файлы нужно загрузить;
  • h96max tv box - используется как tftp-сервер и http сервер. Отсюда загружаются нужные нам файлы;
  • ipxe - создаем два образа для сетевой загрузки - для BIOS и для UEFI;
  • podman - в контейнере собираем ipxe.

Podman

Где нибудь на сервере и (или) рабочей машине под платформой AMD64 будем запускать podman необходимой нам для сборки ipxe.

Для начала создадим ipxe embedded script файл.

mkdir -p ipxe
cd ipxe

cat<<EOF | tee ipxe myscript.ipxe
#!ipxe

dhcp
chain http://192.168.1.18/default.ipxe
EOF
podman run -it --rm -v $(pwd):/app debian:12 bash

apt update -y
apt install build-essential git liblzma-dev -y
git clone https://github.com/ipxe/ipxe.git

cd ipxe/src
make bin/undionly.kpxe EMBED=/app/myscript.ipxe
cp bin/undionly.kpxe /app
make bin-x86_64-efi/ipxe.efi EMBED=/app/myscript.ipxe
cp bin-x86_64-efi/ipxe.efi /app
exit

Полученные файлы undionly.kpxe, ipxe.efi нам нужно скопировать на h96max.

Mikrotik

Скопирую свои настройки, нужно просто адаптировать под свои нужды

[admin@MikroTik] /ip dhcp-server> export 

/ip dhcp-server

add address-pool=dhcp disabled=no interface=bridge name=defconf

/ip dhcp-server option

add code=66 name=TFTP value="'192.168.1.18'"

add code=67 name=Bootfile value="'undionly.kpxe'"

add code=67 name=UEFI value="'ipxe.efi'"

/ip dhcp-server option sets

add name=TFTPD options=Bootfile,TFTP

add name=UEFI options=TFTP,UEFI

/ip dhcp-server network

add address=192.168.1.0/24 comment=defconf dhcp-option-set=TFTPD dns-server=192.168.1.3 gateway=192.168.1.1 next-server=192.168.1.18

По умолчанию сетевая загрузка проходит в BIOS Legacy режиме, если нам нужно загрузить систему через UEFI, то нужно в Mikrotik UI выбрать lease и изменить options sets - выставить UEFI.

После этого данный lease host будет загружать ipxe.efi.

H96Max

Данную железку я купил по совету “У Павла”, в 2025 по сути стоящая замена для raspberry pi. Так как их наштамповали миллионы и продается не дорого.

Также хорошо, что есть emmc на 32 и 64 ГБ.

Также я туда перенес pi.hole.

Нужно установить tftp и http сервера.

sudo apt update -y
sudo apt install -y tftp-hpa tftpd-hpa nginx

sudo mkdir -p /srv/tftp
sudo chown tftp:tftp -R /srv/tftp

cat<<EOF | sudo tee /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create"
EOF

cp /tmp/undionly.kpxe /srv/tftp
cp /tmp/ipxe.efi /srv/tftp

sudo systemctl enable --now tftpd-hpa.service
sudo systemctl enable --now nginx.service

cat<<EOF | sudo tee /var/www/html/default.ipxe
#!ipxe

# Define variables for server details
set serverip 192.168.1.18
set repo mirror.ps.kz
set ksfile ks.cfg          # Kickstart file name

menu
  item --gap -- -------------------------- Rocky Linux 8 Installation --------------------------
  item rocky8_install Install Rocky Linux 8 (HTTP)
  item rocky8_install_ks Install Rocky Linux 8 (HTTP + Kickstart)
  item --gap -- -------------------------- Rocky Linux 9 Installation --------------------------
  item rocky9_install Install Rocky Linux 9 (HTTP)
  item rocky9_install_ks Install Rocky Linux 9 (HTTP + Kickstart)
  item --gap -- -------------------------- Rocky Linux 10 Installation --------------------------
  item rocky10_install Install Rocky Linux 10 (HTTP)
  item rocky10_install_ks Install Rocky Linux 10 (HTTP + Kickstart)
  item --gap -- -------------------------- Debian 12 (bootworm) Installation --------------------------
  item debian12_install Install Debian 12 (HTTP)
  item debian12_install_preseed Install Debian 12 (HTTP + Preseed)
  item --gap -- -------------------------- Debian 13 (trixie) Installation --------------------------
  item debian13_install Install Debian 13 (HTTP)
  item debian13_install_preseed Install Debian 13 (HTTP + Preseed)
  item --gap -- -------------------------- Devuan 5 (daedalus) Installation --------------------------
  item devuan5_install Install Devuan 5 (HTTP)
  item devuan5_install_preseed Install Devuan 5 (HTTP + Preseed)
  item --gap -- -------------------------- Devuan 6 (excalibur) Installation --------------------------
  item devuan6_install Install Devuan 6 (HTTP)
  item devuan6_install_preseed Install Devuan 6 (HTTP + Preseed)
  item --gap -- ----------------------------------------------------------------------------------
  item boot_from_hdd0 Boot from HDD0
  item boot_from_hdd1 Boot from HDD1
  item boot_from_hdd2 Boot from HDD2
  item boot_from_hdd3 Boot from HDD3
  item boot_from_hdd4 Boot from HDD4
  item boot_from_hdd5 Boot from HDD5
  item --gap -- ----------------------------------------------------------------------------------
  item reboot Reboot
  item exit Exit iPXE

# Default boot option
choose --default boot_from_hdd0 --timeout 15000 target && goto ${target}

:rocky8_install
  set osroot RockyLinux/8
  set webpath /rocky/8.10/BaseOS/x86_64/os        # Path on your web server where Rocky Linux 8 ISO content is extracted
  kernel http://${serverip}/${osroot}/vmlinuz inst.repo=http://${repo}${webpath} ip=dhcp
  initrd http://${serverip}/${osroot}/initrd.img
  boot

:rocky8_install_ks
  set osroot RockyLinux/8
  set webpath /rocky/8.10/BaseOS/x86_64/os        # Path on your web server where Rocky Linux 8 ISO content is extracted
  kernel http://${serverip}/${osroot}/vmlinuz inst.repo=http://${repo}${webpath} ip=dhcp inst.ks=http://${serverip}/${osroot}/${ksfile}
  initrd http://${serverip}/${osroot}/initrd.img
  boot

:rocky9_install
  set osroot RockyLinux/9
  set webpath /rocky/9.7/BaseOS/x86_64/os        # Path on your web server where Rocky Linux 8 ISO content is extracted
  kernel http://${serverip}/${osroot}/vmlinuz inst.repo=http://${repo}${webpath} ip=dhcp
  initrd http://${serverip}/${osroot}/initrd.img
  boot

:rocky9_install_ks
  set osroot RockyLinux/9
  set webpath /rocky/9.7/BaseOS/x86_64/os        # Path on your web server where Rocky Linux 8 ISO content is extracted
  kernel http://${serverip}/${osroot}/vmlinuz inst.repo=http://${repo}${webpath} ip=dhcp inst.ks=http://${serverip}/${osroot}/${ksfile}
  initrd http://${serverip}/${osroot}/initrd.img
  boot

:rocky10_install
  set osroot RockyLinux/10
  set webpath /rocky/10.1/BaseOS/x86_64/os        # Path on your web server where Rocky Linux 8 ISO content is extracted
  kernel http://${serverip}/${osroot}/vmlinuz inst.repo=http://${repo}${webpath} ip=dhcp
  initrd http://${serverip}/${osroot}/initrd.img
  boot

:rocky10_install_ks
  set osroot RockyLinux/10
  set webpath /rocky/10.1/BaseOS/x86_64/os        # Path on your web server where Rocky Linux 8 ISO content is extracted
  kernel http://${serverip}/${osroot}/vmlinuz inst.repo=http://${repo}${webpath} ip=dhcp inst.ks=http://${serverip}/${osroot}/${ksfile}
  initrd http://${serverip}/${osroot}/initrd.img
  boot

:debian12_install
  set osroot Debian/bookworm/debian-installer/amd64
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us
  initrd http://${serverip}/${osroot}/initrd.gz
  boot

:debian12_install_preseed
  set osroot Debian/bookworm/debian-installer/amd64
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us preseed/url=http://${serverip}/$
  initrd http://${serverip}/${osroot}/initrd.gz
  boot

:debian13_install
  set osroot Debian/trixie/debian-installer/amd64
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us
  initrd http://${serverip}/${osroot}/initrd.gz
  boot

:debian13_install_preseed
  set osroot Debian/trixie/debian-installer/amd64
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us preseed/url=http://${serverip}/$
  initrd http://${serverip}/${osroot}/initrd.gz
  boot

:devuan5_install
  set osroot Devuan/daedalus/debian-installer/amd64
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us
  initrd http://${serverip}/${osroot}/initrd.gz
  boot

:devuan5_install_preseed
  set osroot Devuan/daedalus/debian-installer/amd64
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us preseed/url=http://${serverip}/$
  initrd http://${serverip}/${osroot}/initrd.gz
  boot

:devuan6_install
  set osroot Devuan/excalibur/debian-installer/amd64
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us
  initrd http://${serverip}/${osroot}/initrd.gz
  boot

:devuan6_install_preseed
  set osroot Devuan/excalibur/debian-installer/amd64
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us preseed/url=http://${serverip}/${osroot}/preseeds/preseed.cfg
  initrd http://${serverip}/${osroot}/initrd.gz
  boot

:boot_from_hdd0
  sanboot --no-describe --drive 0x80

:boot_from_hdd1
  sanboot --no-describe --drive 0x81

:boot_from_hdd2
  sanboot --no-describe --drive 0x82

:boot_from_hdd3
  sanboot --no-describe --drive 0x83

:boot_from_hdd4
  sanboot --no-describe --drive 0x84

:boot_from_hdd5
  sanboot --no-describe --drive 0x85

:reboot
  reboot

:exit
  exit

Ну вроде все настроено и теперь можем использовать сетевую загрузку для различных задач.

Ansible

Нужно было повторить установку еще два раза, ну вот появился повод настроить Ansible-playbook. Чтобы разом можно было сделать установку на 2 хоста и (или) более.

Что нам нужно создать следующие файлы.

ansible.cfg

[defaults]
inventory = inventory

inventory

[netboot]
192.168.1.18

[netboot:vars]
ansible_user=support
ansible_port=22
#ansible_python_interpreter=/usr/bin/python3

main.yaml

---
- name: Install ipxe network boot.
  hosts: all
  become: true

  vars:
    server_ip: "192.168.1.18"

    repo: "mirror.ps.kz"

    oses:
      - distroName: "RockyLinux"
        distroShortName: "rocky"
        distroVersion: "8"
        osRoot: 'RockyLinux/8'
        ks: "ks.cfg"
        webpath: "/rocky/8.10/BaseOS/x86_64/os"
      - distroName: "RockyLinux"
        distroShortName: "rocky"
        distroVersion: "9"
        osRoot: 'RockyLinux/9'
        ks: "ks.cfg"
        webpath: "/rocky/9.7/BaseOS/x86_64/os"
      - distroName: "RockyLinux"
        distroShortName: "rocky"
        distroVersion: "10"
        osRoot: 'RockyLinux/10'
        ks: "ks.cfg"
        webpath: "/rocky/10.1/BaseOS/x86_64/os"
      - distroName: "Debian"
        distroShortName: "debian"
        distroVersion: "12"
        distroVersionName: "bookworm"
        osRoot: "Debian/bookworm/debian-installer/amd64"
        preseed: "preseed/url=http://${serverip}/${osroot}/preseeds/preseed.cfg"
        netboot_url: "https://deb.debian.org/debian/dists/bookworm/main/installer-amd64/current/images/netboot/netboot.tar.gz"
      - distroName: "Debian"
        distroShortName: "debian"
        distroVersion: "13"
        distroVersionName: "trixie"
        osRoot: "Debian/trixie/debian-installer/amd64"
        preseed: "preseed/url=http://${serverip}/${osroot}/preseeds/preseed.cfg"
        netboot_url: "https://deb.debian.org/debian/dists/trixie/main/installer-amd64/current/images/netboot/netboot.tar.gz"
      - distroName: "Devuan"
        distroShortName: "devuan"
        distroVersion: "5"
        distroVersionName: "daedalus"
        osRoot: "Devuan/daedalus/debian-installer/amd64"
        preseed: "preseed/url=http://${serverip}/${osroot}/preseeds/preseed.cfg"
        netboot_url: "https://pkgmaster.devuan.org/devuan/dists/excalibur/main/installer-amd64/current/images/netboot/netboot.tar.gz"
      - distroName: "Devuan"
        distroShortName: "devuan"
        distroVersion: "6"
        distroVersionName: "excalibur"
        osRoot: "Devuan/excalibur/debian-installer/amd64"
        preseed: "preseed/url=http://${serverip}/${osroot}/preseeds/preseed.cfg"
        netboot_url: "https://pkgmaster.devuan.org/devuan/dists/daedalus/main/installer-amd64/current/images/netboot/netboot.tar.gz"
    hdds:
      - 0
      - 1
      - 2
      - 3
      - 4
      - 5

  handlers:
   - name: Daemon-reload
     ansible.builtin.systemd:
       enabled: true

   - name: Service-tftpd-hpa-started
     ansible.builtin.systemd:
       name: tftpd-hpa
       state: started
       enabled: true

   - name: Service-tftpd-hpa-started
     ansible.builtin.systemd:
       name: tftpd-hpa
       state: restarted
       enabled: true

  tasks:
    - name: Ensure that packages are installed.
      ansible.builtin.apt:
        name: "{{ item }}"
        state: present
      with_items:
        - tftp-hpa
        - tftpd-hpa
        - nginx

    - name: Ensure that nginx is enabled and started.
      ansible.builtin.systemd:
        name: nginx
        state: started
        enabled: true

    - name: Ensure that /srv/tftp is exist.
      ansible.builtin.file:
        path: /srv/tftp
        state: directory
        owner: tftp
        group: tftp
        mode: '0755'

    - name: Create or update /etc/default/tftpd-hpa.
      ansible.builtin.copy:
        content: |
          TFTP_USERNAME="tftp"
          TFTP_DIRECTORY="/srv/tftp"
          TFTP_ADDRESS=":69"
          TFTP_OPTIONS="--secure --create"          
        dest: /etc/default/tftpd-hpa
        owner: root
        group: root
        mode: '0644'
      notify:
        - Daemon-reload
        - Service-tftpd-hpa-started
        - Service-tftpd-hpa-started

    - name: Create or update default.ipxe.
      ansible.builtin.template:
        src: default.ipxe.j2
        dest: /var/www/html/default.ipxe
        owner: root
        group: root
        mode: '0644'

    - name: Download pxeboot, netboot files.
      ansible.builtin.include_tasks: download_files.yaml
      with_items: "{{ oses }}"
      loop_control:
        loop_var: os

download_files.yaml

---
- name: Ensure that folder is exist.
  ansible.builtin.file:
    path: "/var/www/html/{{ os['osRoot'] }}"
    state: directory
    owner: root
    group: root
    mode: '0755'

- name: Download RedHat files.
  when:
    - os['distroShortName'] == "rocky"
  ansible.builtin.get_url:
    url: "http://{{ repo }}/{{ os['webpath'] }}/images/pxeboot/{{ item }}"
    dest: "/var/www/html/{{ os['osRoot'] }}/{{ item }}"
    owner: root
    group: root
    mode: '0644'
  with_items:
    - vmlinuz
    - initrd.img

- name: Download Debian files.
  when:
    - os['distroShortName'] in ['debian', 'devuan']
  ansible.builtin.unarchive:
    src: "{{ os['netboot_url'] }}"
    dest: "/var/www/html/{{ os['distroName'] }}/{{ os['distroVersionName'] }}"
    remote_src: true
    owner: root
    group: root

default.ipxe.j2

#!ipxe

# Define variables for server details
set serverip {{ server_ip }}
set repo {{ repo }}
set ksfile ks.cfg          # Kickstart file name

menu
{% for os in oses %}
  item --gap -- -------------------------- {{ os['distroName'] }} {{ os['distroVersion'] }} Installation --------------------------
  item {{ os['distroShortName'] }}{{ os['distroVersion'] }}_install Install {{ os['distroName'] }} {{ os['distroVersion'] }} (HTTP)
{%   if os['ks'] is defined %}
  item {{ os['distroShortName'] }}{{ os['distroVersion'] }}_install_ks Install {{ os['distroName'] }} {{ os['distroVersion'] }} (HTTP + Kickstart)
{%   endif %}
{%   if os['preseed'] is defined %}
  item {{ os['distroShortName'] }}{{ os['distroVersion'] }}_install_preseed Install {{ os['distroName'] }} {{ os['distroVersion'] }} (HTTP + Preseed)
{%   endif %}
{% endfor %}
  item --gap -- ----------------------------------------------------------------------------------
{% for hdd in hdds %}
  item boot_from_hdd{{ hdd }} Boot from HDD{{ hdd }}
{% endfor %}
  item --gap -- ----------------------------------------------------------------------------------
  item reboot Reboot
  item exit Exit iPXE

# Default boot option
choose --default boot_from_hdd0 --timeout 15000 target && goto ${target}

{% for os in oses %}
:{{ os['distroShortName'] }}{{ os['distroVersion'] }}_install
  set osroot {{ os['osRoot'] }}
{%   if os['webpath'] is defined %}
  set webpath {{ os['webpath'] }}
{%   endif %}
{%   if os['distroShortName'] == 'rocky' %}
  kernel http://${serverip}/${osroot}/vmlinuz inst.repo=http://${repo}${webpath} ip=dhcp
  initrd http://${serverip}/${osroot}/initrd.img
{%   elif os['distroShortName'] == 'debian' %}
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us
  initrd http://${serverip}/${osroot}/initrd.gz
{%   elif os['distroShortName'] == 'devuan' %}
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us
  initrd http://${serverip}/${osroot}/initrd.gz
{%   endif %}
  boot
{%   if os['ks'] is defined %}
:{{ os['distroShortName'] }}{{ os['distroVersion'] }}_install_ks
  set osroot {{ os['osRoot'] }}
{%     if os['webpath'] is defined %}
  set webpath {{ os['webpath'] }}
{%     endif %}
  kernel http://${serverip}/${osroot}/vmlinuz inst.repo=http://${repo}${webpath} ip=dhcp inst.ks=http://${serverip}/${osroot}/${ksfile}
  initrd http://${serverip}/${osroot}/initrd.img
  boot
{%   endif %}

{%   if os['preseed'] is defined  %}
:{{ os['distroShortName'] }}{{ os['distroVersion'] }}_install_preseed
  set osroot {{ os['osRoot'] }}
{%     if os['webpath'] is defined %}
  set webpath {{ os['webpath'] }}
{%     endif %}
  kernel http://${serverip}/${osroot}/linux priority=critical interface=auto netcfg/dhcp_timeout=200 language=en country=KZ locale=en_US.UTF-8 keymap=us preseed/url=http://${serverip}/${osroot}/preseeds/preseed.cfg
  initrd http://${serverip}/${osroot}/initrd.gz
  boot
  boot
{%   endif %}
{% endfor %}

{% for hdd in hdds %}
:boot_from_hdd{{ hdd }}
  sanboot --no-describe --drive 0x8{{ hdd }}
{% endfor %}

:reboot
  reboot

:exit
  exit

Запускаем плейбук следующей командой

ansible-playbook main.yaml -K