Источник: Nuances of Programming
Курс SkillFactory Системный администратор. Станьте универсальным специалистом по администрированию Linux с нуля и разверните собственный кластер.
Введение
Автоматизируя процесс развертывания изменений кода в экземпляре Amazon Web Services (AWS) Elastic Compute Cloud (EC2) с помощью Github Actions и Docker Hub, разработчик в итоге экономит время и силы.
В этом руководстве мы пошагово настроим экземпляр AWS EC2, создадим и добавим в DockerHub образ Docker и настроим рабочий поток Github Actions для автоматизации процесса развертывания. В итоге научимся оптимизировать рабочий процесс развертывания, уменьшая риск человеческой ошибки.
Необходимые условия
1. Код для сборки
Он нужен для создания образа Docker — легковесного, автономного, исполняемого пакета, в котором содержится все необходимое для запуска приложения, в том числе код, библиотеки и зависимости.
2. Экземпляр AWS EC2 с файлом .pem и установленным Docker
В AWS EC2 развертывается приложение. Используем экземпляр Ubuntu AWS EC2, хотя это не обязательно должен быть Ubuntu. Настраивается AWS EC2 согласно официальной документации AWS. Файл .pem связан с экземпляром для SSH-доступа. Docker устанавливается в экземпляре AWS EC2 согласно официальной документации Docker.
3. Учетная запись Docker Hub с токеном доступа
Docker Hub — это облачная служба реестра для хранения и обмена образами Docker. Учетную запись для их размещения в ней создаем здесь. Затем, следуя инструкциям официальной документации Docker, генерируем токен доступа для аутентификации в рабочем потоке Github Actions через Docker Hub.
Приступим к автоматизации развертывания AWS EC2.
Создание Dockerfile
Чтобы сделать Docker-образ приложения, создадим dockerfile, в котором описывается сборка образа.
- В корневом каталоге проекта с помощью текстового редактора создаем файл dockerfile.
- В dockerfile начинаем с указания базового образа, например для приложения на Go это официальный Docker-образ Golang:
##указываем базовый образ
FROM golang:1.19-alpine
Этой строкой за основу Docker-образа берем образ golang:1.19-alpine.
3. Затем в Docker-образе указываем рабочий каталог для приложения:
##создаем папку APP
RUN mkdir /app
##задаем главный каталог
WORKDIR /app
Этими строками создаем в Docker-образе каталог app и делаем его рабочим.
4. Копируем остальную часть кода приложения в Docker-образ:
##копируем весь файл в приложение
ADD . /app
Этой строкой в каталог /app Docker-образа копируется остальная часть кода приложения.
5. Создаем исполняемый файл для приложения:
##создаем исполняемый файл
RUN go build -o main .
Чтобы создать для приложения исполняемый файл main, этой строкой в Docker-образе запускается команда go build.
6. Указываем команду, выполняемую при запуске контейнера Docker:
##запускаем исполняемый файл
CMD ["/app/main"]
Этой строкой при запуске контейнера Docker запускается исполняемый файл /app/main.
Вот готовый Dockerfile:
##в качестве базового используем официальный Docker-образ Golang
FROM golang:1.19-alpine
##создаем папку APP
RUN mkdir /app
##задаем главный каталог
WORKDIR /app
##копируем весь файл в приложение
ADD . /app
##создаем исполняемый файл
RUN go build -o main .
##запускаем исполняемый файл
CMD ["/app/main"]
Создадим Docker-образ и отправим его в Docker Hub.
Настройка рабочего процесса GitHub
Настраивая рабочий процесс GitHub, мы автоматизируем тестирование, сборку и развертывание кода и обеспечиваем последовательное и надежное выполнение этих задач.
Чтобы настроить рабочий процесс GitHub, создадим в каталоге репозитория .github/workflows файл YAML, например main.yml. В этом файле определяются запускаемые задания и выполняемые экшены.
Каждое задание состоит из шагов, на каждом шаге выполняется конкретный экшен. Экшены — это многократно используемые блоки кода для выполнения конкретных задач, например проверки кода, создания Docker-образа или развертывания кода у облачного провайдера.
Вот пример простого YAML-файла для сборки и отправки Docker-образа в Docker Hub, когда в ветке main появляется новый push:
# Запускается рабочий процесс, когда в ветке main появляется push
on:
push:
branches:
- main
jobs:
build:
# На компьютере с Ubuntu запускается задание
name: Build
runs-on: ubuntu-latest
steps:
# Извлекаем код из репозитория
- name: Checkout
uses: actions/checkout@v3
# Входим в Docker Hub, используя secrets
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
# Настраиваем Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Создаем Docker-образ и отправляем его в Docker Hub
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKER_USER }}/${{ secrets.IMAGE }}:latest
# Запускаем удаленные команды в экземпляре EC2 через SSH
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_KEY }}
port: ${{ secrets.EC2_PORT }}
# Удаленные команды, выполняемые в экземпляре EC2
script: |
touch deploy.txt
echo "Great, it works! at $(TZ='Asia/Jakarta' date "+%A %d-%b-%Y %H:%M:%S %Z")" >> deploy.txt
sudo chmod 777 /var/run/docker.sock
docker pull ${{ secrets.DOCKER_USER }}/${{ secrets.IMAGE }}:latest
docker stop ${{ secrets.CONTAINER }}
docker rm ${{ secrets.CONTAINER }}
docker rmi ${{ secrets.DOCKER_USER }}/${{ secrets.IMAGE }}
docker run --name ${{ secrets.CONTAINER }} -d -p 80:8000 ${{ secrets.DOCKER_USER }}/${{ secrets.IMAGE }}
Разбор рабочего процесса GitHub
on:
push:
branches:
- main
Здесь определяется событие, которым запускается рабочий процесс. В данном случае это появление нового push в ветке main.
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
Здесь определяется задание build, запускаемое в рабочем процессе на последней версии Ubuntu.
- name: Checkout
uses: actions/checkout@v3
На этом шаге код извлекается из репозитория в runner.
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
На этом шаге входим в Docker Hub, вводя имя пользователя и токен, сохраненный в настройках репозитория как secrets.
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
На этом шаге настраивается CLI-плагин Docker Buildx, которым расширяются возможности интерфейса командной строки Docker.
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKER_USER }}/${{ secrets.IMAGE }}:latest
На этом шаге с помощью Dockerfile в текущем каталоге создается Docker-образ и по указанным тегам отправляется в Docker Hub.
# Запускаем удаленные команды в экземпляре EC2 через SSH
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_KEY }}
port: ${{ secrets.EC2_PORT }}
# Удаленные команды, выполняемые в экземпляре EC2
script: |
touch deploy.txt
echo "Great, it works! at $(TZ='Asia/Jakarta' date "+%A %d-%b-%Y %H:%M:%S %Z")" >> deploy.txt
sudo chmod 777 /var/run/docker.sock
docker pull ${{ secrets.DOCKER_USER }}/${{ secrets.IMAGE }}:latest
docker stop ${{ secrets.CONTAINER }}
docker rm ${{ secrets.CONTAINER }}
docker rmi ${{ secrets.DOCKER_USER }}/${{ secrets.IMAGE }}
docker run --name ${{ secrets.CONTAINER }} -d -p 80:8000 ${{ secrets.DOCKER_USER }}/${{ secrets.IMAGE }}
- В этом блоке кода используется экшен appleboy/ssh-action для выполнения удаленной команды SSH на сервере, указанном в файле secrets. Выполняемый сценарий определяется параметром script.
- Затем, чтобы изменить разрешения сокет-файла Docker для доступа к нему текущего пользователя, выполняется команда sudo chmod 777 /var/run/docker.sock.
- Для получения последней версии Docker-образа, указанной в файле secrets, применяется команда docker pull.
- Команды docker stop, docker rm и docker rmi используются для остановки и удаления любого имеющегося контейнера и образа Docker с тем же именем, что указан в файле secrets.
- Команда docker run используется для запуска нового контейнера Docker с именем, указанным в файле secrets, и с помощью образа из secrets.
- Флагом -d в фоновом режиме запускается контейнер как процесс демона.
- Флагом -p 80:8000 порт 80 хоста сопоставляется с портом 8000 контейнера: получаем доступ к контейнеру по HTTP через порт 80.
- Через флаг -e задаются переменные окружения для контейнера, например DB Host, DB Password, JWT Key и т. д.
В итоге в этом блоке кода получается последняя версия Docker-образа, останавливаются и удаляются любые имеющиеся контейнеры с одинаковым именем, запускается новый контейнер, а порт контейнера 8000 сопоставляется с портом хоста 80.
Настройка секретных переменных
Чтобы развернуть приложение в AWS EC2, настроим несколько секретных переменных в репозитории GitHub. Эти secrets надежно зашифрованы и хранятся в GitHub, доступ к ним имеют только авторизованные пользователи.
- Переходим в репозиторий GitHub.
- Нажимаем на вкладку Settings («Настройки»).
- Выбираем слева Secrets.
- Попадаем на страницу Actions secrets and variables («Секреты и переменные экшенов») репозитория. Здесь создаются, редактируются и удаляются секреты для рабочих процессов GitHub Actions.
Вот список необходимых секретных переменных с кратким описанием.
- DOCKER_USER: имя пользователя Docker Hub.
- DOCKER_TOKEN: токен доступа с разрешением отправлять образы в репозиторий Docker Hub.
- IMAGE: имя Docker-образа.
- CONTAINER: имя контейнера Docker для запуска приложения.
- EC2_HOST: IP-адрес или доменное имя экземпляра EC2, например ec2–xx–xxx–xxx–xx.ap-southeast-1.compute.amazonaws.com.
- EC2_USERNAME: имя пользователя для входа в экземпляр EC2, обычно это ubuntu.
- EC2_KEY: закрытый ключ для входа в экземпляр EC2.
- EC2_PORT: SSH-порт для подключения к экземпляру EC2, по умолчанию 22.
Обязательно настройте эти секретные переменные в репозитории GitHub перед запуском рабочего процесса.
Выполнение рабочего процесса
После настройки фиксируем изменения кода в ветке main репозитория: рабочий процесс запустится автоматически.
Чтобы проверить его статус, переходим на вкладку Actions репозитория GitHub. Здесь имеется список всех выполненных рабочих процессов и их статус. Выбрав рабочий процесс, просматриваем информацию о нем, включая любые ошибки и предупреждения:
Если рабочий процесс выполнится, появится сообщение о том, что развертывание завершено. Можно также проверить работоспособность сайта.
Если рабочий процесс не запускается, возможна проблема с конфигурацией или кодом. Чтобы выявить проблему, просмотрите сообщения об ошибках в логах рабочего процесса и внесите в него или код любые необходимые изменения. Проверьте также, не совпадают ли секретные переменные.
В целом, в выполнении рабочего процесса GitHub нет ничего сложного: с ним разработка и развертывание значительно оптимизируются. При правильной настройке и конфигурации автоматизируются многие времязатратные задачи, связанные с разработкой ПО, и вы можете сосредоточиться на создании отличных продуктов и сервисов.
Заключение
В этом руководстве мы пошагово настроили базовый рабочий процесс GitHub для развертывания контейнеризированного веб-приложения. С мощью GitHub Actions процесс развертывания приложения автоматизируется, а время высвобождается на другие важные задачи. Надеемся, статья была полезной для понимания основ GitHub Actions, Docker, Docker Hub и того, как применять их вместе для упрощения рабочего процесса.
Продолжая работать над проектами, вы сможете использовать освоенные концепции для создания более сложных и специализированных рабочих процессов, настроить их под себя. Возможности безграничны — проявите творческий подход, и вы сможете создать мощные рабочие процессы для достижения ваших целей быстрее и меньшими усилиями.
Читайте также:
Перевод статьи Muhammad Habibullah: Easy and Fast : Automating AWS EC2 Deployment with GitHub Actions and Docker Hub