[배포4] SpringBoot, Docker, Nginx, githubAction 무중단 배포 :도메인 연결, https 설정

디하·2024년 2월 22일
1

배포

목록 보기
4/8
post-thumbnail

🏷️ 가비아 구입 후 ec2 Route 53 연결

가비아에서 구입한 도메인 주소를 도메인 이름 입력칸에 적어주고 '호스팅 영역 생성' 버튼 클릭

빨간 네모 칸 4개의 라우팅을 가비아에 설정해주기

설정버튼 누르고 위에 Route 53에서 받은값들을 차례대로 4개 적어주면 된다

그리고 다시 Route 53으로 돌아와

레코드 생성을 누르고

레코드 이름은 비워두고!
값에 탄력적ip (연결할 인스턴스) 을 적고 레코드 생성버튼을 눌러주면 모든 라우터 설정은 끝이 난다!


🏷️ docker image certbot 설치

Docker에서 Certbot 이미지를 pull

docker pull certbot/certbot

Docker에서 Certbot을 실행하여 SSL 인증서를 생성

하지만 이렇게 코드를 치게 되면 ㅠㅠㅠ 오류가 발생했다!!

docker run -it --rm --name certbot -p 80:80 -p 443:443 -v "/etc/letsencrypt:/etc/letsencrypt" -v "/var/lib/letsencrypt:/var/lib/letsencrypt" certbot/certbot certonly --standalone -d your_domain -d www.your_domain

your_domain : 가비아에서 산 도메인

그런데 이 명령어를 실행했을 때 오류가 발생했다

😧 오류

Error response from daemon: driver failed programming external connectivity on endpoint certbot (ad071f4a6e476500b157196a59a493c03417a407c242051b08a510130a420c87): Bind for 0.0.0.0:80 failed: port is already allocated.
ERRO[0000] error waiting for container: context canceled

즉 80 포트가 사용중이기 때문에 certbot으로 도메인 연결에 실패했다는 의미였다

지금 현재 nginx 서버가 80 포트에 탄력적ip로 연결되어 열려있는 상태였다 그래서 일단 nginx 80 포트를 중지 시켜준 뒤 cerbot 을 설치해서 ssl 인증을 받아야했다

nginx 서버 중지

docker stop 'nginx 서버이름'

내가 설정해놓은 nginx 서버이름이 nginxserver 였기에 나는
docker stop nginxserver
이렇게 입력해주었다

그리고 다시 Certbot을 실행하여 SSL 인증서를 생성을 시도했다

sudo docker run -it --rm --name certbot -p 80:80 -p 443:443 -v "/etc/letsencrypt:/etc/letsencrypt" -v "/var/lib/letsencrypt:/var/lib/letsencrypt" certbot/certbot certonly --standalone -d your domain

your_domain : 가비아에서 산 도메인

그럼 이메일 입력칸이 나온다
본인의 이메일 입력해주고 enter
그리고 계속 yes 해주기!

그럼 이렇게 성공했다고 나온다


🏷️ nginx 서버에 cerbot ssl 인증서 설정

nginx 서버 재시작

docker start nginx서버이름

sudo docker start nginxserver 으로 다시 서버를 재시작 해주었다
그리고 Nginx 설정 파일을 수정하기 위해 Docker 컨테이너에 접속하였다

Docker 컨테이너에 접속

docker exec -it nginx서버이름 bash

nginx 설정 파일 변경

cd /etc/nginx/conf.d/default.conf

// upstream은 웹 서버로 요청을 전달할 업스트림 서버를 지정

upstream blue {
	server 11.111.11.111:8080; // 인스턴스 탄력적 ip :8080
}
upstream green {
	server 11.111.11.111:8081; // 인스턴스 탄력적 ip :8081
}


// HTTP 프로토콜을 사용하는 80 포트에서 들어오는 요청을 처리하는 서버 블록

server {
	listen       80;
	server_name  sandboxproject.shop; // domain 주소
	return 301 https://$host$request_uri; // sandboxproject.shop 도메인으로 들어오는 요청은 모두 HTTPS로 리다이렉션
}

// HTTPS 프로토콜을 사용하는 443 포트에서 들어오는 요청을 처리하는 서버 블록
// sandboxproject.shop 도메인으로 들어오는 요청은 SSL 인증서를 사용하여 암호화된 연결로 처리

server {
listen       443 ssl;
server_name sandboxproject.shop;

// SSL 인증서 파일의 경로는 ssl_certificate와 ssl_certificate_key로 지정

ssl_certificate /etc/letsencrypt/live/sandboxproject.shop/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sandboxproject.shop/privkey.pem;

#access_log  /var/log/nginx/host.access.log  main;

include /etc/nginx/conf.d/service-env.inc;

location / {

		proxy_pass http://$service_url;

	    proxy_set_header X-Real-Ip $remote_addr;
   		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $http_host;

	root   /usr/share/nginx/html;
    index  index.html index.htm;
}

#error_page  404              /404.html;

# redirect server error pages to the static page /50x.html
#
error_page   500 502 503 504  /50x.html;
location = /50x.html {
    root   /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
#    proxy_pass   http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
#    root           html;
#    fastcgi_pass   127.0.0.1:9000;
#    fastcgi_index  index.php;
#    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
#    include        fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
#    deny  all;
#}
}

😫 오류

Nginx 설정도 끝났으니 바로 github Action 으로 cicd 를 실행시켰더니 오류가 났다
이 오류에 빠져서 몇시간을 허비했는지 모른다ㅠㅠㅠㅠㅠ
이 오류가 난다면 꼭 이 방법을 적용해보길 바란다

err: nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/sandboxproject.shop/fullchain.pem": 
BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/letsencrypt/live/sandboxproject.shop/fullchain.pem, r) error:10000080:BIO routines::no such file)

err: nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/sandboxproject.shop/fullchain.pem":

nginx에 분명 ssl 인증서를 참조할 수 있게 넣어주었는 데도 인증서를 찾지못한느 오류에 빠져버렸다

// SSL 인증서 파일의 경로는 ssl_certificate와 ssl_certificate_key로 지정

ssl_certificate /etc/letsencrypt/live/sandboxproject.shop/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sandboxproject.shop/privkey.pem;

1. ⚡️ 일단 먼저 정말 저 경로에 pem 키가 있는지 확인 해준다

우분투 서버에 가서

sudo ls -l /etc/letsencrypt/live/sandboxproject.shop/

본인 도메인 주소에 해당하는 pem 키가 있는 지 확인한다

total 4
-rw-r--r-- 1 root root 692 Feb 21 11:00 README
lrwxrwxrwx 1 root root 43 Feb 21 11:00 cert.pem -> ../../archive/sandboxproject.shop/cert1.pem
lrwxrwxrwx 1 root root 44 Feb 21 11:00 chain.pem -> ../../archive/sandboxproject.shop/chain1.pem
lrwxrwxrwx 1 root root 48 Feb 21 11:00 fullchain.pem -> ../../archive/sandboxproject.shop/fullchain1.pem
lrwxrwxrwx 1 root root 46 Feb 21 11:00 privkey.pem -> ../../archive/sandboxproject.shop/privkey1.pem

이렇게 출력이 되면 pem 키가 존재한다는 것은 확인 된거다

2. ⚡️ nginx 가 pem키에 접근 권한이 없다 -> 권한을 주자!

pem 키에 대한 권한이 없기에 참조하기 어렵다고 판단을 했다 그래서 두가지 방법을 사용했다

1) docker compose 파일에 디렉토리 마운트

docker-compose.yml 파일에 인증서 파일이 위치한 디렉토리를 컨테이너에 마운트 하면 심볼릭 링크가 가리키는 실제 위치한 파일에 위치한 디렉토리를 마운트하는 방법을 사용하였다

docker-compose-blue.yml

version: '3.8'

services:
 blue:
   image: younssue/live_server:latest
   container_name: blue
   ports:
     - "8080:8080"
   environment:
     - PROFILES=blue
     - ENV=blue
   volumes:
     - /etc/letsencrypt:/etc/letsencrypt
     - /etc/letsencrypt/archive:/etc/letsencrypt/archive

🐫 그런데 문제가 있었다.. 위에 코드 처럼 나는 blue/ green 서버에 대한 docker-compose-{env}.yml 밖에 없었다
본질적인 nginxserver docker-compose.yml 이 없던 것이다

그래서 blue/green 서버에만 docker 마운트 방법만 적용하고
nginxserver는 다른 방법을 써야했다

여기서는 나처럼 blue/green 무중단 배포 하는 방법에서 쓰이는 방법이니
위에 처리까지 해줬는데도 안된다면 이 방법도 고려해보길 바란다

2) nginx 컨테이너에 볼륨마운트를 추가하기

sudo docker stop nginxserver
sudo docker rm nginxserver

sudo docker run -d -p 80:80 -p 443:443 -v /etc/letsencrypt:/etc/letsencrypt -v /etc/letsencrypt/archive:/etc/letsencrypt/archive --name nginxserver nginx

/etc/letsencrypt와 /etc/letsencrypt/archive 디렉토리를 nginxserver 컨테이너에 마운트 한다

그런데 이렇게 되면 기존에 설정해둔 Nginx설정이 다 날라간다 🥲
나는 그걸 모르고 바로 해버려서....다시 nginx 설정을 해주었다
볼륨마운트 추가하기전에 미리 코드를 백업해두자!

nginx 설정 파일 다시 만들어주기

cd /etc/nginx/conf.d/default.conf

// upstream은 웹 서버로 요청을 전달할 업스트림 서버를 지정

upstream blue {
	server 11.111.11.111:8080; // 탄력적 ip:8080
}
upstream green {
	server 11.111.11.111:8081; // 탄력적 ip:8081
}


// HTTP 프로토콜을 사용하는 80 포트에서 들어오는 요청을 처리하는 서버 블록

server {
	listen       80;
	server_name  sandboxproject.shop; // domain 주소
	return 301 https://$host$request_uri; // sandboxproject.shop 도메인으로 들어오는 요청은 모두 HTTPS로 리다이렉션
}

// HTTPS 프로토콜을 사용하는 443 포트에서 들어오는 요청을 처리하는 서버 블록
// sandboxproject.shop 도메인으로 들어오는 요청은 SSL 인증서를 사용하여 암호화된 연결로 처리

server {
listen       443 ssl;
server_name sandboxproject.shop;

// SSL 인증서 파일의 경로는 ssl_certificate와 ssl_certificate_key로 지정

ssl_certificate /etc/letsencrypt/live/sandboxproject.shop/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sandboxproject.shop/privkey.pem;

#access_log  /var/log/nginx/host.access.log  main;

include /etc/nginx/conf.d/service-env.inc;

location / {

		proxy_pass http://$service_url;

	    proxy_set_header X-Real-Ip $remote_addr;
   		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $http_host;

	root   /usr/share/nginx/html;
    index  index.html index.htm;
}

#error_page  404              /404.html;

# redirect server error pages to the static page /50x.html
#
error_page   500 502 503 504  /50x.html;
location = /50x.html {
    root   /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
#    proxy_pass   http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
#    root           html;
#    fastcgi_pass   127.0.0.1:9000;
#    fastcgi_index  index.php;
#    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
#    include        fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
#    deny  all;
#}
}


service-env.inc

set $service_url green;

이렇게 하고 돌려주면 된다 생각했는데 ... 또 오류를 만나버렸다
그것은 blue/green 서버가 꼬여버린것이다 🤮

우분투 서버로 다시 가서 아래 명령어를 실행해준다

sudo docker ps -a

이 명령어를 실행해준다면 현재 blue/ green / nginxserver 이렇게 3개가 올라와있었다 (계속 cicd 테스트를 해주다 오류가 나다보니 하나의 서버를 닫아야하는데 그러지 못하고 둘다 살아있는 상태가 되어버렸다)

명령어 예시

sudo docker stop green // green 서버 종료

sudo docker rm green // green 서버 삭제

sudo docker run -d -p 8081:8081 -e "PROFILES=green" -e "ENV=green" --name green {dockerHub UserName}/{docker imageName}:latest
// green 서버 run

그래서 나는 아예 blue / green 서버를 삭제하고

service-env.inc 도 초기설정이 green으로 바꿔주고

set $service_url green;

githubAction으로 cicd를 작동시켜주었더니 성공한 줄 알았다...

이렇게 작동이 되어서 되는 줄 알았다....
하지만 문제는 green 서버가 죽어있는 게 문제가 아니였다
설정의 문제가 있었다

계속 됐다 안됐다의... 굴레 속에 빠져버렸다...

🚨🚨 다시 재설정을 해보자! 🚨🚨

upstream 설정을 domain 주소로 설정 다시 해주기

cd /etc/nginx/conf.d/default.conf

// upstream은 웹 서버로 요청을 전달할 업스트림 서버를 지정

upstream blue {
	server {domain 주소}:8080; // domain 주소
}
upstream green {
	server {domain 주소}:8081; // domain 주소
}
name: CICD

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install JDK 21
        uses: actions/setup-java@v3
        with:
          java-version: '21'
          distribution: 'temurin'

      - name: Build with Gradle
        run: |
          chmod +x ./gradlew
          ./gradlew clean build -x test
      - name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build Docker
        run: docker build --platform linux/amd64 -t ${{ secrets.DOCKERHUB_USERNAME }}/live_server .
      - name: Push Docker
        run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/live_server:latest
        
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Set target IP
        run: |
          STATUS=$(curl -o /dev/null -w "%{http_code}" "https://${{ secrets.LIVE_SERVER_IP }}/env") // 여기서 https로 설정 , LIVE_SERVER_IP -> domain 으로 변경
          echo $STATUS
          if [ $STATUS = 200 ]; then
            CURRENT_UPSTREAM=$(curl -s "https://${{ secrets.LIVE_SERVER_IP }}/env") // 여기서 https로 설정 , LIVE_SERVER_IP -> domain 으로 변경
          else
            CURRENT_UPSTREAM=green
          fi
          echo CURRENT_UPSTREAM=$CURRENT_UPSTREAM >> $GITHUB_ENV
          if [ $CURRENT_UPSTREAM = blue ]; then
            echo "CURRENT_PORT=8080" >> $GITHUB_ENV
            echo "STOPPED_PORT=8081" >> $GITHUB_ENV
            echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV
          else
            echo "CURRENT_PORT=8081" >> $GITHUB_ENV
            echo "STOPPED_PORT=8080" >> $GITHUB_ENV
            echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV
          fi
          
      - name: Docker compose
        uses: appleboy/ssh-action@master
        with:
          username: ubuntu
          host: ${{ secrets.LIVE_SERVER_IP }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script_stop: true
          script: |
            sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/live_server:latest
            sudo docker-compose -f docker-compose-${{env.TARGET_UPSTREAM}}.yml up -d
      
      - name: Check deploy server URL
        uses: jtalk/url-health-check-action@v3
        with:
          url: https://${{ secrets.LIVE_SERVER_IP }}/env // 여기서 https로 설정 , LIVE_SERVER_IP -> domain 으로 변경, :${{env.STOPPED_PORT}} 삭제 
          max-attempts: 5
          retry-delay: 10s

      - name: Change nginx upstream
        uses: appleboy/ssh-action@master
        with:
          username: ubuntu
          host: ${{ secrets.LIVE_SERVER_IP }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script_stop: true
          script: |
            sudo docker exec -i nginxserver bash -c 'echo "set \$service_url ${{ env.TARGET_UPSTREAM }};" > /etc/nginx/conf.d/service-env.inc && nginx -s reload' 
      - name: Stop current server
        uses: appleboy/ssh-action@master
        with:
          username: ubuntu
          host: ${{ secrets.LIVE_SERVER_IP }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script_stop: true
          script: |
            sudo docker stop ${{env.CURRENT_UPSTREAM}}
            sudo docker rm ${{env.CURRENT_UPSTREAM}}

-> 주석으로 처리된 부분을 바꿔주었다

  • http -> https로 변경
  • https://${{ secrets.LIVE_SERVER_IP }}-> LIVE_SERVER_IP : 도메인 주소로 변경
  • healthCheck 수정 해주기
- name: Check deploy server URL
        uses: jtalk/url-health-check-action@v3
        with:
          url: https://${{ secrets.LIVE_SERVER_IP }}/env // 여기서 https로 설정 , LIVE_SERVER_IP -> domain 으로 변경, :${{env.STOPPED_PORT}} 삭제 
          max-attempts: 5
          retry-delay: 10s
http://${{ secrets.LIVE_SERVER_IP }}:${{env.STOPPED_PORT}}/env -> https://${{ secrets.LIVE_SERVER_IP }}/env 으로 변경! 

이렇게 실행해주면 실행이 잘 되게 되어진다!

그렇다면 왜 안됐을까 추정을 해보자면 https 로 수정을 해준 뒤 githubAction 에서 CICD.yml 에 바꿔야할 부분들이 있었는데 수정해주지 않은 채 배포를 시도하니 http쪽 방화벽이 막힌 상태에서 계속 서버를 바꾸려고 했어서 거기서 생긴 오류 같았다

그래서 CICD.yml 파일 https로 설정하고 탄력적ip도 도메인으로 수정을 해주니 막혔던 문제가 해결이 되었다

profile
🖥️ ⌨️🖱️🩵

0개의 댓글

관련 채용 정보