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

  • 24 января, 16:30
  • 3125
  • 0

Можливо, ви чули про такі безсерверні FaaS-рішення, як AWS Lambda. Розповідаємо, як це працює на прикладі запуску JavaScript-проекту з GitLab.

Ідея зводиться до написання коду як набору дискретних функцій, які можуть бути викликані різними подіями. Турботи про підготовку сервера, масштабування, управління внутрішнім стеком і інші операційні завдання абстрагуються. Це призводить до значної економії коштів, оскільки обчислювальні ресурси надаються на вимогу.

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

Хочете дізнатися секрет безсерверності, не виконавши жодної команди в терміналі? Все що потрібно - облікові записи GitLab і AWS.

Створення проекту

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

Називаємо проект, створюємо за допомогою Create project.

Налаштування облікового запису AWS

Проект створений, передаємо доступ в AWS, щоб його розгорнути. Відкриваємо консоль AWS, переходимо в розділ IAM. Тут вибираємо в лівій колонці Users і створюємо нового користувача за допомогою кнопки Add user у верхній частині списку.


Даємо користувачеві ім'я, наприклад, gitlab-serverless. Перш ніж натиснути Next, переконайтеся, що включений прапорець Programmatic access.

Тепер потрібно надати користувачеві відповідні права для розгортання безсерверних функцій. На сторінці Permissions вибираємо Attach existing policies directly і натискаємо Create policy - відкриється нове вікно.

Тут потрібно вибрати вкладку "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.

Натискаємо Review Policy, придумуємо ім'я, наприклад, GitLabServerlessPolicy. Клікаємо Create policy.

Після цього повертаємося у вкладку Add user і знаходимо недавно створену політику (можливо, буде потрібно натиснути значок Refresh). Встановлюємо прапорець поруч з цією політикою і натискаємо Next.

Тиснемо на Next: Tags або переходимо до огляду. Остання сторінка повинна виглядати наступним чином:

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

Введення облікових даних AWS

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

На цій сторінці необхідно розгорнути розділ зі змінними і ввести облікові дані AWS:

Використовуйте AWS_ACCESS_KEY_ID і AWS_SECRET_ACCESS_KEY в якості ключів для двох значень, скопійованих з AWS на попередньому кроці. Не забудьте натиснути кнопку Save variables.

Розгортання першої функції AWS Lambda

Тепер прийшов час розгорнути проект. Якщо ви робите це на gitlab.com, у вас вже є доступ до GitLab ранера на 2000 безкоштовних хвилин CI пайплайна. Якщо немає, то потрібно налаштувати раннер.

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

Натискаємо Run Pipeline, бачимо, як наші завдання починають виконуватися. Цей шаблон проекту містить тести, які будуть автоматично стартувати при кожному запуску пайплайна. Після їх завершення завдання "production" задеплоїть код в AWS Lambda і створить сторінку на GitLab Pages. Через кілька хвилин процес повинен завершитися, і ми зможемо перейти Settings -> Pages, щоб побачити посилання, куди все розгорнулося.

В результаті бачимо там наступне:

Введіть значення і натисніть 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 і подивитися, як це працює.

Після завершення розгортання сторінка проекту повинна виглядати наступним чином:

Вуаля, ми тільки що створили вашу власну безсерверну функцію і розгорнули її без єдиної команди в терміналі.

Джерело перекладу


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