나머지는 기본값으로 세팅 후 "Restore backup" 버튼을 클릭해서 복원한다. 완료가 되면 해당 스냅샷 ID를 가져와서 EC2 > Elastic Block Store > Volume 으로 간다. (해당 스냅샷 ID는 Backup vault에서 확인 가능하다. 아래 사진 참고)
Volume 리스트에 스냅샷으로부터 복원된 EBS가 보인다.
이제 이 복원한 EBS를 EC2에 붙여보자. 그러기 위해선 일단 EC2의 Volume을 떼어내야 한다. 그러기 위해선 EC2를 중지해야 한다. 먼저 중지를 하자. 중지가 완료되면 해당 인스턴스의 볼륨을 떼어낸다. 떼어내고 싶은 인스턴스를 클릭 후 하단 "Storage" 탭에서 볼륨을 클릭한다.
볼륨을 클릭하면 볼륨 리스트에서 해당 볼륨이 보이는데 선택 후 우측 Actions > Detach volume 클릭
잘 떼어내면 상태가 In-Use에서 Available로 변경된다.
이제 백업한 볼륨을 붙여보자. 다시 볼륨리스트로 가서 백업한 볼륨을 선택하고 Actions > Attach volume 클릭
그러면 복원 설정 화면에서 다음을 수정한다.
- Instance: 볼륨에 붙일 인스턴스
- Device name: /dev/xvda
Attach volume을 클릭하면 백업한 볼륨이 인스턴스에 붙는다. 붙었는지 확인하려면 상태를 보자. 해당 볼륨이 Available이 아니고 In Use로 보여진다.
인스턴스에서도 확인 가능하다. 백업한 볼륨을 붙인 인스턴스를 클릭해서 하단 Storage 탭을 클릭하면 볼륨 ID가 보인다. 백업한 볼륨의 ID인지 확인.
RDS 백업
이번에는 RDS를 백업해보자. AWS Backup 서비스로 가서 Dashboard 화면에서 "Create on-demand backup" 버튼 클릭
설정 부분은 다음과 같다.
- Resource type: RDS
- Database name: 백업할 RDS
- Backup window: Create backup now
- Total retention period: Forever
- Backup vault: 위에서 사용했던 vault
- IAM Role: Default role
이렇게 설정한 후 백업을 만든다. 그러면 Job 리스트에서 백업이 진행중인 것을 확인 가능하다.
RDS 백업은 시간이 조금 소요되므로 여유롭게 기다려보자. 시간을 좀 기다리고 나니 다음과 같이 백업이 완료됐다. Status가 Completed 상태가 된 것을 확인할 수 있다.
그리고 이 백업된 스냅샷은 RDS 서비스 내 Snapshots 화면에서도 확인할 수 있다. Backup service 탭을 눌러보면 다음과 같이 스냅샷이 보인다.
여기서 한 가지 알고 가야하는건 RDS 서비스를 만들면 시스템에서 자동으로 백업을 하는데 그 백업 시간이 있다. 이 백업 시간과 내가 직접 백업을 하려는 백업 시간이 너무 가까우면 위 작업을 따라했을 때 백업이 되지 않는다. 이를 참고하자. RDS의 백업 시간은 아래 사진처럼 확인 가능하다.
백업 시간을 확인하고자 하는 RDS를 클릭 > Maintenance & backups 탭 클릭후 Backup 섹션을 보자.
여기서 Backup window 항목을 보면 백업 시간을 확인할 수 있다. 이 시간을 변경할수도 있다. 참고하자.
이제 백업한 RDS로 복원을 해보자. 백업 볼트로 가서 백업한 RDS를 선택해서 Actions > Restore
복원 설정 화면에서 수정할 부분은 다음과 같다.
- DB Instance class: 원하는 클래스 선택
- DB Instance Identifier: 적절하게 입력
그 외는 기본으로 세팅된 원래 RDS 서비스와 동일한 설정을 따르고 복원을 해보면 된다.
근데 위 설정처럼 하면 다음과 같은 에러를 마주하게 된다.
t2.micro는 암호화가 지원이 안된다고 하니 살짝 더 큰 클래스를 선택해보자. (본인은 t2.small을 선택) 이 부분도 시간이 꽤 소요되니까 잘 기다려보자. 다 끝나면 다음과 같이 상태가 Completed로 변경된다.
그리고 RDS 서비스 내 Databases 화면으로 가면 백업한 RDS가 생성됐음을 확인할 수 있다.
백업한 RDS 세부 정보를 보면 Security group이 default로 설정되어 있다. 이것을 바꿔보자. 기존 RDS가 사용하는 보안 그룹으로.
테이블을 만들면 버킷에서 로그가 잘 쌓여있는지 확인해보자. 아까 트래픽을 발생시켰으니까 시간이 좀 지나면서 로그가 쌓일것이다.
아래 사진과 같이 오늘 날짜로 된 폴더 내 로그가 보인다.
이제 해당 로그 파일에서 원하는 쿼리를 날려보자. 다음은 위 링크에서 가져올 수 있는 예시 쿼리이다.
이 쿼리는 클라이언트 IP 주소별로 그룹화된 로드 밸런서가 수신한 HTTP GET 요청의 개수를 계산한다. 날려보자.
SELECT COUNT(request_verb) AS
count,
request_verb,
client_ip
FROM alb_logs
GROUP BY request_verb, client_ip
LIMIT 100;
쿼리 결과는 다음처럼 나온다. 내 IP는 11회 조회를 했고 그 외 나머지는 아마도 AWS의 서울 각 가용영역에서 접근한 것으로 보여진다.
ELB Access Log 중간정리
ELB의 Access Log를 Athena를 통해 쿼리문을 날려 원하는 데이터만 뽑아봤다. 이렇게 접근 관련 로그도 잘 남길 수 있게 됐다.
Amazon VPC Flow Log
이번에는 VPC Flow Log를 활성화해서 Athena에서 확인해보자.
AWS Console에서 VPC를 검색해서 서비스에 들어가고 화면 좌측 VPC를 클릭한다.
Flow log를 활성화 시킬 VPC를 선택한 다음에 하단 Flow logs 탭을 클릭해서 Create flow log 버튼을 누른다.
진입 화면에서 다음 사진에서 빨간 박스 부분을 수정해야 한다.
- Name: cwchoiit-vpc-flow-logs
- Destination: S3 bucket
- S3 bucket ARN: S3 버킷의 ARN을 입력 (S3 Bucket은 직접 만들어서 ARN 입력란에 입력해야 한다)
입력해서 만들면 Flow logs 탭에 만든 플로우 로그가 보인다. (나는 Maximum aggregation interval을 1분으로 수정했다)
이렇게 생성한 후 S3 버킷으로 가보면 폴더가 자동으로 아래처럼 만들어진다. (AWSLogs/AWSAccountID 경로)
아직 로그가 생성되지 않았고 이 로그를 생성하기 위해 트래픽을 발생시켜보자. 이 VPC 플로우 로그는 EC2에 붙어있는 네트워크 인터페이스에서 발생하는 모든 트래픽을 트래킹한다. 아래 사진이 EC2의 네트워크 인터페이스에 들어가면 보여지는 모든 네트워크 인터페이스이다. 모두 동일한 VPC(위에서 설정한 VPC)에 붙어있는 네트워크 인터페이스이고 이 네트워크를 통한 트래픽에 대한 로그를 남긴다.
트래픽을 발생시켜서 S3 버킷에 로그가 쌓이는지 확인해보자. 다음과 같이 오늘 날짜 경로에 로그가 저장된다.
이 로그를 Athena를 통해 쿼리로 데이터를 추출해보자. Athena를 통해 쿼리를 날리려면 VPC 플로우 로그에 대한 테이블이 만들어져야 한다. 테이블 만드는 쿼리는 아래 링크를 참조.
해당 링크에서 테이블 만드는 쿼리를 가져왔으면 Athena 서비스로 들어가서 테이블을 만든다. 다음이 테이블 만드는 쿼리인데 여기서 변경할 부분은 그 바로 아래와 같다.
CREATE EXTERNAL TABLE IF NOT EXISTS `vpc_flow_logs` (
version int,
account_id string,
interface_id string,
srcaddr string,
dstaddr string,
srcport int,
dstport int,
protocol bigint,
packets bigint,
bytes bigint,
start bigint,
`end` bigint,
action string,
log_status string,
vpc_id string,
subnet_id string,
instance_id string,
tcp_flags int,
type string,
pkt_srcaddr string,
pkt_dstaddr string,
region string,
az_id string,
sublocation_type string,
sublocation_id string,
pkt_src_aws_service string,
pkt_dst_aws_service string,
flow_direction string,
traffic_path int
)
PARTITIONED BY (`date` date)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ' '
LOCATION 's3://DOC-EXAMPLE-BUCKET/prefix/AWSLogs/{account_id}/vpcflowlogs/{region_code}/'
TBLPROPERTIES ("skip.header.line.count"="1");
이렇게 해서 테이블을 생성한 다음 한 가지 더 해줄게 있다. 테이블을 수정해야 하는데 데이터를 읽을 수 있도록 파티션을 생성해야 한다. 왜냐하면 테이블 만들 때 쿼리를 잘 보면 PARTITIONED BY ('date' date)라고 입력한 부분이 있다. 날짜로 파티션을 나눴기 때문에 하단 쿼리처럼 원하는 날짜에 대한 파티션을 추가해야 그 날짜에 대한 쿼리에 대한 결과를 출력할 수 있다.
ALTER TABLE vpc_flow_logs
ADD PARTITION (`date`='YYYY-MM-dd')
LOCATION 's3://DOC-EXAMPLE-BUCKET/prefix/AWSLogs/{account_id}/vpcflowlogs/{region_code}/YYYY/MM/dd';
그래서 위 코드에서 파티션을 생성할 날짜를 입력해야 한다. 날짜는 당연히 원하는 쿼리에 해당하는 날짜를 입력해야 한다. 아래는 그 예시다.
ALTER TABLE vpc_flow_logs
ADD PARTITION (`date`='2023-01-28')
LOCATION 's3://cwchoiit-vpc-flow-logs/AWSLogs/135149110460/vpcflowlogs/ap-northeast-2/2023/01/28';
이렇게 파티션을 만들었으면 이제 예시 쿼리를 날려보면 된다. 아래는 예시 쿼리이고 지정한 날짜에 최대 100개의 흐름을 출력한다.
SELECT *
FROM vpc_flow_logs
WHERE date = DATE('2020-05-04')
LIMIT 100;
그럼 다음과 같이 결과가 쭉 나온다.
또 다른 예시는 해당 날짜에 거절된 트래픽이다. 결과가 잘 출력되는 모습이다.
VPC Flow Log 정리
VPC Flow log와 Athena를 통해 원하는 쿼리문을 작성해서 로그를 출력할 수 있었다.
AWS에서 제공해주는 CodeBuild, CodeDeploy를 이용해서 CI/CD 자동화를 구현해보자.
CodePipeline
우선 AWS에서 제공하는 CodePipeline이라는 게 있다. CD를 위한 서비스인데 이 서비스 내 CodeBuild, CodeDeploy 서비스를 사용할 수 있다. 그래서 이 서비스를 이용할건데 이 서비스를 이용해서 CD 파이프라인을 만드려면 buildspec.yaml 파일이란게 필요하다. 그리고 그 yaml파일은 어떤 형식을 가지고 있는데 이 형식에 대한 설명이나 예시는 다음 링크에서 확인할 수 있다.
그래서 이 YAML파일을 수정해보자. 다음은 예시코드인데 여기서 우리 환경에 맞게 수정을 해줘야한다.
Frontend buildspec.yaml 파일 만들기
우선 프론트 소스에 대한 buildspec.yaml 파일을 만들자. 아래를 그대로 쭉 따라해보자.
위 buildspec.yaml 파일에서 이미지 태그가 제대로 동작하지 않는 경우가 있다. 그래서 CodeBuild로 Image를 빌드하면 Image는 만들어 지는데 그 이미지를 가져다가 클러스터의 서비스를 만들어서 실행하는게 안되는 경우가 있다. 그 때는 이미지 태그를 다르게 직접 명시 해줘야한다.
- Image version: Always use the latest image for this runtime version
- Service role: New service role
그 다음 하단 Additional configuration을 눌러 다음 화면과 같이 Privileged를 체크한다. 이는 도커 이미지를 빌드하기 위함이다.
그 외 나머지는 기본값으로 설정 후 생성한다. 그럼 다음과 같이 리스트에 잘 노출된다.
CodeBuild의 Role 수정하기 (ECR을 컨트롤 할 수 있게)
이 다음 이 CodeBuild를 만들면서 위에 Environment 섹션에서 서비스 역할(Service role)을 새로이 만드는 작업(New service role)을 했다. 이 역할을 IAM에 가서 확인해보자.
IAM > Access management > Roles 진입 후 'codebuild'라고만 쳐도 나올 것이다.
저렇게 잘 나오면 저 역할을 선택한다. 이 역할에게 ECR을 컨트롤 할 수 있는 권한을 추가해줘야 한다.
그런 다음 나오는 화면에서 'ECR'이라고 검색 후 다음 정책을 선택하여 추가한다.
추가를 다 하면 이제 CodeBuild로부터 ECR을 컨트롤 할 수 있게 된다. 그럼 이제 CodeBuild에서 Build를 해보자.
CodeBuild로 빌드하기
CodeBuild > Build projects 화면에서 방금 만든 프로젝트를 빌드해보자. 우측 상단 'Start build > Start now'
'Start now'를 클릭하면 다음 화면처럼 진행 중인 모습을 확인할 수 있다.
다 끝나면 다음과 같이 상태가 'Succeeded'로 변경된다.
그럼 정말 잘 됐는지 ECR로 가서 이미지 빌드 상태를 보자. ECR > CodeBuild로 적용한 레포지토리(frontend)
확인하면 다음과 같이 CodeBuild로부터 빌드된 이미지가 잘 업로드됐다.
CodeDeploy 생성하기
CodeBuild로부터 이미지를 잘 빌드했으니 이제 CodeDeploy를 이용해서 클러스터에 배포하는것까지 해보자. 그 전에 이 CodeDeploy가 클러스터에 배포를 할 때 서비스 자원을 건드리기 때문에 관련 IAM 역할을 생성해야 한다.
CodeDeploy용 역할 생성하기
IAM > Roles 화면에서 우측 상단 'Create role'
Select trusted entity 화면이다.
- Trusted entity type: AWS service
- Use case: CodeDeploy
Use case를 CodeDeploy로 선택하면 다음 3가지 경우가 나오는데 'ECS'로 선택 후 'Next'
기본값으로 세팅된대로 쭉쭉 넘어가고 Role Name만 적어주자.
역할 생성하기 버튼을 눌러 생성하면 역할 리스트에서 위에서 설정한 Role name으로 검색하면 잘 나와야한다.
CodeDeploy용 대상 그룹 생성하기
이 대상 그룹은 왜 생성하냐면, 배포를 하면 배포 전과 배포 후 새로운 서비스로 호스팅을 해줘야 하니까 그 새로운 서비스에 대한 대상 그룹을 만드는 것이다.
그 외 값은 기본값으로 만들고 'Next' 이 대상 등록 화면에서는 아무것도 등록하지 않고 만든다.
대상 그룹을 만들었으면 로드밸런서에서 이 대상 그룹을 사용할 수 있도록 추가해주자. ECS용 로드밸런서를 선택하고 리스너 추가를 한다.
Listener details 화면에서는 다음 부분을 수정한다.
- Protocal:Port: HTTPS:8443
- Routing actions: Forward to target groups - 방금 만든 타겟 그룹
Secure listener settings에서는 Certificate source를 From ACM으로, Certificate은 만들어진 도메인으로 선택한다.
이렇게 설정 후 새로운 리스너 규칙을 만들면 다음과 같이 리스트에 잘 노출된다.
Deployment Type이 Blue/green deployment인 클러스터의 서비스 만들기
이제 ECS로 가서 클러스터에서 서비스를 새로 만들어야 한다. 이 서비스는 CodeDeploy와 연동하는 서비스가 될 것. 다음과 같이 새 서비스를 만들기 원하는 클러스터에서 서비스 탭의 생성 버튼을 누른다.
Environment는 동일하게 EC2로 Launch type을 설정한다.
Deployment configuration은 다음과 같이 수정이 필요하다.
- Application type: Service
- Family: 프론트 엔드 용 Task Definition
- Service name: frontend
하단에 Deployment options에서 다음과 같이 수정한다.
- Deployment type: Blue/green deployment
- Service role for CodeDeploy: 방금 위에서 만든 CodeDeploy용 서비스 역할
Load balancing에서 다음과 같이 수정한다.
- Load balancer type: Application Load banalcer
- Load balancer: ECS용 로드밸런서
- Listener: Production listener> 443:HTTPS / Test listener > 8443:HTTPS
대상 그룹은 1번을 기존 frontend-80으로, 2번을 방금 막 만든 bluegreen 80으로 설정한다.
그리고 서비스를 생성한다. 서비스를 생성하면 자동으로 CodeDeploy의 Application이 생성된다. (물론 서비스를 생성하고 정상적으로 Task가 Running 상태로 띄워져야 한다. 그렇게 되면 아래 사진처럼 CodeDeploy의 Application이 자동 생성된다.)
appspec.yaml 파일 수정하기
CodeDeploy에서 사용할 appspec.yaml 파일을 손보자. 이 파일 형식 역시 다음 링크를 참조한다.
이 appspec.yaml 파일은 S3에 업로드해서 사용할 것이다. 그리고 샘플 코드는 다음과 같다.
지금 이 파일에서 수정할 부분은 "어떤 Task Definition을 사용해서 서비스를 띄울건데?" 이다. 그러니까 지금까지 한 내용은 프론트 소스코드에 대해서 CodeBuild와 CodeDeploy를 진행했으니까 역시 Task Definition도 프론트 소스에 대해서 배포해야 한다.
ECS > Task definitions > 원하는 Task definition 화면에서 ARN 정보를 복사한다.
그리고 이 Task definition의 컨테이너가 있다. 그 컨테이너 이름을 가져오면 된다. 다음 화면 참고.
저 이름으로 아래 코드에 적용.
ContainerName: 'frontend'
이렇게 appspec.yaml 파일이 다 수정이 끝났으면 이 파일을 S3에 올려야 한다.
S3에 appspec.yaml 파일 업로드하기
이미 있는 S3나 새로 만들어서 S3에 appspec.yaml 파일을 넣어보자.
CodeDeploy Deployment 만들기
S3에 appspec.yaml 파일을 넣었으면 CodeDeploy에 가서 Deployment를 만들자. Application은 위 클러스터의 서비스를 Blue/green deployment type으로 만들 경우 알아서 만들어진다.
CodeDeploy > Applications > 자동으로 만들어진 Application 선택
안에 들어가면 다음처럼 Deployment 탭이 있다. 해당 탭에서 "Create deployment" 클릭
Deployment settings를 다음과 같이 설정한다.
-Deployment group:방금 클러스터의 Blue/green 배포 타입의 서비스를 만들면서 자동 생성된 배포 그룹 선택
-Revision type:S3
-Revision location:S3의 appspec.yaml 파일 경로
-Revision file type:.yaml
그 외 기본값으로 배포 생성을 하면 다음과 같은 화면이 보인다. 배포가 진행중이다.
그리고 우측을 보면 Original 100% Replacement 0%로 나와있는데 이는 새 버전을 배포하기 전 모든 트래픽을 담당하는 쪽은 아직 구 버전의 배포판이라는 것을 말한다. 즉, 새 버전의 배포가 완료되면 모든 외부와의 트래픽 담당은 새 버전의 배포판이 담당하게 된다. (당연히 그래야 한다)
모든 배포가 끝났고 이제 Replacement가 모든 트래픽을 담당한다. 여기서 Step 4, Step 5가 아직 남았는데 이는 뭐냐면 구 버전의 배포판을 종료하는데 1시간 정도 대기 시간을 두는것이다. 1시간의 대기시간이 끝나면 구 버전의 배포판은 완전히 Terminated된다. 만약 이걸 기다리기 싫다면 우측 상단 "Terminate original task set"을 누르면 곧바로 구 버전의 배포 인스턴스가 종료된다.
중간 정리
이렇게 하면 소스 업데이트 후 새로운 배포를 CodeBuild, CodeDeploy를 통해 반 자동화 할 수 있다. 당연히 아직 완전하지 않다. 이 모든 과정을 자동화하고 싶다. 그것을 CodePipeline에서 할 수 있다.
CodePipeline을 사용해서 Build - Deploy 자동화
이제 CodeBuild, CodeDeploy를 각각 따로 실행해 보았으니 이 둘을 한번에 진행해보자. 이를 위해 CodePipeline 서비스를 사용하면 된다. 근데 그 전에 우리의 buildspec.yaml, appspec.yaml 파일이 있는 프로젝트의 taskdef.json 이라는 파일을 수정해야한다.
위 사진과 같이 taskdef.json 파일이 있는데 이 파일은 어떤 값이 들어가면 되냐? ECS - Task Definition의 Blue/green으로 배포하는 Task Definition의 JSON 값을 넣어주면 된다.
JSON 탭의 JSON을 복사해서 taskdef.json 파일에 넣어주자.
그리고 한가지 더 확인할 것은 buildspec.yaml 파일에서 다음 코드와 같이 artifacts 부분이다.
최종 생성을 하면 아래처럼 CodePipeline이 바로 진행이 된다. 즉, 위에서 순서대로 Source, Build, Deploy를 구성한 것을 토대로 하나씩 진행이 된다. Github로부터 소스를 받아오고, CodeBuild로부터 Build가 진행되고, CodeDeploy로부터 Deploy가 진행된다.
근데 진행중에 마지막 Deploy 단계에서 이런 에러가 난다면 .. "Tags can not be empty"
taskdef.json 파일에서 "tags"를 지워주면 된다. 본인의 경우 이런 빈 값으로 들어가 있었다.
Auto Scaling은 자동으로 EC2 인스턴스의 개수가 줄거나 늘거나 하는 기능을 말한다. Auto Scaling은 두 가지 경우로 나눌 수 있다.
Scaling Up/Down: 인스턴스 타입의 크기를 크거나 작게 만드는 경우
Scaling Out/In: 인스턴스의 개수를 늘리거나 줄이는 경우
Auto Scaling Group
이 Auto Scaling 기능을 이용하기 위해 Auto Scaling Group이 만들어져야 하는데 여기서 최소, 희망, 최대 인스턴스 개수를 설정할 수 있다.
그리고 사용자가 설정한 Auto Scaling 조정 정책, 즉 EC2가 감내할 수 있는 부하의 기준을 정하면 인스턴스 개수가 최대 최소의 인스턴스 내에서 자동으로 조절이 된다. 이런 설정을 하는 것이 Auto Scaling Group이라고 보면 된다.
Launch Template, Golden AMI
Launch Template은 인스턴스를 생성할 때 결정하는 네트워크 정보, 타입, 키페어, AMI(Amazon Machine Image)와 같은 설정값들에 대한 템플릿을 의미한다.
AMI는 인스턴스 생성할 때 지금까지 사용했던 Amazon Linux Image와 같은 그 이미지를 말한다.
Auto Scaling Group을 사용할 때 이 Launch Template을 사용해서 추가적으로 생성될 인스턴스를 만들 수 있게 구성할 수 있다. 이 때, Launch Template을 통해서 사용되는 이미지를 Golden AMI라고 한다. 이 Golden AMI는 OS를 포함해서 필요한 필수 패키지들이 설치되어 있는 표준 이미지를 말한다. Golden 이라는 단어의 의미는 딱히 중요하지 않다. 그냥 완성적이고 필수 패키지들이 잘 적용이 된 상태라서 Golden 이라는 이름이 붙어졌다. 이후에 직접 만들어보면서 더 이해를 하겠지만 직접 나만의 이미지를 만들어서 기존에 사용했던 동일한 인스턴스 상태와 환경을 구성할 수 있게 해준다. 그리고 이런 방식이 앞서 얘기한 Immutable Infra를 만들게 해준다.
Autoscaling Policy
위에서 Auto Scaling 조정 정책이라는 말을 했는데 그것을 알아보자. 이 조정 정책이란 사용자가 "이런 조건에 도달하면 Auto Scaling 기능을 활성화해줘!"라고 정의해놓은 정책을 말한다. 그리고 크게 3가지가 있다.
단순 조정 정책 (Simple Scaling)
Cloudwatch 경보 위반을 기준으로 사용
Cloudwatch 경보 발생 시 특정 인스턴스 개수만큼 스케일 인/아웃 설정이 가능
또는 Cloudwatch 경보 발생 시 Auto Scaling Group의 퍼센티지(%)만큼 스케일 인/아웃
단점은 확장에 대한 세분화된 제어를 제공하지 않음
대상 추적 조정 정책 (Target Tracking Scaling)
Auto Scaling Group이 항상 유지해야 하는 조정 지표 및 지표값을 지정한다. 예를 들어, CPU 80%로 설정하면 항상 그만큼을 유지하도록 자동 스케일 인/아웃 실행
4가지 지표 선택이 가능
ASG Average CPUUtilization: Auto Scaling Group의 평균 CPU 사용률
ASG Average NetworkIn: Auto Scaling Group이 모든 네트워크 인터페이스에서 수신한 평균 바이트 수
ASG Average NetworkOut: Auto Scaling Group이 모든 네트워크 인터페이스에서 보낸 평균 바이트 수
ALB Request CountPerTarget: Auto Scaling Group이 ALB 대상 그룹과 연결된 경우 대상 그룹의 대상 당 완료된 요청 수
단계 조정 정책 (Step Scaling)
위 단순 조정 정책의 단점인 세분화된 제어를 보완하기 위한 정책
Cloudwatch 경보 위반의 정도에 따라 인스턴스 개수를 단계적으로 조정하는 작업을 설정 가능
단계적 조정을 통해 정책은 경보 위반 이벤트 도중에도 추가 경보에 계속 응답할 수 있게 된다.
Auto Scaling Group의 Lifecycle
Auto Scaling Group으로 인스턴스를 만들고 지울 때 인스턴스의 생명주기가 있다. 그림을 보자.
우선 Auto Scaling Group으로부터 인스턴스가 새로 생성되는 Scale Out일 때 인스턴스 상태는 'Pending'이다. Pending에서 인스턴스가 완전히 띄워졌을 때 'InService'상태가 된다. InService상태에서 Health Check가 실패하거나 Scale In이 될 때 'Terminating'상태가 된다. 이 Terminating에서 완전히 인스턴스가 제거된 상태는 'Terminated'이다.
그 외 InService상태에서 인스턴스를 떼어내면 'Detaching'상태가 되고 InService상태에서 인스턴스를 Standby로 설정하면 'EnteringStandby'상태가 된다.
Auto Scaling 직접 사용해보기
이제 Auto Scaling 기능을 실제로 사용해보자. 우선 어떤 형태의 모습이 그려질지는 다음과 같다.
기존에 가지고 있는 EC2 인스턴스가 있고 그 인스턴스를 가지고 Auto Scaling Group을 만들어서 Launch Template을 만든다. 그리고 정책을 설정해서 정책 조건에 도달하면 자동으로 인스턴스가 늘어나는 모습을 확인할 예정. 앞단에는 LB를 붙여서 인스턴스가 3개가 만들어지면 부하 분산 처리가 잘 되는지도 확인해보자.
필요한 보안 그룹 생성
위 그림에서 LB가 있다. LB에 관련된 보안 그룹을 먼저 생성하자.
EC2 > Security Groups에서 보안 그룹 생성.
이름과 VPC를 설정하고, 인바운드 규칙에는 80에 모두(0.0.0.0/0)가 들어올 수 있게 설정한다. 이 상태로 생성.
이번엔 새로 만들 EC2에 대한 보안 그룹을 생성한다. 위 그림에서 총 3개의 EC2가 있는데 이들의 표본이 될 EC2 하나는 만들어야한다. 그래서 보안 그룹 하나를 더 생성.
하단에 보면 3개의 인바운드 규칙이 있다.
22는 우리가 항상 사용하던 OpenVPN으로 SSH 접속할 수 있게 설정
80도 역시 OpenVPN을 통해 접속할 수 있게 설정
80중 또다른 하나는 방금 막 만든 LB 보안 그룹으로 설정
표본 EC2 생성
위에서 말한 표본 EC2를 생성한다. Name 설정은 자유롭게하고 AMI는 Amazon Linux로 선택, 타입은 디폴트인 t2.micro, 키페어는 계속 사용해왔던 키페어로 선택, 네트워크 설정에서는 계속 사용했던 VPC, Private Subnet App A로 하고 보안 그룹은 방금 만든 보안 그룹을 선택하고 생성한다. (이제 EC2 만드는 건 이렇게 말로만 하겠다.)
이제 OpenVPN에 접속해서 이 방금 만든 EC2를 SSH로 접속하자. 접속했으면 여기에 필요한 패키지들을 설치해야 한다. 그것들을 정리한 README.md 파일 경로는 다음과 같다.
여기 부분이 중요한데, AMI를 선택할 때 My AMIs 탭을 선택하고 Owned by me를 클릭한다. 그리고 방금 만든 AMI를 선택
그 다음, 인스턴스 유형과 키페어는 다음과 같이 설정한다. 키페어는 기존에 사용했던 키페어를 계속 사용한다.
그 다음, 네트워크 설정이다. Subnet은 프라이빗 서브넷 App A에 그러니까 표본 EC2가 있는 같은 서브넷으로 선택하고 보안 그룹은 저 EC2와 같은 보안 그룹을 선택한다.
나머지는 기본값으로 설정한 후 만들어보자. 만들고 나면 리스트에 만든 Launch Template이 보여진다.
ALB 생성
이제 앞단에 쓰일 로드밸런서를 만든다. EC2 > Load Balancers에서 로드밸런서 하나를 만들자.
유형은 다음처럼 ALB를 선택한다.
Basic configuration은 다음과 같다.
네트워크 설정은 다음과 같다. AZ-a, AZ-c 두개를 사용한다.
보안 그룹은 앞에서 만든 ALB용 보안 그룹을 선택한다.
그리고 이 ALB를 만들기 전 대상 그룹을 먼저 만들어야 했는데 만들지 않았다. 그래서 Listerners and routing 섹션에 다음처럼 Create target group 링크를 클릭해서 만들자.
대상 그룹을 만들 때 유형은 인스턴스, 프로토콜과 포트는 HTTP:80용으로 한다.
그리고 VPC는 계속 사용했던 것을 선택하면 되고 Health check는 다음 경로로 설정한다.
다음으로 넘어가면 대상을 등록하면 된다. 위에 만든 Auto Scaling을 위한 EC2를 선택하고 'Includ as pending below' 클릭
그리고 만들면 이제 다시 로드밸런서 만들었던 화면으로 돌아가서 방금 만든 대상 그룹을 선택한다.
이렇게 설정하고 로드밸런서를 생성한다. 생성하면 리스트에 다음과 같이 노출된다.
이 만든 로드밸런서를 Route 53에 연결하자. Route 53 > Hosted zones로 가서 우리의 도메인을 선택하고 레코드 하나를 추가하자.
레코드를 다음과 같이 생성한다. subdomain은 autoscaling으로 설정했고 방금 만든 ALB에 붙인다.
이제 방금 만든 도메인으로 접속해보자. 그런데, HTTPS로 접속 가능하게 해보자. 우선 로드밸런서로 가서 규칙 하나를 추가해주자.
이렇게 설정하고 규칙하나를 추가해주자. 그리고 우리가 ALB에 적용한 보안 그룹 역시 443은 열려있지 않기 때문에 인바운드 규칙 하나를 추가해줘야 한다. 이 ALB에 적용한 보안 그룹으로 가서 인바운드 규칙 추가를 누르고 다음 443에 대해 설정해주자.
다 설정이 끝났으면 접속해보자. 잘 보여야 한다.
Auto Scaling Group 생성하기
이제 Auto Scaling Group을 만들어보자. EC2 > Auto Scaling > Auto Scaling Groups 로 들어가자.
생성하기를 누르고 이름을 지정해준다. 그리고 Launch template을 우리가 위에서 만든 것으로 지정한다. 버전은 그냥 Default로 하면 된다. 그리고 Next
VPC는 계속 사용하던 VPC로 설정하고 가용 영역과 서브넷은 A존 C존에 App용 서브넷을 선택한다. 그리고 Next
다음은 로드밸런서를 선택한다. 만든 로드밸런서를 선택하고 그에 맞게 설정한 대상 그룹을 지정한다.
Health Check 주기는 10초로 변경하자. 300초는 너무 길다. 그리고 Next
계속 Next로 넘어가고 태그를 만드는 화면에서 태그 하나를 생성하자. 태그의 키는 Name, Value는 적절한 값을 넣어서 태그 하나를 생성하고 Next
그러면 요약 부분이 나오고 확인 후 생성하면 된다. 생성하면 다음처럼 리스트에 잘 나온다.
들어가서 확인해보면 현재 Desired, Minimum, Maximum capacity가 전부 '1'로 되어 있다. 변경해보자. 우측 Edit 버튼 클릭
최대 용량을 3으로 변경해보자.
이제 Automatic scaling에서 정책을 생성해야 한다. Automatic scaling 탭에서 Dynamic scaling policies 섹션에 생성 버튼을 누른다.
정책 타입은 Target tracking scaling으로 하고 Metric type은 Average CPU utilization으로 선택하자. Target value는 70으로 설정하고 Instance warmup은 10초로 설정하고 만들어보자.
생성하면 다음과 같이 정책 하나가 추가됨을 확인할 수 있다.
정책은 정책이고 이렇게 Auto Scaling Group을 정상적으로 만들면 이 그룹에 의해 자동으로 인스턴스가 만들어진다. 확인해보자.
Auto Scaling Group의 Instance management 탭으로 가면 다음과 같이 인스턴스 하나가 'InService'상태인 것을 확인할 수 있다. 이는 Auto Scaling Group으로부터 자동으로 만들어진 인스턴스다.
실제로 인스턴스에 가서 리스트를 확인해보면 다음처럼 우리가 만든 표본 EC2가 있고 Auto Scaling Group이 만든 EC2가 둘 다 구동중이다.
Auto Scaling Group으로 만들어진 인스턴스들만 로드밸런서가 취급을 할 수 있도록 로드밸런서 쪽 대상 그룹에서 우리가 만든 표본 EC2는 날려주자. EC2 > Load Balancing > Target Groups에서 위에서 만든 로드밸런서에 적용할 대상 그룹에 들어가보면 다음과 같이 타겟이 두개다. 하나는 앞서 만든 표본 EC2, 하나는 Auto Scaling Group으로부터 만들어진 EC2. 표본 EC2를 대상 그룹에서 Deregister하자.
Deregister를 하면 다음처럼 상태가 Draining으로 변하고 이 대상 그룹에 타겟에서 빠진다.
Auto Scaling 확인하기
이제 우리가 만든 Auto Scaling Group의 정책에 따라 CPU 사용률이 70이 넘어가면 Scale Out이 발생하는지 확인해보자. 그러기 위해 Auto Scaling Group이 만들어준 EC2로 들어가서 스트레스를 부여하자.
우선 해당 EC2로 들어가야 한다. 해당 EC2의 프라이빗 IP를 확인해서 접속해보자. 내부로 들어오면 루트 유저로 변경하자.
이러면 Auto Scaling Group에서 만든 정책 조건에 부합한다. 다음을 보자. 이 부분이 Auto Scaling Group에서 설정한 Dynamic scaling policies중 하나다. 저기 보면 'As required to maintain Average CPU utilization at 70' 이런 문장이 있는데 이게 CPU 평균 사용률을 70으로 유지하는데 필요한 경우이다. 우리가 99%까지 CPU 사용률을 높였기 때문에 이 조건에 따라 EC2가 늘어날 것이다.
CPU Policy
Target tracking scaling
Enabled
As required to maintain Average CPU utilization at 70
Add or remove capacity units as required
10 seconds to warm up before including in metric
Enabled
Activity 탭에서 이 부분을 유심히 봐보자.
당장 만들어지지 않을 수 있다. CPU 지표가 Cloudwatch로 전달이 되고 Cloudwatch에 있는 수치를 Auto Scaling Group에서 확인하는데까지 시간이 걸릴 수 있기 때문에 실시간 반영은 되지 않을 수 있다.
조금 기다리면 다음과 같이 새로운 인스턴스가 Launching 된다는 활동 내역이 나온다.
우리가 설정한 최대 3개까지 모두 만들어진 모습이다.
이렇게 3개까지 만들어지면 이제 로드밸런서는 이 세개의 인스턴스를 부하분산 처리해주면서 라운드로빈 방식으로 한번씩 돌아가면서 요청을 전달한다. 이렇게 Scale Out 현상을 잘 확인해보았다. 그럼 반대로 CPU 사용률에 대한 스트레스 프로세스를 죽였을 때 인스턴스가 하나씩 사라져야한다. Scale In. 그것을 확인해보자.
우선 stress 프로세스를 죽이자. 프로세스 ID를 확인해서 kill.
kill -9 stress-pID
스트레스 프로세스가 띄워져 있는지 확인
ps -ef | grep stress
자 이제 Auto Scaling Group으로 가서 인스턴스가 하나씩 종료되는지 확인하자. 다음처럼 하나씩 인스턴스가 종료된다고 나온다.
이 인스턴스가 종료되는 방식은 Auto Scaling Group의 Details 탭에 가보면 Advanced configurations 섹션이 있다.
거기에 Termination policies가 Default인데, 이 Default는 가장 먼저 생성된 인스턴스를 가장 먼저 지우는 방식이다.
Edit 버튼을 눌러서 Terminate policies를 쭉 보면 다음과 같이 여러 방식이 있다.
VPC는 Virtual Private Cloud의 약자로 AWS에서 논리적으로 생성하는 독립적인 네트워크를 말한다. VPC안에 여러 서브넷을 만들 수 있는데 서브넷이라 함은 네트워크 내부의 네트워크이다. 서브넷은 네트워크를 보다 효율적으로 만드는데 서브넷을 통해 네트워크 트래픽은 불필요한 라우터를 통과하지 않고 더 짧은 거리를 이동하여 대상에 도달할 수 있다.
VPC안에 구성할 수 있는 서브넷은 퍼블릭 서브넷과 프라이빗 서브넷이 있다.
퍼블릭 서브넷
퍼블릭 서브넷은 외부와의 자유로운 통신이 가능한, 외부 인터넷 구간과 직접적으로 통신할 수 있는 공공 네트워크이다.
프라이빗 서브넷
프라이빗 서브넷은 외부에서 직접 접근할 수 없는 네트워크이다. 프라이빗 서브넷에 실제 서버가 동작하게끔 설정하고 외부에서 이곳으로 직접 접근이 불가능하게 한 후 퍼블릿 서브넷을 통해 외부에서 요청이 들어오면 퍼블릿 서브넷과 프라이빗 서브넷 사이에 연결을 하여 통신한 후 프라이빗 서브넷에서 필요한 요청 데이터를 NAT Gateway를 통해 요청에 응답하게 설계할 수 있다.
이 개념을 이해하려면 직접 VPC, Public Subnet, Private Subnet, NAT Gateway 등 만들어보기로 하자.
설계할 네트워크 전체 그림은 다음과 같다.
VPC 생성
AWS Console에 'VPC'를 검색해서 나오는 서비스를 클릭한다.
메인 화면 좌측 사이드바에 Virtual private cloud 섹션에 'Your VPCs'를 클릭해서 우측 상단 'Create VPC' 클릭
VPC Name tag와 CIDR을 설정 후 나머지는 기본값으로 설정한 다음 'Create VPC'를 클릭
생성한 VPC 확인
이렇게 VPC가 생성됐으면 VPC에서 사용될 서브넷을 총 6개를 만든다. 그중 2개는 Public 나머지 4개는 Private으로 설정한다.
가용영역을 둘로 나누어서 AZ1에는 Public Subnet 1개, Private Subnet 2개로 만들고 AZ2는 Public Subnet 1개, Private Subnet 2개로 하여 전체 VPC에 두 개의 가용영역이 있고 그 각각의 가용영역에 Public 1, Private 2 서브넷을 각각 가지는 그림으로 만든다.
Subnet 생성
VPC 메인 화면에서 좌측 Subnets을 클릭하고 나온 화면의 우측 상단 'Create subnet' 클릭
서브넷 생성 화면에서 서브넷이 들어갈 VPC를 선택하는데 방금 만든 VPC를 선택
첫 번째 서브넷을 만들자. AZ-a에 public subnet을 만든다. 서브넷의 대역은10.1.1.0/26으로 설정한다.
서브넷 추가를 위해 'Add new subnet'을 클릭해서 또 다른 서브넷을 만든다. AZ-C에 생성될 퍼블릭 서브넷이다. 대역은 10.1.1.64/26으로 설정한다.
지금까지가 퍼블릭 서브넷을 만드는 내용이었다. 각 AZ에 한 개씩 퍼블릭 서브넷이 있고 각 AZ에 이제 프라이빗 서브넷 2개씩을 할당할 것. 또 서브넷을 만들자.
이제 프라이빗 서브넷이다. AZ-a에 10.1.1.128/27 대역으로 한 개를 생성한다.
AZ-c에 같은 APP용 프라이빗 서브넷을 만들자. 대역은 10.1.1.160/27이다.
AZ-a에 DB용 프라이빗 서브넷을 생성하자. 대역은 10.1.1.192/27이다.
마지막으로 AZ-c에 DB용 프라이빗 서브넷을 생성한다. 대역은 10.1.1.224/27이다.
이렇게 총 6개의 서브넷을 만들고 최종적으로 'Create subnet'을 클릭하자. 총 6개의 서브넷이 생성됨을 확인할 수 있다.
인터넷 게이트웨이 생성
이제 인터넷 게이트웨이를 생성해 보자. 인터넷 게이트웨이는 VPC가 인터넷과 통신할 수 있도록 해준다.
인터넷 게이트웨이를 생성하려면 Virtual Private Cloud 섹션에 Internet gateways를 선택하자.
우측 상단 'Create internet gateway'를 클릭해서 만들자. Name tag를 입력하고 생성을 끝마치면 된다.
생성하고 나면 인터넷 게이트웨이 메인 화면에서 우측 상단 'Actions' 셀렉트 박스가 있다. 거기에 Attach to VPC를 클릭해서 만든 VPC와 인터넷 게이트웨이를 연결하자.
위에서 만든 VPC와 연결을 하면 된다.
라우팅 테이블 생성
라우팅 테이블은 VPC 내 서브넷들이 특정 IP로 향할 때 어디로 가야 하는지에 대한 정보를 저장하고 있는 것으로 생각하면 된다.
우선 만들면서 이해하면 더 빠를 것 같다.
마찬가지로 VPC > Virtual private cloud > Route tables로 이동하자. 우측 상단의 Create route table을 클릭해서 생성하면 된다.
Name과 VPC를 설정한 후 생성하면 끝난다. 지금 생성하는 라우트 테이블은 Public Subnets을 위한 라우트 테이블이다.
하나 더 생성한다. 이번엔 Private Subnets을 위한 라우트 테이블이다. 이름만 'my-private-route'로 달리 생성하자.
그렇게 되면 방금 만든 두 개의 라우트 테이블이 있다.
public route는 public subnet을 위함이다. public subnet은 외부와 통신이 가능해야 한다. 그래서 외부와의 통신을 하기 위한 인터넷 게이트웨이를 만들었고, public route에게 해당 인터넷 게이트웨이를 알려줘야 한다. 그 작업을 위해 my-public-route 내부로 들어가자. 하단 Routes 탭에 Edit routes를 클릭하면 된다. 여기 설정된 기본 라우트 탭은 라우트 테이블을 만들면서 연결한 VPC가 기본으로 테이블에 등록되어 있다.
즉, 10.1.0.0/16은 로컬과의 통신을 하면 된다는 뜻이고 여기서 설정할 0.0.0.0/0은 외부로 나갈 인터넷 게이트웨이와 통신을 하게 설정하면 된다.
즉, 최종 public-route는 다음과 같은 설정값을 가진다.
이 뜻은 이 public route에 할당된 서브넷들은 VPC 내부(10.1.0.0/16)에서는 VPC 내부적으로 통신을 하고 그 외 모든 목적지(0.0.0.0/0)는 인터넷 게이트웨이와 통신을 할 것이라는 의미가 된다.
private route는 VPC 내부와 밖에서는 안으로 들어오지 못하게 하고 안에서 밖으로는 나갈 수 있어야 하기 때문에 NAT Gateway가 필요하다. NAT Gateway는 내부에서 외부로 나가는 것만 허용한다. 그래서 private route에 등록할 NAT Gateway를 먼저 만들어보자.
NAT Gateway 생성
NAT Gateway란 무엇일까?
퍼블릭 서브넷과 프라이빗 서브넷이 공존할 때 프라이빗 서브넷은 인터넷과 통신이 불가능한 네트워크 공간이고 퍼블릭은 인터넷과 통신이 가능한 네트워크 공간이다. 이때 프라이빗 서브넷은 말 그대로 프라이빗하게 즉, 보안에 있어 취약하면 안 되는 민감한 데이터를 다루는 곳이어야 한다. 예를 들면 데이터베이스가 있는 곳. 자, 데이터베이스를 프라이빗 서브넷에 위치한 EC2에 설치해 보자. 여기서 의문이 생긴다.
근데 인터넷과 통신이 안되는데 어떻게 설치를 하지?
그렇다. 인터넷과 통신이 불가능한데 설치를 할 수 있을 리 없다. 즉, 이 프라이빗 서브넷일지라도 인터넷과의 통신이 필요한 경우가 더러 있는데 이럴 때 인터넷과 통신을 가능하게 해주는 녀석이 바로 NAT Gateway이다.
위 그림에서 Private subnet App A에서 인터넷과 통신을 하고 싶으면 인터넷과 통신할 수 있는 Public subnet A에 있는 NAT Gateway에게 트래픽을 알려야 한다. 그래서 흐름은 다음과 같다.
중요! 1. Private subnet App A에서 인터넷(ex: 211.158.2.3)으로 요청을 보낸다. 2. Private subnet App A에 매핑된 route table을 보니 로컬로 등록된 IP가 아닌 IP들은 전부 NAT Gateway를 향한다. 3. NAT Gateway로 트래픽을 보낸다. 4. NAT Gateway는 받은 트래픽을 실제 요청지를 저장한 후 다시 Router로 보낸다. Router는 Public subnet A에 매핑된 route table을 확인한다. 5. 확인해 보니 0.0.0.0/0은 Intenet Gateway로 향하고 있다. Internet Gateway로 트래픽을 보낸다. 6. 인터넷으로의 요청에 대한 응답을 Internet Gateway가 받았다. 받으면 이 응답을 Router가 받아서 route table을 확인한다. 응답받을 곳은 NAT Gateway 이므로 NAT Gateway로 향한다. 7. NAT Gateway로 응답을 받고 이 녀석이 저장해 둔 원래 요청지인 Private subnet App A로 다시 응답을 보낸다. 8. Router는 응답을 보낼 주소를 route table에서 찾는다. 찾아보니 Private subnet App A를 가르키고 있다. 해당하는 곳으로 응답을 보낸다.
이 흐름이 NAT Gateway를 통한 Private subnet이 인터넷과 통신하는 방법이다. 이 흐름을 보니 왜 NAT Gateway가 Public subnet에 존재하는지 더 명확하게 알 수 있었다. 인터넷과 통신을 하기 위해서는 Public subnet에 매핑된 route table을 참조해야하기 때문이다.
Public subnet에 매핑된 route table은 0.0.0.0/0이 Internet Gateway를 향하고 있고 Private subnet에 매핑된 route table은 0.0.0.0/0이 NAT Gateway를 향하고 있기 때문이다.
프라이빗 서브넷의 인스턴스가 다른 VPC, 온 프레미스 네트워크 또는 인터넷의 서비스에 연결하는 데 사용할 수 있는 가용성이 뛰어난 관리형 NAT(Network Address Translation) 서비스를 말한다.
NAT Gateway는 두 가지 유형이 있다.
퍼블릭: 프라이빗 서브넷의 인스턴스는 퍼블릭 NAT Gateway를 통해 인터넷에 연결할 수 있지만 인터넷으로부터 원치 않는 인바운드 연결을 수신할 수 없다.
프라이빗: 프라이빗 서브넷의 인스턴스는 프라이빗 NAT 게이트웨이를 통해 다른 VPC 또는 온프레미스 네트워크에 연결할 수 있다.
다음은 my-private-route를 선택하고 서브넷을 연결한다. 여기에는 프라이빗 서브넷들은 선택하면 된다.
EC2 인스턴스 생성
이제 Public subnet A에 EC2 인스턴스를 만들어보자. 이 인스턴스는 Bastion(요새, 보루 즉 거쳐가는 곳을 의미) Host라는 개념으로 보면 된다. 여기를 통해서 AWS DevOps 유저가 직접 접근하여 Private subnet에 있는 EC2 인스턴스에 들어갈 수 있게 될 것.
Security group 생성
AWS Console에 EC2를 검색해서 EC2 Instances로 들어가자. 그래서 새로운 EC2 인스턴스를 만든다. 그러나 인스턴스를 만들기 전 한 가지 먼저 진행할 부분이 있는데 '보안 그룹' 생성을 먼저 하자. Security group을 생성해서 만들 EC2에 해당 보안 그룹을 적용할 것인데 이는 이 인스턴스에 SSH로 접속하는 인바운드 규칙을 만들기 위함이다. 위 큰 그림에서 볼 수 있듯 'AWS DevOps 유저는 Public subnet A에 있는 EC2에 직접 접근할 수 있어야 한다. 그것을 허용하게 하는 보안 그룹을 생성할 것이다.
생성하는 화면에서 Security group name, Description, VPC에 값을 지정한다. VPC는 위에서 만든 VPC를 선택한다.
하단 인바운드 규칙에서는 AWS DevOps 유저가 오로지 현재 본인 만이라고 가정하고 SSH 접속 허용 IP를 My IP로 할당하자.
그리고 생성하기를 누르면 리스트에 방금 만든 보안그룹이 보인다.
Bastion Host(EC2) 생성
이제 인스턴스를 생성해 보자. 생성 화면에서 인스턴스의 Name을 설정한다. 그리고 OS Image는 Amazon Linux를 사용한다.
Key pair는 새로 생성을 해도 되고 기존에 사용했던 key pair가 있으면 그대로 사용해도 된다. 지금은 새로 만들어 사용한다.
Network settings는 위에서 만든 VPC와 서브넷은 Public Subnet A로 설정하고 보안 그룹은 방금 막 만든 보안그룹으로 세팅한다.
나머지 값은 그대로 둔 채 생성을 한다. 인스턴스 리스트에 생성한 인스턴스가 잘 보인다.
Elastic IP를 생성한 EC2에 할당
이제 Elastic IP를 방금 생성한 EC2에 할당한다. Network & Security > Elastic IPs로 가서 우측 상단 Allocate Elastic IP address를 클릭한다.
모든 값은 기본값으로 두고 하단 'Allocate' 버튼을 누르면 방금 생성한 Elastic IP가 나오고 그 녀석을 선택해서 방금 만든 EC2에 할당한다.
EC2에 잘 할당됐는지 확인하기 위해 Instances로 가서 방금 만든 EC2에 저 Elastic IP가 잘 할당됐는지 확인해 보자. 생성한 EC2를 선택하면 하단에 정보가 나오는데 Public IPv4 주소를 보면 방금 생성한 Elastic IP가 잘 할당됐음을 확인할 수 있다.
Private Subnet App A의 인스턴스에 할당할 보안 그룹 생성
위 전체 네트워크 설계 그림에서 보면 Bastion Host(Public Subnet A의 EC2)에서 Private Subnet App A에 있는 인스턴스에 접속할 수 있는 상태이다. 이러기 위해서는 Private Subnet App A에 보안 그룹을 할당해서 Bastion Host로부터 SSH로 들어오는 인바운드 규칙을 할당해줘야 한다. 이 작업을 해주자.
EC2 > Network & Security > Security Groups > Create security group
Name, Description, VPC를 설정해 주고 (VPC는 위에서 만든 VPC) 인바운드 규칙에 SSH로 들어오는 것들 중 허용하는 소스는 위에서 Bastion Host에 할당한 보안 그룹만 할당한다. 즉 Bastion Host에 SSH로 접속 가능한 호스트는 이 Private Subnet App A의 인스턴스에도 SSH로 접속이 가능하다는 의미가 된다.
생성이 잘 됐으면 Private Subnet App A에 생성할 EC2를 만들자.
Private Subnet App A EC2 생성
Name을 적절히 설정해주고 OS Image는 Amazon Linux, 그 외 다 기본값으로 설정하고 Key pair는 위에서 만든 EC2에 사용한 것을 그대로 사용한다.
Network settings는 VPC는 위에서 만든 VPC, Subnet은 Private Subnet App a, Security group은 방금 만든 보안 그룹을 선택하고 생성을 완료한다.
생성이 완료되면 인스턴스 리스트에 잘 보이면 된다.
Copy file over SSH
이렇게 생성이 다 됐으면 Bastion Host에서 Private EC2에 SSH로 접속이 가능해야 한다. 보안 그룹을 그렇게 세팅했으니까.
근데 여기서 한 가지 더 해줄 작업이 있는데 보안 그룹은 Bastion Host로부터 SSH로 접속 가능하게 인바운드 규칙을 만들었는데 그때 사용할 Key pair는 그대로 같은 Key pair를 사용했기 때문에 Bastion Host에 접속할 키를 Bastion Host에도 저장해놔야 한다. 그러기 위해 로컬에서 SSH를 이용해서 다른 서버로 파일을 카피하는 명령어로 .pem키를 우선적으로 보내자.
Connect 'Public Subnet A EC2' to 'Private Subnet App A EC2' via SSH
이제 진짜 Bastion host에서 private EC2에 접속해 보자. 우선 Private EC2의 Private IPv4 Address를 확인하자.
Bastion host에 들어간 후 다음 명령어로 private EC2에 들어가 보자. 당연히 다음처럼 명령어를 사용하려면 .pem 파일이 있는 경로에서 실행해야 한다.
ssh -i my-ec2-keypair.pem ec2-user@10.1.1.156
그러면 이와 같이 정상적으로 접속이 됐음을 확인할 수 있다.
들어와서 한 가지 더 확인할 게 있다. 바로 NAT gateway를 통해 내부에서 외부로 통신이 가능한지를 확인하는 것. 다음 명령어를 통해 외부 인터넷으로 통신이 가능한지 확인해 보자.
curl -v www.google.com
결과는 다음처럼 200 OK가 나와야 한다.
OpenVPN을 이용해 Bastion Host 대신 Private Subnet EC2에 접속해 보기
위 전체 네트워크 설계 그림에서 변경되는 부분이다.
Public subnet A에는 OpenVPN용 EC2가 만들어질 예정이다. 그리고 그 OpenVPN을 통해서 Private EC2에 접속해 보자.
OpenVPN EC2 만들기
Name은 my-openvpn-ec2로 설정한다.
OS Image는 'openvpn'으로 검색
보이는 화면에서 AWS Marketplace AMIs에 가장 상위에 있는 OpenVPN Access Server 이미지를 선택한다.
나머지 설정은 그대로 하고 Key pair는 위에서 사용한 그대로 사용한다.
이제 Network settings은 VPC는 위에 만든 VPC, Subnet은 public subnet A, 보안 그룹은 기본으로 설정된 보안 그룹 그대로 가져가되, 아래 규칙은 모두 My IP로만 가능하게 변경하자. 아래 사진 말고 더 있다. 나오는 거 전부 My IP로 선택.
이렇게 설정한 다음 생성해서 EC2를 만든다. 이렇게 만들었으면 Elastic IP를 이 EC2에 할당해줘야 한다. Elastic IP는 생성하고 할당하는 것까지 위에서 해봤으니 그대로 하면 된다. 할당했으면 EC2에 그 할당된 IP를 확인하여 SSH로 접속한다. 본인의 경우 Elastic IP는 다음과 같다. 접속 유저명은 'openvpnas'로 한다.
ssh -i my-ec2-keypair.pem openvpnas@3.34.163.7
접속하면 이러한 OpenVPN 설정 화면이 노출된다.
모두 기본값 설정으로 적용하고 설정이 완료되면 다음처럼 Admin URL이 보인다.
이 경로로 접속해 보자. 접속하면 다음처럼 경고 화면이 나오는데 '안전하지 않음'으로 접속하면 된다.
그러하면 드디어 이러한 화면이 나온다.
로그인하기 위해 유저 정보를 가져와야 하는데 기본값은 최초 OpenVPN 설정하는 부분에서 알려준다.
유저명은 'openvpn' 패스워드는 'aPlXOBiWw0RL'로 되어있다. 이렇게 로그인해 보자.
이렇게 로그인이 잘 되고 'Agree'를 클릭
로그인이 되면 좌측 USER MANAGEMENT > User Permissions에 들어가서 유저를 새로 만들어보자.
자동 로그인 설정과 패스워드를 입력하고 저장하자.
이제 이 유저로 로그인이 잘 되는지 Admin 패널이 아닌 일반 URL로 들어가서 로그인해 보자.
Sign In을 클릭하면 다음 화면이 나온다.
위 화면에서 OpenVPN Connect for all Platforms 섹션에서 본인의 OS에 맞게 설치를 하자.
본인의 경우 macOS라 다음처럼 보인다. 활성화 버튼을 토글 하면Connect가 된다.
이제 OpenVPN으로 Public Subnet의 EC2에 접속이 되어 있는 상태이므로 로컬에서 Private Subnet App A의 EC2 인스턴스로 SSH로 바로 접속이 되는지 확인해 보자. 그러나 그전에, 위에서는 설정 안 한 게 있는데 OpenVPN EC2는 Bastion host랑은 다른 EC2이기 때문에 이 OpenVPN EC2를 생성했을 때 적용한 보안 그룹 또한 Private EC2에 인바운드 규칙으로 할당해줘야 한다.
Security Groups > my-private-ec2-sg에서 인바운드 규칙에 다음처럼 OpenVPN EC2를 생성했을 때 만든 보안 그룹을 추가한다.
당연히 위에서 테스트 한 Private EC2라서 NAT gateway를 통해 외부로 통신이 가능하다.
VPC Peering
이제 한 발 더 나아가서 Region이 다른 VPC간 Peering을 통해 서로 통신이 가능하도록 만들어보자.
그림은 다음과 같다.
서울과 도쿄에 있는 VPC간 Peering을 해서 서로 통신이 가능하게 해보자.
도쿄에서 VPC 생성
우선 Region을 Tokyo로 변경하자.
우측 상단 Region을 선택해서 원하는 Region으로 변경할 수 있다. 그리고 VPC를 만들어보자.
생성 화면에서 VPC settings에 'VPC and more'를 선택하면 좀 더 템플릿 느낌으로 VPC와 그 외 Subnet, Network, Route tables등을 만들어준다.
여기서 추가 설정을 해주자. 우선 VPC IPv4 CIDR을 192.168.10.0/24로 설정한다.
그 다음 AZ는 2개, Public subnets 2개, Private subnets 2개, NAT gateway는 생성하지 않고, VPC endpoint도 생성하지 않는다.
그 외 나머지는 전부 기본 설정으로 하고 생성을 마친다. 생성이 끝나면 리스트에서 확인할 수 있다.
도쿄에서 EC2 인스턴스 생성
이제 EC2 인스턴스를 생성해보자. Name은 my-tokyo-private-ec2로 설정하고 Amazon Linux 이미지를 사용한다.
여기서는 서버에 직접 접근하지는 않을거니까 Key pair 생성도 생략한다.
네트워크는 방금 만든 도쿄의 VPC와 private subnet-1을 선택하면 된다.
그리고 생성 버튼을 누르면 키페어 없이 생성하는게 맞냐는 안내 팝업이 뜨는데 맞다고 해주고 생성을 진행하자.
보안 그룹 인바운드 규칙 수정
이제 생성된 인스턴스에 적용된 보안 그룹을 수정해야 한다. 서울에서 들어올 수 있게 서울 대역을 허용해주자.
VPC Peering connection
이제 도쿄 VPC에 피어링을 걸어보자. VPC > Virtual private cloud > Peering connections에서 우측 상단 'Create peering connection'을 클릭
Peering connection settings에서 로컬 VPC는 도쿄니까 도쿄의 VPC를 선택하고 연결할 VPC는 일단 내 계정의 다른 지역을 선택해서 서울을 선택하고 서울의 내가 가지고 있는 특정 VPC ID를 입력해주면 된다. 그리고 생성하자.
생성하고 나면 다음처럼 상단에 서울 지역으로 가서 이 요청을 수락해야한다라는 안내 토스트가 나온다.
서울의 피어링 연결로 가서 요청 대기중인 녀석을 수락해주자.
이제 거의 끝났다. 이제 확인만 해보면 되는데 SSH로 서울의 EC2 인스턴스 내부로 들어가자. 그러나 그 전에, 서울의 라우트 테이블에서 프라이빗 용 라우트 테이블에 도쿄 IP를 추가해줘야한다. 반대로도 마찬가지.
서울에서 도쿄로 Ping 날려보기
이제 모든 준비가 끝났다. 위에서 OpenVPN을 커넥트하면 로컬에서 바로 프라이빗 EC2 인스턴스에 SSH로 접속이 가능하다. 접속한 후 도쿄의 EC2 인스턴스 Private IPv4 Address로 핑 명령어를 입력하자.
ping 192.168.10.134
이처럼 핑을 날려서 잘 받으면 된다.
결론
VPC를 최초 생성해 보는것에 시작해서 AZ별 서브넷을 구축하고 퍼블릭 서브넷과 프라이빗 서브넷을 각각의 가용영역에 만들어서 퍼블릭 서브넷은 인터넷 게이트웨이에 프라이빗 서브넷은 NAT 게이트웨이에 연결하여 외부로 통신할 수 있게 하고 그 정의를 라우트 테이블에 해보았다. 더 나아가서 퍼블릭 서브넷에 EC2 인스턴스(Bastion host)를 만들어서 로컬에서 이 곳으로 접근하고 이 곳에서 프라이빗 서브넷에 있는 EC2에 접근하는것을 해보았고 그 과정에서 필요했던 보안 그룹 설정도 만들어보았다. 더 나아가서 Bastion Host에 접근해서 또 다른 EC2에 접근하는 게 아닌 OpenVPN EC2 인스턴스를 만들어서 이 인스턴스가 프라이빗 EC2에 연결할 수 있게 설정하고 로컬에서 OpenVPN을 이용해서 다이렉트로 프라이빗 서브넷의 EC2 인스턴스에 접속할 수 있게도 해보았다. 더 나아가서 서로 다른 Region에 VPC끼리 Peering을 해서 서울에서 도쿄로 통신하는 것까지.
AWS를 DevOps 개념과 엮어 사용하기 위해서 DevOps 개념에 대해 좀 자세히 이해해 보자.
DevOps란?
Develop + Operation의 합성어로 개발과 운영의 경계를 허물고 통합하고자 하는 문화 또는 철학을 의미한다. 기술이 아니다.
소프트웨어 개발 프로세스와 운영의 모든 단계의 통합과 자동화를 목표로 한다. 즉, 개발 - 빌드 - 테스트 - 배포 - 운영 - 모니터링의 이 영원히 반복되는 사이클을 통합하고 자동화하여 하나의 문화로 만드는 것을 목표로 삼는다고 보면 되겠다.
이 DevOps라는 개념이 도입되기 전엔 배포 방식은 개발과 운영의 철저한 분리 상태를 유지하고 개발자는 운영에 대해 생각하지 않고 운영자는 개발에 대해 생각하지 않는 그런 흐름이 진행됐다. 그러다 애자일(Agile)이 등장하고 기존 소프트웨어 개발 방식인 Waterfall 방식과 힘겨루기를 하다 마침내 DevOps라는 개념이 탄생했다.
애자일이란, 기존 Waterfall 방식과 달리(Waterfall 방식은 순차적으로 하나씩 진행하는 방식이다. 계획 - 요구사항 분석 및 정리 - 개발 - 테스트 - 빌드 - 배포... 이러한 일련의 과정을 물이 아래로 흘러내려가는 폭포수같이 단계가 있다고 해서 Waterfall이라는 이름을 채택한 것으로 널리 알려져 있다) 기간을 아주 짧게 설정하고 그 짧은 기간 동안 수행할 Task들을 하나씩 처리하여 기간 동안의 처리된 개발 변동 사항에 대해 작은 사이즈로 릴리스하는 방식을 말한다.
이러다 DevOps에 하나가 더 추가되는데 이는 DevSecOps이다.
DevSecOps는 Security가 추가된 합성어로 보안까지 포함하도록 확장된 개념이다. 소프트웨어 배포에 관여하는 모든 사람들이 보안을 최우선으로 하는 문화.
DevOps 핵심
DevOps 업무의 주 대상은 개발자이다. 그래서 개발자가 운영에 참여할 수 있는 환경과 문화를 제공하고 개발자가 비즈니스 로직에 집중할 수 있도록 지원하는 것이 DevOps의 핵심이라고 본다. 개발과 운영 사이에 놓인 커다란 벽을 허무는 것.
DevOps 업무 도메인
도메인
내용
네트워크
- 가상 네트워크 및 물리 네트워크 구성 - 프록시 / VPN 서버 운영 - DNS 서버 운영
클라우드 플랫폼
- 개발자들이 활용할 수 있도록 클라우드 환경 운영 (자체 클라우드, 퍼블릭 클라우드)
배포 플랫폼
- Gitlab/Github 등 개발 협업 플랫폼 운영 - CI/CD 파이프라인 시스템 구축 및 운영 - QA 테스트 및 성능 테스트를 위한 환경 제공 - 패키지 저장소 운영 및 배포 산출물 관리
보안 플랫폼
- LDAP, AD, SAML 등을 활용하여 통합된 임직원 계정계 운영 - 서버 및 데이터베이스 접근제어 시스템 구축 및 운영
오케스트레이션 플랫폼
- K8s, ECS, Nomad와 같은 오케스트레이션 시스템 구축 및 운영 - Airflow, Argo Workflows와 같은 워크플로우 엔진 구축 및 운영
데이터 플랫폼
- MySQL, DynamoDB, Redis와 같은 데이터베이스 구축 및 운영 - RabbitMQ, Kafka, SQS와 같은 메시징 서비스 구축 및 운영 - 데이터 웨어하우스, BI 대시보드 구축 및 운영
관측 플랫폼
- 로그, 메트릭, 업타임, APM 정보를 관측할 수 있는 중앙화 된 시스템 구축 및 운영 - 주요 이벤트에 대한 알림 시스템 구축
서비스 운영
- 개발자들과 협업하여 서비스 공동 운영
DevOps 팀 핵심 지표
지표
내용
장애 복구 시간 (MTTR: Mean Time To Recovery)
얼마나 빠르게 장애 상황에서 복구할 수 있는가?
변경으로 인한 결함률 (Change Failure Rate)
변경 사항으로 인한 장애가 얼마나 발생하는가?
배포 빈도 (Deployment Frequency)
얼마나 배포를 자주 하는가?
변경 반영 소요 시간 (Lead Time for Changes)
변경 사항을 프로덕션 배포에 걸리는 소요 시간은 얼마인가?
Mutable Infra / Immutable Infra
가변 인프라(Mutable Infra)와불변 인프라(Immutable Infra)에 대해 알아보자.
가변 인프라(Mutable Infra)
어떤 웹 서버 구축을 예로 들어보자. 웹 서버를 구축하기 위해 물리 서버를 한 대 구입하고 그 안에 웹 서버가 실행가능한 상태를 만들어 웹 서버를 실행한다. 그러다 시간이 흘러 버전 업그레이드 또는 기술의 변경 같은 변경사항이 있을 때 서버는 업데이트 및 수정이 일어난다. 즉, 서버가 이전 환경과 달리 변경된 환경이다. 최초 배포 상태와 다른 상태, 환경으로 배포가 된다.
이렇게 인프라가 변하는 환경을 가변 인프라라고 한다.
불변 인프라(Immutable Infra)
위 예시 그대로 웹 서버 구축을 한다고 했을 때 웹 서버를 구축하기 위해 필요한 모든 환경을 구성한 뒤 웹 서버를 실행한다. 그러다 시간이 흘러 버전의 변경 또는 기술의 변경점이 생길 때 기존에 구축된 서버에 변경 사항이 가해지는 게 아니라 아예 새로운 서버를 만들어 기존 서버를 대체한다. 즉, 최초 서버가 배포된 이후 절대 변경되지 않는 형태의 인프라를 불변 인프라라고 한다.
가변 인프라에서 불변 인프라 추세로
가상화 및 클라우드 컴퓨팅이 널리 사용되기 이전 환경은 물리 서버를 중심으로 구성되었다. 물리 서버는 비용도 많이 발생하고 이 물리 서버 위에 서버를 실행하기까지 시간도 많이 소요되는데 이러한 점이 가변 인프라의 시작점이다. 즉, 새로운 서버를 또 구입하는 비용과 구입한 후 설정을 처음부터 다시 하기 부담스러우니 기존 서버를 가지고 변경지점이 생기면 점차 변경해 나가는 것이 더 효율적인 인프라 구축 방식이었다.
그러나, 이 방식은 어떤 문제를 발생시키냐? 점점 변경이 가해지면 가해질수록 최초 서버의 환경에서 설정의 변화가 많이 생겨난다. 이러한 것을 '스노우 플레이크'라고 표현한다. 그리고 서비스의 다운 타임이 최소화되도록 하니 서버의 문서화보다 변경 - 적용 - 배포가 먼저가 되고 그 결과 문서화가 잘 이루어지지 않고 그러다 보니 최초 배포 환경과 점점 다른 설정 환경으로 변하는 지점 파악이 어려워진다. 이러한 것을 '컨피그레이션 드리프트'라고 한다. 이에 더해서 시스템의 변경은 리스크가 따른다. 변경이 원활히 수행된다면 더할 나위 없이 좋겠지만 어디 그랬던 적이 있는가? 항상 문제는 발생하기 마련이기 때문에 변경하다가 문제가 생기면 이 지점에서 또 병목 현상이 생길 수 있다. 그리고 또 하나의 문제는 이렇게 계속 변경사항이 생기면 생길수록 프로덕션 환경과 동일한 스테이지 환경을 만들기도 어려워진다는 것이다. 그 결과 디버깅 및 테스트하기도 어려워진다는 것.
이러한 단점을 가상화/클라우드 컴퓨팅 기술이 활성화되면서 극복하게 됐다. 클라우드 서비스는 비용이 저렴하고 (사용한 만큼만 지불하면 된다. 이 말은 물리서버를 구비하는 물릴 수 없는 자본지출을 운영지출로 대체할 수 있다는 것이고 그 결과 비용의 효율적인 사용이 가능해진다.) 확장이 수월하고 생성 및 폐기까지 몇 초에서 몇 분밖에 걸리지 않는다.
불변 인프라의 모든 배포는 검증되고 버전 관리가 된 이미지를 바탕으로 새 서버를 프로비저닝 하여 실행된다. 그 결과 이러한 배포는 서버의 이전 상태에 의존하지 않게 된다. 불변 인프라의 모든 설정 변경은 문서화와 함께 버전 컨트롤에 변경된 이미지를 저장하고 그 이미지를 교체 서버에 배포하는 자동화되고 통일된 배포 프로세스(클라우드 API나 프로그래밍 친화적으로 자동으로 새 서버에 프로비저닝)를 사용하면서 구현된다. 배포된 후 변경 지점이 전혀 없으니 Configuration Drift가 발생하지 않게 된다.
Terraform
불변 인프라를 프로그래밍 친화적으로 새 서버를 프로비저닝 하는 기술 중 하나인 Terraform을 사용해서 AWS에 인스턴스를 새로 프로비저닝해보자. 'Terraform'은 클라우드에 프로비저닝, 리소스 관리를 자동으로 할 수 있게 해주는 인프라스트럭쳐 자동화 도구이다.
여기선 약간의 가정이 필요한데, 기존 배포된 서버가 있고 새 배포를 하는 것이라고 가정하자. 그렇다는 것은 기존 배포된 서버에서 사용하는 데이터가 있었을 것이고 그 데이터는 AWS S3, DynamoDB에서 관리하고 있었다고 가정해 보자. 그렇기에 S3, DynamoDB를 만드는 작업부터 해보자.
AWS DynamoDB 생성
S3 버킷을 만드는 작업은 이전 포스팅에서 해봤으니 생략하고 DynamoDB를 만들어보자.
검색창에 'DynamoDB'를 검색하면 서비스가 나온다. 클릭해 보자.
우측 상단 'Create table' 버튼을 클릭하자.
상단부터 Table name, Partition key를 입력하자.
그 외 설정은 기본 설정으로 한 뒤 생성하면 된다.
Terraform install
이제 Terraform CLI를 이용해야 하니까 먼저 설치를 해야 한다. 아래 링크에 설치 가이드가 있다.
본인은 macOS를 사용하므로 다음 명령어로 설치할 것.
brew update
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
잘 설치가 됐는지 확인하기 위해 다음 명령어를 입력한다.
terraform -help
Usage 관련 내용이 나오면 성공.
Terraform 소스코드
소스에 대한 깊은 내용은 매뉴얼을 참고하자. 지금 여기서 핵심은 코드로 프로비저닝을 할 수 있는 도구를 사용해 보는 것에 의의를 두는 것.
provider는 AWS이고 IAM 유저의 access_key와 secret_key를 입력해 주자.
variables.tf
variable "region" {
default = "ap-northeast-2"
}
이 파일들이 위치한 경로에서 다음 명령어를 입력한다.
terraform init
입력하고 나면 해당 경로에 .terraform.lock.hcl 파일이 자동으로 생성되는 것을 확인하면 된다.
plan 명령어로 어떤 리소스가 생성될지 확인할 수 있다.
terraform plan -lock=false
output:
data.aws_ami.amazon_linux2: Reading...
data.aws_ami.amazon_linux2: Read complete after 1s [id=ami-0007d44f4d00c13b1]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.ec2_example will be created
+ resource "aws_instance" "ec2_example" {
+ ami = "ami-0007d44f4d00c13b1"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ subnet_id = (known after apply)
+ tags = {
+ "Name" = "cwchoi-tf-ec2-example"
}
+ tags_all = {
+ "Application" = "myapp"
+ "Environment" = "dev"
+ "Name" = "cwchoi-tf-ec2-example"
+ "Terraform" = "true"
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
EC2 인스턴스가 생성된다는 결론이 나온다. 왜냐하면 main.tf 파일에서 resource에 aws_instance를 생성하게 설정했기 때문이다.
이제 새 프로비저닝을 하면 된다. 다음 명령어를 수행하자.
terraform apply -lock=false
다음 확인 메시지가 나오는데 'yes'를 입력하면 된다.
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
생성이 완료되면 다음과 같은 결과가 나온다.
aws_instance.ec2_example: Creating...
aws_instance.ec2_example: Still creating... [10s elapsed]
aws_instance.ec2_example: Still creating... [20s elapsed]
aws_instance.ec2_example: Still creating... [30s elapsed]
aws_instance.ec2_example: Creation complete after 33s [id=i-01e423c5447a7a27f]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
살제로 생성이 됐는지 확인해 보려면 AWS Console로 가보자.
잘 생성된 것을 볼 수 있다.
이렇게 생성된 리소스를 이제 삭제해 보자.
terraform destroy -lock=false
아래처럼 물어보는데 'yes'를 입력하면 된다. 당연히 여기서 삭제하는 건 init을 했을 때의 관리되고 있는 리소스들을 삭제하는 것.
...
Plan: 0 to add, 0 to change, 1 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value:
output:
aws_instance.ec2_example: Destroying... [id=i-01e423c5447a7a27f]
aws_instance.ec2_example: Still destroying... [id=i-01e423c5447a7a27f, 11s elapsed]
aws_instance.ec2_example: Still destroying... [id=i-01e423c5447a7a27f, 21s elapsed]
aws_instance.ec2_example: Still destroying... [id=i-01e423c5447a7a27f, 31s elapsed]
aws_instance.ec2_example: Still destroying... [id=i-01e423c5447a7a27f, 41s elapsed]
aws_instance.ec2_example: Destruction complete after 41s
Destroy complete! Resources: 1 destroyed.
실제로 삭제가 됐는지 확인하기 위해 또 AWS Console에서 확인해 보자.
정상적으로 'Terminated' 됐다.
결론
이런 식으로 코드친화적으로 불변 인프라 방식의 프로비저닝을 할 때 'Terraform'을 이용할 수 있다.
변경될 부분에 대해서 기존 인스턴스에서 수정하는 방식이 아니라 아예 'Destroy' 후 변경사항을 적용 후 새로운 버전으로 프로비저닝을 하는 것.
Amazon S3에서 데이터들을 담고 보관하는 장소를 버킷이라고 표현한다. 버킷을 만들어보자.
AWS Console에서 'S3'를 검색한다.
메인화면 우측 상단에 'Create bucket' 버튼을 클릭한다.
상단부터 버킷 이름과 지역을 선택한다.
'Object ownership'은 기본 설정인 'ACLs disabled'를 선택한다. 이건 모든 오브젝트들은 현재 버킷을 만들고 있는 유저가 전부 Owner인 케이스를 말한다.
Block Public Access settings for this bucket 설정은 'Block all public access'를 선택 해제하고 진행한다. 외부에서 바로 이 Object에 접근해보는걸 테스트해보기 위함이다.
그 외 나머지 세팅 역시 전부 기본으로 설정된 채로 진행하고 하단 'Create bucket' 버튼을 클릭한다. 그러면 다음처럼 버킷이 생성된다.
버킷안으로 들어가보면 여러 탭이 존재하는데 'Objects' 라는 탭이 실제 업로드 되는 데이터들이 모여져 있는 곳이다. 그니까 파일이 3개면 3개 하나하나가 각각 오브젝트 하나하나가 되는 것이다. 여기에 우측 상단 'Upload' 버튼을 눌러 아무 이미지나 업로드해보자.
본인은 이미 이미지 하나를 업로드 한 상태이다.
업로드하고 'Permissions' 탭을 눌러 버킷 정책을 설정할 수 있다. 해당 탭을 눌러 화면을 살짝 내려가보면 'Bucket policy' 부분이 있다. 여기서 버킷 정책을 설정할 수 있다. 'Edit' 버튼을 눌러 버킷 정책을 만들어보자.
다음과 같이 설정한다.
Version: '2012-10-17'이 의미하는 건 policy language의 현재 버전을 의미한다. 그래서 반드시 이 값을 사용해야 한다. 이 값 말고 하나 더 유효한 게 2008-10-17이 있다. 이건 이전 버전이다. 이 버전은 사용하지 말고 지금처럼 2012-10-17 버전으로 사용하면 된다.
Statement: 이 요소는 정책에 대해서 주 요소이다. 필수 값이고 하나 이상의 개별적인 statement가 포함된다. 그러니까 쉽게 말해 정책에 대한 실질적인 적용을 하는 부분으로 생각하면 되겠다.
Sid: Statement ID의 약자이다. 그냥 policy statement의 identifier라고 생각하면 된다. 없어도 무방하다.
Effect: 필수값이며 유효한 값은 "Allow", "Deny"가 있고 Resource에 대한 Action을 허가 또는 제어하는 키워드이다.
Principal: resource-based JSON policy에서 사용되며 resource 기반의 정책에서는 이 값은 필수이다. 쉽게 말해 Resource에 대한 Action을 행하는 대상을 지정하는 것이다. 예를 들면 AWS account가 '123456789012' '555555555555' 인 두명의 유저에 대해서 적용하겠다하면 다음처럼 작성한다.
Identity and Access Management의 약자로 AWS 리소스에 대한 액세스를 안전하게 제어할 수 있는 서비스이다.
IAM User/User Group/Role/Policy로 구성되어 있다. Policy는 리소스에 접근할 수 있는 접근 권한 정책을 말한다.
Policy는 단일 유저에게, 유저 그룹에게, 역할(Role)에 부여할 수 있다.
정책을 다루는 방식은 크게 두 가지로 분류가 가능하다.
RBAC(Role Base Access Control): 역할 기반 접근제어 정책
ABAC(Attribute Base Access Control): 속성기반 접근제어 정책
RBAC
역할 기반 접근제어 정책은 역할에 정책을 부여해서 해당 역할이 접근할 수 있는 리소스(EC2, RDS,...)에 접근 가능하도록 하는 방식이다.
예를 들어 Role DEV라는 역할을 만들고 해당 역할에 DEV 관련 리소스에만 접근 가능하도록 정책을 설정하면 해당 역할을 부여받은 사용자들은 DEV 관련 리소스에만 접근할 수 있는 방식이다.
다음 그림을 보자.
역할 A가 할 수 있는 정책을 부여하고 그 역할A에 단일 유저 또는 그룹을 할당하면 해당 단일 유저나 그룹은 그 역할이 할 수 있는 정책에 따라서 리소스에 접근이 가능한 방식이다. 간단하다. 그러나 이런 방식은 역할이 늘어날 때마다 또는 리소스가 추가될 때마다 역할과 정책을 수정하거나 새로 만들어 할당해야 하는 번거로움을 가지고 있다. 이를 해결하기 위해 ABAC이 있다.
ABAC
속성 기반 접근제어 정책은 속성을 기반으로 권한을 정의하는 권한 부여 전략이다. AWS에서는 이러한 속성을 태그라고 한다. IAM User 또는 그룹 또는 역할과 AWS 리소스에 태그를 연결할 수 있다. IAM User/Group/Role에 대해 단일 ABAC 정책 또는 정책 세트를 만들 수 있고 이 정책 세트나 단일 ABAC 정책은 IAM User/Group/Role가 가진 태그와 리소스의 태그가 일치할 때 작업을 허용하도록 설계하는 방식이다.
위 그림이 가장 가시화가 잘 되는 ABAC을 나타낸 그림이다. 하트 태그가 있는 역할은 하트 태그가 있는 인스턴스 또는 컨테이너(이러한 것들을 전부 리소스라고 한다)에만 접근이 가능하도록 설계하는 방식이다.
RBAC Vs. ABAC
기존 RBAC과 ABAC을 비교해보면 RBAC이 ABAC에 비해 가장 큰 단점은 새 리소스를 추가할 때 해당 리소스에 액세스할 수 있도록 정책을 업데이트해야 한다는 단점이 있다는 것이다.
예를 들어, 세 개의 프로젝트가 있을 때 각 프로젝트에 대한 IAM Role을 생성한다. 그 다음 각 IAM Role에 정책을 연결해서 역할을 맡을 수 있는 모든 사람이 액세스할 수 있는 리소스를 정의한다. 그러다가 세 개의 프로젝트 중 한 개의 프로젝트에서 새로운 EC2 리소스가 필요하여 EC2를 생성했지만 해당 리소스에 접근할 수 있도록 정책을 업데이트해야 하는데 까먹어서 하지 않은 경우 해당 프로젝트에 참여하는 인원이고 역할을 부여받았어도 해당 리소스에 접근하지 못한다. 즉, 리소스가 변경될 때 업데이트의 빈번함이 단점이 된다.
ABAC은 RBAC에 비해 다음과 같은 이점을 제공한다.
ABAC은 새 리소스에 액세스할 수 있도록 기존 정책을 업데이트할 필요가 없다. 위 RBAC의 단점에 대한 예시처럼 특정 프로젝트에 새 EC2 리소스가 추가될 경우 해당 프로젝트가 가지는 태그를 새 EC2 리소스를 만들 때 태그로 넣어주면 태그값이 일치하므로 접근이 가능하다.
ABAC을 사용하면 정책 수가 적어진다. 각 Role에 대해 서로 다른 정책을 생성할 필요가 없기 때문에 생성해야 하는 정책이 더 적어진다. 그에 따라 관리하기도 더 쉬워진다.
AWS Organization
여러 AWS 계정을 조직에 통합하고 중앙에서 관리할 수 있는 계정 관리 서비스이다. 계정 및 리소스 접근제어 관리와 통합 결제 기능을 활용할 수 있고 통합 결제 기능을 활용해서 기업의 예산 관리, 보안 및 규정 준수 요구 사항에 충족할 수 있다. 예를 들어 특정 규제 요구 사항을 충족하는 AWS 서비스에만 접근해야 하는 계정이 있는 경우 이러한 계정을 하나의 조직으로 만들어 넣고 관리할 수 있다.
그림에서 OU는 Organization Unit의 약자로 조직 단위를 의미한다. 이 조직 또한 리소스에 접근하기 위한 정책을 가질 수 있음을 확인할 수 있다.
AWS IAM 계정 생성
루트 계정은 있다고 가정하고 시작한다. 없으면 그냥 만들면 된다. AWS 콘솔에서 'IAM'을 검색하면 다음 사진처럼 'IAM' 서비스가 나온다.
들어가면 좌측 Access management 섹션에서 'Users'를 클릭한다.
메인 화면 우측 상단에 'Create user'를 클릭한다.
다음 화면에서 유저네임을 작성한다.
다음 화면에서는 권한을 설정하는데 우선 어드민 유저로 만들어보자. 그 이후 Policy는 직접 설정하거나 AWS가 만들어 놓은 기존에 있는 Policy를 선택할 수도 있다.
'Attach policies directly'를 클릭하고 하단에 'AdministratorAccess'를 선택하면 어드민 유저 권한이 생긴다.
다음 화면에서 계정 생성 전 최종 확인화면인데 이 부분에서 태그를 입력할 수 있다. 태그는 위에서 말한 ABAC과 관련된 그 태그다.
Create user 버튼을 클릭하면 유저가 최종 생성된다. 다시 IAM Users 화면으로 넘어가고 방금 만든 유저가 나온다. 이 유저를 클릭해보자.
들어오면 'Security credentials' 탭에 Console sign-in 이라는 부분이 있는데 이것을 Enable하면 이 유저로 Console에 로그인을 할 수 있는 기능을 부여하는 것이다. Enable한 후 이 유저로 로그인해보자.
우선 'Set password' 부분에 원하는 패스워드를 입력한 후 Apply해보자.
다 적용을 하면 이 IAM User로 로그인할 수 있다. 그 전에 Account Alias라는 것을 설정해야 하는데 이는 IAM Dashboard로 가보자.
이 Alias를 설정하면 로그인할 때 저 외우기 어려운 Account ID 대신 입력할 수 있다. 설정한 후 로그아웃해서 방금 만든 IAM 유저로 로그인해보자.
Account ID는 위에서 만든 Alias로 입력하고 IAM 유저네임은 방금 만든 유저로 입력해서 로그인하면 정상 로그인이 되어야 한다.
이렇게 IAM 유저를 만들어서 IAM 유저별로 콘솔 로그인이 가능하고 유저마다 역할과 책임을 구분지을 수 있다.
AWS-CLI로 현재 사용자 정보 출력해보기
사용자를 만들었으니까 CLI를 이용해서 AWS와 통신해보자. Part 2에서 AWS-CLI 설치는 했으니 바로 사용해보자.
우선 사용자 별 Access key를 받아야 하는데 그건 Access key를 받고자하는 사용자를 클릭해서 'Security credentials' 탭으로 들어가면 된다.
만들었으면 Access Key ID와 Secret Access Key를 받는데 이것을 잘 저장해서 AWS-CLI에 사용자 설정 시 사용해야 한다.
다음 명령어를 입력하자.
aws configure
그럼 총 4가지를 입력해야 한다.
AWS Access Key ID
AWS Secret Access Key
Default region name
Default output format
위에서 만든 Access Key ID와 Secret Access Key를 차례대로 입력하고 지역과 포맷 설정을 해주면 된다. 그럼 현재 로컬에서 AWS-CLI를 사용하는 유저를 지정하게 된다. 잘 지정이 됐는지 현재 유저를 확인해보는 명령어는 다음과 같다.