[운송장 정보보호 서비스]백엔드(sping, django) & 프론트엔드(react) 도커라이징

신현식·2023년 5월 31일
1

캡스톤디자인

목록 보기
2/7
post-thumbnail

도커라이징

도커라이징(Dockerizing)은 Docker 컨테이너를 사용하여 응용프로그램(application)을 패킹(packing), 배포(deploying), 실행(running) 하는 전 과정을 말한다.

백엔드(spring) 도커라이징

백엔드 서비스는 스프링 코드를 사용하여 개발을 진행한다. 따라서 스프링을 자동으로 빌드하고 jar 형태의 실행파일을 실행시켜줄 수 있는 도커 파일을 만들어서 이미지를 생성할 것이다.

스프링은 gradle로 빌드할 것이다. 개발자가 gradle 8.0.2 버전을 사용하였고 java는 17 버전을 사용하였다. 이를 바탕으로 하나의 인스턴스에서 코드를 빌드하여 실행시켜보고, Dockerfile을 작성하여 도커 이미지를 생성해볼 것이다.

1 인스턴스를 생성한 이후 도커를 설치한다. 밑의 방식을 그대로 따라하면 된다. 이후 사용자에서 도커그룹에 대한 권한을 부여하면 도커 명령을 사용할 수 있다.

우분투에 도커 설치하는 방법

# USER에 docker 그룹 부여
sudo usermod -aG docker $USER
exit

# 이후 재접속하면 도커명령을 사용할 수 있다.

2 spring 코드가 있는 git 저정소를 클론해온다. 이후 openjdk를 설치한 후 gradle를 설치한다.

💡 java 버전 문제

자바 버전을 17를 사용했음으로 openjdk 버전도 java 17과 연동되는 것으로 설치를 진행해야한다. 자바 버전을 낮게 진행할 시 추후에 빌드에 실패할 수 있기때문에 꼭 필수적으로 버전을 맞춰줘야한다.

# java 설치
sudo apt update
sudo apt install -y openjdk-17-jdk

# gradle 설치

# /tmp 디렉토리에서 Gradle 이진 전용 zip 파일을 다운로드
VERSION=8.0.2
wget https://services.gradle.org/distributions/gradle-${VERSION}-bin.zip -P /tmp
sudo unzip -d /opt/gradle /tmp/gradle-${VERSION}-bin.zip

"sudo : unzip : command not found"라는 오류 메시지가 표시되면 unzip 패키지를 설치
$ sudo apt install unzip

# gradle 설치 디렉터리를 가리키는 latest라는 이름의 심볼 링크 설정
sudo ln -s /opt/gradle/gradle-${VERSION} /opt/gradle/latest

# 환경변수 설정
sudo vi /etc/profile.d/gradle.sh # 아래 두줄 작성 후 저장 
export GRADLE_HOME=/opt/gradle/latest
export PATH=${GRADLE_HOME}/bin:${PATH}

# 스크립트 권한 설정
sudo chmod +x /etc/profile.d/gradle.sh
source /etc/profile.d/gradle.sh

# Gradle 설치 확인
gradle -v

-> 출력 결과
Welcome to Gradle 8.0.2!

3 gradle을 빌드하기 위해 gradlew(gradle wrapper)를 설치한다. 이후 코드가 있는 디렉터리에 들어가서 빌드를 실행하면 build 디렉터리가 생성되고 libs 안에 jar 파일이 생성되어 있는 것을 확인 할 수 있다.

gradle wrapper --gradle-version 8.0.2

# 테스트를 위해 BUILD와 TEST를 통해 나온 결과물인 jar파일을 배포시켜본다.
java -jar <파일 명>.jar

4 성공적으로 빌드되는것까지 확인하였으니 이제 Dockerfile을 작성해서 도커 이미지를 만들어준다.

현재 도커허브에 openjdk 17-jre는 "11-ea-17-jre" 버전밖에 존재하지 않는다. 이를 사용해서 진행하였을때 자바 버전 17를 제대로 빌드하지 못하였다. jre를 사용하려 했던 이유는 도커파일로 이미지를 만들어 실행시킬 때에는 굳이 용량을 큰 개발도구까지 필요없고 실행할 수 있는 환경만 있으면 가능했기 때문이다.
하지만 도커허브에서 제공하는 자바 17버전의 jre는 제대로 기능을 하지 못하였기 때문에 가장 최근버전의 자바까지 지원할 수 있고 그나마 가벼운 용량으로 제공하는 openjdk:21-ea-17-jdk-slim 이미지를 사용하였다.

💡 JRE VS JDK

  • JRE(Java Runtime Environment)는 말 그대로 자바 실행 환경이다. JRE는 JVM 뿐만 아니라 Java binaries, Java 클래스 라이브러리 등을 포함하고 있어 자바 프로그램의 실행을 지원한다. 하지만 JRE는 컴파일러나 디버거(Debugger) 등의 도구는 포함하지 않는다. 따라서 자바 프로그램을 개발하는 것이 아니라 실행하기만 원한다면 JRE를 설치하면 된다.
  • JDK(Java Development Kit)는 말 그대로 자바 개발 키트이다. JDK는 자바 애플리케이션을 개발하기 위한 환경을 지원한다. JDK는 JRE를 포함할 뿐만 아니라 컴파일러(javac), javadoc, jar 등 개발에 유용한 도구들을 포함하고 있다. 따라서 자바 프로그램을 개발하기 위해서는 JDK를 다운로드하여야 한다.

🔎 결론은 JRE는 자바 실행환경이고, JDK는 자바 개발 도구라는 것이다. 따라서 개발자들은 JDK를 다운로드 받으면되고 실행만 테스트해볼때에는 JRE를 다운받으면 된다.

FROM gradle:8.0.2-jdk17 AS mbuilder 
COPY ./delivery-service /usr/src/
WORKDIR /usr/src/
RUN gradle wrapper --gradle-version 8.0.2
RUN ./gradlew build 

FROM openjdk:21-ea-17-jdk-slim
COPY --from=mbuilder /usr/src/build/libs/delivery-service-0.0.1-SNAPSHOT.jar /usr/src/
CMD ["java","-jar","/usr/src/delivery-service-0.0.1-SNAPSHOT.jar"] 

다음 Dockerfile로 이미지를 만들었더니 성공적으로 이미지가 잘 빌드되었다. 똑같은 방식으로 스프링으로 개발한 user 서비스도 도커 이미지로 만들어주었다.

프론트엔드(react) 도커라이징

React JS로 프론트엔드 개발을 하고 Nginx로 배포하는 상황이기 떄문에 우리는 nginx.conf 파일을 수정해주고 도커파일을 생성할 것이다.

nginx.conf 파일 생성

💡 nginx proxy_pass 경로 설정

# 예시
location /some/path/ {
    proxy_pass http://www.example.com/link/;
}

이 예제 구성은 이 위치에서 처리된 모든 요청을 지정된 주소의 프록시 서버로 전달한다.
위의 첫 번째 예에서 프록시 서버의 주소 뒤에 URI가 온다. /link/ URI가 주소와 함께 지정된 경우 위치 매개변수와 일치하는 요청 URI 부분을 대체한다. 예를 들어 /some/path/page.html 일 경우 여기에서 URI가 포함된 요청은 http://www.example.com/link/page.html 이다.
URI 없이 주소를 지정하거나 교체할 URI 부분을 결정할 수 없는 경우 전체 요청 URI가 전달된다.


즉 리버스 프록시 적용을 못해주고 그대로 요청을 전달한다는 의미이다.
📢 NGINX 역방향 프록시

💡리버스 프록시란?

클라이언트 요청을 대신 받아 내부 서버로 전달해주는 것을 리버스 프록시(Reverse Proxy) 라고 합니다.
저도 사실 프록시라는 개념이 낯설었는데요, 일단 프록시라는 개념부터 확인해야 합니다.
프록시란 대리라는 의미로, 정보를 대신 전달해주는 주체라고 생각하면 되는데, 만약 이 프록시 없이 웹 서버를 운영한다고 가정하면 사용자가 갑자기 많아지거나, 웹서버가 그대로 노출되어 있기 때문에 보안적으로 위험성이 있다. 때문에 nginx를 사용하면 로드 밸런싱으로 부하를 줄여줄 수 있고, 분산 처리 또한 가능하며 웹서버의 SSL 인증도 적용할 수 있습니다.
따라서 사용자 -> nginx -> 웹서버로 구성해서 사용자의 요청을 nginx가 대신 웹서버로 전달해주도록 구성한다.

💡 reverse proxy 사용하기

외부에서 내부 서버가 제공하는 서비스에 접근할 경우, proxy server(nginx)를 통해서 들어오는 방식이다.

location에 대해서 좀 더 자세히 적자면, nginx는 클라이언트가 접근한 path를 보고, 가장 적합한 location의 블럭으로 요청을 보내서 처리하게 된다. 여러 개가 일치할 경우 우선 순위가 있다.
[높음] = (exactly) , 정확히 일치할 경우
ex) location = /
'''
[낮음] / (prefix match), 앞 부분이 일치할 경우, 여러 개가 충돌할 경우 긴 것이 적용(longest first)
ex) location /
위 사용법에 따라 location을 작성하여 주면, nginx.conf에 설정한대로 reverse proxy가 동작한다.

  • 프론트에서 백엔드로 요청을 보낼때 프론트에서 보낸 요청은 먼저 엔진엑스(web server)를 거쳐서 간다. 그러면 엔진엑스가 설정되어 있는 파일(/etc/nginx/conf.d/default.conf)을 보고 백엔드로 리버스 프록시를 하게된다.
    이때 프론트에서 지정한 경로는 /api/.. 이지만 우리가 설정한 백엔드 주소들은 /api/라는 경로가 설정되어 있지 않기 때문에 proxy_pass의 백엔드 주소에서 http://<백엔드 주소>/ 보내줘야 설정한 주소로 요청을 전달해준다.

  • 코드에 대한 설명은 여기에 자세히 설명되어 있다.

server {
  listen 80;
  client_max_body_size 5M;
  server_name _;

  location / {
    root /usr/share/nginx/html/;
    index index.html;
    error_page 405 =200 $uri;  
    try_files $uri $uri/ /index.html;
  }

  location /api/ {
    proxy_pass http:<백엔드 주소>/;
    proxy_buffer_size   128k;
    proxy_buffers       4 256k;
    proxy_busy_buffers_size 256k;
  }

Web Server는 요청이 들어왔을 때 정적파일을 내어주거나 다른 서버로 Reverse Proxy를 하는 등 안내데스크와 같은 역할만 한다. 흔히 알고있는 백엔드의 경우 WAS에 해당되며, 요청에 따라 실행되어야할 서버사이드 로직이 실행된다. Web Server를 가장 앞단에 두어 프론트엔드는 정적으로 서비스하고 백엔드는 Reverse Proxy로 서비스하는 형태가 되는 것이다.

Dockerfile 작성

npm이 아닌 yarn을 사용하여 프로젝트를 빌드할 예정이다. npm과 yarn은 자바스크립트 런타임 환경인 노드(Node.js)의 패키지 관리자이기 때문에 node 이미지를 사용하여 프로젝트를 빌드한다. 이후 빌드한 프로젝트 파일들을 nginx에 옮겨 실행시킬 것이다. 이때 설정해두었던 nginx.conf 파일을 적용시켜서 프로젝트를 실행 시키도록 하였다.

💡npm vs Yarn 두 패키지 매니저 차이점

  • Speed (Performance)
    npm은 필수 단계를 순차적으로 수행하는 경향이 있어서 다음 패키지로 넘어가기 전에 각 패키지를 완전히 설치해야 한다고 한다. 하지만 Yarn은 동시에 여러 패키지들을 설치할 수 있기 때문에 속도 면에서 크게 향상된다는 것이다. 그런데 이 속도의 문제도 npm 5.0 아래의 버전으로 놓고 봤을 때의 문제이다. 이제는 거의 차이가 없어졌다고 볼 수 있는 문제인 것 같다.

  • Security
    npm은 의존 관계를 가지는 다른 패키지들이 즉시 포함되도록 한다. 이런 부분이 더 편리하긴 한데 보안 문제에 있어 여러 취약점들을 불러올 수 있다고 한다.
    반면에 Yarn은 yarn.lock이나 package.json 파일에 있는 것들만 설치를 한다. 이런 방식은 모든 디바이스에 같은 패키지들을 설치하는 것을 보장하기 때문에 디바이스마다 다른 버전을 설치해서 발생할 수 있는 버그들을 많이 줄였다는 것이다.
    이 보안성 문제가 npm과 yarn을 비교할 때 아주 중요한 측면이라고 할 수 있고, 이 부분은 계속 강화되고 있다고 하니 보안성을 따질 때는 yarn이 더 좋다고 말할 수 있다.

  • 패키지 잠금 파일
    npm은 package-lock.json, yarn은 yarn.lock 파일을 패키지 잠금 파일로 사용한다.
FROM node:16 AS builder
WORKDIR /app
COPY package.json ./
COPY yarn.lock ./
RUN yarn install 
COPY . .
RUN yarn build


FROM nginx:stable-alpine
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
ENTRYPOINT ["nginx", "-g", "daemon off;"]

yarn으로 빌드한 파일들을 엔진엑스에 옮겨주고 설정해둔 nginx.conf 파일을 적용시켜 nginx를 실행한 이미지를 생성해준다.

profile
전공 소개

0개의 댓글