--- title: "Блог на Hugo в K3s: часть 1 - архитектура и первый запуск" date: 2026-01-03 draft: false description: "Как я переехал с Jekyll на Hugo, почему выбрал тему Blowfish и как настроил два окружения с нуля. Начало цикла о том как я строил oakazanin.ru." tags: ["hugo", "blowfish", "gitea", "homelab", "devops"] categories: ["infrastructure"] series: ["Блог на Hugo в K3s"] series_order: 1 --- Предыдущий блог жил на Jekyll. Жил - громко сказано. Скорее существовал, периодически ломаясь при обновлениях и требуя ритуальных танцев с Ruby каждый раз когда я садился за новую статью. Однажды я решил что хватит. --- ## Почему не Jekyll Jekyll - зрелый инструмент с большим сообществом. Но у него есть фундаментальная проблема: он написан на Ruby. Звучит невинно. На практике это означает: **Версионный ад.** Ruby, Bundler, Gems - у каждого своя версия, и они регулярно конфликтуют друг с другом. Клонируешь репозиторий на новую машину - час уходит на то чтобы собрать рабочее окружение. Обновляешь Jekyll - ломаются плагины. Обновляешь плагины - ломается что-то ещё. **Зависимости ради зависимостей.** Простой блог тянет 1000+ gems, половина из которых устаревшая или находится в состоянии "поддерживается постольку-поскольку". Каждый `bundle install` - это лотерея. **Лишний мусор.** Jekyll генерирует страницы без ссылок, которые непонятно зачем существуют. Приходится явно прописывать что не генерировать. Hugo решает все эти проблемы радикально: это один бинарник на Go. Никаких зависимостей, никаких конфликтов версий, никакого `bundle install`. Скачал - работает. На любой машине, всегда. Бинарник весит ~50MB. Собирает сайт из 40+ страниц за полторы секунды. Jekyll на том же контенте думал заметно дольше. --- ## Почему Blowfish Все просто: понравилась. Смотрел темы несколько часов. Blowfish выглядела именно так как я хотел - чисто, без лишнего, с хорошей типографикой. Взял её. Ставится как Git submodule - обновляется одной командой, не засоряет репозиторий. --- ## Архитектура: два окружения У любого нормального инфраструктурного проекта есть тестовый и production контур. Блог - не исключение: обновления Hugo, изменения темы, новые статьи - всё это нужно проверять до того как это увидят читатели. ``` ┌────────────────────────────────────┐ │ Gitea (внутренний) │ │ репозиторий: blog │ │ │ │ ветка: main ветка: dev │ └───────┬────────────────────┬───────┘ │ │ ▼ ▼ ┌───────────────┐ ┌───────────────┐ │ hugo-builder │ │ hugo-builder │ │ prod │ │ dev │ └───────┬───────┘ └───────┬───────┘ │ │ ▼ ▼ ┌───────────────┐ ┌───────────────┐ │ nginx │ │ nginx-dev │ │ blog.ru │ │ dev.blog.ru │ │ (публичный) │ │ (Basic Auth) │ └───────────────┘ └───────────────┘ ``` **Production** (`blog.ru`) - публичный. Ветка `main`. **Development** (`dev.blog.ru`) - тестовый контур. Ветка `dev`. Закрыт Basic Auth через Traefik Middleware - без логина и пароля не войти. Оба окружения в одном K3s namespace. Один репозиторий, две ветки, два независимых пайплайна. --- ## Шаг 1: Создаём Hugo проект ```bash # Создаём новый Hugo сайт cd ~/projects hugo new site blog cd blog # Инициализируем Git репозиторий git init git remote add origin https://git.example.com/user/blog.git # Добавляем тему Blowfish как Git submodule git submodule add -b main https://github.com/nunocoracao/blowfish.git themes/blowfish # Подтягиваем файлы submodule git submodule update --init --recursive # Удаляем дефолтный конфиг Hugo rm -f hugo.toml # Создаём структуру для конфигов mkdir -p config/_default # Копируем примеры конфигов из темы cp themes/blowfish/config/_default/*.toml config/_default/ ``` Структура после инициализации: ``` blog/ ├── content/ ← статьи в Markdown ├── static/ ← изображения, favicon ├── themes/ │ └── blowfish/ ← тема (Git submodule) └── config/ └── _default/ ├── hugo.toml ├── languages.ru.toml ├── menus.ru.toml ├── markup.toml └── params.toml ``` --- ## Шаг 2: Минимальная конфигурация ### hugo.toml ```toml baseURL = "https://blog.ru/" theme = "blowfish" defaultContentLanguage = "ru" ``` ### languages.ru.toml ```bash # Переименовываем файл языка для русского mv config/_default/languages.en.toml config/_default/languages.ru.toml ``` ```toml languageCode = "ru" languageName = "Русский" weight = 1 title = "Мой Блог" [params] displayName = "RU" isoCode = "ru" rtl = false dateFormat = "2 January 2006" [params.author] name = "Ваше Имя" headline = "DevOps Engineer | Kubernetes | Homelab" ``` ### menus.ru.toml ```bash # Переименовываем файл меню mv config/_default/menus.en.toml config/_default/menus.ru.toml ``` ```toml [[main]] name = "Блог" pageRef = "posts" weight = 10 [[main]] name = "О сайте" pageRef = "about" weight = 20 ``` --- ## Шаг 3: Первая статья ```bash # Создаём новую статью hugo new content posts/hello-world/index.md ``` ```markdown --- title: "Hello World" date: 2026-02-13 draft: false description: "Первый пост на новом сайте" tags: ["test"] --- Это первый пост на blog.ru. ``` --- ## Шаг 4: Проверяем локально ```bash # Запускаем локальный сервер Hugo # -D: показывать черновики (draft: true) # --bind 0.0.0.0: доступен с любого IP в локальной сети hugo server -D --bind 0.0.0.0 ``` Открываем `http://192.168.11.10:1313/` (ваш IP) - сайт с Blowfish темой и первой статьёй. Hugo автоматически пересобирает сайт при сохранении файлов - изменения видны сразу после обновления страницы в браузере. --- ## Шаг 5: Пушим в Gitea ```bash # Добавляем все файлы в Git git add . # Создаём первый коммит git commit -m "Initial commit: Hugo + Blowfish v2.98.0" # Переименовываем ветку в main (если по умолчанию master) git branch -M main # Пушим в Gitea git push -u origin main # Создаём ветку dev для development окружения git checkout -b dev git push -u origin dev # Возвращаемся на main git checkout main ``` --- ## Workflow: как я пишу статьи **Пишу (ветка dev):** ```bash # Переходим в папку проекта cd ~/projects/blog # Переключаемся на ветку dev git checkout dev # Запускаем локальный предпросмотр hugo server -D --bind 0.0.0.0 # Создаём новую статью hugo new content posts/название-статьи/index.md # Редактируем в любом редакторе, сохраняем, смотрим в браузере # Когда готово - коммитим git add . git commit -m "feat: новая статья про X" # Пушим в dev ветку git push origin dev # → webhook срабатывает → автосборка → dev.blog.ru ``` **Проверяю:** Открываю `https://dev.blog.ru` (вводя логин/пароль Basic Auth) - вижу статью как её увидят читатели. **Публикую:** ```bash # Переключаемся на main git checkout main # Мержим изменения из dev git merge dev # Пушим в production git push origin main # → webhook срабатывает → автосборка → blog.ru ``` Золотое правило: **все изменения только через ветку `dev`**. В `main` - только через merge. Никогда не редактировать файлы находясь на `main`. Почему это важно - расскажу отдельно, когда дойдём до того как я нарушил это правило и что из этого вышло. --- ## Что дальше Локально всё работает. Но `hugo server` умрёт как только закрою терминал. Нужно развернуть это в K3s: Hugo Builder который пересобирает сайт при каждом пуше, Nginx который раздаёт статику, SSL сертификаты, NFS хранилище. Об этом - в следующей части. --- **Стек этой части:** - Hugo v0.155.3 extended - Blowfish v2.98.0 - Gitea (внутренний) - Рабочая станция: Debian/Ubuntu