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

307 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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