Search
Duplicate

스프링부트 어플리케이션을 도커컨테이너에 배포 해보자

이번에는 스프링부트 어플리케이션을 개발 한 후, 도커 컨테이너에 배포하는 과정을 소개하겠다.
어플리케이션을 jar로 배포하면 간단한데, 굳이 도커를 사용하는지 의문이 들 수 있다.
도커를 왜 사용하는지 설명부터 하면 좋겠지만, 이미 좋은 글들이 많기 때문에 자세한 내용은 각자 찾아보자.
몇 가지 이유를 소개하자면, 어플리케이션이 동작하는 환경 자체를 개발 환경과 운영 환경을 동일하게 구성을 할 수 있다.
도커 컨테이너로 관리를 하게되면, 해당 서비스에 대한 트래픽이 증가하였을 때
보다 효율적으로 서비스를 확장(스케일 아웃)을 하기 좋다.
해당 포스팅에서는 도커 컴포즈, 쿠버네티스는 소개하지 않고 단순하게 도커 이미지 배포, 컨테이너 실행까지만 설명하겠다.

IDE 다운로드

개발 PC에 JDK는 설치가 되어있다고 가정한다.
IDE는 어떤 것을 사용해도 상관없다. 작성자는 무료로 사용할 수 있는 STS를 선택하였다.
먼저 아래 링크로 이동 후, 본인의 맞는 OS의 최신버전 STS를 설치 한다.

기본 개발환경 설정

프로젝트가 위치 할 워크스페이스 경로를 입력한 후 Launch를 클릭한다.
작성자는 UI를 최대한 Simple하게 설정하는 것을 선호한다.. 지금 상태는 마음에 들지 않는다.
각자 취향에 맞게 환경설정을 바꾸자.
이제 마음에 든다.

스프링 부트 프로젝트 생성

'Spring Starter Project' 선택.
적당하게 설정값을 입력하자. 평소에 Maven 프로젝트로만 진행하였는데,
공부하는 차원으로 'Gradle'을 선택했다.
해당 포스팅에서는 단순한 API 하나만 구현할 것이기 때문에 이정도만 선택 후, Finsh 클릭.
Finsh 클릭.
'Progress' View를 보면 진행상태를 알 수 있다.
프로젝트 구성이 완료 되었다.

[번외] Code Fomatter를 적용해보자

협업을 하게 되면 각 팀원들의 다른 코딩 습관으로 인하여, 코드자체의 일관성이 없어진다.
예를 들어 들여쓰기의 경우 space 2개, 4개 tab 등 사람 by 사람으로 다르게 사용할 수 있다.
이러한 문제를 해결하기 위해 팀의 컨벤션(규칙)을 정하는 경우도 있는데, 이 때 컨벤션 자체를 File로 생성하여 팀에 배포를 할 수 있다.
여기서는 Google에서 배포하는 Fomatter를 내려 받아 적용해보겠다.(꼭 Google일 필요는 없다. 취향에 맞는 Stye을 찾아서 적용해도 된다.)
git clone으로 프로젝트를 받거나, Download ZIP을 클릭하여 내려 받는다.
Import 버튼을 클릭한다.
eclipse-java-google-style.xml을 클릭한 후 Open 클릭.
Apply and Close 클릭.
Java > Editor > Save Actions > 화면과 같이 체크 후 Apply and Close 클릭.
코딩 컨벤션을 조금이라도 신경 쓰는 사람이라면 굉장히 불편할 것이다.
파일을 저장하면 자동으로 Formatter가 적용된다.
만약 들여쓰기를 Tab으로 변경하고 싶다면, Formatter 설정으로 돌아가서 Edit 하면 된다.

자동으로 생성된 Boot Application 이름 바꾸기

이건 해도 안해도 그만이다. 개인적으로 Boot Application 클래스 이름을 'App'으로 변경하는 것을 좋아한다.
이름을 변경할 클래스를 우클릭한 후 Rename을 해준다.
대충...해당 클래스를 참조하는 부분도 같이 바꿀건지 .. 그냥 Finish 눌러준다.
Test Class도 같이 바꿀꺼냐.... Yes를 눌러준다.
뭔가 깔끔해진 것 같지 않는가....

테스트 용도의 API 구현

이 정도는 그냥 타이핑을 하는걸로...

어플리케이션 구동 및 테스트

Boot Dashboard > 프로젝트 우클릭 > start 클릭.
특별한 경우가 아니면 정상적으로 8080 Port로 Listen 될 것이다.
여기서 어플리케이션이 올라가지 않는 경우... 이 정도는 본인이 알아서 해결하자.
브라우저에 127.0.0.1:8080 접속 시 정상적으로 응답을 한 것을 확인 할 수 있다.
이제 매우 간단한 API 서버 소스가 준비되었다. 먼저 jar 파일로 빌드를 하자

jar File로 Build 하기.

build.gradle 파일을 연다.
archivesBaseName = '이름' 속성을 추가한다.
이렇게 설정할 경우, Build된 File의 이름은 'msa-0.0.1.jar' 으로 설정된다.
Gradle Tasks View를 추가한다.
Gradle Tasks > 프로젝트 > build > build 클릭.
콘솔창에 빌드 과정이 출력 된다.
만약 Package Explorer에 build 디렉토리가 보이지 않는다면 점3개 아이콘 > Filter를 클릭한다.
'Gradle build folder' 체크 해제 후, OK 클릭
build > libs 디렉토리에 Build 된 jar 파일을 확인 할 수 있다.
터미널에서 'java -jar ㅡmsa-0.0.1.jar' 명령어를 입력 하면 이 어플리케이션이 구동된다.
이제, 이 어플리케이션을 도커 컨테이너로 배포를 해보자.

Dockerfile 만들기

프로젝트 최상단 경로에 'Dockerfile'이름으로 파일을 생성한다.
반드시 'Dockerfile'이라고 이름을 지어야한다.
Dockerfile 내 아래 내용을 C&P를 하자.
(만약 jar 파일 명을 다르게 설정되어 있다면, 그에 맞도록 입력한다.)
FROM openjdk:11 EXPOSE 8080 ADD build/libs/msa-0.0.1.jar msa-0.0.1.jar ENTRYPOINT ["java","-jar","msa-0.0.1.jar"]
Docker
복사
간단하게 설명하자면.. openjdk:11은 openjdk11이 설치가 된 기본 OS 환경을 의미하고
EXPOSE 8080는 Host OS와 연결할 포트를 의미하고.. 쉽게 생각하면
공유기에서 포트포워딩 설정 시 내부 포트를 정의하는것과 유사하다.
ADD에 정의한 파일을 이미지를 build 할 때 추가하겠다라는 보면 된다.
ENTRYPOINT는 컨테이너가 실행될 때 먼저 실행하는 명령어를 배열 형태로 정의한 것이다.

Docker 설치

별거 없다. 본인 OS에 맞는 Docker Desktop을 설치하자.
Windows의 경우 WSL을 설치하라고 나올 텐데, 가이드에 따라서 설치를 진행하자.

Local Docker Image Build & Containder Run

본인의 경우 CLI를 선호 하기 때문에 CLI Command로 설명하겠다.
(Windows 환경인 경우 powershell 터미널을 활용.)
프로젝트가 위치한 디렉토리로 이동한다. (Dockerfile이 존재하는 경로)
아래 명령어를 실행하면 현재 경로에서 Dockerfile을 찾은 후
Dockerfile에 정의한 설정을 이미지로 Build 한다.
# docker build -t msa-0.0.1 . docker build -t '로컬 리파지토리명' .
Bash
복사
성공적으로 빌드를 하였다.
Build한 이미지를 컨테이너에 올려보자.
docker run -p 8080:8080 msa-0.0.1
Bash
복사
브라우저 주소창에 127.0.0.1:8080을 입력하면 API 서버로 부터 정상적으로 응답을 받은 것을 확인할 수 있다.
이전에 개발환경에서 직접올렸을 때는 어플리케이션이 구동되는 환경이
Host OS (작성자의 경우 MacOS)이었고,
현재는 Docker 엔진위에 올라간 Container에서 구동되고 있는 것이다.

Dockerhub에 배포를 해보자.

Github와 유사한 Dockerhub이다.
해당 클라우드 리파지토리에 자신만의 이미지를 배포 할 수 있고, 오픈소스 이미지를 받을 수도 있다.
계정을 생성하자.
계정을 생성하였으면, 터미널에서 Docker Login을 할 수 있게 Access Token을 발급하자.
로그인을 한 후 본인 ID 클릭 > Account Setting
Security > AccessTokens > New Acess Token 클릭 후 사용 용도(메모 개념)을 입력하면 Token이 발급된다.
Token이 발급 되면 복사를 해두자.

Dockerhub에서 리파지토리 생성

깃허브 리파지토리 생성과 동일하다.
기본적으로 한 개의 프라이빗 리파지토리를 제공한다. 일단 여기서는 퍼블릭을 선택하자.

본인의 공개 리파지토리에 Docker Image 배포하기

# 현재 로컬에 Build 되어 있는 이미지 리스트를 출력 docker images # Docker Hub에 로그인한다. 작성자의 경우 이미 로그인을 하였다. # 최초 로그인 시 dockerhub 가입시 입력한 name을 입력하고, # 비밀번호는 이전에 발급한 Access Token을 입력하면 된다. docker login
Bash
복사
# 본인이 설정한 값을 대입하여 명령어 실행 # docker tag msa-0.0.1 parkjangwon/msa-demo docker tag '로컬 app 이미지 리파지토리명' '본인의 docker-hub useranme/리파지토리 명' # 정상적으로 로컬 이미지가 태깅이 된 것을 확인한다. docker images # Docker Hub에 push 한다. # docker push parkjangwn/msa-demo docker push '본인의 docker-hub useranme/리파지토리 명'
Bash
복사
정상적으로 push가 된 것을 확인 할 수 있다.

Docker Hub에 배포한 이미지를 받아 컨테이너로 실행 해보자.

먼저 로컬 컨테이너 및 이미지를 모두 삭제하자.
# 컨테이너 리스트 확인 doker ps -a # 컨테이너 삭제 docker rm CONTAINER ID # 이미지 리스트 확인 docker images # 이미지 강제 삭제 docker rmi -f IMAGE ID
Bash
복사
정상적으로 삭제가 된 것을 확인하고, 원격 리파지토리에서 이미지를 가져온 후
바로 컨테이너로 실행을 해본다.
# 컨테이너 리스트 확인 doker ps -a # 이미지 리스트 확인 docker images # 컨테이너를 실행할 때 로컬에 해당 이미지 리파지토리가 없다면, Docker Hub 리파지토리에서 이미지를 가져와 빌드한 후 컨테이너를 실행한다. # docker run -p 8080:8080 parkjangwon/msa-demo docker run -p Host Port:Container Port '본인의 docker-hub useranme/리파지토리 명'
Bash
복사
브라우저에 127.0.0.1:8080을 입력해서 접속 테스트를 해본다.
이전과 다르게 로컬 이미지는 없고, Docker Hub 리파지토리에서
가져온 이미지만 존재하는 것을 확인 할 수 있다.
컨테이너를 실행 할 때 몇가지 옵션을 추가하면 좀 더 편리하게 컨테이너를 관리 할 수 있다.
# AS-IS docker run -p Host Port:Container Port '본인의 docker-hub useranme/리파지토리 명' # TO-BE docker run --name 컨테이너이름 -d -p Host Port:Container Port '본인의 docker-hub useranme/리파지토리 명' # 편리한 옵션들... --restart always : 도커 데몬 실행 시 해당 컨테이너를 run 한다. --name : 컨테이너 이름 -d : 데몬 형태로 실행 (백그라운드) -p : 외부포트:도커 내 내부포트 = 포트 포워딩 유사 -v : 호스트 디렉토리를 도커 컨테이너에 마운팅 -v "호스트 OS에 도커 데이터를 저장할 디렉토리/":"도커상의 데이터가 떨어지는 디렉토리/" -e : 환경변수 -e ENV1=VALUE1 # echo $ENV1 # VALUE1
Bash
복사
이번 컨테이너는 이름을 지정해줬기 때문에 보다 더 간단하게 컨테이너를 관리 할 수 있고,
-d 옵션을 주었기 때문에 데몬 형식으로 돌고 있다.
컨테이너에 대한 로그를 확인 할때는 'docker log -f 컨테이너 이름' 명령어를 입력하면 된다.

원격 서버에 이미지를 내려 받은 후 컨테이너 구동

당연한 얘기지만 해당 서버에 Docker를 설치한다.
(이미 설치가 된 서버이기 때문에 설치과정은 Skip.)
# Local 환경에서 했던 로그인 방식과 동일하다, 다만 Access Token을 해당 서버용으로 하나 더 발급하자. docker login
Bash
복사
현재 서버에서 사용하지 않은 Port로 컨테이너를 올려보자.
물론 해당 포트에 대한 방화벽 설정은 해줘야한다.
docker run --name 컨테이너이름 -d -p Host Port:Container Port '본인의 docker-hub useranme/리파지토리 명'
Bash
복사
Docker를 사용하여, 로컬 PC에서 개발한 어플리케이션을 이미지로 빌드 후 해당 이미지를 기반으로 컨테이너 형태로 서버에 배포를 하였다.
컨테이너 형태로 배포를 할 경우 Windows Server에 배포를 하던, ubuntu에 배포를 하던, centos7에 배포를 하던 배포한 어플리케이션이 동작하는 환경은 모두 동일하다.
(Dockerfile에 정의한 환경의 컨테이너에서 동작)
이 점을 통해 운영환경과 개발환경의 차이에서 발생하는 이슈를 없앨 수 있다.
예를 들어 여러 어플리케이션이 구동되는데 각 어플리케이션이 같은 종류이면서 다른 버전의 라이브러리를 참조한다던가.. 그런 경우 환경 구성이 다소 난해하다.
각 컨테이너는 독립적이다. 때문에 각 어플리케이션을 컨테이너로 구동시켰다면, 각 어플리케이션이 참조하는 라이브러리의 충돌을 피할 수 있다.
또한, Host Server에 Image가 빌드 되어 있다면, 'docker run' 명령어를 통해 동일한 컨테이너를 하나 더 올려, 서버어플리케이션을 간단하게 증설할 수 있다.
이 포스트에는 아주 기초적인 방법만 작성하였으니, 구글링을 통하여 보다 더 효율적으로 도커를 활용해 봤으면 좋겠다.