CI/CD с помощью AWS и Bamboo

Наша команда состоит из одного разработчика и одного DevOps инженера. Я отвечаю за разворачивание приложения в кластере Amazon ECS. В качестве CI/CD сервера я использую Bamboo. В этой статье я подробно расскажу, как я осуществляю разворачивание приложения в dev-среде.

cnbu3wmmjkhiqhwgxpuhk3pu2fc.png
evozrsbpwhmgd0zodgrz4pukurm.png

Сборка Docker образа


Здесь я выполняю следующие шаги:

  • Шаг 1: Установка и настройка Docker;
  • Шаг 2: Настройка артефактов в Bamboo;
  • Шаг 3: Настройка репозитария Amazon ECR;
  • Шаг 4: Сборка Docker образа в Bamboo.


Сначала я обновляю сервер, на котором установлен Bamboo, устанавливаю необходимые пакеты и настраиваю репозиторий Docker. Здесь надо отметить, что Bamboo я установил на операционной системе CentOS 7. Информацию по установке docker на других операционных системах можно найти на сайте www.docker.com.

$ sudo yum update 
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2 
$ sudo yum-config-manager --add-repo 
https://download.docker.com/linux/centos/docker-ce.repo 


Затем я устанавливаю приложение Docker и запускаю сервис:

$ sudo yum install docker-ce docker-ce-cli containerd.io
$ sudo systemctl enable docker 
$ sudo systemctl start docker 


Потом добавляю пользователя bamboo в группу Docker:

$ sudo usermod -aG docker bamboo 
$ sudo su - bamboo 
$ docker run hello-world


Если после запуска этих команд docker отвечает сообщением «Hello from Docker!», то это означает, что моя установка работает правильно.
Разработка приложения ведется на Grails. При компиляции приложения создается файл с расширением war. Данный файл, в терминологии Bamboo, является артефактом. Настроим Bamboo для использования данного файла в последующих задачах. Для этого я перехожу во вкладку Tasks:

l2xfrhlwvte_3enperkvbqadtey.png

И настраиваю задачу Grails, как показано ниже:

ba6ls73snayld5eazip7mcc0eg4.png

Мы видим, что Grails сначала очищает папку сборки, затем запускает тесты и, наконец, создает war-файл для dev среды.

После этого я нажимаю вкладку Artifacts и кнопку Create artifact:

2asjam033lqmwc9txcj_slq_5jc.png

Я задаю артефакт, как показано ниже:

jw_bwbdddrwx1pa1gh7etxonvys.png

Grails помещает war-файл в каталог build/libs. Поставлю галочку для параметра Shared, так как этот артефакт мне понадобится позже.

Теперь я создаю проект развертывания (deployment project) и указываю артефакт для использования в своем плане сборки:

y7fsufkg7rsi-6d3i-hfk8o6rlc.png

s7nftu9r8vwudgjzi1qdtlaljpw.png

Ещё я настраиваю задачу Artifact download в проекте развертывания:

ievvuvkegkneya15tqd9xlcayhu.png

Таким образом, Bamboo теперь настроен на использование war-файла.


Amazon ECR — это сервис хранения Docker образов, а также управления ими. Для настройки необходимо открыть консоль AWS и выбрать ECR:

ysfd1ijutkdnzcugeldnemvywl4.png

Создав репозитарий, я получаю такой адрес:

aws_account_id.dkr.ecr.us-east-2.amazonaws.com/onboard


После окончания настройки здесь же можно найти инструкцию о том, как залогиниться, как загрузить образ с репозитария и загрузить образ на репозитарий.
Теперь мне необходимо настроить тригер для запуска сборки Docker образа. Для этого я перехожу во вкладку Triggers и нажимаю на кнопку Add trigger:

evgqnu95-my1ralvlnxrw6tx3fe.png

Здесь я выбираю опцию Build after successfully building the plan для того, чтобы Docker образ собирался после компиляции проекта.

Теперь надо добавить задание сборки Docker образа. Для этого переходим во вкладку Tasks, нажинаем Add task, выбираем тип Docker. Вводим описание и выбираем Build a Docker image из списка раскрывающегося меню. В поле Repository вводим

 aws_account_id.dkr.ecr.us-east-2.amazonaws.com/onboard:latest. 


Что же касается Dockerfile, то он может быть таким, как показано ниже:

FROM openjdk:8-jre 
COPY *.war /usr/src/onboard.war 
WORKDIR /usr/src 
CMD ["/bin/bash", "-c", "java -DdataSource.url=$DATASOURCE_URL -DdataSource.username=$DATASOURCE_USERNAME -DdataSource.password=$DATASOURCE_PASSWORD -jar onboard.war"] 


При запуске приложения необходимо указать базу данных. Переменные среды DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD служат для передачи этой информации приложению, но их значения задаются во время запуска контейнера.

На этом настройка процесса сборки Docker образа с приложением завершена. Следующим шагом является настройки загрузки этого образа на репозитарий Amazon ECR.

yxn_3t_bqhvr2azfbyaywzadhpe.png

Загрузка образа в Elastic Container Registry


Для хранения образов, собранных с помощью Bamboo, можно использовать Elastic Container Registry. Для достижения этой цели я выполняю следующие шаги:

  • Шаг 1. Установка Amazon ECR Docker Credential Helper
  • Шаг 2. Подключение IAM роли к серверу Bamboo
  • Шаг 3: Настройка задания загрузки Docker образа


Для загрузки Docker образов на Amazon ECR нужно иметь учетные данные. Такие учетные данные можно получить, выполнив команду

aws ecr get-login


Однако эти учетные данные действительны только в течение 12 часов. Поэтому можно либо каждый раз перед загрузкой образа на ECR выполнять вышеуказанную команду, либо установить ECR Docker Credential Helper, который держит в актуальном состоянии временные учетные данные и логинится на ECR. Для установки ECR Docker Credential Helper необходимо выполнить следующие шаги.

Для начала надо установить git, а затем склонировать github репозитарий:

$ sudo yum install git 
$ sudo su - bamboo 
$ git clone 
https://github.com/awslabs/amazon-ecr-credential-helper.git 
$ make docker 


Потом надо поместить следующие строки в файл /home/bamboo/.docker/config.json:

{ 
        "credsStore": "ecr-login" 
} 


И скопировать собранное приложение в каталог /usr/bin:

$ exit 
$ sudo cp /home/bamboo/docker-credential-ecr-login /usr/bin/ 


Для того, чтобы сервер Bamboo смог использовать ECR, надо создать роль, добавить к этой роли политику AmazonEC2ContainerRegistryPowerUser, а затем прикрепить эту роль к EC2 инстансу Bamboo. Открываем консоль AWS и выбираем IAM. Далее нажимаем кнопку Create role, выбираем AWS service и EC2, как показано ниже:

1ovsjfchxa3p-bgv4y-6unnz8sk.png

Затем нажимаем кнопку Next: Permissions и на следующем экране находим и выбираем политику AmazonEC2ContainerRegistryPowerUser. После этого заканчиваем создание роли и присоединяем ее к своему серверу Bamboo.


Приложение мы собрали и собрали Docker образ с war-файлом. Теперь надо этот образ загрузить в репозитарий. Для этого добавляю еще одно задание Docker, в этот раз для загрузки образа в ECR репозитарий. Перехожу во вкладку Tasks, нажимаю Add task, выбираю тип Docker. Ввожу описание и выбираю Push a Docker image to a Docker registry из списка раскрывающегося меню. Выбираю Custom registry и вписываю адрес репозитория в поле Repository. В качестве типа аутентификации (Authentication type) я выбираю Use the agent«s native credentials.

На этом настройка процесса загрузки Docker образа на репозитарий Amazon ECR завершена. В следующих шагах описывается процесс настройки кластера и сервиса для запуска контейнерного приложения. Но перед этим надо настроить параметры запуска контейнера. Этим мы сейчас и займемся.

f7kuosux7wvkojanpgoy8zd7wya.png

Создание Amazon ECS Task Definition


Task Definition — здесь записываются параметры исполнения контейнеров. Наше приложение использует базу данных, параметры которого указываются при запуске контейнера, поэтому в этом разделе мы еще создадим базу данных. В качестве базы данных я использую Amazon RDS, а пароль для доступа к базе в зашифрованном виде я храню в AWS Systems Manager Parameter Store. Следующие шаги я выполняю для того, чтобы создать Task Definition:

  • Шаг 1. Создание базы данных на инстансе Amazon RDS;
  • Шаг 2. Настройка AWS Systems Manager Parameter Store;
  • Шаг 3. Создание Task Definition.


Наше приложение использует базу данных PostgreSQL. Для создания базы данных открываю консоль AWS, выбираю сервис Amazon RDS, нажимаю кнопку Create database, затем в качестве движка базы данных выбираю PostgreSQL. На следующей странице я выбираю Dev/Test в качестве рабочей среды и нажимаю кнопку Next. Затем обозначаю DB instance identifier как onboard-dev-db, и master username как devdbadmin. Потом я перехожу на следующую страницу, чтобы настроить VPC, группу подсетей (subnet group) и группу безопасности (security group). Эта база данных будет использоваться в частной сети, поэтому для параметра Public accessibility выбираю No. В поле Database name ввожу devdb и нажимаю кнопку Create database.
Пароль базы данных я храню в зашифрованном виде. Для этого открываю AWS консоль и перехожу в AWS Systems Manager → Shared Resources → Parameter Store → Create Parameter. В качестве имени параметра ввожу devdbpassword и выбираю SecureString для типа параметра, затем ввожу пароль базы данных в поле Value.
Amazon ECS — это кластер, в котором запускаются контейнерные приложения. Он использует Task Definition для указания параметров выполнения контейнерного приложения. Для задания таких параметров нажимаю кнопку Create new Task Definition. Затем выбираю в качестве типа запуска Fargate и перехожу к следующему шагу. Здесь я задаю имя как onboard-dev-taskdef. Для параметра Task execution IAM role field выбираю Create new role. Что же касается выделенных для данного приложения ресурсов, я обозначаю 2 ГБ памяти и 1 vCPU. Теперь необходимо добавить параметры запуска контейнера. Контейнер я назову onboard-dev-container. Имя образа обозначу так:

aws_account_id.dkr.ecr.us-east-2.amazonaws.com/onboard: latest. Приложение Amazon ECR Docker Credential Helper будет заботиться об аутентификации на ECR, поэтому опцию Private repository authentication оставляю неотмеченным. В dev-среде приложение доступно по порту 8080, поэтому для параметра port mappings записываю 8080 и выбираю протокол tcp. Параметры URL базы данных, имя пользователя и пароль передаются в контейнер с помощью переменных среды. Эти параметры я задаю в разделе Environment variables. Для того, чтобы получить значение параметра devdbpassword из Parameter Store указываю тип ValueFrom. Последнее, что я настраиваю — это Log configuration, здесь выбираю Auto-configure CloudWatch Logs. Теперь создание Task Definition завершено.

Однако, для роли ecsTaskExecutionRole нужна политика для получения devdbpassword из Parameter Store. Для этого перехожу в IAM Roles и выбираю ecsTaskExecutionRole, нажимаю Add inline policy. В данном случае я добавляю с помощью визуального редактора. Поэтому в поле Service ввожу Systems Manager, в поле Actions — GetParameters. Затем нажимаю Add ARN для поля Resources и заполняю свои значения:

2jwd6r5dpp41jbjj4g7gctfeafw.png

В конце просматриваю значения задаваемых параметров, нажав Review policy, даю ему имя и завершаю работу с конфигурацией ecsTaskExecutionRole.

На этом настройка параметров запуска контейнерного приложения завершена. Теперь надо создать кластер ECS и сервис.

0ku7xnp7bctypewbbcwyzdda_-y.png

Создание сервиса Amazon ECS


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

  • Шаг 1. Создание кластера Amazon ECS;
  • Шаг 2: Создание сервиса


Для создания кластера ECS надо перейти в консоль AWS и выбрать сервис ECS. Затем нажать Create Cluster и выбрать кластерный шаблон Networking only. На следующей странице я называю кластер как onboard-dev-cluster и завершаю создание кластера. Теперь у меня есть кластер ECS основанный на Fargate.
Чтобы создать сервис, я нажимаю на ссылку onboard-dev-cluster, затем перехожу во вкладку Services и нажимаю кнопку Create. Для параметра launch type выбираю Fargate, для Task Definition выбираю onboard-dev-taskdef. Кроме того, я выбираю onboard-dev-cluster в поле Cluster. В поле Service name я прописываю onboard-dev. Устанавливаю параметр Number of tasks на ноль, так как я не хочу запускать приложение прямо сейчас. Я оставляю значения параметров Minimum healthy percent равным 100, а Maximum percent — 200. Для параметра Deployment type выбираю Rolling update и перехожу к следующему шагу.

На странице Configure Network для параметра Cluster VPC я выбираю ранее созданный VPC, который называется Development VPC. Приложение в стадии разработки доступно только в частной сети, поэтому я выбираю две частные подсети. Для настройки групп безопасности я нажимаю кнопку Edit, затем выбираю Select existing security group, затем группу безопасности default и нажимаю кнопку Save. Для параметра Auto-assign public IP я выбираю Disabled. Далее, для параметра Load balancer type я выбираю None и оставляю невыбранным опцию Enable service discovery integration. Затем я нажимаю Next, Next и Create service.

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

dg_9er1txnjnjbhpkrcmn5af4lk.png

Обновление сервиса


Как только разработчик обновляет код приложения, наше развертывание проходит через создание Docker-образа, его загрузка в Elastic Container Registry и, наконец, запуск контейнерного приложения в виде сервиса в кластере ECS Fargate. На данный момент количество заданий, запущенных в кластере равно нулю. Чтобы приложение запустилось нужно обновить сервис, указав количество равным одному. Я выполняю следующие шаги для достижения этой цели:

  • Шаг 1. Установка плагина Tasks for AWS Bamboo;
  • Шаг 2. Обновление сервиса ECS


Tasks for AWS Bamboo — это плагин, который упрощает подготовку и эксплуатацию ресурсов AWS из проектов сборки и развертывания Bamboo. Для установки этого плагина перехожу в проект развертывания, нажимаю Add task, захожу на Atlassian Marketplace и устанавливаю Tasks for AWS (Bamboo).
Теперь в проекте развертывания я добавляю задание Amazon ECS Service. Затем записываю в поле описания задания Update Service for onBoard-dev. В поле Action выбираю Update Service и Force new deployment. Потом в качестве региона запуска приложения выбираю US East (Ohio). Затем записываю в соответствующих полях ARN (Amazon Resource Name) для Task Definiton, кластера и сервиса. В этом задании я обновляю желаемое количество запущенных заданий до одного. Далее я заполняю текстовое поле конфигурации развертывания следующими значениями:

{ 
  "maximumPercent": 200, 
  "minimumHealthyPercent": 100 
} 


Я настраиваю сеть без публичного IP следующим образом:

{ 
   "awsvpcConfiguration": { 
        "assignPublicIp": "DISABLED", 
        "subnets": ["subnet-ID1", "subnet-ID2"], 
        "securityGroups": ["sg-ID"] 
    } 
} 


В разделе Source для параметра AWS Security Credentials я выбираю IAM Role for EC2.

Мне нужно иметь возможность обновлять службу ECS, поэтому я присоединяю политику AmazonECS_FullAccess к своему экземпляру Bamboo EC2. Для этого открываю консоль AWS, выбираю IAM. Затем я выбираю роль, которую использую для своего сервера Bamboo. Нажимаю кнопку Attach policies, нахожу политику AmazonECS_FullAccess, устанавливаю флажок слева и заканчиваю прикреплять политику.

На этом заканчивается настройка CI/CD с использованием AWS и Bamboo. Таким образом, при обновлении кода приложения разработчиком, загрузки этого кода в репозитарий, запускается тестирование, сборка приложения. Затем выполняется сборка Docker образа с war-файлом приложения, и этот образ копируется на репозитарий Amazon ECR. Далее в кластере Amazon ECS запускается в виде сервиса контейнерное приложение, которое обновляет текущее приложение, если оно не было остановлено. Если же приложение было остановлено для экономии ресурсов, то приложение просто запускается. После завершения проверки приложения в dev-среде можно приложение остановить, указав количество заданий, запущенных в кластере равным нулю.

Если вам понравилась статья и есть идеи для улучшения, напишите в комментариях.

© Habrahabr.ru