GitHub Actions: создание VPS и доставка кода проекта

Вот мы и достигли финального шага в нашем пути изучения git — в этой завершающей цикл публикаций статье мы применим на практике все знания из предыдущих публикаций и напишем workflow, который будет создавать VPS на стороне 1cloud, загружать на VM код проекта, настраивать окружение и весь необходимый софт для работы проекта.

Сам workflow и проект можно скачать отсюда. Рекомендуем ознакомиться со всей статьей целиком, так как дальнейшие публикации будут посвящены работе с 1cloud API и материал раздела «Работа с 1cloud API через GitHub Actions» очень пригодится. С преамбулой закончили, переходим к сути — познакомимся со схемой работы автоматизации нашего проекта.

Схема работы автоматизации

Упрощенно план работы автоматизации выглядит так: в ручном режиме запускается workflow -> запускается Python-скрипт создания VPS -> на VPS загружается код проекта -> настраивается виртуальное Python-окружение -> устанавливается и настраивается Nginx.

Итого у нас есть 2 сущности:

  • VPS — виртуальный сервер, который мы будем создавать по заранее заданному шаблону с помощью API. К нему будет обращаться Python-скрипт, который будет запускаться на виртуальной машине GitHub. Для работы с API нам нужен API-ключ, который мы будем передавать в скрипт через секреты. После создания VPS на него можно загружать код проекта.
  • Проект — это блог для Python-комьюнити, написанный на Django. Так как это пока альфа-версия, то мы не используем СУБД, поэтому для работы проекта потребуется только развернуть виртуальное окружение Python из requirements.txt, установить и настроить Nginx и Gunicorn.

Хорошо, у нас есть план и понимание с какими сущностями мы будем работать. Можно начинать писать workflow.

Workflow автоматизации

Workflow состоит из четырех джоб, каждый из которых содержит от двух до пяти шагов. Вначале workflow мы опишем его название, триггер срабатывания и переменные, которые будут использоваться далее по коду:

name: Create new VPS and deploy repo
on: workflow_dispatch
env:
  PROJECT_DIR: /var/www/
  PROJECT_DIR_app: /var/www/pyoops.com/pyoops
  REPO_LINK: https://github.com/Pseudolukian/pyoops.com.git
  REPO_NAME: pyoops.com
  vps_ip_file: VPS_IP.txt

Первая джоба — создание VPS. Она состоит их пяти шагов: 1) Checkout the repo — получение доступа к репозиторию; 2) Setup Python — обновление версии Python до 10; 3) Run the VPS create script — запуск Python-скрипта, который создает VPS; 4) Export VPS IP to file and check — сохранение IP-адреса VPS в файл и его проверка; 5) Save VPS IP — сохранение IP-адреса VPS как артефакта.

create_vps:
runs-on: ubuntu-latest

steps:
  - name: "Checkout the repo"
    uses: actions/checkout@v3
  
  - name: "Setup Python"
    uses: actions/setup-python@v4
    with:
      python-version: '3.10'
      cache: 'pip'
  
  - name: "Run the VPS create script"
    run: |
      pip install requests
      python3 ./1cloud_create_vps.py ${{ secrets.API_KEY }}
  
  - name: Export VPS IP to file and check
    run: |
      echo "${{ env.VPS }}" > ${{ env.vps_ip_file }}
      echo $(tail -1 ${{ env.vps_ip_file }})
 
  - name: Save VPS IP
    uses: actions/upload-artifact@v3
    with:
      name: VPS IP
      path: ${{ env.vps_ip_file }}

Следующая джоба — загрузка кода проекта на VPS. Эта джоба состоит из двух сложносоставных шагов:

  1. Download VPS IP artifact and check it — на этом шаге мы загружаем IP созданной VPS из артефакта в переменную VPS_IP и проверяем, что переменная создалась и содержит IP-адрес VPS;
  2. Upload repo to VPS — тут мы используем модуль ssh-action@master для подключения к VPS по SSH с использованием SSH-ключа, который мы заранее сгенерировали и добавили в Панель 1cloud. После подключения исполняются shell-команды подготовки окружения и загрузки кода проекта на VPS.
  3. upload_repo_to_vps:
        needs: "create_vps"
        runs-on: ubuntu-latest
        
        steps:
          - name: Download VPS IP artifact and check it
            uses: actions/download-artifact@v3
            with:
              name: VPS IP
          - shell: bash
            run: |
              echo "VPS_IP=$(tail -1 ${{ env.vps_ip_file }})" >> $GITHUB_ENV
          - run: |
              echo ${{ env.VPS_IP }}
          - name: "Upload repo to VPS"
            uses: appleboy/ssh-action@master
            with:
              host: ${{ env.VPS_IP }}
              username: ${{ secrets.SSH_USER }}
              key: ${{ secrets.SSH_KEY }}
              script: |
                mkdir ${{ env.PROJECT_DIR }}
                cd ${{ env.PROJECT_DIR }}
                git clone ${{ env.REPO_LINK }}
                echo "REPO_DOWNLOADED=true" >> /etc/profile
                echo "SERVER_NAME='$(hostname -I | awk '{print $1}')'" >> /etc/profile
                source /etc/profile
                chown www-data:www-data ${{ env.PROJECT_DIR_app }}/db.sqlite3 
                  chown www-data:www-data ${{ env.PROJECT_DIR_app }}/static_collections
                chmod 755 ${{ env.PROJECT_DIR_app }}/db.sqlite3

Последняя джоба — установка Nginx. Эта джоба, как и предыдущая джоба состоит из двух шагов: 1) Download VPS IP artifact and check it — чтение артефакта с IP-адресом VPS (мы выполняем этот шаг повторно, так как каждая джоба запускается на отдельной виртуальной машине GitHub); 2) Install Nginx — установка и настройка Nginx.

nginx_install:
needs: "upload_repo_to_vps"
runs-on: ubuntu-latest

    steps:
      - name: Download VPS IP artifact and check it
        uses: actions/download-artifact@v3
        with:
          name: VPS IP
      - shell: bash
        run: |
          echo "VPS_IP=$(tail -1 ${{ env.vps_ip_file }})" >> $GITHUB_ENV
      - run: |
          echo ${{ env.VPS_IP }}
      
      - name: "Install Nginx"
        uses: appleboy/ssh-action@master
        with:
          host: ${{ env.VPS_IP }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            sudo apt update && sudo apt install -y nginx
            ln -s /var/www/pyoops.com/configs/pyoops.conf /etc/nginx/sites-enabled/pyoops

Вот и всё. В четыре несложные джобы мы смогли уложить весь workflow, который автоматизирует процессы создания VPS, доставки на него кода проекта и настройки рабочего окружения. Ознакомиться с полным кодом workflow и проекта можно по ссылке.

Теперь мы расскажем, о некоторых нюансах реализации подобных workflow при работе с 1cloud API.

Работа с 1cloud API через GitHub Actions

Для работы с 1cloud API нужно получить API-ключ. Генератор API-ключей находится в разделе Настройки -> Настройки проекта -> API-ключ:

Для подключения к виртуальному серверу по SSH с помощью SSH-ключа нужно создать пару ключей с помощью команды ssh-keygen -t rsa для Linux или с помощью команды ssh-keygen для Windows (CMD). Публичную часть ключа (расширение .pub) необходимо загрузить в Панель 1cloud -> раздел Настройки -> Личные настройки -> SSH-ключи:

Для добавления нового SSH-ключа нужно нажать на кнопку «Добавить SSH-ключ» и в появившиеся поля ввести: название ключа и скопированное из pub-файла тело ключа. Далее приватную часть SSH-ключа и API-ключ 1cloud нужно сохранить как секреты в GitHub Actions. Оба ключа бессрочные и их можно использовать любое количество раз.

Для создания виртуальной машины через API 1cloud нужно передать в JSON-формате параметры создаваемой машины и ЦОД размещения. Удобнее всего вынести эти параметры в JSON-конфигурацию следующего вида:

{
"Name": "Test_VPS_to_dep", #Название VM, которое будет отражаться в Панели
"DCLocation": "DsMsk", #Техническое название ЦОДа, где будет создана VM
"CPU": 2, #Кол-во ядер процессора
"RAM": 4096, #Объем оперативной памяти в MB.
"HDD": 20, #Размер диска в GB
"ImageID": 2169, #ID образа OS. 2169 — Ubuntu 18.
"HDDType":"SSD", #Тип диска: SSD или HDD.
"isHighPerfomance":false, #Тип пула ресурсов ЦОДа: стандартный/высокий.
"isBackupActive":false, #Подключен ли бекап: true/false.
"SshKeys":[3145] #ID SSH-ключа.
}

Технические названия ЦОДов, ID образов OS и ID SSH-ключей можно получить через API запросы. Как это сделать описано в нашей базе знаний.

Для реализации API запросов к 1cloud удобно и просто использовать Python и встроенную библиотеку requests. Вот пример реализации API-запроса на создание VPS с мониторингом готовности создания виртуального сервера и сохранением его IP-адреса в переменную окружения:

import requests, json, time, os, subprocess, sys

api_key = str(sys.argv[1])
url = "https://api.1cloud.ru/server/"
server_id = str()

headers = {"Content-Type":"application/json", "Authorization": "Bearer " + api_key}
server_config = json.load(open("./configs/vps_config.json"))


def create_server():
    create_server = requests.post(url, headers = headers, json = server_config)
    server_id = create_server.json()["ID"]
    print("Server", server_id, "start to create.")
    time.sleep(10)
    check_server_status = requests.get(url + str(server_id), headers = headers)
    check_server_status.json()

    while check_server_status.json()['State'] == "New":
        time.sleep(45)
        check_server_status = requests.get(url + str(server_id), headers = headers)
        check_server_status.json()
        print("Server", server_id, "creating...")
    server_ip = str(check_server_status.json()["PrimaryNetworkIp"])
    
    os.system(f'echo VPS={server_ip} >> $GITHUB_ENV')
    result = "Server" + str(server_id) + "is created successfully. " + "IP:" + str(server_ip)
    return result

if __name__ == "__main__":
    print(create_server())

Пожалуй из нюансов всё. Мы дали общую схему работы workflow по созданию VPS и загрузки на виртуальный сервер кода проекта, а также рассмотрели некоторые особенности работы с 1cloud API. Настало время подвести итог.

Что нужно знать о GitHub Actions для написания workflow?

Сделаем краткую выжимку по всем 6 статьям цикла, чтобы всё самое важно для работы с GitHub Actions у вас было под рукой в одном месте:

  1. Workflow — это поток выполнения задач (джобов) описанный в формате yaml и хранящийся в специальной поддиректории workflows, директории .github. Вначале файла задается название workflow, которое будет отражаться в Панели управления GitHub Actions и триггер срабатывания workflow: name: Create new VPS and deploy repo
    on: workflow_dispatch
  2. Jobs (джобы) — это задачи, логическое объединение шагов, которые выполняются на отдельных виртуальных машинах GitHub. По умолчанию каждая задача запускается независимо от других и не имеет с ними физической связи. Поэтому вначале каждого блока джобы, указываются два параметра:
    • needs: "create_vps" — название задачи после завершения которой будет выполнена текущая задача;
    • runs-on: ubuntu-latest — образ ОС виртуальной машины, на которой выполняется джоба.
    Если параметр «needs» не указан джоба будет выполняться сразу после старта workflow. В базовом виде блок jobs может выглядеть так: name: Create new VPS and deploy repo
    on: workflow_dispatch

    jobs:
      create_vps: #Название джобы
        runs-on: ubuntu-latest #Шаблон OS
        steps: #Начало секции шагов
          …
  3. Steps (шаги) — это конкретные инструкции к выполнению действий. Шаг имеет название и опции выполнения действий: контекст выполнения, скрипт действия и т.д. Вот несколько примеров шагов реализующих разную логику выполнения действий:
  4. Actions (экшены) — это готовые части кода (мини-программы), написанные пользователем для выполнения стандартных операций. Например, для получения доступа к репозиторию из виртуальной машины GitHub (actions/checkout@v3) или для подключения по SSH к VPS (appleboy/ssh-action@master). Экшены содержатся в магазине готовых решений GitHub.
  5. Переменные — это хранилища данных, в которые можно что-то класть или что-то доставать по ходу выполнения workflow. Есть 5 уровней переменных:
    • Переменные среды на уровне операционной системы, на которой выполняется рабочий процесс. Эти переменные доступны только в рамках одной джобы;
    • Переменные окружения, которые определены в workflow, в секции env. Эти переменные доступны из любой точки workflow;
    • Переменные репозитория. Это переменные, которые определяются не в workflow, а в панели управления репозиторием, например, секреты;
    • Переменные контекста GitHub Actions. Это переменные, которые автоматически определяются GitHub Actions для каждого запущенного рабочего процесса;
    • Переменные уровня организации. Эти переменные доступны для всех workflow репозиториев в рамках какой-то организации.
  6. Артефакты — это файлы, которые генерируются или используются в рабочем процессе и могут быть сохранены и переданы между различными шагами или джобами. В основном артефакты используются для передачи данных между джобами, так как каждая джоба выполняется на своей виртуальной машине. Для работы с артефактами есть готовые экшены:

Итак, теперь вы знаете как работать с базовыми сущностями GitHub Actions, чтобы написать рабочий workflow. Для получения более полной информации рекомендуем ознакомиться с предыдущими публикациями. В них вы найдете много практического материала и некоторые тонкости работы с сущностями GitHub Actions, которые сэкономят много времени при разработке собственных workflow.