📌 컨테이너를 학습하고 3 tier로 운영환경을 구성해봅니다.
개발 환경을 구성해보고 지속적 통합을 경험해봅니다.

1. Reverse Proxy 구성하기

A. 도커 설치

$ 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
  • 도커 컨테이너가 처음인 분들은 강의 영상 확인 후, 포스팅을 참고바랍니다.

B. 컨테이너 실행

  • Dockerfile

    FROM nginx
    
    COPY nginx.conf /etc/nginx/nginx.conf
  • nginx.conf

    events {}
    
    http {
      upstream app {
        server 172.17.0.1:8080;
      }
    
      server {
        listen 80;
    
        location / {
          proxy_pass http://app;
        }
      }
    }
  • 실행

    $ docker build -t brainbackdoor/reverse-proxy .
    $ docker run -d -p 80:80 brainbackdoor/reverse-proxy
    $ curl http://localhost

2. TLS 설정

  • 서버의 보안과 별개로 서버와 클라이언트간 통신상의 암호화가 필요합니다. 평문으로 통신할 경우, 패킷을 스니핑할 수 있기 때문입니다.

  • letsencrypt를 활용하여 무료로 TLS 인증서를 사용할 수 있어요.

    $ docker run -it --rm --name certbot \
      -v '/etc/letsencrypt:/etc/letsencrypt' \
      -v '/var/lib/letsencrypt:/var/lib/letsencrypt' \
      certbot/certbot certonly -d 'yourdomain.com' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory
  • 인증서 생성 후 유효한 URL인지 확인을 위해 DNS TXT 레코드로 추가합니다.

  • DNS를 설정하는 사이트에서 DNS TXT 레코드를 추가한 후, 제대로 반영되었는지 dig 명령어로 확인한 후에 인증서 설정 진행을 계속합니다.

    $ dig -t txt _acme-challenge.example.com +short
  • 생성한 인증서를 활용하여 Reverse Proxy에 TLS 설정을 해봅시다. 우선 인증서를 Dockerfile이 있는 현재 경로로 옮깁니다.

    $ cp /etc/letsencrypt/live/[도메인주소]/fullchain.pem ./
    $ cp /etc/letsencrypt/live/[도메인주소]/privkey.pem ./
  • 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
  • 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;    
        }
      }
    }
  • 방금전에 띄웠던 도커 컨테이너를 중지 & 삭제하고 새로운 설정을 반영하여 다시 띄워봅시다.

    $ docker stop proxy && docker rm proxy
    $ docker build -t brainbackdoor/reverse-proxy:0.0.2 .
    $ docker run -d -p 80:80 -p 443:443 --name proxy brainbackdoor/reverse-proxy:0.0.2

3. 운영 데이터베이스 구성하기

일반적으로, 실제 운영환경에서 컨테이너로 데이터베이스의 영속성 데이터를 다루지 않습니다. 컨테이너의 철학과 데이터베이스의 영속성은 다소 배치되는 부분이 있다고 생각합니다. 여기서는 원활한 실습을 위해 제가 미리 push해둔 컨테이너를 활용합니다.

# id: root, pw: masterpw
$ docker run -d -p 3306:3306 brainbackdoor/data-subway:0.0.1

4. 설정 파일 나누기

  • 실제로 배포를 하려다보면, JUnit을 활용한 test 단계와 local 환경에서 직접 애플리케이션을 확인할 때, 그리고 실제로 배포할 때 등 각 상황에 맞춰 설정을 다르게 적용할 필요성이 생깁니다.

  • 예제 코드의 커밋을 통해 test와 local, prod에서 다른 설정을 사용하는 방법을 익혀봅시다.

  • -Dspring.profiles.active=prod 옵션을 추가하여 실행하면 application-prod.properties의 설정을 사용합니다.

    $ java -jar -Dspring.profiles.active=prod [jar파일명]

5. 배포 스크립트 작성해보기

  • 반복적으로 사용하는 명령어를 Script로 작성해봅니다.

    #!/bin/bash
    
    ## 변수 설정
    
    txtrst='\033[1;37m' # White
    txtred='\033[1;31m' # Red
    txtylw='\033[1;33m' # Yellow
    txtpur='\033[1;35m' # Purple
    txtgrn='\033[1;32m' # Green
    txtgra='\033[1;30m' # Gray
    
    
    echo -e "${txtylw}=======================================${txtrst}"
    echo -e "${txtgrn}  << 스크립트 🧐 >>${txtrst}"
    echo -e "${txtylw}=======================================${txtrst}"
    
    ## 저장소 pull
    ## gradle build
    ## 프로세스 pid를 찾는 명령어
    ## 프로세스를 종료하는 명령어
    ## ...
    
  • 기능 단위로 함수로 만들어봅니다.

    function pull() {
      echo -e ""
      echo -e ">> Pull Request 🏃♂️ "
      git pull origin master
    }
    
    pull;
  • 스크립트 실행시 파라미터를 전달해봅니다.

    • 실행시 파라미터를 전달하도록 하여 범용성 있는 스크립트를 작성해봅니다.

    • read 명령어를 활용하여 사용자의 Y/N 답변을 받도록 할 수도 있어요.

    #!/bin/bash
    
    ## ...
    
    EXECUTION_PATH=$(pwd)
    SHELL_SCRIPT_PATH=$(dirname $0)
    BRANCH=$1
    PROFILE=$2
    
    ## 조건 설정
    if [[ $# -ne 2 ]]
    then
        echo -e "${txtylw}=======================================${txtrst}"
        echo -e "${txtgrn}  << 스크립트 🧐 >>${txtrst}"
        echo -e ""
        echo -e "${txtgrn} $0 브랜치이름 ${txtred}{ prod | dev }"
        echo -e "${txtylw}=======================================${txtrst}"
        exit
    fi
    
    ## ...
    
  • 반복적으로 동작하는 스크립트를 작성해봅니다.

    • github branch 변경이 있는 경우에 스크립트가 동작하도록 작성해봅니다.

    function check_df() {
      git fetch
      master=$(git rev-parse $BRANCH)
      remote=$(git rev-parse origin/$BRANCH)
    
      if [[ $master == $remote ]]; then
        echo -e "[$(date)] Nothing to do!!! 😫"
        exit 1
      fi
    }
  • crontab을 활용해봅니다.

    • 매 10분마다 동작하도록한 후 log를 확인해보세요.

    • crontab과 /etc/crontab의 차이에 대해 학습해봅니다.

6. 추가로 진행해볼만한 작업들

A. 설정 별도로 관리하기

  • 우선, github private 저장소를 생성한 후 application.properties 등의 설정 파일을 올립니다.

  • git의 서브모듈 기능을 활용하여 특정 경로에 private repository를 참조하도록 설정합니다

    • 이후에 소스코드를 받을 때는 서브모듈까지 clone해야 합니다.

    $ git submodule add [자신의 private 저장소] ./src/main/resources/config
    $ git clone --recurse-submodules [자신의 프로젝트 저장소]
  • 설정 파일의 내용이 변경된 경우

    $ git submodule foreach git pull origin main
    $ git submodule foreach git add .
    $ git submodule foreach git commit -m "commit message"
    $ git submodule foreach git push origin main

B. 로컬 테스트시 MultiRun 활용

  • 로컬에서 서버를 띄울 때, IntelliJ의 Multirun 플러그인을 활용하면 보다 손 쉽게 서버를 띄울 수 있습니다.

    • Multi Run 플러그인 설치

    • Multi Run 설정

      • IntelliJ -> Run -> Edit Configurations...

      • Docker 설정

      • NPM 설정

      • Multi Run 설정