드디어 마지막 단계! 기본적인 VPC 구성과 NAT Gateway 생성까지 완료하였다.
이제 실제로 배포할 샘플 애플리케이션을 생성하고 배포하고자 한다.
애플리케이션 생성
사전조건은 다음과 같다.
IntelliJ
Spring Boot
Gradle
1. HomeController.java 생성
package com.tistory.docker;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@GetMapping("/")
public String home(){
return "Hello World!";
}
}
간단히 / 경로를 이동했을 때 "Hello World!"를 출력해주는 웹 애플리케이션이다.
이제 Docker build에 필요한 Dockerfile을 프로젝트 경로 아래에 생성한다.
2. Dockerfile 구성
FROM java:8
ADD ./build/libs/docker-0.0.1-SNAPSHOT.jar docker-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["java", "-jar", "docker-0.0.1-SNAPSHOT.jar"]
ADD 절의 jar 파일 경로와 네임만 각자의 프로젝트에 맞게 적용시킨다.
Dockerfile 각각의 의미는 이 이전 CodeDeploy 게시글에 간단히 설명되어있다.링크
이 상태에서 도커빌드가 제대로 되는지 확인하기 위해 아래 명령어를 해당 프로젝트 로컬 위치에서 실행해본다.
당연한 이야기지만 로컬에 Docker가 설치되어있지 않으면 Docker 를 먼저 설치해야 한다.
docker build -t tistory-docker .
현재 위치에 있는 Dockerfile을 기준으로 tistory-docker라는 도커 이미지를 빌드한다는 의미이다.
해당 도커이미지가 정상적인지 로컬에서 먼저 테스트한다.
docker run -p 8888:8888 tistory-docker
정상적으로 실행되는 것을 확인하였다.
이제 ECR에 해당 이미지를 푸쉬하기 위해 ECR 서비스로 이동한다.
4. ECR구성
먼저, ECR 레파지토리를 생성한다.
생성 후 나타나는 URL이 우리가 Push 해야하는 레파지토리의 주소이다.
로컬 터미널에서 AWS 서비스를 이용할 수 있는 방법 중 하나가 aws-cli이다.
aws-cli를 먼저 설치해주도록 한다.(MacOS 기준)
참고링크
curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
unzip awscli-bundle.zip
sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
아까 빌드한 도커이미지를 이 레파지토리에 푸쉬하기 전에 추가로 설정해주어야 하는 것이 있다.
누구나 내 레파지토리에 푸쉬하면 안되기 때문에 ECR 푸쉬가 가능한 credential을 내 로컬에 저장해야한다.
처음 tistory-docker User를 생성할 때 ECRFullAccess 권한을 주었을 것이다.
따라서 현 유저의 AccessKey와 SecretAccessKey를 저장해야한다.
가장 대표적인 방식으로는,
aws configure
위 명령어를 통해 Credential을 관리할 수 있다.
하지만 모든 사람이 하나의 IAM만 사용하지 않을 것이다.
나같은 경우도 지금 사용하고 있는 컴퓨터로 회사 aws 계정, 내 개인계정, 등등에 접근하고 있는데 그때그때 AccessKey, SecretAccessKey를 변경하고 접근하는 것은 매우 귀찮은 일이다.
로컬 홈경로에서 .aws 로 이동해보자
cd .aws
(만약 해당 디렉터리가 없다면 aws configure 명령어를 입력하고 아무 입력이나 한 후 다시 보면 생겨있을 것이다.)
vim credentials
을 열고 다음과 같이 입력한 후 저장한다.
[tistory-docker]
aws_access_key_id = AccessKey
aws_secret_access_key = SecretAccessKey
이렇게 "tistory-docker" 라는 프로파일의 credential을 저장한 것이다.
해당 정보를 이용해서 ecr 에 로그인하도록 한다.
$(aws ecr get-login --region ap-northeast-2 --no-include--email --profile tistory-docker)
만약 로그인이 된다면 다음으로 넘어가면 된다.
하지만 MFA 설정을 한 유저라면 아마 다음과 같은 메시지가 뜰 것이다.
MFA 설정 유저의 경우 MFA 토큰값을 통해 임시 AccessKey, SecretAccessKey를 발급받고 그 키를 통해 로그인해야 한다.
다음 명령어를 실행한다.
aws sts get-session-token --serial-number arn:aws:iam::자신의AccountId:mfa/tistory-docker --token-code MFA코드번호 --profile tistory-docker
여기서 arn 번호는 루트계정으로 접속한 뒤 IAM 서비스의 tistory-docker user 탭의 MFA device arn이고, token-code는 MFA 번호이다. 출력된 Credential로 다시 .aws/credential을 다음을 추가한다.
[mfa]
aws_access_key_id = AccessKeyId
aws_secret_access_key = SecretAccessKeyId
aws_session_token = SessionToken
이제 다시 로그인해보자.
$(aws ecr get-login --region ap-northeast-2 --no-include--email --profile mfa)
위와같은 메시지가 나온다면 로그인에 성공한 것이다.
이제 다시 로컬의 프로젝트 폴더로 이동해서 ECR 레파지토리에 도커이미지를 푸쉬한다.
docker build -t 자신의AccountId.dkr.ecr.ap-northeast-2.amazonaws.com/tistory-docker .
docker push 자신의AccountId.dkr.ecr.ap-northeast-2.amazonaws.com/tistory-docker:latest
완료되면 ECR 콘솔로 이동해서 확인해보자.
정상적으로 푸쉬되었다.
이제 private Instance에 접속해서 해당 도커 이미지를 실행할 것이다.
4. 애플리케이션 배포
인스턴스에 접근하기 전해 해당 인스턴스에 ECR에 접근할 수 있는 Role을 주어야한다.
로컬에서 했던 것 처럼 configure를 통해 Access Key를 직접 넣을 수도 있지만, 인스턴스가 키를 직접 가지고 있는 것은 보안상 권장하지 않는다.
IAM > Role 에 들어가 새로운 Role을 생성한다.
대상은 EC2를 선택하고, AmazonEC2ContainerRegistryReadOnly 정책을 추가한다.
이제 EC2 에서 private instance를 선택하고 Action > Instance setting > Attach/Replace IAM Role 에 들어가 방금 생성한 Role을 추가하고 적용한다.
완료하였다면,
bastion host를 통해 터널링하고,
sudo ssh -i tistory-docker.pem -L 22:10.0.1.218:22 ec2-user@52.79.224.165
private instance에 접속한다.
ssh -i tistory-docker.pem ec2-user@localhost
그리고 private Instance에서 ECR 로그인 후 이미지를 pull 받는다.
sudo $(aws ecr get-login --region ap-northeast-2 --no-include-email)
sudo docker pull AccountId.dkr.ecr.ap-northeast-2.amazonaws.com/tistory-docker
pull이 완료되었다면 실행한다.
sudo docker run -d -p 80:8888 accountId.dkr.ecr.ap-northeast-2.amazonaws.com/tistory-docker:latest
8888포트는 각자의 애플리케이션 포트에 따라 적절히 변환시켜준다.
80포트로 접속 시 해당 컨테이너의 8888포트로 접속하도록 설정하였으며 -d 옵션은 백그라운드 실행을 뜻한다.
이제 이 Private instance를 80포트로 접속하면 위에서 구현한 웹페이지가 뜰 것이다.
현재 22번 포트만 열려있기 때문에 private instance의 security group에 다음을 추가해준다.
bastion host의 sg를 넣어줌으로써 bastion을 통한 접근만 허용해주었다.
이 상태에서 bastion host에서 다음 명령어를 입력하면,
curl -XGET private-instance-ip/
정상적으로 애플리케이션이 배포된 것을 확인할 수 있다.
4. ALB 생성 후 외부접속
private instance에 웹을 배포했더라도 결국 이 웹에 접근하는 것은 외부이다.
따라서 ALB 를 생성하고 ALB 접근 시 private instace 웹에 접속하도록 설정할 것이다.
그 전에 ALB는 최소 두개의 AZ의 두개의 subnet 을 지정해야하기 때문에 해당 작업을 먼저 수행한다.
VPC > Subnet > Create Subnet
그 후, public Subnet의 routeTable을 다음으로 변경한다.
현재 public subnet이라고 했지만 nat gateway가 연결되어있을 것이다.
igw가 연결되어있는 a존의 public subnet route table로 변경해준다.
이제 ALB를 생성할 수 있는 환경이 구축되었다.
EC2 > Load Balancers > Create Load Balancer > Application Load Balancer를 선택한다.
Name을 설정하고 internet-facing 을 선택한다.
80포트로 접속할 것이기 때문에 Listener 칸은 건드리지 않는다.
그리고 위처럼 두 AZ의 public subnet을 지정한다.
다음은 Security Group이다.
외부에서 모두 접속할 수 있게 해도 되지만, 우선 bastion host의 sg를 지정해준다.(현재 작업하고있는 내 피씨에서만 접속할 수 있도록)
타겟 그룹은 네임만 지정해준다.
Health Checks 경로는 위에서 생성한 Application이 / 경로에 해당하는 접근에 대한 응답을 주고 있기 때문에 따로 건드리지 않아도 된다.
그리고 타겟을 Web Instance로 지정한다.
ALB 생성 완료 후 마지막으로 할 작업이있다.
방금 ALB 의 sg로 설정한 bastion Sg에 80포트로의 접근을 허용하는 것이다.
이제 해당 ALB의 DNS name을 주소창에 입력해보자!
성공!
굉장히 단순하 VPC에 단순한 application을 배포하는 것인데도 작업이 굉장히 많다.
다음에는 애플리케이션을 Jenkins + CodeDeploy를 통해 자동배포하는 것에 대해 포스팅할까한다! 이왕이면 Auto Scaling 기능까지 추가하여 :)
EC2 배포에 대한 포스팅을 마무리하면 ECS > EKS 로 넘어가고 싶은데 ,,
일단 의욕만 앞서는 매일~