Евгений Кучерявый

Как я настроил 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 и скопируйте содержимое — оно нам понадобится при настройке репозитория.

Несколько полезных советов:

Деплоим

У нас есть настроенный сервер и сбилженный проект. Чтобы загрузить его на сервер, запускаем следующую команду:

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. Вы увидите следующую страницу:

Интерфейс настройки секретов в GitHub

Тут нужно нажать New repository secret и добавить три секрета:

Дальше эти секретные значения мы будем использовать в .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

Немного терминов:

Подробнее об этом в документации GitHub.

А теперь построчно разберём наш файл. Сначала указываем информацию, которая будет отображаться в интерфейсе:

1name: Deploy
2run-name: ${{ github.actor }} is deploying content

Как видно на второй строке, сюда можно подставлять разные переменные.

Пример workflow в интерфейсе github

Затем нужно указать события (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 есть несколько нюансов:

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. Или присылать уведомления через чат-бота. В общем, можно реализовать почти всё, до чего у вас хватит фантазии.