728x90
반응형

AWS를 사용해 보니 내가 어떤 것을 사용하고 있는지 보이지가 않아서 답답할 때가 있습니다.

간단한 서버를 배포하는데도 이런데, 훨씬 복잡한 구조라면 어떻게 기억을 할까...라는 생각이 들 때

이름만 알고 있던 Terraform이 생각났고, 이런 기술들을 Iac라고 한다는 것을 알게 되었습니다.

aws에서는 CloudFormation와 CDK가 있는데 오늘은 CDK를 사용해서 간단하게 CI/CD를 구현해보려고 합니다.

 

그전에 우선 CDK 같은 기술들이 나오기까지의 과정을 살펴봅시다.

1. 관리 콘솔로 리소스 만들기

AWS 콘솔과 같이 수동으로 한 땀 한 땀 작업하여 인프라를 구성하는 방식입니다.

처음에는 모두 이렇게 하겠지만 정말 많은 수의 인프라를 구축해야 한다면 거의 불가능한 방식이 아닐까라는 생각이 듭니다...

2. 코드 형태의 명령형 인프라

AWS CLI, SDK 등을 이용하여 스크립트를 작성하여 운영 및 관리를 하는 방식입니다.

하지만 아직 리소스 관리와 구성은 수동으로 이루어지기에 오류 발생 가능성이 높고 반복적인 작업에 시간이 많이 소요됩니다.

3. 코드로서의 선언적 인프라 -> IaC(Infrastructure as Code)

AWS CloudFormation, Terraform를 이용하여 인프라스트럭처를 코드로 정의하고 관리하여, 리소스를 프로그래밍 방식으로

생성 및 업데이트하는 방식입니다. 하지만 서비스 별로 JSON, YAML 템플릿을 구성하는 방법과, 옵션에 관한 러닝 커브가 있습니다.

4. AWS CDK(Cloud Development Kit)

CDK는 주요 aws 서비스를 명령어로 정의하는 높은 수준의 객체지향 추상화를 지원합니다. 제가 이해한 바로는 CDK는

CloudFormation을 좀 더 고도화한 기술이고, CDK로 정의한 인프라는 CloudFormation 템플릿으로 변환하여 배포할 수 있습니다.

Typescript, Python, Java와 같은 많이 사용되는 프로그래밍 언어를 지원하기에 루프 및 조건문 같은 코드 논리를 자연스럽게

표현하며 인프라를 직접 지정할 수 있습니다. 

 

CDK를 사용해 CI/CD 파이프라인 구현

우선 github token을 생성해야 합니다.

1. 아래 경로로 들어가 Generate new token -> Generate new token(classic)을 선택합니다.

https://github.com/settings/tokens

 

GitHub: Let’s build from here

GitHub is where over 100 million developers shape the future of software, together. Contribute to the open source community, manage your Git repositories, review code like a pro, track bugs and fea...

github.com

 

2. Note에 원하는 이름을 넣어주고, repo와 admin:repo_hook 권한을 체크해 줍니다.

 

3. 생성된 토큰 값을 복사하고 AWS Secrets Manager에서 새 암호를 저장합니다.

Step 1) 다른 유형의 보안 암호 -> 일반 텍스트 -> 복사한 토큰 값을 넣어주고 다음을 누릅니다.

Step 2) 암호 이름을 github-token으로 넣어줍니다. 이 이름이 아니면 추가로 설정을 해야 하는 것으로 보입니다.

Step 3, 4) 넘어가줍니다.

 

그 후 터미널에서 아래 명령어들을 실행합니다.

sudo npm install -g typescript aws-cdk-lib
alias cdk="npx aws-cdk"

mkdir cdk-cicd
cd cdk-cicd
cdk init app --language typescript

 

만약 위 과정에서 에러가 나왔다면 aws와 연결이 안 되어있기 때문일 것입니다.

아래 명령어로 aws cli를 설치한 후 aws configure 명령어를 통해 연동을 해야 합니다.

curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg ./AWSCLIV2.pkg -target /
aws --version

aws configure

 

IAM 사용자에 있는 access_key(유저 ID), secret access key와 region(ap-northeast-2), output format(json)을

차례대로 넣어주면 됩니다.

 

그 후 cdk-cicd 폴더에서 아래 명령어들을 실행합니다.

esbuild는 람다를 실행할 때 필요하기 때문에 설치해 줍니다.

npm install
npm install esbuild

 

그럼 아래와 같은 구조로 파일이 생성됩니다.

bin: NestJS의 main.ts 같은 느낌입니다.

cdk.out: CloudFormation에 적용될 JSON이 저장되는 공간입니다.

lib: 사실상 메인 폴더이고 여기서 infrastructure를 정의합니다.

test: jest 테스트용 폴더입니다.

 

이제 main 브랜치에 push가 되면 CI/CD가 시작되어 간단한 람다 함수를 테스트하도록 코드를 작성해 보겠습니다.

코드에서 보이는 Stage와 Stack은 아래의 의미를 가집니다.

Stage: CDK에서 배포 단계를 나타내는 개념입니다. 예를 들어 develop, test, prod 같은 이름으로 지정할 수 있습니다.

Stack: CDK에서 인프라 리소스의 논리적 그룹을 나타내는 단위입니다. 스택은 하나 이상의 AWS 리소스를 정의하고,

그룹핑, 배포 및 관리를 쉽게 할 수 있도록 도와줍니다.

 

lib/cdk-cicd-stack.ts

소스 코드 변경을 감지하고 CI/CD 파이프라인을 실제로 실행시켜 주는 코드입니다.

import * as cdk from "aws-cdk-lib";
import {
  CodeBuildStep,
  CodePipeline,
  CodePipelineSource,
  ShellStep,
} from "aws-cdk-lib/pipelines";
import { Construct } from "constructs";
import { PipelineStage } from "./PipelineStage";

export class CdkCicdStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, "MyPipeline", {
      pipelineName: "MyPipeline",
      synth: new ShellStep("Synth", {
        // input: CodePipelineSource.gitHub('kimjinho1/cdk-cicd-practice', 'cdk-cicd-practice'),
        input: CodePipelineSource.gitHub("kimjinho1/cdk-cicd-practice", "main"),
        commands: ["cd cdk-cicd", "npm ci", "npx cdk synth"],
        primaryOutputDirectory: "cdk-cicd/cdk.out",
      }),
    });

    const testStage = pipeline.addStage(
      new PipelineStage(this, "PipelineTestStage", {
        stageName: "test",
      })
    );

    testStage.addPre(
      new CodeBuildStep("unit-tests", {
        commands: ["cd cdk-cicd", "npm ci", "npm test"],
      })
    );
  }
}

위 코드에서 input 부분은 본인의 레포지터리 이름과 브랜치 이름을 넣어주셔야 합니다.

input: CodePipelineSource.gitHub("kimjinho1/cdk-cicd-practice", "main"),

 

lib/PipelineStage.ts

CodePipeline 스테이지를 구성하는 코드입니다.

import { Stage, StageProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { LambdaStack } from "./LambdaStack";

export class PipelineStage extends Stage {
  constructor(scope: Construct, id: string, props: StageProps) {
    super(scope, id, props);

    new LambdaStack(this, "LambdaStack", {
      stageName: props.stageName,
    });
  }
}

 

lib/LambdaStack.ts

Lambda 함수를 정의하고 생성하는 코드입니다.

import { Stack, StackProps } from "aws-cdk-lib";
import { Runtime } from "aws-cdk-lib/aws-lambda";
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
import { Construct } from "constructs";
import { join } from "path";

interface LambdaStackProps extends StackProps {
  stageName?: string;
}

export class LambdaStack extends Stack {
  constructor(scope: Construct, id: string, props: LambdaStackProps) {
    super(scope, id, props);

    new NodejsFunction(this, "hello-lambda", {
      runtime: Runtime.NODEJS_18_X,
      handler: "handler",
      entry: join(__dirname, "..", "lambda", "hello.ts"),
      environment: {
        STAGE: props.stageName!,
      },
    });
  }
}

 

lambda/hello.ts

실행되는 람다 코드입니다.

async function handler(event: any, context: any) {
  return {
    statusCode: 200,
    body: "Hello AWS",
  };
}

export { handler };

 

test/cdk-cicd.test.ts

람다 함수를 테스트하는 코드입니다.

import { handler } from "../lambda/hello";

describe("Hello describe test suite", () => {
  test("handler should return 200", async () => {
    const result = await handler({}, {});
    expect(result.statusCode).toBe(200);
  });
});

 

이제 cdk deploy 명령어를 실행하면 설정한 AWS 서비스들이 CloudFormation으로 자동으로 세팅이 됩니다.

cdk deploy

CodePipeline 내부에서 실행된 결과를 확인할 수 있습니다. 생각보다 시간이 오래 걸립니다. 4분? 정도

CodePipeline
unit-tests

 

친숙한 프로그래밍 언어로 인프라를 구축할 수 있다는 점이 개발자 친화적이어서 아주 유망한 기술인 것 같습니다만,

그전에 aws에 대해 조금 더 친숙해지고 나서 활용해야겠다는 생각이 들었습니다.

아무리 러닝 커브가 내려갔다고 해도 쉽지는 않은 것 같습니다...

그리고 의아한 점은 CodePipeline이 생각보다 엄청 느리다는 점입니다. 이렇게 간단한 것을 실행하는데도 5분 정도가 걸립니다.

현재 진행하고 있는 프로젝트에서 CodePipeline을 사용할 생각이었는데, github actions과 비교해봐야 할 것 같습니다.

728x90
반응형

+ Recent posts