добавил новую статью про мой факап с настроками wireguard и поправил frontmatter статей, добавил hero в шаблон постов

This commit is contained in:
2026-05-03 02:22:53 +03:00
parent 0acf416206
commit 1cbeef91ea
9 changed files with 603 additions and 20 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"makefile.configureOnOpen": false
}
+8 -3
View File
@@ -7,12 +7,17 @@ slug: '{{ .File.ContentBaseName }}'
description: '' description: ''
categories: categories:
- 'uncategorized' - 'blog'
tags: tags:
- 'draft' - 'linux'
keywords: keywords:
- 'hugo' - 'hugo'
- blog - 'blog'
cover:
image: "hero.svg"
alt: "Hero картинка в SVG"
relative: true
--- ---
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 400" width="100%" height="auto">
<rect width="1200" height="400" fill="#0a0c10"/>
<!-- Hexagon pattern -->
<g stroke="#1a2332" stroke-width="1" fill="none" opacity="0.5">
<polygon points="100,100 130,80 160,100 160,140 130,160 100,140"/>
<polygon points="250,100 280,80 310,100 310,140 280,160 250,140"/>
<polygon points="400,100 430,80 460,100 460,140 430,160 400,140"/>
<polygon points="550,100 580,80 610,100 610,140 580,160 550,140"/>
<polygon points="700,100 730,80 760,100 760,140 730,160 700,140"/>
<polygon points="850,100 880,80 910,100 910,140 880,160 850,140"/>
<polygon points="1000,100 1030,80 1060,100 1060,140 1030,160 1000,140"/>
<polygon points="175,160 205,140 235,160 235,200 205,220 175,200"/>
<polygon points="325,160 355,140 385,160 385,200 355,220 325,200"/>
<polygon points="475,160 505,140 535,160 535,200 505,220 475,200"/>
<polygon points="625,160 655,140 685,160 685,200 655,220 625,200"/>
<polygon points="775,160 805,140 835,160 835,200 805,220 775,200"/>
<polygon points="925,160 955,140 985,160 985,200 955,220 925,200"/>
<polygon points="1075,160 1105,140 1135,160 1135,200 1105,220 1075,200"/>
</g>
<!-- Center text -->
<text x="600" y="175" font-family="monospace" font-size="36" fill="#58a6ff" text-anchor="middle" font-weight="bold">
Ansible Controller на Windows 10
</text>
<text x="600" y="215" font-family="monospace" font-size="20" fill="#8b949e" text-anchor="middle">
WSL2 · Debian · uv · VHDX
</text>
<!-- Decor line -->
<line x1="200" y1="250" x2="1000" y2="250" stroke="#30363d" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,254 @@
---
date: '2026-04-26T04:58:44-04:00'
lastmod: '2026-04-26T04:58:44-04:00'
draft: false
title: 'Ansible в Windows 10 через WSL2: Debian, uv, VHDX и удобный рабочий процесс'
slug: 'ansible-controller-on-win10'
description: ''
categories:
- 'devops'
tags:
- 'devops'
- 'ansible'
- 'windows'
- 'iac'
keywords:
- 'wsl'
- 'win10'
- 'ansible'
- 'powershell'
- 'iac'
cover:
image: "hero.svg"
alt: "Ansible в Windows 10 через WSL2"
relative: true
---
На домашней машине у меня всё еще Winodws 10 стоит(домашние предпочитают удобные цепи закрытого софта вместо свободы), но доставать ноут каждый раз когда нужно управлять инфраструктурой через Ansible бывает сложно, самый практичный путь — поставить Debian в WSL2 и работать уже внутри него. Такой сценарий дает полноценную Linux-среду, не требует отдельной виртуальной машины и удобно бэкапится целиком через wsl --export в формат VHDX.
Ниже — полностью рабочая схема без Microsoft Store: ручная установка WSL2, импорт Debian, настройка uv, установка ansible-core, маппинг проекта на D:\ansible и резервное копирование через VHDX.
## Что понадобится
Для WSL2 на Windows 10 нужна версия 2004 и сборка 19041 или новее, а также включенная аппаратная виртуализация в BIOS/UEFI. Установка WSL и Debian выполняется из PowerShell от имени администратора.
- Windows 10 с поддержкой WSL2.
- PowerShell с правами администратора.
- Доступ к интернету для загрузки компонентов и пакетов.
- Папка для WSL, например D:\WSL\Debian.
- Папка для проектов, например D:\ansible.
## Включаем WSL2 вручную
Если Microsoft Store недоступен или вы не хотите им пользоваться, включите компоненты Windows вручную:
```powershell
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
```
После этого перезагрузи ПК. Эти два компонента нужны для работы WSL2, потому что дистрибутив запускается внутри легковесной виртуальной машины.
Затем задаём WSL2 как версию по умолчанию:
```powershell
wsl --set-default-version 2
```
## Устанавливаем Debian без Store
Для установки Debian без Microsoft Store удобнее использовать импорт rootfs-архива или готового VHDX-образа. Важно не искать случайные архивы по форумам, а брать Debian из официального источника или собирать rootfs на основе официального образа Debian.
На практике удобны два пути:
скачать официальный Debian-образ и подготовить из него rootfs;
либо сразу импортировать уже готовый rootfs-архив, если он у вас есть.
Пример импорта:
```powershell
mkdir D:\WSL\Debian
wsl --import Debian D:\WSL\Debian D:\Downloads\debian-rootfs.tar --version 2
```
После этого запускаем Debian так:
```powershell
wsl -d Debian
```
Если Debian уже был установлен из Store, его можно перенести в управляемый каталог через экспорт и повторный импорт, что особенно удобно для бэкапа и миграции.
## Перенос Debian из Store
Сценарий переноса простой: экспортируем текущий дистрибутив, затем импортируем его в нужную папку. Microsoft документирует wsl --export, wsl --unregister и wsl --import как штатный путь для переноса и резервного копирования WSL-дистрибутивов.
​
```powershell
# Смотрим имя дистрибутива:
wsl -l -v
#Останавливаем WSL
wsl --shutdown
#Экспортируем Debian в архив
wsl --export Debian D:\Backup\Debian.tar
#Удаляем старую регистрацию, если нужен чистый перенос
wsl --unregister Debian
#Импортируем в управляемую папку
wsl --import Debian D:\WSL\Debian D:\Backup\Debian.tar --version 2
```
После этого Debian будет жить в D:\WSL\Debian, а не в Store-профиле пользователя, и им станет проще управлять и делать бэкап.
## Настраиваем Ansible через uv
Для Ansible в WSL2 я рекомендую не системную установку и не pip в системный Python, а отдельное виртуальное окружение, управляемое через uv. Такой подход изолирует зависимости, не трогает системный Python Debian и позволяет легко воспроизводить окружение после восстановления из бэкапа.
ansible-core — это базовый движок Ansible. В отличие от полного пакета ansible, он содержит только ядро: выполнение playbook’ов, inventory-логику, CLI и основную инфраструктуру. Это делает установку легче, прозрачнее и удобнее для проектного workflow.
Минимальный набор для установки:
- uv;
- ansible-core;
- openssh-client;
- git;
- python3.
После входа в Debian:
```bash
sudo apt update
sudo apt install -y curl git openssh-client python3
curl --proto '=https' --tlsv1.2 -LsSf https://releases.astral.sh/github/uv/releases/download/0.11.7/uv-installer.sh | sh
```
После установки uv нужно переоткрытьshell-сессию или добавьте uv в PATH, если установщик это не сделал автоматически. uv умеет создавать виртуальные окружения и ставить пакеты внутрь них, а также работать с выбранным Python-интерпретатором.
Создаем окружение
Если проект лежит в ~/ansible:
```bash
cd ~/ansible
uv venv
source .venv/bin/activate
uv pip install ansible-core
```
Если нужен линтер:
``` bash
uv pip install ansible-core ansible-lint
```
Такой workflow хорош тем, что версию Ansible можно менять независимо от системы, а зависимости проекта не смешиваются с Debian-пакетами.
Почему ansible-core удобнее
Для рабочей машины под Ansible ansible-core обычно удобнее, чем полный пакет ansible, по нескольким причинам. Во-первых, он меньше и чище, поэтому окружение проще поддерживать. Во-вторых, вы явно контролируете, какие коллекции и зависимости нужны именно вашему проекту. В-третьих, это снижает зависимость от версии, которую выдает системный репозиторий Debian.
Практически это означает следующее:
- ansible-core ставится в проектный venv;
- коллекции ставятся отдельно через ansible-galaxy или по requirements.yml;
- при необходимости окружение можно пересоздать за пару минут.
Рекомендуемый workflow
```bash
mkdir -p ~/ansible
cd ~/ansible
uv venv
source .venv/bin/activate
uv pip install ansible-core
```
Дальше в репозитории храним:
- playbook’и;
- inventory;
- requirements.yml;
- ansible.cfg.
Сами зависимости и .venv в Git не добавляем. Это делает проект переносимым и аккуратным.
## Маппинг ~/ansible в D:\ansible
Если хотите хранить проект на диске Windows, используйте стандартный путь WSL: D:\ansible в Linux виден как /mnt/d/ansible. Это штатный механизм WSL, а для обратного преобразования путей существует wslpath.
​
Самый простой вариант:
```bash
cd /mnt/d/ansible
```
Если хотите, чтобы в Linux путь был именно ~/ansible, создайте символическую ссылку:
```bash
rm -rf ~/ansible
ln -s /mnt/d/ansible ~/ansible
```
После этого ~/ansible будет указывать на D:\ansible. Это удобно: в Ansible-проектах вы работаете с привычным Linux-путем, а файлы физически лежат на Windows-диске.
​
## Команды управления WSL
Минимальный набор команд для повседневной работы:
```powershell
# Показать все дистрибутивы и их версию
wsl -l -v
# Запустить Debian
wsl -d Debian
# Сделать Debian дистрибутивом по умолчанию
wsl --set-default Debian
#​ Полностью остановить WSL2
wsl --shutdown
# Остановить только Debian
wsl --terminate Debian
# Перевести Debian в WSL2, если он вдруг оказался в WSL1
wsl --set-version Debian 2
```
## Бэкап через VHDX
Для WSL2 самый удобный резервный формат — VHDX. Microsoft прямо поддерживает экспорт дистрибутива в VHDX через wsl --export ... --vhd, а затем импорт обратно через wsl --import ... --vhd. Это сохраняет весь дистрибутив целиком, включая Ansible, Python-окружение, ключи, историю shell и рабочие файлы.
```powershell
# Перед бэкапом обязательно останавливаем WSL
wsl --shutdown
# Затем создаtv бэкап
wsl --export Debian D:\Backup\Debian\Debian.vhdx --vhd
```
Это удобнее ручного копирования ext4.vhdx, потому что команда сразу создает переносимый снимок дистрибутива в формате VHDX.
## Восстановление
Если нужно восстановить среду из такого бэкапа, импортируем VHDX как дистрибутив:
```powershell
wsl --import DebianRestored D:\WSL\DebianRestored D:\Backup\Debian\Debian.vhdx --vhd --version 2
```
Если мы храним дистрибутив в отдельной папке и используете import-in-place, можно подключить существующий VHDX без распаковки. Это особенно удобно при переносе среды на другой диск или другой компьютер.
## Практичная схема для работы
Для повседневного использования я бы рекомендовал такую схему:
- Debian живет в WSL2;
- Ansible ставится через uv в .venv;
- проекты лежат в D:\ansible и маппятся через /mnt/d/ansible;
- резервная копия делается через wsl --export ... --vhd;
- .venv в Git не хранится, а dependencies фиксируются в документации или requirements.yml.
Такой подход дает хорошую изоляцию, быстрый старт после восстановления и удобную миграцию между машинами. Для WSL2 это один из самых практичных способов использовать Ansible в Windows 10 без необходимости перезагрузки.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 400" width="100%" height="auto">
<rect width="1200" height="400" fill="#0a0c10"/>
<!-- Hexagon pattern -->
<g stroke="#1a2332" stroke-width="1" fill="none" opacity="0.5">
<polygon points="100,100 130,80 160,100 160,140 130,160 100,140"/>
<polygon points="250,100 280,80 310,100 310,140 280,160 250,140"/>
<polygon points="400,100 430,80 460,100 460,140 430,160 400,140"/>
<polygon points="550,100 580,80 610,100 610,140 580,160 550,140"/>
<polygon points="700,100 730,80 760,100 760,140 730,160 700,140"/>
<polygon points="850,100 880,80 910,100 910,140 880,160 850,140"/>
<polygon points="1000,100 1030,80 1060,100 1060,140 1030,160 1000,140"/>
<polygon points="175,160 205,140 235,160 235,200 205,220 175,200"/>
<polygon points="325,160 355,140 385,160 385,200 355,220 325,200"/>
<polygon points="475,160 505,140 535,160 535,200 505,220 475,200"/>
<polygon points="625,160 655,140 685,160 685,200 655,220 625,200"/>
<polygon points="775,160 805,140 835,160 835,200 805,220 775,200"/>
<polygon points="925,160 955,140 985,160 985,200 955,220 925,200"/>
<polygon points="1075,160 1105,140 1135,160 1135,200 1105,220 1075,200"/>
</g>
<!-- Center text -->
<text x="600" y="175" font-family="monospace" font-size="36" fill="#58a6ff" text-anchor="middle" font-weight="bold">
Факап в конфиге WG привёл к потере 6 часов жизни
</text>
<text x="600" y="215" font-family="monospace" font-size="20" fill="#8b949e" text-anchor="middle">
Linux · Netplan · systemd-networkd · Wireguard
</text>
<!-- Decor line -->
<line x1="200" y1="250" x2="1000" y2="250" stroke="#30363d" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+156
View File
@@ -0,0 +1,156 @@
---
date: '2026-05-03T01:26:20+03:00'
lastmod: '2026-05-03T01:26:20+03:00'
draft: false
title: 'Факап с конфиге WG привел к потере 6 часов жизни'
slug: '20260503-dns-routing'
description: ''
categories:
- 'linux'
- 'devops'
tags:
- 'linux'
- 'wireguard'
- 'netplan'
keywords:
- 'wireguard'
- 'dns'
- 'systemd-networkd'
- 'netplan'
cover:
image: "hero.svg"
alt: "Факап с конфиге WG привел к потере 6 часов жизни"
relative: true
---
Расскажу про то как один параметр в кофиге Wireguard причинил мне опыт по разбору половины сетевых настроек Ubuntu. Один с виду безобидный параметр... IaC блин нужен обязательно. Я просто забыл что и для чего менял, а потом уже поздно было вспоминать.
## Симптомы: тишина в эфире и гора dropped пакетов
Всё началось с того, полсе смерти системного диска в моем домашнем сервере, я перестал крутить его 24/7 и набегами по выходным конифгурировал его, пытаясь вернуть привычный набор сервисом. И вот однажды включаю я сервер, а у меня проблема с системным резолвом DNS. Так-то напрямую `nslookup ya.ru 8.8.8.8` выдает всё нормально. но система ни в какую не хочет резолвить адреса. Так-то я забыл, что неделей ранее я настраивал Wireguard со своими VPS, и что именно после настройки Wireguard сервер потерял доступ к локальной сети и интернету. начал ковырять, ИИшка подсказала что на Ubuntu надо netplan копать, начал фигачить yamlики, тут время вышло и я еще на неделю забросил комп. И вот вчера, сейчас далеко за полночь, я его включил и опять начал разбираться, естественно забыл не только то что я две недели назад делал, но и то чем неделю назад занимался. И вот картинка, сервер загрузился, подозрительно долго грузился кстати, но по сети недоступен.
Первая диагностика через `ip -s link show` показала тревожную картину на интерфейсе `eno1`:
* **RX:** 53 000+ пакетов принято.
* **Dropped:** 21 000+ пакетов отброшено.
* **TX:** Всего 40 пакетов отправлено.
Команда `ip route show` выводила только маршрут для интерфейса `wg0`. Маршрута по умолчанию через физический интерфейс не было. Система «не видела» шлюза.
Команда `ip a` показала, что на всех интерфейсах, кроме loopback, ip-адреса отсутствуют.
Попытки перезапустить службы или применить конфигурацию через `sudo netplan apply` не давали результата. Интерфейс зависал в состоянии `configuring`, а адрес не присваивался.
## Расследование: кто виноват?
### 1. Конфликт маршрутизации
Первоначальная гипотеза была в том, что WireGuard перехватывает весь трафик. Однако в конфиге `wg0.conf` параметр `AllowedIPs` был ограничен подсетью туннеля (`10.8.0.0/24`). Он не должен был блокировать локальную сеть.
Проблема оказалась глубже: отсутствие маршрута по умолчанию для `eno1` означало, что ядро просто не знало, куда девать исходящие пакеты, кроме как в туннель (если бы он был активен) или в никуда.
### 2. Почему DHCP молчал?
Ubuntu 24.04 использует стек `systemd-networkd` для управления сетью на серверных сборках. Конфигурация задается через `Netplan` (YAML-файлы).
Проверка статуса показала:
```bash
networkctl status eno1
State: routable (configuring)
```
Статус `configuring` в сочетании с `routable` — это классический признак того, что демон ждет завершения какой-то операции. В логах `journalctl -u systemd-networkd` не было ошибок получения адреса IPv4, зато постоянно терялась аренда IPv6 (`DHCPv6 lease lost`).
Оказалось, что `systemd-networkd` по умолчанию пытается настроить и IPv4, и IPv6. Если сервер DHCPv6 не отвечает или есть проблемы с Router Advertisements (RA), демон может зависнуть в ожидании, блокируя переход интерфейса в полностью рабочее состояние для IPv4.
### 3. Долгая загрузка
При перезагрузке я заметил, что система висит на этапе `Job systemd-networkd-wait-online.service`. Однако служба `wait-online` держала систему, пока сеть не поднимется. Поскольку сеть не могла подняться из-за зависшего DHCP-клиента, загрузка затягивалась.
## Решение: поэтапный демонтаж проблем
### Шаг 1. Отключение ожидания сети
Чтобы система загружалась быстро, даже если сеть сбоит, отключил службу ожидания:
```bash
sudo systemctl disable systemd-networkd-wait-online.service
sudo systemctl mask systemd-networkd-wait-online.service
```
### Шаг 2. Отключение IPv6
Возможно проблема долгой загрузки была в ожидании ответов IPv6, а поскольку в моей инфраструктуре он пока не критичен, то я отключил его на уровне ядра. Это сузило площадь ошибок в конфигурации `systemd-networkd`, я сосредоточился только на IPv4.
В `/etc/sysctl.conf` добавил:
```ini
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.lo.disable_ipv6=1
```
И применил изменения: `sudo sysctl -p`.
### Шаг 3. Переход на прямую конфигурацию systemd-networkd
Файлы Netplan (`/etc/netplan/*.yaml`) генерируют конфиги для бэкенда. У меня их было два, и они могли конфликтовать или содержать избыточные параметры, тем более я уже и не помнил как и почему я их именно так писал. Изолировал прослойку Netplan, создав файл-заглушку `/etc/systemd/network/10-eno1.network` с настройками для `systemd-networkd`:
```ini
[Match]
Name=eno1
[Network]
DHCP=ipv4
IPv6AcceptRA=no
DNS=1.1.1.1
DNS=8.8.8.8
```
Ключевой момент здесь — `DHCP=ipv4`. Тут явно говорим демону: «Используй только IPv4, игнорируй IPv6». Параметр `IPv6AcceptRA=no` дополнительно страхует от ожидания сообщений роутера.
Удалил старые файлы из `/etc/netplan/`, чтобы избежать двойного применения настроек, и перезапустил службу:
```bash
sudo systemctl restart systemd-networkd
```
Интерфейс сразу получил адрес `192.168.1.3` и перешел в статус `routable (configured)`. Системный резолв DNS заработал.
### Шаг 4. Финальный босс: DNS и WireGuard
Пинги пошли, но `nslookup ya.ru` выдавал таймауты на `127.0.0.53` (локальный stub-resolver `systemd-resolved`). Вот про эту штуку я не знал, поэтому теперь знаю что не нужно поднимать на каждом сервер unbound, в systemd все есть из коробки.
Проверка `resolvectl status` показала странность:
* Link 2 (eno1): DNS Servers: 1.1.1.1, 8.8.8.8
* Link 5 (wg0): **DNS Domain: ~.**
Правда я её не заметил сперва, но консультации с ИИ не всегда являются потерей времени. Символ `~.` означает «глобальный поиск». WireGuard, увидев в своем конфиге строку `DNS = 1.1.1.1`, автоматически сообщил системе, что этот DNS-сервер должен обрабатывать **все** запросы, перекрывая настройки физического интерфейса. Но так как туннель до собственных серверов и маршрутов до внешних DNS в нём нет, разрешения имен не работали.
**Решение:**
В файле `/etc/wireguard/wg0.conf` я закомментировал всего лишь одну строку, как потом выяснилось добавленной в конфиг по рекомендаци такой же ИИшечки:
```ini
# DNS = 1.1.1.1
```
Переподнял туннель:
```bash
sudo wg-quick down wg0
sudo wg-quick up wg0
```
Теперь `wg0` имеетв выводк `networkctl status eno1` `Current Scopes: none`, а все DNS-запросы идут через основной интерфейс `eno1` на публичные серверы Cloudflare и Google.
## Итоги и выводы
1. **WireGuard и DNS:** Параметр `DNS` в конфиге WireGuard — это не просто рекомендация, а команда для системы изменить **глобальные** настройки резолвинга. Если ты не хочешь, чтобы туннель перехватывал все DNS-запросы, не указывай этот параметр в конфиге клиента, либо используйте более тонкие настройки `Domains` (если клиент поддерживает).
2. **Ubuntu 24.04 и IPv6:** По умолчанию включенный IPv6 может вызывать задержки при получении адреса IPv4, если инфраструктура не готова к v6. В домашних лабораториях его часто проще отключить, чем дебажить RA и DHCPv6.
3. **Netplan vs systemd-networkd:** Netplan удобен, но иногда прямая конфигурация `systemd-networkd` дает больше прозрачности и контроля, особенно при отладке сложных случаев с DHCP.
4. **systemd-networkd-wait-online:** На серверах, где сеть может падать или долго подниматься, эту службу лучше маскировать, иначе она будет тормозить загрузку всей ОС.
Эта ошибка в конфиге WG стоила мне нескольких часов, но теперь моя домашняя лаборатория работает стабильно, а конфиги приведены к минимальному и понятному виду, а я узнал еще что-то новенькое про любимый Линукс.
(Ссылка на диалог с Qwen)[https://chat.qwen.ai/s/0cfbc11b-dae3-4dba-bfaf-b0788172d07c?fev=0.2.45]
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 400" width="100%" height="auto">
<rect width="1200" height="400" fill="#0a0c10"/>
<!-- Hexagon pattern -->
<g stroke="#1a2332" stroke-width="1" fill="none" opacity="0.5">
<polygon points="100,100 130,80 160,100 160,140 130,160 100,140"/>
<polygon points="250,100 280,80 310,100 310,140 280,160 250,140"/>
<polygon points="400,100 430,80 460,100 460,140 430,160 400,140"/>
<polygon points="550,100 580,80 610,100 610,140 580,160 550,140"/>
<polygon points="700,100 730,80 760,100 760,140 730,160 700,140"/>
<polygon points="850,100 880,80 910,100 910,140 880,160 850,140"/>
<polygon points="1000,100 1030,80 1060,100 1060,140 1030,160 1000,140"/>
<polygon points="175,160 205,140 235,160 235,200 205,220 175,200"/>
<polygon points="325,160 355,140 385,160 385,200 355,220 325,200"/>
<polygon points="475,160 505,140 535,160 535,200 505,220 475,200"/>
<polygon points="625,160 655,140 685,160 685,200 655,220 625,200"/>
<polygon points="775,160 805,140 835,160 835,200 805,220 775,200"/>
<polygon points="925,160 955,140 985,160 985,200 955,220 925,200"/>
<polygon points="1075,160 1105,140 1135,160 1135,200 1105,220 1075,200"/>
</g>
<!-- Center text -->
<text x="600" y="180" font-family="monospace" font-size="42" fill="#58a6ff" text-anchor="middle" font-weight="bold">
hugo new content posts/:slug/
</text>
<text x="600" y="225" font-family="monospace" font-size="20" fill="#8b949e" text-anchor="middle">
Leaf bundles · Archetypes · Permalinks · Slugs
</text>
<!-- Decor line -->
<line x1="200" y1="260" x2="1000" y2="260" stroke="#30363d" stroke-width="1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -19,9 +19,13 @@ tags:
keywords: keywords:
- 'hugo' - 'hugo'
- 'blog' - 'blog'
cover:
image: "hero.svg"
alt: "Автоматизация Hugo"
relative: true
--- ---
Ваш текст поста здесь...
## Проблема: рутина при создании постов ## Проблема: рутина при создании постов
Когда я только начинал вести блог на Hugo, каждый новый пост создавался через боль и страдания: Когда я только начинал вести блог на Hugo, каждый новый пост создавался через боль и страдания:
@@ -38,12 +42,68 @@ keywords:
## Что такое бандл и зачем папка для каждого поста ## Что такое бандл и зачем папка для каждого поста
Hugo поддерживает два типа контента: Hugo поддерживает три типа контента:
- **Leaf bundle** — папка с файлом `index.md`. Внутрь можно складывать изображения, файлы, другие ресурсы. Идеально для блога. Три способа хранения контента в Hugo
- **Branch bundle** — папка с `_index.md`. Используется для секций-списков (например, `/posts/`).
1. Простой файл .md (без папки)
Самый простой способ — просто положить файл мой-пост.md в папку content/posts/:
```text
content/
└── posts/
├── первый-пост.md
├── второй-пост.md
└── третий-пост.md
```
Плюсы:
- Максимально просто, ничего создавать не нужно
- Подходит для простых постов без изображений
Минусы:
- Все изображения нужно класть в общую папку static/images/
- Нельзя прикрепить к посту специфичные файлы (PDF, архивы и т.д.)
- Изображения нужно называть уникально, чтобы не пересекались с другими постами
2. Leaf bundle (папка + index.md)
```text
content/
└── posts/
└── мой-пост/
├── index.md
├── hero.jpg
└── code-example.py
```
Плюсы:
- Все ресурсы поста в одном месте
- Можно ссылаться на изображения относительно: ![герой](hero.jpg)
- Не нужно думать об уникальности имён файлов
Минусы:
- Нужно создавать папку (но мы это автоматизировали)
3. Branch bundle (папка + _index.md)
```text
content/
├── posts/
│ ├── _index.md <- описывает секцию /posts/
│ ├── первый-пост.md
│ └── мой-пост/
│ └── index.md
```
Плюсы:
- _index.md позволяет задать заголовок, описание для всей секции
- Можно настроить отдельный шаблон для списка постов
Структура моего блога: Структура моего блога:
```text
content/ content/
├── posts/ ├── posts/
│ ├── hugo-slugs-archetypes-bundles/ │ ├── hugo-slugs-archetypes-bundles/
@@ -53,11 +113,15 @@ content/
│ │ └── code-example.txt │ │ └── code-example.txt
│ └── другой-пост/ │ └── другой-пост/
│ └── index.md │ └── index.md
```
Плюсы такого подхода: Плюсы такого подхода:
- Все посты хранятся одинаково — папка + index.md. Не нужно думать, какой способ выбрать.
- Все файлы поста в одном месте - Все файлы поста в одном месте
- Можно удобно ссылаться на изображения: `![схема](images/diagram.png)` - Можно удобно ссылаться на изображения: `![схема](images/diagram.png)`
- Не нужно придумывать уникальные имена для картинок глобально - Не нужно придумывать уникальные имена для картинок глобально
- Если я захочу экспортировать пост в другой блог, достаточно скопировать одну папку со всеми ресурсами.
## Почему папку бандла нужно называть на латинице ## Почему папку бандла нужно называть на латинице
@@ -107,10 +171,10 @@ tags = [
``` ```
Разберём ключевые моменты: Разберём ключевые моменты:
Поле Значение Поле Значение
title Берёт имя папки, заменяет дефисы на пробелы и делает заглавные буквы. my-awesome-post → My Awesome Post title Берёт имя папки, заменяет дефисы на пробелы и делает заглавные буквы. my-awesome-post → My Awesome Post
slug Просто берёт имя папки как есть: my-awesome-post slug Просто берёт имя папки как есть: my-awesome-post
.File.ContentBaseName Встроенная переменная Hugo — имя текущей папки без расширения и пути .File.ContentBaseName Встроенная переменная Hugo — имя текущей папки без расширения и пути
После создания поста я вручную меняю title на русский и заполняю description, categories, tags. После создания поста я вручную меняю title на русский и заполняю description, categories, tags.
@@ -147,14 +211,15 @@ make deploy
## Что ещё можно добавить в front matter ## Что ещё можно добавить в front matter
В процессе настройки я выяснил, что Hugo поддерживает много полезных полей: В процессе настройки я выяснил, что Hugo поддерживает много полезных полей:
|Поле |Назначение| | Поле | Назначение |
|publishDate |Отложенная публикация (не рендерится до указанной даты)| |----------------|---------------------------------------------------------------|
|expiryDate |Автоматическое снятие с публикации| | `publishDate` | Отложенная публикация (не рендерится до указанной даты) |
|lastmod |Дата последнего изменения (для SEO)| | `expiryDate` | Автоматическое снятие с публикации |
|aliases |Редиректы со старых URL| | `lastmod` | Дата последнего изменения (для SEO) |
|weight |Ручная сортировка в списке (меньше — выше)| | `aliases` | Редиректы со старых URL |
|images |Изображение для Open Graph и Twitter Cards| | `weight` | Ручная сортировка в списке (меньше — выше) |
|params |Кастомные параметры для темы| | `images` | Изображение для Open Graph и Twitter Cards |
| `params` | Кастомные параметры для темы |
## Итог ## Итог
@@ -174,4 +239,4 @@ make deploy
Теперь можно сосредоточиться на том, ради чего всё затевалось — на содержании. Теперь можно сосредоточиться на том, ради чего всё затевалось — на содержании.
Если у тебя есть свои лайфхаки по Hugo или ты знаешь, как сделать транслитерацию slug прямо из заголовка — пишите мне в Telegram https://t.me/kpa39l. Обсудим. Если у тебя есть свои лайфхаки по Hugo или ты знаешь, как сделать транслитерацию slug прямо из заголовка — пишите мне в Telegram <https://t.me/kpa39l>. Обсудим.