며칠간의 삽질을 하면서 느낀 점이 있다. 꼭 IaC로 모든 설정을 관리해야 할까? 라는 의문이었다. 당연히 IaC는 반복적이고 복잡한 인프라 설정을 코드로 대신할 수 있는 편리한 도구지만, 그 도구에 익숙하지 않아서 시간이 오래 걸린다면 처음에는 IaC로 할 수 있는 일부분만 구현해도 좋겠다는 생각이 들었다. 

 

그래서 어제 언급한 블로그를 찾아서 VPC 선언까지는 IaC로 해 보고, 그 외에 다른 것들은 직접 선언해보기로 했다. 그게 더 빠르게 구현할 수 있는 방법이라고 생각했다. 

 

사실 이렇게 마음먹은 이유가 또 있는데, 바로 ECS를 사용하게 되었을 때의 비용 문제였다. 소마에서 지원해주는 서버 비용을 봤을 때 AWS 인프라에서 돈이 가장 많이 들었던 두 개가 바로 ECS와 RDS였다. RDS의 경우는 구글 드라이브 동기화나, 프리 티어를 사용하는 등의 대체재가 있었다. 그러나 ECS의 경우는 이를 사용할 수 없다면 docker swarm 등의 다른 대체재를 생각해봐야 했다. 무궁무진한 방법들이 있다는 것을 알기에 GPT 질의를 통해 선택지를 좁혀 보기로 했다. 

aws에서 제공하는 여러 서비스들 중 ECS를 잘 쓰고 있었어. 그런데 비용 문제로 이제는 ECS 말고 다른 서비스를 찾아봐야 할 것 같아. CI/CD 파이프라인 구축에서 ECS와 같은 역할을 하는 유사한 다른 서비스들을 추천해줘
그러면 [내가 이해한 것]을 바탕으로 '가장 저렴한 방법'과 '초보자가 가장 쉽게 따라할 수 있는 방법'의 순위를 각각 알려줘

[내가 이해한 것]
총 5개의 옵션이 있다. 
1. aws fargate + app runner
2. google cloud run + github actions
3. aws lambda + serverless
4. docker swarm
5. kubernetes

 

GPT는 총 5개의 옵션을 제안해 주었고, 이 중에서 중요한 것이 '빠르게 구현 가능한지'와 '비용이 저렴한지'였다. 의외로 3번 옵션인 AWS lambda와 serverless 아키텍처를 결합한 옵션 모두가 저렴하면서도 초보자도 빠르게 구현할 수 있다고 했다.

 

저렴한 이유는 트래픽에 비례해서 비용이 증가하는 구조이므로 사용자가 적을 경우 비용이 많이 발생하지 않고, 서버리스이기 때문에 별도의 서버 관리 비용이 없다는 것이었다. 

 

또한 초보자가 빠르게 구현할 수 있는 이유는 함수 단위로 실행되므로 구체적인 인프라 설정이 필요 없다고 했다. yaml 파일만으로도 설정이 가능한 것으로 보였다. 

 

암튼 그렇게 해서 ECS는 AWS lambda로 대체하게 되었다. 아이러니하게도 AWS 생태계를 벗어나지 않았다..! 이러면 이제 aws lambda도 같은 aws provider 안에서 작성 및 apply가 가능하긴 한데... 일단 한 번에 너무 많은 걸 하려고 하지 말고 나에게 생소하고 복잡했던 vpc 설정만 terraform으로 대신해 보자. 

 

💻 aws vpc, subnet, availability zone에 대해 알아보기

어제의 블로그처럼만 따라하면 vpc가 생성된다. 그러면 다 된 거 아닌가?라고 생각할 수 있는데, vpc만 선언하면 되는 게 아니라 그 안에 있는 public/private subnet 등의 추가 설정이 필요하므로 아직 다 된 것이 아니다. 

 

그리고 해당 블로그를 따라하면서 의아했던 부분이 있다. 바로 private subnet이었다. 처음엔 이게 왜 필요한지 의문이었다. 보안 때문인 것이라고만 알고 있는데, 괜히 복잡할 것 같아서 선언하지 않아도 되는 게 아닌가 싶어서 질의해봤다. 

aws vpc에서 subnet을 생성할 때 public, private subnet을 생성할 수 있잖아. 이때 private subnet을 설정해야 하는 이유가 있어?

 

private subnet은 보안 목적으로 생성하는 것이 맞았다. 우선 vpc 안에 subnet 개념이 있었다. 그리고 'vpc 내부 통신'이라는 개념이 있어서 vpc 내부 리소스들은 각자가 따로 통신할 수 있었다. 이 원리도 좀 궁금했는데, 일단은 private subnet의 역할을 먼저 알고 넘어가보자. private subnet 안에 있는 리소스들에 접근하려면, 같은 vpc 안에 있는 리소스들만 이 'vpc 내부 통신'을 통해서 접근할 수 있다. 그러므로 외부에서 vpc 안의 private subnet 안에 있는 리소스에 접근하는 것이 불가능하다. 이 사실 하나만으로도 보안을 증가시킬 수 있다. 

 

그러면 private subnet 안에 있는 리소스들은 아예 외부와 단절된 게 아닌가? 라는 의문이 생길 수 있다. 정확히는 외부에서의 직접적인 호출과는 단절되어 있다. 그러나 결과적으로는 외부에서 private subnet 안에 있는 리소스들을 사용할 수 있고, private subnet 안에 있는 리소스들도 필요하다면 vpc 외부로 요청을 보낼 수 있다. 어떻게 이게 가능할까?

 

우선 인바운드 트래픽, 즉 private subnet 안의 리소스로 접근하는 경우를 보자. 이 경우는 같은 vpc 안의 다른 public subnet에 존재하는 리소스를 통해 가능하다. vpc 안에는 여러 private subnet들과 public subnet들이 존재할 수 있는데, 이 public subnet 안에 있는 리소스들이 일종의 연결다리 역할을 한다고 이해했다. 

 

예시로는 간단하게 private subnet, public subnet을 사용한 아키텍처에서 유저의 요청이 들어왔을 때를 가정해 보자. 모든 subnet들은 하나의 vpc 안에 있다. vpc 안에는 public subnet이 있고(간단함을 위해서 하나의 리전, AZ만 있다고 가정해 보겠다), 이 안에는 로드밸런서(ALB)가 있다. vpc 안에는 private subnet도 있고, 이 안에는 웹 서버와 DB가 있다. 이 상태에서 유저의 요청이 alb로 들어온다. alb는 public subnet에 있기 때문에 외부의 요청을 받을 수 있다. 

 

그러면 alb는 vpc 내부 통신을 통해서 같은 vpc 안의 다른 private subnet에 있는 웹 서버를 호출한다. 그리고 웹 서버에서도 마찬가지로 vpc 내부 통신을 통해 필요 시 같은(다른 private subnet일 수도 있는데 그건 상관없다) private subnet에 있는 DB를 호출한다. 이런 식으로 통신이 일어난다고 한다. 

 

즉 중요한 자원인 서버와 DB를 public subnet에 두어 외부 접근을 가능하게 하지 않고도 로드밸런서를 두어서 이 리소스들에 대한 보안을 증가시킬 수 있겠다!

 

그렇다면 반대의 경우인 아웃바운드 트래픽(private subnet 안에 있는 리소스에서 외부로 요청을 보내는 경우)은 어떨까? 이 경우는 특별한 NAT Gateway라는 친구가 사용된다고 한다. NAT는 network address translation, 즉 네트워크 주소 변환을 의미한다.

 

컴과 수업에서 배운 기억이 희미하게 있는데, 외부와 분리된 private subnet에서 public한 웹 서버 등으로 요청을 보낼 때 거치는 과정이다. 이 과정이 필요한 이유는 private subnet에서 사용하는 주소를 public하게 사용될 수 있는 글로벌 ip주소로 바꿔줘야 하기 때문이다. 나도 여기까지만 생각이 나서 문서를 참고했다. 

 

좀 더 구체적으로는 private subnet의 주소가 글로벌 ip주소로 변환되는 시점이 있다고 이해했다. 그 시점(경계)에 있는 라우터(경계 라우터)에서 이 NAT가 발생한다. private subnet에서 존재하는 요청자의 ip주소 대신, 글로벌 ip주소로 사용될 수 있는 주소를 송신자의 ip주소로 할당하고 패킷을 라우팅한다고 이해했다. 이것도 컴과 과목에서는 아예 하나의 챕터가 따로 있을 정도로 깊은 주제였던 기억이 있어서 일단은 여기까지만 알아보자. 

 

아무튼 private subnet을 설정하고 그 안에 웹 서버와 DB를 두어야 하는 이유는 이것만으로도 설명할 수 있겠다. 그렇다면 또 다른 의문이 생긴다. private, public subnet은 한 쌍의 페어라고 가정한다면, 한 vpc 안에 몇 개의 subnet 페어가 필요할까? 이 역시 모호한 질문이었으므로 질의를 해 보았다. 

aws vpc 안에 존재하는 private, public subnet은 한 쌍의 페어라고 가정한다면, 한 vpc 안에 몇 개의 subnet 페어가 필요할까?

 

GPT답게 하나의 정해진 답을 주지는 않았는데, 이전에 들어본 것처럼 이 페어의 개수는 '가용 영역(AZ)'의 개수에 영향을 받는다고 한다. 여기서 또 vpc와 subnet, 그리고 az(가용 영역)의 관계가 모호하게 느껴져서 추가 질의를 해 보았다. 

vpc 안에 subnet이라는 개념이 존재한다고 이해했어. 그럼 available zone(AZ)은 어떤 개념이야? AZ는 vpc, subnet과 어떤 관계가 있는지 알려줘

 

정의를 살펴보자면 다음과 같다. AZ는 하나의 리전(region) 내에 존재하는 독립적인 물리적 데이터 센터라고 한다. 그리고 vpc는 하나의 리전에 걸쳐 존재한다. 반면 vpc 안의 subnet은 각 az별로 존재한다. 그러니까 하나의 vpc를 선언한 다음, 해당 vpc가 존재하는 리전에 총 몇 개의 az가 있는지에 따라서 private/public subnet 페어의 개수가 달라지겠다. 보통은 az의 개수만큼 페어를 선언하는 것이 일반적이라고 한다. 그 이유는 고가용성과 장애 대응을 위해서라고 한다. 

 

어쨌든 결론은 나왔다. 특수한 상황이 아닌 이상 ALB 등에서도 모든 AZ에 걸쳐 리소스를 배포하도록 설정되어 있다고 한다. 그리고 나는 특수한 상황은 아니니, 일단은 모든 AZ(나의 경우에는 ap-northeast-2 리전의 2a, 2b, 2c, 2d AZ가 되겠다)에 리소스를 배포하도록 설정하자. 그러려면 총 4쌍의 private/public subnet 페어들이 필요할 것이다. 

 

그런데 이렇게 보니 vpc를 만드는 것 외에도 private, public subnet을 만들고, 각 리소스를 subnet에 배치하고, 각 리소스끼리의 연결(가령 ALB가 웹 서버 리소스를 호출하도록 하기 등등)이 필요한 것 같다.... 아무래도 IaC는 다음으로 미루고, VPC를 직접 설정해보는 것부터 해 보면 좋겠다. 

 

💻 aws vpc 설정하기

aws의 여러 서비스들 중 'vpc'를 누르고 'vpc 생성'을 누르면 다음과 같은 화면이 나온다. 여기서 vpc만 설정할지, 아니면 그 외에 관련된 리소스(서브넷과 인터넷 게이트웨이 등)까지 같이 생성할지를 선택할 수 있다. 원래는 관련 리소스도 같이 생성하는 것이 편하겠으나, 여기서는 az를 최대 4개가 아닌 3개까지밖에 기본 옵션으로 제공하지 않아서 직접 생성하기로 했다. 그러므로 'vpc 등'이 아니라 'vpc만'을 선택했다. 

 

cidr도 사실 어떻게 등록해야 할지 감이 잘 오지 않았는데, 이전에 '10.0.0.0/16'으로 등록한 것이 기억나서 이 값을 그대로 따랐다. 이렇게 등록하면 약 60000개가 넘는 ip주소를 vpc에서 사용할 수 있다고 한다. 

 

이렇게 설정을 해 주니 아래와 같이 새 vpc가 등록되었다. 

 

이제는 서브넷을 생성할 차례이다. 방금 생성한 'onestep' 별칭을 가진 vpc를 대상으로 서브넷을 총 8개(public 4개, private 4개) 만들어주자. 

 

우선 vpc 선택에서는 방금 만든 vpc를 선택해주고, 아래와 비슷하게 할당하면 된다. 

 

이런 방식으로 총 가용 영역(az)의 수만큼 만들어주자. 나의 경우 리전이 ap-northeast-2라서 총 가용 영역이 4개인 관계로 4개 만들어줬다. 

 

그리고 현재는 '퍼블릭 액세스 차단' 필드가 비활성 상태이다. 현재는 퍼블릭 서브넷을 만든 거라 이게 맞다. 이걸 활성 상태로 만들어준다면 private subnet의 기능을 할 수 있겠다. 나머지 private subnet들도 모두 만들고, 해당 서브넷들에 대해서는 퍼블릭 액세스를 차단해 줘야겠다. 관련 메뉴가 한 눈에 보이지는 않아서 GPT 질의를 사용해봤다.

aws vpc를 만들고 subnet들을 만들었어. 그런데 이 subnet들을 private subnet으로 만들고 싶어. 어떻게 하면 돼?

 

방법은 서브넷에 연결된 라우팅 테이블(routing table)에서 인터넷 게이트웨이(IGW)로의 연결을 제거하는 것이었다. 그러려면 우선 vpc 안에 있는 라우팅 테이블이 어떤 서브넷들에 연결되어 있는지를 확인하고, 만약 이 중에 private subnet이 포함된다면 그 연결을 제거해주면 되겠다.

 

'vpc 대시보드'에서 '라우팅 테이블' 메뉴를 눌러서 들어가봤다. 그런데 방금 만들어 준 8개의 서브넷들이 모두 라우팅 테이블들과 연결되어 있지 않았다. 이럴 경우 퍼블릭 액세스를 차단하고 있진 않지만, IGW와 연결된 라우팅 테이블과 연결되지 않았으므로 모두가 private subnet처럼 동작하고 있었다고 이해했다. 그래서 이들 중 public이 포함된 서브넷들만 해당 라우팅 테이블과 연결해 주었다. 

 

원래는 '명시적 서브넷 연결'에 나온 서브넷이 한 개도 없었는데, 아래처럼 public 서브넷들만 추가해 주었다. 

 

그런데 또 궁금한 게 생겼다. 그러면 private 서브넷들은 기본 라우팅 테이블과만 연결되어 있다고 했다. 그러면 public 서브넷에 있는 리소스에서 private 서브넷에 있는 리소스로 연결이 필요할 때 어떻게 연결할까? 즉 public 서브넷과 private 서브넷은 현재 같은 vpc에 있다는 것 말고는 라우팅 테이블 등과 일말의 연결고리도 없는 상황인데, 이 상황에서 어떻게 가령 public subnet 01이 private subnet 01을 찾을 수 있을까?즉 이 두 서브넷은 vpc와 az가 같지만, 그 외의 접점은 없다고 이해한 상황이다. 바로 질의해봤다. 

 

결과는 vpc 내부 통신 메커니즘(이게 뭔지는 아직 모른다)을 통해 모든 vpc 내부의 리소스들은 서로 통신이 가능했다. 이 원리는 vpc 내 서브넷의 라우팅 테이블에 공통적으로 존재하는 'local'이라는 레코드 때문이란다. 찾아보니 정말 따로 명시해주지 않았음에도 기본적으로 이 하나의 레코드가 들어 있었다. 

 

이쯤 되니 슬슬 복잡해진다. 그림을 한번 그려보자. 기본값으로 IGW를 통해 외부로 연결되지 않은 subnet들은 다음과 같은 모습일 것이다. 

 

여기서 public 서브넷들은 vpc의 라우팅 테이블을 통해서 IGW(인터넷 게이트웨이)와 연결되어야 하겠다. 즉 아래의 그림과 같은 상황을 원한다. 

그러려면 위에서 public 서브넷들만 연결한 라우팅 테이블이 IGW와 연결되어 있는지를 확인해봐야 하겠다. 그런데 해당 라우팅 테이블의 '라우팅' 섹션을 확인해보니 local 네트워크밖에 없어서, 어떻게 확인해야 할지 모르겠다. 

하나의 vpc에서 사용되는 라우팅 테이블이 있다고 할 때, 이 라우팅 테이블을 인터넷 게이트웨이와 연결하고 싶어. 어떻게 하면 될까?

 

다음과 같은 단계를 거쳐야 했다. 

  1. 인터넷 게이트웨이를 (없으면) 만든다. 
  2. 1번에서 만든 IGW을 vpc와 연결한다. 
  3. 라우팅 테이블과 IGW을 연결한다. 
  4. 서브넷과 3번의 라우팅 테이블을 연결한다. (나는 이 부분을 위에서 했다.)
  5. 서브넷 설정에서 'ipv4 주소 자동할당' 옵션을 활성화한다. 왜냐하면 퍼블릭 서브넷 내의 리소스가 글로벌 ip를 가질 수 있어야 외부에서 접근할 수 있기 때문이다. 
  6. 아웃바운드/인바운드 규칙 편집을 통해 퍼블릭 서브넷으로 접근이 가능하도록 수정한다.

하나씩 해 보자. 우선 1번을 확인하기 위해서 'vpc' 탭의 '인터넷 게이트웨이'를 눌러보았다. 그랬더니 이미 만들어진 IGW 리소스가 보였다. 이걸 사용해도 되는 건지, 아니면 내가 새 vpc를 만들었으니 IGW도 새로 만들어줘야 하는건지 싶어서 질의해봤다. 

하나의 vpc당 하나의 인터넷 게이트웨이가 필요한 거야? 아니면 여러 vpc를 하나의 인터넷 게이트웨이에 연결할 수도 있는 거야?

 

GPT 피셜 vpc와 IGW는 1:1 관계라고 한다. 즉 vpc를 새로 만들었으면 igw도 새로 만들어야 하겠다. 만들어주고 vpc에 연결도 해 주었다. 원래 이 과정을 담아보려고 했는데 생각보다 너무 간단해서(30초컷) 캡처는 생략했다...

 

이제는 vpc 내의 라우팅 테이블과 IGW을 연결할 차례이다. '라우팅 테이블' 탭에 가서 연결할 라우팅 테이블을 선택한 뒤(없으면 만들어주자) '라우팅 편집' 메뉴를 눌러보자. 

 

그러면 아래와 같은 화면(처음에는 local 레코드만 있다)이 나오는데, 여기서 대상을 '0.0.0.0/0' 즉 모든 ip주소로 설정해 주고, 연결 대상을 인터넷 게이트웨이로 설정해준다. 그러면 아까 만든 igw를 선택한 것인지를 옵션으로 확인할 수 있고, 여기서 클릭을 해주면 된다. 

 

변경 사항을 저장하고 확인해보면, 기존엔 local만 있었던 라우팅 테이블 레코드에 인터넷 게이트웨이가 추가되었다. 

 

그 다음엔 라우팅 테이블과 연결된 서브넷이 이 인터넷 게이트웨이를 사용할 수 있도록 연결해줘야 한다. 그런데 이 작업은 위에서 이미 서브넷과 라우팅 테이블을 연결해 주었으므로 건너뛰자. 

 

이제는 퍼블릭 서브넷 설정에서 'ipv4 주소 자동할당' 옵션을 켜자. 그래야 퍼블릭 서브넷 안에 소속된 리소스들(가령 alb)이 ipv4 주소를 할당받을 수 있다. 아래와 같이 서브넷 편집 화면에서 체크박스를 눌러주면 된다. 

 

마지막으로 서브넷에 연결된 보안 관련 리소스의 아웃바운드/인바운드 규칙을 확인해 주자. 신기한 것은 보안 관련 리소스래서 '보안 그룹'만 있는 줄 알았더니 '네트워크 ACL'이라는 친구가 있었다. 처음 보는 녀석이었다. 

 

우선 익숙한 '보안 그룹'부터 설정해보자. 인바운드 규칙의 경우, 해당 개발 서버에서는 HTTP와 HTTPS 연결만 허용하려고 한다. 반면 아웃바운드 규칙의 경우 모든 연결을 허용해보겠다. 

 

이제 '네트워크 ACL'을 설정해 보자. 이 친구와는 초면이었기에 우선 이 녀석이 뭔지부터 알아야 하겠다. 

vpc 리소스의 '보안' 탭에 보안 그룹과 네트워크 ACL이라는 메뉴가 두 개 있는 것을 봤어. 보안 그룹과 네트워크 ACL이 각각 무엇이고 어떻게 다른지 설명해줘

 

보안 그룹과 ACL 모두가상 방화벽이라는 공통점이 있었다. 다만 세 가지의 차이점이 있었다. 

 

  1. 보안 그룹의 경우는 개별 리소스별로 정의되는 방화벽이라면, ACL은 서브넷 단위로 정의되는 방화벽이었다.
  2. 보안 그룹은 stateful 하기 때문에 인바운드 규칙이 허용하는 트래픽은 아웃바운드 규칙에서도 허용된다. 반면 ACL은 stateless 하기 때문에 인바운드 규칙이 어떤 트래픽을 허용하더라도 아웃바운드 규칙에서 허용하려면 별도로 설정을 해 주어야 했다. 
  3. 보안 그룹은 특정 트래픽을 '허용'하는 액션만 가능하지만, ACL은 특정 트래픽을 '제한'하는 액션도 가능했다.

 

왜 이게 필요한가 생각해 봤더니, 서브넷은 아무래도 여러 리소스들이 소속되어 있는 네트워크이다보니 개별적으로 보안그룹을 설정하는것도 물론 가능하겠지만 제어 수준을 전반적으로 관리하기 위해서는 네트워크 ACL이 훨씬 편리하겠다는 생각을 했다. 

 

아무튼 네트워크 ACL은 이런 녀석이었다. 이제 여기서 인터넷의 모든 트래픽을 막고 있는지만 확인해보면 되겠다. '보안' 탭의 '네트워크 ACL'을 눌러 확인해보니 다행히 서브넷 단위에서는 모든 트래픽을 허용하고 있었다. 

 

✅ 궁금한 점

  1. VPC와 IGW가 1:1 관계여야 하는 이유는 무엇일까?
  2. IGW의 정확한 정의는 무엇이고, IGW는 왜 필요할까?
  3. IGW로 연결한다는 게 구체적으로 무슨 의미일까?
  4. vpc 내부 통신의 원리가 궁금하다.

💻 aws route53 module 선언하기

vpc를 만들었으면 이제 route53 호스팅 리소스를 사용해봐야 되겠다. 그래야 다른 aws 리소스들을 vpc 내부에서만이 아니라 외부에서도 퍼블릭하게 사용할 수 있으니 말이다. 어제의 글에서와 같은 문서를 참고하여 나온 예제 코드를 보면서, GPT에게도 질의를 던져 보았다. 

aws route53 리소스를 사용해서 stepby.one이라는 도메인을 호스팅하는 코드를 작성해줘. 중간중간 필요한 값이 있다면 임의로 할당하고 주석으로 임의로 할당했다고만 표현해줘

 

그러다가 문득 내가 잘 하고 있는 게 맞나? 라는 의문이 들었다. 일단 냅다 코드를 입력하는 게 맞을까 싶어서 아래와 같은 명령어를 실행해봤다. 

terraform init
terraform apply

 

그런데 변하는 것이 없었다. 'No changes'란다. 공식문서를 찾아보는 접근 자체는 좋았는데, 그러다보니 실제로 어떤 코드를 입력해야 적용되는지에 대한 이해는 부족했던 것 같다. 'terraform vpc 등록'이라고 검색해서 찾은 블로그를 보니 실제 사용되는 코드에서는 직접 module을 정의할 게 아니라 resource를 가져오는 방식이었다. 

 

물론 당연히 직접 module을 만들고 그걸 사용해서도 terraform을 통해 aws 리소스를 만들 수 있었겠으나, 나에게는 아직 terraform에 대해서는 잘 모르기 때문에 코드를 보고 하나하나 따라서 입력하는 게 더 맞겠다고 판단했다. 그래서 이 코드를 그대로 가져와 보았다. 

provider "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region     = var.aws_region
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "terraform-101"
  }
}

 

그리고 이 블로그에서 알게 된 새로운 사실인데, terraform apply 명령어를 하기 전 terraform plan 명령어를 통해 미리 어떤 리소스가 만들어질지를 볼 수 있다고 한다. 마치 django에서 makemigrations와 migrate의 차이인 것 같았다. 그래서 이 명령어도 한번 테스트 해 봤다. 

terraform plan

 

이번에는 terraform을 통해 aws vpc 리소스가 제대로 추가될 모양이다. 나머지 부분은 내일 이어서 해 보자!

 오늘 배운 것

어제 배운 것을 정리해보면, 우선 AWS provider를 선언한 다음에 VPC, Route53, VPC 보안그룹, 서브넷, EC2 spot 인스턴스 순서대로 생성하면 된다고 이해했다. (물론 아직 RDS, ECS, ECR 등의 리소스는 생성하지 않아서 추가적인 정보가 필요하다.)

 

우선 AWS provider부터 정의해보자. 'terraform aws provider'라고 검색하면 나오는 문서의 코드를 가져와서 변형해봤다. 여기서부터는 vscode 에디터에 입력하면서 해 보자. 다만 access_key와 secret_key 값은 github에 올라가면 안 되는 값이므로 aws의 secrets manager에서 가져오도록 작성해야 하겠다. 

 

💻 aws provider 선언하기

우선 variables.tf 파일을 별도로 만들어서 해당 파일 안에다가 사용할 변수 값들을 집어넣어 주자. 

variable "aws_access_key" {
    type = "string"
}
variable "aws_secret_key" {
    type = "string"
}
variable "aws_region" {
    type = "string"
}

 

그리고 terraform.tfvars 파일을 만들어서 해당 variable의 값으로 사용될 실제 값들을 넣어주자. 

aws_access_key = "your-access-key-id"
aws_secret_key = "your-secret-key"
aws_region     = "us-east-1"

 

마지막으로 main.tf 파일에다가 provider를 선언하는 로직을 만들어주자. 이렇게 하면 provider(일종의 plugin)를 사용해서 terraform에서 aws 서비스의 리소스들을 사용할 수 있다. 

provider "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region     = var.aws_region
}

 

💻 aws vpc module 선언하기

다음으로는 퍼블릭 DNS와는 독립적인, aws 내부에서 동작하는 private network라고 할 수 있는 vpc를 정의하는 작업이 우선이겠다. rds도, ecr이나 ecs, ec2도 vpc 내부에서 선언되기 때문이다. 'aws vpc'라고 검색하면 나오는 문서를 참고해서 코드를 일부 바꿔 보았다. 참고로 해당 코드는 modules 디렉토리 안에 vpc.tf라는 파일에 따로 저장해 주었다. 

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = "vpc_onestep"
  cidr = "10.0.0.0/16"

  azs             = ["ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c", "ap-northeast-2d"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24", "10.0.4.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24", "10.0.104.0/24"]

  enable_nat_gateway = true
  enable_vpn_gateway = true

  tags = {
    Terraform = "true"
    Environment = "dev"
  }
}

 

이게 정확히 무슨 의미일까? 하나씩 봐 보자. 

 

우선 cidr 변수의 의미가 궁금했다. 

해당 [코드]에서 cidr 변수가 무엇을 의미하는지 설명해줘

 

cidr 변수는 예전 네트워크 시간에서 배웠다시피 Classless Inter Domain Routing의 약자로, 이전에 주소 공간을 A, B, C, D, E라는 클래스로 나눠서 정의하던 방식 대신 클래스로 구분하지 않으면서 IP 주소를 할당하는 새로운 방식이었다. 다만 여기서의 cidr 변수는 VPC 내부의 주소를 할당하는 방식을 나타냈다. 

 

그리고 이후에 정의할 subnet(서브넷)들은 이 cidr에서 정의한 블록 내부의 주소 공간을 나눠서 사용하게 된단다. public subnet과 private subnet 모두 마찬가지였다. 그리고 여기서 az(available zone, 가용 영역)s, private_subnets, public_subnets의 length는 모두 같아야 하며, 각 subnet들의 i번째 원소에 해당하는 cidr 주소 블록이 i번째 az에 할당되는 주소 블록이 된다고 이해했다. 

 

그런데 왜 aws에서 vpc를 설정할 때 이런 정보들이 필요한 것일까?

aws의 vpc를 설정할 때 왜 az(availability zone), private_subnet, public_subnet이라는 개념이 필요할까? 각각의 변수들이 어떤 의미이고 서로 어떻게 연결되는지를 설명해줘

 

AZ(가용 영역)가 필요한 이유는 고가용성(한 AZ에서 장애가 발생해도 서비스를 정상적으로 이용할 수 있도록 함)과 재해 복구를 위해서라고 한다. 가용 영역은 한 리전에 여러 개가 있다고 한다. 예를 들면 eu-west-1이라는 리전(region)에는 총 3개(eu-west-1a, eu-west-1b, eu-west-1c)의 가용 영역(AZ)이 있다. 그래서 하나의 리전에 있는 하나의 가용 영역에서 장애가 발생해도 나머지 두 개의 가용 영역을 통해 서비스는 정상적으로 운영된다. 

 

그리고 하나의 AZ에는 여러 개의 서브넷이 포함될 수 있다. 즉 내가 이해하기로는 하나의 VPC 안에는 여러 개의 가용 영역이 포함될 수 있고, 하나의 가용 영역 안에는 여러 개의 서브넷(public, private 서브넷이 모두 포함됨)이 포함될 수 있다고 이해했다. 질의를 통해 이 이해가 맞는지를 점검해 보고 넘어가자. 

내가 이해하기로는 '하나의 VPC 안에는 여러 개의 가용 영역(AZ)이 포함될 수 있고, 하나의 가용 영역 안에는 여러 개의 서브넷(private, public 모두 포함)이 포함될 수 있다'고 이해했어. 이 이해가 맞으면 맞다고, 틀리면 틀리다고 말해주고 모르면 검색해서 정확히 알려줘

 

GPT 피셜로는 맞다고 한다. 그리고 하나의 VPC는 하나의 리전 내에서만 존재할 수 있다고 한다.

 

 오늘 배운 것

저번 포스트에서 IaC를 통해 기존 소마 계정의 AWS 설정을 가져오는 데에는 실패했다. 그런데 다르게 생각해보면 IaC 도구인 terraform을 통해서 코드로 여전히 새 설정을 세팅하는 건 가능하지 않을까? 그렇다면 이 복잡한 과정을 코드로 풀어본다면 문제가 아주 조금은 더 간단해질 수도 있다는 희망을 가져보았다. 

 

그리고 두 가지의 질의도 해 보았다. 

  1. terraform 파일은 버전 관리를 어떻게 할까? 일반 코드와 똑같이 Github에다 올려놓으면 되려나
  2. 만약 그렇다면 terraform 파일에 들어가는 환경변수들은 또 어떻게 관리하면 좋을까

1번은 내가 생각하는 것처럼 github에서 버전 관리를 하는 경우도 많았다. 그리고 2번의 경우는 코드에서 사용하는 환경변수 관리와 똑같았다. 나는 주로 환경변수 관리를 위해서 AWS Secrets Manager를 이용하는데 그 작업을 해 주면 되겠다. 

 

그런데 궁금한 점이 또 있다. AWS의 ECR, ECS, EC2 인스턴스를 terraform에서 새로 생성한다고 가정해 보자. 그러려면 인스턴스가 아직 생성되지 않았으므로 인스턴스의 ID 값 등이 없을 것이다. 어떻게 생성할까? 검색하다 terraform 공식 사이트의 ec2 생성 가이드를 발견하고 읽어보았다. 여러 EC2 인스턴스 중에서도 나는 비용이 저렴한 spot 인스턴스를 사용할 것이라서 관련 코드를 복사해 주었다. 

module "ec2_instance" {
  source  = "terraform-aws-modules/ec2-instance/aws"

  name = "spot-instance"

  create_spot_instance = true
  spot_price           = "0.60"
  spot_type            = "persistent"

  instance_type          = "t2.micro"
  key_name               = "user1"
  monitoring             = true
  vpc_security_group_ids = ["sg-12345678"]
  subnet_id              = "subnet-eddcdzz4"

  tags = {
    Terraform   = "true"
    Environment = "dev"
  }
}

 

그런데 코드를 복사하고 보니 EC2 인스턴스 관련 정보를 정의하기 위해서는 가령 subnet id나 vpc 보안그룹 id 등의 추가 정보가 필요했다. 다른 정보에 의존하고 있었다.

 

그러면 subnet id랑 vpc 보안그룹 id는 또 어떻게 생성하나. 공식 사이트 검색창에 'aws subnet'을 입력하니 또 뭔가가 나왔다. 이번엔 이 코드를 사용해 보자. 

resource "aws_subnet" "main" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"

  tags = {
    Name = "Main"
  }
}

 

vpc 보안그룹도 검색해 보았다. 'aws security group'을 검색해보니 또 다른 뭔가가 나왔다. 이 코드도 일단 복붙해 보자. 이 코드는 그나마 좀 익숙한 감이 있다. 

resource "aws_security_group" "allow_tls" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic and all outbound traffic"
  vpc_id      = aws_vpc.main.id

  tags = {
    Name = "allow_tls"
  }
}

resource "aws_vpc_security_group_ingress_rule" "allow_tls_ipv4" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv4         = aws_vpc.main.cidr_block
  from_port         = 443
  ip_protocol       = "tcp"
  to_port           = 443
}

resource "aws_vpc_security_group_ingress_rule" "allow_tls_ipv6" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv6         = aws_vpc.main.ipv6_cidr_block
  from_port         = 443
  ip_protocol       = "tcp"
  to_port           = 443
}

resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1" # semantically equivalent to all ports
}

resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv6" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv6         = "::/0"
  ip_protocol       = "-1" # semantically equivalent to all ports
}

 

vpc security group 리소스를 정의하기 위해서 더 이상 필요한 정보가 없을까? 잘 모르겠다. GPT에게 질의를 해 보자. 

terraform을 사용해서 해당 [링크]를 보고 AWS의 VPC 보안그룹을 설정하려고 해. 위의 코드를 가져와서 사용하려면 추가로 더 필요한 정보가 있을까? 있으면 있다고 없으면 없다고 말해주고, 모르면 검색해서 알려줘

 

GPT 피셜 'VPC'의 id가 필요하단다. 'vpc'라고 검색하니 또 다른 module 문서가 나왔다. 일단 기본 코드를 냅다 가져와 보자. 

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway = true
  enable_vpn_gateway = true

  tags = {
    Terraform = "true"
    Environment = "dev"
  }
}

 

소스 코드 상 해당 코드를 작성하기 위해서 더 이상 다른 정보가 필요하지는 않아 보인다. 다만 의문이 생긴다. 아직 Route53과 같은 호스팅 서비스를 이용해서 IP주소를 할당한 것도 아닌데 어떻게 알아서 CIDR 주소를 할당할 수 있을까? 이 역시 질의로 물어보았다. 

[코드]를 보고 든 [의문점]에 대해서 해답을 알려줘

 

여기서 정의한 cidr, private_subnets, public_subnets 등의 값은 VPC와 관련된 것으로, 퍼블릭 DNS와는 관계 없는 주소였다. VPC는 AWS의 내부 네트워크 구조이기 때문이다. 애초의 VPC는 Virtual Private Cloud의 약자였다. AWS 내부의 주소 체계이며, 퍼블릭 주소 체계와는 관련 없는 별도의 주소 체계여서 호스팅 서비스와 연결하지 않고 이용이 가능했던 것이다. 

 

그렇다면 두 가지 의문이 생긴다. 

  1. 이 상황에서 Route53과 같은 호스팅 서비스와는 어떻게 연결할 수 있을까?
  2. 호스팅 서비스와 연결하지 않는다면 VPC 내에서만 서비스가 동작한다는 것인데, 어떻게 이게 가능할까?

이 역시 질의를 통해 물어보았다. 1번 질문은 웹사이트에서 찾아보면 되니 2번에 대해서만 일단 물어봤다. 

그렇다면 VPC는 DNS와는 별도의 주소 체계인 것이고 private network라고 이해했어. 그렇다면 만약 Route53과 같은 별도의 호스팅 서비스를 정의하지 않고 VPC 내부에서만 서비스가 동작하게 한다면, 어떻게 해당 서비스에 접속할 수 있어?

 

같은 VPC 내부에서만 접속이 가능하다고 한다. 복잡한 아키텍처나 보안 수준이 높은 아키텍처의 경우는 이러한 방식으로 접속이 이뤄질 수 있겠다. 우선은 우리 서비스의 일은 아니었다. 호스팅 서비스와 연결해서 공공 네트워크에서도 접속이 가능해야 했다. 'aws route53'을 입력하니 역시나 또 다른 module이 정의되어 있었다. 

 

이쯤 되니 궁금한 것이 생겼다. 공식문서 사이트에서 검색할 때 provider랑 module별로 검색결과가 구분되어 나오는데, provider와 module의 정확한 정의는 뭘까?

 

우선 module 공식문서에서는 module을 '같이 사용되는 여러 리소스들을 담아두는 container'라고 정의했다. 그에 비해 provider의 정의는 거의 연관이 없었다. 이전에도 한번 찾아봤던 것 같은데, terraform이 여러 클라우드 서비스 제공자나 SaaS 제공 업체들의 리소스를 잘 가져오고 다루기 위해 사용하는 플러그인이었다. 

 

그렇다면 providers와 modules 중 무엇을 클릭해야 할지 헷갈린다면, 지금 찾아보려는 내용이 아예 'AWS와 terraform 연결하기'처럼 독립적인 개별 서비스와 terraform을 연결하려는 활동인지 아닌지를 생각해보면 되겠다. AWS를 직접 terraform과 연결하기 위해서 맨 처음에 aws provider를 선언하는 것 외에는 내가 aws의 리소스들만 사용하는 이상은 다른 provider를 선언할 일이 없을 것 같다. 

 

아무튼 이 module의 코드도 가져와 보자. 여기서는 zones와 records라는 두 개의 모듈이 있었다.

module "zones" {
  source  = "terraform-aws-modules/route53/aws//modules/zones"
  version = "~> 3.0"

  zones = {
    "terraform-aws-modules-example.com" = {
      comment = "terraform-aws-modules-examples.com (production)"
      tags = {
        env = "production"
      }
    }

    "myapp.com" = {
      comment = "myapp.com"
    }
  }

  tags = {
    ManagedBy = "Terraform"
  }
}

module "records" {
  source  = "terraform-aws-modules/route53/aws//modules/records"
  version = "~> 3.0"

  zone_name = keys(module.zones.route53_zone_zone_id)[0]

  records = [
    {
      name    = "apigateway1"
      type    = "A"
      alias   = {
        name    = "d-10qxlbvagl.execute-api.eu-west-1.amazonaws.com"
        zone_id = "ZLY8HYME6SFAD"
      }
    },
    {
      name    = ""
      type    = "A"
      ttl     = 3600
      records = [
        "10.10.10.10",
      ]
    },
  ]

  depends_on = [module.zones]
}

 

records는 이전에 멘토님과 같이 route53을 연결했을 때 보았던 그 호스팅 레코드가 맞는 것 같았다. 다만 zones은 무엇인지 잘 모르겠다. 일단 질의를 해 봤다. 

[코드]에서 module별로 분리되는 'zones'와 'records'가 어떤 것인지 알려줘

 

zones는 AWS Route53에 DNS hosting zone을 생성하는 데 사용된다고 한다. 여기서부터는 VPC 내부에서만 사용되는 것이 아니라 VPC 내부에 정의된 여러 리소스들이 DNS와 연결되는 지점인 것이라고 이해했다. 그리고 이 zone은 해당 도메인에 대한 모든 record들의 컨테이너 역할을 한단다. 

 

 궁금한 점

  1. VPC gateway와 NAT gateway의 정의가 각각 무엇일까?
  2. terraform에서 provider와 module은 각각 무슨 개념이고 무슨 차이가 있을까?

 

 오늘 배운 것

A 계정에서 B 계정으로 terraform을 통해 인프라 정보를 넘기려면, 우선은 A 계정과 B 계정 모두 aws cli에 등록되어 있어야 했다. 아까 전에 cat 명령어로 조회했을 때는 default 계정만 갖고 있었고, 그 default 계정은 A 계정에 해당했다. 이제는 B 계정을 추가해 주어야 하겠다. 

 

B 계정을 추가하기 위한 명령어는 간단했다. 

aws configure --profile my-second-account	# 새 계정의 이름 (기본 계정: default)

 

그리고 이 명령어를 사용하려면 AWS 계정의 access id와 secret key 값이 필요했다. 확인해 보니 이미 사용하던 IAM 유저의 access key를 내가 하나 만들어 뒀었었다. 그런데 access key id는 계속 볼 수 있었지만 key secrets는 계속 볼 수 없었다. 그런데 이 값이 기억나지 않아서, 하나 더 만들 수밖에 없었다. '액세스 키 2'를 새로 만들었다. 

 

이제 위의 명령어를 입력해 주고, access key id와 key secrets 값을 잘 입력해 주었다. 

 

다시 aws cli에 등록된 계정 정보를 확인해 보니 새로운 계정이 잘 추가된 것을 볼 수 있었다. 

cat ~/.aws/credentials

 

이제 default 계정(기존 소마 계정)으로부터 terraform을 통해 등록된 인프라 정보를 earthyoung 계정으로 넘겨보자. 현재는 default 계정에 있는 정보를 'terraform import' 명령어를 통해 가져오기만 했을 뿐이다. 이를 새 계정에 적용하는 것은 또 다른 일이었으므로, 또 다른 질의를 날려 보았다. 

 

우선 아래 명령어를 통해 default 계정에서 여러 설정들을 잘 가져왔는지를 확인해 볼 수 있겠다. 다행히 어제 열심히 가져왔던 설정들이 잘 들어있었다. 

terraform state list

 

그리고 현재 terraform plugin(aws provider)에서 earthyoung(새 계정)을 사용하도록 설정을 바꿔 주어야겠다.

# dev.tf
provider "aws" {
  region  = "ap-northeast-2"
  profile = "earthyoung"
}

 

이후 해당 터미널에서 aws cli에서도 계속 새 계정을 사용하도록 설정을 바꿔 주고 싶다면 다음 명령어를 이용하자. 

export AWS_PROFILE=earthyoung

 

참고로 이렇게 커맨드로 export 문을 실행하면 terminal을 종료 후 재시작할 경우 export 된 변수가 다시 남아있지 않을 수도 있다고 알고 있다. 영구적인 반영을 원한다면 해당 명령어를 .zshrc 파일에 넣어 주는 것이 안전하겠다. 

 

어찌되었건 .tf 파일에서도 새 계정을 사용하도록 명시해 주었고, 터미널의 기본 계정도 바뀌어 주었으니 이제는 명령어를 통해 해당 설정이 잘 반영되었는지를 확인하자. 그런데 명령어를 돌렸더니 에러가 난다. 아마도 현재 시점은 소마 계정 이관 시점보다 지나서, 해당 Route53 및 EC2 레코드가 지워진 것이라고 판단했다. 

terraform plan

 

그렇다면 일단은 남은 다른 설정이라도 가져오면 좋으련만. 그러려면 현재 에러가 나는 레코드를 import에서 제거해야 하겠다. 'terraform state rm' 명령어로 import 된 설정들 중 특정 설정들만 골라서 제거할 수 있겠다. 

terraform state rm aws_route53_zone.route53_zone
terraform state rm aws_instance.ec2_instance

 

그리고 다시 명령어를 실행해 보았더니, 또 다른 예상치 못한 오류와 마주했다. ECS 클러스터와 SecretsManager에서 난 오류였다. 아마도 소마 계정이 suspended 상태라서 400에러가 난 것이라고 추측했다.

 

그런데 사실 ECS 클러스터를 사용할 수 없으면 ECR, ECS task definition, ECS service에 대한 정보도 모두 무의미한 것은 마찬가지였다. 그리고 Secrets Manager까지 제외하면 새 계정으로 넘길 수 있는 리소스는 RDS 뿐이었다. 하지만 RDS는 사실 인스턴스를 새로 시작해서, Django ORM을 통해 migrate를 하면 바로 백엔드 서버와 똑같은 상태를 사용할 수 있는 것이라서 큰 의미가 없었다. 

 

그래서 결국 IaC를 통해 default 계정에서 earthyoung 계정으로 인프라 설정을 옮기는 이슈는 시작은 좋았으나, 다소 늦게 시작해서 여러 오류로 인해 막혔기 때문에 보류하기로 결정했다. 

 

하지만 이번 기회에 IaC에 대해서 알 수 있었고, GPT의 도움이 9할이었지만 어쨌든 여러 명령어를 알음알음 써볼 수 있었으며, 나중에 프로젝트를 관리할 때는 꼭 설정 정보를 IaC를 통해 관리하기 쉽게 빼 놓아야겠다는 생각을 해볼 수 있어서, 마냥 수확이 없지는 않았다!

 

 궁금한 점

1. terraform import, plan, apply 외의 핵심 명령어는 무엇이며, terraform은 어떤 원리나 추상적인 개념으로 동작할까? 

 

 오늘 배운 것

이제는 서버 이관 시간이 하루 남았다. 어제 남겨둔 이슈를 다시 잡아보자. 어제 끝부분에 물었던 대로 aws cli로 입력한 정보는 terraform의 plugin 중 하나인 aws provider가 인식할 수 있었다. 그러므로 aws cli에서 명령어를 실행해서 정보를 이관하려던 기존 소마 계정의 credential들을 입력해 보자. 

 

aws cli에서 기본으로 사용되는 정보는 다음 명령어를 통해 볼 수 있다고 한다. 명령어를 입력하자 aws의 access key id와 secret access key가 나타났다. 

cat ~/.aws/credentials

 

그러면 해당 프로파일의 이름(나의 경우는 'default')을 .tf 파일에 region(나의 경우는 'ap-northeast-2')과 같이 명시해주면 된다. 이렇게 말이다. 

provider "aws" {
  region  = "ap-northeast-2"
  profile = "default"
}

 

그런데 이렇게만 작성한다고 aws 계정의 모든 것을 가져올 수 있는 건 아니었다. 사실 '가져온다'는 개념이 무엇인지 아직도 모호하기만 하다. 그래서 추가적인 질의를 해 보았다.

 

그랬더니 GPT는 조금은 모호한 코드를 주었다. 코드는 대략 이런 방식으로 생겼는데, 처음 따옴표에 들어간 단어는 aws 서비스의 세부 리소스를 말하는 것 같았으나 그 다음 따옴표에 무엇이 들어가는 것인지를 잘 이해할 수 없었다. 어쩌면 "rds_instance" 대신에 instance의 이름이 들어가야 하는 것 같았는데, 확신을 얻고 싶어서 한번 더 물어봤다. 

# RDS
resource "aws_db_instance" "rds_instance" {
  # Import할 때 기본적으로 빈 블록으로 작성
}

알고보니 이는 RDS 인스턴스의 이름과는 관련이 없었다. 이는 terraform에서 가져올 RDS 정보를 어떻게 칭할지를 나타내는 별칭이었다. 'rds_instance'도 썩 나쁘지 않은 별칭이었으므로 그대로 두기로 했다.

 

그런데 모호한 건 이뿐만이 아니었다. GPT는 RDS resource를 정의할 때는 'import할 때 빈 블록으로 작성'이라는 주석을 달아 주었지만, 그를 제외한 나머지 서비스들은 모두 '~서비스를 가져오기 위한 정의'라는 주석이 있었다. 무언가가 필요하다는 의미였다. 물어보지 않을 수 없었다. 

녀석은 이번에야말로 각 서비스 별로 구체적인 예시를 주었다. 우선은 RDS부터 시작해보자면, 중괄호 안에 명시되어야 할 정보로는 identifier(실제 RDS 이름), engine(mysql, postgres와 같은 RDS 엔진), instance_class('db.t3.micro'와 같은 인스턴스 유형), allocated_storage(스토리지의 크기), publicly_accessible(퍼블릭 액세스가 가능한지에 대한 여부) 정보를 제공해 줘야 했다. 


나머지 경우도 마찬가지다. 실제 작성한 파일을 참고용으로 한번 올려 본다. 우선 AWS provider를 먼저 선언해 주자. 이게 있어야 AWS resource들을 가져올 수 있다. 

 

그리고 아래에는 resource를 선언한다. RDS부터 선언해 보자. 다음과 같이 선언하고, 명령어를 입력하면 성공적으로 import가 되었다고 뜬다. 

terraform import aws_db_instance.rds_instance my-rds-instance	# rds instance의 이름

 

이번에는 Route53을 선언해 보자. 

terraform import aws_route53_zone.route53_zone Z1234567890ABCDEFG	# route53 hosting zone의 ID

 

이번에는 ECR(elastic container registry)를 선언해 보자.

terraform import aws_ecr_repository.ecr_repo my-repo	# ECR repository 이름

 

이번에는 ECS(elastic container service)를 가져와 보자. ECS의 경우 cluster, task definition, service를 차례로 정의해야 해서 코드를 제법 작성해 주어야 했다. 

terraform import aws_ecs_cluster.ecs_cluster my-ecs-cluster		# 클러스터 이름
terraform import aws_ecs_task_definition.ecs_task_definition my-task-family:1	# 태스크 정의 이름
terraform import aws_ecs_service.ecs_service my-service		# 서비스 이름

 

이번에는 EC2 인스턴스 정보를 가져와 보자. 

terraform import aws_instance.ec2_instance i-1234567890abcdef0	# EC2 인스턴스 고유 ID

 

마지막으로 Secrets Manager를 import 해 보자. 

terraform import aws_secretsmanager_secret.secret arn:aws:secretsmanager:region:123456789012:secret:my-secret	# 보안 암호 ARN

 

관련된 설정을 import하는 것 까지는 완료했다. 이제는 해당 정보를 새 계정으로 옮기는 방법에 대해 생각해 보자. 

 

 오늘 배운 것

발등에 불이 떨어진 이슈가 있다. 바로 고도화에 당첨되지 않아서, 11월 29일날 소마 AWS 계정 서버 인프라가 모두 닫힌다는 것이다. 남은 시간은 2일인데, 이 안에 기존 서버 세팅과 똑같이 옮겨야 한다. 그런데 이런 작업은 처음 해 봐서, 어떻게 해야 할지 감이 오지 않았다. 우선은 GPT로 워밍업을 해 보았다. 

 

녀석은 멘토링에서 몇 번 들어본 IaC(Infrastructure as Code)를 추천해 주었다. 인프라를 하나하나 설정하고 기록하는 것은 매우 번거로울 수 있으니 IaC 도구를 통해서 AWS 설정 정보를 저장해 두고, 이걸 다시 내 기존 계정에서 사용하라는 거였다. 오케이. 

우선은 terraform을 설치하고, 'terraform init'을 통해 관리를 시작하라고 했다. git과 비슷한데 인프라를 관리하는 버전 관리 시스템 정도로 일단은 이해했다. Terraform 홈페이지의 Download 페이지에서 쉽게 다운로드 받을 수 있었다. homebrew 다운로드도 있고 binary 다운로드도 가능했는데 나는 좀 더 간단해 보이는 homebrew 다운로드 방법을 선택했다. 

brew tap hashicorp/tap
brew install hashicorp/tap/terraform

 

버전 명령어를 실행시켜서 버전이 잘 나오는지를 확인해보자. 잘 설치되었다. 

 

이제 git처럼 특정 디렉토리에서 terraform을 초기화 시켜주고, 파일을 작성해보자. (아직 원리에 대해서 100% 이해하진 못했다. 그냥 이런 게 있구나 싶다.) 디렉토리를 만들어주고 명령어를 실행해 주었다. 

terraform init

 

이제 .tf 확장자를 가진 terraform 파일을 만들어서 작업을 시작하면 된다. vscode로 디렉토리를 열어보자. 그런데 .tf 확장자를 가진 파일을 만들고 GPT의 예제 코드를 따라하려는 찰나 의문이 생겼다. 나는 아직 내 AWS 계정에 관련된 어떠한 정보도 제공하지 않았고 예제 코드에서도 관련된 부분이 없는데, 어떻게 특정 AWS 서비스의 정보를 가져오나 싶어 의아했다. 

 

알고보니 누락된 부분이 있었다. 해당 작업 상황에서는 우선 aws cli가 설치되었다는 것을 가정으로 하는데, 나는 aws 명령어를 터미널에서 인식할 수 있어서 이 과정은 패스했다. 

 

GPT 피셜, aws configure 명령어로 aws 인증 프로필을 생성한 뒤, 해당 프로필 정보를 terraform 파일에 집어넣으면 된다고 했다. 그렇다면 aws cli에서 입력한 정보를 terraform 파일에서 인식할 수 있는 건가?

알고보니 aws cli에서 입력한 정보는, aws 프로바이더가 자동으로 인식할 수 있어서 terraform에서도 가져와서 사용할 수 있다고 했다. aws 프로바이더가 terraform과 관련 있는 장치냐고 묻자 그렇다고 했다. aws 프로바이더는 terraform에서 aws와 같은 클라우드 서비스를 관리하기 위한 플러그인이라고 한다. 

 

 궁금한 점

1. plugin이 정확히 뭘까?

plugin은 소프트웨어의 코어를 바꾸지 않고도 소프트웨어에 새로운 기능을 추가해 줄 수 있는 구성요소라고 한다. 

 

 오늘 배운 것

오늘의 할 일은 다음과 같다. 

 

완성 단계에 있는 디자인을 적용해주면 되고, 위에가 여러 화면과 컴포넌트 디자인 이슈들 중 내가 맡은 것들이다. 차근히 하나씩 해 보자. 위의 이슈들이 컴포넌트 반영 이슈들이고, 아래의 이슈들이 스크린(화면)을 만드는 이슈들이다. 컴포넌트를 만들고 화면을 만드는 것이 우선순위이니 차례대로 해 보자. 

 

우선 맨 위의 '카테고리바 컴포넌트 디자인 반영' 이슈를 보자. 

 

다음은 기존의 코드이다.

const renderItem = ({ item, handlePress }) => (
  <ListItem
    title={item.title}
    accessoryLeft={renderItemIcon}
    onPress={() => handlePress(item)}
  />
);

 

UI Kitten에서 제공하는 라이브러리 덕분에 ListItem이라는 컴포넌트만 쓰면 이런 예쁜 화면이 나왔었다. 

 

이제 이걸 뜯어내 보면, 다시 제로부터 돌아간 태초의 화면이 보인다. 이제 이 화면을 예쁘게 만들어줘야 한다.

 

이제는 외부 라이브러리에서 제공하는 아이콘을 사용하는 게 아니라 SVG 아이콘을 직접 사용해야 한다. 그러므로 피그마에서 아이콘을 직접 다운받고 .svg 파일의 포맷을 사용하기 위해서 필요한 별도 라이브러리를 다운받아 주자. 

npm install react-native-svg react-native-svg-transformer

 

그리고 피그마에 맞게 padding, margin 등의 값을 잘 입력해 주면, 다음과 같은 화면을 볼 수 있다. 

 

+ Recent posts