Запуск бессерверного JS-проекта с GitLab

Возможно, вы слышали о таких бессерверных FaaS-решениях, как AWS Lambda. Рассказываем, как это работает на примере запуска JavaScript-проекта с GitLab.

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

Хотите узнать секрет бессерверности, не выполнив ни одной команды в терминале? Всё что нужно – учётные записи GitLab и AWS.

Для начала создадим проект с помощью бессерверного шаблона. Открываем страницу нового проекта и выбираем вкладку Create from template. Прокручиваем вниз и выбираем шаблон Serverless Framework/JS.

Запуск бессерверного JS-проекта с GitLab

Называем проект, создаём с помощью Create project.

Проект создан, передаём доступ в AWS, чтобы его развернуть. Открываем консоль AWS, переходим в раздел IAM. Здесь выбираем в левой колонке Users и создаём нового пользователя с помощью кнопки Add user в верхней части списка.

Запуск бессерверного JS-проекта с GitLab

Даём пользователю имя, например, gitlab-serverless. Прежде чем нажать Next, убедитесь, что включен флажок Programmatic access.

Запуск бессерверного JS-проекта с GitLab

Теперь нужно предоставить пользователю соответствующие права для развёртывания бессерверных функций. На странице Permissions выбираем Attach existing policies directly и нажимаем Create policy – откроется новое окно.

Запуск бессерверного JS-проекта с GitLab

Здесь нужно выбрать вкладку «JSON» и вставить следующее:

 { "Statement": [ { "Action": [ "apigateway:*", "cloudformation:CancelUpdateStack", "cloudformation:ContinueUpdateRollback", "cloudformation:CreateChangeSet", "cloudformation:CreateStack", "cloudformation:CreateUploadBucket", "cloudformation:DeleteStack", "cloudformation:Describe*", "cloudformation:EstimateTemplateCost", "cloudformation:ExecuteChangeSet", "cloudformation:Get*", "cloudformation:List*", "cloudformation:PreviewStackUpdate", "cloudformation:UpdateStack", "cloudformation:UpdateTerminationProtection", "cloudformation:ValidateTemplate", "dynamodb:CreateTable", "dynamodb:DeleteTable", "dynamodb:DescribeTable", "ec2:AttachInternetGateway", "ec2:AuthorizeSecurityGroupIngress", "ec2:CreateInternetGateway", "ec2:CreateNetworkAcl", "ec2:CreateNetworkAclEntry", "ec2:CreateRouteTable", "ec2:CreateSecurityGroup", "ec2:CreateSubnet", "ec2:CreateTags", "ec2:CreateVpc", "ec2:DeleteInternetGateway", "ec2:DeleteNetworkAcl", "ec2:DeleteNetworkAclEntry", "ec2:DeleteRouteTable", "ec2:DeleteSecurityGroup", "ec2:DeleteSubnet", "ec2:DeleteVpc", "ec2:Describe*", "ec2:DetachInternetGateway", "ec2:ModifyVpcAttribute", "events:DeleteRule", "events:DescribeRule", "events:ListRuleNamesByTarget", "events:ListRules", "events:ListTargetsByRule", "events:PutRule", "events:PutTargets", "events:RemoveTargets", "iam:CreateRole", "iam:DeleteRole", "iam:DeleteRolePolicy", "iam:GetRole", "iam:PassRole", "iam:PutRolePolicy", "iot:CreateTopicRule", "iot:DeleteTopicRule", "iot:DisableTopicRule", "iot:EnableTopicRule", "iot:ReplaceTopicRule", "kinesis:CreateStream", "kinesis:DeleteStream", "kinesis:DescribeStream", "lambda:*", "logs:CreateLogGroup", "logs:DeleteLogGroup", "logs:DescribeLogGroups", "logs:DescribeLogStreams", "logs:FilterLogEvents", "logs:GetLogEvents", "s3:CreateBucket", "s3:DeleteBucket", "s3:DeleteBucketPolicy", "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:GetObject", "s3:GetObjectVersion", "s3:ListAllMyBuckets", "s3:ListBucket", "s3:PutBucketNotification", "s3:PutBucketPolicy", "s3:PutBucketTagging", "s3:PutBucketWebsite", "s3:PutEncryptionConfiguration", "s3:PutObject", "sns:CreateTopic", "sns:DeleteTopic", "sns:GetSubscriptionAttributes", "sns:GetTopicAttributes", "sns:ListSubscriptions", "sns:ListSubscriptionsByTopic", "sns:ListTopics", "sns:SetSubscriptionAttributes", "sns:SetTopicAttributes", "sns:Subscribe", "sns:Unsubscribe", "states:CreateStateMachine", "states:DeleteStateMachine" ], "Effect": "Allow", "Resource": "*" } ], "Version": "2012-10-17"
} 

Эта политика является примером, охватывающим практически всё, что может понадобиться бессерверной платформе на AWS. Но большая её часть даже не будет использована. Можно ограничить платформу в соответствии с вашими нуждами и требованиями безопасности. Как минимум понадобится доступ: cloudformation, iam, lambda, logs и функции s3 .

Запуск бессерверного JS-проекта с GitLab

Нажимаем Review Policy, придумываем имя, например, GitLabServerlessPolicy. Кликаем Create policy.

После этого возвращаемся во вкладку Add user и находим недавно созданную политику (возможно, потребуется нажать значок Refresh). Устанавливаем флажок рядом с этой политикой и нажимаем Next.

Запуск бессерверного JS-проекта с GitLab

Жмём на Next: Tags или переходим к обзору. Последняя страница должна выглядеть следующим образом:

Запуск бессерверного JS-проекта с GitLab

После нажатия кнопки Create user будет показана страницу с учётными данными для доступа к новой учётной записи AWS. Выбираем Show рядом с secret access key и копируем его вместе с ID в укромное место.

Вернёмся к GitLab. Нам нужно ввести данные в настройки CI/CD нашего проекта. Выберите Settings -> CI/CD в левом меню.

Запуск бессерверного JS-проекта с GitLab

На этой странице необходимо развернуть раздел с переменными и ввести учётные данные AWS:

Запуск бессерверного JS-проекта с GitLab

Используйте AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY в качестве ключей для двух значений, скопированных из AWS на предыдущем шаге. Не забудьте нажать кнопку Save variables.

Теперь пришло время развернуть проект. Если вы делаете это на gitlab.com, у вас уже есть доступ к GitLab раннеру на 2000 бесплатных минут CI пайплайна. Если нет, то нужно настроить раннер.

Перейдите в меню CI/CD -> Pipelines слева и нажмите Run Pipeline. Для развлечения давайте введём переменную окружения с ключом A_VARIABLE и дадим ей любое значение. Это будет использоваться нашей функцией.

Запуск бессерверного JS-проекта с GitLab

Нажимаем Run Pipeline, видим, как наши задания начинают выполняться. Этот шаблон проекта содержит тесты, которые будут автоматически стартовать при каждом запуске пайплайна. После их завершения задание «production» задеплоит код в AWS Lambda и создаст страницу на GitLab Pages. Через несколько минут процесс должен завершиться, и мы сможем посетить Settings -> Pages, чтобы увидеть ссылку, куда все развернулось.

Запуск бессерверного JS-проекта с GitLab

В результате видим там следующее:

Запуск бессерверного JS-проекта с GitLab

Введите значение и нажмите Run function. Этот инпут отправится в бессерверную функцию, результат будет выведен в поле Function Output. Обратите внимание, что здесь также присутствует значение среды, которое мы предоставили с помощью ключа A_VARIABLE.

Как насчёт простого калькулятора? Откройте Web IDE и внесите следующие изменения.

Внутри src/handler.js добавьте следующую функцию:

 module.exports.add = async function(event) { const A = Number(event.queryStringParameters.A); const B = Number(event.queryStringParameters.B); const result = A + B; return { statusCode: 200, headers: { "Access-Control-Allow-Origin": "*" }, body: result };
}; 

Откройте public/index.html и замените его на следующее:

 <!DOCTYPE html>
<html> <head> <title>GitLab Serverless Framework example</title> </head> <body> <h3>Add two values:</h3> <label>A: <input type="text" id="inputA" placeholder="0" name="A"/></label> <label>B: <input type="text" id="inputB" placeholder="0" name="B"/></label> <strong>=</strong> <span id="functionOutput">?</span> <br /> <button>Calculate!</button> <script> fetch("./stack.json").then(response => { response.json().then(myJson => { const functionUrl = myJson.ServiceEndpoint + "/add"; const inputA = document.querySelector("#inputA"); const inputB = document.querySelector("#inputB"); const output = document.querySelector("#functionOutput"); document.querySelector("button").addEventListener("click", () => { const A = Number(inputA.value); const B = Number(inputB.value); fetch(functionUrl + "?A=" + A + "&B=" + B) .then(response => response.text()) .then(result => (output.textContent = result)); }); }); }); </script> </body>
</html> 

Наконец, в serverless.yml добавьте функцию «add» под «hello»:

 functions: hello: handler: src/handler.hello events: - http: path: hello method: get cors: true add: handler: src/handler.add events: - http: path: add method: get cors: true 

Выполните коммит в главную ветку. Это автоматически приведёт к запуску нового пайплайна. Можно посетить CI/CD -> Pipelines и посмотреть, как это работает.

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

Запуск бессерверного JS-проекта с GitLab

Вуаля, мы только что создали вашу собственную бессерверную функцию и развернули её без единой команды в терминале.

Как вам туториал? Хотели бы видеть больше таких статей?

proglib.io

Добавить комментарий

Ваш e-mail не будет опубликован.

пять × 4 =