Как я настроил CI/CD для блога на Go
В прошлой статье я писал о своём движке для статических блогов gossrng. А теперь настало время настроить автоматизацию деплоя готового сайта на сервер.
Переписывание движка
В прошлой итерации у движка были некоторые недостатки:
- Для работы с ним нужно было клонировать репозиторий.
- Нужно было настраивать конфиг.
- Файлы лежали в двух разных местах — в репе движка и в репе с контентом.
В итоге я решил его немного исправить, чтобы он работал как cli-утилита. То есть чтобы его можно было запустить через терминал, передать нужные параметры и не париться. А все шаблоны и конфиги быле перенесены в директорию с контентом.
Также была добавлена фича инициализации проекта. Теперь для сборки проекта нужно всего три команды:
1$ go install github.com/e-kucheriavyi/gossrng/cmd/gossrng@latest # устанавливаем утилиту
2$ gossrng -m init -r ./content # инициализируем шаблонный проект
3$ gossrng -m export -r ./content # экспортируем проект в папку ./dist
Вот так просто у нас на руках появляется директория, которую можно закинуть на сервер и радоваться.
Базовые алгоритмы движка не изменились, просто были проведены работы по улучшению UX. Детально останавливаться не будем, но всегда можно посмотреть вот этот коммит: 369fd12d7c1c4c36f3632056ec0ef7764e62a6a3
Настройка сервера и ручной деплой
Мы не будем ничего выдумывать и просто будем отправлять статические файлы через scp
— secure copy protocol, который работает поверх SSH. А раздавать всё это будет nginx.
Настраиваем nginx
Я буду просто раздавать статические файлы из директории /var/www/html
, поэтому у меня будет достаточно простая настройка nginx.
Для начала, убеждаемся, что у нас установлены nginx (веб-сервер) и ufw (файервол). Дальше нужно выполнить несколько команд:
1$ sudo ufw allow http # Открываем порты: 80 для http и 443 для https
2$ sudo systemctl enable nginx # Включаем автозапуск nginx
Дальше нужно отредактировать конфиг nginx, который по умолчанию лежит в /etc/nginx/nginx.conf
или /etc/nginx/sites-available/default
:
1server {
2 root /var/www/html;
3
4 index index.html;
5 server_name kucheriavyi.ru;
6
7 error_page 404 /404.html;
8 error_page 403 /403.html;
9 error_page 500 /500.html;
10
11 location / {
12 try_files $uri $uri/ =404;
13 }
14}
Такой конфиг позволит раздавать статические файлы по HTTP, а это значит, что большинство современных браузеров будут ругаться, что ваш сайт небезопасный. Чтобы начать раздавать его по HTTPS придётся выписать TLS-сертификат. Я рекомендую использовать letsencrypt, потому что он бесплатный. Для этого я обычно устанавливаю certbot по инструкции, а потом запускаю следующую команду:
1$ sudo certbot -d kucheriavyi.ru --nginx
Далее перезапускаем nginx:
1$ sudo systemctl restart nginx
И всё, наш сервер готов.
Настраиваем SSH
Я ожидаю, что на вашем VPS уже включен SSH и вы смогли к нему подключиться. Если нет, то нужен простой копеечный:
1$ sudo apt install ssh
2$ sudo ufw allow 22
Для подключения к SSH желательно создать нового пользователя, который будет заниматься исключительно деплоем. Далее для этого пользователя нам нужно будет подготовить отдельный ключ. Для этого на своём локальном компьютере запустите команду ssh-keygen
и создайте ключ без пароля:
1ssh-keygen
2Generating public/private ed25519 key pair.
3Enter file in which to save the key (/path/to/your/file):
Чтобы ваш сервер запомнил этот ключ, нужно запусить команду ssh-copy-id -i /path/to/your/file user@host
и ssh -i /path/to/your/file user@host
, чтобы уведиться, что ключ созранился.
Далее запустите команду cat /path/to/your/file
и скопируйте содержимое — оно нам понадобится при настройке репозитория.
Несколько полезных советов:
- Поменяйте дефолтный порт для SSH с
22
на какой-нибудь другой, а потом начните банить все IP-адреса, с которых стучатся в дефолтный порт. - Запретите подключение к серверу через root-пользователя.
- Запретите подключение по паролю и оставьте только по приватному ключу.
Деплоим
У нас есть настроенный сервер и сбилженный проект. Чтобы загрузить его на сервер, запускаем следующую команду:
1$ scp -i /path/to/your/file -r ./dist/* user@host:/var/www/html
Ждём пока файлы скопируются и всё готово. Открываем сайт и радуемся.
Это не самый быстрый вариант, если у вас много файлов — тогда лучше отправлять всё в сжатом виде. Например, с помощью
tar
.
Настройка github actions
Деплоить каждый раз руками довольно скучно, поэтому мы можем настроить CI/CD через GitHub actions. Сейчас было много непонятных слов, поэтому давайте разбираться.
CI/CD (англ. continious integration/continious deploy) — аббревиатура для непрерывной интеграции и непрерывного деплоя. А GitHub Actions — приблуда, с помощью которой мы можем настроить действия, при хоторых наш ci/cd будет запускаться.
Настроить его можно с помощью .yml
-файла, но мы начнём с прописывания секретов через интерфейс GitHub.
Сохраняем секреты для GitHub actions
Зайдите в свой репозиторий > Settings
. В боковом меню выберите пункт Secrets and variables
> Actions
. Вы увидите следующую страницу:
Тут нужно нажать New repository secret
и добавить три секрета:
SSH_USER
— имя пользователя, который подключается по SSH.SSH_HOST
— хост, к которому нужно подключаться.SSH_KEY
— приватный ключ (мы генерировали его с помощьюssh-keygen
).
Дальше эти секретные значения мы будем использовать в .yml
-файле для настройки CI/CD.
ВАЖНО! Никогда не храните ключи и секреты в репозитории. Даже если он приватный.
Настраиваем .github/workflows
Все файлы для GitHub Actions должны лежать в директории .github/workflows
в корне репозитория. Для нашего проекта мы создадим там файл deploy.yml
. Если хотите просто всё скопировать, то вот итоговый файл:
1name: Deploy
2run-name: ${{ github.actor }} is deploying content
3on:
4 push:
5 branches: [main]
6jobs:
7 deploy:
8 runs-on: ubuntu-latest
9 strategy:
10 matrix:
11 go-version: ['1.25']
12
13 steps:
14 - uses: actions/checkout@v4
15 - name: Setup Go ${{ matrix.go-version }}
16 uses: actions/setup-go@v5
17 with:
18 go-version: ${{ matrix.go-version }}
19 - name: Install dependencies
20 run: go install github.com/e-kucheriavyi/gossrng/cmd/gossrng@latest
21 - name: Build
22 run: gossrng -m export -r ./
23 - name: setup ssh
24 run: |
25 echo "${{ secrets.SSH_KEY }}" > ./.id_rsa && chmod 600 ./.id_rsa
26 - name: deploy
27 run: scp -o StrictHostKeyChecking=no -i ./.id_rsa -r ./dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/var/www/html
А теперь давайте разбираться.
Из чего состоит CI/CD на github
Немного терминов:
- Workflow — готовая конфигурация, которая записана в
.yml
-файле в директории.github/workflows
. - Event — триггер, по которому запускается Workflow.
- Job — набор шагов (steps), которые запускает runner.
- Step — отдельные команды или скрипты, которые запускаются на сервере. Это может быть как команды bash, так и целые actions.
- Runner — сервер, который запускает ваши джобы (job).
- Action — Набор job или кода, который запускается на сервере.
Подробнее об этом в документации GitHub.
А теперь построчно разберём наш файл. Сначала указываем информацию, которая будет отображаться в интерфейсе:
1name: Deploy
2run-name: ${{ github.actor }} is deploying content
Как видно на второй строке, сюда можно подставлять разные переменные.
Затем нужно указать события (Events) при которых workflow будет запускаться. В нашем случае — пуш в ветку main
:
1on:
2 push:
3 branches: [main]
Дальше нужно создать джобу, дать ей название, указать раннер и прочие параметры вроде версии Go:
1jobs: # список джоб
2 deploy: # джоба с названием deploy
3 runs-on: ubuntu-latest # на каком сервере будем запускать
4 strategy:
5 matrix:
6 go-version: ['1.25'] # версия go
Последни шаг — создать шаги (я не извиняюсь за каламбуры). Начнём с настройки окружения:
1steps:
2 - uses: actions/checkout@v4 # встроенный action для загрузки актуального кода
3 - name: Setup Go ${{ matrix.go-version }}
4 uses: actions/setup-go@v5 # встроенный action для установки го
5 with:
6 go-version: ${{ matrix.go-version }}
Теперь нужно подгрузить зависимости. В нашем случае — движок:
1- name: Install dependencies
2 run: go install github.com/e-kucheriavyi/gossrng/cmd/gossrng@latest
3- name: Build
4 run: gossrng -m export -r ./
У шага с SSH есть несколько нюансов:
- Мы не можем просто передать ключ, потому что его нужно сохранить в файл.
- При первом подключении к SSH у нас просят добавить хост в
known_host
. Для этого нужно либо вручную дать согласие, либо использовать утилитуyes
. Но чтобы не париться мы просто пропишем опцию-o StrictHostKeyChecking=no
. Я не уверен, насколько это безопасно.
1- name: setup ssh
2 run: |
3 echo "${{ secrets.SSH_KEY }}" > ./.id_rsa && chmod 600 ./.id_rsa
4 - name: deploy
5 run: scp -o StrictHostKeyChecking=no -i ./.id_rsa -r ./dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/var/www/html
Готово!
Если всё сделано правильно, то пуше в ветку main
у нас запустится workflow и всё задеплоится на сервер.
Заключние
Может показаться, что эта настройка какая-то муторная. На самом же деле всё довольно просто. Особенно, если разобраться. Всё-таки, для деплоя блога на gossrng никаких сложных действий не требуется. Но если делать это каждый раз руками, то уйдёт уйма времени и рано или поздно где-то будет допущена ошибка.
Это не всё, что можно знать о CI/CD, потому что есть ещё куча разных приколов. Например, можно запускать разные тесты, когда создаётся pull request. Или присылать уведомления через чат-бота. В общем, можно реализовать почти всё, до чего у вас хватит фантазии.