11/10/2023

인프라의 세계

발단

리팩토링한 애플리케이션을 배포하기 위해서 가장 널리 사용되는 클라우드 서비스인 AWS를 사용하기로 했다. 위코드에서 1차 프로젝트 완료 후 배포를 하기 위해 EC2와 RDS를 사용했는데 처음 사용해서 그런지 인프라가 너무 허접하고 초라했다... 강의를 듣고 AWS 공식 문서를 보면서 여기서 더 발전된 형태로 인프라를 구축하고 싶었다! 구체적으로 적용한 서비스를 나열하면 VPC, RDS, ECR, ECS(EC2), ALB, ElastiCache, Route 53! 사용한 서비스를 사용하면서 배운점, 어려움과 해결책에 대해 적어본다~


네트워크

VPC란 뭘까? 일단 AWS의 전체 인프라는 보안 시설로서 클라우드에서 실행되는 사용자의 모든 자원은 높은 보안성을 보장받을 수 있으며 이는 기본적으로 AWS를 사용하는 모든 기업들이 보장받을 수 있는 안정성이다. VPC는 사용자가 정의한 공인(public)과 사설(private)의 논리적으로 격리/독립된 가상 네트워크로 어느 자원이라도 논리적으로 분리된 영역에 격리할 수 있으며 사용자가 네트워크 환경 설정에 대한 완전한 통제권을 가진다. 구체적으로 사용자가 VPC를 통해 만든 네트워크에서 자체 IP 주소, 공인 및 사설 서브넷, 라우트 테이블, 네트워크 게이트웨이 등 모든 기능을 활용할 수 있다는 것을 의미한다. 따라서 VPC의 주 목적은 사용자 수준에서 보안의 수준을 높이는 것이다.

10.0.0.0/16

VPC를 생성하는데 IPv4 CIDR 항목이 있는데 CIDR은 클래스없는 도메인 간 라우팅으로 IP 주소 지정 체계이다. 예시의 10.0.0.0/24에서 24는 IP 네트워크 접두사로 앞의 24비트가 네트워크 주소로 10.0.0(즉, 10.0.0.0/24의 모든 CIDR 블록의 네트워크 주소)이고 나머지 8비트는 호스트 주소이다. 다시 말해, 예시의 IP 주소 범위는 10.0.0.0 - 10.0.0.255. 나는 조금 널널하게 사용하기 위해 10.0.0.0/16을 사용했다(10.0.0.0 - 10.0.255.255). 주의할 점은 VPC는 사설 네트워크이기 때문에 사설 IP 주소 범위만 사용할 수 있다! 참고로 10.0.0.0/16은 IP 클래스로 보면 A 클래스에 해당하고 사설 IPv4 주소의 범위이다. 자세한 내용은 네트워크를 공부하자...

총 6개의 서브넷

다음으로 서브넷을 생성한다! 서브넷은 서브네트워크의 줄임말로 하나의 네트워크를 분할(서브넷팅)하여 분할된 네트워크이다. VPC에서 서브넷은 IP 주소 범위이다. 주의할 점이 있는데 AWS는 서브넷의 5개의 IP 주소(처음 4개와 마지막 1개)를 예약해서 자원에 할당할 수 없다. 따라서 IP 주소 개수를 계산할 때 이를 반드시 고려해야 한다. 또한 당연한 점이지만 서브넷의 IP 주소 범위가 반드시 VPC에서 설정한 CIDR에 속해야 한다. 생성된 VPC에 공인 서브넷과 사설 서브넷 각각 3개씩 만들었는데 범위는 공인 서브넷의 IP 주소 범위는 10.0.0.0/24, 10.0.0.1/24, 10.0.0.2/24 사설 서브넷의 IP 주소 범위는 10.0.16.0/20, 10.0.32.0/20, 10.0.48.0/20.

메인 경로 테이블을 포함해 총 3개의 경로 테이블

공인 서브넷의 경우 인터넷과 연결되어야 하는데 연결 통로를 제공하는 것이 IGW이다, 하지만 IGW 자체만으로는 인터넷에 접근할 수 없으며 경로 테이블이 필요하고 서브넷 설정에서 Auto-assign IP를 활성화 해야한다! 또한 사설 서브넷은 사설 경로 테이블과 연결되어야 한다. 생성된 VPC에 사설 경로 테이블과 공인 경로 테이블을 만들고 Subnet association의 Explicit subnet associations에서 의도에 맞는 서브넷(공인 경로 테이블-공인 서브넷, 사설 경로 테이블-사설 서브넷)을 지정한다. 공인 경로 테이블은 추가적으로 Routes의 Edit routes에서 생성한 IGW를 설정한다.

이렇게 해서 설정된 VPC의 자원 맵은 다음과 같다!

마지막으로 네트워크 ACL! 네트워크 ACL은 접근 제어 목록으로 접근 제어는 보안상 위협으로부터 제반 시설 및 환경을 보호하기 위한 보안 정책으로 인가된 대상은 접근에 허용하고 인가되지 않은 대상은 접근을 거부하여 보안을 강화할 수 있다. 네트워크 ACL 서브넷 수준에서 특정한 인바운드 또는 아웃바운드 트래픽을 허용하거나 거부한다. 보안 그룹과의 가장 큰 차이점은 네트워크 ACL은 상태를 유지하지 않으므로(stateless) 이전에 보낸 또는 받은 트래픽에 대한 정보가 저장되지 않는다. 즉, 특정 인바운드 트래픽을 허용하기 위한 네트워크 ACL 규칙을 생성하면 해당 트래픽에 대한 응답은 자동으로 허용되지 않는다. 이에 반해, 보안 그룹은 상태를 가지므로(stateful) 이전에 보낸 혹은 받은 트래픽에 대한 정보가 저장되어 요청에 대한 응답이 자동으로 허용된다. 

그림을 그려서 해결!

네트워크 ACL을 적용하면서 가장 헷갈리고 힘들었던 부분이 바로 임시(ephemeral) 포트이다. 임시 포트는 운영체제가 외부 연결에 사용하는 포트로 운영체제에 따라 다르기에 일반적으로 1024에서 65535이다. 예를 들어, 공인 서브넷의 애플리케이션이 사설 서브넷의 MySQL 데이터베이스에 연결을 시작하면 원본 포트로 임시 포트를 사용한다. 연결이 성립하면 원본 포트는 애플리케이션이 선택한 임시 포트가 되고 대상 포트는 3306이다. 다시 말해서 공인 서브넷의 아웃바운드와 사설 서브넷의 인바운드는 포트 3306을 수용해야 한다. 따라서 MySQL의 대답이 애플리케이션에 도달할 수 있도록 공인 서브넷의 인바운드와 사설 서브넷의 아웃바운드는 임시 포트를 수용할 수 있도록 1024에서 65535까지 열어야 한다.


Docker 이미지

Docker 이미지를 사용해서 통해 애플리케이션 배포를 진행하는데 강의 중에 ECR와 ECS가 바로 생각났다! 일단 ECR은 관리형 컨테이너 이미지 레지스트리 서비스로 Docker Hub와 비슷하게 Docker 이미지를 저장하고 관리한다. 이미지를 저장하는 레포지토리는 공인 레포지토리와 사설 레포지토리로 나뉘는데 이미지를 공개하고 싶지 않으면 사설 레포지토리를 이용할 수 있다. 사설 레포지토리에 저장되는 이미지는 공인 레포지토리와 달리 올바른 IAM 권한을 가져야만 풀(pull)할 수 있다. 즉, ECS 클러스터의 EC2 인스턴스는 적합한 IAM 역할이 있어야 사설 레포지토리의 Docker 이미지를 풀할 수 있다.

Docker 이미지를 만드는 과정은 어렵지 않았다. AWS에 로그인 후 ECR 서비스에서 사설 레포지토리를 만들고 싶어서 Private registry의 Repositories을 선택했다. 레포지토리 생성 후 Docker 이미지를 푸시(push)하기 위해서 View push commands을 클릭한 후 설명대로 진행했다. 이 때, AWS CLI를 설치한 후 IAM 서비스로 이동해서 AmazonEC2ContainerRegistryFullAccess 권한을 사용자에게 추가해야 한다. 나의 경우, AWS 계정 생성 후 AdministratorAccess 권한을 가지는 admin 그룹에 사용자를 추가해서 위의 과정은 필요 없었다.

다음으로 사용자의 Security credentials에서 접근 키를 생성해야 한다. 접근 키가 없으면 AWS CLI를 사용할 수 없다! 접근 키 생성 후 aws configure 명령을 터미널에 치고 다음 접근 키와 비밀 접근 키를 입력한다. 이제 View push commands의 설명대로 진행하며 다음과 같이 Docker 이미지가 레포지토리에 푸시된다!
4iz!


클러스터

ECS는 컨테이너화된 애플리케이션을 쉽게 배포, 관리 및 확장할 수 있도록 도와주는 완전히 관리되는 컨테이너 오케스트레이션 서비스로 ECS 클러스트에 ECS 작업을 시작해서 Docker 컨테이너를 실행한다. 2가지 실행(launch) 타입이 존재하는데 하나는 EC2 실행 타입이고 다른 하나는 Fargate 실행 타입이다. 가장 큰 차이점은 EC2 실행 타입은 EC2 인스턴스를 프로비저닝하고 유지 보수해야 하지만 Fargate 실행 타입은 서버리스(serverless)이기 때문에 관리할 EC2 인스턴스가 없다.

사실 두 가지 방법을 모두 적용해보는 것이 당연히 좋지만 일단 하나를 선택해야 했다. 고민 끝에 EC2 실행 타입을 사용하기로 결정했다. 강의에 따르면 Fargate 실행 타입에 EC2 스토리지인 EFS를 사용하면 모든 것을 서버리스로 관리하기 때문에 편리하다고 하는데 편리하지만 불편한 방법도 알고는 있어야겠지?! EC2 실행 타입도 장점이 있는데 만약 사용하지 않으면 EC2에서 Docker를 사용하기 위해 서버에서 Docker를 수동으로 설치하고 관리하는 번거로움 작업을 직접해야 한다.

ECS 서비스에서 Clusters을 클릭하고 Create cluster을 연다. Infrastructure에서 AWS Fargate의 체크를 제거하고 Amazon EC2 instnaces를 체크하며 여러 옵션이 나오는데 ASG는 없기 때문에 당연히 새로 생성하고 운영체제는 Amazon Linux 2, EC2 인스턴스 타입은 무료인 t2.micro을 선택했다. 스케일 아웃으로 증가할 수 있는 인스턴스 개수의 최댓값은 5, 스케일 인으로 감소할 수 있는 인스턴스 개수의 최솟값은 1로 정했다. SSH 키 쌍의 경우 EC2 인스턴스에 접속하고 싶은 경우 명시하는데 이미 존재하는 키 쌍을 골랐다. 루트 EBS 볼륨 크기는 최소 크기인 30 GiB! 네트워크 설정의 경우 기본 VPC가 아닌 새로 만든 VPC를 선택했고 서브넷은 공인 서브넷만 적용했고 보안 그룹은 역시 VPC처럼 새로 만든 EC2 보안 그룹을 선택했다.
클러스터 생성 완료!


작업 정의

이제 작업 정의를 만들어야 하는데 작업 정의는 애플리케이션의 청사진으로 애플리케이션을 형성하는 매개변수 및 하나 이상의 컨테이너를 설명하는 JSON 형식의 텍스트 파일이다. 즉, 작업 정의는 JSON 형식의 메타데이터로 ECS에게 Docker 컨테이너를 실행하는 방법을 알려준다. 지정할 수 있는 몇 가지 매개변수는 각 작업의 각 컨테이너에 사용할 Docker 이미지, 각 작업 또는 작업 내의 각 컨테이너에 사용할 CPU 및 메모리 양, 호스팅되는 작업의 인프라를 결정하는 사용할 실행 타입, 호스트/컨테이너 포트 바인딩, 환경 변수이다.

ECS 서비스에서 Task definitions을 클릭하고 Create new task definition을 연다. 실행 타입은 EC2, 운영체제는 Linux/X86_64, 네트워크는 기본 awsvpc가 아닌 bridge 네트워크를 사용했다. bridge 네트워크 모드는 Docker의 내장 가상 네트워크를 사용하며 각각의 EC2 인스턴스가 작업을 호스팅하는 내부에서 실행된다. bridge 네트워크는 동일한 bridge 네트워크에 연결된 각 컨테이너가 서로 통신할 수 있는 내부 네트워크 이름공간으로 동일한 bridge 네트워크에 연결되지 않은 컨테이너로부터 격리 경계를 제공한다. bridge 네트워크의 중요한 점은 정적 또는 동적 포트 매핑을 사용하여 컨테이너의 포트를 EC2 호스트의 포트와 매핑한다. 동적 호스트 포트 매핑을 사용할 경우 컨테이너 포트만 정의하면 EC2 인스턴스 내부에서 각 ECS 작업은 호스트의 포트를 통해 접근할 수 있다. 즉, EC2 인스턴스 내부에 여러 개의 작업이 존재하면 컨테이너 포트가 모두 동일해도 각 작업은 서로 다른 호스트 포트를 부여받는다. 이 기능 덕분에 ALB가 올바른 포트를 찾을 수 있다. 나는 애플리케이션이 하나라서 그냥 3000으로 호스트 포트를 정했다.
메모리 때문에 고생했다...
작업의 메모리가 굉장히 중요한데 t2.micro 인스턴스의 메모리는 1 GiB 밖에 안되기 때문에 0.5 GB로 설정했다(이상하게 CPU는 1로 설정해도 문제가 발생하지 않았는데 이유를 모르겠다... 이 메모리 때문에 삽집을 얼마나 했는지 하...). 만약 컨테이너 및 애플리케이션의 메모리 총합이 t2.micro 인스턴스에서 사용 가능한 메모리를 초과하며 운영체제와 같은 프로세스에 필요한 메모리가 없을 수 있다. Resource allocation limits에 구체적인 값(컨테이너에 할당되는 CPU, GPU, 메모리 등의 용량)을 명시하지 않았는데 정확한 제한 값을 몰라서 공부가 더 필요하다(이 부분도 이해가 안되는 부분이다...)! 다음으로 환경 변수는 DB_TYPE, DB_HOST와 같은 애플리케이션이 필요한 환경 변수로 .env 파일의 내용을 사용하되 DB_HOST의 경우 RDS의 주소로 설정했다.
명시적으로 호스트 포트 설정~


스케일링

이제 애플리케이션을 배포할 시간이 왔다! Clusters에서 Create를 클릭한다. Compute option 항목을 보면 Capacity provider strategy와 Launch type이 있는데 Info 링크의 설명에 따르면 이 옵션은 작업이 클러스터 인프라 전체에 분산되는 방식을 결정하는데 전자는 작업이 하나 이상의 용량 제공자(capacity provider)에 걸쳐 분산되고 후자의 경우 Fargate 혹은 수동으로 클러스터에 등록한 EC2 인스턴스에서 작업이 시작된다고 한다.

이를 보자마자 강의에서 언급한 내용이 생각났다! ECS 자동 스케일링은 AWS Application Auto Scaling 서비스를 사용해서 자동으로 ECS 작업의 개수를 증가하고 감소시킨다. Application Auto Scaling은 EC2 이상의 개별 AWS 서비스에 대한 확장 가능한 자원을 자동으로 조절해야 하는 해결책을 제공한다. Application Auto Scaling은 다음 조건에 따라 확장 가능한 자원을 자동으로 조절할 수 있다. Target tracking scaling은 특정 CloudWatch 메트릭에 대한 대상 값에 따라 자원을 스케일링한다. Step scaling은 경보 위반의 크기에 따라 달라지는 일련의 스케일 조정을 기반으로 자원을 스케일링한다. Scheduled scaling은 자원을 일회성 또는 주기적인 일정에 따라 스케일링한다.

중요한 점은 ECS 자동 스케일링(작업 수준)은 EC2 자동 스케일링(EC2 인스턴스 수준)과 일치하는 않는다! 즉, ECS 자동 스케일링을 수용하려면 일반적으로 EC2 인스턴스를 추가해야하는데 ASG는 CPU 사용률을 기반으로 시간이 지나면 EC2 인스턴스를 추가한다. 하지만 ECS 자동 스케일링의 Target tracking 메트릭인 평균 CPU 사용량(ECSServiceAverageCPUUtilization), 평균 메모리 사용량(ECSServiceAverageMemoryUtilization), 대상 당 요청 개수(ALBRequestCountPerTarget) 때문에 EC2 인스턴스가 추가되지 않을 수 있다. 이 문제를 해결하는 ECS Cluster Capacity Provider는 자동으로 ECS 작업에 대한 인프라를 프로비저닝하고 스케일링하며 ASG과 함께 사용되기에 수용 능력이 부족할 때(즉, EC2 인스턴스의 CPU, RAM 등) EC2 인스턴스를 추가한다. Capacity provider strategy가 바로 이 방법이다! 따라서 당첨~

서비스와 작업을 만들 수 있는데 무엇을 선택해야 하는지 헷갈렸는데 나같은 사람을 위해 Info 링크에 친절하게 설명이 나와있다! Service는 장기 실행되는 상태가 없는 서비스 및 애플리케이션에 이상적인데 서비스 작업 중 하나가 실패하거나 중지되면 서비스 스케줄러가 해당 작업 정의의 다른 인스턴스를 시작하여 대체한다. 반면에 Task는 배치 작업과 같이 실행되고 종료되는 독립형 작업에 적합하다고 한다. 웹 애플리케이션을 배포하기 때문에 Service를 선택했다! 옵션으로 지정된 Service auto scaling이 앞서 말한 ECS 자동 스케일링으로 작업의 개수를 자동으로 증가 및 감소시키는 기능으로 최솟값은 1, 최댓값은 5 그리고 평균 CPU 사용량을 기준으로 스케일링을 실행한다. scale-out cooldown period는 새로운 인스턴스를 추가한 후 또 다른 새로운 인스턴스가 추가되지 않는 시간이고 scale-in cooldown period는 반대로 인스턴스를 제거한 후 다른 인스턴스 제거되지 않는 시간이다.

작업 개수를 2개로 정하면 EC2 인스턴스가 자동으로 2개 생성되고 다음과 같이 잘 작동한다!
헬스 체크 통과~~


로드 밸런서

ECS 자동 스케일링과 EC2 자동 스케일링 덕분에 트래픽이 증가하면 스케일 아웃으로 EC2 인스턴스의 개수를 증가시킬 수 있지만 부하를 분산할 수 없다. 이를 위해 로드 밸런서를 사용해야 한다! 로드 밸런서는 트래픽을 여러 EC2 인스턴스에 분산해서 부하를 줄이고 EC2 인스턴스 하나가 고장나면 트래픽을 정상적으로 자동하는 다른 인스턴스로 전달하기 때문에 다운타임 없이 애플리케이션이 정상적으로 작동한다.

응용 계층인 HTTP 및 HTTPS에서 로드 밸런싱을 적용할 것이기 때문에 ALB를 선택했다. 로드 밸런서를 사용하면 API를 요청하는 클라이언트는 애플리케이션의 인스턴스와 직접 연결되지 않으며 클라이언트는 로드 밸런서에 요청을 보내고 로드 밸런서는 요청을 애플리케이션 인스턴스 중 하나로 전달한다. 따라서, 먼저 HTTP 트래픽을 허용하는 인바운드 규칙을 가지는 보안 그룹을 만들었다. 다음으로 정상 상태인 모든 EC2 인스턴스는 ALB가 전달하는 모든 트래픽을 허용해야 하기에 EC2 보안 그룹에서 ALB의 보안 그룹을 인바운드 규칙에 반드시 추가해야 한다!
ALB 보안 그룹이 EC2 보안 그룹의 인바운드 규칙!

이제 다시 서비스를 생성해서 애플리케이션을 배포해야 하는데 작업 정의에서 호스트 포트를 명시하지 않은 새로운 버전을 만들었다. 호스트 포트는 동적으로 할당되며 ALB가 동적 호스트 포트 매핑 기능을 사용해서 각 작업의 정확한 호스트 포트를 찾는다.
호스트 포트를 동적으로 설정~

서비스 생성 시 모든 설정은 앞서 보여준 설정과 똑같으며 Load balancing 옵션만 추가하면 끝이다. 작업의 개수는 3으로 설정했으며 ALB와 대상 그룹을 만드는 과정은 별로 어렵지 않기 때문에 생성 과정은 생략했다.

서비스가 시작되면 3개의 작업이 생성되고 이에 따라 3개의 EC2 인스턴스가 시작된다.
서비스의 작업 개수는 3개

대상 그룹에 들어가면 모든 EC2 인스턴스가 정상인 상태인 것을 확인할 수 있다.

마지막으로 ALB의 DNS를 통해 애플리케이션에 접속하면 성공적으로 작동하는 것을 확인할 수 있다!


캐시

4iz 캐싱과 Redis 포스트에서 캐싱을 구현하기 위해 Redis를 사용했다고 적었다. AWS의 RDS를 통해 AWS가 관리하는 클라우드에서 MySQL, MariaDB, Postgres와 같은 관계형 데이터베이스를 생성할 수 있다. 중요한 점은 관리되는 서비스로 자동 프로비저닝(시스템을 가동 가능 상태로 만드는 작업), 운영체제 패치, 지속적인 백업, 시점(PIT) 복원, 읽기 레플리카(replica), 재해 복구를 위한 다중 가용영역, 스케일링과 같은 서비스를 제공해서 관계형 데이터베이스의 설치와 운영을 효율적이고 간편하게 만들어준다. 그래서 혹시 Redis와 관련된 서비스가 있나 확인했는데 그게 바로 ElastiCache Redis이다! 간단하게 말해서 RDS가 관계형 데이터베이스를 생성, 운영 및 관리하는 것과 같은 방식으로 ElastiCache는 Redis 또는 Memcached를 생성, 운영 및 관리한다(운영체제 관리/패치, 최적화, 설정, 구성, 고장 복구, 백업 등.).

나는 관련 포스트에서 말했듯이 상품 상세 API가 시간이 많이 걸려서 캐싱을 적용했다. 구체적으로 살펴보면, 현재 나의 AWS 아키텍처를 살펴보면 애플리케이션이 상품 조회 쿼리를 RDS에 수행한다. 하지만 ElastiCache을 적용하면 애플리케이션이 RDS가 아니라 ElastiCache에 쿼리를 먼저 수행하고 찾는 상품이 ElastiCache에 존재하면 상품을 ElastiCache에서 바로 읽어온다. 이를 캐시 히트라고 한다. 하지만 상품이 ElastiCache에 존재하지 않을 경우, 즉 캐시 미스인 경우 RDS에서 상품을 읽어오고 ElastiCache에 상품을 적어서 다음 조회 쿼리가 발생하면 캐시 히트가 발생하도록 한다. 결과적으로 필요한 데이터를 ElastiCache에서 바로 가져올 수 있어 속도가 향상되고 읽기 중심 작업의 경우 RDS에서 발생하는 부하를 줄일 수 있다. 하지만 데이터를 캐시에 저장하기 때문에 가장 최신 데이터만 사용되도록 하기 위한 캐시 무효화 전략이 있어야 한다. 현재 내 애플리케이션은 주문 완료 및 주문 취소 시 상품의 옵션에 따라 재고가 변하기 때문에 관련 API 호출 시 캐시가 무효화된다!

ElastiCache Redis 생성 과정은 Design your own cache에서 Cluster cache를 선택했는데 모든 구성 옵션을 직접 선택할 수 있다. 클러스터 모드는 비활성화! 처음 생성하는 것이기도 하고 내 애플리케이션의 트래픽이 클러스터가 필요할 정도로 많을 가능성이 과연 얼마일까? 다중 AZ와 자동 페일오버(failover)도 비활성화했는데 돈이 나갈 수 있다고 어디서 읽었다. 노드 타입도 무료 버전인 cache.t2.micro, 포트는 그대로 6379 다중 AZ를 사용하지 않기 때문에 레플리카 개수는 0 네트워크에서 RDS처럼 사설 서브넷을 선택했고 EC2 인스턴스 보안 그룹의 인바운드 트래픽을 포트 6379에 허용하는 보안 그룹을 따로 만들었다. 보안의 경우 데이터 암호화(at rest와 in transit) 둘 다 비활성화 했는데 이 부분은 공부가 더 필요하다.

이미지에서 가려진 부분이 중요한데 애플리케이션이 접근해야 하는 Redis 주소이기 때문이다! 두 가지가 있는데 AWS 공식 문서에 따르면 클러스터 비활성화의 경우 Nodes의 노드의 엔드포인트를 사용하라고 한다. 이 엔드포인트는 Primary 엔드포인트와 동일하다.

다시 ECS의 작업 정의로 돌아와서 원래 있던 작업 정의에 다음과 같이 REDIS_HOST와 REDIS_PORT 환경 변수를 추가한다.

 작동!
캐싱이 제대로 적용되지 있는지 확인하기 위해서 ECS로 생성된 EC2 인스턴스를 통해 ElastiCache Redis에 접속해서 데이터를 확인하면 다음과 같이 키가 존재하고 만료 시간이 설정한 180초라는 것을 알 수 있다!


DNS

4iz 토큰과 쿠키 포스트에서 쿠키의 여러 속성 중에서 Secure 속성은 true이면 HTTPS만 허용하여 중간자 공격을 완화한다. 그 순간 HTTPS를 설정하는 방법을 배우고 실제로 설정해야 한다는 생각을 했는데 드디어 시간이 왔다! 중간자 공격에서 공격자는 클라이언트와 서버 사이에 자신을 위치시켜 트래픽을 볼 수 있고 이를 수정할 수 있다. 평문으로 데이터를 전송하는 HTTP와 달리 HTTPS는 암호화를 적용하기 때문에 공격자가 트래픽을 보더라도 의미를 파악할 수 없다. 암호화뿐만 아니라 HTTPS는 웹 사이트의 신원을 인증할 수 있다. 그런데 HTTPS를 구현하려면 인증 기관에서 발급한 인증서가 필요한데 다행히도 AWS Certificate Manager가 이를 제공한다! 하지만 먼저 도메인을 등록해야 하는데 AWS Route 53을 통해 도메인을 등록할 수 있다.

Route 53은 고가용성 및 확장 가능하며 완전히 관리되는 DNS 서비스이다. Route 53을 사용하여 도메인 등록, DNS 라우팅 및 상태 확인을 포함한 세 가지 주요 기능을 수행할 수 있다. DNS(Domain Name System)은 사람이 이해하기 쉬운 호스트 이름을 기계가 이해할 수 있는 IP 주소로 변환하는데 계층적인 구조를 띈다. https://abc.www.wow.com.에서 마지막의 .는 루트, .com.은 최상위 도메인(TLD), .wow.com.은 2단계 도메인(SLD), .www.wow.com.은 하위 도메인, abc.www.wow.com.은 전체 주소 도메인 이름(FQDN), https는 프로토콜, 전체를 URL이라고 부른다.

일단 도메인을 등록해야 하는데 AWS에서 Route 53으로 가서 Domains의 Registered domains에 Register domains를 클릭한다. 사용 가능한 도메인 이름을 클릭하고 13달러를 지불한다! 결제를 하고 절차대로 수행하면 Domains의 Registered domains에 도메인이 다음과 같이 등록되어 있다!

Route 53의 레코드는 트래픽을 특정 도메인으로 어떻게 라우팅할지 정의한다. 각 레코드에는 도메인 이름 또는 서브도메인, 레코드 유형, 레코드의 값, 라우팅 정책(Route 53이 쿼리에 응답하는 방식), TTL(레코드가 DNS Resolver에 캐시될 시간)이 존재한다. 레코드 타입은 A, AAAA, CNAME, NS가 기본 타입이고 더 많은 타입이 있는데 나는 IPv4를 사용할 것이기 때문에 A 타입을 사용한다.

이제 라우팅을 설정하기 위해 Hosted Zones으로 가자! Hosted Zones는 레코드의 컨테이너로 트래픽을 도메인 및 서브도메인으로 어떻게 라우팅할지 정의한다. 공인 Hosted Zones과 사설 Hosted Zones이 있는데 전자는 인터넷 상의 트래픽을 라우팅하는 방법을 지정하는 레코드를 포함하고 후자는 VPC 상의 트래픽을 라우팅하는 방법을 지정하는 레코드를 포함한다. 당연히 나는 공인을 사용해서 트래픽을 로드 밸런서로 라우팅해야 한다.

Hosted zones의 Records에서 Create record를 클릭한다.여기서 Alias를 활성화 해야한다! 근데 Alias가 뭘까? 이 질문에 답을 하기 전에 CNAME과 Alias의 차이점을 알아보자! CNAME은 호스트 이름을 다른 호스트 이름으로 지정할 수 있게 해준다(e.g., api.wow.com -> hmm.yam.com). 하지만 CNAME은 루트가 아닌 도메인 이름에만 작동한다. 즉, api.wow.com인 경우에만 작동하며 wow.com 자체에는 작동하지 않는다. 반면에 Alias는 호스트 이름을 특정 AWS 자원으로 지정할 수 있다(api.wow.com -> hmm.amazonaws.com). 가장 큰 차이점은 Alias를 루트 도메인 및 루트가 아닌 도메인 모두에 사용할 수 있다. 즉, wow.com도 작동한다! 

Record name은 루트 도메인을 그래도 사용할 것이기 때문에 비워둔다. Route traffic to에서 엔드포인트는 Alias to Application and Classic Load Balancer을 리전은 ap-northeast-2을 선택한다. 라우팅 정책은 굉장히 많아서 기본 Simple routing으로 설정했다.


HTTPS

이제 도메인을 등록했고 DNS 라우팅도 설정했으니 HTTPS를 적용하자! AWS에서 Certificate Manager로 이동한다. List certificates의 Certificates에서 Request를 클릭한다. 공인 인증서를 선택하고 Route 53에서 생성한 도메인 이름을 입력한다! DNS로 도메인 소유권을 검증하기 때문에
Route 53에서 Certificate Manager가 CNAME 레코드를 생성한다. 모든 과정을 끝나면 인증서가 나와있다!

진짜 마지막으로 로드 밸런서에서 HTTPS:443에서 트래픽을 허용하도록 새로운 리스너를 추가하고 보안을 위해 HTTP:80에서 듣고 있는 리스너를 수정한다! 프로토콜은 HTTPS, 라우팅 행위는 대상 그룹으로 전달을 선택하고 이전에 만든 대상 그룹을 선택한다.

Secure listener settings에서 From ACM을 선택하고 아까 생성한 공인 인증서를 선택한다!

처음 만든 HTTP:80 리스너에서 HTTP:80으로 들어오는 트래픽을 HTTPS:443으로 재전송한다.

깜빡했는데 로드 밸런서의 보안 그룹에서 HTTPS로 들어오는 트래픽을 허용하는 인바운드 규칙을 추가해야 한다! 그럼 진짜 끝!
HTTPS 적용 완료!

update: 2024.01.27

댓글 없음:

댓글 쓰기