재형이의 성장통 일지
  • ECS를 활용한 웹 애플리케이션 배포 - 개인프로젝트
    2023년 08월 16일 15시 03분 39초에 업로드 된 글입니다.
    작성자: 재형이
    반응형

    📢 양조절에 실패...해버렸습니다...

    목차와 오른쪽 밑에 맨위로(TOP∧) 버튼을 활용해주세요...

     

     

    목차

       

       

       

       

       

      웹 애플리케이션 아키텍쳐


      ECS 개요

      • Amazon Elastic Container Service(Amazon ECS)는 컨테이너화된 애플리케이션을 쉽게 배포, 관리, 스케일링할 수 있도록 도와주는 완전 관리형 컨테이너 오케스트레이션 서비스입니다

      ECS 구성 요소

      1. 클러스터
        : 컨테이너를 실행할 수  있는 가상 공간. 논리적으로 묶인 그룹을 의미. 보통 프로젝트 단위
        ex) 쿠버네티스에서 pod와 비슷
      2. 작업
        : 생성하는 컨테이너를 구동하는 최소 단위, 하나의 task 내에 여러 컨테이너가 있을 수 있음
      3. 작업정의
        : task를 실행할 때, 컨테이너 네트워크 모드, task 역할, 도커 이미지, cpu 메모리 제한 등 설정이 필요한데, 이런 설정을 매번 하는 것이 아니라 하나의 집합 단위로 두고 필요할 때 사용할 수 있게 해줌. 이 것을 task definition이라고 함
      4. 서비스
        : cluster는 두가지 방식으로 task를 실행할 수 있는데, 하나가 task definition으로 직접 task 실행, 다른 하나는 service를 정의하여 여러 task를 동시에 실행
        ex) 쿠버네티스에서 deployment와 비슷

      ECS vs EKS

      • EKS는 쿠버네티스의 기능을 빌려다 사용함
      • ECS는 자체 AWS 기능 활용
      • ECS는 AWS 리소스와 연동이 더 용이함
      • 정리하자면 ECS는 EKS 대비 AWS 서비스와 자원을 더 활용하고, EKS는 kubernetes가 지원하는 환경을 더 활용한다는 차이점이 있다

      요구사항

      • 다수의 사용자가 이용할 것을 고려해 유연하게 확장 가능한 구성
      • 가용성을 높이기 위해 다중 AZ를 기본 구성
      • CI/CD 파이프라인을 구성해 애플리케이션를 빠르게 배포
      • 각 계층별 적절한 보안 대책(침입 방지, 인증 데이터의 적절한 관리, 로그 저장, 서버를 통한 내부 접근 방지 등) 마련

      1. 운영 설계

      1-1. 로깅 설계

      • ECS/Fargate 구성에서 애플리케이션 로그를 수집하는 방법으로는 'FireLens'라는 로그 통합 관리 기능을 사용한다
      • FireLens를 이용하면 CloudWatch Logs 외에도 AWS 서비스나 AWS가 아닌 SaaS에 로그를 쉡게 전송할 수 있고 Firehose와 연계해 S3나 Redshift, OpenSearch Service에도 로그를 전송할 수 있다는 장점이 있다
      • FireLens에서는 로그 라우팅(Log routing) 기능을 담당하는 소프트웨어로 오픈 소스인 'Fluentd' 또는 'Fluent Bit'를 선택할 수 있다. 'Fluent Bit'는 'Fluentd'에 비해 플러그인이 적지만 자원 효율이 좋고 AWS도 Fluent Bit를 사용할 것을 추천하고 있다
      • Fluent Bit를 이용하는 경우 AWS가 공식적으로 제공하는 전용 컨테이너 이미지를 사용할 수도 있다. ECS 테스크 정의 안에 애플리케이션용 컨테이너와 Fluent Bit 컨테이너를 함께 넣는 구성이 된다. 컨테이너 디자인 패턴의 하나인 사이드카 구성으로 Fluent Bit 컨테이너를 경유해 로그 수집을 할 수 있다

      FireLens를 이용한 로그 운영

      1-2. CI/CD 설계

      • 애플리케이션을 실행하기 위해서는 빌드, 테스트, 배포와 같은 절차가 필요하지만, CI/CD를 구축해 이 절차를 자동화할 수 있다
      • 테스트를 하고 자동으로 프로덕션 환경에 배포가 가능하게 만들어 비즈니스의 민첩성을 비약적으로 높일 수 있다
      • CI/CD를 사용하면 변경 사항을 자동으로 릴리스할 수 있게 되므로 지금까지 빌드 및 배포에 소모된 시간을 애플리케이션 개발에 집중할 수 있다. 그리고 테스트 자동화를 통합해 신속하게 문제를 발견할 수 있으므로 품질 개선 효율화도 기대할 수 있다

      CI/CD

      1-3. 이미지 유지 보수 운영

      • 빌드된 컨테이너 이미지를 ECR에 저장하면 이미지의 내구성이나 확장성에 신경 쓰지 않고 ECS와 연계할 수 있다.
        ECR에 저장된 컨테이너 이미지는 실제로는 S3에 저장된다
      • S3는 우수한 데이터 내구성을 가지고 있어 안전하게 데이터를 보존할 수 있다. 그리고 컨테이너 이미지는 소스 코드와 Dockerfile만 있다면 언제든 다시 빌드할 수 있다

      ECR을 이용한 컨테이너 이미지 관리

      • ECR은 저장한 컨테이너 이미지 용량만큼 요금이 발생한다. 내부적으로 S3를 이용하지만, S3보다 요금이 비싸 과거 이력까지 모두 영구 보존할 경우 비용이 많이 든다
      • 컨테이너 이미지는 적절히 관리해야 한다
      • ECR에는 '수명 주기 정책'이라는 기능이 있다. 컨테이너 이미지를 일정 기간만 이용하고 싶다거나 지정한 세대만 저장하고 싶을 때 이 기능을 이용한다

      ECR 수명 주기 정책

      1-4. Bastion 설계

      • 장애 분리를 목적으로 내부 네트워크에서 애플리케이션 통신을 확인하거나 DB인스턴스에 로그인해서 데이터베이스를 정비해야 하는 경우가 있다
      • 이런 요구사항을 충족하기 위해 외부 네트워크와 내부 네트워크를 연결하기 위해 일반적으로 'Bastion(Jump Server)'을 이용한다
      • 운영담당자가 Bastion을 통해 작업을 하면 불특정 다수로부터의 내부 접속을 막을 수 있으며 내부 작업에 대한 작업 기록도 취득할 수 있다

      EC2 인스턴스에 Bastion 구성

      📢 ECS/Fargate를 중심으로 하는 구성에서 EC2를 Bastion으로 구축할 때 주의할 점

      • 베스쳔 호스트는 일단 누구나 접속할 수 있는 상태가 된다
      • EC2 인스턴스의 OS 관리라는 운영 부담이 발생한다
      • 이 문제점은 다음 두 가지를 만족하는 아키텍처로 해결할 수 있다
      1. 베스쳔 호스트를 프라이빗 서브넷에 두어 공격 지점(Attack surface)을 줄인다
      2. 베스쳔 호스트를 ECS/Fargate 구성으로 하여 OS 관리를 AWS에 위탁한다

      Fargate와 세션 메니저를 활용한 Bastion 구성

      2. 보안 설계

      2-1. 이미지에 대한 보안 대책

      • 생성된 컨테이너 이미지에는 애플리케이션뿐만 아니라 애플리케이션 실행에 필요한 라이브러리 등이 포함돼 있다.
        한편 소프트웨어 취약점은 매일 새로 공개되고 있다. 컨테이너 이미지에 포함된 라이브러리는 시간이 지나면 버전 취약점 등에 해당될 수 있다
      • 취약점을 내재하고 있는 애플리케이션을 방치하는 것은 비지니스 보안 관점에서 바람직하지 않다.
        그리고 발견된 취약점도 중요도에 따라 긴급 대응을 하지 않으면 비즈니스에 막대한 영향을 끼치는 경우도 있다.
        컨테이너를 안전하게 이용하기 위해서는 컨테이너 이미지에 어떤 취약점이 있는지 주기적으로 스캔해서 취약점을 제거하는 것이 중요하다
      • 컨테이너의 취약점 유무를 확인하기 위해 ECR 이미지 스캔과 OSS(Open Source Software)인 Trivy를 활용한 대책을 검토한다
      1. ECR을 이용한 취약점 스캔
        : ECR에는 푸시된 컨테이너 이미지의 취약점을 스캔하는 기능이 있다. 레지스트리로 ECR을 이용하는 경우 이 기능을 활성화하면 취약점 존재 여부를 간단하게 알 수 있다
        • 푸시 시 스캔(Scan on push)
        • 수동 스캔(Manual scan)
        ECR에서 이미지를 스캔하는 방법은 2가지가 있다.
        ECR 이미지 스캔은 추가 비용없이 이용할 수 있다. ECR 리포지토리를 만들 때 '푸시 시 스캔'을 활성
      2. Trivy를 이용한 취약점 스캔
        : Trivy는 Teppei Fukuda(@knqyf263)가 개인적으로 개발했고, 이후 AquaSecurity가 이어서 개발하고 있는 OSS다.
        영국 정부 디지털 서비스에서도 활용하고 있으며 GitLab의 컨테이너 이미지 스캐너로 도입됐다. Red Hat의 인증 스캔 도구이기도 하다.
        현재는 컨테이너 보안 수준을 높이기 위해서 반드시 이용해야 하는 도구로 자리매김하고 있다.
        Trivy의 우수한 특징 중 하나는 스캔 대상이다. 기본 이미지에 포함된 OS 패키지뿐만 아니라 Python(pip)과 Ruby(gem), Node.js(npm, yarn) 등 애플리케이션 종속성도 스캔 대상이다. 지원하는 컨테이너의 OS도 다음과 같이 다양하다.
        • Alpine
        • Red Hat Enterprise Linux
        • CentOS
        • Oracle Linux
        • Debian GNU/Linux
        • Ubuntu
        • Amazon Linux
        • OpenSUSE Leap
        Trivy는 실행 방법도 매우 간단하며, CI/CD 파이프라인과도 쉽게 통합할 수 있다. CodeBuild 워크플로 파일에 기입하는 것만으로 쉽게 스캔할 수 있다.
      3. 지속적인 스캔을 위한 설계
        : 컨테이너 이미지의 취약점 대책에서 중요한 점은 자동으로 정기적으로 스캔을 실행해야 한다는 것이다.
        이미지를 생성할 당시에는 최신 버전이었어도 몇 개월 후 새로운 취약점이 발견되는 등 대책 마련이 필요해질 수 있다

      지속적인 이미지 스캔을 고려한 CI/CD 파이프라인 구성

      2-2. '이미지 설정'에 대한 대책

      • Docker로 컨테이너 이미지를 빌드하는 경우 Dockerfile 내용에 따라 처리가 이루어진다. 이 Dockerfile은 개발자가 작성하지만 작성 방법이나 개발자의 수준에 따라서는 보안상 바람직하지 않은 구성이 될 수도 있다
      • 예를 들어 애플리케이션이 root 권한으로 실행된다면 시스템 영역에 파일 쓰기나 프로세스 조작 등이 가능하기 때문에 침해 사고가 발생하면 더 큰 영향을 받을 수 있다
      • 이때 도움이 되는것이 Dockle이다
      • Dockle은 Tomaya Amachi(@tomoyamachi)가 개발한 오픈 소스 소프트웨어로 컨테이너 설정 내용을 확인해주는 도구다
      • Dockle을 이용하면 전체적인 컨테이너 이미지 확인이 가능해 안전한 컨테이너 개발에 도움이 된다
        Trivy와 마찬가지로 Dockle도 빌드된 이미지를 지정하는 것만으로 간단하게 실행할 수 있다. CI/CD 프로세스에 넣으면 지속적인 이미지 설정 확인이 가능하다

      2-3. '악성코드 포함'에 대한 대책

      • 신뢰할 수 있는 제공자가 제공한 기본 이미지 이용
      • 검증이 불충분한 외부 컨테이너 이미지를 이용하는 것은 취약점이나 악성 코드가 포함될 위험성이 있다. 직접 빌드하고 충분한 테스트를 거친 이미지를 이용해 위험성을 없애야 한다
      • 기본적인 대책은 환경에 맞는 신뢰할 수 있는 이미지 및 리포지토리를 한 곳에서 관리하는 것이다. AWS에서는 관리형 리포지토리 서비스인 ECR을 이용하면 이미지를 한곳에서 쉽게 관리할 수 있다
      • 추가 대책으로 효과적인 것은 이미지의 서명을 검증하는것이다. Docker에는 기본 이미지 등이 변조된 것을 탐지하기 위한 DCT(Docker Content Trust)라는 기능이 있다. 이를 통해 컨테이너 이미지에 다이제스트 서명을 추가해 레지스트리에 저장된 이미지와 일치하는지 검증할 수 있다
      • 예를 들어 도커 허브에 서명된 기본 이미지가 공개돼 있다면 DCT를 활성화해서 사전에 이미지 변조 여부를 확인할 수 있다

      2-4. '평문 기밀 정보 포함'에 대한 대책

      • 애플리케이션을 개발할 때 데이터 베이스 접속 정보나 서드파티 API를 호출하기 위한 API 키와 같은 기밀 정보를 취급하는 경우가 종종 발생한다
      • 이런 기밀 정보를 소스 코드나 이미지 내에 평문으로 저장하면 이미지에 접근 가능한 사람이라면 누구라도 취득할 수 있다. 그렇기 때문에 기밀 정보는 이미지 밖의 안전한 영역에 저장하고 컨테이너를 실행할 때 동적으로 제공되게 설정하는 것이 바람직하다
      • ECS/Fargate에서는 Secrets Manager 또는 System Manager(구 SSM) Parameter Store에 기밀 정보를 저장하고 환경 변수로 컨테이너 안에 안전하게 기밀 정보를 전달하는 방법을 제공한다
        기밀 정보가 저장된 Secrets Manager의 ARN과 환경 변수명을 태스크 정의 안에서 매핑해서 컨테이너 이미지의 OS 환경 변수로 인식시킬 수있다

      Secrets Manager를 이용해 컨테이너에 기밀 정보를 전달

      2-5. 레포지토리에 대한 보안 대책

      레포지토리에 대한 보안 대책

      1. '레지스트리 내의 오래된 이미지'에 대한 대책
        • ECR에서는 수명 주기 정책을 적절히 설정해 버전 관리를 간단히 자동화할 수 있다. 운영과 비용 관전뿐만 아니라 보안 관점에서도 오래된 이미지는 적절히 삭제해야 한다
        • ECR에서는 리포지토리에 푸시된 태그와 동일한 태그를 가진 이미지를 푸시하면 기존에 있던 이미지의 태그는 삭제되며, 이미지의 버전 정보가 사라 진다. 만약 ECR에 배포된 ECS 태스크가 삭제된 버전 정보를 참조하고 있다면 정합성이 없어질 위험성이 있다
        • ECR에는 이미지 태그 덮어쓰기를 금지하는 IMMUTABLE(변경 불가능) 설정을 할 수 있다. 컨테이너 이미지의 일관성을 유지하고 잘못된 이미지가 푸시되는 것을 막기 위해 활성화하는 것이 좋다
      2. '불충한 인증, 인가 제한'에 대한 대책
        • 프라이빗 리포지토리 선택

      2-6. 오케스트레이터에 대한 보안 대책

      • '제한 없는 관리 접속'에 대한 대책
        : ECS는 컨테이너 워크로드를 구현하는 중심이므로 모두에게 ECS 관리 접속을 허용하는 것은 바람직하지 않다.
        예를 들어 직무별로 IAM 계정에 그룹을 지정하고, 각 그룹에 부여한 IAM 정책에 따라 ECS 조작 범위를 한정하면 관리 접속을 제한할 수 있다.
        또한 각 팀의 업무에 따라 IAM 그룹을 지정하고, 업무와 관련된 ECS 클러스터를 매핑하면 업무 단위로 권한을 분리하는 것도 가능하다.

      IAM 정책을 통해 직무를 고려한 ECS 클러스터 접속 제한

      2-7. 컨테이너에 대한 보안 대책

      • ECS/Fargate에서 실행하는 ECS 태스크는 모두 VPC 위에 배치된다. ECS 태스크에 대한 네트워크 연결 방법은 몇 가지가 있으며, 이를 '네트워크 모드'라고 한다. Fargate에서 호스팅되는 ECS 태스크는 'awsvpc'라는 네트워크 모드가 선택된다
      • ECS 태스크 별로 고유한 ENI(Elastic Network Interface)가 할당되고, ENI에 프라이빗 IPv4 주소가 할당된다
      • ECS/Fargate 네트워크 보안은 AWS VPC 전체 모습을 생각하며 설정해야 함

      📢 보안 대책

      1. AWS에서는 관리형 WAF(Web Application Firewall) 서비스를 제공한다. WAF를 활용하면 애플리케이션에 대한 보안 대책이 가능하다. 하지만 WAF와 연계 가능한 서비스는 다음 3가지밖에 없다.
        • CloudFront
        • ALB(Application Load Balancer)
        • API Gateway(HTTP API는 대응하지 않음)
        그렇기 때문에 인터넷을 경유하는 ECS 태스크 통신에 WAF를 도입하기 위해서는 위 3가지 서비스와의 연계를 고려해 설계해야 한다
        대표적인 구성은 ALB를 이용하는 것이다. ALB를 퍼블릭 서브넷에 배치하고 ECS 태스크를 프라이빗 서브넷에 배치하면 가용성 향상과 함께 보안도 향상시킬 수 있다
      2. ECS 태스크 간 네트워크에 대해서는 ECS 태스크와 ALB에 연결된 보안 그룹 규칙을 적절히 설정하면 간단히 통신을 제어할 수 있다
      3. 프라이빗 서브넷에 ECS 태스크를 배치하면서도 퍼블릭 네트워크와 통신이 가능하도록 NAT 게이트웨이를 이용한 네트워크 설계

      3. 안정성 설계

      Well Architected 프레임워크 설계 원칙 중 안정성 파트에 대한 핵심 키워드들을 추려보았다.

      1. 장애 복구
      2. 가용성
      3. 자동

      을 중점으로 안정성 설계 진행

      3-1. 다중 AZ 구성을 통한 가용성 향상

      다중 AZ로 ECS 태스크 배치를 하는 구성

      • Fargate로 ECS 서비스를 실행시키면 ECS 서비스 내부의 스케줄러가 최적화 작없을 통해 AZ 부하 균형을 조절하며 ECS 태스크를 배치한다. 따라서 이용자는 태스크를 AZ에 어떻게 배치할지에 대한 고민을 하지 않아도 된다

      3-2. 장애 시 전환 및 복구

      1. CloudWatch를 활용한 ECS 태스크 장애 탐지

      Cloudwatch 지표

      • ECS 태스크 장애는 RunningTaskCount 지표 또는 TaskCount 지표와 CloudWatch 알람의 조합으로 탐지할 수 있다. 참고로 RunningTaskCount 지표와 TaskCount 지 표의 차이는 CloudWatch의 차원(dimension)이다
      • 실행 중인 태스크가 ECS 서비스에 소속돼 있는지 독립형인지에 따라 대상이 되는 지표가 달라진다는 점에 주의해야 한다

      2. ECS 서비스를 통한 ECS 태스크의 자동 복구

      • ECS 서비스는 ECS 서비스를 만들 때 지정한 태스크 수를 유지하려고 한다. 그렇기 때문에 정지된 ECS 태스크는 자동으로 실행된다
      • 가장 중요한 기능을 담당하는 시스템이라면 안정적인 동작과 더불어 데이터의 불일치에도 신경을 써야 한다. 이런 시스템에서 ECS 태스크가 1개 정지했을 때 데이터 정합성에 문제가 없는지 곧바로 확인해야 하는 경우도 생긴다. 이런 경우라면 ECS 태스크 변동과 관련된 알림 통지가 필요하다

      3. Fargate 작업 유지 관리로 인한 ECS 태스크 정지

      • ECS 태스크 자체의 장애뿐 아니라 제어 플레인이 ECS 태스크 정지를 지시할 때의 처리에 대해서도 고려해야 한다
      • ECS에서는 ECS 태스크 정지를 지시할 때 대상 ECS 태스크에 대해 SIGTERM 시그널을 전송한다. 애플리케이션이 SIGTERM에 대해 응답하지 않는 경우 기본적으로 30 초를 기다린 후 SIGKILL 시그널이 전송된다
      • 애플리케이션이 SIGTERM을 핸들링하도록 구현하지 않았다면 SIGKILL을 통해 강제 종료된다. 즉, 애플리케이션 내부에서 처리 중인 내용이 갑자기 종료되기 때문에 데이터가 손실되거나 문제가 발생할 가능성이 있다
      • ECS/Fargate를 이용한 서비스를 구축할 때 애플리케이션이 SIGTERM 시그널을 받아 안전하게 종료될 수 있게 구현하는 것도 염두에 둬야 한다

      3-3. 시스템 유지 보수를 위한 서비스 정지

      • 애플리케이션이나 시스템 구성, 비즈니스 관련 이유로 배포할 대 시스템을 점검해야 하는 경우가 있다.
        이런 상황에 대응하기 위해 사용자에게는 점검 중이라는 것을 알리며 배포 대상 애플리케이션에 요청이 전달되지 않게 하는 설계가 필요하다
      • API Gateway와 ALB의 리스너 규칙, 타겟 그룹을 활용하여 점검 시 JSON 형식의 메세지나 HTML 페이지를 보여주게 할 수 있다

      점검을 실행할 때는 관리 콘솔이나 Lambda를 이용한 API 등으로 ALB 리스너 규칙의 우선도를 변경

      4. 성능 설계

      Application Auto Scaling 활용

      • 스케일아웃 전략을 이용하면 성능 효율을 최적화할 수 있다. 여기에 'Application Auto Scaling(서비스 자동 크기 조정)'을 같이 이용하면 수요 변화에 맞춰 실행할 컨테이너 수를 자동으로 조절할 수 있다. 운영 부하를 줄이는 것 뿐만 아니라 비용 최적화도 기대할 수 있다
      • ECS/Fargate에서 이용할 수 있는 Application Auto Scaling에서는 CloudWatch 알람에서 정한 지표 임계치에 따라 스케일아웃이 실행된다

      Application Auto Scaling을 이용한 스케일아웃 구조

      5. 구현 과정 및 결과

      ※ 참고

      VPC 엔드포인트

      • 트래픽이 프라이빗하게 이루어질 수 있도록 vpc 엔드포인트 활용

      📢 Amazon ECR은 컨테이너 이미지 및 아티팩트를 Amazon S3에 저장하기 때문에 따로 vpc 엔드포인트를 추가해주었다

      5-1. 프론트엔드 구현

      프라이빗 레포지토리 - 프론트엔드

      • 도커로 이미지를 빌드한 후 AWS ECR에 올려주었다

      작업 정의

      • 위에서 올려놓은 이미지로 ecs 테스크 생성

      ecs로 작업 생성

      • 서비스와 테스크 모두 사용해보기 위해서 프론트엔드는 서비스가 아닌 테스크로 생성
      • 실무에서는 서비스 사용 추천 (블루/그린 배포가 가능하기 때문)

      5-2. 백엔드 구현

      • 프론트엔드 구현 시와 동일하게 도커로 이미지를 빌드해서 ECR에 올린 후, 해당 이미지로 작업 정의를 생성

      • 프론트엔드와 다르게 ECS 서비스를 사용하여 구현

      • 배포 유형을 블루/그린 배포로 설정을 해주었기 때문에 무중단 배포가 가능

      • Additional configuration에서 대상 그룹을 하나는 블루 배포용, 다른 하나는 그린 배포용 두개를 지정해서 배포 시 로드밸런서의 대상 그룹을 자동 변경

      ECS 서비스 업데이트
      CodeDeploy를 이용한 Blue/Green 배포 완료

      5-3. 데이터베이스 구현

      • AWS 오로라 데이터베이스를 사용하여 구현
      • 가용성을 위해 리더, 라이터 인스턴스로 구분 (트래픽 분산)

      • 보안을 위해서 secrets manager를 활용하여 dbname, host, username, password 등을 숨겼다

      • secrets manager에서 암호를 가져올 수 있도록 iam 역할을 수정해준 후 컨테이너 정의에서 환경변수를 추가

      5-4. 구현된 웹 애플리케이션 모습

      기본 페이지 및 로그인 화면
      회원가입 및 로그인 성공 화면

      • 프론트엔드용 로드밸런스(internet facing)와 백엔드용 로드밸런스(internal)로 나누어서 구현

      5-5. CI/CD 구현

      • 로직이 비슷하므로 백엔드 부분만 CI/CD로 구현함

      📢 실무에서는 프론트엔드 부분도 CI/CD를 구현해주게 되면 빠르게 빌드/배포가 가능해짐

      AWS CodeCommit, CodeBuild, CodeDeploy를 이용한 CI/CD 구성도

      5-5-1. CI/CD 구현 - IAM

      • cloud9에 부착한 역할에 ecr 접근 권한 및 codecommit 접근 권한을 부여

      • Code Build에서 ECR에 접근이 가능하도록 IAM 역할에 접근 권한 부여

      5-5-2. CI/CD 구현 - Code Commit 생성

      • CodeCommit 프라이빗 레포지토리 생성 후 관련 코드 push

      5-5-3. CI/CD 구현 - Code Build 생성

      • 다음으로, 리포지토리에 푸시된 애플리케이션을 빌드하기 위한 'CodeBuild'를 생성한다. CodeBuild를 이용해 컨테이너 이미지를 생성하고, 생성한 컨테이너 이미지를 컨테이너 레지스트리에 등록하도록 구축한다
      • 빌드 사양 정의 파일 생성 (buildspec.yml)
        : CodeCommit에 등록된 애플리케이션에 대한 '빌드 사양 정의 파일'을 생성한다. 파일의 내용은 Dockerfile과 같아 보이지만, Docker 이미지를 빌드하는 것이 아니라 Docker 이미지 빌드를 포함한 애플리케이션 빌드 처리 전체를 정의한 것이다. 빌드 사양 정의에는 'buildspec.yml'이라는 YAML 형식의 정의 파일이 이용된다. 정의 파일 이름은 변경할 수 있지만, 이때는 명시적으로 파일 이름을 CodeBuild에 지정해야 한다. 여기서는 기본 이름인 'buildspec.yml'을 그대로 이용한다.
      • buildspec.yml은 소스 코드의 루트 디렉터리에 위치해야 한다
      version: 0.2
      
      env:
          variables:
              AWS_REGION_NAME: ap-northeast-2
              ECR_REPOSITORY_NAME: sbcntr-backend
              DOCKER_BUILDKIT: "1"
          
      phases:
          install:
              runtime-versions:  
                  docker: 20
      
          pre_build:
              commands:
                  - AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
                  - aws ecr --region ap-northeast-2 get-login-password | docker login --username AWS --password-stdin https://${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-2.amazonaws.com/sbcntr-backend
                  - REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION_NAME}.amazonaws.com/${ECR_REPOSITORY_NAME}
                  # 태그 이름에 Git 커밋 해시를 이용
                  - IMAGE_TAG=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
          build:
              commands:
                  - docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} .
          post_build:
              commands:
                  - docker image push ${REPOSITORY_URI}:${IMAGE_TAG}
                  - printf '{"name":"%s","ImageURI":"%s"}' $ECR_REPOSITORY_NAME $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
      
      artifacts:
          files:
              - imageDetail.json
      1. install 단계
        : 빌드 환경에서의 패키지 설치에만 사용된다.
        'runtime-versions' 섹션을 지정하고 있다. 이것은 CodeBuild의 특정 호스트 환경에서 빌드할 때 필요하다
      2. pre_build 단계
        : 빌드 전 처리를 수행한다. 'install' 단계에서는 라이브러리와 런타임 설치를 했지만, 'pre_build'에서는 ECR 로그인 및 의존성 해결, 변수 설정 등을 수행한다. ECR 로그인 및 변수 설정을 수행한다
      3. build 단계
        : 실제 빌드 처리를 정의한다. CodeBuild에서 테스트 처리를 하고 싶을 때는 테스트 명령을 정의한다. 'docker build'명령을 통해 컨테이너 이미지 생성만 수행한다
      4. post_build 단계
        : 빌드 후의 처리를 수행한다. 여기서 수행하는 내용은 출력 아티팩트(출력 파일) 생성 및 ECR에 이미지 푸시, 빌드 알람 전송 등이다. ECR에 이미지 푸시만을 수행한다

      📢 'post_build' 단계에서 빌드 아티팩트 JSON을 생성하고 'atifacts'의 'file' 스테이지에서 출력 아티팩트로서 이 파일을 지정한다. JSON 파일 이름은 반드시 'imageDetail.json'으로 지정해야 한다.

      https://docs.aws.amazon.com/ko_kr/codepipeline/latest/userguide/file-reference.html

       

      이미지 정의 파일 참조 - AWS CodePipeline

      소스 리포지토리가 Amazon S3 버킷인 경우, JSON 파일을 압축해야 합니다.

      docs.aws.amazon.com

      5-5-4. CI/CD 구현 - Code Pipeline 생성

      📢 원래대로라면 CodeDeploy는 다양한 구성 요소를 이용해 설정을 수행한다. 앞서 ECS 백엔드 서비스 생성 시 자동으로 생성된 CodeDeploy를 이용하므로 따로 생성하지는 않는다

       

      1. 애플리케이션 사양 파일(appspec.yaml) 생성
          : 애플리케이션 사양 파일이란 각 배포를 관리하기 위해 이용하는 파일로 CodePipeline에서 CodeDeploy를 설정할 때 사용된다. 어느 서비스를 배포할지, 어떤 정의를 기반에 배포할지 등을 정의하고 있다. 이 정보는 소스 코드로 관리되며 배포할 때마다 파이프라인에 정보를 전달하게끔 만들어져 있다

      version: 1
      Resources:
      - TargetService:
          Type: AWS::ECS::Service
          Properties:
              TaskDefinition: <TASK_DEFINITION>
              LoadBalancerInfo: 
                  ContainerName: "app"
                  ContainerPort: 80
      • TaskDefinition은 배포 대상이 되는 작업 정의를 지정하지만, 파이프라인이 실행될 때 자동으로 대체된다. 그렇기 때문에 구체적인 ARN을 지정하는 것이 아니라 <TASK_DEFINITION>이라는 문자열을 지정한다.
      • 추가한 애플리케이션 정의 파일은 다음 작업 정의 파일과 함께 CodeCommit에 푸시한다

      2. 작업 정의 파일(taskdef.json) 생성

          : ECS 작업 정의 파일을 생성한다

      {
        "executionRoleArn": "arn:aws:iam::[aws_account_id]:role/ecsTaskExecutionRole",
        "containerDefinitions": [
          {
            "logConfiguration": {
              "logDriver": "awslogs",
              "secretOptions": null,
              "options": {
                "awslogs-group": "/ecs/sbcntr-backend-def",
                "awslogs-region": "ap-northeast-2",
                "awslogs-stream-prefix": "ecs"
              }
            },
            "portMappings": [
              {
                "hostPort": 80,
                "protocol": "tcp",
                "containerPort": 80
              }
            ],
            "cpu": 256,
            "readonlyRootFilesystem": true,
            "environment": [],
            "secrets": [
              {
                "valueFrom": "[Secrets Manager 보안 암호 ARN]:host::",
                "name": "DB_HOST"
              },
              {
                "valueFrom": "[Secrets Manager 보안 암호 ARN]:dbname::",
                "name": "DB_NAME"
              },
              {
                "valueFrom": "[Secrets Manager 보안 암호 ARN]:password::",
                "name": "DB_PASSWORD"
              },
              {
                "valueFrom": "[Secrets Manager 보안 암호 ARN]:username::",
                "name": "DB_USERNAME"
              }
            ],
            "memory": null,
            "memoryReservation": 512,
            "image": "<IMAGE1_NAME>",
            "essential": true,
            "name": "app"
          }
        ],
        "memory": "1024",
        "taskRoleArn": null,
        "family": "sbcntr-backend-def",
        "requiresCompatibilities": ["FARGATE"],
        "networkMode": "awsvpc",
        "cpu": "512"
      }
      • 여기서 <IMAGE1_NAME>은 앞에서의 <TASK_DEFINITION>과 마찬가지로 CodePipeline이 자동으로 값을 교체한다. CodeDeploy가 새로운 이미지를 배포할 때 작업 정의의 어느 부분을 어떻게 바꿔야 할지 가리키는 역할을 한다.
        그렇게 때문에 구체적으로 자원 이름 등을 기재하지 않고 <IMAGE1_NAME>라고 기재한다

      CodePipeline 소스 스테이지 설정
      CodePipeline 빌드 스테이지 설정
      CodePipeline 배포 스테이지 설정

      • 현재 appspec.yaml과 taskdef.json 파일을 codedeploy에서 가져오지 못하고 있다. 왜냐하면 소스스테이지에서 appspce.yaml과 taskdef.json을 함께 패키징을 해서 출력을 하고 있기 때문이다. 그래서 codedeploy를 편집해주어야 한다

      • 편집 모양 클릭

      • 위에처럼 편집 후 저장

      • 정상 종료 확인

      CI/CD가 잘 동작하고 있다!!!

      5-6. 운영 설계 & 보안 설계: 애플리케이션 이미지 추가 설정

      • 태그 변경 불가능 설정
        : 컨테이너 이미지의 세대 관리를 위한 태그에 대해서는 중복 등록을 허가하지 않음
      • ECR에 이미지를 푸시할 때 스캔 설정
        : 컨테이너 이미지가 푸시될 때 취약점 스캔을 하도록 설정 추가
      • 수명 주기 정책 추가
        : ECR은 이용 크기에 따라 과금되므로 수명 주기를 설정해 불필요한 컨테이너를 삭제하는 운영 설계를 추가

      태그 변경 불가능 설정 및 ECR 푸시할 때 스캔 설정
      수명 주기 정책 설정

      5-7. 성능 설계: 수평 확장을 이용한 가용성 향상

      수평 확장을 이용한 가용성 향상 구성
      Auto Scaling 설정을 위한 ECS 서비스 설정
      자동 작업 조정 정책 추가
      CPU 사용률이 설정한 80%를 넘어서고 있다

      • 원하는 태스크가 조정 정책에 의해 2 → 3으로 변경이 되어 수평 확장이 되고 있다!!!

      📢 프런트엔드에서 백엔드에 스케일아웃이 발생할 정도로 부하를 거는 것은 어렵다. 부하를 걸기 위한 도구로 Apache HTTP server benchmarking tool(ab)을 이용한다. 'ab'는 Linux에 기본적으로 설치된 경우가 많고 Amazon Linux 2에도 설치되어 있다

      5-8-1. 보안 설계: 응용 프로그램 무단 접근 방지 - WAF 추가

      📢 이용할 AWS 관리형 규칙

      규칙 규칙 이름
      Web 애플리케이션에 대한 핵심 규칙 AWS-AWSMangerdRulsCommonRuleSet
      Amazon IP 평가 목록에 대한 규칙 AWS-AWSMangerdRulsAmazonIPReputationList
      익명 IP 목록에 대한 규칙 AWS-AWSMangerdRulsAnonymousIpList
      올바르지 않은 입력 데이터에 대한 규칙 AWS-AWSMangerdRulsKnownBadInputsRuleSet
      SQL 인젝션에 대한 규칙 AWS-AWSMangerdRulsSQLiRuleSet
      • WAF를 ALB에 부착해서 생성

      5-8-2. 보안 설계: 응용 프로그램 무단 접근 방지 - WAF 추가 후 확인

      1. SQL 인젝션을 차단하는가

      • ex) http://[Frontend ALB의 DNS]/"'some' or 'A'='A'"

      URI 경로의 일부에 SQL인젝션 구문을 넣어 요청

      • 403 Forbidden 페이지가 반환된다. WAF의 규칙이 잘 동작하고 있다. 쿼리 문자열에 동일하게 SQL 인젝션을 포함한 요청을 전송해도 동일한 결과를 얻을 수 있다.

      2. XSS를 차단하는가

      • ex) http://[Frontend ALB의 DNS]/<script>alert(document.cookie)</script>

      URI 경로의 일부에 파라미터로 JavaScript 구문을 넣어 요청

      • ex) http://[Frontend ALB의 DNS]/name=<script>alert(document.cookie)</script>

      쿼리 문자열에도 공격 구문을 넣어 요청

      5-9. 운영 설계 & 보안 설계: 로그 수집 기반 구축

      로그 수집 기반 실습 구성도

      1. 애플리케이션 로그를 저장할 S3 버킷을 생성한다

      2. FireLens용 컨테이너 기본 이미지 생성 후 ECR에 등록
          : amazon/aws-for-fluent-bit:2.16.1 이미지를 사용하여 생성 후 커스터마이징하여 사용하였다.

      // Dockerfile
      FROM amazon/aws-for-fluent-bit:2.16.1
      
      COPY ./fluent-bit-custom.conf /fluent-bit/custom.conf
      COPY ./myparsers.conf /fluent-bit/myparsers.conf
      COPY ./stream_processor.conf /fluent-bit/stream_processor.conf
      
      RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
      //fluent-bit-custom.conf
      [SERVICE]
          Parsers_File /fluent-bit/myparsers.conf
          Streams_File /fluent-bit/stream_processor.conf
      
      [FILTER]
          Name parser
          Match *-firelens-*
          Key_Name log
          Parser json
          Reserve_Data true
      
      [OUTPUT]
          Name   cloudwatch
          Match  access-log
          region ${AWS_REGION}
          log_group_name ${LOG_GROUP_NAME}
          log_stream_prefix from-fluentbit/
          auto_create_group true
      
      [OUTPUT]
          Name   cloudwatch
          Match  error-log
          region ${AWS_REGION}
          log_group_name ${LOG_GROUP_NAME}
          log_stream_prefix from-fluentbit/
          auto_create_group true
      
      [OUTPUT]
          Name s3
          Match  access-log
          region ${AWS_REGION}
          bucket ${LOG_BUCKET_NAME}
          total_file_size 1M
          upload_timeout 1m
      // parsers.conf
      [PARSER]
          Name json
          Format json
      // stream_processor.conf
      [STREAM_TASK]
          Name access
          Exec CREATE STREAM access WITH (tag='access-log') AS SELECT * FROM TAG:'*-firelens-*' WHERE status >= 200 AND uri <> '/healthcheck';
      
      [STREAM_TASK]
          Name error
          Exec CREATE STREAM error WITH (tag='error-log') AS SELECT * FROM TAG:'*-firelens-*' WHERE status >= 400 and status < 600;

      3. FireLens용 컨테이너에서 사용할 CloudWatch 로그 그룹 생성

      4. 작업 정의에 부착할 역할에 위에서 만든 s3에 대한 접근 권한과 CloudWatch Logs 접속 권한을 부여한다

      5. ECS 작업에 로그 라우터 컨테이너 추가

         : Fluent Bit의 사용자 정의를 읽어 들일 수 있게 하기 위해서 작업 정의 JSON을 통한 구성을 통해 밑에 항목을 추가한다

      "firelensConfiguration": {
           "type": "fluentbit",
           "options": {
           "config-file-type": "file",
           "config-file-value": "/fluent-bit/custom.conf"
           }
       }


      6.  서비스 배포 후 로그 기록이 잘되나 확인

         : curl 명령어를 통해 로드밸런서로 요청을 날리면 FireLens가 로그 수집을 시작한다.

      CloudWatch에 출력된 접속 로그(일부 생략)

      또한 로그 저장용으로 생성한 S3에 2023년 8월 3일 9시 27분대의 로그라면 '[버킷 이름]/fluent-bit-logs/access-log/2023/08/03/09/27/{임의의 파일 이름}' 형식으로 저장이 된다.

      S3에 저장된 로그 데이터(일부 생략)

      5-10. 운영 설계: Fargate를 이용한 Bastion 구축

      세션관리자를 통해 Bastion 구축을 해보겠다

      Fargate Bastion 구축

      1. Fargate Bastion으로 이용할 컨테이너 이미지 등록

      // Dockerfile
      FROM amazonlinux:2
      RUN yum install -y sudo jq awscli shadow-utils htop lsof telnet bind-utils yum-utils && \
          yum install -y https://s3.ap-northeast-2.amazonaws.com/amazon-ssm-ap-northeast-2/latest/linux_amd64/amazon-ssm-agent.rpm && \
          rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 && \
          yum install -y yum localinstall https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm && \
          yum-config-manager --disable mysql80-community && \
          yum-config-manager --enable mysql57-community && \
          yum install -y mysql-community-client && \
          adduser ssm-user && echo "ssm-user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/ssm-agent-users && \
          mv /etc/amazon/ssm/amazon-ssm-agent.json.template /etc/amazon/ssm/amazon-ssm-agent.json && \
          mv /etc/amazon/ssm/seelog.xml.template /etc/amazon/ssm/seelog.xml
      COPY run.sh /run.sh
      CMD ["sh", "/run.sh"]
      
      // run.sh
      #/bin/sh
      # Preparation
      SSM_SERVICE_ROLE_NAME="sbcntr-SSMServiceRole"
      SSM_ACTIVATION_FILE="code.json"
      AWS_REGION="ap-northeast-2"
      
      # Create Activation Code on Systems Manager
      aws ssm create-activation \
      --description "Activation Code for Fargate Bastion" \
      --default-instance-name bastion \
      --iam-role ${SSM_SERVICE_ROLE_NAME} \
      --registration-limit 1 \
      --tags Key=Type,Value=Bastion \
      --region ${AWS_REGION} | tee ${SSM_ACTIVATION_FILE}
      
      SSM_ACTIVATION_ID=`cat ${SSM_ACTIVATION_FILE} | jq -r .ActivationId`
      SSM_ACTIVATION_CODE=`cat ${SSM_ACTIVATION_FILE} | jq -r .ActivationCode`
      rm -f ${SSM_ACTIVATION_FILE}
      
      # Activate SSM Agent on Fargate Task
      amazon-ssm-agent -register -code "${SSM_ACTIVATION_CODE}" -id "${SSM_ACTIVATION_ID}" -region ${AWS_REGION}
      
      # Delete Activation Code
      aws ssm delete-activation --activation-id ${SSM_ACTIVATION_ID}
      
      # Execute SSM Agent
      amazon-ssm-agent

      컨테이너 이미지 생성 후 ECR에 푸시한다.

      2. IAM 설정

         : 위에서 생성한 run.sh에서 확인할 수 있듯이 세션 관리자에서 ECS 작업에 접속하기 위해 활성화 코드 및 ID를 생성해 내부 SSM 에이전트에 등록해야 한다. ECS 작업 내에서 활성화 코드를 생성하므로 ECS 작업에서 Systems Manager를 조작하기 위한 권한이 필요하다. 

          활성화 코드는 aws ssm create-activation 명령으로 생성한다. 이 명령의 인수로 Systems Manager용 서비스 역할을 지정해야 한다. 따라서 다음 IAM 역할과 정책을 생성한다.

      • ECS 작업이 이용할 IAM 역할과 정책

      • ECS 작업이 활성화 코드를 생성할 때 Systems Manager에 전달할 IAM 역할

      3. VPC 엔드포인트 생성

         : Systems Manager에는 2개의 VPC 엔드포인트가 있다. 첫 번째는 활성화 코드 생성 API와 같은 Systems Manager와 관련된 서비스에 접속하기 위한 엔드포인트다. 두 번재는 세션 관리자 접속을 위한 엔드포인트다. 지금 만드는 Fargate Bastion에서 이용할 서비스는 세션 관리자다. Systems Manager 세션 관리자의 기능으로 AWS의 서비스에서 세션 채널을 생성해 접속한다. 세션 채널은 보통 Systems Manager의 엔드포인트와는 별도의 'ssmmessages' 엔드포인트로 제공된다.

      그래서 두개의 엔드포인트를 생성해주어야 한다.

      • 엔드포인트 서비스 이름 : com.amazonaws.ap-northeast-2.ssmmessages, com.amazonaws.ap-northeast-2.ssm

      3. Systems Manager 인스턴스 티어 변경

         : Session Manager로 자체 인스턴스에 접속하기 위해서는 고급(Advanced) 인스턴스 티어를 이용해야 한다.

      4. 확인

      Fargate Bastion용 작업 정의 생성 후 Fargate Bastion용 작업을 생성한다.

      ECS 작업을 시작해 'RUNNING' 상태가 되면 Systems Manager 서비스로 이동해 Fargate Bastion용 작업에 접속해본다.

      Aurora 클러스터의 리더 인스턴 스 엔드포인트 주소로 접속 후 데이터 확인이 가능한지 해본다.

      Fargate Bastion을 이용해 데이터베이스에 접속 및 데이터 확인을 완료했다.

      Bastion은 사용하지 않을 때는 실행할 필요가 없다. 작업이 완료되면 세션 관리자의 세션은 종료하고 ECS 작업도 종료한다.

      5-11. 보안 설계: Trivy/Dockle을 이용한 보안 확인

      ECR에 푸시할 때 이미지를 스캔하도록 설정하는 항목이 있지만, 보다 높은 보안을 달성하기 위해 컨테이너 이미지 스캔 도구인 'Trivy'와 모범사례 확인 도구인 'Dockle'을 사용한다.

      푸시할 때 Trivy, Dockle을 실행하는 구성

      CodeBuild의 동작 사양을 정의하는 빌드 정의(buildspec.yml) 파일을 수정한다.

      // buildspec.yml
      version: 0.2
      
      env:
          variables:
              AWS_REGION_NAME: ap-northeast-2
              ECR_REPOSITORY_NAME: sbcntr-backend
              DOCKER_BUILDKIT: "1"
          
      phases:
          install:
              runtime-versions:
                  docker: 19
      
          pre_build: 
              commands:
                  - AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
                  - aws ecr --region ap-northeast-2 get-login-password | docker login --username AWS --password-stdin https://${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-2.amazonaws.com/sbcntr-backend
                  - REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION_NAME}.amazonaws.com/${ECR_REPOSITORY_NAME}
                  # 태그 이름으로 Git 커밋 해시를 이용
                  - IMAGE_TAG=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
                  # 사전 준비: trivy를 설치
                  # 주석 처리된 curl 명령을 이용해 최신 버전을 취득하도록 구성할 수 있음
                  #- TRIVY_VERSION=$(curl -sS https://api.github.com/repos/aquasecurity/trivy/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
                  - TRIVY_VERSION=0.19.2
                  - rpm -ivh https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.rpm
                  # 사전 준비: dockle 설치
                  # 주석 처리된 curl 명령을 이용해 최신 버전을 취득하도록 구성할 수 있음
                  #- DOCKLE_VERSION=$(curl -sS https://api.github.com/repos/goodwithtech/dockle/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
                  - DOCKLE_VERSION=0.3.15
                  - rpm -ivh https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.rpm
          
          build:
              commands:
                  - docker image build -t ${REPOSITORY_URI}:${IMAGE_TAG} .
          
          post_build:
              commands:
                  # trivy를 이용한 이미지 스캔(결과 저장용)
                  - trivy --no-progress -f json -o trivy_results.json --exit-code 0 ${REPOSITORY_URI}:${IMAGE_TAG}
                  # trivy를 이용한 이미지 스캔(취약점 수준이 CRITICAL인 경우는 빌드를 강제 종료)
                  - trivy --no-progress --exit-code 1 --severity CRITICAL ${REPOSITORY_URI}:${IMAGE_TAG}
                  - exit `echo $?`
                  # dockle를 이용한 이미지 확인(취약점 수준이 FATAL인 경우는 빌드를 강제 종료)
                  - dockle --format json -o dockle_results.json --exit-code 1 --exit-level "FATAL" ${REPOSITORY_URI}:${IMAGE_TAG}
                  - exit `echo $?`
                  # Docker이미지를 ECR에 푸시
                  - docker image push ${REPOSITORY_URI}:${IMAGE_TAG}
                  # 이미지 URL을 기록한 JSON 생성
                  - printf '{"name":"%s","ImageURI":"%s"}' $ECR_REPOSITORY_NAME $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
      
      artifacts:
          files:
              - imageDetail.json
              - trivy_results.json
              - dockle_results.json

      Trivy를 이용한 이미지 스캔을 2회 실시한다.

      첫 번째는 SEVERITY(심각도)에 상관없이 모든 취약점 정보를 기록하기 위한 실행이다.

      두 번째는 취약점 수준이 CRITICAL인 경우 빌드를 중지하기 위한 목적으로 실행한다. 첫 번째 실행에서 모든 취약점 정보를 캐시에 저장하고 있고, CRITICAL 수준의 취약점만을 탐지하므로 두 번째 스캔은 빠르게 완료된다.

      Trivy 실행이 완료되면 Dockle이 이미지 검사를 실시한다. Dockle도 Trivy와 마찬가지로 FATAL 수준의 취약점이 있으면 빌드를 중지하고 종료한다. 무사히 검사가 완료되면 이미지를 ECR에 푸시하고 빌드 아티팩트로 Trivy와 Dockle의 실행 결과를 S3에 저장한다.

      📢 취약점이 있는 이미지 파일을 사용할 경우

      CodeBuild에서 에러가 발생한다.

      'Trivy' 스캔에서 'CRITICAL'인 취약점이 발생해 에러가 났다는 것을 알 수 있다. 취약점 ID를 통해 해당 문제의 원인을 파악할 수 있다. 위 에러는 이미지에서 사용하는 버전이 최신 버전이 아니라서 발생하는 취약점이다 (보여주기 위해 일부러 설정)

      취약점 제거 후 다시 이미지를 푸시한 후 정상 동작된 로그 확인을 위해 S3에 저장된 아티팩트를 확인한다.

      가장 최근에 생성된 아티팩트 확인
      Trivy 실행 결과 확인
      Dockle 실행 결과 확인

      'fatal' 항목은 없으나 'warn' 항목이 존재하는 것을 확인할 수 있다.

      이처럼 컨테이너를 빌드할 때 Trivy와 Dockle을 실행하면 보다 안전한 컨테이너 운영을 할 수 있다.

      한 번 빌드된 이미지는 이후 버전 취약점 등을 검사하지 않아 취약한 상태로 운영될 가능성도 있다. 따라서 정기적으로 이런 검사 도구를 실행하게 구성하는 것도 검토해야 한다.

      5-12. 트러블 슈팅

      📢 CodeBuild에서 발생하는 'Too Many Requests.' 에러 해결 방법

      CodeBuild 실행 시 'Too Many Requests.' 에러가 발생하는 이유는 도커 허브의 이미지 취득 제한 때문이다. 2020년 11월 기준, 도커 허브에서 이미지를 받을 때 다음과 같은 제한이 걸려있다.

      • 익명 이용자로부터의 요청은 6시간에 100회까지
      • Docker 계정을 가진 무과금 이용자(Personal user)로부터의 요청은 6시간에 200회까지
      • 유료 이용자(Pro 이상)는 하루 5,000회 이상

      익명 이용자란 'docker login' 명령을 사용하지 않고 도커를 이용하는 경우에 해당한다. 그리고 익명 이용자는 IP 주소로 집계한다. 문제가 되는 부분은 이곳이다. 가령 하나의 네트워크에 있는 여러 대의 단말은 도커 허브에서는 같은 공인 IP를 가지고 접속하므로 한 명의 사용자로 계산한다. 즉, 어떤 공인 IP 주소를 이용하는 CodeBuild 환경에서 6시간 이내에 100회의 'docker image pull'이 실행됐다면 이 에러가 발생한다. 예를 들어, 학원과 같은 장소에서 이런 에러가 발생할 수 있다.

       

      이 에러를 피할 수 있는 방법은 3가지가 있다.

      1. 도커 계정을 생성해 CodeBuild 내에서 'docker login'을 수행해 6시간 이내의 요청 수를 늘림
      2. ECR에 'docker image pull' 대상 이미지를 등록해 ECR에서 이미지를 취득
        (결과적으로 도커 허브에서 이미지를 취득하지 않음)
      docker image pull golang:1.16.8-alpine3.13
      AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
      aws ecr --region ap-northeast-2 get-login-password | docker login --username AWS --password-stdin https://${AWS_ACCOUNT_ID}.dkr.ecr.apnortheast-2.amazonaws.com/sbcntr-base
      docker image tag golang:1.16.8-alpine3.13 ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-2.amazonaws.com/sbcntr-base:golang1.16.8-alpine3.13
      docker image push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-2.amazonaws.com/sbcntr-base:golang1.16.8-alpine3.13

       

      FROM golang:1.16.8-alpine3.13 AS build-env
      // Dockerfile를 위에서 아래처럼 수정하여 이미지를 sbcntr-base ECR 레포지트리에서 가져와서 빌드함
      FROM [본인 aws 계정 ID].dkr.ecr.ap-northeast-2.amazonaws.com/sbcntr-base:golang1.16.8-alpine3.13

        3. VPC 안에서 CodeBuild를 실행해 다른 AWS 이용자의 영향을 받지 않도록 함

       

      📢 각 방법에는 장점과 단점이 있으므로 프로젝트나 조직 규모에 따라 선택하는 것이 좋다. 작은 조직이라면 1번 방법을 이용하는 것이 관리 비용도 낮으며 편하다. 조직이 크다면 조금 관리 비용이 높아지지만 2번 방법으로 공통 기본 이미지를 ECR에 저장해서 이용할 수 있다.

       

      6. 마무리

      • EKS를 이용해서 프로젝트를 진행해본 적은 있지만 따로 ECS를 활용하여 프로젝트를 진행해본 적이 없어서 본 프로젝트를 진행하게 되었습니다
      • 개인프로젝트로써 진행되었습니다

       


       

      참고 교재

      https://product.kyobobook.co.kr/detail/S000061352144

       

      AWS 컨테이너 설계와 구축 철저 입문 | 아라이 마사야 - 교보문고

      AWS 컨테이너 설계와 구축 철저 입문 | AWS에서 컨테이너 환경의 설계와 구축 노하우를 기본부터 실무 지식까지 확실히 배운다!앱이나 온라인 콘텐츠 사업을 지속적으로 발전시키기 위해서는 기

      product.kyobobook.co.kr

       

      반응형

      '클라우드 > AWS' 카테고리의 다른 글

      AWS Profile 전환 자동화하기  (0) 2024.08.12
      솔데스크 AWS 클라우드 교육 과정 후기  (2) 2023.08.26
      API Gateway 란  (0) 2023.08.14
      WAF 란  (0) 2023.08.13
      Cognito 란  (0) 2023.08.13
      댓글