Programming-[Infra]/Docker

도커 교과서(엘튼 스톤맨, 심효섭) - 18. 리버스 프록시-1: nginx, 로드밸런싱, 라우팅과 SSL, Traefik

컴퓨터 탐험가 찰리 2023. 5. 21. 18:14
728x90
반응형

 

1. 프록시, 리버스 프록시 서버

 

보통 프록시 서버는 클라이언트단을 관리하는 의미인 것 같다. 내 컴퓨터에서 인터넷으로 요청을 하면 프록시가 적용된 네트워크에서 해당 요청을 가로채서 허용된 사이트인지 확인하거나 빠른 속도를 위해 접근하려는 사이트의 캐시를 제공하는 용도로 사용한다.

 

리버스 프록시 서버는 서버단을 관리하는 의미인 것 같다. 서버로 들어오거나 서버에서 나가는 모든 트래픽을 관리하면서 해당 트래픽이 어떤 애플리케이션에서 출발한 것인지 파악한다. 그리고 서버 내 애플리케이션의 응답 내용을 캐싱하였다가 적절하게 가공하여 클라이언트에게 전달하기도 한다.

 

[리버스 프록시- 애플리케이션 컨테이너들간 상호 작용 그림 추가]

 

 

그림에서 리버스 프록시는 포트를 외부로 공개한 유일한 컨테이너가 된다. 이 덕분에 각 애플리케이션의 포트를 노출할 필요가 없게된다.

 

 

2. NGINX로 리버스 프록시 적용

 

nginx 실행

nginx는 인터넷의 30%를 차지할 정도로 오랫동안 많은 서비스에서 리버스 프록시로 쓰였다고 한다.

 

cd ch20/exercises
docker network create ch20
docker-compose -f nginx/docker-compose.yml -f nginx/override-linux.yml up -d

 

네트워크를 생성하고 nginx 설정을 적용한 도커 컴포즈 파일을 통해 nginx를 실행한다.

 

에러

 

certs라는 bind용 source path가 없다는 에러가 나와서 mkdir를 통해 /certs path를 생성해주고 위 명령어를 다시 실행했다.

 

 

그런데도 nginx에서 numbers-api를 참조하는데 찾을 수 없다는 에러가 뜨면서 컨테이너가 실행되지 않았다. 그래서 아래 명령대로 numbers-api 경로에 들어가서 해당 애플리케이션을 실행했다.

cd ../numbers
docker-compose -f docker-compose.yml up -d

 

그리고... iotd, access-log, image-gallery와 whoami 서비스까지 다 실행해줘야 nginx가 일단 실행됐다. 예제 파일의 디렉터리에 있는 프로젝트들을 docker-compose 명령어를 사용하여 실행시키면 컨테이너 내에서 sites-enabled에 각 애플리케이션들이 등록되지 않았다는 경고문은 뜨지만 http:localhost:80으로 접근하면 nginx가 정상적으로 표시되긴한다. 아마 저자가 실습 후의 이미지가 nginx의 이미지로 등록되어 있는 것 같다.

 

 

리버스 프록시 적용

리버스 프록시를 통해서 같은 포트를 통해 각 애플리케이션으로 요청이 전달될려면 사이트별 설정 파일을 추가해야한다. 저자가 구성해놓은 네트워크 환경 설정상 앞에서 살펴본 sites-enabled 경로에 nginx 설정 파일을 넣어주면 된다. 그리고 nginx 컨테이너에서 HTTP 요청상의 헤더에 host=www.naver.com 같은 정보를 추가해줘야하는데, 아래처럼 설정하면 된다.

 

# 도메인 whoami.local을 hosts 파일에 추가
echo $'\n127.0.0.1 whoami.local' | sudo tee -a /etc/hosts
docker-compose -f whoami/docker-compose.yml up -d # 위 에러 처리에서 실행했으므로 생략
# 애플리케이션 설정 파일을 nginx 설정 파일 디렉터리로 복사
cp ./nginx/sites-available/whoami.local ./nginx/sites-enabled/
# nginx를 재시작해 설정 업데이트
docker-compose -f nginx/docker-compose.yml restart nginx

 

다시 말해 localhost에서 who-am-i 애플리케이션으로 http://whoami.local 도메인으로 요청을 보낼 것이다. 이 호스트를 허용하기 위해서 localhost를 호스팅하는 로컬 컴퓨터의 /etc/hosts에 sudo tee -a 명령어를 통해 127.0.0.1 whoami.local 이라는 호스트를 추가하는 것이다.

 

그리고 sites-available 디렉터리 내에 각 애플리케이션별로 설정 파일이 있는 것을 볼 수 있다.

 

cat 명령어를 통해서 whoami.local 파일을 살펴보면 아래처럼 proxy_pass, proxy_set_header, add_header 등 nginx에서 참고할 설정들이 있는 것을 볼 수 있다. 이것을 저자가 nginx가 참조하도록한 sites-enabled 디렉터리 내로 옮겨서 설정을 추가하는 것이다. 만약 호스팅의 주체가 로컬 컴퓨터가 아니라 클라우드로 구동되는 다른 컴퓨터라면 이런 설정 파일을 참조하는 위치를 클라우드 컴퓨터의 특정 path로 지정하고 거기에 설정 파일을 넣어주면 될 것 같다!

 

참고로 nginx 설정은 각자 다음의 의미를 갖는다.

  • proxy_pass : 콘텐츠가 위치한 주소
  • proxy_set_header : 호스트 정보를 콘텐츠 위치로 설정
  • add_header : 응답의 호스트 정보를 프록시 이름으로 변경

 

nginx의 설정을 적용하기 위해서는 재시작이 필요하다.

그리고 nginx의 설정에 포함된 애플리케이션들이 반드시 실행 중인 상태여야한다. nginx는 모든 업스트림 중 하나라도 접근 불가한 것이 있다면 그대로 종료된다.

 

그리고 http://whoami.local에 접속하면 호스팅 중인 로컬 컴퓨터의 정보를 출력해주는 웹 애플리케이션이 실행되고 있는 것을 볼 수 있다. 

 

업스트림과 다운스트림

  • 업스트림: 리버스 프록시가 요청을 받아서 애플리케이션에 전달
  • 다운스트림: 애플리케이션의 응답을 리버스 프록시가 클라이언트에 전달

 

리버스 프록시는 HTTP로 제공되는 콘텐츠라면 웹 사이트뿐만 아니라 REST API, TCP/IP나 gRPC 등도 가능하다. 그리고 모든 애플리케이션의 트래픽이 프록시를 경유하므로 설정의 중심 역할을 할 수 있다. 인프라스트럭쳐 관련 내용을 개별적으로 애플리케이션에서 설정하는 것이 아니라 컨테이너 수준에서 분리하여 중앙 집중형으로 관리할 수 있는 것이다.

 

 

 

3. 로드밸런싱, 라우팅과 SSL 적용하기

 

로드 밸런싱

업스트림 체크 완료 후 nginx는 호스트명과 IP 주소를 연결한 내부 라우팅 리스트를 만든다. 업스트림마다 여러 개의 컨테이너가 존재한다면 로드 밸런싱 처리도 해준다.

 

# 도메인을 hosts 파일에 추가
echo $'\n127.0.0.1 image-gallery.local' | sudo tee -a /etc/hosts
# 컨테이너 3개 실행
docker-compose -f ./image-gallery/docker-compose.yml up -d --scale image-gallery=3
cp ./nginx/sites-available/image-gallery.local ./nginx/sites-enabled/
docker-compose -f ./nginx/docker-compose.yml restart nginx
curl -i --head http://image-gallery.local

 

curl 명령어를 입력했을 때 응답으로 X-Upstream 값을 출력해준다. 이 값은 nginx가 응답을 받아온 컨테이너의 IP 주소이며 172.20으로 시작하는 도커 컨테이너의 주소이다. curl로 몇 번 더 호출하면 nginx가 로드밸런싱을 적용해서 매번 호출되는 컨테이너의 IP 주소가 변경되는 것을 볼 수 있다.

 

라우팅

여러 컨테이너를 하나의 도메인으로 묶어내고 특정 경로로 들어왔을 때는 특정 컨테이너에 요청이 전달되도록 라우팅할 수 있다. 지금 살펴보는 image-gallery.local 도메인은 iotd라는 API와 image-gallery라는 웹으로 구성되어있다. 여기서 설정 파일을 다음처럼 바꿔준다.

 

# 기존 설정 파일 삭제
rm ./nginx/sites-enabled/image-gallery.local
cp ./nginx/sites-available/image-gallery-2.local ./nginx/sites-enabled/image-gallery.local
docker-compose -f ./nginx/docker-compose.yml restart nginx
curl -i http://image-gallery.local/api/image

 

변경된 설정 파일은 다음과 같다.

server {
    server_name image-gallery.local;

    location = /api/image {
        proxy_pass             http://iotd/image;
        proxy_set_header       Host $host;
        add_header             X-Proxy $hostname;         
        add_header             X-Upstream $upstream_addr;
    }

    location / {
        proxy_pass             http://image-gallery;
        proxy_set_header       Host $host;
        add_header             X-Proxy $hostname;         
        add_header             X-Upstream $upstream_addr;
    }        
}

 

image-gallery.local 도메인 아래에 /api/image 경로로 요청을 보내면 프록시가 http://iotd/image로 요청을 전달하여 API에 접근하도록 라우팅을 해주는 것이다.

 

SSL 적용

보안을 위해 HTTPS를 적용한다. 이를 위해서는 설정과 인증서가 위치해야하는데, 각 애플리케이션마다 존재하는 것이 아니라 중앙의 프록시에 두는 것이 훨씬 낫다. 테스트를 위해 자체 서명 인증서를 만들고 적용해본다.

 

# 자체 서명 인증서 생성
docker container run -v "$(pwd)/nginx/certs:/certs" -e HOST_NAME=image-gallery.local diamol/cert-generator
rm ./nginx/sites-enabled/image-gallery.local
# SSL이 포함된 설정 파일 복사
cp ./nginx/sites-available/image-gallery-3.local ./nginx/sites-enabled/image-gallery.local
docker-compose -f nginx/docker-compose.yml restart nginx

 

자체 서명 인증서 생성 컨테이너를 실행하면 RSA를 기반으로 ca-key.pem, ca.password등 여러가지 인증서용 파일들을 만든다.

 

nginx를 재시작한 후에 웹 브라우저로 http://image-gallery.local 에 접근한다. 웹 브라우저로 http 주소에 접근하기 때문에 안전하지 않은 연결이라는 경고가 나온다. 고급에 들어가서 강제로 사이트로 이동해보면 https로 리다이렉트 된다.

 

인증서가 포함된 설정 파일은 80번 포트를 주시하다가 301 응답으로 443번 포트를 사용하는 HTTPS 사이트로 리다이렉트하는 설정을 포함한다. 일부만 살펴보면 아래와 같다.

server {
    server_name image-gallery.local;
    listen 80;
	return 301 https://$server_name$request_uri;
}

server {
	server_name  image-gallery.local;
	listen 443 ssl;

	ssl_certificate        /etc/nginx/certs/server-cert.pem;
	ssl_certificate_key    /etc/nginx/certs/server-key.pem;
	ssl_session_cache      shared:SSL:10m;
	ssl_session_timeout    20m;
	ssl_protocols          TLSv1 TLSv1.1 TLSv1.2;

	ssl_prefer_server_ciphers on;

//중략

 

 

4.  응답 캐싱

 

nginx의 기능 중 하나인 응답 캐싱 기능을 학습한다. 사용자에 따라 달라지는 응답 내용이 아니라 정적인 콘텐츠 등 캐싱이 가능한 부분이 있다면 리버스 프록시에서 이를 캐싱 프록시로 저장해두었다가 실제 업스트림의 컨텐츠에 접근하지 않고 캐시에서 바로 응답을 내보내는 기능이다.

 

이렇게하면 응답 속도가 향상되고 리버스 프록시 - 애플리케이션간 트래픽을 줄일 수 있으므로 그만큼 더 많은 요청을 처리할 수 있게 된다. 앞서 언급한대로 개인화된 내용은 동적으로 제공해야해서 캐싱을 하면 안된다. 이를 위해 인증 쿠키가 포함된 요청을 캐싱하지 않도록 설정한다.

 

# 현재 설정 파일 제거
rm ./nginx/sites-enabled/image-gallery.local
cp ./nginx/sites-available/image-gallery-4.local ./nginx/sites-enabled/image-gallery.local
docker-compose -f ./nginx/docker-compose.yml restart nginx

curl -i --head --insecure https://image-gallery.local
curl -i --head --insecure https://image-gallery.local

 

이제 응답 헤더를 살펴보면 X-Cache가 포함된 것을 볼 수 있다. 첫 요청에서는 애플리케이션에서 정보를 가져오기 때문에 X-Cache의 값이 MISS이지만, 두 번째 같은 호출에서는 캐시에서 정보를 가져와서 HIT값을 응답했다.

 

설정에서 캐시가 유지되는 시간, 사용할 메모리 및 디스크의 용량 등을 설정할 수 있다.

 

 

5. Traefik: 클라우드 네이티브 리버스 프록시

 

nginx는 컨테이너 이전부터 있었던 기술이고 앞선 내용들은 nginx를 컨테이너 단위로 적용했을 뿐이다. 이번에 배워볼 클라우드 네이티브 리버스 프록시인 Traefik은 프록시용 컨테이너가 도커 엔진에 연결되어서 도커 엔진과 연결된 다른 컨테이너에 대한 정보들을 얻는 방식으로 작동한다. 그렇기 때문에 애플리케이션마다 설정 파일을 따로 둘 필요 없이 각 컨테이너에 레이블을 추가하면 스스로 설정과 라우팅 맵을 구성한다.

 

이는 프록시 적용도 애플리케이션 배포의 일부처럼 작동하는 개념이다. 설정을 동적으로 구성하게 되므로 Traefik을 실행 전 업스트림 애플리케이션들이 모두 실행돼야한다거나, 설정 변경 후 재시작을 할 필요가 없다는 장점이 있다.

 

# 기존 사용했던 컨테이너 제거(선택적으로 적용하자. 교재에서 사용하는 컨테이너 외 컨테이너가 있다면 지워질 수도 있으므로 조심)
docker container rm -f $(docker container ls -aq)
docker-compose -f traefik/docker-compose.yml -f traefik/override-linux.yml up -d

 

그리고 브라우저에서 http://localhost:8080에 접근한다.

 

Traefik의 기본 구성요소는 다음과 같다. 일반적인 웹용 프레임워크와 다 같은 개념인 것 같다.

 

  • 엔트리 포인트: 외부에서 들어오는 트래픽을 주시하는 포트. 이 포트와 컨테이너의 공개 포트가 매핑된다.
  • 라우터: 요청을 분배할 컨테이너를 결정하는 규칙
  • 서비스: 업스트림 컴포넌트를 지칭
  • 미들웨어: 라우터 <> 서비스 간 전달되는 요청을 변경하는 역할. 요청에 포함된 경로, 헤더를 변경하거나 인증을 강제할 수 있다.

 

트래픽도 엔진엑스와 마찬가지로 위 각 구성요소에 대한 설정을 설정 파일을 통해 제어할 수 있다. 자세한 설정은 필요할 때 실습해봐도 될 것 같다.

728x90
반응형