[Docker] 클라우드 네이티브 애플리케이션과 애플리케이션 실습(Vue.js, SpringBoot, Postgres)

yoonthegarden·2024년 5월 15일
0

Docker

목록 보기
6/9
post-custom-banner

Part5. 컨테이너 애플리케이션 구성

애플리케이션을 이미지로 빌드하고, 컨테이너로 배포해보자.

우선 컨테이너 애플리케이션의 특징을 이해하기 위해 클라우드의 개념을 학습하고 Cloud Native Application 특징과 MSA 아키텍처에 대해 간단히 배워보자.

실습은 애플리케이션을 컨테이너로 빌드하는 것이다! (1. Vue.js → Nginx 2. Springboot → Java 컨테이너. + Postgres 컨테이너로 빌드)

클라우드 네이티브 애플리케이션

클라우드

클라우드는 스토리지 저장소를 의미한다.

Naver Cloud, Google Cloud.. 등 과 같은 클라우드 서비스를 사용하면 내가 원하는 파일을 클라우드에 저장해 놓을 수 있다. 사용한 만큼만 비용을 지불하고, 더 많은 파일을 저장하고 싶을 경우는 저장한 만큼 추가 비용을 지불하면 된다.

업로드한 파일은 실제 어디에 저장될까?

각각의 클라우드 업체들은 자체적으로 서버들을 가지고 있으며, 여기에 모든 사용자들의 파일이 물리적으로 섞여 있는 상태로 저장된다. 즉, 하나의 물리 드라이브에 여러 사용자의 파일이 저장될 수 있는 것이다.

클라우드 서비스를 사용한다는 것은 이런 인프라적인 부분을 신경 쓰지 않고 실제 서비스의 기능만 사용하는 것을 뜻한다.

극단적으로 클라우드 스토리지를 사용하지 않는다고 생각하면 당장 USB부터 들고 다녀야 한다. 따라서 클라우드를 사용하면 물리적인 디바이스에서 자유로워지고, 필요할 때 마다 빠르게 확장할 수 있는 편리함을 누릴 수 있다. (공유 경제와 비슷한 원리이다. 그린카나 쏘카, 에어비엔비 같이 직접 구매하지 않고도 시간당 요금으로 빌리는 원리라고 생각하면 된다.)

클라우드 컴퓨팅은 단순히 스토리지에 국한되지 않고, 전반적인 서버 컴퓨터로 개념이 확장된다. 예를 들어 2코어의 4GB 메모리를 가진 컴퓨터를 사용하고 싶은 경우, 클라우드 사업자의 계정을 만들어 서버를 구매하면 5~10분내로 이 서버에 접속할 수 있다. 클라우드 사업자는 대형 데이터 센터를 지역별로 구축해 놓고 가상화 기술을 활용한다. 사용자가 서버를 구매할 때마다 VM을 한 대 만들어서 접속 정보를 제공해준다.

일반 기업이 서버를 새로 한 대 구매하려면 서버 구매 과정부터 배송, 설치, 유지보수까지 많은 비용과 시간을 소비하게 된다. 더불어 서버 기계 자체의 감가상삭, 유지하기 위한 인력, 전기, 폐기 비용까지 모두 서버 소유자의 몫이 된다. 그래서 기업들은 이런 비용들을 없애고 클라우드 서비스를 사용한다.

따라서 클라우드는 다른 회사 서버를 빌려서 운영하는 것이며,

다른 회사가 모두에게 서버를 빌려줄 경우 → Public Cloud

  • 마이크로소프트 Azure, 아마존 AWS, 구글 GCP가 있다.
  • 어떤 사용자든 계정만 만들면 서비스를 사용할 수 있다.

다른 회사가 특정 조직에게만 서버를 빌려줄 경우 → Private Cloud

  • 조직 내에서 IT 계열사가 제공하는 경우이다.
  • 계열사가 제공하는 서비스는 같은 조직 내에 속해 있는 회사에만 서버를 만들어서 제공한다.
  • 비교적 보안도 뛰어나고, 비용적으로 효율적이다.

클라우드 특징

  • 사용 요청 즉시 서버를 생성해준다. (Provisioning)
  • 실제 사용한 시간 만큼만 비용을 지불한다.

실제 클라우드를 사용하는 핵심적인 이유

→ 현대 애플리케이션이 겪는 다양한 문제들을 클라우드 환경 구성을 통해 해결할 수 있기 때문이다.

  1. 트래픽이 증가할 때 빠르게 대처할 수 있는가? (확장성, Scalability)

    • 현대 애플리케이션은 사용자의 수요 변동이 크다.(커머스, 할인 등 사용자의 요청이 급증할 수 있으며, 회계 시스템 경우 특정 기간 아닐 때는 트래픽이 아예 없을 수 있다.) 이런 트래픽 변동에 유연하게 대처할 수 있는지 중요하다.
    • 트래픽 증가 시 서버를 늘리고, 감소 시 줄여야 하는데 사내에 직섭 서버를 구성할 경우 서버의 용량을 증가하는 것이 쉽지 않다. 따라서 최댓값 예상에 맞춰 구성한다. (서버 다운과 낭비..)

    → 클라우드 환경에서는 서버 추가가 10분내로 이뤄진다. 온프레스미스에서는 서버가 미리 준비되어 있지 않은 경우 새로운 서버를 증가하는데 오랜 시간이 소요된다. (주문, 배송, 설치 등..)
    → 클라우드를 사용하면 확장성을 가질 수 있고 트래픽 증가에 빠르게 대처할 수 있다.

  2. 장애 발생 시 빠르게 복구할 수 있는가? (복원력, Resilience)

    • 클라우드 서버를 운영한다해서 모든 것이 해결되진 않지만, 전 세계의 다수의 데이터 센터를 가지고 있기 때문에 비교적 안전하다.

    → 클라우드 환경에서는 백업 및 복구가 빠르게 이뤄질 수 있다.(Disaster Recovery) 장애에 대응하기 위한 다양한 지역의 서버를 구축할 수 있다.

  3. 운영 비용을 효율적으로 운영할 수 있는가?

    • 전문 아키텍트가 서버 가용량을 적절하게 구성해야 하며, 비용 최적화를 지속적으로 수행해야 한다.

    → 사용한 만큼만 지불할 수 있기 때문에 운영 비용이 더 효율적이다.


클라우드 네이티브

  • 클라우드 : 복잡한 대형 애플리케이션이 겪는 다양한 문제들을 클라우드 환경 구성을 통해 해결한다.
  • 클라우드 네이티브 애플리케이션 : 클라우드 환경을 더 잘 활용할 수 있는 애플리케이션 구조
  1. MSA

    • 트래픽 증가에 빠르게 대처하기 위해선 애플리케이션이 MSA 구조로 개발되어야 한다.

    → 애플리캐이션을 여러 단위로 분리해서 트래픽 증가에 효율적으로 대처하기 위한 소프트웨어 아키텍처이다.

  2. 컨테이너 Container

    • 컨테이너를 활용해 실행 환경에 종속되지 않는 동작이 보장되어야 한다.

    → 컨테이너의 이미지에는 소프트웨어가 실행하기 위한 환경들이 모두 포함되어 있다. 따라서 이미지를 가지고 있으면 어떤 환경에서든 동일한 실행 동작을 보일 수 있다.
    → 컨테이너를 사용하지 않을 경우 각각의 서버의 프로그램을 별도로 운영해야 하고 결국 환경 불일치 문제가 생길 가능성이 크다.

  3. 상태비저장 Stateless

    • 애플리케이션 서버는 상태를 가지지 않아야 한다.
    • 상태를 가지지 않는 애플리케이션은 어디에나 즉시 배포될 수 있고, 여러 대의 서버가 동시에 같은 역할을 하도록 운영할 수 있다.

    → 상태를 가질 경우에는 외부의 이 상태가 분리되어있어야 한다.
    → 상태를 가진다는 것은 결국 각각의 서버가 다르게 동작할 수 있다는 것을 의미한다.
    → 이미지를 컨테이너로 실행할 때 컨테이너에 읽기쓰기 레이어가 생기는 것, 삭제하면 이 읽기쓰기 레이어도 함께 삭제되는 것처럼 컨테이너는 태생적으로 비저장의 특징을 가진다.

  4. DevOps 및 CI/CD

    • 배포가 자동화되어야 하고 빠르게 릴리즈가 수행되어야 한다.

    12factor.net/ko/ : 클라우드 환경에서 운영하는 애플리케이션의 요구 사항에 대해 잘 정리해놨다.


MSA

MicroService Architecture

모놀리식(Monolithic)

  • 애플리케이션을 개발할 때 모든 기능을 하나의 애플리케이션에 구성한다.
  • 하나의 애플리케이션 크기가 커지고 애플리케이션 실행 시간이 오래 걸린다.
  • 트레픽이 증가할 때는 이 트래픽을 받을 수 있는 새로운 서버를 증가시켜야 하는데 서버의 실행시간이 길어지면 트래픽 대처 능력도 떨어진다.
  • 애플리케이션이 크면 클수록 개발에 들어가는 빌드, 배포 시간이 오래걸린다.
  • 서버 증가 시 전체 기능을 늘려야 한다.

마이크로서비스아키텍처(MSA)

  • 각각의 모듈을 나눠서 구성한다.
  • 모듈 크기가 작아졌기 때문에 개발도 간편해지고 서버를 스케일아웃하는 시간도 빨라진다.
    • 스케일아웃 : 서버의 대수를 늘려서 트래픽에 대처한다.
    • 스케일인 : 서버의 대수를 줄이는 것
  • 모듈 별로 다른 언어로 개발 가능하다.

→ MSA의 단점은 복잡성이 높다는 것이다. 하지만 MSA는 클라우드 네이티브에 잘 어울리는 소프트웨어 구조이다. MSA 아키텍처의 장점을 잘 이용하기 위해서 컨테이너를 활용하는 것이 필수이다.

Leafy 애플리케이션 구성

LEAFY : 식물 관리 웹 애플리케이션

  • 식물 정보 제공
  • 나의 식물 리스트
  • 식물 다이어리
  • 물 주기 계산

  • Vue.js 프론트 프레임워크를 사용해 개발, 빌드 시 html, css, js 파일들을 얻을 수 있다.
  • 패키징된 파일들을 Nginx 웹서버에 배포해서 사용자가 웹서버에 접속했을 때 페이지를 제공해준다.
  • SpringBoot 백엔드 프레임워크를 사용해 개발, Tomcat이라는 웹애플리케이션 서버로 실행된다.
  • PostgreSQL는 실제 DB서버이다.

→ DB서버를 구성하고 실행한 뒤, 백엔드애플리케이션, Nginx 서버 순으로 이미지 빌드하고, 컨테이너를 실행시킬 것이다. (Web, WAS, DB 세개의 컨테이너로 애플리케이션이 정상적으로 동작하도록 한다.)

각 모듈을 이미지로 빌드하기전 Leafy 애플리케이션이 잘 실행되는지 확인한다.
이미지는 지금까지 사용한 devwiki에 저장된 Leafy 관련 이미지를 사용할 예정이다.
컨테이너들이 사용할 네트워크를 정의해야 한다.

leafy 애플리케이션이 사용할 네트워크 생성

docker network create leafy-network 

leafy-postgres 컨테이너 생성

docker run -d --name leafy-postgres --network leafy-network devwikirepo/leafy-postgres:1.0.0

leafy-postgres 컨테이너

docker logs -f leafy-postgres
# ctrl + c -> 로그 탈출

→ database system is ready가 나와야 정상적으로 실행된 것
→ DB 실행 전 SpringBoot 애플리케이션 실행 시 에러가 발생한다.

leafy-postgres 컨테이너 생성

docker run -d -p 8080:8080 -e DB_URL=leafy-postgres --network leafy-network --name leafy devwikirepo/leafy-backend:1.0.0

백엔드 컨테이너 로그 조회

docker logs -f leafy

→ Started leafyApplication이 나오면 정상적으로 백엔드 애플리케이션이 실행된 것
→ 애플리케이션이 잘 실행된 것은 DB와 잘 연결되었다는 것

leafy-front 컨테이너 생성

docker run -d -p 80:80 --network leafy-network --name leafy-front devwikirepo/leafy-frontend:1.0.0

→ 세가지 종류의 컨테이너를 실행했다. 프론트엔드 웹서버로 접속해서 파일을 다운로드 받은 후, 이 파일을 출력하고 브라우저를 출력하는 과정에서 필요한 데이터들을 백엔드 애플리케이션의 API 요청을 통해 불러왔다.
→ 백엔드 애플리케이션은 요청을 받을 경우 PostgreSQL로 요청을 보내 저장되어 있는 데이터를 불러오거나, 저장한 후 JSON 응답으로 보내준다.
→ 3가지 모듈들이 유기적으로 상호작용한다.

실습 컨테이너 삭제

docker rm -f leafy-front leafy leafy-postgres

→ 컨테이너를 사용하면 소프트웨어를 실행하기 위해 필요한 것들을 컴퓨터에 설치하지 않고도 애플리케이션을 구성할 수 있다. 컨테이너만 삭제하면 완전히 실행 전과 동일한 컴퓨터 상태로 돌릴 수 있다.
→ 기존에 빌드하고 푸시해놓은 이미지로 애플리케이션을 구성했다면, 직접 컨테이너 이미지를 빌드해볼 것


PostgreSQL 컨테이너 구성

Leafy PostgreSQL 구성 프로세스

  1. OS 구성 및 PostgreSQL 설치
    • PostgreSQL은 데이터를 저장하고 조회할 수 있는 DB이다.
    • Nginx처럼 이미지에 소프트웨어가 포함되어 있기 때문에 별도의 설정없이 PostgreSQL만 실행해도 DB를 사용할 수 있다.
    • 하지만 기본 이미지에는 아무 데이도 없기 때문에 초기 데이터를 구성하는 SQL을 작성해서 PostgreSQL에 전달한다.
    • postgres 13버전으로 베이스를 사용하면 이 안에 기본 OS 파일 시스템 위에 PostgreSQL DB를 설치한 상태로 이미지를 시작할 수 있다.
  2. 환경 설정 파일 작성
    • 설정 파일은 ETC의 PostgreSQL의 Custom.conf 파일로 복사해서 서버를 실행할 때 여기에 있는 설정 파일을 사용하도록 지정할 것이다.
  3. SQL문 작성
    • DB 데이터 초기 세팅
    • SQL문을 실행해서 테이블을 생성하고 데이터를 삽입할 수 있다.
    • 빌드 컨텍스트에 내가 실행하고 싶은 SQL문을 작성한 다음 SQL문을 postgres 이미지 안에 docker-entrypoint-initdb.d 폴더에 넣으면 postgres 이미지가 컨테이너 실행될 때 자동으로 이 폴더 안에 있는 SQL문을 실행한다.
  4. 데이터베이스 실행(postgres)
    • 컨테이너를 실행할 때 postgres를 실행시키기 위해 실행 명령은 cmd에 작성해야 한다. (-f옵션을 통해서 config 파일을 etc/postgresql에 파일로 저장한다.)

실습

git clone https://github.com/daintree-henry/leafy.git
cd leafy/leafy-postgresql
git switch 00-init → 도커파일 작성 직접 할 경우
git switch 01-dockerfile → 도커파일 작성 되어있음

# POSTGRESQL.CONF FILE
# ---------------------

# CONNECTIONS AND AUTHENTICATION
listen_addresses = '*'        # IP 주소, 호스트명 또는 '*'로 모든 IP에 대한 연결을 허용합니다.
max_connections = 100         # 동시 접속자 수 제한
authentication_timeout = 5min   # 인증 시간 초과 시간 (5분)
password_encryption = md5     # 패스워드 암호화 방식

# QUERY TUNING
work_mem = 64MB              # 개별 연결에서 사용 가능한 메모리 양
shared_buffers = 256MB       # 공유 메모리 버퍼 크기
effective_cache_size = 2GB   # 임시 파일 및 인덱스 생성 시 사용할 메모리 크기

# ERROR REPORTING AND LOGGING
log_destination = 'stderr'   # 로그 파일 출력 대상 설정
logging_collector = on        # 로그 수집기를 사용하도록 설정
log_directory = 'pg_log'     # 로그 파일이 저장될 디렉토리 경로
log_filename = 'postgresql-%a.log'  # 로그 파일 이름 지정

# REPLICATION
wal_level = replica          # 스트리밍 복제 구성
max_wal_senders = 5          # 스트리밍 복제 전송자의 최대 수

# PERFORMANCE
effective_io_concurrency = 200   # 파일 I/O 수행을 위해 사용할 동시성 레벨
random_page_cost = 1.1           # 임의 액세스 비용 인덱스 스캔시 고려

→ 원하는 설정 파일을 기본 이미지에 덮어 쓰게 함으로써 원하는 상태의 서버 설정을 만들 수 있다.

-- Users table creation
CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    gender VARCHAR(1) NOT NULL,
    birth_date DATE,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP
);

-- Plants table creation
CREATE TABLE plants (
    plant_id SERIAL PRIMARY KEY,
    plant_name VARCHAR(50) NOT NULL,
    plant_type VARCHAR(50) NOT NULL,
    plant_desc VARCHAR(255),
    image_url VARCHAR(255),
    temperature_low FLOAT NOT NULL,
    temperature_high FLOAT NOT NULL,
    humidity_low FLOAT NOT NULL,
    humidity_high FLOAT NOT NULL,
    watering_interval INT NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP
);

-- User_Plants table creation
CREATE TABLE user_plants (
    user_plant_id SERIAL PRIMARY KEY,
    user_id INT NOT NULL,
    plant_id INT NOT NULL,
    plant_nickname VARCHAR(50) NOT NULL,
    FOREIGN KEY (user_id) REFERENCES Users(user_id),
    FOREIGN KEY (plant_id) REFERENCES Plants(plant_id) ON DELETE SET NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP
);

-- Plant_Logs table creation
CREATE TABLE plant_logs (
    plant_log_id SERIAL PRIMARY KEY,
    user_plant_id INT NOT NULL,
    log_date DATE NOT NULL,
    note VARCHAR(255),
    watered BOOLEAN,
    FOREIGN KEY (user_plant_id) REFERENCES User_Plants(user_plant_id),
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP
);

-- Users 테이블에 데이터 삽입
-- $2a$10$vYR4pPQqR/oZcUDZfXrahecEejQHY0kLkDB5s.FctPRMcEMh1PYhG = password123
-- $2a$10$Vqx3VUuB8gy9NvtKHQARWOOYB2wG4wV2WXy1sdQHIoY8TivSHZ3sC = password456
-- $2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q = password789
INSERT INTO users (name, email, password, gender, birth_date) VALUES
('John', 'john123@qmail.com', '$2a$10$vYR4pPQqR/oZcUDZfXrahecEejQHY0kLkDB5s.FctPRMcEMh1PYhG', 'M', '1988-05-01'),
('Jane', 'jane456@qmail.com', '$2a$10$vYR4pPQqR/oZcUDZfXrahecEejQHY0kLkDB5s.FctPRMcEMh1PYhG', 'F', '1995-08-15'),
('Peter', 'peter789@qmail.com', '$2a$10$vYR4pPQqR/oZcUDZfXrahecEejQHY0kLkDB5s.FctPRMcEMh1PYhG', 'M', '1981-12-25'),
('Susan', 'susan321@qmail.com', '$2a$10$vYR4pPQqR/oZcUDZfXrahecEejQHY0kLkDB5s.FctPRMcEMh1PYhG', 'F', '1990-06-02'),
('David', 'david654@qmail.com', '$2a$10$vYR4pPQqR/oZcUDZfXrahecEejQHY0kLkDB5s.FctPRMcEMh1PYhG', 'M', '1992-03-11'),
('Judy', 'judy987@qmail.com', '$2a$10$vYR4pPQqR/oZcUDZfXrahecEejQHY0kLkDB5s.FctPRMcEMh1PYhG', 'F', '1983-10-19'),
('Timothy', 'timothy012@qmail.com', '$2a$10$Vqx3VUuB8gy9NvtKHQARWOOYB2wG4wV2WXy1sdQHIoY8TivSHZ3sC', 'M', '1996-11-30'),
('Lisa', 'lisa345@qmail.com', '$2a$10$Vqx3VUuB8gy9NvtKHQARWOOYB2wG4wV2WXy1sdQHIoY8TivSHZ3sC', 'F', '1988-07-20'),
('Steve', 'steve678@qmail.com', '$2a$10$Vqx3VUuB8gy9NvtKHQARWOOYB2wG4wV2WXy1sdQHIoY8TivSHZ3sC', 'M', '1977-01-05'),
('Emily', 'emily321@qmail.com', '$2a$10$Vqx3VUuB8gy9NvtKHQARWOOYB2wG4wV2WXy1sdQHIoY8TivSHZ3sC', 'F', '1994-09-23'),
('Henry', 'henry654@qmail.com', '$2a$10$Vqx3VUuB8gy9NvtKHQARWOOYB2wG4wV2WXy1sdQHIoY8TivSHZ3sC', 'M', '1989-06-14'),
('Grace', 'grace987@qmail.com', '$2a$10$Vqx3VUuB8gy9NvtKHQARWOOYB2wG4wV2WXy1sdQHIoY8TivSHZ3sC', 'F', '1982-04-28'),
('Mike', 'mike012@qmail.com', '$2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q', 'M', '1998-02-08'),
('Sophie', 'sophie345@qmail.com', '$2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q', 'F', '1991-12-12'),
('Daniel', 'daniel678@qmail.com', '$2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q', 'M', '1980-07-01'),
('Olivia', 'olivia321@qmail.com', '$2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q', 'F', '1992-05-28'),
('Jackson', 'jackson654@qmail.com', '$2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q', 'M', '1985-02-18'),
('Amelia', 'amelia987@qmail.com', '$2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q', 'F', '1995-01-10'),
('Tom', 'tom012@qmail.com', '$2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q', 'M', '1987-08-03'),
('Sarah', 'sarah345@qmail.com', '$2a$10$ke3IM6noeWfQtX6POjZHl.49gSolYbqfrSTIn8sOQubdwjP2IT94q', 'F', '1984-03-09');

-- Plants 테이블에 데이터 삽입
INSERT INTO plants (plant_name, plant_type, plant_desc, image_url, temperature_low, temperature_high, humidity_low, humidity_high, watering_interval)
VALUES 
('아이비', '덩굴식물', '아이비는 빠르게 성장하는 인기 있는 덩굴식물로, 공기 정화 능력이 뛰어납니다. 벽이나 거치대에 올려두면 빠르게 뻗어나가 아름다운 모습을 연출합니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/아이비.jpg', 12, 28, 40, 70, 7),
('스투키', '선인장', '스투키는 독특한 모양의 선인장으로, 견고하고 건조한 환경에도 잘 적응할 수 있습니다. 물을 적게 주어도 건강하게 자라며 관리가 쉽습니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/스투키.jpg', 10, 30, 10, 50, 21),
('로즈마리', '허브', '로즈마리는 향긋한 향기를 가진 허브로, 요리에 활용되기도 합니다. 건조한 환경에도 잘 적응하며, 햇빛을 좋아하는 식물입니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/로즈마리.jpg', 10, 30, 30, 50, 14),
('자스민', '꽃', '자스민은 아름다운 꽃과 달콤한 향기로 사랑받는 식물입니다. 화분이나 정원에서 재배할 수 있으며, 온화한 기후를 선호합니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/자스민.jpg', 15, 30, 40, 70, 7),
('스파티필럼', '실내식물', '스파티필럼은 큰 잎과 화이트 꽃이 특징인 실내 장식용 식물로, 공기 정화 능력이 높아 인기가 많습니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/스파티필럼.jpg', 18, 28, 50, 70, 10),
('스킨답서스', '실내식물', '스킨답서스는 작은 크기의 초록색 잎과 긴 줄기가 특징인 실내 장식용 식물입니다. 건조한 환경과 낮은 빛 조건에서도 잘 자라며, 관리가 쉽습니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/스킨답서스.jpg', 15, 30, 30, 50, 10),
('페퍼민트', '허브', '페퍼민트는 상쾌한 향기를 가진 허브로, 차나 요리에 활용되기도 합니다. 물이 잘 공급되는 환경을 선호하며, 햇빛을 좋아하는 식물입니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/페퍼민트.jpg', 15, 28, 40, 60, 7),
('산세베리아', '실내식물', '산세베리아는 긴 검은 잎이 특징인 실내 장식용 식물로, 공기 정화 능력이 뛰어납니다. 건조한 환경과 낮은 빛 조건에서도 잘 자라며, 관리가 쉽습니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/산세베리아.jpg', 15, 30, 20, 50, 21),
('식물성이끼', '이끼', '식물성이끼는 물에 잘 적응한 식물로, 습한 환경에서 자라는데 적합합니다. 실내 정원이나 수초 양식에서 인기가 많으며, 공기 정화에도 도움이 됩니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/식물성이끼.jpg', 10, 25, 70, 100, 5),
('올리브', '나무', '올리브는 과실과 나무로 인기가 있는 식물로, 지중해 기후를 선호합니다. 정원이나 화분에서 재배할 수 있으며, 올리브 오일이나 식재료로 사용됩니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/올리브.jpg', 10, 30, 30, 50, 14),
('바질', '허브', '바질은 향긋한 향기를 가진 허브로, 토마토 요리에 자주 사용됩니다. 햇빛을 좋아하며, 다소 습한 환경에서 잘 자라는 식물입니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/바질.jpg', 18, 30, 40, 60, 7),
('방울토마토', '채소', '방울토마토는 작고 맛있는 과실이 특징인 채소로, 화분이나 정원에서 쉽게 재배할 수 있습니다. 햇빛을 좋아하며, 꾸준한 물 공급이 필요합니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/방울토마토.jpg', 18, 30, 40, 70, 5),
('히야신스', '꽃', '히야신스는 다양한 색상의 아름다운 꽃과 향기로 봄의 대표적인 꽃입니다. 온화한 기후를 선호하며, 화분이나 정원에서 재배할 수 있습니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/히야신스.jpg', 10, 25, 40, 60, 7),
('해바라기', '꽃', '해바라기는 거대한 꽃과 높이가 특징인 식물로, 햇빛을 매우 좋아합니다. 정원이나 대형 화분에서 재배할 수 있으며, 씨앗이 간식이나 새의 먹이로 사용됩니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/해바라기.jpg', 15, 30, 30, 60, 7),
('아레카야자', '야자', '아레카야자는 열대 실내 장식용 식물로 유명하며, 큰 잎과 세련된 모습으로 인기가 많습니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/아레카야자.jpg', 18, 27, 40, 60, 7),
('파키라', '실내식물', '파키라는 견고하고 관리하기 쉬운 식물로, 두꺼운 줄기와 큰 둥글둥글한 잎이 특징입니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/파키라.jpg', 15, 28, 40, 60, 10),
('유칼립투스', '나무', '유칼립투스는 상쾌한 향기와 아름다운 잎 모양으로 많은 사랑을 받는 식물입니다. 특히 건조한 공간에서도 잘 자라기 때문에 인기가 높습니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/유칼립투스.jpg', 10, 25, 30, 55, 14),
('피나타', '실내식물', '피나타는 높은 곳에서 뻗어 나오는 날렵한 잎으로 장식성이 높은 식물입니다. 거실이나 베란다 같은 잘 통풍되는 장소에 두기 좋습니다.', 'https://leafyapplicationfiles.blob.core.windows.net/plantimages/피나타.jpg', 15, 30, 40, 70, 12);

-- User_Plants 테이블에 데이터 삽입
INSERT INTO user_plants (user_id, plant_id, plant_nickname) VALUES
(1, 1, '노을이'), (1, 2, '햇님'), (2, 1, '별빛'), (2, 4, '새벽'), (2, 6, '향기'), (3, 7, '구름'), (3, 9, '바람'), (4, 10, '무지개'), (4, 12, '햇살'), (5, 14, '노을'),
(5, 15, '풀밭'), (6, 16, '물방울'), (6, 18,'풀풀이'), (7, 15, '뾰족이'), (8, 1, '여름'), (8, 3, '쑥쑥이'), (8, 5, '검정'), (9, 2, '커피'), (9, 6, '봄이'), (10, 4, '초록이'),
(11, 7, '딸기'), (12, 8, '노랑'), (13, 11, '바다'), (14, 13, '우주'), (15, 17, '하양');

-- Plant_Logs 테이블에 데이터 삽입
INSERT INTO plant_logs (user_plant_id, log_date, note, watered) VALUES
(1, '2023-03-22', '관리가 어려워서 죽었습니다', false),
(1, '2023-03-23', '새로운 아이비를 구입했습니다', true),
(1, '2023-03-24', '1주일에 한 번 비료를 주기로 했습니다', false),
(5, '2023-03-22', '근처에 이끼가 생겼습니다', false),
(5, '2023-03-23', '이끼를 제거하고 화분을 청소했습니다', true),
(5, '2023-03-24', '2일마다 스프레이로 적신 흔적이 있습니다', true),
(6, '2023-03-22', '잎이 말라서 살짝 노랗게 변했습니다', false),
(6, '2023-03-23', '조금 더 많이 관수하도록 조절했습니다', true),
(6, '2023-03-24', '잎을 분무기로 적신 흔적이 있습니다', true),
(7, '2023-03-22', '물을 주지 않아 꽃이 시들었습니다', false),
(7, '2023-03-23', '좀 더 자주 물을 주도록 조절했습니다', true),
(7, '2023-03-24', '잎에 먼지가 쌓여서 닦아주었습니다', true),
(8, '2023-03-22', '나뭇잎이 말라서 살짝 물을 주었습니다', true),
(8, '2023-03-23', '조금 더 어둡고 습한 곳으로 옮겼습니다', true),
(8, '2023-03-24', '이전보다 잎이 좀 더 생기기 시작했습니다', false),
(9, '2023-03-22', '가지고 있는 토양이 마르고 흙이 헐렁했습니다', false),
(9, '2023-03-23', '새로운 토양으로 교체하여 옮겼습니다', true),
(9, '2023-03-24', '이전보다 잎색이 좀 더 진해졌습니다', false),
(10, '2023-03-22', '낮은 온도로 인해 성장이 늦어졌습니다', false),
(10, '2023-03-23', '조금 더 따뜻한 곳으로 옮겼습니다', true),
(10, '2023-03-24', '새로운 잎이 조금씩 나오기 시작했습니다', false),
(11, '2023-03-22', '물을 너무 많이 주어 뿌리가 부패되었습니다', false),
(11, '2023-03-23', '새로운 화분으로 옮겨서 치료 중입니다', true),
(11, '2023-03-24', '뿌리 상태가 좋아지기 시작했습니다', false),
(12, '2023-03-22', '잎이 말라 색이 바래졌습니다', false),
(12, '2023-03-23', '분무기로 물을 주고 조금 더 어두운 곳으로 옮겼습니다', true),
(12, '2023-03-24', '잎의 색이 조금씩 회복되기 시작했습니다', false),
(13, '2023-03-22', '잎이 말라 피부가 매우 건조해졌습니다', false),
(13, '2023-03-23', '조금 더 습한 곳으로 옮기고 분무기로 물을 주었습니다', true),
(13, '2023-03-24', '잎과 피부 상태가 조금씩 개선되기 시작했습니다', false),
(14, '2023-03-22', '잎이 너무 습해서 흰색 곰팡이가 생겼습니다', false),
(25, '2023-03-22', '잎에 먼지가 많이 쌓여서 닦아주었습니다', false),
(25, '2023-03-23', '새로운 잎이 많이 나오기 시작했습니다', true),
(25, '2023-03-24', '잎의 상태가 좋아지고 성장하는 모습이 보입니다', false);

→ 테이블은 user, plant, userplant, plantlog 총 4개의 테이블을 생성한다.
→ 각 테이블에는 개발에 사용할 샘플 데이터를 INSERT 하고 있다.

#PostgreSQL 13 버전을 베이스 이미지로 사용
FROM postgres:13

#init.sql파일을 /docker-entrypoint-initdb.d/ 로 복사, /docker-entrypoint-initdb.d/에 있는 sql문은 컨테이너가 처음 실행 시 자동실행됨
COPY ./init/init.sql /docker-entrypoint-initdb.d/

#postgresql.conf파일을 /etc/postgresql/postgresql.conf 로 복사, 기본 설정 파일을 덮어쓰기하여 새로운 설정 적용
COPY ./config/postgresql.conf /etc/postgresql/custom.conf

#PostgreSQL 계정정보 설정
ENV POSTGRES_USER=myuser
ENV POSTGRES_PASSWORD=mypassword
ENV POSTGRES_DB=mydb

#PostgreSQL 포트
EXPOSE 5432

CMD ["postgres", "-c", "config_file=/etc/postgresql/custom.conf"]

→ FROM에 postgres 13버전으로 지정. 이 이미지를 실행하면 기본적인 데이터베이스 서버를 실행할 수 있는 상태로 제공된다.
→ 하지만 서버에는 데이터가 없어서 init.sql파일을 이미지내에서 실행시켜야 한다.
→ init.sql파일을 /docker-entrypoint-initdb.d/ 로 복사한다. (해당 폴더는 postgres 이미지가 실행될 때 자동으로 실행하는 SQL을 저장하기로 약속된 폴더이다.
→ -c를 통해 config 파일을 지정해서 이미지 안에 있는 기본 설정파일을 사용하지 않고 빌드를 통해 주입한 설정파일을 사용하게 해준다.

네트워크 리스트 확인

docker network ls

새로운 네트워크 생성(leafy-network가 없을 경우에만)

docker network create leafy-network

도커파일을 사용해 postgres 이미지 빌드

docker build -t 레지스트리계정명/leafy-postgres:1.0.0 .

빌드한 이미지 push

docker push yoonjeongwon/leafy-postgres:1.0.0

빌드한 이미지를 사용해 leafy-postgres 컨테이너 실행

docker run -d --name leafy-postgres --network leafy-network 레지스트리계정명/leafy-postgres:1.0.0

→ 이 이름은 나중에 Springboot에서 접근하기 때문에 꼭 기억해놓고 설정해야함

leafy-postgres 컨테이너의 로그 확인

docker logs leafy-postgres

→ init 문과 서버가 잘 실행된 것을 볼 수 있다.

leafy-postgres 컨테이너 내에서 명령어 실행 후 결과 출력 (postgres의 터미널로 접근 가능)

docker exec -it leafy-postgres su postgres bash -c "psql --username=myuser --dbname=mydb"

→ SELECT * FROM USER; 가능
→ q 누르면 나옴
→ exit 해서 터미널 나옴

leafy-postgres 이미지의 레이어 확인

docker image history 레지스트리계정명/leafy-postgres:1.0.0

→ 0~24는 PostgreSQL의 기본 이미지인 postgres 13 버전의 레이어
→ PostgreSQL 이미지는 데비안 11이라는 OS 이미지를 베이스 이미지로 한다.
→ Postgres 이미지는 데비안이라는 OS 이미지가 베이스로 빌드
→ 우리는 Postgres 이미지를 베이스로 repeat Postgres 이미지를 빌드
→ 이처럼 레이어의 중첩관계를 가질 수 있다. (25부턴 커스텀)



이미지 레지스트리 실습

→ 위의 dockerfile이 해주는 역할을 실제로 dockerfile을 사용하지않고 직접할 경우

  • cp 명령어 : 실행 중인 컨테이너로 특정 파일을 복사할 수 있다. 컨테이너 내부에 있는 파일도 호스트머신으로 복사해 올 수 있다.

컨테이너와 호스트 머신 간 파일 복사

docker cp 원본위치 복사위치

컨테이너 → 호스트머신으로 파일 복사

docker cp 컨테이너명:원본위치 복사위치

호스트머신 → 컨테이너로 파일 복사

docker cp 원본위치 컨테이너명:복사위치



SpringBoot 백엔드 컨테이너 구성

백엔드 빌드 프로세스(Spring Boot)

  1. OS구성 및 Java Runtime 설치
  • 자바로 개발된 소스코드는 jar, war 파일로 프로그램을 빌드할 수 있다.
  • jar, war 파일을 실행시키려면 OS에 자바 런타임이 설치되어 있어야 한다.
  1. 빌드 도구 설치
  • 소스코드를 애플리케이션으로 빌드하려면 Maven이나 Gradle이라는 빌드 프로그램이 필요하다.
  1. 소스코드 다운로드
  • git clone …
  1. 의존성 라이브러리 설치 및 빌드
  • gradle clean build 명령어 사용해서 애플리케이션으로 빌드한다.
  • 빌드가 완료되면 jar 파일이 생성된다.
  1. 애플리케이션 실행
  • java -jar leafy.jar
  • 애플리케이션을 실행할 때 이렇게 자바 명령과 jar 파일의 경로를 지정해서 애플리케이션을 실행할 수 있다.

→ 백엔드 애플리케이션에서는 소스코드 빌드 과정이 포함되어 있기 때문에 멀티 스테이징 빌드 기술을 활용한다.
→ 애플리케이션 빌드에 사용되는 이미지는 Gradle 이미지를 사용하고,
→ 이 gradle 이미지에서 빌드를 실행해서 만들어진 jar 파일을 이 자바 애플리케이션 실행 기능만 가지고 있는 OpenJDK 이미지로 복사한다.
→ OpenJDK 이미지에서는 이 컨테이너를 실행할 때 복사한 jar 파일 애플리케이션으로 실행할 것이다.



실습

# 빌드 이미지로 OpenJDK 11 & Gradle을 지정
FROM gradle:7.6.1-jdk11 AS build

# 소스코드를 복사할 작업 디렉토리를 생성
WORKDIR /app

# 라이브러리 설치에 필요한 파일만 복사
COPY build.gradle settings.gradle ./

RUN gradle dependencies --no-daemon

# 호스트 머신의 소스코드를 작업 디렉토리로 복사
COPY . /app

# Gradle 빌드를 실행하여 JAR 파일 생성
RUN gradle clean build --no-daemon

# 런타임 이미지로 OpenJDK 11-jre-slim 지정
FROM openjdk:11-jre-slim

# 애플리케이션을 실행할 작업 디렉토리를 생성
WORKDIR /app

# 빌드 이미지에서 생성된 JAR 파일을 런타임 이미지로 복사
COPY --from=build /app/build/libs/*.jar /app/leafy.jar

EXPOSE 8080 
ENTRYPOINT ["java"] 
CMD ["-jar", "leafy.jar"]

→ 도커파일을 사용해 이미지로 빌드하기


도커파일을 사용해 leafy-backend로 이미지 빌드

docker build -t 레지스트리계정명/leafy-backend:1.0.0 .

이미지 push

docker push 레지스트리계정명/leafy-backend:1.0.0

도커파일을 사용해 leafy-backend 이미지 빌드(컨테이너 실행)

docker run -d -p 8080:8080 -e DB_URL=leafy-postgres --name leafy --network leafy-network 레지스트리계정명/leafy-backend:1.0.0

leafy 컨테이너 로그 확인

docker logs leafy

curl http://localhost:8080/api/v1/users → leafy 컨테이너의 로그 확인



Vue.js 프론트엔드 컨테이너 구성

  • 프레임워크 상관없이 프론트엔드는 html, css, js 파일들로 결과물을 빌드할 수 있다.
  • 빌드한 파일은 root dir에 dist라는 폴더에 만들어진다.
  • 이 파일들을 Nginx와 같은 웹서버의 특정 경로에 업로드하면 클라이언트가 브라우저를 통해서 웹서버에 접속했을 때 개발자가 개발한 웹페이지를 응답할 수 있다.

프론트엔드 빌드 프로세스(Vue.js)

  1. OS구성 및 Nginx 웹 서버 설치
    • OS에 Nginx 웹서버가 설치되어 있어야 한다.
  2. 빌드 도구 설치
    • 소스코드를 빌드하기 위해서 node.js와 npm이 설치되어 있어야 한다.
  3. 소스코드 다운로드
    • git clone …
  4. 의존성 라이브러리 설치 및 빌드
    • 외부 라이브러리는 npm ci 명령어로 다운 가능하다.
    • 소스코드를 빌드하는 명령어는 npm run build 이다. → dist 폴더에 결과 생성
  5. 빌드 결과 폴더를 /usr/share/nginx/html 폴더로 복사
    • cp ./dist /usr/share/nginx/html
  6. 웹서버 실행
    • nginx -g daemon off;

→ 멀티스테이징 빌드 기술을 활용해 불필요한 파일 크기를 최소화 한다.
→ 빌드과정에서 생성되는 파일은 웹서버에서는 사용되지 않는 파일들이기 때문에 빌드 과정을 node:14 이미지에서 실행시키고 결과파일인 dist 디렉터리만 실행 이미지 nginx로 복사해준다.

실습

# 빌드 이미지로 node:14 지정 
FROM node:14 AS build

WORKDIR /app

# 라이브러리 설치에 필요한 파일만 복사
COPY package.json .
COPY package-lock.json .

# 라이브러리 설치
RUN npm ci

# 소스코드 복사
COPY . /app

# 소스코드 빌드
RUN npm run build

# 프로덕션 스테이지
FROM nginx:1.21.4-alpine 
COPY nginx.conf /etc/nginx/conf.d/default.conf.template
ENV BACKEND_HOST leafy
ENV BACKEND_PORT 8080

COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# 빌드 이미지에서 생성된 dist 폴더를 nginx 이미지로 복사
COPY --from=build /app/dist /usr/share/nginx/html

EXPOSE 80
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

leafy-frontend 이미지 빌드

docker build -t 레지스트리계정명/leafy-frontend:1.0.0 .

leafy-frontend 이미지 푸시

docker push 레지스트리계정명/leafy-frontend:1.0.0 

leafy-frontend 컨테이너 실행

docker run -d -p 80:80 --name leafy-frontend --network leafy-network 레지스트리계정명/leafy-frontend:1.0.0 



Leafy 애플리케이션 테스트

Leafy 애플리케이션 아키텍처

정상 실행 확인 후 구성 환경 삭제

docker rm -f leafy-frontend leafy leafy-postgres

→ 네트워크를 생략하고 진행한 내용, 네트워크는 다음 장에서..

profile
https://garden-ying.tistory.com/
post-custom-banner

0개의 댓글