참새의 이야기
nginx와 spring boot 서버를 docker-compose로 배포 본문
지난 글을 읽다보면, 한 가지 이상한 점이 있다.
docker compose를 EC2 인스턴스에 install 해두고 실제로는 사용을 하지 않았다.
이번에는 nginx를 프록시 서버로 추가하고 이 모든 것을 docker compose를 이용하여 EC2 인스턴스에 구성해보려고 한다.
workflow의 개선
이에 앞서, 지난번에 작성한 workflow도 개선을 해보려고 한다.
- github actions의 environment 기능을 사용하여 배포 환경을 분리하고
- 명령을 직접 입력했던 것들을 Github actions의 Marketplace에 있는 공용 action으로 replace하려고 한다.
- 일부 action의 경우, 현 시점 latest 버전으로 최신화도 해줄 것이다.
아래의 workflow는 최종본이 아니고, 1차 수정본이다.
최종본이 궁금하다면, 여기에서 확인할 수 있다.
name: build and deploy
on:
push:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
# 배포 환경에 따라 다른 secret key를 사용하거나 다른 설정이 필요한 경우에 사용하면 좋다.
environment: master
steps:
- name: Checkout Github actions
# v4가 가장 최신 버전이기 때문에 변경
uses: actions/checkout@v4
- name: Set up JDK 17
# v4가 가장 최신 버전이기 때문에 변경
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Run chmod to make gradlew executable
run: chmod +x ./gradlew
- name: Build with Gradle
# 명령으로 했던 빌드를 공용 action으로 replace
uses : gradle/gradle-build-action@v2
with:
arguments: |
build
--scan
- name: Login to DockerHub
# v3가 가장 최신 버전이기 때문에 변경
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# 명령어를 공용 action으로 replace
- name: Docker에 Build & Push
uses: docker/build-push-action@v5
with:
# context를 안 넣어주면 Dockerfile에서 'COPY ${JAR_FILE} app.jar'를 실행하다가 오류가 난다.
context: .
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/infra_practice_server:latest
# docker-compose 파일을 EC2 인스턴스에 복사하기 위해 추가한 것. deploy 과정의 ssh-action과 다른거다.
- name: copy docker-compose -> EC2 server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.INFRA_PRACTICE_SERVER_IP }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
source: docker-compose.yml
target: /home/ubuntu/
deploy:
runs-on: ubuntu-latest
environment: master
needs: build
steps:
- name: Docker compose
uses: appleboy/ssh-action@v1.0.3
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
with:
host: ${{ secrets.INFRA_PRACTICE_SERVER_IP }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
envs: DOCKERHUB_USERNAME
script: |
echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
sudo docker pull ${{ env.DOCKERHUB_USERNAME }}/infra_practice_server:latest
# port 등 세부 설정을 docker-compose로 옮기고 docker-compose를 up 명령으로 실행한다.
sudo docker compose -f /home/ubuntu/docker-compose.yml up -d
# 사용하지 않는 이미지들은 제거한다.
sudo docker image prune -a -f
Build with Gradle
에서의 argument, Docker에 Build & Push
에서의 context 등을 제대로 입력해주지 않아서 고생했다.
주석으로 일부 설명해뒀으니 참고하면 좋을 것 같다.
docker compose 제대로 활용하기
이제부터는 위에서 추가한 docker compose up
이 사용할 docker-compose.yml 파일을 생성해보자.
nginx와 관련된 내용은 아래에서 추가할 것이므로 여기서는 spring boot 서버만 잘 돌아갈 수 있도록 구성할 것이다.
version: "3.8"
services:
backend:
image: ${DOCKERHUB_USERNAME}/infra_practice_server:latest
container_name: deploy_container
restart: always
ports:
- "8080:8080"
network_mode: host
env_file:
- .env
environment:
- TZ=Asia/Seoul
docker-compose 파일을 처음 사용해보는 것이기 때문에 아래의 네 가지는 어떤 목적인지 알아보자.
1. restart
컨테이너가 종료되거나 실행에 실패할 경우 자동으로 다시 시작할 것인지를 나타낸다.
always로 설정했기 때문에 컨테이너가 정상적으로 실행되지 않으면 계속해서 실행을 시도한다.
docker-compose 파일에 nginx 설정을 할 때 어려움을 겪었는데, log를 까보면 1분 주기로 컨테이너를 실행하려고 재시도하고 있는 것을 확인할 수 있었다.
2. ports
이 컨테이너를 호스트 머신과 연결할 때 어떤 포트로 연결할 것인지를 나타낸다.
사실 스프링 부트는 기본적으로 8080 포트를 사용하기 때문에 다른 포트를 사용할 때에만 ports를 명시하면 된다.
이 파일에서 ports를 명시하는 것과 별개로 Dockerfile에는 EXPOSE로 어떤 포트를 사용하는지 알릴 수 있는데, 이는 문서화의 목적일 뿐이고 실제로 포트를 매핑하는 것은 docker-compose의 ports이다.
Dockerfile의 EXPOSE는 문서화의 목적이므로 없어도 괜찮다.
3. network_mode
도커의 네트워크에 대해서는 따로 공부를 더해줘야 할 것 같다.
우선은 컨테이너가 호스트의 네트워크를 공유하도록 하기 위해 host로 설정했다.
4. env_file
docker-compose.yml 파일에서 사용하는 값들 중 DOCKERHUB_USERNAME처럼 외부에 노출하고 싶지 않은 값들을 미리 인스턴스의 파일에 저장해두고 사용하는 것이다.
nginx를 사용하기 위한 세팅
nginx.conf 작성하기
events {
worker_connections 10;
}
http {
upstream deploy_container {
server localhost:8080;
}
server{
listen 80;
location / {
proxy_pass http://deploy_container/test;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
events
블록이 없으면 nginx: [emerg] no "events" section in configuration
이 뜨면서 무한 restart가 시작된다.
worker_connections는 동시에 처리할 수 있는 클라이언트 연결 수를 의미한다.
infra 구축 연습용이기 때문에 10으로 작게 설정했다.
아래의 http
블록은 웹 서버의 전반적인 설정을 가지고 있다.
upstream
은 여러 포트를 사용하는 경우에 사용하면 좋다.
현재는 8080 포트에서만 서버가 돌아가고 있으므로, upstream
블록이 없어도 되지만 가능한 다양한 설정을 경험해보기 위해서 upstream
블록도 넣어봤다.
upstream
을 사용하지 않는다면, proxy_pass의 deploy_container를 localhost:8080로 대체하면 된다.
server
블록의 listen은 nginx가 80 포트에 대해 열려있음을 의미하고, location /
는 /로 들어오는 요청들을 proxy_pass로 넘긴다는 것을 의미한다.
github actions로 nginx.conf EC2에 복사하기
이제 위의 nginx.conf 파일을 EC2 인스턴스에서 사용할 수 있도록 복사해보자.
workflow에서 copy docker-compose -> EC2 server
의 source에 nginx.conf를 추가하면 된다.
- name: copy docker-compose -> EC2 server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.INFRA_PRACTICE_SERVER_IP }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
# 추가한 부분
source: docker-compose.yml, nginx.conf
target: /home/ubuntu/
docker-compose에 nginx 관련 내용 추가
version: "3.8"
services:
nginx:
image: nginx
container_name: nginxserver
restart: always
ports:
- "80:80"
volumes:
- "./nginx.conf:/etc/nginx/nginx.conf"
depends_on:
- backend
network_mode: host
environment:
- TZ=Asia/Seoul
backend:
image: ${DOCKERHUB_USERNAME}/infra_practice_server:latest
container_name: deploy_container
restart: always
ports:
- "8080:8080"
network_mode: host
env_file:
- .env
environment:
- TZ=Asia/Seoul
docker-compose 파일에 추가한 것들 중, 새롭게 볼만한 것으로는 volumes
가 있다.
./nginx.conf:/etc/nginx/nginx.conf
는 EC2 인스턴스의 ./nginx.conf
를 컨테이너의 /etc/nginx/nginx.conf
에 연결한다는 것이다.
복사의 개념이 아니라 연결이라는 것에 주목하면 좋을 것 같다.
정리
이제 nginx로 웹 서버를 구성하기 위한 모든 작업을 마쳤다.
docker compose up이 docker-compose에 있는 두 컨테이너가 실행되도록 하고, 실제로 잘 돌아가는 것을 docker의 log로 확인했다.
'deploy' 카테고리의 다른 글
배포 자동화 도전기 (github actions+docker+ec2) (4) | 2024.01.06 |
---|