oakazanin/content/posts/blog-part-1-architecture/index.md

11 KiB
Raw Blame History

title date draft description tags categories series series_order showComments
Блог на Hugo в K3s: часть 1 - архитектура и первый запуск 2026-01-03 false Как я переехал с Jekyll на Hugo, почему выбрал тему Blowfish и как настроил два окружения с нуля. Начало цикла о том как я строил oakazanin.ru.
hugo
blowfish
gitea
homelab
devops
infrastructure
Блог на Hugo в K3s
1 true

Предыдущий блог жил на 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 проект

# Создаём новый 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

baseURL = "https://blog.ru/"
theme = "blowfish"
defaultContentLanguage = "ru"

languages.ru.toml

# Переименовываем файл языка для русского
mv config/_default/languages.en.toml config/_default/languages.ru.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

# Переименовываем файл меню
mv config/_default/menus.en.toml config/_default/menus.ru.toml
[[main]]
  name = "Блог"
  pageRef = "posts"
  weight = 10

[[main]]
  name = "О сайте"
  pageRef = "about"
  weight = 20

Шаг 3: Первая статья

# Создаём новую статью
hugo new content posts/hello-world/index.md
---
title: "Hello World"
date: 2026-02-13
draft: false
description: "Первый пост на новом сайте"
tags: ["test"]
---

Это первый пост на blog.ru.

Шаг 4: Проверяем локально

# Запускаем локальный сервер 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

# Добавляем все файлы в 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):

# Переходим в папку проекта
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) - вижу статью как её увидят читатели.

Публикую:

# Переключаемся на 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