Dangil project(12)

Junyoung·2024년 5월 23일

Dangil Project

목록 보기
12/20

이제 앞에 학습한 내용을 직접 적용해보고자 합니다.

cd ../exec
# Working container check
# docker compose -p deploy-blue -f docker-compose.blue.yaml ps 컴포즈 작동 확인
EXIST_BLUE=$(docker compose -p deploy-blue -f docker-compose.blue.yaml ps | grep Up)

# -z zero confirm blue 컴포즈
if [ -z "$EXIST_BLUE" ]; then
    # blue
    docker compose -p deploy-blue -f docker-compose.blue.yaml up -d
    BEFORE_COLOR="green"
    AFTER_COLOR="blue"
else
    # green
    docker compose -p deploy-green -f docker-compose.green.yaml up -d
    BEFORE_COLOR="blue"
    AFTER_COLOR="green"
fi

# Spring Server health checking
for retry_count in {1..60}
do
    # spring_blue or spring_green 컨테이너 이름을 적어주고 싶다 !
    # (curl) http 요청을 보내본다.
    response=$(curl -s http://spring_${AFTER_COLOR}:8080/api/health)
    up_count=$(echo $response | grep 'UP' | wc -l)

    if [ $up_count -ge 1 ]
    then
        echo "=========================="
        echo "> Spring Server is working"
        echo "=========================="
        break
    else
        echo "> Spring Health is not working: ${response}"
    fi

    # about 10 minuetes
    # (60번의 요청 이후에도 서버가 정상적으로 올라오지 않는다면) next 서버를 다운 시키고 엔진엑스의 포트 포워딩을 진행하지 않는다 !
    if [ $retry_count -eq 60 ]
    then
        echo "> Spring Server working failed"
        docker compose -p deploy-${AFTER_COLOR} -f docker-compose.${AFTER_COLOR}.yaml down
        exit 1;
    fi
    # wating 10 seconds(한번 요청 보내고 10초 기다린다 ! )
    sleep 10
done

# 엔진엑스의 포트포워딩
# 위에서 정상적으로 health 체크가 됬다면 down 되지 않는다 !
EXIST_AFTER=$(docker compose -p deploy-${AFTER_COLOR} -f docker-compose.${AFTER_COLOR}.yaml ps | grep Up)

if [ -n "$EXIST_AFTER" ]; then
    echo "nginx Setting"
    docker exec -i nginx /bin/bash -c "echo -e 'set \$spring_color ${AFTER_COLOR};' > /etc/nginx/conf.d/service-color.inc && nginx -s reload"
    set $spring_color = AFTER_COLOR

    echo "Completed Deploy!"
    echo "$BEFORE_COLOR server down(spring_${BEFORE_COLOR})"

    # 이전 컨테이너 (compoes) 를 다운시킨다
    docker compose -p deploy-${BEFORE_COLOR} -f docker-compose.${BEFORE_COLOR}.yaml down
fi
server {
    listen 80;
    listen [::]:80;
    server_name dangil.store;
    access_log off;

    location /.well-known/acme-challenge/ {
        allow all;
        root /var/www/certbot;
    }

    location / {
        return 301 https://dangil.store$request_uri;
    }
}

map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

server {
    listen 443 ssl;
    server_name dangil.store;
    # include /etc/nginx/conf.d/service-url.inc;

    # 서트봇이 볼륨으로 남겨두는 인증서를 가지고 이미지를 만들었고 엔진엑스 컨테이너에서 해당 인증서를 사용한다
    ssl_certificate /etc/letsencrypt/live/dangil.store/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dangil.store/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # 부적절한 헤더도 요청을 허용한다.
    ignore_invalid_headers off;

    location /api {
        proxy_pass http://today_back:8080;
        proxy_set_header X-Forwarded-Host $server_name;

        # 디폴트로 IP는 프록시로 변경되고 난뒤에 (리버스 프록시 기능)
        # 원래의 요청의 헤더에 설정을 수정 및 추가한다
        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;
    }
}
set $spring_color blue

포트 public 으로 여는것을 막고자

expose로 처리하고 싶다 !

따라서 컨테이너 이름으로 포워딩을 만들기 위해 spring_blue, spring_green 으로 각 컨테이너를 띄운다.

이후 블루 그린을 스왑하는 과정에서 deploy.sh 에서 default.conf를 수정해서 다시 reload 시켜준다 !

엔진엑스는 환경변수를 로드해서 가져가는 것이 아닌 conf 디렉터리 안의 변수를 변경해줘야한다 !

하지만 스왑이 안될 뿐더러

spring_blue 컨테이너가 있음에도 불구하고

2024/04/30 12:15:10 [error] 10#10: *162 no resolver defined to resolve spring_blue, client: 182.224.88.51, server: dangil.store, request: "GET /api/health HTTP/1.1", host: "dangil.store"
182.224.88.51 - - [30/Apr/2024:12:15:10 +0000] "GET /api/health HTTP/1.1" 502 559 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" "-"
2024/04/30 12:15:10 [error] 10#10: *162 open() "/etc/nginx/html/favicon.ico" failed (2: No such file or directory), client: 182.224.88.51, server: dangil.store, request: "GET /favicon.ico HTTP/1.1", host: "dangil.store", referrer: "https://dangil.store/api/health"
182.224.88.51 - - [30/Apr/2024:12:15:10 +0000] "GET /favicon.ico HTTP/1.1" 404 555 "https://dangil.store/api/health" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" "-"

에러가 발생한다 !

해당 에러는

1. no resolver defined to resolve spring_blue:
이 에러 메시지는 NGINX 서버가 "spring_blue"라는 호스트 이름을 IP 주소로 변환할 DNS 리졸버가 정의되어 있지 않다는 것을 나타냅니다. 서버 설정에서 resolver 지시문이 제대로 설정되지 않았거나 누락되었을 수 있습니다. 이 문제를 해결하려면 NGINX 설정 파일에서 resolver 지시문을 추가하거나 수정하여 유효한 DNS 서버 주소를 제공해야 합니다.
2. open() "/etc/nginx/html/favicon.ico" failed (2: No such file or directory):
이 에러는 NGINX가 요청받은 favicon.ico 파일을 찾을 수 없다는 것을 의미합니다. 클라이언트가 웹사이트 아이콘을 요청했지만 해당 파일이 서버에 존재하지 않거나 경로가 잘못되었습니다. 이 문제를 해결하려면 올바른 위치에 favicon.ico 파일을 배치하거나, NGINX 설정에서 해당 요청을 올바른 경로로 리다이렉트하는 설정을 추가해야 합니다.
두 에러 모두 NGINX의 설정 문제로 보이며, 이를 해결하기 위해 NGINX의 설정 파일을 점검하고 필요한 변경을 적용해야 합니다.

즉 spring_blue가 DNS에 정의되어 있지 않다는 것이다 !

그렇다면 내부 네트워크의 DNS는 어떻게 확인하고 요청을 이어갈수 있을까?

Docker DNS Server: Docker는 자체 DNS 서버(127.0.0.11)를 사용하여 컨테이너 간 이름 해석을 관리합니다. 컨테이너가 Docker 네트워크에 연결되면, 이 DNS 서버가 해당 컨테이너의 이름과 IP 주소를 자동으로 매핑합니다. 이를 통해 컨테이너는 서로의 이름을 사용하여 직접 통신할 수 있습니다.
docker network inspect today-net

네트워크가 잘 구축 되어있고 컨테이너 이름이 곧 IP로 매핑이 DNS에 되는것이다 !

하지만 127.0.0.11 에 docker DNS가 존재하고 엔진엑스 입장에서는 해당 DNS를 모를수도 있다 !

Docker Network Nginx Resolver

resolver 127.0.0.1 valid=30s
resolver 127.0.0.1 valid=30s는 NGINX 서버의 설정 내에서 사용되는 구문입니다. 이 구문은 NGINX가 DNS 조회를 수행할 때 사용할 DNS 리졸버의 IP 주소와 DNS 캐시의 유효 기간을 설정합니다. 자세한 내용은 다음과 같습니다:

resolver
resolver 지시어는 NGINX가 도메인 이름을 IP 주소로 해석할 때 사용할 DNS 서버의 주소를 지정합니다. 이 경우 127.0.0.1은 로컬 호스트 주소로, 이 주소에 DNS 서버가 실행 중이어야 합니다. NGINX가 이 주소로 DNS 조회를 보내 도메인 이름을 IP 주소로 변환하게 됩니다.
127.0.0.1
127.0.0.1은 일반적으로 로컬 컴퓨터를 가리키는 루프백 주소입니다. 이 주소를 사용하면 NGINX는 자신이 실행되고 있는 시스템 내의 DNS 서비스를 조회하려고 시도합니다. 로컬 시스템에 DNS 서버가 설정되어 있고 해당 서버가 도메인 이름을 해석할 수 있는 경우에만 이 설정이 유효합니다.
valid=30s
valid=30s는 NGINX가 DNS 조회 결과를 얼마나 오래 캐싱할지를 나타냅니다. 이 경우에는 30초 동안입니다., NGINX는 DNS 조회 결과를 30초 동안 메모리에 저장하고, 이 시간이 지나면 다시 DNS 서버에 조회를 요청하여 정보를 갱신합니다. 이 설정은 도메인의 IP 주소가 자주 변경될 수 있는 환경에서 유용하며, 너무 자주 DNS 서버에 요청을 보내는 것을 방지하여 성능을 최적화하는데 도움을 줍니다.
사용 상황
이 설정은 특히 동적인 IP 주소를 가진 서비스(: 클라우드 환경에서 실행되는 서비스)에 연결할 때 유용합니다. 예를 들어, 클라우드 서비스에서 IP 주소가 자주 바뀔 수 있으므로, valid 값을 적절히 조절하여 NGINX가 최신의 IP 정보를 유지할 수 있도록 합니다.
이 구문을 NGINX 설정 파일에 추가하면, NGINX는 설정된 DNS 서버를 사용하여 필요한 DNS 조회를 수행하고, 결과를 일정 시간 동안 캐싱하여 서버 성능과 응답 속도를 향상시킬 수 있습니다.

근데 스프링에서 DB로는 왜 reslover 없이 가능하지 ?


NGINX에서 resolver 설정을 사용하고, 스프링(Spring) 같은 애플리케이션에서는 내부 컨테이너 간의 통신을 위해 별도로 resolver를 설정하지 않아도 되는 이유는 두 서비스가 사용하는 네트워킹과 DNS 해석 메커니즘의 차이 때문입니다.

NGINX에서 resolver 설정이 필요한 이유
DNS 해석 메커니즘: NGINX는 기본적으로 운영 체제의 DNS 설정을 사용하지 않고, 명시적으로 지정된 DNS 서버를 통해 이름을 해석해야 합니다. 특히 Docker 환경에서 컨테이너 이름을 기반으로 동적인 서비스 발견과 로드 밸런싱을 하기 위해, NGINX는 이 이름들을 IP 주소로 변환할 수 있는 DNS 정보가 필요합니다. Docker가 제공하는 DNS 서비스(127.0.0.11)는 컨테이너 간의 동적 IP 관리와 서비스 발견을 지원합니다.
동적 서비스 발견: spring_$spring_color:8080와 같은 동적 변수를 사용하는 경우, 이 변수의 값이 바뀔 때마다 해당 서비스의 위치(IP 주소)를 다시 찾아야 할 필요가 있습니다. NGINX가 resolver를 통해 설정된 DNS를 사용하여 이러한 이름을 IP 주소로 지속적으로 해석할 수 있습니다.
성능과 캐시 관리: valid=30s는 DNS 캐시의 생명주기를 제어합니다. 클라우드나 컨테이너 환경에서 서비스의 IP 주소가 자주 변경될 수 있으므로, NGINX가 구식 또는 잘못된 정보를 사용하는 것을 방지하고, 최신 상태를 유지할 수 있도록 합니다.
스프링에서 내부 컨테이너 간 DB 접근 시 DNS 설정이 불필요한 이유
내장 DNS 지원: Docker와 같은 컨테이너 오케스트레이션 플랫폼은 내부 DNS를 제공하여, 컨테이너가 서로의 이름을 자동으로 IP 주소로 해석할 수 있게 합니다. 이는 컨테이너가 시작될 때 자동으로 설정됩니다.
애플리케이션 레벨의 네트워킹: 스프링과 같은 애플리케이션 프레임워크는 종종 내부적으로 서비스 등록 및 발견 메커니즘을 구현하여, 애플리케이션 내에서 서비스 간 통신을 간소화합니다. 예를 들어, 스프링 클라우드는 Eureka와 같은 서비스 디스커버리 도구를 사용하여 컨테이너 간 통신을 자동으로 처리할 수 있습니다.
결론적으로, NGINX의 resolver 설정은 동적 네트워킹 환경에서 NGINX가 동적으로 변화하는 서비스 위치를 지속적으로 추적하고 정확하게 연결할 수 있도록 해주는 중요한 역할을 합니다. 이는 NGINX가 로드 밸런서 또는 리버스 프록시로서 효과적으로 기능할 수 있게 지원합니다.

그렇다고 한다 !

즉 DB는 정적 컨테이너이기 때문에 스프링은 로드시에 도커 DNS 을 로드하여 스프링 서버에서 가지고있다

정적 + 디스커버리 사용으로 문제없이 사용할수 있다 !

또한 이름으로 매핑을 하기에

일반적으로 Docker Compose를 사용하여 컨테이너를 중지(stop), 시작(start), 또는 재시작(restart)할 때 컨테이너의 IP 주소는 변경되지 않습니다. 이는 Docker의 기본 동작 방식으로, 컨테이너의 IP 주소는 컨테이너가 생성될 때 동적으로 할당되며, 해당 컨테이너의 생애 주기 동안 유지됩니다.
but down 하면 없어진다 ! 따라서 조심하는게 좋을듯 ?

스프링(Spring) 애플리케이션의 경우: 스프링과 같은 애플리케이션 프레임워크는 보통 내부 서비스 간 통신을 위해 애플리케이션 레벨에서 서비스 디스커버리(: Eureka, Consul)를 통해 서비스 위치를 자동으로 관리하고 해석합니다. 컨테이너화된 환경에서는 Docker의 내장 DNS가 컨테이너 이름을 IP 주소로 자동 변환해주기 때문에 별도의 DNS 설정을 필요로 하지 않습니다. 이는 애플리케이션이 시작할 때 도커 오케스트레이션 플랫폼에서 제공하는 내장 DNS 설정을 로드하여 사용하기 때문입니다.
NGINX의 경우: NGINX는 프록시 서버 또는 로드 밸런서로서 동적으로 서비스의 위치를 추적하고 관리해야 할 필요가 있습니다. NGINX는 기본적으로 운영체제의 DNS 설정을 사용하지 않고, 설정 파일에서 명시적으로 지정된 DNS 서버(: resolver)를 통해 도메인 이름을 해석합니다. NGINX에서 resolver 설정을 사용하는 주된 이유는 동적인 서비스 위치(: 컨테이너 IP 주소 변경)를 신속하게 감지하고 대응하기 위해서입니다. 따라서, NGINX는 도커의 DNS 정보를 'resolver' 설정을 통해 별도로 관리해야 합니다.
결론적으로, 스프링 애플리케이션은 도커의 내장 DNS를 자동으로 사용하며, NGINX는 명시적인 DNS 설정을 요구합니다. 이는 각각의 시스템이 서비스 위치를 관리하고 해석하는 방식에 따른 차이입니다.

리졸버를 통해서 성공적으로 spring_blue를 찾아간다 !

엔진엑스 로그를 봐도 성공적으로 리버스 프록시의 작용이 일어난것을 볼수있다 !


2. spring에 health check api를 만들자

public class healthController {
  @GetMapping("")
  public ResponseEntity<?> getStatus(){
    log.info("receive health check request");
    return getResponseEntity(SuccessCode.OK, "isWorking");
  }
}

젠킨스에서 스왑이 정상적으로 일어나는지 확인해보자

echo "nginx Setting"
    docker exec -i nginx /bin/bash -c "echo -e 'set \$spring_color ${AFTER_COLOR};' > /etc/nginx/conf.d/service-color.inc && nginx -s reload"
    set $spring_color = AFTER_COLOR

파이프라인이 지나간 뒤

엔진엑스 컨테이너에 접근해서 직접 service-color.inc 파일을 확인하니

green으로 덮어 쓰여있는 것을 확인했다 !

이를 통해 완전한 무중단 배포를 구축할 수 있었다.

profile
라곰

0개의 댓글