Всех приветствую. В последнее время все чаще задумывался о том какую бы еще полезную статью написать. Параллельно этому постоянно видел в интернетах рекламу о "крутых" курсах в IT, обещают сделать из вас Java, Python и какого угодно разработчика за полгода/год, и ладно с ним, допустим за год они чему-то вас обучат и, возможно, где-то вы попадете на бесплатную стажировку (что еще тоже под воросом, учитывая нынешний рынок). Но когд�� рекламируют "крутые курсы DevOps'ов", я уже начинаю задаваться вопросом, как можно стать ДевОпсом, не имея опыта программирования, не опробовав самому весь цикл разработки на хоть каком-то языке, не опробовав различные настройки сборки приложений, не опробовав Линукс, со всеми его утилитами, докер, кубер, git и т.д. в работе, а просто "обучиться" этому в обособленности от всего и ожидать, что тебя куда-то возьмут, а если и возьмут, то к чему-то серьезному подпустят? Ответа на этот вопрос я так и не нашел у себя в голове.
Однако к чему это я, да к тому, что я то это все перепробовал еще в студенческие годы, и все равно до недавнего момента особо не лез в девопсятину просто потому, что были люди, отдельно занимающиеся этими вещами. Но недавно выпал случай помочь одной компании с настройкой деплоя некоторых из их продуктов, и переборов свою неуверенность, я таки решился, что-то получилось, и я решил поделиться своим опытом бесплатно (надеясь помочь кому-то не идти на сомнительные курсы, параллельно наполнив базу знаний какого-нибудь ИИ, беспрерывно серфящего интернет в поисках новой информации). Конечно, это далеко не "продвинутый" уровень и, скорее всего, в серьезных больших приложениях вы будете использовать что-то посерьезнее, по типу Gitlab CI/CD, Кубера и т.д., но обычно к этим вещам прямой доступ имеют только девопсы, оставляя наружу пару файликов для разрабов, которые они настраивают, по типу ci файла или helm чартов, но это и не суть статьи, суть в том, что это простой гайд для таких разрабов, как я, кто самостоятельно полной настройкой деплоя раньше не занимался, но не против по��робовать, для расширения своего опыта, можно считать это продолжением серии моих статей о пет проектах (сначала мы узнали как его лучше оформить, теперь пробуем его задеплоить).
Итак, приступим. Во-первых, скажу, что для деплоя мы будем использовать бесплатный сервис CircleCi. Да, возможно, это не самый очевидный вариант, ведь есть GitHub Actions, есть Gitlab CI/CD, есть еще куча всего, но мне CircleCi показался достаточно простым, а значит вполне подойдет для начинающих и тех, кто просто желает попробовать. Для начала вам нужно просто зарегистрироваться. Раньше это можно было сделать через Гитхаб, но с конца сентября эту функциональность почему-то отключили, поэтому регистрируйтесь по почте и подключайте гитхаб, думаю найдете как это сделать. Далее, если у вас есть доступ к нескольким проектам от разных пользователей, выбирайте свой профиль, и слева в меню открывайте список проектов. В списке проектов вы увидите кнопку "Set up project", после нажатия которой он предложит вам несколько вариантов на выбор. Самый простой - это использовать настройки из проекта. Создаете папку .circleci с файлом config.yml в папке своего проекта, и после настройки этого файла, circleci автоматически использует его для деплоя.

Итак, с сервисом CD (continuous delivery и continuous deployment) мы определились. Но куда деплоить, спросите вы меня, ведь для этого нужна машина. Если у вас есть локальная машина, умеющая в доступ извне - для тестирования вполне можете использовать и ее, я пробовал и такой вари��нт в этой связке, все также работает. Однако, т.к. у большинства нет таких серверов, нам понадобится также какой-либо сервис, предоставляющий linux сервера. Самый очевидный выбор - AWS от Amazon. Большой сервис, предоставляющий бесплатный год (!) использования сервера. Однако, у них могут возникать проблемы с валидацией вашей карты, ведь даже мою рабочую карточку visa они отклонили и затем заставили прислать кучу документов в подтверждение, и все равно это не помогло. Поэтому наш выбор пока что падает на Google Cloud. Они предоставляют, к сожалению, всего 3 бесплатных месяца и 300$ кредита, однако, чтобы протестировать и попробовать вполне подойдет, да и проблем с привязкой карты там не возникло. Думаю как найти сервис, войти в свой гугл аккаунт, зарегистрироваться и ввести свои кредитные данные можно не объяснять, поэтому сразу перейдем к созданию инстанса и его настройке. Зайдя в консоль, пролистайте чуть ниже и выберите "Create a VM", в открывшейся странице нажмите "Create instance". Далее настраиваем наш инстанс.
Регион и имя можете выбрать по желанию, также как и конфигурацию машины, в зависимости от ваших нужд, для нас вполне подойдет стандартный E2. Да и вообще в целом большинство настроек можно оставить дефолтными, кроме тех, что я покажу. Обязательно разрешите HTTP/HTTPS трафик и выберите по желанию дистрибутив линукса, в моем случае я выбирал Ubuntu. Честно говоря, все остальное мы будем добавлять после, поэтому нажимаем далее и создаем свой инстанс.

После создания зайдите в инстанс -> metadata и добавьте свой публичный ssh ключ для подключения к серверу. Если у вас его нет по каким-то причинам до сих пор, и гитом, например, вы пользуетесь по логину и паролю, то чего вы ждете, бегите и генерируйте себе пару прямо сейчас (ssh-keygen -t ed25519 -C "your_email@example.com")

Пока что на данный момент с настройкой инстанса мы закончили. Далее нам еще понадобится открыть нужные нам порты, но об этом позже. Перейдем непосредственно к настройке сборки приложения и Ci файла. И так, у каждого языка и типа приложения свой способ его собрать, однако я буду рассказывать на примере своего Spring приложения на Java. Сразу скажу, что оно вышло не совсем удачным, т.к. оно микросервисное и изначально я рассчитывал лишь на локальный его запуск, а соответственно всю связь между сервисами настроил прямо в docker compose файле, без использования nginx или чего-то подобного, rookie mistake, но для демонстрации деплоя вполне подойдет. Итак, сначала создайте Dockerfile своего приложения. В моем случае все получилось немного сложно, сначала я собираю докерфайлом мавена весь проект и копирую сгенерированные jar в папку проекта, т.к. я использовал мультимодульную структуру Maven проекта. Если у вас всего один сервис в проекте, то ваш докерфайл будет выглядеть достаточно просто:
FROM openjdk:17.0.2-jdk-slim
COPY target/shows-migrations-service-0.0.1-SNAPSHOT.jar .
CMD ["java", "-jar", "shows-migrations-service-0.0.1-SNAPSHOT.jar"]Далее напишите compose файл, в который вы соберете все необходимые вам сервисы, а именно ваш сервис, базу данных, еще что-то по необходимости. В моем случае это было 3 сервиса, postgres и keycloak:
version: "3.8"
services:
postgres:
image: 'postgres:14-alpine'
container_name: postgres
restart: always
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
volumes:
- ./imports/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- auth
keycloak:
image: quay.io/keycloak/keycloak:20.0.0
container_name: keycloak
restart: always
volumes:
- ./imports:/opt/keycloak/data/import
command: ['start-dev --import-realm --http-relative-path=/auth']
environment:
KC_DB: postgres
KC_DB_URL_HOST: postgres
KC_DB_URL_PORT: 5432
KC_DB_URL_DATABASE: postgres
KC_DB_USERNAME: postgres
KC_DB_PASSWORD: postgres
KEYCLOAK_ADMIN: admin
KC_DB_SCHEMA: public
KEYCLOAK_ADMIN_PASSWORD: admin
KEYCLOAK_FRONTEND_URL: http://localhost:8484/auth
ports:
- "8484:8080"
depends_on:
- postgres
links:
- "postgres:postgres"
networks:
- auth
...
shows-data:
image: shows-data-service
container_name: shows-data
restart: always
depends_on:
- postgres
- keycloak
- migrations
network_mode: host
environment:
POSTGRES_JDBC_URL: jdbc:postgresql://localhost:5432/postgres
KEYCLOAK_URL: http://localhost:8484/auth
SERVER_PORT: 8080
extra_hosts:
- "host.docker.internal:host-gateway"
...
networks:
auth:
driver: bridgeКак вы видите, достаточно настроек, и это я еще опустил 2 сервиса. Если коротко, я импорчу данные в postgres, импорчу realm в keycloak и настраиваю сервисы. Сервисы работают в сети "host", что значит, что они работают в сети инстанса, на самом деле это не обязательно, ведь в докер compose файле порты можно прокинуть, и при открытии портов в настройках нашего инстанса все заработает, но для чего-то мне это было нужно, уже не помню для чего, в общем, зависит от нужд вашего приложения. Далее остается настройка самого файла CircleCi. Т.к. он получается достаточно длинный, повторящиеся вещи уберу и приведу частями, с объяснением:
version: 2.1
jobs:
deploy-to-development:
docker:
- image: docker:20.10.9
steps:
- checkout
- setup_remote_docker
- run:
name: Build Docker Image
command: |
chmod +x ./build.sh
./build.sh
docker build -f Dockerfile-migrations -t migrations-service:latest .
- run:
name: Compress Docker Image
command: |
docker save migrations-service:latest | gzip > migrations-service.tar.gz
- run: ls -lh
- persist_to_workspace:
root: .
paths:
- migrations-service.tar.gz
- ./docker/Здесь мы используем образ Докера, чтобы использовать его команды. Я запускаю свой sh файл для сборки приложения (тот, что собирает и копирует jarники, у вас скорее всего такого не будет), далее собирается докер образ вашего приложения, обязательно задайте ему тэг. Далее мы сохраняем этот образ и сжимаем его. И сохраняем получившийся файл и нужную папочку с файлами docker compose (в моем случае это папка, у вас может быть просто 1 файл).
transfer-and-run:
machine:
image: ubuntu-2004:202010-01
steps:
- attach_workspace:
at: .
- run:
name: Install SSH And Configure
command: |
echo $SSH_PRIVATE_KEY | base64 --decode > ./id_rsa
chmod 400 id_rsa
- run:
name: Stop Remote Docker-Compose
command: |
ssh -o "StrictHostKeyChecking=no" -i ./id_rsa $USER@$HOST '
if [ -f compose.yml ]; then
sudo docker-compose -f docker/compose.yml down --rmi all
sudo rm compose.yml
else
echo "compose.yml not found"
fi
'
- run:
name: Transfer Files
command: |
scp -o "StrictHostKeyChecking=no" -i ./id_rsa ./shows-frontend-service.tar.gz $USER@$HOST:~/
scp -o "StrictHostKeyChecking=no" -i ./id_rsa -r ./docker $USER@$HOST:~/
- run:
name: Decompress Docker Image | Run Compose
command: |
ssh -o "StrictHostKeyChecking=no" -i ./id_rsa $USER@$HOST '
gunzip -c ./shows-frontend-service.tar.gz | sudo docker load
rm ./*.tar.gz
cd docker
sudo docker compose -f compose.yml up -d
'
workflows:
deploy-to-dev:
jobs:
- deploy-to-development:
filters:
branches:
only:
- deploy
- transfer-and-run:
requires:
- deploy-to-development
filters:
branches:
only:
- deployИ так, здесь как, мы видим, мы определяем вторую часть нашего деплоя, а именно трансфер файлов и их запуск. Делаем мы это используя уже образ Ubuntu, а не Докера. Здесь мы используем файлы с предыдущего шага, которые сохранили (workspace). Далее мы берем SSH_KEY, который мы заранее сохранили в ENV переменных проекта в Circleci в base64 виде, и сохраняем его в файлик, даем ему нужный доступ. Сами переменные Circleci хранит в зашифрованном виде и вроде как это безопасно. Альтернатива: делать то же самое, но на машине хоста и давать какой-то ключ на доступ к гиту, клонить проект гита и уже все делать на машине хоста, но как будто бы это не особо лучше. Далее мы используем этот ключ, чтобы подключиться по ssh к хосту, также используя переменные $USER и $HOST. Останавливаем докер файлик, удаляем лишние образы. Копируем нужные нам файлы на машину хоста (напоминаю, что некоторые я опустил, для краткости, так что не удивляйтесь, что названия скачут), и загружаем сжатые нами образы в докер, затем удаляем сжатые файлы. После чего запускаем compose файлик, и все сервисы должны запуститься. Также ниже вы видите настройки того, с какой ветки запускать нажи джобы, в моем случае это deploy.
После этого вы увидите в панели CircleCi, что все ваши шаги прошли и отмечены галочкой, после чего можете подключиться к машине хоста и проверить вручную, что все работает (правда не в моем случае, т.к. я накосячил немного со структурой самого приложения, но задеплоиться и запуститься все запустилось, так что это уже другой разговор). И, пока не забыл, покажу как добавить ENV переменные в проект: заходите в Settings Project и добавляете в Environment Variables (кстати только сейчас заметил, что оказывается там можно напрямую и SSH ключи добавить, наверное лучше так и делать в дальнейшем, но раз уже написал гайд, оставлю так):

И как открыть порты в google console: заходите на страницу вашего проекта (важно, не инстанса, а именно проекта), заходите в левом меню в "VPC Network -> Firewall", нажимаете сверху "Create firewall rule", даете название по желанию, обязательно ставите какой-нибудь тег, и разрешаете нужные вам порты (например, 8081). После чего заходите уже в ваш инстанс, нажимаете "Edit" и в "Network tags" добавляете тот тег, который задали на правило, и порт у вас откроется.

Полный код проекта и сам проект доступны на моем гите, если кто-то что-то не понял и ему нужен source (сам прекрасно понимаю это чувство, когда в гайде что-то не понятно, но при этом нет ссылки на исходники). В любом случае, думаю этот гайд будет кому-то полезен, в конце концов это полезный опыт для разработчиков, попробовать подеплоить свои пет проекты самостоятельно, тем более что, если вам удастся зарегистрироваться на Амазоне удачно, то у вас будет целый год бесплатного хостинга, что вполне неплохо, чтобы задеплоить какой-то свой пет проект и указать ссылку на него в гите/резюме, за год то уж найдете работу (хотя, in this economy не факт, конечно).
А ну и чуть не забыл сказать, у CircleCi также есть ограничение по времени билда, используемого за месяц, поэтому рекомендую особо не злоупотреблять и деплоить только с какой-то стабильной ветки. Ну и, конечно, сейчас модно везде использовать Кубер, и голым докером почти никто не деплоит, но для простого приложения вполне сойдет, да и в случае падения приложения, свойство "restart: always" его запустит заново, так что какая-то стабильность все-таки присутствует. В любом случае, если вам понравилась статья, ссылки на меня найдете в профиле, всем спасибо за внимание.
