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

Как я настроил CI/CD для блога на Go

В прошлой статье я писал о своём движке для статических блогов gossrng. А теперь настало время настроить автоматизацию деплоя готового сайта на сервер.

Переписывание движка

В прошлой итерации у движка были некоторые недостатки:

В итоге я решил его немного исправить, чтобы он работал как cli-утилита. То есть чтобы его можно было запустить через терминал, передать нужные параметры и не париться. А все шаблоны и конфиги быле перенесены в директорию с контентом.

Также была добавлена фича инициализации проекта. Теперь для сборки проекта нужно всего три команды:

$ go install github.com/e-kucheriavyi/gossrng/cmd/gossrng@latest # устанавливаем утилиту
$ gossrng -m init -r ./content # инициализируем шаблонный проект
$ gossrng -m export -r ./content # экспортируем проект в папку ./dist

Вот так просто у нас на руках появляется директория, которую можно закинуть на сервер и радоваться.

Базовые алгоритмы движка не изменились, просто были проведены работы по улучшению UX. Детально останавливаться не будем, но всегда можно посмотреть вот этот коммит: 369fd12d7c1c4c36f3632056ec0ef7764e62a6a3

Настройка сервера и ручной деплой

Мы не будем ничего выдумывать и просто будем отправлять статические файлы через scp — secure copy protocol, который работает поверх SSH. А раздавать всё это будет nginx.

Настраиваем nginx

Я буду просто раздавать статические файлы из директории /var/www/html, поэтому у меня будет достаточно простая настройка nginx.

Для начала, убеждаемся, что у нас установлены nginx (веб-сервер) и ufw (файервол). Дальше нужно выполнить несколько команд:

$ sudo ufw allow http # Открываем порты: 80 для http и 443 для https
$ sudo systemctl enable nginx # Включаем автозапуск nginx

Дальше нужно отредактировать конфиг nginx, который по умолчанию лежит в /etc/nginx/nginx.conf или /etc/nginx/sites-available/default:

server {
	root /var/www/html;

	index index.html;
	server_name kucheriavyi.ru;

	error_page 404 /404.html;
	error_page 403 /403.html;
	error_page 500 /500.html;

	location / {
		try_files $uri $uri/ =404;
	}
}

Такой конфиг позволит раздавать статические файлы по HTTP, а это значит, что большинство современных браузеров будут ругаться, что ваш сайт небезопасный. Чтобы начать раздавать его по HTTPS придётся выписать TLS-сертификат. Я рекомендую использовать letsencrypt, потому что он бесплатный. Для этого я обычно устанавливаю certbot по инструкции, а потом запускаю следующую команду:

$ sudo certbot -d kucheriavyi.ru --nginx

Далее перезапускаем nginx:

$ sudo systemctl restart nginx

И всё, наш сервер готов.

Настраиваем SSH

Я ожидаю, что на вашем VPS уже включен SSH и вы смогли к нему подключиться. Если нет, то нужен простой копеечный:

$ sudo apt install ssh
$ sudo ufw allow 22

Для подключения к SSH желательно создать нового пользователя, который будет заниматься исключительно деплоем. Далее для этого пользователя нам нужно будет подготовить отдельный ключ. Для этого на своём локальном компьютере запустите команду ssh-keygen и создайте ключ без пароля:

ssh-keygen                         
Generating public/private ed25519 key pair.
Enter 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 и скопируйте содержимое — оно нам понадобится при настройке репозитория.

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

Деплоим

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

$ 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. Если хотите просто всё скопировать, то вот итоговый файл:

name: Deploy
run-name: ${{ github.actor }} is deploying content
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        go-version: ['1.25']

    steps:
      - uses: actions/checkout@v4
      - name: Setup Go ${{ matrix.go-version }}
        uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go-version }}
      - name: Install dependencies
        run: go install github.com/e-kucheriavyi/gossrng/cmd/gossrng@latest
      - name: Build
        run: gossrng -m export -r ./
      - name: setup ssh
        run: |
          echo "${{ secrets.SSH_KEY }}" > ./.id_rsa && chmod 600 ./.id_rsa
      - name: deploy
        run: scp -o StrictHostKeyChecking=no -i ./.id_rsa -r ./dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/var/www/html

А теперь давайте разбираться.

Из чего состоит CI/CD на github

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

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

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

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

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

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

Затем нужно указать события (Events) при которых workflow будет запускаться. В нашем случае — пуш в ветку main:

on:
  push:
    branches: [main]

Дальше нужно создать джобу, дать ей название, указать раннер и прочие параметры вроде версии Go:

jobs: # список джоб
  deploy: # джоба с названием deploy
    runs-on: ubuntu-latest # на каком сервере будем запускать
    strategy:
      matrix:
        go-version: ['1.25'] # версия go

Последни шаг — создать шаги (я не извиняюсь за каламбуры). Начнём с настройки окружения:

steps:
  - uses: actions/checkout@v4 # встроенный action для загрузки актуального кода
  - name: Setup Go ${{ matrix.go-version }}
    uses: actions/setup-go@v5 # встроенный action для установки го
    with:
      go-version: ${{ matrix.go-version }}

Теперь нужно подгрузить зависимости. В нашем случае — движок:

- name: Install dependencies
  run: go install github.com/e-kucheriavyi/gossrng/cmd/gossrng@latest
- name: Build
  run: gossrng -m export -r ./

У шага с SSH есть несколько нюансов:

- name: setup ssh
  run: |
    echo "${{ secrets.SSH_KEY }}" > ./.id_rsa && chmod 600 ./.id_rsa
  - name: deploy
    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. Или присылать уведомления через чат-бота. В общем, можно реализовать почти всё, до чего у вас хватит фантазии.