소프트웨어 개발에서 시스템 설계는 서비스의 성능, 확장성, 유지보수에 직접적인 영향을 미칩니다. 그중에서도 모놀리식 아키텍처(Monolithic Architecture) 마이크로서비스 아키텍처(Microservices Architecture) 널리 사용되는 설계 방식입니다. 글에서는 가지 아키텍처의 개념, 장단점, 그리고 언제 적용하면 좋은지 살펴보겠습니다.

모놀리식 아키텍처

모놀리식 아키텍처는 하나의 애플리케이션이 서비스 전체 기능을 처리하도록 설계한 방법입니다. 그렇기 때문에 전반적으로 서비스 구조가 매우 간단하며 이 간단한 구조 덕분에 애플리케이션 운영과 개발이 편리하다는 장점이 있습니다. 소규모 개발 팀이 비교적 간단하고 작은 기능을 제공하는 서비스를 개발할 때 효율적입니다.

장점

  • 개발자는 하나의 데이터 저장소에 하나의 애플리케이션만 개발하면 되므로 데이터를 처리하는 일에만 집중하면 된다.
  • 하나의 서비스안에서 클래스들간에만 데이터를 주고 받으면 되기 때문에 데이터가 유실될 걱정이 없다. (MSA 방식은 분산된 서비스들 간에 데이터를 주고받아야 하기 때문에 네트워크를 통해 전송된다.)
  • 시스템 장애나 기능에 버그가 생기면 하나의 애플리케이션에서만 원인을 파악하면 되기 때문에 고치기 쉽다.
  • 데이터 저장소가 하나이므로 트랜잭션 기능을 쉽게 사용할 수 있으며 일관성을 유지하기 쉽다.
  • 테스트 환경을 쉽게 구성할 수 있으며, 유닛 테스트나 통합 테스트를 작성하기 편하다.

단점

모놀리식 방식은 구조가 간단하지만 서비스 규모가 커지면 하나의 애플리케이션 서버에서 여러 기능을 제공해야 하므로 오히려 구조가 복잡해질 수 있습니다. 뿐만 아니라 서비스 확장 측면에서 단점이 명확합니다. 예를 들어 하나의 애플리케이션 서버가 상품 조회 기능과 상품 검색 기능을 모두 처리한다고 가정하고 호출 비율은 상품 조회가 10배 많다고 가정해 봅시다. 이럴 경우 상품 조회 기능만 서비스를 확장하면 될텐데 모놀리식 방식은 서버가 하나이기 때문에 잘 사용하지 않는 기능까지 확장하게 되고 그렇게 되면 리소스가 낭비가 발생할 수 있습니다.

마이크로서비스 아키텍처

마이크로서비스 아키텍처는 분산 시스템 아키텍처 중 하나로 서비스 전체 기능을 독립된 작은 단위로 나누어 분리하고, 각각의 컴포넌트는 독립된 시스템 형태로 개발하고 운영합니다. 이렇게 기능단위로 분리된 애플리케이션들을 마이크로서비스라고 하고 미리 정의된 인터페이스를 통해 서로 유기적으로 동작합니다. 아래는 마이크로서비스 어키텍처가 갖는 특징들입니다.

  • 잘 분리된 마이크로서비스로 인한 탈중앙화
  • 대규모 시스템을 위한 아키텍처
  • 가벼운 네트워크 프로토콜
  • 느슨한 결합
  • 서비스지향 아키텍처

장점

  • 독립성, 기능단위로 독립된 마이크로서비스들간의 간섭이 최소화된다.
  • 각각의 마이크로서비스는 독립된 데이터 저장소를 갖고 있기 때문에 대용량 데이터를 저장하고 처리하는 데 용이하다.
  • 기능단위로 분리되어있기 때문에 배포주기가 빠르다.
  • 마이크로서비스 단위로 확장할 수 있어 서비스 전체적으로 확장성이 좋다. 또한 필요한 마이크로서비스만 확장할 수 있어 효율적으로 시스템 자원을 사용할 수 있다.
  • 사용자 반응에 따라 서비스를 고도화하거나 제외할 수 있기 때문에 사용자 반응에 민첩하게 대응할 수 있다.
  • 특정 서비스에 장애가 발생해도 다른 서비스에 영향을 주지 않는다. (잘 설계되고 독립되어있다고 가정)

단점

  • 진입장벽이 높다. 기능별로 분리된 서비스들이 네트워크상에 분산되어 있어 여러 상황을 고려해야 한다. 특히 각각의 서비스가 독립된 데이터 저장소를 가지고 있기 때문에 데이터 정합성을 보장하기 어렵다.
  • 운영하기 매우 어려운 아케텍처다.
  • 서비스가 분산되어있기 때문에 에러가 발생했을때 에러 지점을 찾기 어렵다. 또한 서비스간에 의존성을 가지고 있는 경우 하나의 서비스에서 에러가 발생했을때 연쇄적으로 다른 서비스에도 영향을 미칠 수가 있다.
  • 설계하기 매우 까다롭다. 앞의 단점들을 보면 분산된 서비스들간의 관계에서 발생하는 경우가 많다. 그렇기 때문에 설계단계에서 잘 설계해야지 그렇지 않으면 오히려 모놀리식 방식보다 유지보수하기 어려워질 수가 있다.
  • 여러 마이크로서비스를 운영해야하기 때문에 자동화된 시스템이 필요하다. 이런 자동화된 시스템들을 운영하고 유지 보수하는 것도 쉬운 일은 아니다.

정리하면 마이크로서비스들은 각각 다른 마이크로서비스에서 독립적으로 구성되어야 하며 서로 의존성을 최소화 해야합니다. 뿐만아니라 기능과 성격에 맞게 잘 분리되어야하며 너무 작게 혹은 너무 크게 설계되어서는 안됩니다.

 

이렇게 마이크로서비스 아키텍처는 장점도 많지만 단점도 많은 아키텍처입니다. 그렇기 때문에 적절한 상황에 마이크로서비스 아키텍처를 적용 해야합니다. 만약 다음과 같은 상황에 처해있다면 마이크로서비스 아키텍처를 적용해보는 것을 고려해 볼 수 있습니다.

  • 클라이언트 요청이 점점 많아지는데 로드 밸런서로 확장해도 한계가 있을 때
  • 데이터베이스 성능을 높여도 더 이상 성능 개선의 여지가 없을 때
  • 기능 확장 요구가 많지만 현재 시스템 구조로 불가능할 때
  • 소스 코드가 너무 복잡해서 리팩토링이 필요할 때
  • 기능 중 하나라도 변경되면 전체 QA를 해야 할 때
  • 기능을 수정하면 다른 기능에 연쇄적으로 버그가 발생할 때
  • 개발자는 늘었는데, 개발 속도는 이전 같지 않을 때

'Server' 카테고리의 다른 글

Docker 컨테이너로 젠킨스 설치하기  (0) 2023.02.03
도커  (0) 2023.01.03

1. Docker Desktop 설치

먼저 도커 컨테이너를 이용해 Jenkins를 컨테이너화 하고 배포를 진행할 것이기 때문에 각자 운영체제에 맞춰서 Docker Desktop을 설치해줍니다.

https://www.docker.com/products/docker-desktop/

 

Download Docker Desktop | Docker

Docker Desktop is available to download for free on Mac, Windows, or Linux operating systems. Get started with Docker today!

www.docker.com

2. Docker에 젠킨스 설치

docker pull jenkins/jenkins:lts-jdk11

위 명령어를 이용해 도커에 젠킨스 이미지를 다운받습니다. 앞의 jenkins는 만들고자 하는 계정이고 뒤의 jenkins는 레포지토리의 이름을 의미하고 따로 태그를 작성해주지 않으면 자동으로 latest 버전을 설치해줍니다.

설치에 성공하신 분들은 docker desktop의 이미지 탭을 보시면 아래와 같이 잘 설치된 것을 확인할 수 있습니다.

 

설치에 성공했으면 이제 Jenkins 이미지를 가지고 컨테이너를 실행해야 합니다. 터미널에서 아래 명령어를 실행합니다.

docker run -d -v jenkins_home:/var/jenkins_home -p 8080:8080 -p 50000:50000 --restart=on-failure --name jenkins-server jenkins/jenkins:lts-jdk11

명령어를 간단하게 설명하면 run 명령어는 이미지가 설치되어있는지 확인하고 설치되어있다면 그 이미지를 가지고 컨테이너를 실행하는 명령어 입니다.

-d 옵션은 detach 모드로 현재 실행하고있는 터미널과 분리해서 실행하겠다는 의미입니다.

-p 옵션은 publisher 옵션으로 컨테이너 내부에 있는 포트를 컨테이너 바깥쪽에 있는 환경에서 어떻게 접속해서 사용할것인지를 나타내는 설정입니다. 앞의 포트가 호스트 포트(바깥쪽 포트)이고 뒤의 포트가 컨테이너 포트입니다.

-v 옵션은 볼륨으로 도커가 실행되고있는 환경과 도커 내부 환경을 마운트 하는 설정입니다. 도커 내부에서 생성된 데이터는 도커가 삭제되면 해당 데이터도 함께 삭제되기 때문에 따로 데이터를 보관하고자 설정해주었습니다.

--name 옵션은 실행할 컨테이너에 이름을 부여하는 설정입니다. 이름을 따로 설정하지 않으면 도커가 이름을 랜덤하게 생성합니다. 

 

위 명령어를 실행하고 터미널에 docker ps 명령어를 실행하면 현재 실행되고 있는 컨테이너 정보를 얻을 수 있습니다. 현재 jenkins 컨테이너가 잘 실행되고 있는 것을 확인할 수 있네요

3. 젠킨스 접속하기

컨테이너 실행까지 하고 나서 로그를 출력해 보면 초기 비밀번호를 얻을 수 있습니다. 

docker logs CONTAINER ID

docker ps 명령어로 얻은 CONTAINER ID 를 가지고 위 명령어를 실행해 보면 초기 비밀번호를 확인할 수 있습니다. 따로 안전한 곳에 저장해 놓는 것을 추천드립니다.

 

그런 다음 localhost:8080으로 접속하셔서 초기비밀번호를 입력하시면 다음과 같은 화면이 나오는데 모든 Plugin을 설치할 것인지 선택해서 설치할 것인지 묻는 것으로 모든 플러그인 설치를 선택해줍니다.

선택해주고 나서 페이지 순서대로 계정을 생성하고 Jenkins 접속 url을 설정해주고 나면 Jenkins에 접속하실 수 있습니다.

4. 첫번째 Item(Project) 생성

Item은 Jenkins에서 사용하고 있는 작업의 최소 단위를 의미합니다. 접속하신 Jenkins 화면의 Dashboard에서 새로운 Item의 + 버튼을 눌러서 생성해보겠습니다.

Item의 이름을 작성하고 Freestyle project를 선택해줍니다. Item이 생성되면 나오는 화면에서 아래로 내리면 Build Steps이라는 항목이 보이는데 여기서 Execute shell을 선택해줍니다. 지금 만든 아이템에서 이 아이템을 실행하면 지정한 쉘 스크립트가 실행되도록 하기 위함입니다.

 

일단은 지금까지 한 작업이 잘 수행됐는지 확인하기 위해 아래와 같은 스크립트 문장을 작성해 주겠습니다. 

echo "Welcome to my first project using Jenkins"
javac -version

 

스크립트를 작성하고 Apply를 누른뒤 저장하면 아래와 같은 화면이 나올겁니다. 이 화면에서 왼쪽 사이드 탭에서 지금 빌드를 선택해줍니다.

 

 

빌드하고 나면 사이드 바 아래에 위와 같은 탭이 하나 생성되는데 #1 에 마우스를 올렸을때 나오는 메뉴에서 Consol Output을 클릭하면 아래와 같은 화면이 나오고 저희가 작성한 스크립트도 잘 출력된 것을 확인할 수 있습니다. 

5. GitHub 플러그인 확인

이제 GitHub Repository에 있는 Gradle 프로젝트로 빌드를 진행해 보겠습니다. 먼저 젠킨스 페이지에서 플러그인 관리로 들어가서 GitHub plugin 이 설치되어있는지 확인해줍니다.

6. GitHub 설정

Global Tool Configuration 페이지에서 아래와 같이 GitHub 설정을 추가한 후 Apply하고 저장해줍니다.

7. Gradle 플러그인 확인

GitHub와 마찬가지로 플러그인 관리에서 Gradle 플러그인이 설치되어 있는지 확인해줍니다. 만약 빌드 툴로 Maven을 사용하신다면 Maven Integration plugin을 설치하신 후 새로운 Item을 생성할때 Maven Project로 생성해주시면 됩니다.

8. Gradle 설정

Global Tool Configuration 페이지에서 아래와 같이 Gradle 설정을 추가한후 Apply, Save를 수행해 줍니다.

Gradle 버전은 실제 배포할 프로젝트의 버전으로 gradle-wrapper.properties 파일에 명시되어 있습니다.

9. 새로운 아이템 생성

Item 이름을 지정하고 Freestyle project로 프로젝트를 생성해 줍니다.

간단하게 프로젝트 설명을 작성하고 소스 코드 관리 탭에서 Git Repository 정보를 추가해줍니다.

마지막으로 Build Steps 항목에서 Invoke Gradle script를 선택한후 아래 사진처럼 Use Gradle Wrapper를 선택하고 Tasks를 작성해 줍니다. clean build -x text의 뜻은 빌드를 기존에 한 것이 있다면 빌드한 파일을 지운 후 다시 빌드하라는 내용이고 -x test는 테스트를 진행하지 않겠다는 뜻입니다.

 

위 설정을 모두 해주신후 Apply, Save를 수행하고 지금 빌드를 클릭해줍니다.

만약 빌드가 잘됐다면 Console Output을 확인했을때 아래와 같이 빌드가 잘 됐다는 로그를 확인할 수 있습니다.

'Server' 카테고리의 다른 글

[Spring] 마이크로서비스 아키텍쳐 (Micro Service Architecture, MSA)  (1) 2023.02.28
도커  (0) 2023.01.03

컨테이너

컨테이너란 호스트 OS상에 논리적인 구획(컨테이너)을 만들고, 애플리케이션을 작동시키기 위해 필요한 라이브러리나 애플리케이션 등을 하나로 모아, 마치 별도의 서버인 것처럼 사용할 수 있게 만든 것

도커

Docker(도커)는 애플리케이션의 실행에 필요한 환경을 하나의 이미지로 모아두고, 그 이미지를 사용하여 다양한 환경에서 애플리케이션 실행 환경을 구축 및 운용하기 위한 오픈소스 플랫폼. 내부에서 컨테이너 기술을 사용하고 있는 것이 특징

 

도커는 인프라 환경을 컨테이너로 관리한다. 애플리케이션의 실행에 필요한 모든 파일 및 디렉토리들을 컨테이너로서 모아버린다. 컨터이너의 바탕이 되는 Docker 이미지를 Docker Hub와 같은 리포지토리에서 공유한다.

도커의 특징 - 확장성/이식성

  • 도커가 설치되어 있다면 어디서든 컨테이너를 실행할 수 있음
  • 특정 회사나 서비스에 종속적이지 않음
  • 쉽게 개발 서버를 만들 수 있고 테스트 서버 생성도 간편함

도커의 특징 - 표준성

  • 컨테이너라는 표준으로 서버를 배포하므로 모든 서비스들의 배포과정이 동일해짐

도커의 특징 - 이미지

  • 이미지에서 컨테이너를 생성하기 때문에 반드시 이미지를 만드는 과정이 필요
  • Dockerfile을 이용하여 이미지를 만들고 처음부터 재현 가능
  • 빌드 서버에서 이미지를 만들면 해당 이미지를 이미지 저장소에 저장하고 운영서버에서 이미지를 불러옴

도커의 특징 - 환경변수

  • 설정은 보통 환경변수로 제어함
  • MYSQL_PASS = password와 같이 컨테이너를 띄울 떄 환경변수를 같이 지정
  • 하나의 이미지로 환경변수에 따라 동적으로 설정파일 생성 가능

도커의 특징 - 자원관리

  • 컨테이너는 삭제 후 새로 만들면 모든 데이터가 초기화됨
  • 업로드 파일을 외부 스토리지와 링크하여 사용하거나 S3같은 별도의 저장소가 필요
  • 세션이나 캐시를 memcashed나 redis와 같은 외부로 분리

스케줄링

  • 컨테이너를 적당한 서버에 배포해 주는 작업
  • 여러 대의 서버 중 가장 할일 없는 서버에 배포하거나 그냥 차례대로 배포 또는 아예 랜덤하게 배포
  • 컨테이너 개수를 여러 개로 늘리면 적당히 나눠서 배포하고 서버가 죽으면 실행 중이던 컨테이너를 다른 서버에 띄워줌

클러스터링

  • 여러 개의 서버를 하나의 서버처럼 사용
  • 여기저기 흩어져 있는 컨테이너도 가상 네트워크를 이용하여 마치 같은 서버에 있는 것처럼 쉽게 통신

서비스 디스커버리

  • 서비스를 찾아주는 기능
  • 클러스터 환경에서 컨테이너는 어느 서버에 생성될지 알 수 없고 다른 서버로 이동할 수도 있음 따라서 컨테이너와 통신을 하기 위해서 어느 서버에서 실행중인지 알아야 하고 컨테이너가 생성되고 중지될 때 어딘가에 IP와 Port같은 정보를 업데이트해줘야 함
  • 키-벨류 스토리지에 정보를 저장할 수도 있고 내부 DNS 서버를 이용

도커 컨테이너 명령어

docker run

도커 이미지를 사용하여 컨테이너를 만드는 동시에 컨테이너를 실행한다.

docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

옵션 설명
-d 컨테이너를 백그라운드로 실행하며, 실행할 때 container Id를 출력한다.
-h 호스트 이름을 설정한다.
-p 컨테이너 내부 포트와 외부 포트를 매핑하여 포트 포워딩을 한다.
-v 호스트와 컨테이너의 디렉토리를 연결
-e 환경 변수를 설정한다.
--name 컨테이너 이름을 설정한다.
--restart 컨테이너가 있으면 재시작한다.
--rm 컨테이너를 종료한 후 자동으로 컨테이너를 삭제한다.
-it -i 와 -t를 동시에 사용한 것으로 터미널 입력을 위한 옵션
--network 네트워크 연결

docker ps [OPTIONS]

실행 중인 컨테이너 리스트를 출력한다.

-a 옵션을 추가하면 종료 상태의 컨테이너 리스트까지 출력한다.

docker stop [CONTAINER ID or NAME]

실행 중인 컨테이너를 종료 상태로 만든다.

docker start [CONTAINER ID or NAME]

종료 상태인 컨테이너를 실행 상태로 만든다.

docker kill [CONTAINER ID or NAME]

컨테이너를 강제 종료한다.

docker rm [CONTAINER ID or NAME]

컨테이너를 지운다.

docker exec -it [CONTAINER ID or NAME] bash

실행 중인 도커 컨테이너에 접속한다.

도커 이미지 관련 명령어

docker images

도커 호스트 머신에 있는 도커 이미지 리스트들을 확인한다.

docker rmi [option] IMAGE [image ID]

로컬 호스트 머신에 있는 도커 이미지를 삭제한다. 삭제할 도커 이미지 ID를 인자로 입력하고 만약 삭제하려는 이미지가 다른 이미지에 사용되고 있다면 삭제할 수 없다.

-force 옵션으로 강제로 삭제 가능

docker pull [image name]:[tag name]

도커 이미지 저장소에서 이미지 이름, 태그와 매칭 되는 도커 이미지를 로컬 호스트 머신으로 복사한다.

만약 tag name을 생략하면 자동으로 최신(latest) 태그로 복사한다.

+ Recent posts