기존 프로젝트를 리팩토링하기 전에 도커, AWS, Github Actions를 이용해 배포 파이프라인을 만들어보려고 한다. 이 글을 그중 첫번째인 EC2 세팅과 스프링 부트 도커라이즈를 다룬다.
순서
- ec2 인스턴스와 고정 Ip 할당, 보안 그룹 생성
- ec2에 깃허브와 도커 설치
- 로컬에서 Spring Boot 프로젝트에 hello world api 추가
- 로컬에서 Spring Boot 프로젝트 도커라이즈하고 깃허브에 푸시
- ec2에서 깃허브 클론해서 도커 이미지 빌드하고 컨테이너 실행
ec2 인스턴스와 보안 그룹 생성
프리티어 계정에서 진행한다.
ec2 대시보드 ui는 자주 변경되어서, 캡쳐 대신 지정해야할 항목만 작성했다.
- 태그 : Name : 서비스이름
- AMI : Amazon Linux2 AMI (HVM)
- 인스턴스 유형 : t2.micro
- 키 페어 : RSA pem키 생성, 추후 로컬에서 가상머신으로 ssh 접속할 때 사용 예정
- 네트워크 보안그룹 추가
- 규칙1 로컬에서 ssh 접속 : 내IP:22
- 규칙2 외부에서 애플리케이션 접속 : 모든IP:8080
- 스토리지 : 30 GiB
탄력적 IP 할당
가상머신 인스턴스가 고정 IP를 가지도록 탄력적 IP를 할당하고 인스턴스를 연결한다.
- 왼쪽 카테고리 > 탄력적 IP > 새 IP 할당
- IP 선택 후 작업 > 인스턴스, 프라이빗 IP 주소 선택 > 연결
ec2 대시보드로 돌아가서 퍼블릭, 탄력적 IP가 잘 연결되었는지 확인한다.
생성한 탄력적 IP는 무조건 EC2에 바로 연결하고, EC2를 삭제할 때 반드시 함께 삭제한다. 안하면 비용 청구됨!!
ec2 접속 (MAC)
다운받은 pem 키 ~/.ssh에 복사하고 권한 부여
cp zerozone-service.pem ~/.ssh
cd ~/.ssh
ls | grep *.pem
chmod 600 ~/.ssh/zerozone-service.pem
~/.ssh에 config 파일 생성하고 권한 설정, ssh 접속
vim ~/.ssh/config
# 작성
HOST 서비스명
HostName 탄력적IP
User ec2-user
IdentityFile ~/.ssh/zerozone-service.pem
# 권한 변경
chmod 700 ~/.ssh/config
# ssh 접속
ssh 서비스명
타임존 KST로 변경
sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime
호스트네임 변경
sudo hostnamectl set-hostname 호스트네임
sudo reboot
ssh 서비스명
/etc/hosts에 호스트네임 추가
sudo vim /etc/hosts
# 작성
127.0.0.1 zerozone
# 호스트네임 등록 확인
curl zerozone
# 정상적으로 등록되었다면 80 에러 발생 (아직 80 포트에서 돌고 있는 서비스가 없으므로)
curl: (7) Failed to connect to zerozone port 80 after 0 ms: Couldn't connect to server
ec2에 도커 설치
AWS Linux2가 제공하는 amazon-linux-extras 패키지를 이용해 도커를 설치한다.
# 설치
sudo amazon-linux-extras install docker
# 설치 확인
docker -v
이렇게 해서 도커 컨테이너가 실행될 환경 세팅을 마쳤다. 서버를 만들 때마다 이 작업을 하는 것이 귀찮다면, 세팅해둔 EC2의 AMI를 생성해 이 AMI로 가상 머신을 시작하는 것을 추천한다. (Golden AMI!)
hello world!
스프링 부트 프로젝트를 생성한다.
- Java 17
- 빌드툴 : Gradle
- 패키징 : Jar
dependency
- Spring Web
- Spring Data Jpa
- Lombok
- H2 Database
Hello World API 추가
package com.example.zerozonerefactoring.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello World!";
}
}
스프링 부트 프로젝트 도커라이즈
도커파일 작성
# 멀티 스테이징, gradle 기반의 build 이미지
FROM gradle:7.6.1-jdk17-alpine as build
ENV APP_HOME=/apps
WORKDIR $APP_HOME
# gradle로 프로젝트 의존성 설치
COPY build.gradle settings.gradle gradlew $APP_HOME/
RUN gradle build --parallel --continue > /dev/null 2>&1 || true
# Gradle 관련 설정 파일들을 컨테이너 내부로 복사
COPY gradle $APP_HOME/gradle
RUN chmod +x gradlew
# jar 생성
RUN ./gradlew build || return 0
# 소스 코드 복사
COPY src $APP_HOME/src
# jar을 제외한 빌드 결과 제거
RUN ./gradlew clean build
FROM openjdk:17-jdk-slim
ENV APP_HOME=/apps
ARG ARTIFACT_NAME=app.jar
ARG JAR_FILE_PATH=build/libs/ZeroZoneRefactoring-1.0.jar
WORKDIR $APP_HOME
COPY --from=build $APP_HOME/$JAR_FILE_PATH $ARTIFACT_NAME
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
로컬에서 이미지 빌드
- m1에서 빌드한다면 --platform linux/x86_64 옵션을 붙여서 실행한다. vm에서 빌드할 때는 옵션 넣으면 안된다.
- gradle:7.6.1-jdk17-alpine 이미지가 없는 문제가 있었는데, 아직 arm64가 지원하지 않는 듯 하다. https://velog.io/@kikiki0611/Mac-M1-docker-build-문제
docker build -t mingadinga/zerozone:1.0 . --platform linux/x86_64
docker run -p 8080:8080 -d mingadinga/zerozone:1.0
도커 컨테이너가 로컬에서 잘 실행된 모습이다.