일반적으로 Proxy 서버는 LAN -> WAN 으로의 요청을 대리하여 처리해준다. 예를 들어 내가 집에서 와이파이로 특정 국가의 서비스를 이용하려고 하는데, 해당 서비스에서 한국의 IP를 막아두었다면, 나는 VPN 등의 서비스를 통해서 다른 국가의 IP로 우회하여 해당 서비스에 접근하게 된다.
Reverse Proxy의 경우는 그 반대라고 할 수 있다. WAN -> LAN 으로의 요청을 대리하여 처리해준다. 클라이언트로부터 어플리케이션 서버로 요청이 들어올 때, 중간에서 reverse proxy server 가 개입하여 다양한 전후처리를 해준다. 요청을 처리한 뒤, 어플리케이션 서버 역시 응답을 클라이언트로 바로 전달하지 않고, reverse proxy server 로 전달한다. 요청을 받은 reverse proxy server 는 서버 대신 클라이언트에게 응답을 전달한다.
Reverse Proxy 를 사용하면 결국 클라이언트와 어플리케이션 서버 사이에서 필요한 다양한 전후처리를 할 수 있다. 어플리케이션 서버는 정말 비즈니스 로직, 그 자체에만 집중하면 된다.
Reverse Proxy vs. Load Balancer
reverse proxy 는
TLS 암호화 등을 맡아 보안성을 향상시키거나,
확장성을 향상시키거나,
압축, SSL 처리로 인한 백엔드 리소스 확보, 캐싱처리 등을 통해 웹의 속도를 향상시키는 역할을 한다.
# 1. 우선 도커를 설치한다.
$ sudo apt-get update && \
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common && \
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - && \
sudo apt-key fingerprint 0EBFCD88 && \
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" && \
sudo apt-get update && \
sudo apt-get install -y docker-ce && \
sudo usermod -aG docker ubuntu && \
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
sudo chmod +x /usr/local/bin/docker-compose && \
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# 2. 아래와 같은 내용으로 Dockerfile 을 생성한다.
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
# 3. nginx.conf 파일을 아래와 같이 작성한다.
events {}
http {
upstream app {
server 172.17.0.1:8080;
}
server {
listen 80;
location / {
proxy_pass http://app;
}
}
}
# 4. nextstep/reverse-proxy 라는 이름으로 도커 이미지를 빌드한 뒤, 80포트로 실행한다.
$ docker build -t nextstep/reverse-proxy .
$ docker run -d -p 80:80 nextstep/reverse-proxy
4. Reverse Proxy에 TLS 설정
클라이언트 - 서버 간의 통신은 언제나 해킹의 위험에 노출되어있다. 그냥 평문으로 통신할 경우, 패킷이 가로채질 수 있으므로, 반드시 TLS 암호화를 하여 통신하도록 한다.
4-1. letsencrypt 를 활용하여 무료로 TLS 인증서 받기
# 1. docker 명령어를 통해서 내 도메인인 "subway-mhson.kro.kr"에 대한 인증서를 요청한다.
docker run -it --rm --name certbot \
-v '/etc/letsencrypt:/etc/letsencrypt' \
-v '/var/lib/letsencrypt:/var/lib/letsencrypt' \
certbot/certbot certonly -d 'subway-mhson.kro.kr' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory
# 2. 위의 명령어를 실행하면, 아래와 같은 결과가 나온다.
# 도메인 구입한 사이트에서 DNS TXT 레코드에 안내된 값을 추가해준다.
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for subway-mhson.kro.kr
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:
_acme-challenge.infra.subway-mhson.kro.kr.
with the following value:
jiRlP5PscTZ9f_kh1GfNbmBl6YiSt4i3MKo2irIHmgQ
Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.subway-mhson.kro.kr.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
# 3. 아래와 같은 결과가 나오면 성공!
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/subway-mhson.kro.kr/fullchain.pem
Key is saved at: /etc/letsencrypt/live/subway-mhson.kro.kr/privkey.pem
This certificate expires on 2023-06-03.
These files will be updated when the certificate renews.
NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DNS를 설정하는 사이트에서 DNS TXT 레코드를 추가한 후, 제대로 반영되었는지 dig 명령어로 확인한 후에 인증서 설정 진행을 계속한다.
$ dig -t txt _acme-challenge.example.com +short
4-2. 생성된 인증서를 활용하여 Reverse Proxy 에 TLS 설정하기
# 1. 우선 인증서 현재 경로로 옮기기
$ cp /etc/letsencrypt/live/[도메인주소]/fullchain.pem ./
$ cp /etc/letsencrypt/live/[도메인주소]/privkey.pem ./
# 2. 위에서 생성했던 Dockerfile 을 아래와 같이 수정한다.
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
COPY fullchain.pem /etc/letsencrypt/live/[도메인주소]/fullchain.pem
COPY privkey.pem /etc/letsencrypt/live/[도메인주소]/privkey.pem
# 3. nginx.conf 도 아래와 같이 수정한다.
events {}
http {
upstream app {
server 172.17.0.1:8080;
}
# Redirect all traffic to HTTPS
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/[도메인주소]/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/[도메인주소]/privkey.pem;
# Disable SSL
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# 통신과정에서 사용할 암호화 알고리즘
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
# Enable HSTS
# client의 browser에게 http로 어떠한 것도 load 하지 말라고 규제합니다.
# 이를 통해 http에서 https로 redirect 되는 request를 minimize 할 수 있습니다.
add_header Strict-Transport-Security "max-age=31536000" always;
# SSL sessions
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://app;
}
}
}
# 4. 방금 전에 띄웠던 도커 컨테이너를 중지 + 삭제하고, 새로운 설정을 반영하여 다시 띄운다.
$ docker stop proxy && docker rm proxy
$ docker build -t nextstep/reverse-proxy:0.0.2 .
$ docker run -d -p 80:80 -p 443:443 --name proxy nextstep/reverse-proxy:0.0.2
5. 운영 데이터베이스 구성하기
일반적으로 데이터베이스를 컨테이너로 다루지는 않는다. 컨테이너는 사라지면 그만이니, 데이터베이스처럼 영속성을 가진 데이터를 다루기에는 적절하지 않은 면이 있다. 하지만, 지금은 데이터베이스가 중요한 것이 아니라 인프라 구축에 대한 실습을 하고 있는 것이므로, 도커를 이용해 빠르게 데이터베이스를 구축해보자.
# 1. 데이터베이스 실행하기
$ docker run -d -p 3306:3306 brainbackdoor/data-subway:0.0.1
# 2. 데이터베이스 내부 접속하기
$ docker ps -al
$ docker exec -it [container id] /bin/bash
# 3. mysql 실행하여 데이터 확인하기
mysql -u [id] -p
(password 입력)
show databases;
use subway;
show tables;
select * from line;
6. 로컬/운영 설정 파일 나누기
지금까지의 실습에서는 데이터베이스의 데이터를 조회하지는 않았다. 이제 로컬 개발환경과 운영 환경이 구분되었으므로 application.properties 설정파일을 통해 운영 환경별로 데이터베이스 설정을 분리해본다.
PUBLIC-2a:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bc1885093f7d nextstep/reverse-proxy:0.0.2 "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp proxy
비록 나는 시간이 없어서 적용해보지 못했지만, 아래와 같은 설정들을 추가해보면 개발시나 운영시에 편리해진다.
9-1. [추가] 데이터베이스 테이블 스키마 버전관리
보통 운영 데이터베이스는 JPA등과 같은 ORM 을 사용하여 기존의 테이블을 변경하지 않는다. 데이터가 유실되거나 참조 무결성 제약 등으로 인해 어려움이 있기 때문이다. 그래서 보통은 로컬에서는 H2 와 같은 in-memory 데이터베이스를 이용하여 빠르게 개발을 하고, 운영 데이터베이스는 점진적으로 migration 을 해나간다.
이때, 운영 데이터베이스의 테이블 스키마에 대해서도 버전관리를 하는 것이 필요한데, 이때 사용하는 것이 바로 Flyway 이다.