앱 및 웹 서버들을 배포하면서 기록해놓는 것이 좋겠다고 생각하여 글을 쓰게 되었습니다.
배포 과정은 아래 그림과 같습니다.

웹도 하나의 EC2로 배포하기 위해 위와 같이 nginx를 리버스 프록시서버로 활용하여 요청 url에 따라 포트번호를 나눠서 연결이 되도록하였습니다.
이때 다음과 같이 3개의 도커 컨테이너를 띄웠습니다.
웹서버를 동시에 띄우지 않을 분들이라면 1,3번만 컨테이너 띄우시면 될 것 같습니다.
서버를 돌리기 위한 AWS EC2를 생성합니다. 프리티어는 크게 따로 설정할 것이 없고 아래 4가지 정도만 설정하시면 될 것 같습니다.
프리티어 가능한 os 설정

프리티어 가능한 인스턴스 유형 설정

키페어 생성 및 보관



EC2 퍼블릭IP 주소는 EC2 -> 인스턴스 -> 인스턴스ID 클릭 으로 알 수 있습니다.
ssh -i ~/.ssh/{본인 키페어 이름}.pem ubuntu@{ec2 퍼블릭IP 주소}
저는 가비아에서 도메인을 구매하였습니다. 참고로 shop이 1년에 500원으로 가장 저렴합니다. 그리고 도메인 연결참고 링크 이 블로그를 참고하여 ec2와 도메인을 연결했습니다.
ssh -i ~/.ssh/{본인 키페어 이름}.pem ubuntu@{도메인 주소}
이제 위와 같이 ec2에 원격접속이 가능해집니다.
EC2 내부에서 certbot을 이용해 SSL 인증서를 발급받았습니다.
apt-get install python3-certbot-nginx
certbot certonly --nginx -d <도메인 주소>
이제 https 프로토콜 사용이 가능합니다.









우선 EC2에 원격 접속하여 링크된 블로그를 참고하여 도커를 설치합니다.
version: '1'
services:
backend:
container_name: springboot-app
image: {도커 사용자 아이디}/{백엔드 이미지 이름}
expose:
- 8080
volumes:
- ./config:/config
frontend:
container_name: react-app
image: {도커 사용자 아이디}/{프론트앤드 이미지 이름}
expose:
- 3000
nginx:
container_name: nginx
image: nginx:latest
restart: always
volumes:
- ./conf/:/etc/nginx/conf.d
- /var/log/nginx:/var/log/nginx
- /etc/letsencrypt:/etc/letsencrypt
ports:
- 80:80
- 443:443
depends_on:
- backend
- frontend
'springboot-app'이라는 이름으로 컨테이너를 띄웁니다. 이 서비스는 docker hub에서 backend-image라는 이미지를 가져와서 실행 시키고 컨테이너를 8080포트로 노출합니다.
그리고 EC2 내부의 config폴더와 컨테이너 내부의 config폴더를 마운트하여 컨테이너 내부에서 config 폴더를 사용할 수 있도록 해줬습니다. 위에서 작성한 도커 파일 내용 중 application.yml 파일을 외부에서 주입하여 사용하기 위해 적용했던 옵션을 위한 설정입니다.
EC2 내부의 config 폴더 안에는 application.yml 파일이 작성되어 있어야 합니다.
sudo mkdir config
cd config
sudo vi application.yml
위와 같이 Spring 설정파일을 작성해줍니다.
이번에는 nginx 설정파일을 작성해야 합니다. ~ 경로에서 다음과 같이 명령어를 입력한 후 내용을 작성합니다.
sudo mkdir conf
cd conf
sudo vi default.conf
server {
listen 80;
server_name {서버도메인 이름};
underscores_in_headers on;
return 301 https://{서버도메인 이름}$request_uri; # http로 들어오면 https로 redirect 해주는 부분
}
server {
listen 443 ssl;
server_name {서버도메인 이름};
ssl_certificate /etc/letsencrypt/live/{서버도메인 이름}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{서버도메인 이름}/privkey.pem;
location /api {
proxy_pass http://springboot-app:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://react-app:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
underscores_in_headers on;
}
스프링 프로젝트 최상위 폴더에 Dockerfile이름의 파일을 만들고 다음과 같이 작성하여 줍니다.
FROM openjdk:17
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar", "--spring.config.location=/config/application.yml"]

name: Dev CI/CD
on:
push:
branches:
- main
jobs:
build:
# 실행 환경 지정
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '17'
- name: Make Application.yml
run: |
cd ./src/main/resources
touch ./application-dev.yml
echo "${{ secrets.APPLICATION_DEV}}" > ./application.yml
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# 스프링부트 코드 Build
- name: Build with Gradle
run: ./gradlew clean build
# 도커 로그인하고 도커 이미지 빌드 후 푸시한다.
- name: web docker build and push
run: |
docker login -u ${{ secrets.DOCKER_DEV_USERNAME }} -p ${{ secrets.DOCKER_DEV_PASSWORD }}
docker build --platform linux/amd64 -t ${{ secrets.DOCKER_DEV_USERNAME }}/{이미지이름} .
docker push ${{ secrets.DOCKER_DEV_USERNAME }}/{이미지이름}
# ssh로 EC2 접속해서 도커 컨테이너를 모두 멈춘 후, 도커 파일을 새로 받아 다시 docker-compose로 이미지를 실행시킨다.
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.DEV_HOST_ID }}
username: ubuntu
key: ${{ secrets.DEV_PRIVATE_KEY }}
script: |
sudo docker rm -f $(sudo docker ps -qa)
sudo docker pull ${{ secrets.DOCKER_DEV_USERNAME }}/{이미지 이름}
sudo docker-compose up -d
sudo docker image prune -f
깃허브 시크릿을 활용하여 도커 유저아이디,비밀번호, pem키 등을 설정해줍니다. 그리고 위와 같은 경로에 yml파일을 작성하고 푸쉬합니다. 이제 main 브랜치에 푸쉬 될 때마다 자동으로 배포과정이 진행되게 됩니다.