개발 환경을 하나의 집으로 비유했을 때, 이 집에는 Java, MySQL 같은 기술 스택이 설치된다는 것은 이것들이 입주자 처럼 생활 하는 것과 같음.
한 가지 서비스만 개발한다면 큰 문제가 없지만,
서비스A는 Java 17버전이 필요한데, 서비스B는 Java 21버전을 필요로 함
두 버전을 동시에 설치하면 충돌이 발생하고 복잡한 설정이 필요함
다른 기술들도 버전 충돌 문제가 발생할 수 있음
결국 한 집에서 여러 프로젝트를 진행하면 입주자들이 함께 지내기 어려워짐.
가상화(Virtualization)의 한계
이 문제를 해결하기 위해 가상화 기술(VirtualBox, VMWare, Parallels 등)을 사용할 수 있음. 하나의 OS안에 또 다른 OS를 설치하여 서비스별로 분리된 환경을 제공함.
컴퓨터 자원(CPU, 메모리)이 각 가상 환경마다 고정적으로 할당되어 비효율적
각 가상 환경마다 별도의 OS가 필요해 저장 공간 낭비
OS기능들(부엌, 화장실 등)이 중복되어 자원 낭비
특히 성능이 중요한 서버 환경에서는 이러한 낭비가 더 큰 문제가 됨.
도커 컨테이너
도커는 컨테이너 개념을 통해 이 문제를 해결함.
격리된 작업 공간 : 컨테이너는 대저택 안의 업무 공간과 같음. 각 근무자가 자신만의 공간에서 일할 수 있음
자원 효율성 : 컨테이너들은 호스트 시스템 지원(CPU, 메모리)을 효율적으로 공유함. 필요한 만큼 사용하고 낭비를 줄임.
버전 충돌 방지 : 각 컨테이너는 독립적이므로 서로 다른 버전이 충돌없이 실행될 수 있음.
연결 가능 : 여러 컨테이너가 연결되어 복잡한 서비스를 구성할 수 있음.
또한 도커의 가장 큰 장점은 개발 환경에서 서버 환경으로의 배포를 간소화한다는 점.
선언적 설정 : 개발자는 컨테이너의 설계도(Dockerfile, Docker Compose)를 통해 설치 방법, 실행 방법, 연결 방법들을 명확히 정의할 수 있음 → 환경 일관성 유지
쉬운 배포 : 코드와 설계도를 서버에 전송하면 동일한 환경이 자동으로 구성됨
도커는 서로 다른 기술 스택과 버전들이 충돌 없이 공존할 수 있게 해주며, 개발 환경과 서버 환경의 일관성을 보장함.
[실습]
docker run -it node
로컬 환경에 'node'라는 이미지가 없다면, 자동으로 Docker Hub에서 최신 버전의 Node.js 이미지를 다운로드
이미지 다운로드 후 해당 이미지 기반으로 새로운 컨테이너를 생성하고 실행
docker ps
--name 이름 node 이런식으로 이름을 설정하지 않으면 자동으로 컨테이너 이름이 생성됨 (kind_bell)
docker exec -it kind_bell bash
'kind_bell' 컨테이너 안에서 bash shell을 실행
컨테이너 내부를 통해 가상의 리눅스 환경으로 들어감
Dockerfile: 이미지를 만들기 위한 설계도
🤔 node 이미지를 받았는데 나만의 이미지를 왜 또 만드냐?
컴퓨터에서 http-server란 프로그램을 돌리려면 Node.js만 설치되어 있어서는 안 됨. npm 등으로 http-server가 전역으로 깔려 있어야 함. node 이미지는 아직 http-server가 없는 상태이기 때문에, 컨테이너로 실행할 때마다 매번 http-server를 받아줘야 함. (서비스를 돌릴 때마다 이런 모듈을 일일이 다운 받아야 함) →Node.js에다가 http-server까지 깔린 상태가 이미지로 있으면 컨테이너로 서비스를 실행하기 보다 수월해짐~!
Dockerfile
# 이 node 이미지에 덧붙여서 개조, 튜닝
FROM node:12.18.4
# 이미지 생성 과정에서 실행할 명령어
# Node.js가 깔린 node 이미지여서 npm 명령어 가능
# http-server 설치
RUN npm install -g http-server
# 이미지 내에서 명령어를 실행할(현 위치로 잡을) 디렉토리 설정
WORKDIR /home/node/app
# 컨테이너 실행시 실행할 명령어
CMD ["http-server", "-p", "8080", "./public"]
# 이미지 생성 명령어 (현 파일과 같은 디렉토리에서)
# docker build -t {이미지명} .
# 컨테이너 생성 & 실행 명령어
# docker run --name {컨테이너명} -v $(pwd):/home/node/app -p 8080:8080 {이미지명}
RUN : 이미지를 생성하는 과정에서 실행되는 것, 이미지에서 컨테이너를 실행하는 시점에서는 이미 http-server가 설치.
CMD : 이미지로부터 컨테이너가 만들어져 가동될 때 바로 실행되는 명령어
1) 이미지 생성
docker build -t frontend-img .
frontend-img 이미지에는 이미 http-server가 깔린 상태
2) 컨테이너로 실행 (run)
docker run --name frontend-con -v $(pwd):/home/node/app -p 8080:8080 frontend-img
Volume: 컨테이너와 특정 폴더를 공유
FROM mysql:5.7
# 이미지 환경변수들 세팅
# 실전에서는 비밀번호 등을 이곳에 입력하지 말 것!
# 서버의 환경변수 등을 활용하세요.
ENV MYSQL_USER mysql_user
ENV MYSQL_PASSWORD mysql_password
ENV MYSQL_ROOT_PASSWORD mysql_root_password
ENV MYSQL_DATABASE visitlog
# 도커환경에서 컨테이너 생성시 스크립트를 실행하는 폴더로
# 미리 작성된 스크립트들을 이동
COPY ./scripts/ /docker-entrypoint-initdb.d/
COPY : RUN처럼 이미지를 생성하는 과정에서 해당 이미지 안에 특정 파일을 미리 넣어둠
'create_table.sql' , 'insert_data.sql' 파일들은 컨테이너 초기화 과정에 필요하기 때문에 COPY로 이미지 안에 미리 넣어두는 것
Docker Compose
여러 컨테이너를 정의하고 실행하기 위한 도구
version: '3'
services:
database:
# Dockerfile이 있는 위치
build: ./database
# 내부에서 개방할 포트 : 외부에서 접근할 포트
ports:
- "3306:3306"
backend:
build: ./backend
# 연결할 외부 디렉토리 : 컨테이너 내 디렉토리
volumes:
- ./backend:/usr/src/app
ports:
- "5000:5000"
# 환경변수 설정
environment:
- DBHOST=database
frontend:
build: ./frontend
# 연결할 외부 디렉토리 : 컨테이너 내 디렉토리
volumes:
- ./frontend:/home/node/app
ports:
- "8080:8080"