Принимайте участие в зарплатном опросе! Уже собрано почти 8 000 анкет.
×Закрыть

Туториал по развертыванию Rails-приложений на Amazon с помощью Docker. Часть 3

Всем привет! Перед вами последняя часть туториала, в котором мы учимся самостоятельно разворачивать Rails-приложение с помощью Docker и инструментов AWS.

В предыдущих частях туториала мы уже успели проделать много работы.

В первой части мы рассмотрели теорию Docker: принцип работы, его компоненты, а также преимущества и недостатки работы Docker в сравнении с виртуальными машинами. Кроме того, мы прошли пошаговую сборку Rails-приложения в Dockerfile и запустили наше Spree-приложение и все зависимые сервисы на локальной машине.

Во второй части мы:

  • Реализовали возможность хранения sensitive data приложения.
  • Создали Docker образ для веб-сервера Nginx.
  • Подготовили конфигурацию для развертывания staging инфраструктуры на AWS.
  • Запустили staging приложение на AWS.

В этой части мы переходим к этапу развертыванию production-окружения. Поехали!

Какую проблему решаем

Итак, как уже было сказано ранее, в предыдущей части мы развернули наше staging-приложение, протестировали его и убедились, что все работает. Теперь необходимо развернуть готовое к масштабированию production-приложение.

На инфографике мы видим, что мы находимся на последнем этапе: Production. Также мы видим этапы нашей работы и стэк технологий, который будем использовать.

В этой части мы рассматриваем последний этап — Production. Также мы видим ПО, которое будем использовать на данном этапе

Решение

Стек сервисов production-приложения остается таким же, как и на предыдущем этапе Staging. Инфраструктура имеет следующие отличия:

  • Cloud database. В качестве базы данных для нашего приложения мы будем использовать Amazon RDS, который упрощает развертывание и настройку реляционных баз данных в облаке.
  • Cloud in-memory storage. В качестве in-memory store мы будем использовать Amazon ElastiCache, который облегчает развертывание и управление средами Redis и Memcached.
  • Масштабирование и балансировщик нагрузки. В случае увеличения количества запросов, мощностей одного сервера может быть недостаточно. Нужен сервис, который будет распределять запросы между несколькими серверами в зависимости от их нагрузки. Мы будем использовать Amazon Elasticloadbalancing.
  • Декомпозиция. Если на предыдущих этапах все зависимые сервисы приложения размещались на одном инстансе, то в условиях горизонтальной масштабируемости приложения нужно увеличивать только те количества сервисов, на которые приходится наибольшая нагрузка. В контексте нашей инфраструктуры, необходимо вынести в отдельные инстансы Server App и Worker App, так как не всегда, когда нужно масштабировать Server App, нужно масштабировать и Worker App, и наоборот.

Таким образом, архитектура production-приложения преображается в следующую структуру:

Постановка задачи

Для корректной и безопасной работы production-окружения нам понадобится:

  • Сохранить все переменные production окружения в зашифрованном виде.
  • Создать Postgresql инстанс с помощью RDS.
  • Создать Redis инстанс с помощью ElasticCache.
  • Создать кластер для production приложения на AWS ECS.
  • Создать compose файл для запуска приложения и его зависимых сервисов на AWS ECS.
  • Запустить сервис с production версией приложения на AWS.

Поскольку количество окружений возросло, необходимо автоматизировать процесс деплоя, а именно, интегрировать CircleCI для автоматизации деплоя для staging-окружения.

Хранение данных приложения

Начнем с добавления AWS credentials в файл с глобальными переменными приложения, используя команду EDITOR=nano rails credentials:edit.

production:
  # ...
  AWS_ACCESS_KEY_ID: 'YOUR_AWS_ACCESS_KEY_ID'
  AWS_SECRET_ACCESS_KEY: 'YOUR_AWS_SECRET_ACCESS_KEY'
  DEVISE_SECRET_KEY: 'YOUR_DEVISE_SECRET_KEY'
  SECRET_KEY_BASE: 'YOUR_SECRET_KEY_BASE'

Конфигурация сервисов AWS

Конфигурация доступа

Коммуникация клиента с инфраструктурой нашего приложения будет происходить через elasticloadbalancing. В целях безопасности, доступ ко всем остальным сервисам будет предоставлен только в рамках внутренней сети AWS и определенных security groups.

Исходя из диаграммы production-окружения, указанной в начале, создадим группы доступа:

# GroupId группы load balancer обозначим, как `$BALANCER_SG`
aws ec2 create-security-group \
  --group-name spreeproject-production-balancer \
  --description "Spree project Production Balancer"

# GroupId группы серверного приложения обозначим, как `$SERVER_APP_SG`
aws ec2 create-security-group \
  --group-name spreeproject-production-server-app \
  --description "Spree project Production Server App"

# GroupId группы баз данных приложения обозначим, как `$DB_SG`
aws ec2 create-security-group \
  --group-name spreeproject-production-db \
  --description "Spree project Production DB"

# GroupId группы для memory stores сервиса обозначим, как `$STORE_SG`
aws ec2 create-security-group \
  --group-name spreeproject-production-store \
  --description "Spree project Production Memory Store"

И укажем правила коммуникации сервисов:

# Откроем доступ любому клиенту на 80-й порт балансера.
aws ec2 authorize-security-group-ingress \
  --group-id $BALANCER_SG \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0

# Откроем доступ балансеру на 8080-й порт будущим инстансам, где будет запущен Nginx.
aws ec2 authorize-security-group-ingress \
  --group-id $SERVER_APP_SG \
  --protocol tcp \
  --port 8080 \
  --source-group $BALANCER_SG

# Откроем доступ будущим инстансам с приложением к RDS, где по 5432 порту будет доступен Postgres.
aws ec2 authorize-security-group-ingress \
  --group-id $DB_SG \
  --protocol tcp \
  --port 5432 \
  --source-group $SERVER_APP_SG

# Откроем доступ будущим инстансам с приложением к ElastiCache, где по 6379 порту будет доступен Redis.
aws ec2 authorize-security-group-ingress \
  --group-id $STORE_SG \
  --protocol tcp \
  --port 6379 \
  --source-group $SERVER_APP_SG

Хранение изображений и быстрый доступ к ним

Для хранения изображений наше приложение будет использовать S3-bucket. Создадим его с помощью следующей команды:

aws s3api create-bucket --bucket spreeproject-production

После создания bucket на S3 обновим переменные credentials:

production:
  # ...
  S3_BUCKET_NAME: 'spreeproject-production'
  S3_REGION: 'us-east-1'
  S3_HOST_NAME: 's3.amazonaws.com'

RDS

Amazon предоставляет управляемый сервис RDS, который упрощает настройку, использование и масштабирование реляционной базы данных.

Чтобы начать работать, вам достаточно просто создать RDS-инстанс с выбранной вами базой данных и получить хост-сервиса для подключения вашего приложения к нему.

В качестве базы данных мы будем использовать инстанс RDS с готовой сборкой для Postgres. В $YOUR_DB_USERNAME и $YOUR_DB_PASSWORD укажите собственные значения.

aws rds create-db-instance \
  --engine postgres \
  --no-multi-az \
  --vpc-security-group-ids $DB_SG \
  --db-instance-class db.t2.micro \
  --allocated-storage 20 \
  --db-instance-identifier spreeproject-production \
  --db-name spreeproject_production \
  --master-username $YOUR_DB_USERNAME \
  --master-user-password $YOUR_DB_PASSWORD

Выполним команду для проверки статуса RDS инстанса:

aws rds describe-db-instances

В отличие от EC2-инстансов, RDS будет доступен через некоторое время, и после успешной инициализации будет доступен его endpoint.

Обновим наш файл с encrypted credentials:

production:
  # ...
  DB_NAME: 'spreeproject_production'
  DB_USERNAME: 'YOUR_DB_USERNAME'
  DB_PASSWORD: 'YOUR_DB_PASSWORD'
  DB_HOST: 'YOUR_RDS_ENDPOINT'
  DB_PORT: '5432'

ElastiCache

Amazon предоставляет управляемый сервис ElastiCache, который облегчает развертывание и запуск в облаке серверных узлов, совместимых с протоколами Memcached или Redis. Сервис упрощает и берет на себя управление средами в памяти, их мониторинг и решение связанных операционных вопросов, позволяя клиентам сосредоточить усилия технических специалистов на разработке приложений.

В качестве in-memory store будем использовать сервис ElastiCache с готовой сборкой Redis:

aws elasticache create-cache-cluster \
  --engine redis \
  --security-group-ids $STORE_SG \
  --cache-node-type cache.t2.micro \
  --num-cache-nodes 1 \
  --cache-cluster-id spree-production

Команда для проверки статуса инстанса:

aws elasticache describe-cache-clusters --show-cache-node-info

На инициализацию ElastiCache также потребуется некоторое время. После успешной инициализации ElastiCache сохраним полученный Address сервиса в encrypted credentials:

production:
  # ...
  REDIS_DB: "redis://YOUR_EC_ENDPOINT:6379"

ELB

В случае увеличения количества запросов, мощностей одного сервера может быть недостаточно. Это приводит к увеличению времени обслуживания запросов. Чтобы устранить эту проблему, подключим к нашей инфраструктуре балансировщик нагрузки (load balancer), который будет распределять запросы между несколькими нодами в зависимости от их нагрузки. У AWS есть готовое решение Elasticloadbalancing.

Использование ELB для распределения нагрузки

Для создания инстанса необходимо получить Subnets, VPС и Keypair для будущего инстанса:

aws ec2 describe-subnets

В результате команды выбираем SubnetId, у которых одинаковый VpcId и DefaultForAz параметр имеет значение true. И записываем их в переменную.

# Пример
AWS_SUBNETS=subnet-e49c19b8,subnet-20ae1647,subnet-319d1a1f

Создаем непосредственно сам loadbalancer:

aws elb create-load-balancer \
  --load-balancer-name spreeproject-balancer-production \
  --listeners "Protocol=HTTP,LoadBalancerPort=80,InstanceProtocol=HTTP,InstancePort=8080" \
  --subnets $YOUR_SUBNETS \
  --security-groups $BALANCER_SG

Добавим к нему connection settings:

aws elb modify-load-balancer-attributes \
  --load-balancer-name spreeproject-balancer-production \
  --load-balancer-attributes "{\"ConnectionSettings\":{\"IdleTimeout\":5}}"

Состояние созданного балансировщика можно проверить с помощью команды:

aws elb describe-load-balancers

В дальнейшем, наше Spree-приложение будет доступно через его DNSName.

Настраиваем production окружение

ECS Cluster

Работа с ECS Cluster и создание в нем сервисов идентична главе ECS Cluster из предыдущей части, где мы разворачивали staging-приложение.

Создадим конфигурацию для будущего кластера spreeproject-production, введя следующую команду в консоли:

CLUSTER_NAME=spreeproject-production

ecs-cli configure --region us-east-1 --cluster $CLUSTER_NAME --config-name $CLUSTER_NAME

Необходимо получить список доступных VPC:

aws ec2 describe-vpcs

Дальше VpcId этих subnets записываем в переменную $AWS_VPC. Например:

AWS_VPC=vpc-0e934a76

После, создадим кластер spreeproject-production, к которому будет подвязан один EC2 инстанс типа t2.micro. Для этого вводим в терминале следующую команду:

ecs-cli up \
  --keypair spreeproject_keypair \
  --capability-iam \
  --size 3 \
  --instance-type t2.micro \
  --vpc $AWS_VPC \
  --subnets $AWS_SUBNETS \
  --image-id ami-0a6be20ed8ce1f055 \
  --security-group $SERVER_APP_SG \
  --cluster-config $CLUSTER_NAME \
  --verbose

ECR

Создадим репозитории для хранения образов production-версии приложения.

Предварительно нужно аутентифицироваться с помощью следующей команды:

$(aws ecr get-login --region us-east-1 --no-include-email)

Актуализируем образы наших сервисов, вызвав команду:

docker-compose -p spreeproject -f docker-compose.development.yml build

После, загрузим локальный образ в репозиторий YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com. YOUR_ECR_ID — registryId созданного репозитория:

docker tag spreeproject_server_app:latest $YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/server_app:production
docker push $YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/server_app:production

Сделаем то же самое для web_server, в котором будет образ Nginx:

aws ecr create-repository --repository-name spreeproject/web_server
docker tag spreeproject_web_server:latest $YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/web_server:production
docker push $YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/web_server:production

ECS Tasks

После, создадим docker-compose.production.yml как compose production версии приложения. Замените YOUR_ECR_ID и YOUR_RAILS_MASTER_KEY на собственные значения:

cd ../docker && touch docker-compose.production.yml
version: '3'

services:
  web_server:
    image: YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/web_server:production
    ports:
      - 8080:8080
    links:
      - server_app

  server_app:
    image: YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/server_app:production
    command: bundle exec puma -C config/puma.rb
    entrypoint: "./docker-entrypoint.sh"
    expose:
      - 3000
    environment:
      RAILS_ENV: production
      RAILS_MASTER_KEY: YOUR_RAILS_MASTER_KEY

Создадим файл схему ecs-params

touch ecs-params.production.yml
version: 1
task_definition:
  ecs_network_mode: bridge

  task_size:
    cpu_limit: 768
    mem_limit: 0.5GB

  services:
    web_server:
      essential: true
    server_app:
      essential: true

Запускаем production-окружение

ECS Services

Создаем задачу для будущего сервиса ECS:

# создайте task definition для контейнера docker
ecs-cli compose \
  --file docker-compose.production.yml \
  --project-name $CLUSTER_NAME \
  --ecs-params ecs-params.production.yml \
  --cluster-config $CLUSTER_NAME \
  create

После создания задачи ей будет присвоенный определенный номер. Запишем этот номер в переменную TASK_NUMBER и создадим сервис ECS:

aws ecs create-service \
  --service-name 'spreeproject' \
  --cluster $CLUSTER_NAME \
  --task-definition "$CLUSTER_NAME:$TASK_NUMBER" \
  --load-balancers "loadBalancerName=$BALANCER_NAME,containerName=web_server,containerPort=8080" \
  --desired-count 2 \
  --deployment-configuration "maximumPercent=200,minimumHealthyPercent=50"

Теперь на оставшемся незанятом инстансе нашего кластера, запустим приложение для background processing.

Создадим docker-compose-worker.production.yml как compose production версии Sidekiq приложения. Замените YOUR_ECR_ID и YOUR_RAILS_MASTER_KEY на собственные значения:

touch docker-compose-worker.production.yml
version: '3'

services:
  worker_app:
    image: YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/server_app:production
    command: bundle exec sidekiq -C config/sidekiq.yml
    environment:
      RAILS_ENV: production
      RAILS_MASTER_KEY: YOUR_RAILS_MASTER_KEY

Создадим файл схему ecs-params touch ecs-params-worker.production.yml:

version: 1
task_definition:
  ecs_network_mode: bridge

  task_size:
    cpu_limit: 768
    mem_limit: 0.5GB

  services:
    worker_app:
      essential: true

Создаем задачу для будущего сервиса ECS:

# create task definition for a docker container
ecs-cli compose \
  --file docker-compose-worker.production.yml \
  --project-name "$CLUSTER_NAME-worker" \
  --ecs-params ecs-params-worker.production.yml \
  --cluster-config $CLUSTER_NAME \
  create

И создадим сервис для этой задачи:

aws ecs create-service \
  --service-name "spreeproject-worker" \
  --cluster $CLUSTER_NAME \
  --task-definition "$CLUSTER_NAME-worker:$TASK_NUMBER" \
  --desired-count 1 \
  --deployment-configuration "maximumPercent=200,minimumHealthyPercent=50"

Continuous Deployment

Какую проблему решаем

Поскольку поддерживаемых окружений стало больше, нужно упростить процесс обновления приложения.

Решение

Image Source

В основе термина Continuous deployment лежит слово Continuous, которое, несмотря на широкое употребление, разработчики трактуют по-разному. Понимание этого слова заключается в определении трех аспектов:

  • Continuous Delivery ‒ отвечает за поставку бизнесу каждой части функционала постепенно. Такой подход позволяет получить отклик от клиента своевременно и, при необходимости, сделать изменения.
  • Continuous Deployment ‒ отвечает за то, чтобы весь новый функционал после тестирования сразу же интегрировался в работающее приложение без ручного вмешательства инженеров DevOps.
  • Continuous Integration ‒ ключевой компонент практики Agile Development. Основывается на постоянном попадании кода в центральный репозиторий после успешного прохождения тестов. Основные цели Continuous Integration — поиск и устранение потенциальных проблем как можно быстрее, улучшение качества ПО и сокращение времени на выпуск обновлений.

В качестве сервиса, который позволяет реализовать Continuous методологию, мы будем использовать CircleCI. Для Continuous Deployment будем использовать ecs-deploy. Все связанные скрипты для развертывания приложения будем хранить в директории config/deploy. Там же определим список зависимых инструментов для развертывания.

Конфигурация процесса деплоя

Создадим файл dependencies.txt

cd ../spree-docker-demo
mkdir config/deploy
touch config/deploy/dependencies.txt

И укажем в нем пакеты, которые понадобятся для деплоя:

ecs-deploy==1.4.3
awscli==1.16.38

Далее в deploy.sh создадим собственный скрипт с удобным интерфейсом для обновления наших сервисов на ECS:

mkdir config/deploy/lib
touch config/deploy/lib/deploy.sh
#!/bin/bash

# Required variables:
# - region # ECR Region
# - aws-access-key # AWS Access Id
# - aws-secret-key # AWS Secret Key
# - service # Service's name
# - repo # Service's repository
# - cluster # Service's cluster
# - name # Application's container name
# - task # AWS ECS task name

set -e

deploy() {
  while [[ $# -gt 0 ]]
  do
  key="$1"

  case $key in
      --region)
      REGION="$2"
      shift
      shift
      ;;
      --aws-access-key)
      AWS_ACCESS_KEY_ID="$2"
      shift
      shift
      ;;
      --aws-secret-key)
      AWS_SECRET_ACCESS_KEY="$2"
      shift
      shift
      ;;
      --repo)
      REPO="$2"
      shift
      shift
      ;;
      --cluster)
      CLUSTER="$2"
      shift
      shift
      ;;
      --task)
      TASK="$2"
      shift
      shift
      ;;
      --service)
      SERVICE="$2"
      shift
      shift
      ;;
      --name)
      APP_NAME="$2"
      shift
      shift
      ;;
      --skip-build)
      SKIP_BUILD="$2"
      shift
      shift
      ;;
      *)
      echo "Unknown option $1\n"
      shift
      shift
  esac
  done

  VERSION=${CIRCLE_BRANCH:="$(git rev-parse --abbrev-ref HEAD)"}
  BUILD_APP=$APP_NAME:$VERSION
  BUILD_REPO=$REPO:$VERSION

  echo "📦  Install dependencies"
  dependencies_setup

  echo "🐳  Build docker image $BUILD_APP"
  push_to_docker

  echo "🚀  Deploy $BUILD_APP to $CLUSTER:$SERVICE"
  ecs_deploy

  echo '✅  Deploy successfully finished'
}

# Устанавливаем пакеты, необходимые для деплоя
dependencies_setup () {
  python3 -m venv venv
  . venv/bin/activate
  pip install -r config/deploy/dependencies.txt
}

# Пушим текущую версию в репозиторий
push_to_docker() {
  if [ -n "$SKIP_BUILD" ]; then echo 'Skip build'
  else
    $(aws ecr get-login --region $REGION --no-include-email)

    docker build --cache-from=$BUILD_APP -t $BUILD_APP .
    docker tag $BUILD_APP $BUILD_REPO
    docker push $BUILD_REPO
  fi
}

# Деплоим сервис
ecs_deploy() {
  aws ecs update-service \
    --service $SERVICE \
    --cluster $CLUSTER \
    --task-definition $TASK \
    --force-new-deployment
}

exec "$@"

После того как мы создали скрипт деплоя приложения на ECS, создадим скрипты для деплоя staging- и production-окружений.

Конфигурация деплоя Staging окружения

После, опишим скрипт для деплоя основного сервиса на staging:

touch config/deploy/staging.sh
chmod +x config/deploy/staging.sh

AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY будут взяты из окружения, где запускается этот скрипт:

source 'config/deploy/lib/deploy.sh'

RAILS_ENV='staging'

deploy \
  --name 'spreeproject_server_app' \
  --aws-access-key "$AWS_ACCESS_KEY_ID" \
  --aws-secret-key "$AWS_SECRET_ACCESS_KEY" \
  --region 'us-east-1' \
  --repo "$YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/server_app" \
  --cluster 'spreeproject-staging' \
  --service 'spreeproject'
  --task 'spreeproject-staging'

Конфигурация деплоя Production-окружения

Внимание! Процесс обновления production-сервера необходимо контролировать. Поскольку возможна ситуация ошибочного мержа изменений в production-ветку, то настоятельно рекомендуется избежать использования данного скрипта вместе с CircleCI или другими инструментами, реализующими continuous delivery.

Опишем скрипт для деплоя основного сервиса на production:

touch config/deploy/production.sh
chmod +x config/deploy/production.sh
source 'config/deploy/lib/deploy.sh'

RAILS_ENV='production'

# Deploy server app
deploy \
  --name 'spreeproject_server_app' \
  --aws-access-key "$AWS_ACCESS_KEY_ID" \
  --aws-secret-key "$AWS_SECRET_ACCESS_KEY" \
  --region 'us-east-1' \
  --repo "$YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/server_app" \
  --cluster 'spreeproject-production' \
  --service 'spreeproject'
  --task 'spreeproject-production'

# Deploy worker app
deploy \
  --name 'spreeproject_worker_app' \
  --aws-access-key "$AWS_ACCESS_KEY_ID" \
  --aws-secret-key "$AWS_SECRET_ACCESS_KEY" \
  --region 'us-east-1' \
  --repo "$YOUR_ECR_ID.dkr.ecr.us-east-1.amazonaws.com/spreeproject/server_app" \
  --cluster 'spreeproject-production' \
  --service 'spreeproject-worker'
  --task 'spreeproject-production-worker'
  --skip-build true

Создадим production ветку приложения:

git checkout -b production

Примечание: перед деплоем вручную создайте следующие теги, чтобы переиспользовать ранее созданные слои:

docker tag spreeproject_server_app:latest spreeproject_server_app:production
docker tag spreeproject_web_server:latest spreeproject_web_server:production

Создадим файл с конфигурацией CircleCI:

touch circle.yml
app_image: &app_image
  docker:
    - image: circleci/ruby:2.5-node
      environment:
        RAILS_ENV: test
        DB_HOST: localhost
        DB_PORT: 5432
        DB_NAME: spreedemo_test
        DB_USERNAME: postgres
        DB_PASSWORD: postgres
        SECRET_KEY_BASE: SECRET_KEY_BASE
        DEVISE_SECRET_KEY: DEVISE_SECRET_KEY

    - image: circleci/postgres:10
      environment:
        POSTGRES_USER: postgres
        POSTGRES_PASSWORD: postgres
        POSTGRES_DB: spreedemo_test

deploy_image: &deploy_image
  docker:
    - image: circleci/python:3.7

caches:
  - &bundle_cache spreedemo-bundle-v1-{{ checksum "Gemfile.lock" }}
  - &docker_images_cache spreedemo-docker-v1-{{ checksum "Gemfile.lock" }}-{{ checksum "Dockerfile" }}
  - &deploy_dependencies_cache spreedemo-pip-v1-{{ checksum "config/deploy/dependencies.txt" }}

steps:
  - &restore_bundle_cache
    restore_cache:
        name: Restore bundle cache
        keys:
          - *bundle_cache

  - &store_bundle_cache
    save_cache:
      name: Store bundle cache
      key: *bundle_cache
      paths:
        - vendor/bundle

  - &restore_docker_images_cache
    restore_cache:
        name: Restore docker images cache
        keys:
          - *docker_images_cache

  - &store_docker_images_cache
    save_cache:
      name: Store docker images cache
      key: *docker_images_cache
      paths:
        - images

  - &load_docker_image
    run:
      name: Load Docker image layer cache
      command: set +o pipefail && docker load -i images/spreeproject_server_app-$CIRCLE_BRANCH.tar | true

  - &save_docker_image
    run:
      name: Save Docker image layer cache
      command: mkdir -p images && docker save -o images/spreeproject_server_app-$CIRCLE_BRANCH.tar spreeproject_server_app:$CIRCLE_BRANCH

  - &restore_deploy_dependencies_cache
    restore_cache:
        name: Restore deploy dependecies
        keys:
          - *deploy_dependencies_cache

  - &store_deploy_dependencies_cache
    save_cache:
      name: Store deploy dependecies
      key: *deploy_dependencies_cache
      paths:
        - venv

  - &bundle_install
    run: bundle install --path vendor/bundle

  - &migrate_db
    run: bundle exec rails db:migrate

  - &run_test
    run:
      name: Run tests
      command: bundle exec rspec --color -f d spec

  - &deploy
    run:
      name: Deploy
      command: config/deploy/$CIRCLE_BRANCH.sh

version: 2
jobs:
  build_and_test:
    <<: *app_image
    steps:
      - checkout
      
      - *restore_bundle_cache
      - *bundle_install
      - *store_bundle_cache

      - *migrate_db

      - *run_test

  deploy:
    <<: *deploy_image
    steps:
      - checkout
      - setup_remote_docker

      - *restore_deploy_dependencies_cache
      - *restore_docker_images_cache
      - *load_docker_image

      - *deploy

      - *save_docker_image
      - *store_docker_images_cache
      - *store_deploy_dependencies_cache


workflows:
  version: 2
  build:
    jobs:
      - build_and_test
      - deploy:
          requires: 
            - build_and_test
          filters:
            branches:
              only:
                - dev
                - staging

Важно! Для того, чтобы инстансы CircleCI могли развертывать приложение на AWS ECS, ему необходимо предоставить AWS credentials. Для этого добавьте их в setting проекта.

Результат: после мержа определенных изменений в staging или dev branches будет происходить автоматический запуск тестов, по успешному прохождению которых будет происходить деплой.

Делаем выводы

Целью этого туториала было заинтересовать Ruby/Ruby on Rails разработчиков Docker-ом, показать способы развертывания на AWS с рабочими примерами. Я хотел собрать полезный туториал в едином формате, чтобы человек, который до этого не работал с Docker и ECS, сэкономил время на поиск информации, которая могла бы помочь решить ему задачу деплоя. Надеюсь, что у меня это получилось :) Буду рад вашим комментариям, замечаниям и советам и отвечу на каждый из них.

Спасибо команде RubyGarage за помощь в подготовке этого туториала, без вас этот туториал не получился бы таким, как он есть. Спасибо Владимиру Воробьеву за вдохновение, менторинг и технически правки, Анастасии Щербань за классные иллюстрации, Марине Завийбороде за фасилитацию процесса и команде DOU за публикацию!


Предыдущие части туториала: часть 1, часть 2.

LinkedIn

2 комментария

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Спасибо за цикл статей.
Не совсем очевидно в конце, что дает CircleCI и как откатывать билд, если что то пошло не так (Blue/Green/Canary?).

Могу лишь добавить, что AWS CodePipeline мог бы дать возможность избежать велосипеда со скриптами и CircleCI. Более того, исходя из документации, он имеет интеграцию с ECS, умеет тригерить деплой, если произошел push в ECR, и, соответсвенно, откатывать его, если что то пошло не так.

Здравствуйте. Спасибо за отзыв. Да согласен, нужно было бы подробнее описать работу с CircleCI. В данном контексте CircleCI позволяет нам автоматизировать процесс запуска тестов и деплоя на dev, staging окржуения.

Про AWS CodePipeline не слышал, но звучит очень круто. Большое спасибо, обязательно возьму на изучение

Подписаться на комментарии