무중단 고가용성 앱 배포, 서비스를 끊기지 않게 제공

다양한 앱 배포

웹어플리케이션

웹에서 동작하는 어플리케이션

웹어플리케이션 프레임워크

앱 제작 가이드 또는 툴

  • 자바 - spring(java + tomcat 서버 필요), springboot(버전이 맞는 java 필요, tomcat 내장)
    • openjdk + tomcat
  • 파이썬 -  fastapi, flask, django
    • python 및 의존성 파일 설치
  • 자바스크립트 - nodejs(명확히 따지면 프레임워크가 아님)
    • nodejs 및 npm, 의존성 파일 설치

소스코드를 앱으로 만들려면?

# java 웹프레임워크인 springboot로 짜여진 소스코드가 있다면, maven이나 gradle같은 빌드툴과 자바가 설치된 상태에서 'build'를 통해 war이나 jar같은 압축 파일을 만들어내면 된다.

# 자바의 경우, 자바의 버전(ex. openjdk-11 같은)과 빌드툴의 버전도 맞춰줘야 한다.


1. 물리서버에 간단한 springboot 앱을 배포

vm, IP : 211.183.3.150

소스코드 준비

git clone https://github.com/oolralra/sb_code.git

# sb_code라는 디렉토리에 소스코드가 존재
# pom.xml, README.md 참고

 

자바설치

# 내가 설치하고 싶은 버전이 설치가능한지 확인

  • 자바8 설치
root@ubun-tem:~/sb_code# apt install -y openjdk-8-jdk

빌드툴 설치

springboot1.x 버전은 maven 3.x 버전의 빌드툴을 쓰면 된다는것을 경험을 통해 터득한 상태

  • apt search maven 을 통해 어떤 버전의 maven인지 확인

  • 설치
root@ubun-tem:~/sb_code# apt install -y maven
  • 설치 됐는지 확인

빌드

  • 빌드 전 : target 폴더, jar파일이 존재하지 않는다.

# tom

root@ubun-tem:~/sb_code# mvn clean package

# 이 패키징된 앱(springbootApp.jar)을 springboot이므로 java가 설치된 환경에서 실행시켜면 된다.

  • target 폴더에 존재하는 springbootApp.jar 앱을 동작
root@ubun-tem:~/sb_code# java -jar target/springbootApp.jar

# 이 앱은 아까 README.md 에서 포트가 8085인걸 확인함

# 만약 README 정보가 없다면, 소스코드를 뒤져서 찾아야한다.

앱 동작


2. 이 소스코드(앱)를 컨테이너화

실습

방법1) 호스트에서 빌드한 다음 app 파일을 컨테이너 이미지에 넣어서 실행
방법2) 빌드도 컨테이너에서 한 후 app을 동작
  • new 컨테이너 생성 및 실행
root@ubun-tem:~/sb_code# docker run -it --name new openjdk:8-jdk

# 컨테이너 안에서 각종 명령어 실행 불가, ctrl+pq로 나옴

  • 소스코드 준비
root@ubun-tem:~# cd /docker
root@ubun-tem:/docker# mkdir source
root@ubun-tem:/docker# cd source
root@ubun-tem:/docker/source# git clone https://github.com/oolralra/sb_code.git
  • 깃클론한 내용을 컨테이너 안으로 복사
root@ubun-tem:/docker/source# docker cp sb_code/ new:/
  • 컨테이너 내부로 접속
root@ubun-tem:/docker/source# docker exec -it new bash

방법1) 호스트에서 빌드한 다음 app 파일을 컨테이너 이미지에 넣어서 실행

root@ubun-tem:~/sb_code# vi Dockerfile
FROM openjdk:8-jdk
WORKDIR /
COPY target/springbootApp.jar app.jar
CMD ["java", "-jar", "app.jar"]
root@ubun-tem:~/sb_code# docker build -t spring-app .
root@ubun-tem:~/sb_code# docker run -dp 8282:8085 --name springapp spring-app


방법2) 빌드도 컨테이너에서 한 후 app을 동작

root@ubun-tem:~/sb_code# vi Dockerfile
# 1단계: 빌드
FROM openjdk:8-jdk
WORKDIR /app
COPY . .
RUN cd sb_code/ && mvn clean package

# 2단계: 실행
FROM openjdk:8-jdk
WORKDIR /
COPY target/springbootApp.jar app.jar
CMD ["java", "-jar", "app.jar"]
root@ubun-tem:~/sb_code# docker build -t spring-app2 .
root@ubun-tem:~/sb_code# docker run -dp 8585:8085 --name springapp2 spring-app2


풀이1)

방법1) 호스트에서 빌드한 다음 app파일을 컨테이너이미지에 넣어서 실행
root@host:~# git clone https://github.com/oolralra/sb_code.git
root@host:~# cd sb_code
root@host:~/sb_code# apt update -y && apt install -y openjdk-8-jdk maven

 

만약 동일한 서버에 버전이 다른 자바가 설치되어 있다면,

1. 내가 원하는 자바 버전을 선택

update-alternatives --config java

# 변경된 자바 버전을 확인

2. 아니면 11버전을 깔끔하게 삭제하면되는데, 잘 안된다.

  • 빌드
root@host:~/sb_code# mvn clean package

root@host:~/sb_code# java -jar target/springbootApp.jar

# 보통 에러가 뜨는 원인은, 자바 버전이 다르거나 포트를 이미 사용중인 경우, 혹은 의존성 파일같은것들이 잘 설치가 안된경우. 페이지를 올려보면서 에러 원인을 찾아내야 한다.

# 도커허브에서 내가 원하는 이미지의 레포를 검색해서 이동

# 8-jdk-alpine

# alpine : 초경량화 버전

# slim : 불필요한 파일을 제거한 경량화 버전

# 8-jdk : 일반, 제일 무거움

vi Dockerfile
FROM openjdk:8-jdk-alpine
WORKDIR /app
COPY target/springbootApp.jar app.jar
CMD ["java","-jar","app.jar"]
root@host:~/sb_code# docker build -t sb:1 .
root@host:~/sb_code# docker run -dp 8086:8085 --name sbapp sb:1

# 잘 동작하는걸 확인

  • alpine 버전은 쉘이 bash가 아니라 sh
root@host:~/sb_code# docker exec -it sbapp sh

# 만약에 안되면 docker logs로 트러블슈팅

풀이2)

방법2) 빌드도 컨테이너에서 한 후 app을 동작시키는 컨테이너 이미지 실행
  • 빌드하기에 적절한 이미지를 선택
  • 소스코드에 대한 이해가 필요 = maven3.6과 openjdk-8 이 필요한 소스코드

# 위의 소스코드를 빌드할 수 있는 컨테이너이미지

root@host:~/sb_code# vi Dockerfile
FROM maven:3.6.3-openjdk-8-slim
WORKDIR /app
COPY . .
# 호스트의 Dockerfile의 위치에 있는 모든 파일이
# 컨테이너 내부의 /app 에 복사가 됨.
RUN mvn clean package
# target 폴더에 springbootApp.jar 생성
CMD ["java","-jar","target/springbootApp.jar"]
    • 컨테이너 생성
root@host:~/sb_code# docker build -t sbimage:1 .
root@host:~/sb_code# docker run -dp 8989:8085 --name app sbimage:1


dockerignore

컨테이너 이미지 빌드시 넣지 않고 싶은, 불필요한 파일을 제외시킬 수 있다.

root@host:~/sb_code# vi .dockerignore

  •  ls로는 안보이고 ls -al 로 볼 수 있음

  • 이미지 빌드후 컨테이너 실행해서 정말 안들어갔는지
root@host:~/sb_code# docker build -t sbimage:2 .
root@host:~/sb_code# docker run -itp 8989:8085 --name app sbimage:2 bash

방법1이든, 방법2든 우리에게 필요한 파일은 앱파일(springbootApp.jar) !


nodejs 웹어플리케이션을 배포

root@host:~/sb_code# cd ~
git clone https://github.com/earth-space/Node.js-small-project-example
root@host:~# mv Node.js-small-project-example/ node

# mongoDB: noSQL, port 27017, listen port 3000/3001

# 사용하는포트(=접속할포트) : 3000

# 몽고디비를 연동할 포트는 27017번


1. 몽고디비 컨테이너를 띄울예정

root@host:~# docker run -dp 27017:27017 --name mongo mongo:latest

# 잘 동작중인걸 확인

root@host:~# docker exec -it mongo mongosh

# 몽고디비는 mongosh 라는 쉘을 쓰므로 접속이 되는지 여부만 체크

# 나중에 앱을 연동을 시키면 아마 node라는 db가 생성될것

 

nodejs 앱은 물리서버에 띄워보자.

  • nodejs와 npm(node package manager): 개발환경 구성
root@host:~# cd node/
root@host:~/node# apt update -y && apt install -y nodejs npm
  • 앱동작에 의존성 패키지 설치
root@host:~/node# npm install express

# 이 목록을 npm install 이라는 명령으로 설치 가능

  • 패키지 설치
root@host:~/node# npm install
  • app.js 동작
root@host:~/node# node app.js

# 수정할 소스코드 파일을 찾아야 한다

root@host:~/node# vi views/home.ejs

  • 수정 후 다시 동작
root@host:~/node# vi app.js
root@host:~/node# node app.js

# app.js 파일의 mongodb 주소도 수정
# 채팅서버가 mongodb를 사용하므로 잘 동작하면 몽고디비와 연동도 잘 됐음을 확인 가능


실습)

nodejs앱을 컨테이너화하기
root@ubun-tem:~/node# vi Dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node","app.js"]
root@ubun-tem:~/node# docker build -t nodejsapp .
root@ubun-tem:~/node# docker run -dp 3000-3001:3000-3001 --name nodejs nodejsapp
  • 도커 전부 실행
docker start $(docker ps -aq)

views/home.ejs 에서 컨테이너주소를 적어도 찾아갈 수 있게 만들 수 있는지

풀이)

root@host:~/node# vi app.js

  • 외부로 publish 하지 않고 띄우자
root@host:~/node# docker run -d --name mongo mongo:latest
root@host:~/node# vi views/home.ejs

# 내부끼리 통신을 할때는 내부주소로 찾아가는게 좋긴 하지만, 이 앱 자체가 그렇게 만들어져있지 않기때문에 어쩔수 없이 앱내부에서 통신을 할때도 외부주소를 명시해서 쓸 수 밖에 없다.

root@host:~/node# vi .dockerignore
node_modules
Dockerfile

# 설치된 패키지들을 넣는게 아니라, 소스코드의 package*.json 파일의 설치목록을 통해 컨테이너 내부에 패키지를 설치할것이기 때문에 node_modules를 제외시켜주자

# 컨테이너이미지를 찾기위해 nodejs의 버전을 확인

  • node 레포에서 10.19 태그로 검색

root@ubun-tem:~/node# vi Dockerfile
FROM node:10.19-slim
WORKDIR /app
COPY package*.json .
# 일단은 설치할 패키지목록만 복사
RUN npm install
# 패키지 설치
COPY . .
# 소스코드 복사
CMD ["node","app.js"]

    • Dockerfile에서 굳이 패키지목록을 미리 복사해서 설치하고 난 다음에 소스코드를 복사하는 이유
      → 레이어를 잘 나누면 시간을 세이브할 수 있다.

root@ubun-tem:~/node# docker build -t node:1 .
root@ubun-tem:~/node# docker run -dp 3000-3001:3000-3001 --link mongo --name node node:1

# todo가 몽고디비와 연동하기때문에 todo가 잘 동작하는걸 확인

하나 통으로 모놀로식이 아닌

api 하나하나로  MSA(MicroService Architecture) 구조로 나눠야 한다!