안녕하세용~ 오늘은 CI/CD를 경험하며 며칠 동안 고생했던 Blue/Green
배포에서 502 Bad Gateway
문제를 해결한 사례를 가져 왔습니다.ㅜㅜ 진짜 하나하나 뜯어고쳐야 했고 진짜 문제를 찾기 위해 뒤져본 곳이 방대해서 나중에 이런 문제를 마주했을 때 헤매지 않기 위해 글을 쓰도록 하겠습니다. 미리 감사합니다.
문제 원인 분석
Docker로 배포된 Green port 접근 불가 (Bad Gateway)
저는 blue를 8081
포트 green을 8082
포트로 사용했습니다. 하지만 8081 포트로 배포를 진행했을때는 문제가 없었지만 8082 포트로 진행했을 때 BadGateway
문제가 발생하며 접근이 불가능했습니다. SpringBoot코드에까지 혹시 오타가 있는것은 아닌지 꼼꼼히 확인해봤습니다.
AWS 구조 문제
"도메인은 잘 작동하는데 Nginx 서버의 url은 작동하지않네??" 라는 의문을 품고 관련된 문제를 해결하기 위해 직접적인 조치를 취했습니다. AWS 구조와 연관된 문제였는데, AWS 인프라 구성에 부족한 점이 있다는것을 어렴풋 인지하고 있었습니다. 이 문제를 해결하기 위해 AWS 로드 밸런서, 보안 규칙, 타겟 그룹 등을 점검하였고, 특히 8082 포트에 대한 접근 권한을 명확히 허용하는 조치를 취하였습니다. 그럼에도 불구하고 문제는 계속 발생했습니다.. 사실 이정도면 해결될 줄 알았습니다..
NginX server 404, 500, 502에러
EC2 내부에선 Docker를 사용하여 배포된 Nginx 인스턴스에 대한 설정을 여러 차례 조정하며, 탄력적 IP를 통해 Nginx 서버에 접속하여 테스트를 진행했습니다. 이 과정에서, 404(Not Found), 500(Internal Server Error), 502(Bad Gateway) 등 다양한 HTTP 응답 코드를 포함한 여러 오류가 발생했습니다. 이는 일반적으로 (404) NginX 내부 서버 설정문제 , (500,502) 네트워크 연결 문제, 혹은 백엔드 서버와의 통신 장애를 말하기에, NginX가 제 기능을 하지 않는 것 같다고 판단한 부분이었습니다.
1. Docker 접근 // log 파헤쳐 보기
Dockerfile
에서 env 설정을 잘못해서 green으로 변수가 안 바뀌는 건지 알아보기 위해 먼저 log를 출력하려 시도했습니다. log 관리를 하나도 안 해줬어서 디테일하게 설정해줬습니다.
# 변경전 코드
ENTRYPOINT ["java","-Dspring.profiles/active=${PROFILES}","-Dserver.env=${ENV}", "-jar" ,"app.jar"]
# 변경된 코드
ENTRYPOINT ["sh", "-c", "java -Dspring.profiles.active=${PROFILES} -Dserver.env=${ENV} -jar app.jar > log.out 2>&1"]
✅ 이후에 배포된 스프링 도커이미지 내부에 접근해 blue or green은 잘 출력하는 것을 알 수 있었습니다.
2024-03-05T18:58:18.048Z INFO 7 --- [ main] c.b.BookEverywhereApplication : Starting BookEverywhereApplication v0.0.1-SNAPSHOT using Java 17.0.2 with PID 7 (/app.jar started by root in /)
2024-03-05T18:58:18.056Z INFO 7 --- [ main] c.b.BookEverywhereApplication : The following 3 profiles are active: "blue", "common", "secret"
✅ 이후에 NginX에도 로그를 출력하려 시도했습니다.
하지만 진짜 도무지 로그가 출력되지 않아서 여러 설정을 만져보고 레퍼런스를 찾아봤습니다.
root@{NginX container ID}:/var/log/nginx# ls -rtl
total 0
lrwxrwxrwx 1 root root 11 Feb 14 19:53 error.log -> /dev/stderr
lrwxrwxrwx 1 root root 11 Feb 14 19:53 access.log -> /dev/stdout
✅ 여기서 심볼릭 링크가 선언되어 있었고 다음과 같은 방법으로 해제할 수 있습니다
root@{NginX container ID}:/var/log/nginx# rm -f /var/log/nginx/*
✅ 이후 nginx를 다시 로드해줬고 log를 확인할 수 있었습니다.
root@{NginX container ID}:/var/log/nginx# ls -rtl
total 0
root@{NginX container ID}:/var/log/nginx# cd ..
root@{NginX container ID}:/var/log/nginx# nginx -s reload
root@{NginX container ID}:/var/log/nginx# ls -rtl
✅ 후에 알게 된 것으론 간단하게 아래와 같은 코드로 확인할 수 있었습니다.😅
docker logs nginxserver
✅ 이쯤에서 문제가 해결되지 않자 AWS문제인가 싶어 AWS홈페이지를 파고들어봅니다!
2. AWS 구조 조정
먼저 가장 큰 문제가 ALB의 TARGET GROUP
설정이었습니다 지금까지 8081 포트의 BLUE를 사용할 수 있었던 것은 TARGET GROUP 설정을 통해 8081 포트로 직접 접근
하였기 때문에 가능했습니다. 즉 NginX는 EC2에서 실행만 되고 있었지 실질적 사용은 되지 않고 있었습니다. 그렇기에 80 포트로 전환해주고 제대로 NginX를 거치도록 수정해 주었습니다.
❗ ❗ 여기서 ALB가 있는데 NginX를 거치는 이유는 NginX내부에서 프라이빗 IP를 통해 포트 스위칭
작업이 일어나기 때문입니다. 아래의 절차와 같이 타겟그룹을 삭제하고 바꿔주니 깔끔하게 바뀌었습니다 ❗ ❗
✅ TARGET GROUP의 포트도 변경해주기 위해 target-bookeverywhere-v2
를 만들어 새롭게 만들어 주었습니다.
✅ 이에 따라 TARGET GROUP은 ALB에 맵핑되어있기 때문에 ALB도 수정해 줍니다.
✅ 이에따라 깔끔하게 변경된 리소스 맵입니다. 🤗
3. NginX 설정 최적화
AWS의 구조 조정을 마쳤습니다. 하지만 ALB 가 443 포트를 통해 외부요청을 80 포트로 연결해 줬는데도 불구하고 404 에러가 발생한다는 것은 NginX자체의 문제인 것 같다는 생각에 다시 NginX로 돌아왔습니다,,
변경 전 코드
root@{NginX container ID}:/etc/nginx/conf.d# cat default.conf
upstream blue{
server {PRIVATE IP}:8081;
}
upstream green{
server {PRIVATE IP}:8082;
}
server {
listen 80;
listen [::]:80;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
server_name api.bookeverywhere.site;
include /etc/nginx/conf.d/service-env.inc;
access_log on;
error_log on;
access_log /var/log/nginx/access.log proxy_log;
location /api {
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@{NginX container ID}:/etc/nginx/conf.d# cat service-env.inc
set $service_url blue;
GitHub Actions
에서 workflow
를 동작시킬 때마다 service-env.inc의 url이 blue에서 green으로 잘 바뀌길래 정말 못 찾겠어서 좀 오래 헤맸던 것 같습니다.
변경 후 코드
server {
listen 80;
listen [::]:80;
server_name api.bookeverywhere.site;
include /etc/nginx/conf.d/service-env.inc;
access_log on;
error_log on;
access_log /var/log/nginx/access.log proxy_log;
location / {
proxy_pass http://$service_url;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
- 💡 Location Block: location /을 사용하여 모든 경로에 대한 요청을 처리하도록 변경하였습니다. 404 에러가 떴던것이 api였고 탄력적IP/api를 했더니 500에러가 떠서 아 이거구나 싶었습니다.
- 💡 Proxy Header 설정변경: proxy_http_version, Upgrade, Connection, Host 헤더를 설정을 바꿨습니다. NginX를 처음 써보면서 그냥 docker에서 다운로드한 대로 바로 사용했는데 , 그럴 것이 아니라 사람들이 많이 사용하는 version으로 체인지해서 사용했습니다.
✅ 이렇게 설정하고 로드밸런서에 접근하니 통신이 성공했습니다.
하하..😅😅😅
이후 GitHub Actions를 통해서 재배포하니 NginX를 정확히 거쳐서 포트 스위칭이 잘 일어나는 과정을 확인했고 docker를 통한 배포 프로세스도 정상동작하는 것을 확인했습니다.
감사합니다!!!!😃
+++추가
내가 만든 CI/CD는 정말 안 멈추는 것인가? 를 알기 위해 재배포하는 동안 계속 요청을 날렸더니 10초 정도 서버가 멈췄습니다.
##문제의 코드
- name: Check deploy server URL
uses: jtalk/url-health-check-action@v3
with:
url: https://${{ secrets.BOOKEVERYWHERE_URL }}/env
max-attempts: 5
retry-delay: 10s
Check deploy server URL동작에서 만약 blue -> green으로 간다면 8082 port의 health-check를 해야 합니다. 하지만 https기에 외부에서 어떤 포트를 사용할지 알 수 없습니다. 그렇기에 생각한 방법은 ❗ ❗
blue/green 배포의 방식에서 blue와 green의 port를 둘 다 띄워 놓는다.
Deploy코드를 아래와 같이 변경했습니다.
❗ ❗ 주요 변경사항 stop current server를 위로 올리고 변수를 TARGET\_UPSTREAM
으로 변경하여 재배포할 서버를 껐다가 키는 방식으로 변경하였습니다. 이로써 서버는 blue와 green 두 개가 가동되게 만들었습니다. ❗ ❗
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Set target IP
run: |
STATUS=$(curl -o /dev/null -w "%{http_code}" "https://${{ secrets.BOOKEVERYWHERE_URL }}/env")
echo $STATUS
if [ "$STATUS" = "200" ]; then
CURRENT_UPSTREAM=$(curl -s "https://${{ secrets.BOOKEVERYWHERE_URL }}/env" | jq -r '.data')
else
CURRENT_UPSTREAM=blue
fi
echo CURRENT_UPSTREAM=$CURRENT_UPSTREAM >> $GITHUB_ENV
if [ $CURRENT_UPSTREAM = blue ]; then
echo "CURRENT_PORT=8081" >> $GITHUB_ENV
echo "STOPPED_PORT=8082" >> $GITHUB_ENV
echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV
elif [ $CURRENT_UPSTREAM = green ]; then
echo "CURRENT_PORT=8082" >> $GITHUB_ENV
echo "STOPPED_PORT=8081" >> $GITHUB_ENV
echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV
else
echo "error"
exit 1
fi
- name: Stop current server
uses: appleboy/ssh-action@master
with:
username: ubuntu
host: ${{ secrets.BOOKEVERYWHERE_IP }}
key: ${{ secrets.EC2_SSH_KEY }}
script_stop: true
script: |
sudo docker stop ${{env.TARGET_UPSTREAM}}
sudo docker rm ${{env.TARGET_UPSTREAM}}
- name: Docker compose
uses: appleboy/ssh-action@master
with:
username: ubuntu
host: ${{ secrets.BOOKEVERYWHERE_IP }}
key: ${{ secrets.EC2_SSH_KEY }}
script_stop: true
script: |
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/bookeverywhere: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.BOOKEVERYWHERE_URL }}/env #:${{env.STOPPE}}
max-attempts: 5
retry-delay: 10s
- name: Change nginx upstream
uses: appleboy/ssh-action@master
with:
username: ubuntu
host: ${{ secrets.BOOKEVERYWHERE_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'
하지만 문제가 EC2 서버가 FreeTier
의 T2.micro
여서 그런지 두 개를 동시에 가동하는 순간 서버가 죽어버려서 EC2 인스턴스를 계속 껐다가 다시 부팅해야 했습니다,, EC2 스펙문제인 것 같아서 나중에 더 좋은 서버가 있다면 시도해보고 싶네요
비록 10초 정도 서버가 멈추긴 하지만 CI/CD 구현을 어떻게 해야 할지 명확히 알게 되어 좋은 과정이었다고 생각합니다..
긴 글 읽어주셔서 감사합니다.😊
'DevOps > AWS' 카테고리의 다른 글
[AWS] AWS Network의 이해 (이론 모음) (0) | 2024.03.11 |
---|---|
[AWS] Route53을 통한 도메인 연결 및 HTTPS 설정하기 (2) | 2024.03.07 |
[AWS] EC2에서 내 컴퓨터로 파일 가져오기 (0) | 2023.11.15 |
[AWS] 기본적인 배포 스크립트 (1) | 2023.11.14 |