제리의 배움 기록

[서비스배포] AWS에 서비스 배포하기 -<2> 서버 구성 및 서비스 배포 본문

개발기록

[서비스배포] AWS에 서비스 배포하기 -<2> 서버 구성 및 서비스 배포

제리92 2021. 12. 1. 22:32

목표 시스템 구성도

서버 구성

  • 외부망 1대
    • reverse proxy용 nginx
    • 웹서비스용 자바어플리케이션 서버
  • 내부망 1대
    • 웹서비스에서 사용할 데이터베이스 저장소

실행 흐름

1) 사용자가 https://jerry92k-subway.n-e.kr url을 호출
2) DNS 서버로부터 도메인을 해석한 IP를 응답받아 Server A의 443 포트를 호출(https 프로토콜)
3) Nginx는 80 포트로의 요청(http 프로토콜로 요청하는 경우)을 443 포트로 리다이렉트하고, 443 포트에 대한 요청을 웹어플리케이션으로 전달
4) 웹어플리케이션은 Server B의 데이터베이스를 참조

단계별 구성

1.서버 생성

  • AWS EC2 인스턴스를 이용하여 외부망에 1대, 내부망에 1대 총 2대의 서버를 생성
  • 서버 OS: ubuntu 18.04

2. 서버 설정 및 서비스 배포

Server B

  • 설치 패키지 리스트 최신화
    sudo apt update
  • docker 설치
    • apt가 https를 통해 데이터 및 패키지에 접근할 수 있도록 패키지 설치
      sudo apt install apt-transport-https ca-certificates curl software-properties-common
    • 리눅스 배포 종류를 자동으로 인식하여 적절한 버전을 설치해주는 스크립트를 이용하여 docker 설치
      sudo wget -qO- https://get.docker.com/ | sh
  • mysql docker container 설정파일 생성
    • mysql container 용 폴더 생성
      mkdir mysql-docker
    • 설정파일 생성
      vi docekr-compose.yml
    • version: '3' services: local-db: platform: linux/x86_64 # 추가된 라인 image: library/mysql:5.7 container_name: local-db restart: always ports: - 13306:3306 environment: MYSQL_ROOT_PASSWORD: secret TZ: Asia/Seoul volumes: - ./db/mysql/data:/var/lib/mysql - ./db/mysql/init:/docker-entrypoint-initdb. command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci
  • container 생성 및 실행
    docker-compose up -d
  • mysql container 접속
    sudo docker exec -it local-db bash
  • mysql console 접속
    mysql -u [계정ID] -p[비밀번호]
  • 웹어플리케이션에서 사용하는 database 생성

Server A

  • 설치 패키지 리스트 최신화
    sudo apt update
  • 자바 빌드 및 실행을 위한 패키지 설치
    • jdk 설치
      sudo apt install default-jdk
    • java path 설정
  • 실행할 어플리케이션의 프로젝트 소스 git으로 clone
    • git clone ..
  • 프로젝트 빌드
    ./gradlew clean build
  • 어플리케이션 실행
    java -jar [실행 jar 파일] -Dspring.profiles.active=prod실행파일
    • config(application-prod.properties)에 DB source가 Server B의 mysql container로 설정되어 있는지 확인

3.도메인 등록

  • 무료 도메인 사이트를 이용하여 도메인을 발급
    발급받은 도메인 : jerry92k-subway.n-e.kr
    내 도메인 한국
    => HTTPS 프로토콜 적용을 위한 SSL 인증서 발급시 생성한 도메인에 대해 인증받는다.
  • Server A의 공인IP를 A레코드에 등록
  • 도메인으로 웹어플리케이션을 접속해본다
    http://jerry92k-subway.n-e.kr:8080/
    [참고] 8080 포트로 서비스

4. Nginx를 이용한 https 적용 - Server A

  • docker 설치 : Server B 설치 참조
  • SSL 인증서 설치
    • docker를 이용해 letsencrypt로 간편하게 ssl 생성
    • docker run -it --rm --name certbot \ -v '/etc/letsencrypt:/etc/letsencrypt' \ -v '/var/lib/letsencrypt:/var/lib/letsencrypt' \ certbot/certbot certonly -d 'jerry92k-subway.n-e.kr' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory
    • 인증 확인 : 위 커맨드가 실행될때 웹어플리케이션서버가 구동되고 있으면 인증 확인됨
  • nginx container용 디렉토리 생성
    mkdir nginx
  • 인증서를 작업 디렉토리로 복사(Dockerfile은 build context 바깥 경로를 참조할 수 없으므로)
  • nginx 설정파일 생성
    vi nginx.conf
events {}

http {       
  upstream app {
    server [사설IP]:8080; # localhost나 127.0.0.1로 표현하는 경우 오류!!!(아래 주의사항 참고)
  }

  # Redirect all traffic to HTTPS
  server {
    listen 80;
    return 301 https://$host$request_uri;
  }

  server {
    listen 443 ssl;  
    ssl_certificate /etc/letsencrypt/live/jerry92k-subway.n-e.kr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/jerry92k-subway.n-e.kr/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;    
    }
  }
}
  • Dockerfile 생성
    vi Dockerfile
    • 이미지 내 파일시스템으로 설정파일과 키 복사하도록 커맨드
FROM nginx

COPY nginx.conf /etc/nginx/nginx.conf  
COPY fullchain.pem /etc/letsencrypt/live/jerry92k-subway.n-e.kr/fullchain.pem  
COPY privkey.pem /etc/letsencrypt/live/jerry92k-subway.n-e.kr/privkey.pem

=> docker build 시 nginx가 설치되어있지 않으면 자동으로 다운로드

  • nginx 이미지 빌드
    sudo docker build -t nginx/reverse-proxy:0.0.1 .
  • nginx container 실행
    sudo docker run -d -p 80:80 -p 443:443 --name proxy nginx/reverse-proxy:0.0.1

5. 서비스 확인

https://jerry92k-subway.n-e.kr

Nginx와 웹어플리케이션서버를 같은 서버에서 사용하는 경우 주의!

nginx.conf 에서 리다이렉트할 웹어플리케이션 경로를 명하는 부분이 있습니다.
이때 동일 서버인 경우 호스트 자신을 가리키는 localhost나 127.0.0.1로 표현할 수 있는데요,

http {       
  upstream app {
    server localhost:8080;
    # server 127.0.0.1:8080;
    }
    ...

현재 시스템 구성도에서는 이렇게 사용하면 서비스를 502 bad gateway 오류가 발생합니다.
이유는, docker container는 localhost, 127.0.0.1 호출시 호스트 서버를 가리키지 않고 container 스스로를 가리키기 때문입니다.
관련 참고 자료

이를 해결하기 위해서는
1)위 예시와 같이 서버 자신의 사설IP를 사용 하거나,

http {       
  upstream app {
    server [사설IP]:8080;
    }
    ...

2)호스트와 Docker engine간의 가상 네트워크 인터페이스인 bridge를 사용하면 됩니다.
별도의 설정을 하지 않았다면 default로 172.17.0.1이 bridge 네트워크의 게이트웨이로서 호스트를 가리킵니다.

http {       
  upstream app {
    server [172.17.0.1]:8080;
    }
    ...

Dockerfile, docker-compose.yml 차이

mysql container를 생성할 때는 docker-compose.yml을 정의하여 docker-compose up -d 명령어로 빌드와 실행을 한번에 하였고

nginx container를 생성할 때는 Dockerfile을 정의하여 docker build명령어로 빌드하고 docker run 으로 실행하였습니다.

두 방법 모두 docker 실행 이미지를 생성하고 container를 만들 수 있습니다.

두 방법의 차이점은 Dockerfile은 하나의 이미지를 생성하는 간단한 설정파일이고, docker-compose는 여러개의 컨테이너를 한번에 묶어서 관리할 수 있습니다.

docker-compose를 이용하면 여러개의 컨테이너를 한번에 실행하거나 정지할 수 있습니다.
하나의 어플리케이션을 위한 다양한 컨테이너 생성 정보를 묶어서 관리할 수 있기 때문에 개발-운영 환경을 편리하게 나눌 수 있고, 배포가 용이해져 특히 인스턴스 확장과 같은 경우에도 용이하게 사용될 수 있습니다.

이 프로젝트에선 두 가지 방법을 사용해보기 위해 mysql container 생성을 docker-compose로 정의하였지만, 하나의 컨테이너만 생성하고 있는 것이니 Dockerfile 방식으로 하여도 무방합니다.

[참고]
우아한 테크 캠프 Pro 3기 - [미션4]그럴듯한 서비스 만들기
dockerlabs - Difference between Docker Compose vs Dockerfile
docker container network

Comments