애플리케이션을 이미지로 빌드하고, 컨테이너로 배포해보자.
우선 컨테이너 애플리케이션의 특징을 이해하기 위해 클라우드의 개념을 학습하고 Cloud Native Application 특징과 MSA 아키텍처에 대해 간단히 배워보자.
실습은 애플리케이션을 컨테이너로 빌드하는 것이다! (1. Vue.js → Nginx 2. Springboot → Java 컨테이너. + Postgres 컨테이너로 빌드)
클라우드는 스토리지 저장소를 의미한다.
Naver Cloud, Google Cloud.. 등 과 같은 클라우드 서비스를 사용하면 내가 원하는 파일을 클라우드에 저장해 놓을 수 있다. 사용한 만큼만 비용을 지불하고, 더 많은 파일을 저장하고 싶을 경우는 저장한 만큼 추가 비용을 지불하면 된다.
업로드한 파일은 실제 어디에 저장될까?
각각의 클라우드 업체들은 자체적으로 서버들을 가지고 있으며, 여기에 모든 사용자들의 파일이 물리적으로 섞여 있는 상태로 저장된다. 즉, 하나의 물리 드라이브에 여러 사용자의 파일이 저장될 수 있는 것이다.
클라우드 서비스를 사용한다는 것은 이런 인프라적인 부분을 신경 쓰지 않고 실제 서비스의 기능만 사용하는 것을 뜻한다.
극단적으로 클라우드 스토리지를 사용하지 않는다고 생각하면 당장 USB부터 들고 다녀야 한다. 따라서 클라우드를 사용하면 물리적인 디바이스에서 자유로워지고, 필요할 때 마다 빠르게 확장할 수 있는 편리함을 누릴 수 있다. (공유 경제와 비슷한 원리이다. 그린카나 쏘카, 에어비엔비 같이 직접 구매하지 않고도 시간당 요금으로 빌리는 원리라고 생각하면 된다.)
클라우드 컴퓨팅은 단순히 스토리지에 국한되지 않고, 전반적인 서버 컴퓨터로 개념이 확장된다. 예를 들어 2코어의 4GB 메모리를 가진 컴퓨터를 사용하고 싶은 경우, 클라우드 사업자의 계정을 만들어 서버를 구매하면 5~10분내로 이 서버에 접속할 수 있다. 클라우드 사업자는 대형 데이터 센터를 지역별로 구축해 놓고 가상화 기술을 활용한다. 사용자가 서버를 구매할 때마다 VM을 한 대 만들어서 접속 정보를 제공해준다.
일반 기업이 서버를 새로 한 대 구매하려면 서버 구매 과정부터 배송, 설치, 유지보수까지 많은 비용과 시간을 소비하게 된다. 더불어 서버 기계 자체의 감가상삭, 유지하기 위한 인력, 전기, 폐기 비용까지 모두 서버 소유자의 몫이 된다. 그래서 기업들은 이런 비용들을 없애고 클라우드 서비스를 사용한다.
따라서 클라우드는 다른 회사 서버를 빌려서 운영하는 것이며,
다른 회사가 모두에게 서버를 빌려줄 경우 → Public Cloud
다른 회사가 특정 조직에게만 서버를 빌려줄 경우 → Private Cloud
→ 현대 애플리케이션이 겪는 다양한 문제들을 클라우드 환경 구성을 통해 해결할 수 있기 때문이다.
트래픽이 증가할 때 빠르게 대처할 수 있는가? (확장성, Scalability)
→ 클라우드 환경에서는 서버 추가가 10분내로 이뤄진다. 온프레스미스에서는 서버가 미리 준비되어 있지 않은 경우 새로운 서버를 증가하는데 오랜 시간이 소요된다. (주문, 배송, 설치 등..)
→ 클라우드를 사용하면 확장성을 가질 수 있고 트래픽 증가에 빠르게 대처할 수 있다.
장애 발생 시 빠르게 복구할 수 있는가? (복원력, Resilience)
→ 클라우드 환경에서는 백업 및 복구가 빠르게 이뤄질 수 있다.(Disaster Recovery) 장애에 대응하기 위한 다양한 지역의 서버를 구축할 수 있다.
운영 비용을 효율적으로 운영할 수 있는가?
→ 사용한 만큼만 지불할 수 있기 때문에 운영 비용이 더 효율적이다.
MSA
→ 애플리캐이션을 여러 단위로 분리해서 트래픽 증가에 효율적으로 대처하기 위한 소프트웨어 아키텍처이다.
컨테이너 Container
→ 컨테이너의 이미지에는 소프트웨어가 실행하기 위한 환경들이 모두 포함되어 있다. 따라서 이미지를 가지고 있으면 어떤 환경에서든 동일한 실행 동작을 보일 수 있다.
→ 컨테이너를 사용하지 않을 경우 각각의 서버의 프로그램을 별도로 운영해야 하고 결국 환경 불일치 문제가 생길 가능성이 크다.
상태비저장 Stateless
→ 상태를 가질 경우에는 외부의 이 상태가 분리되어있어야 한다.
→ 상태를 가진다는 것은 결국 각각의 서버가 다르게 동작할 수 있다는 것을 의미한다.
→ 이미지를 컨테이너로 실행할 때 컨테이너에 읽기쓰기 레이어가 생기는 것, 삭제하면 이 읽기쓰기 레이어도 함께 삭제되는 것처럼 컨테이너는 태생적으로 비저장의 특징을 가진다.
DevOps 및 CI/CD
→ 12factor.net/ko/ : 클라우드 환경에서 운영하는 애플리케이션의 요구 사항에 대해 잘 정리해놨다.
MicroService Architecture
모놀리식(Monolithic)
마이크로서비스아키텍처(MSA)
→ MSA의 단점은 복잡성이 높다는 것이다. 하지만 MSA는 클라우드 네이티브에 잘 어울리는 소프트웨어 구조이다. MSA 아키텍처의 장점을 잘 이용하기 위해서 컨테이너를 활용하는 것이 필수이다.
LEAFY : 식물 관리 웹 애플리케이션
→ 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
→ 컨테이너를 사용하면 소프트웨어를 실행하기 위해 필요한 것들을 컴퓨터에 설치하지 않고도 애플리케이션을 구성할 수 있다. 컨테이너만 삭제하면 완전히 실행 전과 동일한 컴퓨터 상태로 돌릴 수 있다.
→ 기존에 빌드하고 푸시해놓은 이미지로 애플리케이션을 구성했다면, 직접 컨테이너 이미지를 빌드해볼 것
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을 사용하지않고 직접할 경우
컨테이너와 호스트 머신 간 파일 복사
docker cp 원본위치 복사위치
컨테이너 → 호스트머신으로 파일 복사
docker cp 컨테이너명:원본위치 복사위치
호스트머신 → 컨테이너로 파일 복사
docker cp 원본위치 컨테이너명:복사위치
→ 백엔드 애플리케이션에서는 소스코드 빌드 과정이 포함되어 있기 때문에 멀티 스테이징 빌드 기술을 활용한다.
→ 애플리케이션 빌드에 사용되는 이미지는 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 컨테이너의 로그 확인
→ 멀티스테이징 빌드 기술을 활용해 불필요한 파일 크기를 최소화 한다.
→ 빌드과정에서 생성되는 파일은 웹서버에서는 사용되지 않는 파일들이기 때문에 빌드 과정을 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
정상 실행 확인 후 구성 환경 삭제
docker rm -f leafy-frontend leafy leafy-postgres
→ 네트워크를 생략하고 진행한 내용, 네트워크는 다음 장에서..