AWS 서비스 사용과 CI/CD 구현을 처음 해보았는데요.
AWS는 정말 많은 서비스를 제공하는 만큼 시스템이 복잡하고 모르는 용어도 많이 등장하고.. 네트워크 통신 개념 이해와 보안 개념을 알고 있어야 많은 활용을 할 수 있겠더라구요.
CI/CD 구현은 전체적인 빌드와 배포 과정을 이해하고 있어야 설정을 커스텀 할 수 있었습니다.
처음 숙지하는 데 오래걸렸지만, 한 번 구현하고 나면 이렇게 쉬운 게 없습니다. 😎
배포가 처음엔 다들 어렵다더라고요. 저도 그랬습니다.
추후에 다시 활용할 수 있도록 이번에 프로젝트에서 사용한 배포 과정을 기록해보겠습니다.
아직 미숙한 부분도 많아 이상한 부분은 알려주시면 감사하겠습니다!
과정은 크게 4가지로 나눌 수 있습니다.
- Java 애플리케이션
- AWS EC2
- Docker
- GitHub Actions
CI/CD 파이프라인에서 자동적으로 빌드하기 때문에 직접 빌드해 보는 것은 필수 과정은 아닙니다.
하지만 우리는 강력한 빌드 도구를 사용하고 있기에 Java 프로젝트 빌드에 어떤 조건이 필요하며 성공적으로 완수되는지 한 번쯤은 수동적으로 확인해보는 것이 좋을 것 같습니다.
gradle 기준으로 다음 명령어를 사용하여 jar 파일을 생성하면 됩니다.
./gradlew clean build 또는 ./gradlew buildgradlew.bat clean build 또는 gradlew.bat buildclean build는 빌드 디렉토리를 삭제한 후에 새로운 빌드를 시작합니다. 이전 빌드의 잔여물이 남아 있는 경우 이를 제거하기 때문에 더 신뢰성 있는 빌드를 보장합니다.
build는 현재 상태에서 빌드를 시작하며, 이전 빌드의 결과물을 재사용할 수 있습니다. 이로 인해 빌드 시간이 단축될 수 있지만, 이전 빌드의 결과가 남아 있어 문제가 발생할 수 있습니다.

빌드에 성공하면 build/libs/*.jar 파일이 생성됩니다.
jar 파일의 이름은 gradle.build 파일의 version으로 명시할 수 있습니다.
group = 'site.mygumi'
version = '0.0.1-SNAPSHOT'


주의! 기본적으로 테스트 애플리케이션도 검사합니다. 테스트에 필요한 환경변수가 주입되고 있지 않다면 위처럼 빌드 오류가 발생할 수 있습니다.
스크린샷의 경우, GoodbiteApplication에만 환경변수 설정이 되어있고, GoodbiteApplicationTests의 환경변수는 없어서 빌드 오류가 발생합니다.
GoodbiteApplicationTests의 환경변수에 필요한 값을 설정하면 됩니다.
또는, ./gradlew clean build -x test 명령어를 사용하면 테스트를 검사하지 않고 빌드할 수 있습니다.
제 컴퓨터로 애플리케이션을 상시로 돌리기는 부담스러우니 AWS의 EC2 가상화 클라우드 서비스를 선택했습니다.
AWS 서비스를 처음 사용하니 간단하게 가입하고 콘솔 홈으로 이동합니다.

처음이라면 스크린샷 ①번이 서울인지 꼭! 확인합니다.
AWS 서비스는 모두 리전에 따라 대시보드 내용이 달라집니다. 서울에 인스턴스 하나, 미국에 인스턴스 하나 생성해보시면 알게 됩니다.
리전을 서울로 바꾸는 것이 속도 면에서도 안정적이고 편리한 면이 있습니다. 저는 이것을 확인하지 않고 삽질한 경험이 있죠 🔨 망치질
②번에서 EC2 대시보드 페이지로 이동합니다.

인스턴스 시작 버튼을 누릅니다. (인스턴스 생성)

인스턴스 이름을 설정하고,
가장 중요한 것은 인스턴스의 OS입니다. 내 애플리케이션이 어떤 환경이 필요한지 고려하고 선택하면 좋을 것 같습니다.
이 프로젝트는 Windows에 의존적이지 않기 때문에 굳이 선택할 이유가 없어 Ubuntu를 선택하였습니다.
이유:
리눅스 인스턴스는 일반적으로 윈도우즈 인스턴스보다 비용이 저렴하며, 가벼운 OS로 인해 성능이 더 나은 경우가 많습니다. 그리고 일반적으로 보안과 안정성에서 강점을 보입니다. 특히, 서버 자원을 효율적으로 사용하는 백엔드 애플리케이션이라면 리눅스가 더 나을 수 있습니다.

프리 티어로 부담없이 쓸 수 있는 t2.micro를 선택하였습니다. 메모리가 1GiB인 것에 주의하여야합니다. 개복치가 따로없습니다. 이것을 보완하기 위한 방법은 다음 링크를 참고해주세요.
[EC2 메모리 부족 현상 보완 페이지]
키 페어 없이 진행할 수 있지만 호스트 주소만 알아내면 쉽게 접속이 가능하기 때문에 보안상 좋지 않습니다. 키 페어가 아직 없다면 새로 생성합니다.

저는 RSA 암호화 방식과 .pem 키 파일 타입을 선택하였습니다.
키 페어 생성 버튼을 누르면, .pem 파일을 로컬 머신에 저장할 수 있습니다.
.pem 파일: Privacy Enhanced Mail 파일 형식의 약자로, 주로 보안 인증서, 개인 키, 공개 키, 그리고 인증서 체인을 저장하는 데 사용됩니다. 데이터를 인코딩하여 일반 텍스트 형식으로 저장합니다.

저는 프로젝트 폴더 최상단에 저장해주었습니다. 나중에 SSH로 연결할 때 편합니다.

.gitignore 파일을 설정하여 키 파일들은 GitHub 원격저장소에는 노출되지 않도록 하는 것이 좋습니다.

마지막으로 네트워크 설정입니다. 보안 그룹을 생성해도 되고, 기존 보안 그룹을 선택해도 되는데 처음이라면 보안 그룹을 생성해줍니다.
프로젝트가 HTTP와 HTTPS 통신 중 사용하는 것을 포함시키고,
SSH는 포함하는 것이 개발에 편리합니다. 나 혼자 이 서버를 관리한다면 내 IP만 허용하도록 해도 좋습니다. 저는 팀원을 포함하여 여러 머신에서 접속할 수 있도록 위치 무관을 선택하였습니다.
이제 인스턴스를 시작하면 됩니다.

인스턴스 상태가 실행 중으로 뜬다면 준비는 되었습니다.

인스턴스를 클릭하고 보안탭을 보면 설정한 인바운드 규칙을 볼 수 있습니다.
이 규칙을 추가하거나 수정하려면 보안 그룹 이름을 누르거나 아래처럼
네트워크 및 보안 > 보안 그룹에 가면 확인할 수 있습니다.

서버 배포와 관련하여 보안 설정에 따라 규칙을 수정하는 것이 필요합니다. 예를 들어, https를 설정하지 않아 443 포트를 사용하지 않는다면 인바운드 규칙에서 제거해야 합니다.
source(번역명이 원본 혹은 소스로 혼용)를 모두 허용(0.0.0.0/0)하는 것보다 최소의 바운더리만 허용하는 것이 보안상 좋을 것 같습니다.
그렇지만 지금은 백엔드 서버에 누구나 접속해볼 수 있도록 의도했기 때문에 위와 같이 설정하였습니다.
도커 이미지 생성 + 컨테이너 실행 을 자동으로 실행해주도록 배포하고 싶다면 Docker 원격 저장소인 Docker Hub를 사용하는 것이 편리합니다.
먼저 Docker Hub 계정을 생성합니다.

계정을 생성하고 로그인하면 우측 상단 Account settings를 클릭합니다.

도커 허브 레포지토리에 접근하려면 계정 이름과 패스워드가 필요합니다.
그런데 내 패스워드를 사용하면 팀원들이 접근을 해야한다면 내 패스워드 노출이 필요하게 됩니다.
이런 점을 보완하기 위해 Personal Access Tokens(PATs)를 사용할 수 있습니다.
Personal access tokens 페이지로 이동합니다.

Generate new token 버튼 클릭

모든 권한을 부여하도록 하겠습니다.

1번은 docker hub에 로그인하는 명령어지만 우리는 자동으로 처리할 것입니다. 사용자 이름만 기억하면 됩니다!
2번은 도커 액세스 토큰을 얻기 위한 패스워드입니다.
이 정보는 뒤에 GitHub Actions 섹션에서 활용하겠습니다.
주의! 2번은 재차 확인할 수 없습니다. 다시 찾을 수 없다면 access token을 다시 생성해야 합니다..

프로젝트 최상단에 Dockerfile 을 작성합니다.

FROM openjdk:21-jdk-slim
LABEL authors="white"
COPY build/libs/goodbite-0.0.1-SNAPSHOT.jar /app/goodbite.jar
EXPOSE 443
ENV ..
ENTRYPOINT ["java", "-jar", "/app/goodbite.jar"]