Всех приветствую. В последнее время все чаще задумывался о том какую бы еще полезную статью написать. Параллельно этому постоянно видел в интернетах рекламу о "крутых" курсах в 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" его запустит заново, так что какая-то стабильность все-таки присутствует. В любом случае, если вам понравилась статья, ссылки на меня найдете в профиле, всем спасибо за внимание.