목록 보기
Github Actions의 의존성 캐싱을 통한 서버 배포속도 향상
기타

Github Actions의 의존성 캐싱을 통한 서버 배포속도 향상

핏펫
핏펫
2022년 7월 28일

많은 분이 CI/CD 속도에 많은 골머리를 앓고 있을 거로 생각합니다. 핏펫에서도 느린 빌드 시간으로 인해 많은 골머리를 앓고 있었습니다. 느린 빌드는 개발자의 피로도를 증가시키게 되고 기다리는 시간이 증가하게 되어 개발하는 속도에도 영향을 미쳐 악순환을 반복하게됩니다. 서버의 규모가 점점 커지게 되면서 눈덩이처럼 문제가 커져만 갔습니다. 규모가 커져 속도는 더욱 느려졌고 보다 많아진 개발자분들이 불편함을 호소하게되었습니다. 예를 들어 5분 정도의 배포 시간이 걸린다고 가정합니다. 하루에 30번 배포를 진행한다고 했을 때 대략 150분이 소요됩니다. 단순히 배포만 하는 데 있어 2시간이 넘는 시간을 쏟게됩니다. 위와 같은 개발자가 겪고 있는 악순환을 제거 제거하기 속도 개선을 진행하고자하였습니다. TLDR Github Actions의 종속성 캐싱을 통하여 빌드속도향상 테스트 환경 저희 서버와 비슷한 환경을 구축하기 위해 테스트 서버를구축하였습니다. 현재는 AWS ECS로 제공되고 있습니다. ECS는 정말 간단하고 쉽게 Container Orchestration을 제공할 수 있는 서비스로서 빠르게 서버를 제공할 수 있는 좋은서비스입니다. 전체적으로 간단한 Java 코드를 생성 후 Terraform으로 서버 환경 구축 후 GitHub Actions로 배포하는 구조를 만들어보았습니다. 모든 코드는 아래 Github를 통해 확인가능합니다. https://github.com/b100to/blog-test Architecture 테스트 프로젝트생성 Terraform AWS ECSFargate Java11 — Gradle Git Actions IaC, Terraform을 통한 ECS 테스트 환경구성 테라폼을 통하여 간단하게 테스트 환경을구성하였습니다. Terraform Code version.tf versions.tf # AWS 프로바이더 선언 및 리전, 프로파일 세팅 provider "aws" ( region = "ap-northeast-2" profile = "dev" ) #AWS 프로바이더 버전 지정 terraform ( required_providers ( aws = ( source = "hashicorp/aws" version = Ř.22.0" ) ) ) data.tf data.tf #AWS ID data "aws_caller_identity" "current" () #기존 VPC data "aws_vpc" "this" ( id = var.vpc_id ) #기존 퍼블릭 서브넷a data "aws_subnet" "public1" ( id = var.public_subnet1 ) #기존 퍼블릭 서브넷c data "aws_subnet" "public2" ( id = var.public_subnet2 ) #기존 프라이빗 서브넷a data "aws_subnet" "private1" ( id = var.private_subnet1 ) #기존 프라이빗 서브넷c data "aws_subnet" "private2" ( id = var.private_subnet2 ) #기존 SG data "aws_security_group" "this" ( id = var.sg_id ) networks.tf networks.tf #TG 생성 - 서비스 핼스체크 및 private IP 테이블 역할(?) 담당 resource "aws_lb_target_group" "this" ( name = "test-blog123" vpc_id = data.aws_vpc.this.id target_type = "ip" load_balancing_algorithm_type = "round_robin" port = 80 deregistration_delay = ř" protocol = "HTTP" protocol_version = "HTTP1" slow_start = 0 tags = () health_check ( protocol = "HTTP" port = "traffic-port" path = "/" enabled = true matcher = 薘" interval = 5 timeout = 3 healthy_threshold = 2 unhealthy_threshold = 2 ) stickiness ( cookie_duration = 86400 enabled = false type = "lb_cookie" ) ) #ALB 생성 - 자체 생성 된 DNS를 통하여 HTTP로 외부와 인터넷 연결 가능 resource "aws_lb" "this" ( name = "test-blog123" load_balancer_type = "application" ip_address_type = "ipv4" desync_mitigation_mode = "defensive" subnets = [ data.aws_subnet.public1.id , data.aws_subnet.public2.id ] security_groups = [ data.aws_security_group.this.id ] idle_timeout = 60 drop_invalid_header_fields = false enable_deletion_protection = false enable_http2 = true enable_waf_fail_open = false internal = false tags = () ) #LB 리스너 생성 - LB과 TG을 연결 시켜준다. resource "aws_lb_listener" "this" ( load_balancer_arn = aws_lb.this.arn port = 80 protocol = "HTTP" tags = () default_action ( target_group_arn = aws_lb_target_group.this.arn type = "forward" ) ) main.tf main.tf #ECS 클러스터 생성 resource "aws_ecs_cluster" "this" ( name = "blog-test" tags = () tags_all = () setting ( name = "containerInsights" value = "disabled" ) ) #ECR 생성 resource "aws_ecr_repository" "this" ( image_tag_mutability = "MUTABLE" name = "blog-test" tags = () image_scanning_configuration ( scan_on_push = false ) ) #테스크 정의 생성 - Task 기본 스펙 설정 resource "aws_ecs_task_definition" "this" ( family = "test-blog" cpu = 蛐" memory = 񓐠" network_mode = "awsvpc" runtime_platform ( cpu_architecture = "X86_64" operating_system_family = "LINUX" ) requires_compatibilities = [ "FARGATE", ] task_role_arn = "arn:aws:iam::$(data.aws_caller_identity.current.account_id):role/CloudEcsTaskRole" execution_role_arn = "arn:aws:iam::$(data.aws_caller_identity.current.account_id):role/ecsTaskExecutionRole" container_definitions = jsonencode( [ ( name = "app" image = "sha256:72b7e95ac29f46e2e53a97413fcfdfe3f7599136d5bc745ffb60a65f464d565e" portMappings = [ ( containerPort = 8080 hostPort = 8080 protocol = "tcp" ), ] cpu = 0 environment = [] essential = true logConfiguration = ( logDriver = "awslogs" options = ( awslogs-group = "/ecs/test-blog" awslogs-region = "ap-northeast-2" awslogs-stream-prefix = "ecs" ) ) mountPoints = [] volumesFrom = [] ), ] ) tags = ( "ecs:taskDefinition:createdFrom" = "ecs-console-v2" "ecs:taskDefinition:stackId" = "arn:aws:cloudformation:ap-northeast-2:$(data.aws_caller_identity.current.account_id):stack/ECS-Console-V2-TaskDefinition-4bcedecd-24f5-4570-8d0c-936d1acd52cc/1c346740-fcd6-11ec-bfdb-02068544a002" ) ) #ECS 서비스 생성 resource "aws_ecs_service" "this" ( name = "test-blog123" cluster = aws_ecs_cluster.this.arn iam_role = "aws-service-role" desired_count = 1 health_check_grace_period_seconds = 60 deployment_minimum_healthy_percent = 50 deployment_maximum_percent = 200 task_definition = "$(aws_ecs_task_definition.this.id):$(aws_ecs_task_definition.this.revision)" tags = () # 스팟 비중을 어느정도 설정할지 지정합니다. capacity_provider_strategy ( base = 0 capacity_provider = "FARGATE_SPOT" weight = 1 ) # LB 세팅 load_balancer ( container_name = "app" container_port = 8080 target_group_arn = aws_lb_target_group.this.arn ) # 네트워크 세팅 network_configuration ( assign_public_ip = true security_groups = [ data.aws_security_group.this.id ] subnets = [ data.aws_subnet.private1.id, data.aws_subnet.private2.id ] ) ) Java DockeFile 간단히 Intelli를 통하여 많이들 사용하시는 Gradle 빌드 도구를 이용하여 Java HelloWorld를 생성하였습니다. 이후 Dockerfile을 이용하여 Dockerizing 작업을진행하였습니다. Dockerfile #상용에서는 빌드 속도를 위해 Alpine 이미지을 사용합니다. FROM openjdk:11 RUN addgroup --system spring && adduser --system spring USER spring:spring COPY ./build/libs/*-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/app.jar"] Github Actions를 통하여 AWS ECS CI/CD 속도 향상시키기. Github Actions는 Workflow가 매번 새로 셋업이 됩니다. 그로 인해 로컬과 달리 캐시 지원을 할 수 있도록 세팅을 해줘야 합니다. 로컬에서 작업하는 것과 같이 캐시를 사용하려면 Github의 cache Action을 사용해야 합니다. 이 Action은 고유키를 이용하여 캐시를 식별하고가져옵니다. 사용 예시 deploy_test.yml name: deploy_test on: push: branches: - main #main 브랜치에서 시작. #UI에서 재시작 가능. workflow_dispatch: #전역 변수 지정 env: ECS_CLUSTER_NAME: blog-test ECS_SERVICE_NAME: test-blog123 IMAGE_NAME: blog-test jobs: Build: runs-on: ubuntu-latest # 사용할 OS를 선택합니다. steps: - uses: actions/checkout@v3 # 사용할 Gradle 설치 및 버전 지정합니다. - name: Setup Gradle uses: gradle/gradle-build-action@v2 with: gradle-version: wrapper # JDK 버전 및 Gradle 빌드합니다. - name: Setup Java JDK uses: actions/setup-java@v3 with: distribution: temurin java-version: 11 cache: gradle # 종속성 제거를 위한 캐쉬 지원 가능. - run: ./gradlew --no-daemon build --stacktrace # Deploy Job에서 넘겨 받도록 업로드합니다. - name: Upload artifact uses: actions/upload-artifact@v2 with: name: app path: build/libs Deploy: runs-on: ubuntu-latest needs: build # Build Job먼저 작업 후 진행하도록 합니다. steps: - name: Checkout uses: actions/checkout@v3 # 위 빌드한 Build job에서 Jar파일을 가저옵니다. - name: Download app uses: actions/download-artifact@v2 with: name: app path: build/libs # Docker Builx를 사용하도록 지정합니다. - name: Set up Docker Buildx id: buildx uses: docker/setup-buildx-action@v2 with: install: true # 고유키를 생성합니다. - name: Cache Docker layers uses: actions/cache@v2 with: path: /tmp/.buildx-cache key: $(( runner.os ))-buildx-$(( github.sha )) restore-keys: | $(( runner.os ))-buildx- # ECR에 로그인 할 수 있도록 Credentials를 지정합니다. - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: $(( secrets.DEV_AWS_ACCESS_KEY_ID )) aws-secret-access-key: $(( secrets.DEV_AWS_SECRET_ACCESS_KEY )) aws-region: ap-northeast-2 # ECR 로그인 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 # Short SHA를 생성합니다. - uses: benjlevesque/short-sha@v1.2 id: short-sha with: length: 6 - run: echo $SHA env: SHA: $(( steps.short-sha.outputs.sha )) - run: echo $SHA env: SHA: $(( env.SHA )) # docker Build 및 Push를 합니다. - name: Build and push id: docker-build uses: docker/build-push-action@v2 with: push: true # 이미지를 푸쉬합니다. tags: $(( steps.login-ecr.outputs.registry ))/$(( env.IMAGE_NAME )):$(( env.SHA )) cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new # 오래된 캐쉬를 날립니다. - name: Move cache run: | rm -rf /tmp/.buildx-cache mv /tmp/.buildx-cache-new /tmp/.buildx-

댓글 0

댓글을 작성하려면 로그인이 필요합니다.

댓글을 불러오는 중...