Непрерывное развертывание инфраструктуры AWS без доверия стороннему CI

Непрерывное развертывание инфраструктуры AWS без доверия стороннему CI

by moiseevrus

Я работал со многими клиентами, которые размещают свою производственную инфраструктуру на AWS, но их конвейеры CI размещаются у стороннего поставщика, такого как Travis, Wercker и CircleCI. Довольно легко понять, почему: сторонние ЭК очень просты в настройке и использовании! У них также есть неплохая документация, и они широко используются в проектах с открытым исходным кодом. CI обычно используется для запуска тестов, создания артефактов выпуска, а иногда даже для развертывания приложений.

Однако многие команды чувствуют себя неловко, когда дело доходит до использования CI для развертывания изменений инфраструктуры. Обычно это происходит не потому, что они не доверяют автоматизированному процессу, а потому, что они не хотят предоставлять широкий доступ к учетным записям AWS, которые обычно требуются при управлении инфраструктурой как кодом.

Однако я обнаружил, что автоматизация изменений инфраструктуры может быть полезной. Я думаю, что это значительно снижает когнитивную нагрузку, когда речь идет о синхронизации вашей инфраструктуры. Итак, есть ли способ, которым мы могли бы по-прежнему обеспечивать непрерывное развертывание для изменений инфраструктуры, не доверяя стороннему CI? В этой статье я представлю одно решение этой проблемы. В основном я работаю с AWS, поэтому я создал решение специально для него.

Ограничения

Мы хотим автоматизировать изменения инфраструктуры, но есть ограничения. Давайте разберем проблему.

Ограничение № 1. Мы не хотим предоставлять стороннему CI доступ к инфраструктуре AWS, который позволит ему нанести какой-либо ущерб, если доступ по какой-либо причине попадет в чужие руки. Например, если произойдет утечка учетных данных AWS, назначенных CI, злоумышленник, использующий эти учетные данные, не сможет удалить наши базы данных и запустить крипто-майнеры.

Ограничение № 2: мы хотим, чтобы изменения инфраструктуры запускались без участия человека. Как только изменение кода инфраструктуры будет зафиксировано в основной ветке в репозитории Git, оно в конечном итоге будет развернуто в рабочей среде при условии, что остальные этапы автоматизации перед его выполнением будут успешно выполнены.

Ограничение № 3. Мы хотим, чтобы изменения инфраструктуры инициировались после конвейера CI, подключенного к тому же репозиторию кода. Некоторые репозитории Git могут содержать как код приложения, так и код инфраструктуры. Например, репозиторий Git может содержать исходный код веб-приложения и код инфраструктуры для развертывания приложения и окружающих служб (например, корзины S3, экземпляры RDS). В этих случаях важно убедиться, что изменения инфраструктуры не выполняются, если тесты не пройдены в конвейере CI.

Ограничение № 4. Мы хотим, чтобы это решение работало с несколькими репозиториями Git. Как упоминалось в приведенном выше ограничении, у нас может быть код инфраструктуры, совмещенный с кодом приложения, а это означает, что нам необходимо поддерживать изменения инфраструктуры, поступающие из нескольких репозиториев.

Грубый архитектурный эскиз

Имея в виду эти ограничения, давайте представим, как примерно будет выглядеть архитектура.

Если мы хотим развернуть изменения инфраструктуры AWS, но в то же время сделать это безопасно, обычно рекомендуется запускать их в самом AWS. Таким образом, вам нужно доверять только AWS, а не дополнительным третьим сторонам. В конце концов, вы уже доверяете AWS свою инфраструктуру. Кроме того, если вы развертываете изменения из AWS, вы можете использовать роли IAM, которыми гораздо сложнее злоупотреблять, чем пользователями IAM .

Предполагая, что у нас есть этот волшебный деплойер, размещенный в AWS, нам нужен способ его запуска. Если мы запустим его напрямую от нашего поставщика Git (например, веб-перехватчики Github), мы не можем гарантировать, что он будет работать после завершения конвейера CI (ограничение № 3). По этой причине мы предоставим возможность запускать деплойер для репозитория по запросу из CI (ограничение № 2), но не более того. Разработчик по-прежнему будет нести ответственность за получение исходного кода и любых других деталей, связанных с запуском развертывания, вместо того, чтобы доверять CI передавать правильную информацию (ограничение № 1).

Нам также нужен регистр, чтобы содержать информацию о том, какие именно репозитории можно использовать с развертывателем. Когда развертыватель запускается, он проверяет данные репозитория из реестра. Таким образом, мы можем поддерживать несколько исходных репозиториев с одним и тем же деплойером (ограничение № 4), но также убедиться, что мы не запускаем код из произвольных источников. Мы также можем включить в реестр такую ​​информацию, как данные аутентификации Git и команды развертывания.

Поиск решений

Давайте заглянем в нашу кучу LEGO, которая является каталогом продуктов AWS , и посмотрим, какие компоненты мы можем найти для решения нашей проблемы.

Фото Рика Мейсона на Unsplash

КодБилд

Что мы действительно хотим сделать, так это непрерывное развертывание на AWS, и AWS рекомендует использовать для этого CodePipeline и CodeBuild. Подводя итог, можно сказать, что CodePipeline — это решение для связывания различных шагов для формирования конвейера доставки вашего кода, а CodeBuild предназначен для работы в качестве шага в конвейере, который запускает произвольные команды, такие как тесты и упаковка программного обеспечения.

Поскольку все, что мы хотим сделать, это запустить один шаг, развернуть код инфраструктуры, CodeBuild должно быть достаточно для наших целей. CodeBuild может запускать контейнеры Docker, поэтому мы можем создать собственный образ Docker, включающий весь код и инструменты, необходимые для получения исходного репозитория и внесения изменений в инфраструктуру. Затем мы можем выполнить его независимо, используя вызов API StartBuild .

Проксирование вызовов CodeBuild

Итак, мы просто создадим проект CodeBuild и предоставим возможность вызывать API StartBuild из стороннего CI, верно? К сожалению, это раскрывает слишком многое. API StartBuild позволяет вам переопределить множество деталей, указанных в проекте CodeBuild, в том числе какие команды запускать. В случае утечки доступа к API злоумышленник, имеющий доступ, может выполнять произвольные команды в нашей инфраструктуре AWS.

Если бы только у нас был какой-то прокси для соединения и фильтрации вызовов от стороннего CI к CodeBuild.

Я сомневаюсь, что у AWS есть продукт, специально предназначенный для решения этой проблемы, но мы всегда можем положиться на Lambda — часть AWS LEGO 2×4 — чтобы склеить эти части вместе. Мы можем создать тему SNS, где CI может отправить сообщение, чтобы инициировать развертывание в AWS. Затем тема SNS запускает функцию Lambda, которая запускает задание CodeBuild. Мы также можем использовать функцию Lambda для проверки репозитория и запускать задание CodeBuild только в том случае, если репозиторий находится в белом списке.

параметры ССМ

Нам по-прежнему нужно место для хранения сведений о том, какие репозитории можно использовать с развертывателем. Я обнаружил, что параметры SSM являются полезным решением для записи произвольных пар ключ-значение небольшого объема. Он также поддерживает IAM для управления доступом и KMS для шифрования значений, поэтому мы можем ограничить, кто может изменять список репозиториев, внесенных в белый список для развертывателя, и безопасно хранить учетные данные Git.

Диаграмма архитектуры

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

Покажи мне код!

Это не было бы большой демонстрацией, если бы я просто показал вам кучу архитектурных диаграмм. Давайте посмотрим на некоторые примеры кода.

У меня есть полный пример, размещенный в репозитории GitHub: I Wanna Own My Pipeline in AWS . Я создал его с помощью AWS CDK с TypeScript в качестве языка программирования, но здесь подойдет практически любой инструмент «инфраструктура как код» с поддержкой AWS.

Информация о проекте в параметрах SSM

Во-первых, давайте посмотрим, как мы храним информацию о проекте в параметрах SSM.

Я решил разместить всю информацию о проекте по одному корневому пути ( /iwomp-in-aws), при этом каждый проект имеет один параметр, содержащий все необходимые сведения в формате JSON. JSON включает следующие ключи:

  • gitUrl: URL-адрес репозитория Git, из которого нужно извлечь код.
  • authToken: Токен для аутентификации на основе токенов. (по желанию)
  • basicUsername: Имя пользователя для базовой аутентификации. (по желанию)
  • basicPassword: Пароль для базовой аутентификации. (по желанию)
  • deployDir: Каталог, в котором находится весь код инфраструктуры. По умолчанию: корневой каталог Git. (по желанию)
  • command: команда для развертывания изменений инфраструктуры. Обратите внимание, что одна и та же команда выполняется всегда для каждого запуска, поэтому, если вам нужно выполнить какую-либо фильтрацию веток Git, это необходимо учитывать в сценарии.

Например, /iwomp-in-aws/demoможет содержать следующий документ JSON:

{ 
"gitUrl": "https://github.com/jkpl/cdk-demo ", 
"command": "./deploy.sh" 
}

Проект использует «demo» в качестве идентификатора проекта, код извлекается из репозитория cdk-demo, и ./deploy.shкоманда выполняется каждый раз, когда запускается деплойер.

Информация о проекте может управляться вручную или автоматически. Любой способ работает лучше всего для вас. Репозиторий Git содержит пример того, как управлять проектами с помощью CDK .

Проект CodeBuild

Поскольку мы собираемся запускать собственный контейнер Docker в CodeBuild, мы сначала настроим репозиторий ECR для размещения образов Docker . Я расскажу о содержимом образа Docker в следующем разделе.

import * as ecr from '@aws-cdk/aws-ecr';
const containerImageRepo = new ecr.Repository(this, 'repo', {
repositoryName: 'iwomp-in-aws',
});

Теперь мы можем создать проект CodeBuild для нашего деплойнера . Мы настроим его на чтение образа Docker из репозитория ECR выше и запустим команду развертывания iwomp-in-aws, доступную в образе. Я дополнительно установлю CONFIGPATHпеременную среды, которая сообщает контейнеру, где мы можем найти всю информацию о проекте в параметрах SSM.

import * as codebuild from '@aws-cdk/aws-codebuild';
const configPath = iwompProps.configPath || 'iwomp-in-aws';
const worker = new codebuild.Project(this, 'worker', {
buildSpec: codebuild.BuildSpec.fromObject({
version: '0.2',
phases: {
build: {
commands: ['iwomp-in-aws'],
},
},
}),
description: 'iwomp-in-aws worker',
environment: {
buildImage: codebuild.LinuxBuildImage.fromEcrRepository(containerImageRepo),
computeType: codebuild.ComputeType.SMALL,
environmentVariables: {
'CONFIGPATH': {value: configPath},
}
},
});

Нам потребуется предоставить IAM-разрешения проекту CodeBuild, чтобы он работал. CDK автоматически создаст роль IAM для проекта CodeBuild, которому мы можем предоставить разрешение. Во- первых, нам нужно предоставить ему доступ для чтения параметров SSM .

import * as iam from '@aws-cdk/aws-iam';
const configPath = iwompProps.configPath || 'iwomp-in-aws';
const configPathArn = this.formatArn({
service: 'ssm',
resource: `parameter/${configPath}/*`
});
worker.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: [configPathArn],
actions: ['ssm:GetParameter'],
}));

Мы также хотим предоставить доступ к управлению другими сервисами AWS, которыми мы хотим управлять автоматически. В репозитории Git есть один пример того, как это сделать .

Инструмент развертывания

Проекту CodeBuild требуется специальный инструмент для клонирования исходного репозитория Git и выполнения команды развертывания в соответствии с информацией, хранящейся в параметрах SSM. Это iwomp-in-awsкоманда, упомянутая в последнем разделе.

Я решил закодировать инструмент, используя Go в качестве языка программирования, потому что я знаком с ним, и поскольку он создает статические двоичные файлы, я могу просто вставить любой образ Docker.

Параметры запуска инструмента считываются из переменных среды с помощью envconfig . Эти параметры предоставляются при запуске задания CodeBuild.

package mainimport (
	"fmt"
	"log"
	"github.com/kelseyhightower/envconfig"
)type appConfig struct {
	ConfigPath  string `default:"iwomp-in-aws"`
	ProjectName string `required:"true"`
	GitBranch   string `required:"true"`
}

func (c *appConfig) projectPath() string {
	return fmt.Sprintf("/%s/%s", c.ConfigPath, c.ProjectName)
}

func (c *appConfig) load() error {
	return envconfig.Process("", c)
}func main() {
	if err := mainWithErr(); err != nil {
		log.Fatalf("iwomp-in-aws: %s", err)
	}
}

func mainWithErr() error {
	// Load app config
	var appConf appConfig
	if err := appConf.load(); err != nil {
		return err
	}
	// continued in the next code block ...
}

Вот для чего нужны конфигурации:

  • ConfigPath: базовый путь для параметров SSM.
  • ProjectName: Имя проекта для развертывания. Имя должно совпадать с идентификатором проекта в параметрах SSM. Например, если установлено значение demo и ConfigPathиспользуется значение по умолчанию, сведения о проекте извлекаются из параметра SSM /iwomp-in-aws/demo.
  • GitBranch: имя ветки Git для клонирования проекта.

Далее мы загрузим конфигурацию проекта из параметров SSM.

import (
	"encoding/json"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ssm"
)type projectConfig struct {
	GitURL        string `json:"gitUrl"`
	AuthToken     string `json:"authToken"`
	BasicUsername string `json:"basicUsername"`
	BasicPassword string `json:"basicPassword"`
	DeployDir     string `json:"deployDir"`
	Command       string `json:"command"`
}func (c *projectConfig) load(ac *appConfig, sess *session.Session) error {
	ssmSVC := ssm.New(sess)

	projectPath := ac.projectPath()
	projectOut, err := ssmSVC.GetParameter(&ssm.GetParameterInput{
		Name:           aws.String(projectPath),
		WithDecryption: aws.Bool(true),
	})
	if err != nil {
		return fmt.Errorf("failed to fetch from SSM path %s: %s", projectPath, err)
	}
	if err := c.loadFromParameter(projectOut.Parameter); err != nil {
		return fmt.Errorf("failed to load from SSM path %s: %s", projectPath, err)
	}

	// Fill in the gaps
	if c.DeployDir == "" {
		c.DeployDir = "."
	}

	// Validate
	if c.GitURL == "" {
		return fmt.Errorf("no Git URL specified for project %s", ac.ProjectName)
	}
	if c.Command == "" {
		return fmt.Errorf("no command specified for project %s", ac.ProjectName)
	}

	return nil
}

func (c *projectConfig) loadFromParameter(parameter *ssm.Parameter) error {
	return json.Unmarshal([]byte(*parameter.Value), c)
}func mainWithErr() error {
	// continued from the previous code block ...

	// Start AWS session
	sess, err := session.NewSession(&aws.Config{})
	if err != nil {
		return err
	}

	// Load config for the project
	var projectConf projectConfig
	if err := projectConf.load(&appConf, sess); err != nil {
		return err
	}

	// continued in the next code block ...
}

Конфигурация проекта включает в себя все параметры, перечисленные в разделе «Информация о проекте в параметрах SSM». Как упоминалось ранее, мы загрузим один параметр SSM и проанализируем его содержимое как JSON. Некоторые параметры имеют разумные значения по умолчанию, поэтому мы просто восполним пробелы, если они есть. Мы также проверим, что по крайней мере URL-адрес Git и команда развертывания были указаны, и быстро завершится сбой, если они не указаны.

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

import (
	git "github.com/go-git/go-git/v5"
	gitPlumbing "github.com/go-git/go-git/v5/plumbing"
	gitTransport "github.com/go-git/go-git/v5/plumbing/transport"
	gitHTTP "github.com/go-git/go-git/v5/plumbing/transport/http"
)func (c *projectConfig) gitAuth() gitTransport.AuthMethod {
	if c.AuthToken != "" {
		return &gitHTTP.TokenAuth{
			Token: c.AuthToken,
		}
	}
	if c.BasicPassword != "" {
		return &gitHTTP.BasicAuth{
			Username: c.BasicUsername,
			Password: c.BasicPassword,
		}
	}

	return nil
}func cloneRepository(appConf *appConfig, projectConf *projectConfig) error {
	log.Printf("cloning repo %s branch %s", projectConf.GitURL, appConf.GitBranch)

	_, err := git.PlainClone(".", false, &git.CloneOptions{
		URL:           projectConf.GitURL,
		Auth:          projectConf.gitAuth(),
		ReferenceName: gitPlumbing.NewBranchReferenceName(appConf.GitBranch),
		SingleBranch:  true,
		Progress:      os.Stdout,
		Depth:         1,
	})
	return err
}func mainWithErr() error {
	// continued from the previous code block ...	// Clone repo for the project based on app config
	if err := cloneRepository(&appConf, &projectConf); err != nil {
		return err
	}

	// continued in the next code block ...
}

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

Наконец, мы можем выполнить команду развертывания, указанную для проекта.

import (
	"os"
	"os/exec"
)func (c *projectConfig) run(appConf *appConfig) error {
	cmd := exec.Command(c.Command, appConf.GitBranch)
	cmd.Dir = c.DeployDir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}func mainWithErr() error {
	// continued from the previous code block ...

	// Run the project command
	return projectConf.run(&appConf)
}

Вы можете найти полный исходный код этого инструмента в репозитории Git .

Развертывание Docker-образа

Нам нужен образ Docker для обработки процесса развертывания. Он должен включать следующее:

  1. Инструмент развертывания из предыдущего раздела.
  2. Фоновые зависимости, необходимые для выполнения фактического развертывания. Например, CDK, Terraform, Pulumi или любой другой инструмент, который вы хотите использовать для управления инфраструктурой AWS.

Начнем с создания инструмента развертывания на этапе сборки в Docker:

FROM golang:1.14 as builder

WORKDIR /project

# Download dependencies
COPY go.mod go.sum ./
RUN go mod download

# Build the app
COPY main.go ./
RUN CGO_ENABLED=0 go build -o iwomp-in-aws

Создав инструмент, мы можем подготовить образ Docker для нашего проекта CodeBuild.

FROM node:14-slim

# Install system packages
RUN apt-get update && \
    apt-get install -y ca-certificates && \
    rm -rf /var/lib/apt/lists/*

# Install CDK
RUN npm install -g aws-cdk

# Non-root user
RUN groupadd -g 10101 cdk && \
    useradd -m -d /project -g cdk -u 10101 cdk
USER cdk:cdk
WORKDIR /project

# Runner script
COPY --from=builder /project/iwomp-in-aws /usr/bin/iwomp-in-aws
ENTRYPOINT [ "iwomp-in-aws" ]

Последнее, что нам осталось сделать, — это создать образ Docker и отправить его в репозиторий ECR , который мы создали ранее.

Лямбда-функция

Далее давайте посмотрим на функцию Lambda, которая используется для запуска развертывателя.

Мы создадим обработчик JavaScript для приема событий SNS. Во-первых, мы проверим входящее событие, чтобы оно содержало достаточно информации. Он должен содержать идентификатор проекта и ветку Git, содержащую код инфраструктуры.

exports.handler = async function(event) {
    if (!event.Records[0]) {
        throw new Error('No SNS event found');
    }
    const message = parseMessage(event.Records[0].Sns.Message);
    // continued in the next code block ...
}function parseMessage(message) {
    const json = JSON.parse(message);
    if (!json.project) {
        throw new Error('No project name provided');
    }
    if (!json.branch) {
        throw new Error('No branch provided');
    }
    return json;
}

Далее мы проверим наличие проекта в белом списке, проверив, что он записан в параметрах SSM. Базовый путь для параметров SSM предоставляется через CONFIGPATHпеременную среды.

const aws = require('aws-sdk');
const ssm = new aws.SSM();exports.handler = async function(event) {
    // continued from the previous code block ...
    try {
        await validateProject(message.project);
    } catch (e) {
        throw new Error(`Invalid project ${message.project}: ${e.message}`);
    }
    // continued in the next code block ...
};async function validateProject(project) {
    const ssmPath = `/${process.env.CONFIGPATH}/${project}`
    const params = {
        Name: ssmPath,
        WithDecryption: true
    };
    const parameter = await ssm.getParameter(params).promise();
    const json = JSON.parse(parameter.Parameter.Value);
    if (!json.gitUrl) {
        throw new Error('No Git URL set');
    }
}

Наконец, мы запустим задание CodeBuild, используя сведения о событии. Lambda прочитает имя задания CodeBuild из WORKER_PROJECT_NAME переменной среды. Мы передадим в задание имя проекта и ветку Git в качестве переменных среды. Инструмент, который мы создали с помощью Go, будет читать их с помощью envconfig, как показано ранее.

const codebuild = new aws.CodeBuild();exports.handler = async function(event) {
    // continued from the previous code block ...
    await launchWorkerJob(message);
    return 'ok';
};async function launchWorkerJob(message) {
    console.log(`Triggering job for project ${message.project} on branch ${message.branch}`);
    const params = {
        projectName: process.env.WORKER_PROJECT_NAME,
        environmentVariablesOverride: [
            {
                name: 'PROJECTNAME',
                value: message.project,
            },
            {
                name: 'GITBRANCH',
                value: message.branch,
            },
        ],
    };
    const data = await codebuild.startBuild(params).promise();
    console.log(`Job started: ${data.build.id}`);
    return data;
}

Развертывание лямбда-функции

Мы можем использовать CDK для развертывания функции Lambda . Код загружается из каталога с таким именем lambda, расположенного в том же репозитории. Здесь мы передадим путь конфигурации и имя проекта CodeBuild в лямбда-код в качестве переменных среды.

import * as lambda from '@aws-cdk/aws-lambda';
const launcher = new lambda.Function(this, 'launcher', {
runtime: lambda.Runtime.NODEJS_12_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '..', 'lambda')),
description: "iwomp-in-aws launcher",
environment: {
'CONFIGPATH': configPath,
'WORKER_PROJECT_NAME': worker.projectName,
},
});

Lambda также нуждается в доступе для запуска заданий CodeBuild и чтения параметров SSM .

const configPathArn = this.formatArn({
service: 'ssm',
resource: `parameter/${configPath}/*`
});
launcher.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: [worker.projectArn],
actions: ['codebuild:StartBuild']
}))
launcher.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: [configPathArn],
actions: ['ssm:GetParameter'],
}));

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

import * as sns from '@aws-cdk/aws-sns';
import * as lambdaES from '@aws-cdk/aws-lambda-event-sources';
const jobTopic = new sns.Topic(this, 'topic', {});
launcher.addEventSource(new lambdaES.SnsEventSource(jobTopic));

Запуск развертывания из стороннего CI

Теперь у нас должен быть настроен конвейер развертывания в AWS. Теперь нам просто нужно запустить его из стороннего CI. Мы можем сделать это с помощью команды публикации SNS в интерфейсе командной строки AWS.

aws sns publish \ 
  --topic-arn "$TOPIC_ARN" \ 
  --message "{\"project\": \"$PROJECT\", \"branch\": \"$BRANCH\"}"

Это опубликует событие SNS в формате JSON, содержащее два поля projectи branch. Нам нужно указать три параметра:

  • TOPIC_ARN: ARN для темы SNS, которую мы создали ранее.
  • PROJECT: идентификатор проекта. Напримерdemo
  • BRANCH: ветка Git, из которой запускается этот конвейер.

В качестве альтернативы мы можем использовать скрипт, который самостоятельно определяет тему ARN и ветку .

Выводы

В этой статье я рассмотрел решение для автоматического запуска изменений кода инфраструктуры AWS, не доверяя сторонним поставщикам CI. Я разбил проблему на четыре ограничения и разработал решение, исходя из них. Наконец, я рассмотрел код, используемый для управления решением.

Этот пост оказался немного длиннее, чем я изначально ожидал. Однако пусть это не обескураживает вас! Там довольно много кода, чтобы настроить все это, но на самом деле нет никаких компонентов, которые работают непрерывно. Вместо этого все компоненты основаны на сервисах AWS, счета за которые выставляются в зависимости от использования, что должно снизить затраты, когда конвейер не используется активно. Именно так, как мне нравится.

Конечно, в представленном мной решении есть много возможностей для расширений. Я не рассказывал о каких-либо механизмах публикации отзывов от развертывателя. Например, средство развертывания может быть расширено для отправки результатов развертывания обратно в сторонний CI, запросы на вытягивание GitHub или в Slack. Я также опустил некоторые детали, такие как аутентификация SSH для Git.

Статья является переводом medium.com

You may also like

Leave a Comment