Добавил серию Blog 1-5 части. Подправил настройки и внешний вид сайта
|
|
@ -11,7 +11,7 @@ autoSwitchAppearance = false
|
||||||
|
|
||||||
enableA11y = false
|
enableA11y = false
|
||||||
enableSearch = true
|
enableSearch = true
|
||||||
enableCodeCopy = true
|
enableCodeCopy = false
|
||||||
enableStructuredBreadcrumbs = false
|
enableStructuredBreadcrumbs = false
|
||||||
# enableStyledScrollbar = true # disable to use native scrollbar style (defaults to true)
|
# enableStyledScrollbar = true # disable to use native scrollbar style (defaults to true)
|
||||||
|
|
||||||
|
|
@ -70,8 +70,8 @@ forgejoDefaultServer = "https://v11.next.forgejo.org"
|
||||||
showDateUpdated = false
|
showDateUpdated = false
|
||||||
showAuthor = true
|
showAuthor = true
|
||||||
# showAuthorBottom = false
|
# showAuthorBottom = false
|
||||||
showHero = false
|
showHero = true
|
||||||
# heroStyle = "basic" # valid options: basic, big, background, thumbAndBackground
|
heroStyle = "background" # valid options: basic, big, background, thumbAndBackground
|
||||||
layoutBackgroundBlur = true # only used when heroStyle equals background or thumbAndBackground
|
layoutBackgroundBlur = true # only used when heroStyle equals background or thumbAndBackground
|
||||||
layoutBackgroundHeaderSpace = true # only used when heroStyle equals background
|
layoutBackgroundHeaderSpace = true # only used when heroStyle equals background
|
||||||
showBreadcrumbs = false
|
showBreadcrumbs = false
|
||||||
|
|
@ -85,8 +85,8 @@ forgejoDefaultServer = "https://v11.next.forgejo.org"
|
||||||
invertPagination = false
|
invertPagination = false
|
||||||
showReadingTime = true
|
showReadingTime = true
|
||||||
showTableOfContents = true
|
showTableOfContents = true
|
||||||
# showRelatedContent = false
|
showRelatedContent = true
|
||||||
# relatedContentLimit = 3
|
relatedContentLimit = 6
|
||||||
showTaxonomies = true # use showTaxonomies OR showCategoryOnly, not both
|
showTaxonomies = true # use showTaxonomies OR showCategoryOnly, not both
|
||||||
showCategoryOnly = false # use showTaxonomies OR showCategoryOnly, not both
|
showCategoryOnly = false # use showTaxonomies OR showCategoryOnly, not both
|
||||||
showAuthorsBadges = false
|
showAuthorsBadges = false
|
||||||
|
|
@ -96,8 +96,8 @@ forgejoDefaultServer = "https://v11.next.forgejo.org"
|
||||||
# externalLinkForceNewTab = false # disable to allow external links in the same tab (defaults to true)
|
# externalLinkForceNewTab = false # disable to allow external links in the same tab (defaults to true)
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
showHero = false
|
showHero = true
|
||||||
# heroStyle = "background" # valid options: basic, big, background, thumbAndBackground
|
heroStyle = "background" # valid options: basic, big, background, thumbAndBackground
|
||||||
layoutBackgroundBlur = true # only used when heroStyle equals background or thumbAndBackground
|
layoutBackgroundBlur = true # only used when heroStyle equals background or thumbAndBackground
|
||||||
layoutBackgroundHeaderSpace = true # only used when heroStyle equals background
|
layoutBackgroundHeaderSpace = true # only used when heroStyle equals background
|
||||||
showBreadcrumbs = false
|
showBreadcrumbs = false
|
||||||
|
|
@ -107,7 +107,7 @@ forgejoDefaultServer = "https://v11.next.forgejo.org"
|
||||||
showTableOfContents = false
|
showTableOfContents = false
|
||||||
showCards = false
|
showCards = false
|
||||||
orderByWeight = false
|
orderByWeight = false
|
||||||
groupByYear = true
|
groupByYear = false
|
||||||
cardView = false
|
cardView = false
|
||||||
cardViewScreenWidth = false
|
cardViewScreenWidth = false
|
||||||
constrainItemsWidth = false
|
constrainItemsWidth = false
|
||||||
|
|
@ -117,8 +117,8 @@ forgejoDefaultServer = "https://v11.next.forgejo.org"
|
||||||
|
|
||||||
[taxonomy]
|
[taxonomy]
|
||||||
showTermCount = true
|
showTermCount = true
|
||||||
showHero = false
|
showHero = true
|
||||||
# heroStyle = "background" # valid options: basic, big, background, thumbAndBackground
|
heroStyle = "background" # valid options: basic, big, background, thumbAndBackground
|
||||||
showBreadcrumbs = false
|
showBreadcrumbs = false
|
||||||
showViews = false
|
showViews = false
|
||||||
showLikes = false
|
showLikes = false
|
||||||
|
|
@ -126,8 +126,8 @@ forgejoDefaultServer = "https://v11.next.forgejo.org"
|
||||||
cardView = false
|
cardView = false
|
||||||
|
|
||||||
[term]
|
[term]
|
||||||
showHero = false
|
showHero = true
|
||||||
# heroStyle = "background" # valid options: basic, big, background, thumbAndBackground
|
heroStyle = "background" # valid options: basic, big, background, thumbAndBackground
|
||||||
showBreadcrumbs = false
|
showBreadcrumbs = false
|
||||||
showViews = false
|
showViews = false
|
||||||
showLikes = false
|
showLikes = false
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 1008 KiB |
|
|
@ -0,0 +1,306 @@
|
||||||
|
---
|
||||||
|
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
|
||||||
|
After Width: | Height: | Size: 1.4 MiB |
|
|
@ -0,0 +1,593 @@
|
||||||
|
---
|
||||||
|
title: "Блог на Hugo в K3s: часть 2 - деплой в кластер"
|
||||||
|
date: 2026-01-08
|
||||||
|
draft: false
|
||||||
|
description: "NFS хранилище, Hugo Builder с webhook listener, Nginx с Prometheus exporter и автоматический SSL от Let's Encrypt. Полный деплой production окружения."
|
||||||
|
tags: ["hugo", "k3s", "kubernetes", "nfs", "traefik", "cert-manager"]
|
||||||
|
categories: ["infrastructure"]
|
||||||
|
series: ["Блог на Hugo в K3s"]
|
||||||
|
series_order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
В первой части мы запустили Hugo локально. Сайт работает пока открыт терминал. Закрыл терминал - сайт умер.
|
||||||
|
|
||||||
|
Пора переносить это в K3s.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Архитектура деплоя
|
||||||
|
|
||||||
|
```
|
||||||
|
Git Push
|
||||||
|
↓
|
||||||
|
Gitea (внутренний)
|
||||||
|
↓ webhook POST
|
||||||
|
Hugo Builder
|
||||||
|
├→ git clone + submodule
|
||||||
|
├→ hugo --minify
|
||||||
|
└→ output → NFS
|
||||||
|
↓
|
||||||
|
/export/blog-public/
|
||||||
|
↓
|
||||||
|
Nginx (x2 реплики)
|
||||||
|
↓
|
||||||
|
Traefik Ingress
|
||||||
|
↓
|
||||||
|
your-blog.ru (SSL)
|
||||||
|
```
|
||||||
|
|
||||||
|
Пять компонентов:
|
||||||
|
1. **NFS** - хранилище для статики (OpenMediaVault)
|
||||||
|
2. **Hugo Builder** - пересобирает сайт при каждом пуше
|
||||||
|
3. **Nginx** - раздаёт статику с NFS
|
||||||
|
4. **cert-manager** - автоматический SSL от Let's Encrypt
|
||||||
|
5. **Traefik IngressRoute** - маршрутизация с SSL терминацией
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 1: NFS хранилище
|
||||||
|
|
||||||
|
Hugo собирает статику в HTML/CSS/JS файлы. Nginx раздаёт эти файлы. Значит нужно общее хранилище куда Hugo пишет, а Nginx читает.
|
||||||
|
|
||||||
|
NFS - самый простой вариант для homelab. У меня OpenMediaVault на отдельной машине.
|
||||||
|
|
||||||
|
### Создаём директории на NAS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Подключаемся к NAS (SSH на нестандартном порту для безопасности)
|
||||||
|
ssh -p 33322 nasadmin@192.168.11.30
|
||||||
|
|
||||||
|
# Создаём папки для production и development окружений
|
||||||
|
sudo mkdir -p /srv/storage/blog/blog-public
|
||||||
|
sudo mkdir -p /srv/storage/blog/blog-public-dev
|
||||||
|
|
||||||
|
# Выдаём права на запись (контейнеры пишут от root)
|
||||||
|
sudo chmod -R 775 /srv/storage/blog/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Почему SSH на порту 33322?** Стандартный порт 22 - первая цель сканеров и ботов. Нестандартный порт снижает шум в логах и количество brute-force попыток до нуля. Безопасность через скрытность работает для домашних серверов.
|
||||||
|
|
||||||
|
### Настраиваем NFS через OMV Web UI
|
||||||
|
|
||||||
|
Storage → Shared Folders → Create:
|
||||||
|
- Name: `blog-public`
|
||||||
|
- Device: основной диск
|
||||||
|
- Path: `/blog/blog-public`
|
||||||
|
|
||||||
|
Services → NFS → Shares → Create:
|
||||||
|
- Shared folder: `blog-public`
|
||||||
|
- Client: `192.168.11.0/24`
|
||||||
|
- Privilege: Read/Write
|
||||||
|
- Extra options: `rw,sync,no_subtree_check,no_root_squash`
|
||||||
|
|
||||||
|
То же для `blog-public-dev`.
|
||||||
|
|
||||||
|
**Критично:** `no_root_squash` - без этого контейнеры не смогут записывать файлы (они пишут от root внутри контейнера).
|
||||||
|
|
||||||
|
### Проверяем экспорт
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Заходим на NAS
|
||||||
|
ssh -p 33322 nasadmin@192.168.11.30
|
||||||
|
|
||||||
|
# Проверяем что NFS экспортирует наши шары
|
||||||
|
sudo exportfs -v | grep blog
|
||||||
|
|
||||||
|
# Ожидаемый вывод - две строки с настройками экспорта:
|
||||||
|
# /export/blog-public 192.168.11.0/24(rw,sync,no_root_squash,...)
|
||||||
|
# /export/blog-public-dev 192.168.11.0/24(rw,sync,no_root_squash,...)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 2: PersistentVolumes в K3s
|
||||||
|
|
||||||
|
K3s нужно сказать где лежат NFS шары. Создаём манифест с PersistentVolume ресурсами.
|
||||||
|
|
||||||
|
**Файл:** `02-pv.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: blog-public-pv
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 5Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
nfs:
|
||||||
|
server: 192.168.11.30 # IP вашего NAS
|
||||||
|
path: /export/blog-public
|
||||||
|
mountOptions:
|
||||||
|
- nfsvers=3
|
||||||
|
- hard
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: blog-public-dev-pv
|
||||||
|
spec:
|
||||||
|
capacity:
|
||||||
|
storage: 5Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
nfs:
|
||||||
|
server: 192.168.11.30
|
||||||
|
path: /export/blog-public-dev
|
||||||
|
mountOptions:
|
||||||
|
- nfsvers=3
|
||||||
|
- hard
|
||||||
|
```
|
||||||
|
|
||||||
|
**Почему NFSv3, а не NFSv4?** Потому что NFSv4.2 в K3s не работал - поды виснут в `ContainerCreating` с ошибкой `mount.nfs: No such file or directory`. NFSv3 работает стабильно. Не надо усложнять то что работает.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем манифест
|
||||||
|
kubectl apply -f 02-pv.yaml
|
||||||
|
|
||||||
|
# Проверяем что PV создались и привязались
|
||||||
|
kubectl get pv | grep blog
|
||||||
|
# blog-public-pv 5Gi RWX Bound blog/blog-public-pvc
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 3: Hugo Builder
|
||||||
|
|
||||||
|
Нужен контейнер который слушает webhook от Gitea, клонирует репозиторий и собирает Hugo.
|
||||||
|
|
||||||
|
### Зачем нужен Hugo Builder?
|
||||||
|
|
||||||
|
**Проблема:** Hugo генерирует статику командой `hugo`. Где её запускать? На локальной машине? Тогда нужно вручную заливать файлы на сервер после каждого изменения. Неудобно и ломает автоматизацию.
|
||||||
|
|
||||||
|
**Решение:** Контейнер который живёт в K3s, слушает webhook от Gitea и автоматически пересобирает сайт при каждом `git push`.
|
||||||
|
|
||||||
|
### Dockerfile
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM alpine:3.19
|
||||||
|
|
||||||
|
# Устанавливаем всё что нужно Hugo и Git
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
git nodejs npm bash curl wget \
|
||||||
|
libc6-compat libstdc++ ca-certificates
|
||||||
|
|
||||||
|
# Скачиваем Hugo Extended v0.155.3
|
||||||
|
WORKDIR /tmp
|
||||||
|
RUN wget https://github.com/gohugoio/hugo/releases/download/v0.155.3/hugo_extended_0.155.3_linux-amd64.tar.gz && \
|
||||||
|
tar -xzf hugo_extended_0.155.3_linux-amd64.tar.gz && \
|
||||||
|
cp hugo /usr/bin/hugo && \
|
||||||
|
chmod +x /usr/bin/hugo && \
|
||||||
|
rm -rf /tmp/*
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
# Копируем скрипты
|
||||||
|
COPY webhook-listener.sh /usr/local/bin/
|
||||||
|
COPY build.sh /usr/local/bin/
|
||||||
|
RUN chmod +x /usr/local/bin/*.sh
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["/usr/local/bin/webhook-listener.sh"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### build.sh - скрипт сборки Hugo
|
||||||
|
|
||||||
|
**Зачем:** Отдельный скрипт сборки нужен чтобы его можно было запускать не только из webhook listener, но и вручную для тестирования. Один скрипт - одна ответственность.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -e # Остановиться при первой ошибке
|
||||||
|
|
||||||
|
export GIT_TERMINAL_PROMPT=0 # Не запрашивать пароли интерактивно
|
||||||
|
|
||||||
|
REPO_URL="https://git.example.com/user/blog.git" # URL вашего Gitea репозитория
|
||||||
|
BRANCH="${BRANCH:-main}" # Ветка (передаётся через env)
|
||||||
|
OUTPUT_DIR="/mnt/blog-public" # Куда складывать собранную статику (NFS)
|
||||||
|
WORK_DIR="/tmp/build" # Временная папка для клонирования
|
||||||
|
|
||||||
|
# Чистим рабочую директорию от прошлой сборки
|
||||||
|
rm -rf ${WORK_DIR}
|
||||||
|
mkdir -p ${WORK_DIR}
|
||||||
|
|
||||||
|
# Клонируем репозиторий (только нужную ветку, без истории)
|
||||||
|
cd ${WORK_DIR}
|
||||||
|
git clone --branch ${BRANCH} --depth 1 ${REPO_URL} site 2>&1
|
||||||
|
cd site
|
||||||
|
|
||||||
|
# Подтягиваем тему Blowfish как Git submodule
|
||||||
|
git submodule update --init --recursive --depth 1 2>&1
|
||||||
|
|
||||||
|
# Собираем сайт (минифицируем CSS/JS/HTML)
|
||||||
|
hugo --minify --destination ${OUTPUT_DIR} 2>&1
|
||||||
|
|
||||||
|
# Проверяем что сборка прошла успешно
|
||||||
|
if [ -f "${OUTPUT_DIR}/index.html" ]; then
|
||||||
|
echo "Build successful!"
|
||||||
|
else
|
||||||
|
echo "Build failed - index.html not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Убираем за собой
|
||||||
|
rm -rf ${WORK_DIR}
|
||||||
|
```
|
||||||
|
|
||||||
|
### webhook-listener.sh - слушатель webhook
|
||||||
|
|
||||||
|
**Зачем:** Gitea отправляет HTTP POST запрос при каждом `git push`. Нужен простой HTTP сервер который принимает этот запрос и запускает сборку. netcat - самый простой способ поднять HTTP listener без зависимостей.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Starting webhook listener on port 8080..."
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
# Принимаем HTTP запрос через netcat и сразу отвечаем 200 OK
|
||||||
|
echo -e "HTTP/1.1 200 OK\r\n\r\nWebhook received" | nc -l -p 8080
|
||||||
|
|
||||||
|
# Запускаем сборку синхронно (чтобы видеть логи в kubectl logs)
|
||||||
|
echo "$(date): Webhook triggered, starting build..."
|
||||||
|
/usr/local/bin/build.sh
|
||||||
|
echo "$(date): Build completed, waiting for next webhook..."
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сборка и деплой образа
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Собираем Docker образ
|
||||||
|
docker build -t hugo-builder:latest .
|
||||||
|
|
||||||
|
# Сохраняем в tar файл
|
||||||
|
docker save hugo-builder:latest -o /tmp/hugo-builder.tar
|
||||||
|
|
||||||
|
# Копируем на все K3s worker ноды
|
||||||
|
for ip in 210 211; do
|
||||||
|
scp /tmp/hugo-builder.tar k3s@192.168.11.$ip:/tmp/
|
||||||
|
|
||||||
|
# Импортируем образ в containerd K3s
|
||||||
|
ssh k3s@192.168.11.$ip "sudo k3s ctr images import /tmp/hugo-builder.tar && rm /tmp/hugo-builder.tar"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment и Service
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: hugo-builder-prod
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: hugo-builder-prod
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: hugo-builder-prod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hugo-builder
|
||||||
|
image: hugo-builder:latest
|
||||||
|
imagePullPolicy: Never # Образ локальный, не тянуть из registry
|
||||||
|
env:
|
||||||
|
- name: BRANCH
|
||||||
|
value: "main" # Для prod используем main ветку
|
||||||
|
volumeMounts:
|
||||||
|
- name: public
|
||||||
|
mountPath: /mnt/blog-public # NFS хранилище
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
volumes:
|
||||||
|
- name: public
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: blog-public-pvc
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: hugo-builder-prod
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: hugo-builder-prod
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
name: webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем манифест
|
||||||
|
kubectl apply -f 01-hugo-builder-prod.yaml
|
||||||
|
|
||||||
|
# Проверяем что под запустился
|
||||||
|
kubectl get pods -n blog | grep hugo-builder
|
||||||
|
|
||||||
|
# Смотрим логи - должна быть строка "Starting webhook listener"
|
||||||
|
kubectl logs -n blog deployment/hugo-builder-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 4: Nginx с Prometheus exporter
|
||||||
|
|
||||||
|
Nginx раздаёт статику с того же NFS где Hugo её собрал. Две реплики для минимальной доступности при обновлениях.
|
||||||
|
|
||||||
|
Бонус: sidecar контейнер с nginx-prometheus-exporter для мониторинга через Grafana.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
replicas: 2 # Две реплики для доступности
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
# Основной контейнер - Nginx
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.25-alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
volumeMounts:
|
||||||
|
- name: html
|
||||||
|
mountPath: /usr/share/nginx/html
|
||||||
|
readOnly: true # Nginx только читает, не пишет
|
||||||
|
- name: config
|
||||||
|
mountPath: /etc/nginx/nginx.conf
|
||||||
|
subPath: nginx.conf
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 64Mi
|
||||||
|
|
||||||
|
# Sidecar - экспортер метрик для Prometheus
|
||||||
|
- name: nginx-exporter
|
||||||
|
image: nginx/nginx-prometheus-exporter:1.1.0
|
||||||
|
args:
|
||||||
|
- -nginx.scrape-uri=http://localhost/nginx_status
|
||||||
|
ports:
|
||||||
|
- containerPort: 9113
|
||||||
|
name: metrics
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 16Mi
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: html
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: blog-public-pvc # NFS хранилище
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: nginx-config
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 80
|
||||||
|
name: http
|
||||||
|
- port: 9113
|
||||||
|
targetPort: 9113
|
||||||
|
name: metrics # Для Prometheus
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 5: SSL сертификаты
|
||||||
|
|
||||||
|
cert-manager автоматически получает сертификаты от Let's Encrypt через HTTP-01 challenge.
|
||||||
|
|
||||||
|
**Важно:** Сначала настрой A-запись у DNS провайдера:
|
||||||
|
```
|
||||||
|
your-blog.ru A 77.37.XXX.XXX (ваш внешний IP)
|
||||||
|
www.your-blog.ru A 77.37.XXX.XXX
|
||||||
|
```
|
||||||
|
|
||||||
|
Без этого Let's Encrypt не сможет проверить что домен принадлежит вам.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: blog-tls
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
secretName: blog-tls
|
||||||
|
issuerRef:
|
||||||
|
name: letsencrypt-prod
|
||||||
|
kind: ClusterIssuer
|
||||||
|
dnsNames:
|
||||||
|
- your-blog.ru
|
||||||
|
- www.your-blog.ru
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем манифест
|
||||||
|
kubectl apply -f 04-certificate.yaml
|
||||||
|
|
||||||
|
# Ждём 30-60 секунд пока cert-manager получит сертификат
|
||||||
|
kubectl get certificate -n blog
|
||||||
|
|
||||||
|
# Должно быть READY=True
|
||||||
|
# NAME READY SECRET AGE
|
||||||
|
# blog-tls True blog-tls 45s
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 6: IngressRoute через Traefik
|
||||||
|
|
||||||
|
Traefik маршрутизирует трафик на Nginx и делает SSL терминацию.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
# HTTP → HTTPS редирект (опционально)
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: blog-http
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web # Порт 80
|
||||||
|
routes:
|
||||||
|
- match: Host(`your-blog.ru`) || Host(`www.your-blog.ru`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: nginx
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
# HTTPS с SSL
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: blog-https
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- websecure # Порт 443
|
||||||
|
routes:
|
||||||
|
- match: Host(`your-blog.ru`) || Host(`www.your-blog.ru`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: nginx
|
||||||
|
port: 80
|
||||||
|
tls:
|
||||||
|
secretName: blog-tls # Сертификат от cert-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем манифест
|
||||||
|
kubectl apply -f 05-ingressroute.yaml
|
||||||
|
|
||||||
|
# Проверяем что сайт доступен
|
||||||
|
curl -I https://your-blog.ru
|
||||||
|
# HTTP/2 200
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 7: Webhook в Gitea
|
||||||
|
|
||||||
|
Последний шаг - связать Gitea с Hugo Builder.
|
||||||
|
|
||||||
|
Gitea → ваш репозиторий → Settings → Webhooks → Add Webhook → Gitea
|
||||||
|
|
||||||
|
- **URL:** `http://hugo-builder-prod.blog.svc.cluster.local:8080`
|
||||||
|
- **HTTP Method:** POST
|
||||||
|
- **Content Type:** application/json
|
||||||
|
- **Trigger On:** Push events
|
||||||
|
- **Branch filter:** `main`
|
||||||
|
|
||||||
|
Нажимаем "Test Delivery" - должен вернуть `200 OK`.
|
||||||
|
|
||||||
|
Проверяем логи Hugo Builder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Следим за логами в реальном времени
|
||||||
|
kubectl logs -n blog deployment/hugo-builder-prod -f
|
||||||
|
|
||||||
|
# Должно появиться:
|
||||||
|
# Webhook triggered, starting build...
|
||||||
|
# Cloning repository...
|
||||||
|
# Initializing submodules...
|
||||||
|
# Building Hugo site...
|
||||||
|
# Build successful!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проверка работы
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Меняем статью
|
||||||
|
cd ~/hugo-projects/blog
|
||||||
|
git checkout main
|
||||||
|
echo "## Тестовая правка" >> content/posts/hello-world/index.md
|
||||||
|
|
||||||
|
# Коммитим и пушим
|
||||||
|
git add .
|
||||||
|
git commit -m "test: проверка автосборки"
|
||||||
|
git push origin main
|
||||||
|
|
||||||
|
# Следим за логами Hugo Builder
|
||||||
|
kubectl logs -n blog deployment/hugo-builder-prod -f
|
||||||
|
|
||||||
|
# Через 5-7 секунд сборка завершится
|
||||||
|
# Проверяем что изменение попало на сайт
|
||||||
|
curl -s https://your-blog.ru/posts/hello-world/ | grep "Тестовая правка"
|
||||||
|
```
|
||||||
|
|
||||||
|
Если видите "Тестовая правка" - всё работает. Каждый `git push` автоматически обновляет сайт.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что дальше
|
||||||
|
|
||||||
|
Production окружение развёрнуто. Но пока только для ветки `main`.
|
||||||
|
|
||||||
|
В следующей части добавим development окружение с отдельным Hugo Builder, Nginx и защитой через Basic Auth. Два независимых пайплайна в одном namespace.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Стек этой части:**
|
||||||
|
- K3s 1.30
|
||||||
|
- NFS на OpenMediaVault
|
||||||
|
- Hugo Builder (Alpine + Hugo v0.155.3)
|
||||||
|
- Nginx 1.25 + Prometheus exporter
|
||||||
|
- cert-manager + Let's Encrypt
|
||||||
|
- Traefik IngressRoute
|
||||||
|
After Width: | Height: | Size: 1.3 MiB |
|
|
@ -0,0 +1,504 @@
|
||||||
|
---
|
||||||
|
title: "Блог на Hugo в K3s: часть 3 - development окружение"
|
||||||
|
date: 2026-01-15
|
||||||
|
draft: false
|
||||||
|
description: "Второй пайплайн для ветки dev с отдельным Hugo Builder, Nginx и защитой через Basic Auth. Тестируем изменения перед публикацией в production."
|
||||||
|
tags: ["hugo", "k3s", "kubernetes", "traefik", "basic-auth"]
|
||||||
|
categories: ["infrastructure"]
|
||||||
|
series: ["Блог на Hugo в K3s"]
|
||||||
|
series_order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
В части 2 мы развернули production окружение для ветки `main`. Каждый пуш в `main` автоматически обновляет публичный сайт.
|
||||||
|
|
||||||
|
Проблема: нельзя проверить как выглядит статья до публикации. Локальный `hugo server` показывает одно, а production может выглядеть по-другому из-за версий Hugo, конфигов, CSS.
|
||||||
|
|
||||||
|
Нужен второй пайплайн - тестовый контур где можно проверить изменения перед мержем в `main`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Архитектура dev окружения
|
||||||
|
|
||||||
|
```
|
||||||
|
Git Push (dev branch)
|
||||||
|
↓
|
||||||
|
Gitea
|
||||||
|
↓ webhook
|
||||||
|
Hugo Builder Dev
|
||||||
|
├→ Clone dev branch
|
||||||
|
├→ hugo --minify
|
||||||
|
└→ Output → NFS (dev)
|
||||||
|
↓
|
||||||
|
/export/blog-public-dev/
|
||||||
|
↓
|
||||||
|
Nginx Dev (1 реплика)
|
||||||
|
↓
|
||||||
|
Traefik Ingress
|
||||||
|
├→ Basic Auth Middleware
|
||||||
|
└→ dev.blog.ru (SSL)
|
||||||
|
```
|
||||||
|
|
||||||
|
Отличия от production:
|
||||||
|
- Отдельный Hugo Builder (переменная `BRANCH=dev`)
|
||||||
|
- Отдельный NFS volume (`blog-public-dev`)
|
||||||
|
- Отдельный Nginx (одна реплика вместо двух)
|
||||||
|
- **Basic Auth** - доступ только по логину и паролю
|
||||||
|
- Отдельный домен (`dev.blog.ru`)
|
||||||
|
|
||||||
|
Всё это живёт в том же namespace что и production. Два независимых пайплайна, нулевое пересечение.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 1: Hugo Builder для dev
|
||||||
|
|
||||||
|
Используем тот же Docker образ что и для production. Разница - в переменной окружения `BRANCH`.
|
||||||
|
|
||||||
|
**Файл:** `01-hugo-builder-dev.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: hugo-builder-dev
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: hugo-builder-dev
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: hugo-builder-dev
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hugo-builder
|
||||||
|
image: hugo-builder:latest
|
||||||
|
imagePullPolicy: Never
|
||||||
|
env:
|
||||||
|
- name: BRANCH
|
||||||
|
value: "dev" # Главное отличие - используем dev ветку
|
||||||
|
volumeMounts:
|
||||||
|
- name: public
|
||||||
|
mountPath: /mnt/blog-public
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
volumes:
|
||||||
|
- name: public
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: blog-public-dev-pvc # Отдельный PVC
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: hugo-builder-dev
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: hugo-builder-dev
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
name: webhook
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем манифест
|
||||||
|
kubectl apply -f 01-hugo-builder-dev.yaml
|
||||||
|
|
||||||
|
# Проверяем что под запустился
|
||||||
|
kubectl get pods -n blog | grep hugo-builder-dev
|
||||||
|
|
||||||
|
# Смотрим логи
|
||||||
|
kubectl logs -n blog deployment/hugo-builder-dev
|
||||||
|
# Starting webhook listener on port 8080...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 2: Nginx для dev
|
||||||
|
|
||||||
|
Одна реплика вместо двух - для тестового окружения высокая доступность не критична.
|
||||||
|
|
||||||
|
**Файл:** `03-nginx-dev-deployment.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-dev
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
replicas: 1 # Тестовому окружению достаточно одной реплики
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx-dev
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx-dev
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.25-alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
volumeMounts:
|
||||||
|
- name: html
|
||||||
|
mountPath: /usr/share/nginx/html
|
||||||
|
readOnly: true
|
||||||
|
- name: config
|
||||||
|
mountPath: /etc/nginx/nginx.conf
|
||||||
|
subPath: nginx.conf
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 64Mi
|
||||||
|
volumes:
|
||||||
|
- name: html
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: blog-public-dev-pvc # Отдельный PVC
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: nginx-dev-config
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: nginx-dev
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: nginx-dev
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 80
|
||||||
|
name: http
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем манифест
|
||||||
|
kubectl apply -f 03-nginx-dev-deployment.yaml
|
||||||
|
|
||||||
|
# Проверяем
|
||||||
|
kubectl get pods -n blog | grep nginx-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 3: Basic Auth через Traefik
|
||||||
|
|
||||||
|
Dev окружение должно быть закрыто от посторонних. Traefik поддерживает Basic Auth через Middleware.
|
||||||
|
|
||||||
|
### Создаём пароль
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Генерируем htpasswd (логин: dev, пароль: ваш пароль)
|
||||||
|
htpasswd -nb dev your-password
|
||||||
|
# dev:$apr1$...хеш...
|
||||||
|
|
||||||
|
# Кодируем в base64 для Kubernetes Secret
|
||||||
|
echo -n "dev:$apr1$...хеш..." | base64
|
||||||
|
# ZGV2OiRhcHIxJC4uLg==
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secret с паролем
|
||||||
|
|
||||||
|
**Файл:** `07-basic-auth-secret.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: dev-basic-auth
|
||||||
|
namespace: blog
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
users: ZGV2OiRhcHIxJC4uLg== # ваш base64 хеш
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем секрет
|
||||||
|
kubectl apply -f 07-basic-auth-secret.yaml
|
||||||
|
|
||||||
|
# Проверяем что секрет создался
|
||||||
|
kubectl get secret -n blog | grep dev-basic-auth
|
||||||
|
```
|
||||||
|
|
||||||
|
### Middleware для Basic Auth
|
||||||
|
|
||||||
|
**Файл:** `08-basic-auth-middleware.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: dev-basic-auth
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
basicAuth:
|
||||||
|
secret: dev-basic-auth
|
||||||
|
removeHeader: true # Убираем заголовок Authorization после проверки
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем middleware
|
||||||
|
kubectl apply -f 08-basic-auth-middleware.yaml
|
||||||
|
|
||||||
|
# Проверяем
|
||||||
|
kubectl get middleware -n blog
|
||||||
|
# NAME AGE
|
||||||
|
# dev-basic-auth 5s
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 4: IngressRoute с Basic Auth
|
||||||
|
|
||||||
|
Связываем всё вместе: домен → middleware → nginx-dev.
|
||||||
|
|
||||||
|
**Файл:** `06-ingressroute-dev.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
# HTTP (без SSL)
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: blog-dev-http
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
routes:
|
||||||
|
- match: Host(`dev.blog.ru`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: nginx-dev
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
# HTTPS с Basic Auth
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: blog-dev-https
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
routes:
|
||||||
|
- match: Host(`dev.blog.ru`)
|
||||||
|
kind: Rule
|
||||||
|
middlewares:
|
||||||
|
- name: dev-basic-auth # Добавляем Basic Auth
|
||||||
|
services:
|
||||||
|
- name: nginx-dev
|
||||||
|
port: 80
|
||||||
|
tls:
|
||||||
|
secretName: blog-dev-tls
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем IngressRoute
|
||||||
|
kubectl apply -f 06-ingressroute-dev.yaml
|
||||||
|
|
||||||
|
# Проверяем
|
||||||
|
kubectl get ingressroute -n blog | grep dev
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 5: SSL сертификат для dev
|
||||||
|
|
||||||
|
cert-manager выпустит отдельный сертификат для `dev.blog.ru`.
|
||||||
|
|
||||||
|
**Не забудьте:** Добавить A-запись в DNS:
|
||||||
|
```
|
||||||
|
dev.blog.ru A 77.37.XXX.XXX
|
||||||
|
```
|
||||||
|
|
||||||
|
**Файл:** `04-certificate-dev.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: blog-dev-tls
|
||||||
|
namespace: blog
|
||||||
|
spec:
|
||||||
|
secretName: blog-dev-tls
|
||||||
|
issuerRef:
|
||||||
|
name: letsencrypt-prod
|
||||||
|
kind: ClusterIssuer
|
||||||
|
dnsNames:
|
||||||
|
- dev.blog.ru
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Применяем манифест
|
||||||
|
kubectl apply -f 04-certificate-dev.yaml
|
||||||
|
|
||||||
|
# Ждём получения сертификата (30-60 секунд)
|
||||||
|
kubectl get certificate -n blog
|
||||||
|
|
||||||
|
# Должно быть READY=True
|
||||||
|
# NAME READY SECRET AGE
|
||||||
|
# blog-dev-tls True blog-dev-tls 45s
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 6: Webhook в Gitea для dev
|
||||||
|
|
||||||
|
Создаём второй webhook который триггерится на пуши в ветку `dev`.
|
||||||
|
|
||||||
|
Gitea → ваш репозиторий → Settings → Webhooks → Add Webhook → Gitea
|
||||||
|
|
||||||
|
- **URL:** `http://hugo-builder-dev.blog.svc.cluster.local:8080`
|
||||||
|
- **HTTP Method:** POST
|
||||||
|
- **Content Type:** application/json
|
||||||
|
- **Trigger On:** Push events
|
||||||
|
- **Branch filter:** `dev` ← Главное отличие от prod
|
||||||
|
|
||||||
|
Нажимаем "Test Delivery" → должен вернуть `200 OK`.
|
||||||
|
|
||||||
|
Проверяем логи:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим логи Hugo Builder Dev
|
||||||
|
kubectl logs -n blog deployment/hugo-builder-dev -f
|
||||||
|
|
||||||
|
# Должно появиться:
|
||||||
|
# Webhook triggered, starting build...
|
||||||
|
# Cloning repository (branch: dev)...
|
||||||
|
# Build successful!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проверка работы
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Создаём тестовую статью в dev
|
||||||
|
cd ~/projects/blog
|
||||||
|
git checkout dev
|
||||||
|
|
||||||
|
# Создаём статью
|
||||||
|
hugo new content posts/test-dev/index.md
|
||||||
|
echo "Тестовая статья в dev окружении" >> content/posts/test-dev/index.md
|
||||||
|
|
||||||
|
# Коммитим и пушим
|
||||||
|
git add .
|
||||||
|
git commit -m "test: проверка dev окружения"
|
||||||
|
git push origin dev
|
||||||
|
|
||||||
|
# Следим за логами Hugo Builder Dev
|
||||||
|
kubectl logs -n blog deployment/hugo-builder-dev -f
|
||||||
|
|
||||||
|
# Через 5-7 секунд сборка завершится
|
||||||
|
```
|
||||||
|
|
||||||
|
Открываем `https://dev.blog.ru` в браузере:
|
||||||
|
|
||||||
|
1. Браузер запросит логин и пароль (Basic Auth)
|
||||||
|
2. Вводим: логин `dev`, пароль который задали
|
||||||
|
3. Видим тестовую статью
|
||||||
|
|
||||||
|
**Критично:** Статья появилась на `dev.blog.ru`, но её **нет** на `blog.ru` - окружения изолированы.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflow: dev → prod
|
||||||
|
|
||||||
|
Типичный процесс работы:
|
||||||
|
|
||||||
|
**1. Пишу статью в dev:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/blog
|
||||||
|
git checkout dev
|
||||||
|
|
||||||
|
# Создаю статью
|
||||||
|
hugo new content posts/kubernetes-intro/index.md
|
||||||
|
|
||||||
|
# Пишу контент, коммичу
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: статья про Kubernetes"
|
||||||
|
git push origin dev
|
||||||
|
# → автосборка → dev.blog.ru
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Проверяю на dev.blog.ru:**
|
||||||
|
|
||||||
|
Открываю `https://dev.blog.ru` (вводя логин/пароль), читаю статью, проверяю форматирование, ссылки, изображения.
|
||||||
|
|
||||||
|
Нахожу опечатку - исправляю локально, пушу в `dev` снова. Повторяю пока не доволен результатом.
|
||||||
|
|
||||||
|
**3. Публикую в production:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Всё отлично на dev - мержу в main
|
||||||
|
git checkout main
|
||||||
|
git merge dev
|
||||||
|
git push origin main
|
||||||
|
# → автосборка → blog.ru
|
||||||
|
```
|
||||||
|
|
||||||
|
Статья появляется на публичном сайте.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Итоговая архитектура
|
||||||
|
|
||||||
|
Два полностью независимых пайплайна в одном namespace:
|
||||||
|
|
||||||
|
```
|
||||||
|
Production:
|
||||||
|
main branch → hugo-builder-prod → blog-public-pvc → nginx (x2) → blog.ru
|
||||||
|
|
||||||
|
Development:
|
||||||
|
dev branch → hugo-builder-dev → blog-public-dev-pvc → nginx-dev (x1) → dev.blog.ru + Basic Auth
|
||||||
|
```
|
||||||
|
|
||||||
|
Общее:
|
||||||
|
- Namespace: `blog`
|
||||||
|
- Gitea репозиторий
|
||||||
|
- Docker образ Hugo Builder
|
||||||
|
- Traefik IngressRoute
|
||||||
|
- cert-manager
|
||||||
|
|
||||||
|
Отдельное:
|
||||||
|
- Deployments
|
||||||
|
- Services
|
||||||
|
- PersistentVolumes
|
||||||
|
- SSL сертификаты
|
||||||
|
- Домены
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что дальше
|
||||||
|
|
||||||
|
Два окружения работают. Можно писать статьи, проверять на dev, публиковать в production.
|
||||||
|
|
||||||
|
Но есть проблема: я долго работал в двух папках - `~/projects/blog` (main) и `~/projects/blog-dev` (dev). Это создавало конфликты при merge, рассинхронизацию веток и головную боль.
|
||||||
|
|
||||||
|
В следующей части расскажу как я от этого избавился и почему одна папка с переключением веток лучше чем две отдельные папки.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Стек этой части:**
|
||||||
|
- Hugo Builder Dev (та же версия Hugo)
|
||||||
|
- Nginx Dev (одна реплика)
|
||||||
|
- Traefik Basic Auth Middleware
|
||||||
|
- cert-manager (отдельный сертификат)
|
||||||
|
- NFS (отдельный volume)
|
||||||
|
After Width: | Height: | Size: 1.4 MiB |
|
|
@ -0,0 +1,417 @@
|
||||||
|
---
|
||||||
|
title: "Блог на Hugo в K3s: часть 4 - выбор Git workflow"
|
||||||
|
date: 2026-02-16
|
||||||
|
draft: false
|
||||||
|
description: "Две папки или одна с переключением веток? Разбираем варианты организации работы с dev и production окружениями на практических примерах."
|
||||||
|
tags: ["git", "workflow", "devops", "hugo"]
|
||||||
|
categories: ["infrastructure"]
|
||||||
|
series: ["Блог на Hugo в K3s"]
|
||||||
|
series_order: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
В части 3 мы развернули два окружения - production и development. Один репозиторий, две ветки (`main` и `dev`), два пайплайна.
|
||||||
|
|
||||||
|
Теперь встаёт вопрос: **как организовать работу локально?**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Проблема
|
||||||
|
|
||||||
|
У нас есть:
|
||||||
|
- Репозиторий в Gitea
|
||||||
|
- Две ветки: `main` (production) и `dev` (development)
|
||||||
|
- Необходимость постоянно переключаться между ними
|
||||||
|
|
||||||
|
Как это делать на локальной машине? Два варианта.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Вариант А: Две отдельные папки
|
||||||
|
|
||||||
|
Клонируем репозиторий дважды - в разные папки:
|
||||||
|
|
||||||
|
```
|
||||||
|
~/projects/
|
||||||
|
├── blog/ ← ветка main (production)
|
||||||
|
└── blog-dev/ ← ветка dev (development)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Логика:** Хочу работать с dev - иду в `blog-dev`. Хочу что-то проверить в production - иду в `blog`. Без переключения веток.
|
||||||
|
|
||||||
|
### Кажущиеся преимущества
|
||||||
|
|
||||||
|
**Параллельная работа.** Можно держать открытыми два терминала - в одном `hugo server` для dev, в другом смотреть production код.
|
||||||
|
|
||||||
|
**Изоляция.** Каждая папка - своя песочница. Изменения в одной не влияют на другую.
|
||||||
|
|
||||||
|
**Простота навигации.** `cd blog-dev` вместо `git checkout dev`. Меньше команд.
|
||||||
|
|
||||||
|
**Привычный паттерн.** Многие админы и разработчики держат несколько клонов для разных задач.
|
||||||
|
|
||||||
|
### Реальные проблемы
|
||||||
|
|
||||||
|
#### Проблема 1: Рассинхронизация локальных ветокin
|
||||||
|
|
||||||
|
Работаю в `blog-dev` - пишу статьи, коммичу, пушу в `origin/dev`. Всё хорошо.
|
||||||
|
|
||||||
|
Но локальная ветка `dev` в папке `blog` при этом **не обновляется**. Она отстаёт от `origin/dev`.
|
||||||
|
|
||||||
|
Приходишь делать merge:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd blog
|
||||||
|
git checkout main
|
||||||
|
git merge dev
|
||||||
|
# Already up to date. ← НО есть НЮАНС!
|
||||||
|
```
|
||||||
|
|
||||||
|
Git говорит "всё актуально", имея в виду **локальную** ветку `dev`, которая отстала на три коммита. Статьи не попадают в production.
|
||||||
|
|
||||||
|
Приходится помнить делать `git pull origin dev` перед каждым merge. Забыл - публикуешь устаревшую версию.
|
||||||
|
|
||||||
|
#### Проблема 2: Конфликты при merge
|
||||||
|
|
||||||
|
Редактируешь `config/params.toml` в обеих папках независимо:
|
||||||
|
- В `blog-dev` добавил Firebase конфиг
|
||||||
|
- В `blog` изменил название сайта
|
||||||
|
|
||||||
|
При merge Git честно сообщает о конфликте:
|
||||||
|
|
||||||
|
```
|
||||||
|
CONFLICT (content): Merge conflict in config/params.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
И это повторяется **каждый раз** когда трогаешь конфигурацию. Потому что две папки - это две независимые истории изменений одного файла.
|
||||||
|
|
||||||
|
#### Проблема 3: Работа не в той папке
|
||||||
|
|
||||||
|
Несколько раз ловил себя на том что редактирую статьи прямо в `blog` - папке production. Это нарушает весь смысл раздельных окружений.
|
||||||
|
|
||||||
|
#### Проблема 4: Умственная нагрузка
|
||||||
|
|
||||||
|
Постоянный вопрос "в какой папке я сейчас?" Для простого блога это лишняя когнитивная нагрузка.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Вариант Б: Одна папка с переключением веток
|
||||||
|
|
||||||
|
Один клон репозитория, работа через `git checkout`:
|
||||||
|
|
||||||
|
```
|
||||||
|
~/projects/
|
||||||
|
└── blog/ ← одна папка, две ветки: main и dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Как это работает
|
||||||
|
|
||||||
|
**Пишу статью:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/blog
|
||||||
|
|
||||||
|
# Переключаюсь на dev
|
||||||
|
git checkout dev
|
||||||
|
|
||||||
|
# Проверяю что dev актуален
|
||||||
|
git pull origin dev
|
||||||
|
|
||||||
|
# Запускаю локальный сервер
|
||||||
|
hugo server -D --bind 0.0.0.0
|
||||||
|
|
||||||
|
# Создаю статью
|
||||||
|
hugo new content posts/название/index.md
|
||||||
|
|
||||||
|
# Коммичу и пушу
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: новая статья"
|
||||||
|
git push origin dev
|
||||||
|
```
|
||||||
|
|
||||||
|
**Публикую:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Убеждаюсь что dev актуален
|
||||||
|
git checkout dev
|
||||||
|
git pull origin dev
|
||||||
|
|
||||||
|
# Переключаюсь на main и мержу
|
||||||
|
git checkout main
|
||||||
|
git pull origin main
|
||||||
|
git merge dev
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Реальные преимущества
|
||||||
|
|
||||||
|
**Никакой рассинхронизации.** Все ветки в одном репозитории. `git pull` обновляет всё что нужно.
|
||||||
|
|
||||||
|
**Нет конфликтов из-за независимых изменений.** Когда работаешь в одной папке, `params.toml` существует в одном экземпляре. Все изменения делаются в `dev`, в `main` попадают только через merge.
|
||||||
|
|
||||||
|
Конфликт возможен только если кто-то редактирует `main` напрямую - а это нарушение workflow.
|
||||||
|
|
||||||
|
**Невозможно ошибиться с веткой.** `git branch` показывает где ты сейчас. Случайно отредактировать файлы в `main` - сложнее.
|
||||||
|
|
||||||
|
**Меньше места на диске.** Один клон вместо двух. Один `.git` вместо двух.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сравнение на практических примерах
|
||||||
|
|
||||||
|
### Пример 1: Обновление темы Blowfish
|
||||||
|
|
||||||
|
**Две папки:**
|
||||||
|
```bash
|
||||||
|
cd blog-dev
|
||||||
|
git submodule update --remote themes/blowfish
|
||||||
|
git add themes/blowfish
|
||||||
|
git commit -m "update: Blowfish theme"
|
||||||
|
git push origin dev
|
||||||
|
|
||||||
|
# Проверяешь на dev.blog.ru
|
||||||
|
# Если всё ок - мержишь
|
||||||
|
cd ../blog
|
||||||
|
git checkout dev
|
||||||
|
git pull origin dev # ← ЛЕГКО ЗАБЫТЬ
|
||||||
|
git checkout main
|
||||||
|
git merge dev
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
**Одна папка:**
|
||||||
|
```bash
|
||||||
|
cd blog
|
||||||
|
git checkout dev
|
||||||
|
git pull origin dev
|
||||||
|
git submodule update --remote themes/blowfish
|
||||||
|
git add themes/blowfish
|
||||||
|
git commit -m "update: Blowfish theme"
|
||||||
|
git push origin dev
|
||||||
|
|
||||||
|
# Проверяешь на dev.blog.ru
|
||||||
|
# Если всё ок - мержишь
|
||||||
|
git checkout main
|
||||||
|
git pull origin main
|
||||||
|
git merge dev
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
Меньше команд, меньше переходов между папками, меньше шансов забыть `git pull`.
|
||||||
|
|
||||||
|
### Пример 2: Правка опечатки в production
|
||||||
|
|
||||||
|
Нашёл опечатку на `blog.ru`. Нужно исправить быстро.
|
||||||
|
|
||||||
|
**Две папки:**
|
||||||
|
|
||||||
|
Опасность: хочется исправить прямо в `blog` (ветка `main`). Это нарушает workflow - все изменения должны идти через `dev`.
|
||||||
|
|
||||||
|
Правильно:
|
||||||
|
```bash
|
||||||
|
cd blog-dev
|
||||||
|
git checkout dev
|
||||||
|
# Исправляешь
|
||||||
|
git commit -m "fix: опечатка"
|
||||||
|
git push origin dev
|
||||||
|
cd ../blog
|
||||||
|
git checkout main
|
||||||
|
git pull origin dev # ← опять легко забыть
|
||||||
|
git merge dev
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
**Одна папка:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd blog
|
||||||
|
git checkout dev
|
||||||
|
git pull origin dev
|
||||||
|
# Исправляешь
|
||||||
|
git commit -m "fix: опечатка"
|
||||||
|
git push origin dev
|
||||||
|
git checkout main
|
||||||
|
git merge dev
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
Проще, меньше команд, понятнее.
|
||||||
|
|
||||||
|
### Пример 3: Долгая работа над статьёй
|
||||||
|
|
||||||
|
Пишешь большую статью несколько дней. Между сеансами работы кто-то (или ты сам) запушил другие изменения в `dev`.
|
||||||
|
|
||||||
|
**Две папки:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd blog-dev
|
||||||
|
# День 1: пишешь
|
||||||
|
git add .
|
||||||
|
git commit -m "wip: статья"
|
||||||
|
|
||||||
|
# День 2: продолжаешь
|
||||||
|
git pull origin dev # Подтягиваешь чужие изменения
|
||||||
|
# Пишешь дальше
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: закончил статью"
|
||||||
|
git push origin dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Всё так же как и с одной папкой. Разницы нет.
|
||||||
|
|
||||||
|
**Одна папка:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd blog
|
||||||
|
git checkout dev
|
||||||
|
|
||||||
|
# День 1: пишешь
|
||||||
|
git add .
|
||||||
|
git commit -m "wip: статья"
|
||||||
|
|
||||||
|
# День 2: продолжаешь
|
||||||
|
git pull origin dev # Подтягиваешь чужие изменения
|
||||||
|
# Пишешь дальше
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: закончил статью"
|
||||||
|
git push origin dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Идентично. Этот пример работает одинаково в обоих вариантах.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что выбрать?
|
||||||
|
|
||||||
|
**Если ты только начинаешь - сразу делай одну папку.**
|
||||||
|
|
||||||
|
Две папки кажутся удобными, но создают проблемы которые регулярно прерывают работу:
|
||||||
|
- Рассинхронизация веток
|
||||||
|
- Конфликты при merge
|
||||||
|
- Когнитивная нагрузка
|
||||||
|
|
||||||
|
Одна папка с переключением веток - стандартный Git workflow, проверенный миллионами разработчиков. Требует чуть больше дисциплины (`git checkout dev` вместо `cd blog-dev`), но избавляет от всех проблем выше.
|
||||||
|
|
||||||
|
**Золотое правило:** Никогда не редактировать файлы находясь на ветке `main`. Все изменения - через `dev`. Всегда.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Миграция: если начал с двух папок
|
||||||
|
|
||||||
|
Если уже работаешь в двух папках - переход простой.
|
||||||
|
|
||||||
|
### Шаг 1: Убеждаемся что всё запушено
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверяем обе папки
|
||||||
|
cd ~/projects/blog-dev
|
||||||
|
git status
|
||||||
|
git push origin dev
|
||||||
|
|
||||||
|
cd ~/projects/blog
|
||||||
|
git status
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 2: Синхронизируем ветку dev в основной папке
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/blog
|
||||||
|
|
||||||
|
# Обновляем локальную ветку dev из remote
|
||||||
|
git checkout dev
|
||||||
|
git pull origin dev
|
||||||
|
|
||||||
|
# Проверяем что всё актуально
|
||||||
|
git log --oneline -5
|
||||||
|
|
||||||
|
# Возвращаемся на main
|
||||||
|
git checkout main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 3: Удаляем вторую папку
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Убеждаемся что в blog-dev нет несохранённых изменений
|
||||||
|
cd ~/projects/blog-dev
|
||||||
|
git status
|
||||||
|
# Должно быть: nothing to commit, working tree clean
|
||||||
|
|
||||||
|
# Удаляем папку
|
||||||
|
cd ~/projects
|
||||||
|
rm -rf blog-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 4: Проверяем что всё работает
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/blog
|
||||||
|
|
||||||
|
# Переключаемся на dev и запускаем сервер
|
||||||
|
git checkout dev
|
||||||
|
hugo server -D --bind 0.0.0.0
|
||||||
|
|
||||||
|
# Открываем http://localhost:1313/
|
||||||
|
# Видим dev версию сайта
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Новый workflow: шпаргалка
|
||||||
|
|
||||||
|
### Создаю статью
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/blog
|
||||||
|
|
||||||
|
git checkout dev
|
||||||
|
hugo new content posts/название/index.md
|
||||||
|
|
||||||
|
# Пишу, сохраняю, проверяю в hugo server
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: название статьи"
|
||||||
|
git push origin dev # → dev.blog.ru
|
||||||
|
```
|
||||||
|
|
||||||
|
### Публикую статью
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout dev
|
||||||
|
git pull origin dev # Убеждаюсь что dev актуален
|
||||||
|
|
||||||
|
git checkout main
|
||||||
|
git pull origin main # Убеждаюсь что main актуален
|
||||||
|
git merge dev
|
||||||
|
git push origin main # → blog.ru
|
||||||
|
```
|
||||||
|
|
||||||
|
### Меняю конфигурацию
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout dev # ВСЕ изменения только через dev!
|
||||||
|
nano config/params.toml
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: изменил конфиг"
|
||||||
|
git push origin dev # Проверяю на dev.blog.ru
|
||||||
|
|
||||||
|
# Если всё ок
|
||||||
|
git checkout main
|
||||||
|
git merge dev
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что дальше
|
||||||
|
|
||||||
|
Workflow выбран, окружения работают. Можно писать статьи.
|
||||||
|
|
||||||
|
Но есть ещё одна тема которую стоит разобрать - что делать когда что-то сломалось. Как диагностировать проблемы когда сайт вдруг начал отдавать 503, или SSL перестал работать, или webhook не срабатывает.
|
||||||
|
|
||||||
|
В следующей части покажу процесс диагностики на реальном примере - как я чинил `blog.ru` когда он внезапно стал недоступен из интернета.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Рекомендация этой части:**
|
||||||
|
- Одна папка `~/projects/blog`
|
||||||
|
- Переключение веток через `git checkout`
|
||||||
|
- Все изменения через `dev` → merge в `main`
|
||||||
|
- Никогда не редактировать находясь на `main`
|
||||||
|
After Width: | Height: | Size: 1.3 MiB |
|
|
@ -0,0 +1,634 @@
|
||||||
|
---
|
||||||
|
title: "Блог на Hugo в K3s: часть 5 - что делать когда всё внезапно сломалось"
|
||||||
|
date: 2026-02-17
|
||||||
|
draft: false
|
||||||
|
description: "Сайт работал вчера, а сегодня 503. Алгоритм диагностики Kubernetes проблем за 5 минут - от DNS до пода, без паники и танцев с бубном."
|
||||||
|
tags: ["kubernetes", "k3s", "traefik", "debugging", "nginx", "troubleshooting"]
|
||||||
|
categories: ["infrastructure"]
|
||||||
|
series: ["Блог на Hugo в K3s"]
|
||||||
|
series_order: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
В части 4 мы разобрались с Git workflow. Всё работает: пушишь в `dev` - видишь на тестовом окружении, мержишь в `main` - публикуется на production.
|
||||||
|
|
||||||
|
А потом в один прекрасный день открываешь свой сайт и видишь `503 Service Temporarily Unavailable`.
|
||||||
|
|
||||||
|
Вчера же все работало! Ты ничего не менял. Что произошло?
|
||||||
|
|
||||||
|
Добро пожаловать в мир эксплуатации Kubernetes, где проблемы тоже возникают и требуют системного подхода без паники.
|
||||||
|
|
||||||
|
Эта статья - алгоритм диагностики от DNS до пода. Проходишь по шагам сверху вниз, находишь проблему за 5 минут. Не гадаешь, не тыкаешь наугад - работаешь по системе.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Анатомия HTTP запроса в K3s
|
||||||
|
|
||||||
|
Прежде чем искать проблему, нужно понять путь запроса от браузера до nginx:
|
||||||
|
|
||||||
|
```
|
||||||
|
Браузер
|
||||||
|
↓ DNS запрос
|
||||||
|
DNS сервер (провайдер или Cloudflare)
|
||||||
|
↓ Возвращает IP адрес
|
||||||
|
Роутер/Файрвол (OPNsense, MikroTik)
|
||||||
|
↓ Port Forward 443 → K3s node
|
||||||
|
MetalLB LoadBalancer
|
||||||
|
↓ External IP
|
||||||
|
Traefik Ingress Controller
|
||||||
|
↓ IngressRoute matching
|
||||||
|
Kubernetes Service
|
||||||
|
↓ Endpoint selection
|
||||||
|
Pod (Nginx контейнер)
|
||||||
|
↓ Volume mount
|
||||||
|
NFS хранилище
|
||||||
|
```
|
||||||
|
|
||||||
|
Проблема может быть на любом из этих уровней. Секрет эффективной диагностики - проверять снаружи внутрь, последовательно исключая рабочие компоненты.
|
||||||
|
|
||||||
|
Когда тыкаешь наугад, проверяя сначала поды, потом DNS, потом снова поды - тратишь время. Когда идёшь по алгоритму - находишь проблему за минуты.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 1: DNS - доходит ли домен до твоего IP
|
||||||
|
|
||||||
|
Первым делом проверяем что домен резолвится в правильный IP. Без этого дальше проверять бессмысленно - браузер просто не знает куда направлять запрос.
|
||||||
|
|
||||||
|
### Проверка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверяем DNS резолв (используй свой домен)
|
||||||
|
dig blog.example.com +short
|
||||||
|
|
||||||
|
# Альтернатива если dig не установлен
|
||||||
|
nslookup blog.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ожидаемый результат
|
||||||
|
|
||||||
|
```
|
||||||
|
77.37.XXX.XXX
|
||||||
|
```
|
||||||
|
|
||||||
|
Должен вернуться твой **публичный IP адрес** (тот который прописан в A-записи у DNS провайдера).
|
||||||
|
|
||||||
|
### Что может пойти не так
|
||||||
|
|
||||||
|
| Симптом | Причина | Как проверить | Решение |
|
||||||
|
|---------|---------|---------------|---------|
|
||||||
|
| Возвращается старый IP | DNS кеш не обновился | `dig blog.example.com @8.8.8.8` | Подожди TTL (обычно 300-3600 сек) |
|
||||||
|
| `NXDOMAIN` ошибка | Домен не делегирован | Проверь NS записи у регистратора | Настрой DNS правильно |
|
||||||
|
| Возвращается `127.0.0.1` | Локальный override | `cat /etc/hosts \| grep blog` | Удали строку из /etc/hosts |
|
||||||
|
| Возвращается несколько IP | Round-robin DNS | Проверь все ли IP твои | Удали лишние A-записи |
|
||||||
|
|
||||||
|
Если DNS правильный - идём дальше.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 2: Внешний доступ - доходит ли запрос до сервера
|
||||||
|
|
||||||
|
Теперь проверяем что запрос физически доходит до сервера. DNS может быть правильным, но файрвол может блокировать трафик.
|
||||||
|
|
||||||
|
### Проверка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Пробуем подключиться извне (важно - НЕ из локальной сети!)
|
||||||
|
curl -v https://blog.example.com 2>&1 | head -30
|
||||||
|
```
|
||||||
|
|
||||||
|
**Важно:** Запускай эту команду с **внешнего** сервера или используй мобильный интернет. Тест из локальной сети ничего не докажет - можешь обходить файрвол.
|
||||||
|
|
||||||
|
### Ситуация А: Connection refused или timeout
|
||||||
|
|
||||||
|
```
|
||||||
|
curl: (7) Failed to connect to blog.example.com port 443: Connection refused
|
||||||
|
```
|
||||||
|
|
||||||
|
Запрос вообще не дошёл до сервера. Проблема на сетевом уровне.
|
||||||
|
|
||||||
|
**Возможные причины:**
|
||||||
|
|
||||||
|
**1. Порт 443 закрыт на файрволе/роутере**
|
||||||
|
|
||||||
|
Проверь Port Forward правила на OPNsense/MikroTik. Должно быть:
|
||||||
|
```
|
||||||
|
WAN:443 → 192.168.X.X:443 (IP любой K3s ноды)
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. MetalLB не назначил External IP для Traefik**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверяем MetalLB
|
||||||
|
kubectl get svc -n traefik traefik
|
||||||
|
|
||||||
|
# Ожидаемый результат
|
||||||
|
NAME TYPE EXTERNAL-IP PORT(S)
|
||||||
|
traefik LoadBalancer 192.168.X.X 80:30080/TCP,443:30443/TCP
|
||||||
|
```
|
||||||
|
|
||||||
|
Если `EXTERNAL-IP` показывает `<pending>` - MetalLB не работает или пул IP адресов не настроен.
|
||||||
|
|
||||||
|
**3. Traefik под не запущен**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверяем что Traefik работает
|
||||||
|
kubectl get pods -n traefik
|
||||||
|
|
||||||
|
# Должны быть все Running
|
||||||
|
NAME READY STATUS
|
||||||
|
traefik-xxxxxxxxxx-xxxxx 1/1 Running
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ситуация Б: TLS handshake прошёл, но 503
|
||||||
|
|
||||||
|
```
|
||||||
|
< HTTP/2 503
|
||||||
|
< content-type: text/plain; charset=utf-8
|
||||||
|
< content-length: 20
|
||||||
|
no available server
|
||||||
|
```
|
||||||
|
|
||||||
|
Отлично - наша ситуация! Traefik работает, SSL сертификат отдаёт, но дальше запрос упирается в стену.
|
||||||
|
|
||||||
|
Сообщение `no available server` означает что Traefik **нашёл роутер**, но **не нашёл живой бэкенд** за ним.
|
||||||
|
|
||||||
|
Проблема внутри кластера. Идём глубже.
|
||||||
|
|
||||||
|
### Ситуация В: SSL certificate problem
|
||||||
|
|
||||||
|
```
|
||||||
|
curl: (60) SSL certificate problem: unable to get local issuer certificate
|
||||||
|
```
|
||||||
|
|
||||||
|
Сертификат невалидный или не выпущен.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверяем Certificate объект (используй свой namespace)
|
||||||
|
kubectl get certificate -n blog
|
||||||
|
|
||||||
|
# Должно быть READY=True
|
||||||
|
NAME READY SECRET AGE
|
||||||
|
blog-tls True blog-tls 2d
|
||||||
|
```
|
||||||
|
|
||||||
|
Если `READY=False` - cert-manager не смог выпустить сертификат.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим что пошло не так
|
||||||
|
kubectl describe certificate blog-tls -n blog
|
||||||
|
|
||||||
|
# Ищем секцию Events внизу вывода - там описание проблемы
|
||||||
|
```
|
||||||
|
|
||||||
|
### Главное: Запрос доходит до Traefik
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверка (с ВНЕШНЕГО сервера!)
|
||||||
|
curl -I https://blog.example.com
|
||||||
|
|
||||||
|
# Ожидаемый результат (любой из двух)
|
||||||
|
HTTP/2 200 # Всё работает
|
||||||
|
HTTP/2 503 # Traefik работает, но бэкенд недоступен
|
||||||
|
|
||||||
|
# Если connection refused/timeout - проблема в сети (см. выше)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 3: Traefik - правильно ли маршрутизируется трафик
|
||||||
|
|
||||||
|
Traefik получил запрос на твой домен. Что он с ним делает? Смотрим логи.
|
||||||
|
|
||||||
|
### Проверка логов Traefik
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим последние 50 строк логов Traefik
|
||||||
|
kubectl logs -n traefik deployment/traefik --tail=50
|
||||||
|
|
||||||
|
# Фильтруем только свой домен (убираем шум от других сервисов)
|
||||||
|
kubectl logs -n traefik deployment/traefik --tail=100 | grep blog.example
|
||||||
|
```
|
||||||
|
|
||||||
|
### Что искать в логах
|
||||||
|
|
||||||
|
**Нормальный запрос:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"request": "GET / HTTP/2.0",
|
||||||
|
"status": 200,
|
||||||
|
"size": 8994,
|
||||||
|
"router": "blog-blog-https-xxxxx@kubernetescrd",
|
||||||
|
"service": "blog-nginx-blog@kubernetescrd",
|
||||||
|
"backend": "http://10.42.2.40:80",
|
||||||
|
"duration": 12
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ключевые поля:
|
||||||
|
- **router:** Traefik нашёл нужный IngressRoute (`blog-blog-https`)
|
||||||
|
- **backend:** IP пода nginx куда проксируется запрос (`10.42.2.40:80`)
|
||||||
|
- **status:** HTTP код ответа от nginx (`200` = всё хорошо)
|
||||||
|
|
||||||
|
**Проблемный запрос:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"request": "GET / HTTP/2.0",
|
||||||
|
"status": 503,
|
||||||
|
"router": "blog-blog-https-xxxxx@kubernetescrd",
|
||||||
|
"error": "no available server"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Traefik нашёл роутер, но поле `backend` отсутствует - под недоступен или не существует.
|
||||||
|
|
||||||
|
### Проверяем список IngressRoute
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим все IngressRoute в кластере
|
||||||
|
kubectl get ingressroute -A
|
||||||
|
|
||||||
|
# Фильтруем только свой домен
|
||||||
|
kubectl get ingressroute -A | grep blog.example
|
||||||
|
```
|
||||||
|
|
||||||
|
**Важный момент:** Если один и тот же домен прописан в **двух разных IngressRoute** из разных namespace - Traefik будет балансировать между ними.
|
||||||
|
|
||||||
|
Например:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAMESPACE NAME AGE
|
||||||
|
blog blog-https 10d # СТАРЫЙ namespace
|
||||||
|
blog-new blog-https 2d # НОВЫЙ namespace
|
||||||
|
```
|
||||||
|
|
||||||
|
Оба IngressRoute имеют `match: Host('blog.example.com')`. Traefik видит оба, честно балансирует трафик 50/50.
|
||||||
|
|
||||||
|
Если один из бэкендов мёртв - половина запросов уходит в пустоту. 503 через раз.
|
||||||
|
|
||||||
|
**Решение:** Удалить старый IngressRoute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Удаляем дубль из старого namespace
|
||||||
|
kubectl delete ingressroute blog-https blog-http -n blog
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверяем синтаксис match
|
||||||
|
|
||||||
|
Traefik очень требователен к синтаксису. Частая ошибка - забыть backticks или скобки.
|
||||||
|
|
||||||
|
**Неправильно:**
|
||||||
|
```yaml
|
||||||
|
match: Host(blog.example.com) # Нет backticks
|
||||||
|
match: Host `blog.example.com` # Нет скобок вокруг Host
|
||||||
|
match: Host("blog.example.com") # Двойные кавычки вместо backticks
|
||||||
|
```
|
||||||
|
|
||||||
|
**Правильно:**
|
||||||
|
```yaml
|
||||||
|
match: Host(`blog.example.com`)
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверяем:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим манифест IngressRoute
|
||||||
|
kubectl get ingressroute blog-https -n blog -o yaml | grep match:
|
||||||
|
|
||||||
|
# Должно быть со скобками и backticks
|
||||||
|
match: Host(`blog.example.com`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Главное: Traefik нашёл роутер
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверка
|
||||||
|
kubectl logs -n traefik deployment/traefik --tail=50 | grep blog.example
|
||||||
|
|
||||||
|
# Ожидаемый результат - есть строки с "router": "blog-blog-https"
|
||||||
|
# Если router не найден - проблема в IngressRoute match синтаксисе
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 4: Service - видит ли он поды
|
||||||
|
|
||||||
|
Traefik нашёл роутер, проксирует трафик на Service. Но Service может не видеть поды если selector неправильный.
|
||||||
|
|
||||||
|
### Проверка endpoints
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим endpoints для Service (используй своё имя Service)
|
||||||
|
kubectl get endpoints nginx -n blog
|
||||||
|
|
||||||
|
# Ожидаемый результат - НЕ пустой список IP
|
||||||
|
NAME ENDPOINTS
|
||||||
|
nginx 10.42.0.44:80,10.42.2.40:80
|
||||||
|
```
|
||||||
|
|
||||||
|
Если видишь `<none>` - Service не нашёл ни одного пода. Две возможные причины.
|
||||||
|
|
||||||
|
### Причина 1: Selector не совпадает с labels
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим selector у Service
|
||||||
|
kubectl get svc nginx -n blog -o yaml | grep -A3 "selector:"
|
||||||
|
|
||||||
|
# Вывод
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
|
||||||
|
# Смотрим labels у подов
|
||||||
|
kubectl get pods -n blog --show-labels | grep nginx
|
||||||
|
|
||||||
|
# Вывод
|
||||||
|
nginx-xxxxxxxxxx-xxxxx 1/1 Running app=nginx-old
|
||||||
|
```
|
||||||
|
|
||||||
|
Видишь проблему? Service ищет `app: nginx`, а под помечен `app: nginx-old`. Не совпадает.
|
||||||
|
|
||||||
|
**Решение:** Исправить Deployment или Service чтобы labels совпадали.
|
||||||
|
|
||||||
|
### Причина 2: Поды не Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим статус подов
|
||||||
|
kubectl get pods -n blog
|
||||||
|
|
||||||
|
# Видим
|
||||||
|
NAME READY STATUS
|
||||||
|
nginx-xxxxxxxxxx-xxxxx 0/1 CreateContainerError
|
||||||
|
```
|
||||||
|
|
||||||
|
Под существует, но не работает. Service правильно не включает его в endpoints. Идём в следующий шаг - разбираемся почему под не запускается.
|
||||||
|
|
||||||
|
### Главное: Service видит поды
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверка
|
||||||
|
kubectl get endpoints nginx -n blog
|
||||||
|
|
||||||
|
# Ожидаемый результат - НЕ пустой
|
||||||
|
nginx 10.42.0.44:80,10.42.2.40:80
|
||||||
|
|
||||||
|
# Если <none> - проблема в селекторах или поды не Running
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 5: Pod - что происходит внутри контейнера
|
||||||
|
|
||||||
|
Самый глубокий уровень. Под не запускается или падает в цикле перезапусков.
|
||||||
|
|
||||||
|
### Проверка статуса подов
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим все поды в namespace
|
||||||
|
kubectl get pods -n blog
|
||||||
|
|
||||||
|
# Фильтруем только nginx
|
||||||
|
kubectl get pods -n blog | grep nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
**Возможные статусы проблем:**
|
||||||
|
|
||||||
|
### CreateContainerError
|
||||||
|
|
||||||
|
Контейнер вообще не может стартануть. Обычно проблема с volumes или образом.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим детали пода (используй своё имя пода)
|
||||||
|
kubectl describe pod nginx-xxxxxxxxxx-xxxxx -n blog | tail -30
|
||||||
|
```
|
||||||
|
|
||||||
|
Ищем секцию `Events` внизу вывода. Там будет описание проблемы:
|
||||||
|
|
||||||
|
**Пример 1: PVC не примонтировался**
|
||||||
|
|
||||||
|
```
|
||||||
|
Events:
|
||||||
|
Warning FailedMount MountVolume.SetUp failed for volume "blog-public-pvc":
|
||||||
|
mount failed: mount.nfs: Connection timed out
|
||||||
|
```
|
||||||
|
|
||||||
|
NFS хранилище недоступно. Возможные причины:
|
||||||
|
- NFS сервер выключен или перезагружается
|
||||||
|
- Неправильный IP или путь в PersistentVolume
|
||||||
|
- Файрвол блокирует NFS трафик (порт 2049)
|
||||||
|
|
||||||
|
**Пример 2: Образ не скачался**
|
||||||
|
|
||||||
|
```
|
||||||
|
Events:
|
||||||
|
Warning Failed Failed to pull image "nginx:latest": rpc error: code = Unknown
|
||||||
|
```
|
||||||
|
|
||||||
|
Контейнер не может скачать образ. Обычно это означает что `imagePullPolicy: Never`, а образ не импортирован на ноду.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверяем что образ есть на ноде (используй IP своей worker ноды)
|
||||||
|
ssh user@192.168.X.X "sudo k3s crictl images | grep nginx"
|
||||||
|
```
|
||||||
|
|
||||||
|
Если образа нет - импортируй его через `k3s ctr images import`.
|
||||||
|
|
||||||
|
**Пример 3: ConfigMap не найден**
|
||||||
|
|
||||||
|
```
|
||||||
|
Events:
|
||||||
|
Warning FailedMount ConfigMap "nginx-config" not found
|
||||||
|
```
|
||||||
|
|
||||||
|
Deployment ссылается на несуществующий ConfigMap.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверяем что ConfigMap существует
|
||||||
|
kubectl get configmap -n blog | grep nginx-config
|
||||||
|
```
|
||||||
|
|
||||||
|
Если нет - создай или исправь имя в Deployment.
|
||||||
|
|
||||||
|
### CrashLoopBackOff
|
||||||
|
|
||||||
|
Контейнер запускается, но сразу падает. Смотрим логи **предыдущего** запуска:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Логи последнего упавшего контейнера
|
||||||
|
kubectl logs nginx-xxxxxxxxxx-xxxxx -n blog --previous
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пример: Nginx падает из-за неправильного конфига**
|
||||||
|
|
||||||
|
```
|
||||||
|
nginx: [emerg] unexpected "}" in /etc/nginx/nginx.conf:15
|
||||||
|
nginx: configuration file /etc/nginx/nginx.conf test failed
|
||||||
|
```
|
||||||
|
|
||||||
|
Синтаксическая ошибка в `nginx.conf`. Проверяем ConfigMap:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Смотрим содержимое конфига
|
||||||
|
kubectl get configmap nginx-config -n blog -o yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Находим ошибку, исправляем, применяем. Под перезапустится автоматически.
|
||||||
|
|
||||||
|
### Главное: Под работает
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверка
|
||||||
|
kubectl get pods -n blog | grep nginx
|
||||||
|
|
||||||
|
# Ожидаемый результат - все Running
|
||||||
|
nginx-xxxxxxxxxx-xxxxx 1/1 Running 0 2d
|
||||||
|
|
||||||
|
# Если не Running - смотри troubleshooting выше
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаг 6: Контент - есть ли файлы для отдачи
|
||||||
|
|
||||||
|
Под работает, Service видит его, Traefik проксирует трафик. Но сайт отдаёт `404 Not Found` или пустую страницу.
|
||||||
|
|
||||||
|
Проблема: Hugo Builder не записал файлы на NFS или записал не туда.
|
||||||
|
|
||||||
|
### Проверка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Заходим в под nginx (используй своё имя пода)
|
||||||
|
kubectl exec -it nginx-xxxxxxxxxx-xxxxx -n blog -- sh
|
||||||
|
|
||||||
|
# Внутри пода смотрим что примонтировалось
|
||||||
|
ls -la /usr/share/nginx/html/
|
||||||
|
|
||||||
|
# Должен быть index.html и папки posts, tags, etc
|
||||||
|
```
|
||||||
|
|
||||||
|
**Если директория пустая** - Hugo Builder не сработал. Проверяем его логи:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Логи Hugo Builder
|
||||||
|
kubectl logs -n blog deployment/hugo-builder-prod --tail=50
|
||||||
|
```
|
||||||
|
|
||||||
|
Ищем строку `Build successful!` и список созданных файлов. Если её нет:
|
||||||
|
|
||||||
|
1. **Webhook не сработал** - проверь настройки webhook в Gitea
|
||||||
|
2. **Hugo упал с ошибкой** - читай логи выше, смотри на что ругается
|
||||||
|
3. **Собрал в другую директорию** - проверь переменную `OUTPUT_DIR` в build.sh
|
||||||
|
|
||||||
|
### Главное: Контент на месте
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Проверка (используй своё имя пода)
|
||||||
|
kubectl exec -it nginx-xxxxxxxxxx-xxxxx -n blog -- ls /usr/share/nginx/html/ | head -5
|
||||||
|
|
||||||
|
# Ожидаемый результат
|
||||||
|
index.html
|
||||||
|
posts/
|
||||||
|
tags/
|
||||||
|
categories/
|
||||||
|
|
||||||
|
# Если пусто - Hugo Builder не отработал (см. выше)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Быстрый чеклист для любой проблемы
|
||||||
|
|
||||||
|
Сохрани эту последовательность - она работает для 95% проблем:
|
||||||
|
|
||||||
|
```
|
||||||
|
[ ] DNS: dig домен → правильный IP?
|
||||||
|
[ ] Сеть: curl -v https://домен → доходит до Traefik?
|
||||||
|
[ ] MetalLB: kubectl get svc -n traefik → External IP назначен?
|
||||||
|
[ ] Traefik: kubectl get pods -n traefik → Running?
|
||||||
|
[ ] IngressRoute: kubectl get ingressroute -A | grep домен → нет дублей?
|
||||||
|
[ ] Match синтаксис: Host(`домен`) со скобками и backticks?
|
||||||
|
[ ] Endpoints: kubectl get endpoints -n namespace → не пустые?
|
||||||
|
[ ] Selector: labels подов совпадают с selector Service?
|
||||||
|
[ ] Pods: kubectl get pods -n namespace → все Running?
|
||||||
|
[ ] PVC: kubectl get pvc -n namespace → все Bound?
|
||||||
|
[ ] Контент: kubectl exec ls /usr/share/nginx/html → файлы есть?
|
||||||
|
```
|
||||||
|
|
||||||
|
Проходишь по списку сверху вниз. Останавливаешься на первом `[ ]` где что-то не так. Чинишь. Проверяешь снова.
|
||||||
|
|
||||||
|
Не прыгай хаотично между уровнями. Алгоритм экономит время.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Реальный пример: 503 через раз
|
||||||
|
|
||||||
|
Мой сайт отдавал `503` примерно в 50% запросов. Половина запросов работала, половина нет.
|
||||||
|
|
||||||
|
Прошёл по алгоритму:
|
||||||
|
|
||||||
|
1. ✅ DNS - правильный IP
|
||||||
|
2. ✅ Сеть - Traefik отвечает
|
||||||
|
3. ✅ MetalLB - External IP назначен
|
||||||
|
4. ✅ Traefik - поды Running
|
||||||
|
5. ❌ IngressRoute - **два роутера на один домен**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get ingressroute -A | grep oakazanin
|
||||||
|
|
||||||
|
NAMESPACE NAME
|
||||||
|
blog blog-https # СТАРЫЙ, бэкенд в CreateContainerError
|
||||||
|
oakazanin blog-https # НОВЫЙ, работает
|
||||||
|
```
|
||||||
|
|
||||||
|
Traefik видел два роутера, честно балансировал трафик 50/50. Каждый второй запрос улетал в мёртвый `blog/nginx`.
|
||||||
|
|
||||||
|
Диагноз поставлен за 3 минуты. Лечение - одна команда:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Удаляем дубль из старого namespace
|
||||||
|
kubectl delete ingressroute blog-https blog-http -n blog
|
||||||
|
```
|
||||||
|
|
||||||
|
Мораль: **всегда чисти за собой**. Старые namespace с нерабочими сервисами - источник неочевидных проблем.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Откат и cleanup
|
||||||
|
|
||||||
|
Если в процессе диагностики что-то сломал ещё больше - откатываемся:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Восстанавливаем предыдущую версию манифеста
|
||||||
|
kubectl apply -f nginx-deployment.yaml
|
||||||
|
|
||||||
|
# Перезапускаем поды принудительно
|
||||||
|
kubectl rollout restart deployment/nginx -n blog
|
||||||
|
|
||||||
|
# Смотрим что изменения применились
|
||||||
|
kubectl rollout status deployment/nginx -n blog
|
||||||
|
```
|
||||||
|
|
||||||
|
**Золотое правило:** Перед экспериментами делай бэкапы манифестов:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Экспортируем текущее состояние с датой
|
||||||
|
kubectl get deployment,service,ingressroute -n blog -o yaml > backup-$(date +%Y%m%d).yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что дальше
|
||||||
|
|
||||||
|
Ты умеешь диагностировать проблемы. Но лучше их вообще не создавать.
|
||||||
|
|
||||||
|
В следующей части покажу как правильно мигрировать сервисы между namespace - без даунтайма, дублей IngressRoute и других сюрпризов которые приводят к 503.
|
||||||
|
|
||||||
|
Разберём реальный пример: переносим Gitea между namespace с NFS данными, получаем новый SSL за 32 секунды, и удаляем старый namespace навсегда.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Стек этой части:**
|
||||||
|
- Traefik 2.11 IngressRoute
|
||||||
|
- Kubernetes 1.30 (K3s)
|
||||||
|
- kubectl CLI
|
||||||
|
- curl для внешних проверок
|
||||||
|
- dig для DNS диагностики
|
||||||
|
Before Width: | Height: | Size: 663 KiB |
|
Before Width: | Height: | Size: 672 KiB |
|
Before Width: | Height: | Size: 741 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
|
@ -3,13 +3,9 @@ title: "K3s HA для homelab: архитектура без боли"
|
||||||
date: 2025-10-14
|
date: 2025-10-14
|
||||||
draft: false
|
draft: false
|
||||||
description: "Полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Разбираем архитектуру K3s HA кластера: почему именно 3 master ноды, зачем embedded etcd и сколько ресурсов закладывать."
|
description: "Полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Разбираем архитектуру K3s HA кластера: почему именно 3 master ноды, зачем embedded etcd и сколько ресурсов закладывать."
|
||||||
summary: "Kubernetes слишком тяжёлый, Docker Swarm мёртв, а хочется нормальный кластер для экспериментов. K3s решает эту проблему - полноценный Kubernetes в 50MB. Разберём архитектуру HA кластера без боли."
|
|
||||||
tags: ["kubernetes", "k3s", "homelab", "proxmox", "architecture", "ha", "devops"]
|
tags: ["kubernetes", "k3s", "homelab", "proxmox", "architecture", "ha", "devops"]
|
||||||
series: ["K3s HA кластер для homelab"]
|
series: ["K3s HA кластер для homelab"]
|
||||||
series_order: 1
|
series_order: 1
|
||||||
# seriesOpened: false
|
|
||||||
# showTableOfContents: true
|
|
||||||
showAuthor: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Kubernetes слишком тяжёлый, Docker Swarm мёртв, а хочется нормальный кластер для экспериментов. Знакомо? K3s решает эту проблему - полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Но без правильного планирования вы получите нестабильную конструкцию, которая падает в самый неподходящий момент.
|
Kubernetes слишком тяжёлый, Docker Swarm мёртв, а хочется нормальный кластер для экспериментов. Знакомо? K3s решает эту проблему - полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Но без правильного планирования вы получите нестабильную конструкцию, которая падает в самый неподходящий момент.
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
|
@ -1,14 +1,11 @@
|
||||||
---
|
---
|
||||||
title: "Подготовить инфраструктуру для K3s в Proxmox"
|
title: "K3s HA для homelab: Готовим инфраструктуру в Proxmox"
|
||||||
date: 2025-10-21
|
date: 2025-10-21
|
||||||
draft: false
|
draft: false
|
||||||
description: "Создаём 5 VM в Proxmox с Debian 12, настраиваем сеть, отключаем swap и готовим систему для K3s. Пошаговая инструкция без сюрпризов."
|
description: "Создаём 5 VM в Proxmox с Debian 12, настраиваем сеть, отключаем swap и готовим систему для K3s. Пошаговая инструкция без сюрпризов."
|
||||||
summary: "Архитектура спланирована, ресурсы посчитаны - пора создавать виртуальные машины. Подготовим 5 VM в Proxmox и настроим ОС так, чтобы K3s установился без сюрпризов."
|
|
||||||
tags: ["kubernetes", "k3s", "homelab", "proxmox", "infrastructure", "debian", "devops"]
|
tags: ["kubernetes", "k3s", "homelab", "proxmox", "infrastructure", "debian", "devops"]
|
||||||
series: ["K3s HA кластер для homelab"]
|
series: ["K3s HA кластер для homelab"]
|
||||||
series_order: 2
|
series_order: 2
|
||||||
# seriesOpened: true
|
|
||||||
showTableOfContents: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Архитектура спланирована, ресурсы посчитаны - пора создавать виртуальные машины. В этой статье подготовим 5 VM в Proxmox и настроим ОС так, чтобы K3s установился без сюрпризов.
|
Архитектура спланирована, ресурсы посчитаны - пора создавать виртуальные машины. В этой статье подготовим 5 VM в Proxmox и настроим ОС так, чтобы K3s установился без сюрпризов.
|
||||||
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 3.6 MiB After Width: | Height: | Size: 3.6 MiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
|
@ -1,14 +1,11 @@
|
||||||
---
|
---
|
||||||
title: "Установить K3s HA кластер"
|
title: "K3s HA для homelab: Ставим K3s HA кластер"
|
||||||
date: 2025-11-02
|
date: 2025-11-02
|
||||||
draft: false
|
draft: false
|
||||||
description: "Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl."
|
description: "Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl."
|
||||||
summary: "Инфраструктура готова: 5 VM работают, ОС настроена, порты открыты. Пора устанавливать K3s. Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер."
|
|
||||||
tags: ["kubernetes", "k3s", "homelab", "installation", "ha", "etcd", "devops"]
|
tags: ["kubernetes", "k3s", "homelab", "installation", "ha", "etcd", "devops"]
|
||||||
series: ["K3s HA кластер для homelab"]
|
series: ["K3s HA кластер для homelab"]
|
||||||
series_order: 3
|
series_order: 3
|
||||||
# seriesOpened: true
|
|
||||||
showTableOfContents: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Инфраструктура готова: 5 VM работают, ОС настроена, порты открыты. Пора устанавливать K3s. Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер.
|
Инфраструктура готова: 5 VM работают, ОС настроена, порты открыты. Пора устанавливать K3s. Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер.
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -23,7 +23,7 @@ article:
|
||||||
other: "{{ .Count }} нравится"
|
other: "{{ .Count }} нравится"
|
||||||
part_of_series: "Эта статья — часть серии."
|
part_of_series: "Эта статья — часть серии."
|
||||||
part: "Часть"
|
part: "Часть"
|
||||||
this_article: "Читаешь сейчас"
|
this_article: "Ты уже здесь"
|
||||||
related_articles: "Статьи по теме"
|
related_articles: "Статьи по теме"
|
||||||
reply_by_email: "Ответить по электронной почте"
|
reply_by_email: "Ответить по электронной почте"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="canonical" href="http://192.168.11.190:1313/404.html">
|
<link rel="canonical" href="http://localhost:1313/404.html">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta property="og:url" content="http://192.168.11.190:1313/404.html">
|
<meta property="og:url" content="http://localhost:1313/404.html">
|
||||||
<meta property="og:site_name" content="Олег Казанин">
|
<meta property="og:site_name" content="Олег Казанин">
|
||||||
<meta property="og:title" content="404 Page not found">
|
<meta property="og:title" content="404 Page not found">
|
||||||
<meta property="og:locale" content="ru">
|
<meta property="og:locale" content="ru">
|
||||||
|
|
@ -150,14 +150,12 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script
|
<script
|
||||||
defer
|
defer
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
id="script-bundle"
|
id="script-bundle"
|
||||||
src="/js/main.bundle.min.bdda7dece6cbaf08deef7d254f7f842f3261c2524d247905127c9a20decc03f1011a2950048464c79272c1ce0705a49a41147f39f2b95163bb71d404b33263ef.js"
|
src="/js/main.bundle.min.858f7f82734cfae08d59fcf8d0eb186f01706a84e2f7d20d39edfd7bc8bed6166e02d5c65ecce1de82b1ac52d1e01d77bd1a82d19186fdae5fe6e12d867fcf68.js"
|
||||||
integrity="sha512-vdp97ObLrwje730lT3+ELzJhwlJNJHkFEnyaIN7MA/EBGilQBIRkx5Jywc4HBaSaQRR/OfK5UWO7cdQEszJj7w=="
|
integrity="sha512-hY9/gnNM+uCNWfz40OsYbwFwaoTi99INOe39e8i+1hZuAtXGXszh3oKxrFLR4B13vRqC0ZGG/a5f5uEthn/PaA=="
|
||||||
data-copy="Копировать"
|
data-copy="Копировать"
|
||||||
data-copied="Скопировано"></script>
|
data-copied="Скопировано"></script>
|
||||||
|
|
||||||
|
|
@ -202,6 +200,13 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module" src="/js/firebase.min.cad74e0625f72f359ec6d6fed579b87733749de70400e7614048050ed08832ee3f58983d5d139fb1ddc5f7f2f5047d45ed80ec923534a3660fc3a7965f936866.js" integrity="sha512-ytdOBiX3LzWextb+1Xm4dzN0necEAOdhQEgFDtCIMu4/WJg9XROfsd3F9/L1BH1F7YDskjU0o2YPw6eWX5NoZg=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -228,7 +233,7 @@
|
||||||
"headline": "404 Page not found",
|
"headline": "404 Page not found",
|
||||||
|
|
||||||
"inLanguage": "ru",
|
"inLanguage": "ru",
|
||||||
"url" : "http://192.168.11.190:1313/404.html",
|
"url" : "http://localhost:1313/404.html",
|
||||||
"author" : {
|
"author" : {
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"name": "Олег Казанин"
|
"name": "Олег Казанин"
|
||||||
|
|
@ -543,7 +548,7 @@
|
||||||
<div
|
<div
|
||||||
id="search-wrapper"
|
id="search-wrapper"
|
||||||
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
||||||
data-url="http://192.168.11.190:1313/">
|
data-url="http://localhost:1313/">
|
||||||
<div
|
<div
|
||||||
id="search-modal"
|
id="search-modal"
|
||||||
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 6.3 KiB |
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="canonical" href="http://192.168.11.190:1313/authors/">
|
<link rel="canonical" href="http://localhost:1313/authors/">
|
||||||
|
|
||||||
|
|
||||||
<link rel="alternate" type="application/rss+xml" href="/authors/index.xml" title="Олег Казанин" />
|
<link rel="alternate" type="application/rss+xml" href="/authors/index.xml" title="Олег Казанин" />
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta property="og:url" content="http://192.168.11.190:1313/authors/">
|
<meta property="og:url" content="http://localhost:1313/authors/">
|
||||||
<meta property="og:site_name" content="Олег Казанин">
|
<meta property="og:site_name" content="Олег Казанин">
|
||||||
<meta property="og:title" content="Authors">
|
<meta property="og:title" content="Authors">
|
||||||
<meta property="og:locale" content="ru">
|
<meta property="og:locale" content="ru">
|
||||||
|
|
@ -153,14 +153,12 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script
|
<script
|
||||||
defer
|
defer
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
id="script-bundle"
|
id="script-bundle"
|
||||||
src="/js/main.bundle.min.bdda7dece6cbaf08deef7d254f7f842f3261c2524d247905127c9a20decc03f1011a2950048464c79272c1ce0705a49a41147f39f2b95163bb71d404b33263ef.js"
|
src="/js/main.bundle.min.858f7f82734cfae08d59fcf8d0eb186f01706a84e2f7d20d39edfd7bc8bed6166e02d5c65ecce1de82b1ac52d1e01d77bd1a82d19186fdae5fe6e12d867fcf68.js"
|
||||||
integrity="sha512-vdp97ObLrwje730lT3+ELzJhwlJNJHkFEnyaIN7MA/EBGilQBIRkx5Jywc4HBaSaQRR/OfK5UWO7cdQEszJj7w=="
|
integrity="sha512-hY9/gnNM+uCNWfz40OsYbwFwaoTi99INOe39e8i+1hZuAtXGXszh3oKxrFLR4B13vRqC0ZGG/a5f5uEthn/PaA=="
|
||||||
data-copy="Копировать"
|
data-copy="Копировать"
|
||||||
data-copied="Скопировано"></script>
|
data-copied="Скопировано"></script>
|
||||||
|
|
||||||
|
|
@ -205,6 +203,32 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module" src="/js/firebase.min.cad74e0625f72f359ec6d6fed579b87733749de70400e7614048050ed08832ee3f58983d5d139fb1ddc5f7f2f5047d45ed80ec923534a3660fc3a7965f936866.js" integrity="sha512-ytdOBiX3LzWextb+1Xm4dzN0necEAOdhQEgFDtCIMu4/WJg9XROfsd3F9/L1BH1F7YDskjU0o2YPw6eWX5NoZg=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script id="firebase-config"
|
||||||
|
type="application/json"
|
||||||
|
data-views="views_taxonomy_authors"
|
||||||
|
data-likes="likes_taxonomy_authors">
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"apiKey": "AIzaSyBBfzADrGgnwTIyW67gfZSrAtkoybxvmdI",
|
||||||
|
"authDomain": "oakazanin-hugo-blowfish.firebaseapp.com",
|
||||||
|
"projectId": "oakazanin-hugo-blowfish",
|
||||||
|
"storageBucket": "oakazanin-hugo-blowfish.firebasestorage.app",
|
||||||
|
"messagingSenderId": "945151844512",
|
||||||
|
"appId": "1:945151844512:web:22602cc010f5b7e0cca9c5",
|
||||||
|
"measurementId": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -231,7 +255,7 @@
|
||||||
"headline": "Authors",
|
"headline": "Authors",
|
||||||
|
|
||||||
"inLanguage": "ru",
|
"inLanguage": "ru",
|
||||||
"url" : "http://192.168.11.190:1313/authors/",
|
"url" : "http://localhost:1313/authors/",
|
||||||
"author" : {
|
"author" : {
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"name": "Олег Казанин"
|
"name": "Олег Казанин"
|
||||||
|
|
@ -433,7 +457,87 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<header class="mt-5">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="hero" class="h-[150px] md:h-[200px]"></div>
|
||||||
|
|
||||||
|
<div class="fixed inset-x-0 top-0 h-[800px] single_hero_background nozoom">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img
|
||||||
|
id="background-image"
|
||||||
|
src="/img/background_hu_42f1c83933308119.png"
|
||||||
|
role="presentation"
|
||||||
|
loading="eager"
|
||||||
|
decoding="async"
|
||||||
|
fetchpriority="high"
|
||||||
|
class="absolute inset-0 w-full h-full object-cover"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-t from-neutral dark:from-neutral-800 to-transparent mix-blend-normal"></div>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 opacity-60 bg-gradient-to-t from-neutral dark:from-neutral-800 to-neutral-100 dark:to-neutral-800 mix-blend-normal"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="background-blur"
|
||||||
|
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/background-blur.min.605b3b942818f0ab5a717ae446135ec46b8ee5a2ad12ae56fb90dc2a76ce30c388f9fec8bcc18db15bd47e3fa8a09d779fa12aa9c184cf614a315bc72c6c163d.js"
|
||||||
|
integrity="sha512-YFs7lCgY8KtacXrkRhNexGuO5aKtEq5W+5DcKnbOMMOI+f7IvMGNsVvUfj+ooJ13n6EqqcGEz2FKMVvHLGwWPQ=="
|
||||||
|
data-blur-id="background-blur"
|
||||||
|
data-image-id="background-image"
|
||||||
|
data-image-url="/img/background_hu_42f1c83933308119.png"></script>
|
||||||
|
|
||||||
|
<header class="mt-5">
|
||||||
|
|
||||||
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">Authors</h1>
|
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">Authors</h1>
|
||||||
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||||
|
|
@ -583,7 +687,7 @@
|
||||||
<div
|
<div
|
||||||
id="search-wrapper"
|
id="search-wrapper"
|
||||||
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
||||||
data-url="http://192.168.11.190:1313/">
|
data-url="http://localhost:1313/">
|
||||||
<div
|
<div
|
||||||
id="search-modal"
|
id="search-modal"
|
||||||
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
<channel>
|
<channel>
|
||||||
<title>Authors on Олег Казанин</title>
|
<title>Authors on Олег Казанин</title>
|
||||||
<link>http://192.168.11.190:1313/authors/</link>
|
<link>http://localhost:1313/authors/</link>
|
||||||
<description>Recent content in Authors on Олег Казанин</description>
|
<description>Recent content in Authors on Олег Казанин</description>
|
||||||
<generator>Hugo -- gohugo.io</generator>
|
<generator>Hugo -- gohugo.io</generator>
|
||||||
<language>ru</language>
|
<language>ru</language>
|
||||||
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
||||||
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
||||||
<copyright>© 2026 Олег Казанин</copyright>
|
<copyright>© 2026 Олег Казанин</copyright>
|
||||||
<atom:link href="http://192.168.11.190:1313/authors/index.xml" rel="self" type="application/rss+xml" />
|
<atom:link href="http://localhost:1313/authors/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="canonical" href="http://192.168.11.190:1313/categories/">
|
<link rel="canonical" href="http://localhost:1313/categories/">
|
||||||
|
|
||||||
|
|
||||||
<link rel="alternate" type="application/rss+xml" href="/categories/index.xml" title="Олег Казанин" />
|
<link rel="alternate" type="application/rss+xml" href="/categories/index.xml" title="Олег Казанин" />
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta property="og:url" content="http://192.168.11.190:1313/categories/">
|
<meta property="og:url" content="http://localhost:1313/categories/">
|
||||||
<meta property="og:site_name" content="Олег Казанин">
|
<meta property="og:site_name" content="Олег Казанин">
|
||||||
<meta property="og:title" content="Categories">
|
<meta property="og:title" content="Categories">
|
||||||
<meta property="og:locale" content="ru">
|
<meta property="og:locale" content="ru">
|
||||||
|
|
@ -153,14 +153,12 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script
|
<script
|
||||||
defer
|
defer
|
||||||
type="text/javascript"
|
type="text/javascript"
|
||||||
id="script-bundle"
|
id="script-bundle"
|
||||||
src="/js/main.bundle.min.bdda7dece6cbaf08deef7d254f7f842f3261c2524d247905127c9a20decc03f1011a2950048464c79272c1ce0705a49a41147f39f2b95163bb71d404b33263ef.js"
|
src="/js/main.bundle.min.858f7f82734cfae08d59fcf8d0eb186f01706a84e2f7d20d39edfd7bc8bed6166e02d5c65ecce1de82b1ac52d1e01d77bd1a82d19186fdae5fe6e12d867fcf68.js"
|
||||||
integrity="sha512-vdp97ObLrwje730lT3+ELzJhwlJNJHkFEnyaIN7MA/EBGilQBIRkx5Jywc4HBaSaQRR/OfK5UWO7cdQEszJj7w=="
|
integrity="sha512-hY9/gnNM+uCNWfz40OsYbwFwaoTi99INOe39e8i+1hZuAtXGXszh3oKxrFLR4B13vRqC0ZGG/a5f5uEthn/PaA=="
|
||||||
data-copy="Копировать"
|
data-copy="Копировать"
|
||||||
data-copied="Скопировано"></script>
|
data-copied="Скопировано"></script>
|
||||||
|
|
||||||
|
|
@ -205,6 +203,32 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module" src="/js/firebase.min.cad74e0625f72f359ec6d6fed579b87733749de70400e7614048050ed08832ee3f58983d5d139fb1ddc5f7f2f5047d45ed80ec923534a3660fc3a7965f936866.js" integrity="sha512-ytdOBiX3LzWextb+1Xm4dzN0necEAOdhQEgFDtCIMu4/WJg9XROfsd3F9/L1BH1F7YDskjU0o2YPw6eWX5NoZg=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script id="firebase-config"
|
||||||
|
type="application/json"
|
||||||
|
data-views="views_taxonomy_categories"
|
||||||
|
data-likes="likes_taxonomy_categories">
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"apiKey": "AIzaSyBBfzADrGgnwTIyW67gfZSrAtkoybxvmdI",
|
||||||
|
"authDomain": "oakazanin-hugo-blowfish.firebaseapp.com",
|
||||||
|
"projectId": "oakazanin-hugo-blowfish",
|
||||||
|
"storageBucket": "oakazanin-hugo-blowfish.firebasestorage.app",
|
||||||
|
"messagingSenderId": "945151844512",
|
||||||
|
"appId": "1:945151844512:web:22602cc010f5b7e0cca9c5",
|
||||||
|
"measurementId": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -231,16 +255,16 @@
|
||||||
"headline": "Categories",
|
"headline": "Categories",
|
||||||
|
|
||||||
"inLanguage": "ru",
|
"inLanguage": "ru",
|
||||||
"url" : "http://192.168.11.190:1313/categories/",
|
"url" : "http://localhost:1313/categories/",
|
||||||
"author" : {
|
"author" : {
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"name": "Олег Казанин"
|
"name": "Олег Казанин"
|
||||||
},
|
},
|
||||||
|
"copyrightYear": "2026",
|
||||||
|
"dateCreated": "2026-02-17T00:00:00\u002b00:00",
|
||||||
|
"datePublished": "2026-02-17T00:00:00\u002b00:00",
|
||||||
|
|
||||||
|
"dateModified": "2026-02-17T00:00:00\u002b00:00",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -433,7 +457,87 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<header class="mt-5">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="hero" class="h-[150px] md:h-[200px]"></div>
|
||||||
|
|
||||||
|
<div class="fixed inset-x-0 top-0 h-[800px] single_hero_background nozoom">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img
|
||||||
|
id="background-image"
|
||||||
|
src="/img/background_hu_42f1c83933308119.png"
|
||||||
|
role="presentation"
|
||||||
|
loading="eager"
|
||||||
|
decoding="async"
|
||||||
|
fetchpriority="high"
|
||||||
|
class="absolute inset-0 w-full h-full object-cover"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-t from-neutral dark:from-neutral-800 to-transparent mix-blend-normal"></div>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 opacity-60 bg-gradient-to-t from-neutral dark:from-neutral-800 to-neutral-100 dark:to-neutral-800 mix-blend-normal"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="background-blur"
|
||||||
|
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/background-blur.min.605b3b942818f0ab5a717ae446135ec46b8ee5a2ad12ae56fb90dc2a76ce30c388f9fec8bcc18db15bd47e3fa8a09d779fa12aa9c184cf614a315bc72c6c163d.js"
|
||||||
|
integrity="sha512-YFs7lCgY8KtacXrkRhNexGuO5aKtEq5W+5DcKnbOMMOI+f7IvMGNsVvUfj+ooJ13n6EqqcGEz2FKMVvHLGwWPQ=="
|
||||||
|
data-blur-id="background-blur"
|
||||||
|
data-image-id="background-image"
|
||||||
|
data-image-url="/img/background_hu_42f1c83933308119.png"></script>
|
||||||
|
|
||||||
|
<header class="mt-5">
|
||||||
|
|
||||||
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">Categories</h1>
|
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">Categories</h1>
|
||||||
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||||
|
|
@ -473,6 +577,23 @@
|
||||||
|
|
||||||
<section class="flex flex-wrap max-w-prose -mx-2 overflow-hidden">
|
<section class="flex flex-wrap max-w-prose -mx-2 overflow-hidden">
|
||||||
|
|
||||||
|
<article class="w-full px-2 my-3 overflow-hidden sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/4">
|
||||||
|
<h2 class="flex items-center">
|
||||||
|
<a
|
||||||
|
class="text-xl font-medium decoration-primary-500 hover:underline hover:underline-offset-2"
|
||||||
|
href="/categories/infrastructure/"
|
||||||
|
>Infrastructure</a
|
||||||
|
>
|
||||||
|
|
||||||
|
<span class="px-2 text-base text-primary-500">·</span>
|
||||||
|
<span class="text-base text-neutral-400">
|
||||||
|
5
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -583,7 +704,7 @@
|
||||||
<div
|
<div
|
||||||
id="search-wrapper"
|
id="search-wrapper"
|
||||||
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
||||||
data-url="http://192.168.11.190:1313/">
|
data-url="http://localhost:1313/">
|
||||||
<div
|
<div
|
||||||
id="search-modal"
|
id="search-modal"
|
||||||
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,24 @@
|
||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
<channel>
|
<channel>
|
||||||
<title>Categories on Олег Казанин</title>
|
<title>Categories on Олег Казанин</title>
|
||||||
<link>http://192.168.11.190:1313/categories/</link>
|
<link>http://localhost:1313/categories/</link>
|
||||||
<description>Recent content in Categories on Олег Казанин</description>
|
<description>Recent content in Categories on Олег Казанин</description>
|
||||||
<generator>Hugo -- gohugo.io</generator>
|
<generator>Hugo -- gohugo.io</generator>
|
||||||
<language>ru</language>
|
<language>ru</language>
|
||||||
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
||||||
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
||||||
<copyright>© 2026 Олег Казанин</copyright>
|
<copyright>© 2026 Олег Казанин</copyright>
|
||||||
<atom:link href="http://192.168.11.190:1313/categories/index.xml" rel="self" type="application/rss+xml" />
|
<lastBuildDate>Tue, 17 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://localhost:1313/categories/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Infrastructure</title>
|
||||||
|
<link>http://localhost:1313/categories/infrastructure/</link>
|
||||||
|
<pubDate>Tue, 17 Feb 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/categories/infrastructure/</guid>
|
||||||
|
<description></description>
|
||||||
|
|
||||||
|
</item>
|
||||||
|
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<channel>
|
||||||
|
<title>Infrastructure on Олег Казанин</title>
|
||||||
|
<link>http://localhost:1313/categories/infrastructure/</link>
|
||||||
|
<description>Recent content in Infrastructure on Олег Казанин</description>
|
||||||
|
<generator>Hugo -- gohugo.io</generator>
|
||||||
|
<language>ru</language>
|
||||||
|
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
||||||
|
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
||||||
|
<copyright>© 2026 Олег Казанин</copyright>
|
||||||
|
<lastBuildDate>Tue, 17 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://localhost:1313/categories/infrastructure/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 5 - что делать когда всё внезапно сломалось</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-5-debugging/</link>
|
||||||
|
<pubDate>Tue, 17 Feb 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-5-debugging/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-5-debugging/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 4 - выбор Git workflow</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-4-git-workflow/</link>
|
||||||
|
<pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-4-git-workflow/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-4-git-workflow/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 3 - development окружение</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-3-dev-environment/</link>
|
||||||
|
<pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-3-dev-environment/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-3-dev-environment/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 2 - деплой в кластер</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-2-k8s-deployment/</link>
|
||||||
|
<pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-2-k8s-deployment/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-2-k8s-deployment/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 1 - архитектура и первый запуск</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-1-architecture/</link>
|
||||||
|
<pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-1-architecture/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-1-architecture/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</channel>
|
||||||
|
</rss>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<title>http://localhost:1313/categories/infrastructure/</title>
|
||||||
|
<link rel="canonical" href="http://localhost:1313/categories/infrastructure/">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="refresh" content="0; url=http://localhost:1313/categories/infrastructure/">
|
||||||
|
</head>
|
||||||
|
</html>
|
||||||
|
Before Width: | Height: | Size: 737 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 774 KiB |
|
After Width: | Height: | Size: 202 KiB |
1398
public/index.html
|
|
@ -2,43 +2,93 @@
|
||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
<channel>
|
<channel>
|
||||||
<title>Олег Казанин</title>
|
<title>Олег Казанин</title>
|
||||||
<link>http://192.168.11.190:1313/</link>
|
<link>http://localhost:1313/</link>
|
||||||
<description>Recent content on Олег Казанин</description>
|
<description>Recent content on Олег Казанин</description>
|
||||||
<generator>Hugo -- gohugo.io</generator>
|
<generator>Hugo -- gohugo.io</generator>
|
||||||
<language>ru</language>
|
<language>ru</language>
|
||||||
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
||||||
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
||||||
<copyright>© 2026 Олег Казанин</copyright>
|
<copyright>© 2026 Олег Казанин</copyright>
|
||||||
<lastBuildDate>Sun, 02 Nov 2025 00:00:00 +0000</lastBuildDate><atom:link href="http://192.168.11.190:1313/index.xml" rel="self" type="application/rss+xml" />
|
<lastBuildDate>Tue, 17 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://localhost:1313/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<title>Установить K3s HA кластер</title>
|
<title>Блог на Hugo в K3s: часть 5 - что делать когда всё внезапно сломалось</title>
|
||||||
<link>http://192.168.11.190:1313/posts/k3s-installation/</link>
|
<link>http://localhost:1313/posts/blog-part-5-debugging/</link>
|
||||||
<pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate>
|
<pubDate>Tue, 17 Feb 2026 00:00:00 +0000</pubDate>
|
||||||
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
<guid>http://192.168.11.190:1313/posts/k3s-installation/</guid>
|
<guid>http://localhost:1313/posts/blog-part-5-debugging/</guid>
|
||||||
<description>Инфраструктура готова: 5 VM работают, ОС настроена, порты открыты. Пора устанавливать K3s. Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер.</description>
|
<description></description>
|
||||||
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://192.168.11.190:1313/posts/k3s-installation/featured.png" />
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-5-debugging/featured.png" />
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<title>Подготовить инфраструктуру для K3s в Proxmox</title>
|
<title>Блог на Hugo в K3s: часть 4 - выбор Git workflow</title>
|
||||||
<link>http://192.168.11.190:1313/posts/k3s-infrastructure/</link>
|
<link>http://localhost:1313/posts/blog-part-4-git-workflow/</link>
|
||||||
|
<pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-4-git-workflow/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-4-git-workflow/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 3 - development окружение</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-3-dev-environment/</link>
|
||||||
|
<pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-3-dev-environment/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-3-dev-environment/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 2 - деплой в кластер</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-2-k8s-deployment/</link>
|
||||||
|
<pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-2-k8s-deployment/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-2-k8s-deployment/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 1 - архитектура и первый запуск</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-1-architecture/</link>
|
||||||
|
<pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-1-architecture/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-1-architecture/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>K3s HA для homelab: Ставим K3s HA кластер</title>
|
||||||
|
<link>http://localhost:1313/posts/k3s-part3-installation/</link>
|
||||||
|
<pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/k3s-part3-installation/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/k3s-part3-installation/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>K3s HA для homelab: Готовим инфраструктуру в Proxmox</title>
|
||||||
|
<link>http://localhost:1313/posts/k3s-part2-infrastructure/</link>
|
||||||
<pubDate>Tue, 21 Oct 2025 00:00:00 +0000</pubDate>
|
<pubDate>Tue, 21 Oct 2025 00:00:00 +0000</pubDate>
|
||||||
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
<guid>http://192.168.11.190:1313/posts/k3s-infrastructure/</guid>
|
<guid>http://localhost:1313/posts/k3s-part2-infrastructure/</guid>
|
||||||
<description>Архитектура спланирована, ресурсы посчитаны - пора создавать виртуальные машины. Подготовим 5 VM в Proxmox и настроим ОС так, чтобы K3s установился без сюрпризов.</description>
|
<description></description>
|
||||||
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://192.168.11.190:1313/posts/k3s-infrastructure/featured.png" />
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/k3s-part2-infrastructure/featured.png" />
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<title>K3s HA для homelab: архитектура без боли</title>
|
<title>K3s HA для homelab: архитектура без боли</title>
|
||||||
<link>http://192.168.11.190:1313/posts/k3s-architecture/</link>
|
<link>http://localhost:1313/posts/k3s-part1-architecture/</link>
|
||||||
<pubDate>Tue, 14 Oct 2025 00:00:00 +0000</pubDate>
|
<pubDate>Tue, 14 Oct 2025 00:00:00 +0000</pubDate>
|
||||||
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
<guid>http://192.168.11.190:1313/posts/k3s-architecture/</guid>
|
<guid>http://localhost:1313/posts/k3s-part1-architecture/</guid>
|
||||||
<description>Kubernetes слишком тяжёлый, Docker Swarm мёртв, а хочется нормальный кластер для экспериментов. K3s решает эту проблему - полноценный Kubernetes в 50MB. Разберём архитектуру HA кластера без боли.</description>
|
<description></description>
|
||||||
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://192.168.11.190:1313/posts/k3s-architecture/featured.png" />
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/k3s-part1-architecture/featured.png" />
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
</channel>
|
</channel>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
import{initializeApp}from"https://www.gstatic.com/firebasejs/9.23.0/firebase-app.js";import{getFirestore,doc,getDoc,setDoc,updateDoc,increment,onSnapshot,}from"https://www.gstatic.com/firebasejs/9.23.0/firebase-firestore.js";import{getAuth,signInAnonymously}from"https://www.gstatic.com/firebasejs/9.23.0/firebase-auth.js";let app,db,auth,oids;try{const e=document.getElementById("firebase-config");if(!e?.textContent)throw new Error("Firebase config element not found");const t=JSON.parse(e.textContent);app=initializeApp(t.config),oids={views:e.getAttribute("data-views"),likes:e.getAttribute("data-likes")},db=getFirestore(app),auth=getAuth(app)}catch(e){throw console.error("Firebase initialization failed:",e.message),e}const id=oids?.views?.replaceAll("/","-"),id_likes=oids?.likes?.replaceAll("/","-");let liked=!1,authReady=!1;function formatNumber(e){return e.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")}function toggleLoaders(e){var t,s,n=e.className;if(n=="")return;t=n.split(" ");for(s in t)e.classList.toggle(t[s])}function updateDisplay(e,t){const n=document.getElementById(t);if(!n)return;const s=t.replaceAll("/","-");onSnapshot(doc(db,e,s),t=>{n.innerText=t.exists()?formatNumber(t.data()[e]):0,toggleLoaders(n)},e=>{console.error("Firebase snapshot update failed:",e)})}async function recordView(e){if(!e||localStorage.getItem(e))return;try{const t=doc(db,"views",e),n=await getDoc(t);n.exists()?await updateDoc(t,{views:increment(1)}):await setDoc(t,{views:1}),localStorage.setItem(e,!0)}catch(e){console.error("Record view operation failed:",e.message)}}function updateButton(e){const t=document.querySelectorAll("span[id='button_likes_heart']"),n=document.querySelectorAll("span[id='button_likes_emtpty_heart']"),s=document.querySelectorAll("span[id='button_likes_text']");t.forEach(t=>{t.style.display=e?"":"none"}),n.forEach(t=>{t.style.display=e?"none":""}),s.forEach(t=>{t.innerText=e?"":"\xa0Like"})}async function toggleLike(e){if(!id_likes||!authReady)return;try{const t=doc(db,"likes",id_likes),n=await getDoc(t);liked=e,e?localStorage.setItem(id_likes,!0):localStorage.removeItem(id_likes),updateButton(e),n.exists()?await updateDoc(t,{likes:increment(e?1:-1)}):await setDoc(t,{likes:e?1:0})}catch(t){console.error("Like operation failed:",t.message),liked=!e,e?localStorage.removeItem(id_likes):localStorage.setItem(id_likes,!0),updateButton(!e)}}signInAnonymously(auth).then(()=>{authReady=!0,document.querySelectorAll("span[id^='views_']").forEach(e=>{e.id&&updateDisplay("views",e.id)}),document.querySelectorAll("span[id^='likes_']").forEach(e=>{e.id&&updateDisplay("likes",e.id)}),recordView(id),id_likes&&localStorage.getItem(id_likes)&&(liked=!0,updateButton(!0));const e=document.getElementById("button_likes");e&&e.addEventListener("click",()=>{toggleLike(!liked)})}).catch(e=>{console.error("Firebase anonymous sign-in failed:",e.message),authReady=!1}),window.process_article=()=>toggleLike(!liked)
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
<title>http://192.168.11.190:1313/</title>
|
<title>http://localhost:1313/</title>
|
||||||
<link rel="canonical" href="http://192.168.11.190:1313/">
|
<link rel="canonical" href="http://localhost:1313/">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="refresh" content="0; url=http://192.168.11.190:1313/">
|
<meta http-equiv="refresh" content="0; url=http://localhost:1313/">
|
||||||
</head>
|
</head>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 1008 KiB |
|
After Width: | Height: | Size: 698 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 1003 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 928 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 280 KiB |
|
After Width: | Height: | Size: 986 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 931 KiB |
|
|
@ -2,43 +2,93 @@
|
||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
<channel>
|
<channel>
|
||||||
<title>Posts on Олег Казанин</title>
|
<title>Posts on Олег Казанин</title>
|
||||||
<link>http://192.168.11.190:1313/posts/</link>
|
<link>http://localhost:1313/posts/</link>
|
||||||
<description>Recent content in Posts on Олег Казанин</description>
|
<description>Recent content in Posts on Олег Казанин</description>
|
||||||
<generator>Hugo -- gohugo.io</generator>
|
<generator>Hugo -- gohugo.io</generator>
|
||||||
<language>ru</language>
|
<language>ru</language>
|
||||||
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
<managingEditor>oakazanin@ya.ru (Олег Казанин)</managingEditor>
|
||||||
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
<webMaster>oakazanin@ya.ru (Олег Казанин)</webMaster>
|
||||||
<copyright>© 2026 Олег Казанин</copyright>
|
<copyright>© 2026 Олег Казанин</copyright>
|
||||||
<lastBuildDate>Sat, 14 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://192.168.11.190:1313/posts/index.xml" rel="self" type="application/rss+xml" />
|
<lastBuildDate>Tue, 17 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://localhost:1313/posts/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 5 - что делать когда всё внезапно сломалось</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-5-debugging/</link>
|
||||||
|
<pubDate>Tue, 17 Feb 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-5-debugging/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-5-debugging/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 4 - выбор Git workflow</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-4-git-workflow/</link>
|
||||||
|
<pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-4-git-workflow/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-4-git-workflow/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 3 - development окружение</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-3-dev-environment/</link>
|
||||||
|
<pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-3-dev-environment/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-3-dev-environment/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 2 - деплой в кластер</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-2-k8s-deployment/</link>
|
||||||
|
<pubDate>Thu, 08 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-2-k8s-deployment/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-2-k8s-deployment/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Блог на Hugo в K3s: часть 1 - архитектура и первый запуск</title>
|
||||||
|
<link>http://localhost:1313/posts/blog-part-1-architecture/</link>
|
||||||
|
<pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/blog-part-1-architecture/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/blog-part-1-architecture/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>K3s HA для homelab: Ставим K3s HA кластер</title>
|
||||||
|
<link>http://localhost:1313/posts/k3s-part3-installation/</link>
|
||||||
|
<pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/k3s-part3-installation/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/k3s-part3-installation/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>K3s HA для homelab: Готовим инфраструктуру в Proxmox</title>
|
||||||
|
<link>http://localhost:1313/posts/k3s-part2-infrastructure/</link>
|
||||||
|
<pubDate>Tue, 21 Oct 2025 00:00:00 +0000</pubDate>
|
||||||
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
|
<guid>http://localhost:1313/posts/k3s-part2-infrastructure/</guid>
|
||||||
|
<description></description>
|
||||||
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/k3s-part2-infrastructure/featured.png" />
|
||||||
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<title>K3s HA для homelab: архитектура без боли</title>
|
<title>K3s HA для homelab: архитектура без боли</title>
|
||||||
<link>http://192.168.11.190:1313/posts/k3s-architecture/</link>
|
<link>http://localhost:1313/posts/k3s-part1-architecture/</link>
|
||||||
<pubDate>Sat, 14 Feb 2026 00:00:00 +0000</pubDate>
|
<pubDate>Tue, 14 Oct 2025 00:00:00 +0000</pubDate>
|
||||||
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
||||||
<guid>http://192.168.11.190:1313/posts/k3s-architecture/</guid>
|
<guid>http://localhost:1313/posts/k3s-part1-architecture/</guid>
|
||||||
<description>Kubernetes слишком тяжёлый, Docker Swarm мёртв, а хочется нормальный кластер для экспериментов. K3s решает эту проблему - полноценный Kubernetes в 50MB. Разберём архитектуру HA кластера без боли.</description>
|
<description></description>
|
||||||
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://192.168.11.190:1313/posts/k3s-architecture/featured.png" />
|
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:1313/posts/k3s-part1-architecture/featured.png" />
|
||||||
</item>
|
|
||||||
|
|
||||||
<item>
|
|
||||||
<title>Подготовить инфраструктуру для K3s в Proxmox</title>
|
|
||||||
<link>http://192.168.11.190:1313/posts/k3s-infrastructure/</link>
|
|
||||||
<pubDate>Sat, 14 Feb 2026 00:00:00 +0000</pubDate>
|
|
||||||
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
|
||||||
<guid>http://192.168.11.190:1313/posts/k3s-infrastructure/</guid>
|
|
||||||
<description>Архитектура спланирована, ресурсы посчитаны - пора создавать виртуальные машины. Подготовим 5 VM в Proxmox и настроим ОС так, чтобы K3s установился без сюрпризов.</description>
|
|
||||||
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://192.168.11.190:1313/posts/k3s-infrastructure/featured.png" />
|
|
||||||
</item>
|
|
||||||
|
|
||||||
<item>
|
|
||||||
<title>Установить K3s HA кластер</title>
|
|
||||||
<link>http://192.168.11.190:1313/posts/k3s-installation/</link>
|
|
||||||
<pubDate>Sat, 14 Feb 2026 00:00:00 +0000</pubDate>
|
|
||||||
<author>oakazanin@ya.ru (Олег Казанин)</author>
|
|
||||||
<guid>http://192.168.11.190:1313/posts/k3s-installation/</guid>
|
|
||||||
<description>Инфраструктура готова: 5 VM работают, ОС настроена, порты открыты. Пора устанавливать K3s. Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер.</description>
|
|
||||||
<media:content xmlns:media="http://search.yahoo.com/mrss/" url="http://192.168.11.190:1313/posts/k3s-installation/featured.png" />
|
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
</channel>
|
</channel>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 663 KiB After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 761 KiB |
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="canonical" href="http://192.168.11.190:1313/posts/k3s-architecture/">
|
<link rel="canonical" href="http://localhost:1313/posts/k3s-architecture/">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta property="og:url" content="http://192.168.11.190:1313/posts/k3s-architecture/">
|
<meta property="og:url" content="http://localhost:1313/posts/k3s-architecture/">
|
||||||
<meta property="og:site_name" content="Олег Казанин">
|
<meta property="og:site_name" content="Олег Казанин">
|
||||||
<meta property="og:title" content="K3s HA для homelab: архитектура без боли">
|
<meta property="og:title" content="K3s HA для homelab: архитектура без боли">
|
||||||
<meta property="og:description" content="Полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Разбираем архитектуру K3s HA кластера: почему именно 3 master ноды, зачем embedded etcd и сколько ресурсов закладывать.">
|
<meta property="og:description" content="Полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Разбираем архитектуру K3s HA кластера: почему именно 3 master ноды, зачем embedded etcd и сколько ресурсов закладывать.">
|
||||||
|
|
@ -83,13 +83,13 @@
|
||||||
<meta property="article:tag" content="Proxmox">
|
<meta property="article:tag" content="Proxmox">
|
||||||
<meta property="article:tag" content="Architecture">
|
<meta property="article:tag" content="Architecture">
|
||||||
<meta property="article:tag" content="Ha">
|
<meta property="article:tag" content="Ha">
|
||||||
<meta property="og:image" content="http://192.168.11.190:1313/posts/k3s-architecture/featured.png">
|
<meta property="og:image" content="http://localhost:1313/posts/k3s-architecture/featured.png">
|
||||||
<meta property="og:see_also" content="http://192.168.11.190:1313/posts/k3s-installation/">
|
<meta property="og:see_also" content="http://localhost:1313/posts/k3s-installation/">
|
||||||
<meta property="og:see_also" content="http://192.168.11.190:1313/posts/k3s-infrastructure/">
|
<meta property="og:see_also" content="http://localhost:1313/posts/k3s-infrastructure/">
|
||||||
|
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<meta name="twitter:image" content="http://192.168.11.190:1313/posts/k3s-architecture/featured.png">
|
<meta name="twitter:image" content="http://localhost:1313/posts/k3s-architecture/featured.png">
|
||||||
<meta name="twitter:title" content="K3s HA для homelab: архитектура без боли">
|
<meta name="twitter:title" content="K3s HA для homelab: архитектура без боли">
|
||||||
<meta name="twitter:description" content="Полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Разбираем архитектуру K3s HA кластера: почему именно 3 master ноды, зачем embedded etcd и сколько ресурсов закладывать.">
|
<meta name="twitter:description" content="Полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Разбираем архитектуру K3s HA кластера: почему именно 3 master ноды, зачем embedded etcd и сколько ресурсов закладывать.">
|
||||||
|
|
||||||
|
|
@ -214,6 +214,39 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module" src="/js/firebase.min.cad74e0625f72f359ec6d6fed579b87733749de70400e7614048050ed08832ee3f58983d5d139fb1ddc5f7f2f5047d45ed80ec923534a3660fc3a7965f936866.js" integrity="sha512-ytdOBiX3LzWextb+1Xm4dzN0necEAOdhQEgFDtCIMu4/WJg9XROfsd3F9/L1BH1F7YDskjU0o2YPw6eWX5NoZg=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script id="firebase-config"
|
||||||
|
type="application/json"
|
||||||
|
data-views="views_posts/k3s-architecture/index.md"
|
||||||
|
data-likes="likes_posts/k3s-architecture/index.md">
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"apiKey": "AIzaSyBBfzADrGgnwTIyW67gfZSrAtkoybxvmdI",
|
||||||
|
"authDomain": "oakazanin-hugo-blowfish.firebaseapp.com",
|
||||||
|
"projectId": "oakazanin-hugo-blowfish",
|
||||||
|
"storageBucket": "oakazanin-hugo-blowfish.firebasestorage.app",
|
||||||
|
"messagingSenderId": "945151844512",
|
||||||
|
"appId": "1:945151844512:web:22602cc010f5b7e0cca9c5",
|
||||||
|
"measurementId": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -241,7 +274,7 @@
|
||||||
"headline": "K3s HA для homelab: архитектура без боли",
|
"headline": "K3s HA для homelab: архитектура без боли",
|
||||||
"description": "Полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Разбираем архитектуру K3s HA кластера: почему именно 3 master ноды, зачем embedded etcd и сколько ресурсов закладывать.",
|
"description": "Полноценный Kubernetes в бинарнике на 50MB вместо 1.5GB зависимостей. Разбираем архитектуру K3s HA кластера: почему именно 3 master ноды, зачем embedded etcd и сколько ресурсов закладывать.",
|
||||||
"inLanguage": "ru",
|
"inLanguage": "ru",
|
||||||
"url" : "http://192.168.11.190:1313/posts/k3s-architecture/",
|
"url" : "http://localhost:1313/posts/k3s-architecture/",
|
||||||
"author" : {
|
"author" : {
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"name": "Олег Казанин"
|
"name": "Олег Казанин"
|
||||||
|
|
@ -447,6 +480,83 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="hero" class="h-[150px] md:h-[200px]"></div>
|
||||||
|
|
||||||
|
<div class="fixed inset-x-0 top-0 h-[800px] single_hero_background nozoom">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img
|
||||||
|
id="background-image"
|
||||||
|
src="/posts/k3s-architecture/featured_hu_79735d97c46e93a6.png"
|
||||||
|
role="presentation"
|
||||||
|
loading="eager"
|
||||||
|
decoding="async"
|
||||||
|
fetchpriority="high"
|
||||||
|
class="absolute inset-0 w-full h-full object-cover"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-t from-neutral dark:from-neutral-800 to-transparent mix-blend-normal"></div>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 opacity-60 bg-gradient-to-t from-neutral dark:from-neutral-800 to-neutral-100 dark:to-neutral-800 mix-blend-normal"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="background-blur"
|
||||||
|
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/background-blur.min.605b3b942818f0ab5a717ae446135ec46b8ee5a2ad12ae56fb90dc2a76ce30c388f9fec8bcc18db15bd47e3fa8a09d779fa12aa9c184cf614a315bc72c6c163d.js"
|
||||||
|
integrity="sha512-YFs7lCgY8KtacXrkRhNexGuO5aKtEq5W+5DcKnbOMMOI+f7IvMGNsVvUfj+ooJ13n6EqqcGEz2FKMVvHLGwWPQ=="
|
||||||
|
data-blur-id="background-blur"
|
||||||
|
data-image-id="background-image"
|
||||||
|
data-image-url="/posts/k3s-architecture/featured_hu_79735d97c46e93a6.png"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<header id="single_header" class="mt-5 max-w-prose">
|
<header id="single_header" class="mt-5 max-w-prose">
|
||||||
|
|
||||||
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
|
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
|
||||||
|
|
@ -1807,6 +1917,9 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -1955,7 +2068,7 @@
|
||||||
<div
|
<div
|
||||||
id="search-wrapper"
|
id="search-wrapper"
|
||||||
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
||||||
data-url="http://192.168.11.190:1313/">
|
data-url="http://localhost:1313/">
|
||||||
<div
|
<div
|
||||||
id="search-modal"
|
id="search-modal"
|
||||||
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 672 KiB After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 980 KiB |
|
After Width: | Height: | Size: 786 KiB |
|
After Width: | Height: | Size: 258 KiB |
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="canonical" href="http://192.168.11.190:1313/posts/k3s-infrastructure/">
|
<link rel="canonical" href="http://localhost:1313/posts/k3s-infrastructure/">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta property="og:url" content="http://192.168.11.190:1313/posts/k3s-infrastructure/">
|
<meta property="og:url" content="http://localhost:1313/posts/k3s-infrastructure/">
|
||||||
<meta property="og:site_name" content="Олег Казанин">
|
<meta property="og:site_name" content="Олег Казанин">
|
||||||
<meta property="og:title" content="Подготовить инфраструктуру для K3s в Proxmox">
|
<meta property="og:title" content="Подготовить инфраструктуру для K3s в Proxmox">
|
||||||
<meta property="og:description" content="Создаём 5 VM в Proxmox с Debian 12, настраиваем сеть, отключаем swap и готовим систему для K3s. Пошаговая инструкция без сюрпризов.">
|
<meta property="og:description" content="Создаём 5 VM в Proxmox с Debian 12, настраиваем сеть, отключаем swap и готовим систему для K3s. Пошаговая инструкция без сюрпризов.">
|
||||||
|
|
@ -83,13 +83,13 @@
|
||||||
<meta property="article:tag" content="Proxmox">
|
<meta property="article:tag" content="Proxmox">
|
||||||
<meta property="article:tag" content="Infrastructure">
|
<meta property="article:tag" content="Infrastructure">
|
||||||
<meta property="article:tag" content="Debian">
|
<meta property="article:tag" content="Debian">
|
||||||
<meta property="og:image" content="http://192.168.11.190:1313/posts/k3s-infrastructure/featured.png">
|
<meta property="og:image" content="http://localhost:1313/posts/k3s-infrastructure/featured.png">
|
||||||
<meta property="og:see_also" content="http://192.168.11.190:1313/posts/k3s-installation/">
|
<meta property="og:see_also" content="http://localhost:1313/posts/k3s-installation/">
|
||||||
<meta property="og:see_also" content="http://192.168.11.190:1313/posts/k3s-architecture/">
|
<meta property="og:see_also" content="http://localhost:1313/posts/k3s-part1-architecture/">
|
||||||
|
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<meta name="twitter:image" content="http://192.168.11.190:1313/posts/k3s-infrastructure/featured.png">
|
<meta name="twitter:image" content="http://localhost:1313/posts/k3s-infrastructure/featured.png">
|
||||||
<meta name="twitter:title" content="Подготовить инфраструктуру для K3s в Proxmox">
|
<meta name="twitter:title" content="Подготовить инфраструктуру для K3s в Proxmox">
|
||||||
<meta name="twitter:description" content="Создаём 5 VM в Proxmox с Debian 12, настраиваем сеть, отключаем swap и готовим систему для K3s. Пошаговая инструкция без сюрпризов.">
|
<meta name="twitter:description" content="Создаём 5 VM в Proxmox с Debian 12, настраиваем сеть, отключаем swap и готовим систему для K3s. Пошаговая инструкция без сюрпризов.">
|
||||||
|
|
||||||
|
|
@ -214,6 +214,39 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module" src="/js/firebase.min.cad74e0625f72f359ec6d6fed579b87733749de70400e7614048050ed08832ee3f58983d5d139fb1ddc5f7f2f5047d45ed80ec923534a3660fc3a7965f936866.js" integrity="sha512-ytdOBiX3LzWextb+1Xm4dzN0necEAOdhQEgFDtCIMu4/WJg9XROfsd3F9/L1BH1F7YDskjU0o2YPw6eWX5NoZg=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script id="firebase-config"
|
||||||
|
type="application/json"
|
||||||
|
data-views="views_posts/k3s-infrastructure/index.md"
|
||||||
|
data-likes="likes_posts/k3s-infrastructure/index.md">
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"apiKey": "AIzaSyBBfzADrGgnwTIyW67gfZSrAtkoybxvmdI",
|
||||||
|
"authDomain": "oakazanin-hugo-blowfish.firebaseapp.com",
|
||||||
|
"projectId": "oakazanin-hugo-blowfish",
|
||||||
|
"storageBucket": "oakazanin-hugo-blowfish.firebasestorage.app",
|
||||||
|
"messagingSenderId": "945151844512",
|
||||||
|
"appId": "1:945151844512:web:22602cc010f5b7e0cca9c5",
|
||||||
|
"measurementId": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -241,7 +274,7 @@
|
||||||
"headline": "Подготовить инфраструктуру для K3s в Proxmox",
|
"headline": "Подготовить инфраструктуру для K3s в Proxmox",
|
||||||
"description": "Создаём 5 VM в Proxmox с Debian 12, настраиваем сеть, отключаем swap и готовим систему для K3s. Пошаговая инструкция без сюрпризов.",
|
"description": "Создаём 5 VM в Proxmox с Debian 12, настраиваем сеть, отключаем swap и готовим систему для K3s. Пошаговая инструкция без сюрпризов.",
|
||||||
"inLanguage": "ru",
|
"inLanguage": "ru",
|
||||||
"url" : "http://192.168.11.190:1313/posts/k3s-infrastructure/",
|
"url" : "http://localhost:1313/posts/k3s-infrastructure/",
|
||||||
"author" : {
|
"author" : {
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"name": "Олег Казанин"
|
"name": "Олег Казанин"
|
||||||
|
|
@ -447,6 +480,83 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="hero" class="h-[150px] md:h-[200px]"></div>
|
||||||
|
|
||||||
|
<div class="fixed inset-x-0 top-0 h-[800px] single_hero_background nozoom">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img
|
||||||
|
id="background-image"
|
||||||
|
src="/posts/k3s-infrastructure/featured_hu_16a8e5a35380d2ac.png"
|
||||||
|
role="presentation"
|
||||||
|
loading="eager"
|
||||||
|
decoding="async"
|
||||||
|
fetchpriority="high"
|
||||||
|
class="absolute inset-0 w-full h-full object-cover"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-t from-neutral dark:from-neutral-800 to-transparent mix-blend-normal"></div>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 opacity-60 bg-gradient-to-t from-neutral dark:from-neutral-800 to-neutral-100 dark:to-neutral-800 mix-blend-normal"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="background-blur"
|
||||||
|
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/background-blur.min.605b3b942818f0ab5a717ae446135ec46b8ee5a2ad12ae56fb90dc2a76ce30c388f9fec8bcc18db15bd47e3fa8a09d779fa12aa9c184cf614a315bc72c6c163d.js"
|
||||||
|
integrity="sha512-YFs7lCgY8KtacXrkRhNexGuO5aKtEq5W+5DcKnbOMMOI+f7IvMGNsVvUfj+ooJ13n6EqqcGEz2FKMVvHLGwWPQ=="
|
||||||
|
data-blur-id="background-blur"
|
||||||
|
data-image-id="background-image"
|
||||||
|
data-image-url="/posts/k3s-infrastructure/featured_hu_16a8e5a35380d2ac.png"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<header id="single_header" class="mt-5 max-w-prose">
|
<header id="single_header" class="mt-5 max-w-prose">
|
||||||
|
|
||||||
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
|
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
|
||||||
|
|
@ -569,14 +679,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<span class="ps-2"><span class="flex cursor-pointer">
|
|
||||||
<span
|
|
||||||
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
|
||||||
Черновик
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -950,7 +1052,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
||||||
<a href="/posts/k3s-architecture/">
|
<a href="/posts/k3s-part1-architecture/">
|
||||||
Часть 1:
|
Часть 1:
|
||||||
K3s HA для homelab: архитектура без боли
|
K3s HA для homelab: архитектура без боли
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -1768,7 +1870,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
||||||
<a href="/posts/k3s-architecture/">
|
<a href="/posts/k3s-part1-architecture/">
|
||||||
Часть 1:
|
Часть 1:
|
||||||
K3s HA для homelab: архитектура без боли
|
K3s HA для homelab: архитектура без боли
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -1802,6 +1904,283 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h2 class="mt-8 text-2xl font-extrabold mb-10">Статьи по теме</h2>
|
||||||
|
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<article
|
||||||
|
class="article-link--related relative min-h-full min-w-full overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600">
|
||||||
|
|
||||||
|
<div class="flex-none relative overflow-hidden thumbnail_card_related">
|
||||||
|
<img
|
||||||
|
src="/posts/k3s-part1-architecture/featured_hu_93e9342ed126d912.png"
|
||||||
|
role="presentation"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
fetchpriority="low"
|
||||||
|
class="not-prose absolute inset-0 w-full h-full object-cover">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="p-4">
|
||||||
|
<header>
|
||||||
|
<a
|
||||||
|
|
||||||
|
href="/posts/k3s-part1-architecture/"
|
||||||
|
|
||||||
|
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
|
||||||
|
<h2>
|
||||||
|
K3s HA для homelab: архитектура без боли
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
<div class="text-sm text-neutral-500 dark:text-neutral-400">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap items-center">
|
||||||
|
|
||||||
|
|
||||||
|
<time datetime="2025-10-14T00:00:00+00:00">14 октября 2025</time><span class="px-2 text-primary-500">·</span><span title="Время чтения">8 минут</span><span class="px-2 text-primary-500">·</span><span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span
|
||||||
|
id="views_posts/k3s-part1-architecture/index.md"
|
||||||
|
class="animate-pulse inline-block text-transparent max-h-3 rounded-full -mt-[2px] align-middle bg-neutral-300 dark:bg-neutral-400"
|
||||||
|
title="views"
|
||||||
|
>loading</span
|
||||||
|
>
|
||||||
|
<span class="inline-block align-text-bottom"><span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
||||||
|
<path fill="currentColor" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></span>
|
||||||
|
</span>
|
||||||
|
<span class="px-2 text-primary-500">·</span><span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span
|
||||||
|
id="likes_posts/k3s-part1-architecture/index.md"
|
||||||
|
class="animate-pulse inline-block text-transparent max-h-3 rounded-full -mt-[2px] align-middle bg-neutral-300 dark:bg-neutral-400"
|
||||||
|
title="likes"
|
||||||
|
>loading</span
|
||||||
|
>
|
||||||
|
<span class="inline-block align-text-bottom"><span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<path fill="currentColor" d="M47.6 300.4L228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6 0 115.2 0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z"/></svg></span></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap items-center">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/kubernetes/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Kubernetes
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/k3s/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
K3s
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/homelab/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Homelab
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/proxmox/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Proxmox
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/architecture/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Architecture
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/ha/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Ha
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/devops/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Devops
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="px-6 pt-4 pb-2"></div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -1819,7 +2198,7 @@
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="flex text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
|
class="flex text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
|
||||||
href="/posts/k3s-architecture/">
|
href="/posts/k3s-part1-architecture/">
|
||||||
<span class="leading-6">
|
<span class="leading-6">
|
||||||
<span class="inline-block rtl:rotate-180">←</span> K3s HA для homelab: архитектура без боли
|
<span class="inline-block rtl:rotate-180">←</span> K3s HA для homelab: архитектура без боли
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -1963,7 +2342,7 @@
|
||||||
<div
|
<div
|
||||||
id="search-wrapper"
|
id="search-wrapper"
|
||||||
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
||||||
data-url="http://192.168.11.190:1313/">
|
data-url="http://localhost:1313/">
|
||||||
<div
|
<div
|
||||||
id="search-modal"
|
id="search-modal"
|
||||||
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 672 KiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 741 KiB After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 993 KiB |
|
After Width: | Height: | Size: 904 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="canonical" href="http://192.168.11.190:1313/posts/k3s-installation/">
|
<link rel="canonical" href="http://localhost:1313/posts/k3s-installation/">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta property="og:url" content="http://192.168.11.190:1313/posts/k3s-installation/">
|
<meta property="og:url" content="http://localhost:1313/posts/k3s-installation/">
|
||||||
<meta property="og:site_name" content="Олег Казанин">
|
<meta property="og:site_name" content="Олег Казанин">
|
||||||
<meta property="og:title" content="Установить K3s HA кластер">
|
<meta property="og:title" content="Установить K3s HA кластер">
|
||||||
<meta property="og:description" content="Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl.">
|
<meta property="og:description" content="Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl.">
|
||||||
|
|
@ -83,13 +83,13 @@
|
||||||
<meta property="article:tag" content="Installation">
|
<meta property="article:tag" content="Installation">
|
||||||
<meta property="article:tag" content="Ha">
|
<meta property="article:tag" content="Ha">
|
||||||
<meta property="article:tag" content="Etcd">
|
<meta property="article:tag" content="Etcd">
|
||||||
<meta property="og:image" content="http://192.168.11.190:1313/posts/k3s-installation/featured.png">
|
<meta property="og:image" content="http://localhost:1313/posts/k3s-installation/featured.png">
|
||||||
<meta property="og:see_also" content="http://192.168.11.190:1313/posts/k3s-infrastructure/">
|
<meta property="og:see_also" content="http://localhost:1313/posts/k3s-part2-infrastructure/">
|
||||||
<meta property="og:see_also" content="http://192.168.11.190:1313/posts/k3s-architecture/">
|
<meta property="og:see_also" content="http://localhost:1313/posts/k3s-part1-architecture/">
|
||||||
|
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<meta name="twitter:image" content="http://192.168.11.190:1313/posts/k3s-installation/featured.png">
|
<meta name="twitter:image" content="http://localhost:1313/posts/k3s-installation/featured.png">
|
||||||
<meta name="twitter:title" content="Установить K3s HA кластер">
|
<meta name="twitter:title" content="Установить K3s HA кластер">
|
||||||
<meta name="twitter:description" content="Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl.">
|
<meta name="twitter:description" content="Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl.">
|
||||||
|
|
||||||
|
|
@ -214,6 +214,39 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module" src="/js/firebase.min.cad74e0625f72f359ec6d6fed579b87733749de70400e7614048050ed08832ee3f58983d5d139fb1ddc5f7f2f5047d45ed80ec923534a3660fc3a7965f936866.js" integrity="sha512-ytdOBiX3LzWextb+1Xm4dzN0necEAOdhQEgFDtCIMu4/WJg9XROfsd3F9/L1BH1F7YDskjU0o2YPw6eWX5NoZg=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script id="firebase-config"
|
||||||
|
type="application/json"
|
||||||
|
data-views="views_posts/k3s-installation/index.md"
|
||||||
|
data-likes="likes_posts/k3s-installation/index.md">
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"apiKey": "AIzaSyBBfzADrGgnwTIyW67gfZSrAtkoybxvmdI",
|
||||||
|
"authDomain": "oakazanin-hugo-blowfish.firebaseapp.com",
|
||||||
|
"projectId": "oakazanin-hugo-blowfish",
|
||||||
|
"storageBucket": "oakazanin-hugo-blowfish.firebasestorage.app",
|
||||||
|
"messagingSenderId": "945151844512",
|
||||||
|
"appId": "1:945151844512:web:22602cc010f5b7e0cca9c5",
|
||||||
|
"measurementId": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -241,7 +274,7 @@
|
||||||
"headline": "Установить K3s HA кластер",
|
"headline": "Установить K3s HA кластер",
|
||||||
"description": "Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl.",
|
"description": "Один curl-скрипт на каждую ноду - и через 15 минут у вас работающий HA-кластер. Устанавливаем K3s на 3 master и 2 worker ноды, настраиваем kubectl.",
|
||||||
"inLanguage": "ru",
|
"inLanguage": "ru",
|
||||||
"url" : "http://192.168.11.190:1313/posts/k3s-installation/",
|
"url" : "http://localhost:1313/posts/k3s-installation/",
|
||||||
"author" : {
|
"author" : {
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
"name": "Олег Казанин"
|
"name": "Олег Казанин"
|
||||||
|
|
@ -447,6 +480,83 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="hero" class="h-[150px] md:h-[200px]"></div>
|
||||||
|
|
||||||
|
<div class="fixed inset-x-0 top-0 h-[800px] single_hero_background nozoom">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img
|
||||||
|
id="background-image"
|
||||||
|
src="/posts/k3s-installation/featured_hu_535e29ddbb3baa24.png"
|
||||||
|
role="presentation"
|
||||||
|
loading="eager"
|
||||||
|
decoding="async"
|
||||||
|
fetchpriority="high"
|
||||||
|
class="absolute inset-0 w-full h-full object-cover"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-t from-neutral dark:from-neutral-800 to-transparent mix-blend-normal"></div>
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 opacity-60 bg-gradient-to-t from-neutral dark:from-neutral-800 to-neutral-100 dark:to-neutral-800 mix-blend-normal"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="background-blur"
|
||||||
|
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/background-blur.min.605b3b942818f0ab5a717ae446135ec46b8ee5a2ad12ae56fb90dc2a76ce30c388f9fec8bcc18db15bd47e3fa8a09d779fa12aa9c184cf614a315bc72c6c163d.js"
|
||||||
|
integrity="sha512-YFs7lCgY8KtacXrkRhNexGuO5aKtEq5W+5DcKnbOMMOI+f7IvMGNsVvUfj+ooJ13n6EqqcGEz2FKMVvHLGwWPQ=="
|
||||||
|
data-blur-id="background-blur"
|
||||||
|
data-image-id="background-image"
|
||||||
|
data-image-url="/posts/k3s-installation/featured_hu_535e29ddbb3baa24.png"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<header id="single_header" class="mt-5 max-w-prose">
|
<header id="single_header" class="mt-5 max-w-prose">
|
||||||
|
|
||||||
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
|
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
|
||||||
|
|
@ -569,14 +679,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<span class="ps-2"><span class="flex cursor-pointer">
|
|
||||||
<span
|
|
||||||
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
|
||||||
Черновик
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -966,7 +1068,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
||||||
<a href="/posts/k3s-architecture/">
|
<a href="/posts/k3s-part1-architecture/">
|
||||||
Часть 1:
|
Часть 1:
|
||||||
K3s HA для homelab: архитектура без боли
|
K3s HA для homelab: архитектура без боли
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -976,7 +1078,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
||||||
<a href="/posts/k3s-infrastructure/">
|
<a href="/posts/k3s-part2-infrastructure/">
|
||||||
Часть 2:
|
Часть 2:
|
||||||
Подготовить инфраструктуру для K3s в Proxmox
|
Подготовить инфраструктуру для K3s в Proxmox
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -1827,7 +1929,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
||||||
<a href="/posts/k3s-architecture/">
|
<a href="/posts/k3s-part1-architecture/">
|
||||||
Часть 1:
|
Часть 1:
|
||||||
K3s HA для homelab: архитектура без боли
|
K3s HA для homelab: архитектура без боли
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -1837,7 +1939,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
class="py-1 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
||||||
<a href="/posts/k3s-infrastructure/">
|
<a href="/posts/k3s-part2-infrastructure/">
|
||||||
Часть 2:
|
Часть 2:
|
||||||
Подготовить инфраструктуру для K3s в Proxmox
|
Подготовить инфраструктуру для K3s в Proxmox
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -1861,6 +1963,552 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h2 class="mt-8 text-2xl font-extrabold mb-10">Статьи по теме</h2>
|
||||||
|
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<article
|
||||||
|
class="article-link--related relative min-h-full min-w-full overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600">
|
||||||
|
|
||||||
|
<div class="flex-none relative overflow-hidden thumbnail_card_related">
|
||||||
|
<img
|
||||||
|
src="/posts/k3s-part1-architecture/featured_hu_93e9342ed126d912.png"
|
||||||
|
role="presentation"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
fetchpriority="low"
|
||||||
|
class="not-prose absolute inset-0 w-full h-full object-cover">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="p-4">
|
||||||
|
<header>
|
||||||
|
<a
|
||||||
|
|
||||||
|
href="/posts/k3s-part1-architecture/"
|
||||||
|
|
||||||
|
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
|
||||||
|
<h2>
|
||||||
|
K3s HA для homelab: архитектура без боли
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
<div class="text-sm text-neutral-500 dark:text-neutral-400">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap items-center">
|
||||||
|
|
||||||
|
|
||||||
|
<time datetime="2025-10-14T00:00:00+00:00">14 октября 2025</time><span class="px-2 text-primary-500">·</span><span title="Время чтения">8 минут</span><span class="px-2 text-primary-500">·</span><span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span
|
||||||
|
id="views_posts/k3s-part1-architecture/index.md"
|
||||||
|
class="animate-pulse inline-block text-transparent max-h-3 rounded-full -mt-[2px] align-middle bg-neutral-300 dark:bg-neutral-400"
|
||||||
|
title="views"
|
||||||
|
>loading</span
|
||||||
|
>
|
||||||
|
<span class="inline-block align-text-bottom"><span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
||||||
|
<path fill="currentColor" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></span>
|
||||||
|
</span>
|
||||||
|
<span class="px-2 text-primary-500">·</span><span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span
|
||||||
|
id="likes_posts/k3s-part1-architecture/index.md"
|
||||||
|
class="animate-pulse inline-block text-transparent max-h-3 rounded-full -mt-[2px] align-middle bg-neutral-300 dark:bg-neutral-400"
|
||||||
|
title="likes"
|
||||||
|
>loading</span
|
||||||
|
>
|
||||||
|
<span class="inline-block align-text-bottom"><span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<path fill="currentColor" d="M47.6 300.4L228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6 0 115.2 0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z"/></svg></span></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap items-center">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/kubernetes/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Kubernetes
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/k3s/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
K3s
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/homelab/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Homelab
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/proxmox/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Proxmox
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/architecture/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Architecture
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/ha/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Ha
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/devops/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Devops
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="px-6 pt-4 pb-2"></div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<article
|
||||||
|
class="article-link--related relative min-h-full min-w-full overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600">
|
||||||
|
|
||||||
|
<div class="flex-none relative overflow-hidden thumbnail_card_related">
|
||||||
|
<img
|
||||||
|
src="/posts/k3s-part2-infrastructure/featured_hu_fa55fce8dafb1970.png"
|
||||||
|
role="presentation"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
fetchpriority="low"
|
||||||
|
class="not-prose absolute inset-0 w-full h-full object-cover">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="p-4">
|
||||||
|
<header>
|
||||||
|
<a
|
||||||
|
|
||||||
|
href="/posts/k3s-part2-infrastructure/"
|
||||||
|
|
||||||
|
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
|
||||||
|
<h2>
|
||||||
|
Подготовить инфраструктуру для K3s в Proxmox
|
||||||
|
|
||||||
|
</h2>
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
<div class="text-sm text-neutral-500 dark:text-neutral-400">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap items-center">
|
||||||
|
|
||||||
|
|
||||||
|
<time datetime="2025-10-21T00:00:00+00:00">21 октября 2025</time><span class="px-2 text-primary-500">·</span><span title="Время чтения">10 минут</span><span class="px-2 text-primary-500">·</span><span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span
|
||||||
|
id="views_posts/k3s-part2-infrastructure/index.md"
|
||||||
|
class="animate-pulse inline-block text-transparent max-h-3 rounded-full -mt-[2px] align-middle bg-neutral-300 dark:bg-neutral-400"
|
||||||
|
title="views"
|
||||||
|
>loading</span
|
||||||
|
>
|
||||||
|
<span class="inline-block align-text-bottom"><span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
||||||
|
<path fill="currentColor" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></span>
|
||||||
|
</span>
|
||||||
|
<span class="px-2 text-primary-500">·</span><span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span
|
||||||
|
id="likes_posts/k3s-part2-infrastructure/index.md"
|
||||||
|
class="animate-pulse inline-block text-transparent max-h-3 rounded-full -mt-[2px] align-middle bg-neutral-300 dark:bg-neutral-400"
|
||||||
|
title="likes"
|
||||||
|
>loading</span
|
||||||
|
>
|
||||||
|
<span class="inline-block align-text-bottom"><span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<path fill="currentColor" d="M47.6 300.4L228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6 0 115.2 0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z"/></svg></span></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap items-center">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/kubernetes/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Kubernetes
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/k3s/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
K3s
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/homelab/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Homelab
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/proxmox/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Proxmox
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/infrastructure/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Infrastructure
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/debian/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Debian
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="relative mt-[0.5rem] me-2" href="/tags/devops/">
|
||||||
|
<span class="flex cursor-pointer">
|
||||||
|
<span
|
||||||
|
class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400">
|
||||||
|
Devops
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="px-6 pt-4 pb-2"></div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -1878,7 +2526,7 @@
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="flex text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
|
class="flex text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
|
||||||
href="/posts/k3s-infrastructure/">
|
href="/posts/k3s-part2-infrastructure/">
|
||||||
<span class="leading-6">
|
<span class="leading-6">
|
||||||
<span class="inline-block rtl:rotate-180">←</span> Подготовить инфраструктуру для K3s в Proxmox
|
<span class="inline-block rtl:rotate-180">←</span> Подготовить инфраструктуру для K3s в Proxmox
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -1892,6 +2540,19 @@
|
||||||
</span>
|
</span>
|
||||||
<span class="flex flex-col items-end">
|
<span class="flex flex-col items-end">
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="flex text-right text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
|
||||||
|
href="/posts/blog-part-1-architecture/">
|
||||||
|
<span class="leading-6">
|
||||||
|
Блог на Hugo в K3s: часть 1 - архитектура и первый запуск <span class="inline-block rtl:rotate-180">→</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<span class="me-6 mt-1 text-xs text-neutral-500 dark:text-neutral-400">
|
||||||
|
<time datetime="2026-01-03T00:00:00+00:00">3 января 2026</time>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -2009,7 +2670,7 @@
|
||||||
<div
|
<div
|
||||||
id="search-wrapper"
|
id="search-wrapper"
|
||||||
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
||||||
data-url="http://192.168.11.190:1313/">
|
data-url="http://localhost:1313/">
|
||||||
<div
|
<div
|
||||||
id="search-modal"
|
id="search-modal"
|
||||||
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 741 KiB After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 20 KiB |