가상머신 vs. 도커
- 가상화(Virtualization): 물리적인 컴퓨터 리소스의 특징을 다른 시스템, 응용 프로그램, 최종 사용자들이 리소스와 상호 작용하는 방식으로부터 감추는 기술이다.
가상화의 종류
Host OS: 물리적 하드웨어 위에 OS를 설치해 그 위에 가상화 소프트웨어와 가상 머신을 올린다. VMWare, VirtualBox 등이 있다. 가상 머신이 Host OS와 커널을 공유하지 않기 때문에 보안성이 높지만 다른 방법에 비해 상대적으로 성능이 떨어진다.
- Hypervisor: Host OS 대신 Hypervisor를 통하여 가상머신이 동작하는 방식으로 Host OS에 할당할 리소스가 필요 없기 때문에 Host OS 방식에 비하여 오버헤드가 적지만 자체적인 관리 기능은 없기 때문에 별도의 관리 콘솔이 필요하다. 전가상화, 반가상화로 나누어 진다.
- 전가상화(Full-Virtualization): 하드웨어를 완전히 가상화하는 방식으로 DOM0라고 하는 관리용 가상 머신이 실행되어 모든 가상 머신의 하드웨어 접근이 DOM0를 통해서 이루어진다. 하드웨어를 완전히 가상화하기 때문에 Guest OS를 수정할 필요가 없지만 성능이 떨어지는 문제점이 있다.
- 반가상화(Half-Virtualization): Guest OS가 Hyper Call이라는 인터페이스를 통하여 Hypervisor에게 요청을 보내는 방식으로 전가상화에 비해 성능이 좋지만 OS의 커널을 수정해야 하기 때문에 오픈 소스 OS가 아니면 반가상화를 이용하기 어렵다.
- Container: Host OS 위에 Docker Engine이 올라가고 각 Container에는 application의 실행에 필요한 binary만 올라간다. 나머지 커널 부분은 Host OS의 커널을 공유한다. 이때, Linux의 namespace 기술을 이용하여 각 Container가 격리된다. 커널을 공유하기 때문에 I/O가 발생할 경우 Host OS 방식에 비하여 성능이 좋다. 하지만 커널을 공유하기 때문에 Host OS와 전혀 다른 OS는 Container로 올릴 수 없다. 때문에 Windows에서 Linux 기반인 Docker를 사용할 때 Hypervisor인 Hyper-V를 이용한다.
도커의 특징
확장성/이식성
- 도커가 설치되어 있다면 어디서든 컨테이너를 실행할 수 있음
- 특정 회사나 서비스에 종속적이지 않음
- 쉽게 개발서버를 만들 수 있고 테스트서버 생성도 간편함
표준성
- 도커를 사용하지 않는 경우 ruby, node.js, go, php로 만든 서비스들의 배포 방식은 제각각 다름
- 컨테이너라는 표준으로 서버를 배포하므로 모든 서비스들의 배포 과정이 동일해짐
이미지
- 이미지에서 컨테이너를 생성하기 때문에 반드시 이미지를 만드는 과정이 필요
- Dockerfile을 이용하여 이미지를 만들고 처음부터 재현 가능
- 빌드 서버에서 이미지를 만들면 해당 이미지를 이미지 저장소에 저장하고 운영서버에서 이미지를 불러옴
설정관리
- 설정은 보통 환경변수로 제어함
- MYSQL_PASS=password와 같이 컨테이너를 띄울 때 환경변수를 같이 지정
- 하나의 이미지가 환경변수에 따라 동적으로 설정파일을 생성하도록 만들어져야 함
자원관리
- 컨테이너는 삭제 후 새로 만들면 모든 데이터가 초기화 됨
- 업로드 파일을 외부 스토리지와 링크하여 사용하거나 S3 같은 별도의 저장소가 필요
- 세션이나 캐시를 memcached나 redis와 같은 외부로 분리
도커가 가져온 변화
- 클라우드 이미지보다 관리하기 쉬움
- 다른 프로세스와 격리되어 가상머신처럼 사용하지만 성능 저하 거의 없음
- 복잡한 기술(namespace, cgroups, network 등)을 몰라도 사용할 수 있음
- 코드와 설정으로 관리하여 재현 및 수정 가능
- 특정 회사 기술에 종속적이지 않은 오픈 소스 기반
컨테이너의 미래
kubernetes
- Docker가 한 대의 서버와 한 개의 서비스를 관리한다면 여러 대의 서버와 여러 개의 서비스를 관리하기 쉽게 해준다.
스케줄링
- 컨테이너를 적당한 서버에 배포해 주는 작업
- 여러 대의 서버 중 가장 할 일 없는 서버에 배포하거나 그냥 차례대로 배포 또는 아예 랜덤하게 배포
- 컨테이너 개수를 여러 개로 늘리면 적당히 나눠서 배포하고 서버가 죽으면 실행 중이던 컨테이너를 다른 서버에 띄워줌
클러스터링
- 여러 개의 서버를 하나의 서버처럼 사용
- 작게는 몇 개 안되는 서버부터 많게는 수천 대의 서버를 하나의 클러스터로
- 여기저기 흩어져 있는 컨테이너도 가상 네트워크를 이용하여 마치 같은 서버에 있는 것처럼 쉽게 통신
서비스 디스커버리
- 서비스를 찾아주는 기능
- 클러스터 환경에서 컨테이너는 어느 서버에 생성될지 알 수 없고 다른 서버로 이동할 수도 있음
- 따라서 컨테이너와 통신을 하기 위해서는 어느 서버에서 실행 중인지 알아야 하고 컨테이너가 생성되고 중지될 때 어딘가에 IP와 Port 같은 정보를 업데이트해줘야 함
- Key-Value Storage에 정보를 저장할 수도 있고 내부 DNS 서버를 이용할 수도 있음
도커 기본 명령어
run
docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]
OPTIONS | 동작 |
---|---|
-d | detached mode (백그라운드 모드) |
-p | 호스트와 컨테이너의 포트를 연결, <호스트>:<컨테이너>컨테이너>호스트> |
-v | 호스트와 컨테이너의 디렉토리를 연결 |
-e | 컨테이너 내에서 사용할 환경변수 설정 |
–name | 컨테이너 이름 설정 |
–rm | 프로세스 종료시 컨테이너 자동 제거 |
-it | -i와 -t를 동시에 사용한 것으로 터미널 입력을 위한 옵션 |
–network | 네트워크 연결 |
ubuntu 20.04 컨테이너 만들기
docker run ubuntu:20.04
run 명령어를 사용하면 사용할 이미지가 저장되어 있는지 확인하고 없다면 다운로드(pull) 한 후 컨테이너를 생성(create)하고 시작(start)한다.
컨테이너는 프로세스이기 때문에 실행중인 프로세스가 없다면 컨테이너는 종료된다. 따라서, 위의 명령어로 컨테이너를 만들면 생성되자마자 종료된다.
docker run --rm -it ubuntu:20.04 /bin/sh
컨테이너 내부에 들어가기 위해 sh를 실행하고 키보드 입력을 위해 -it 옵션을 주었다.
프로세스가 종료되면 컨테이너가 자동으로 제거되도록 –rm 옵션을 주었다. 주지 않을 경우 수동으로 컨테이너를 삭제해야 한다.
웹 어플리케이션 실행하기
docker run --rm -p 5678:5678 hashicorp/http-echo -text="hello world"
- -p 명령어로 호스트의 5678 포트와 hashicorp/http-echo 이미지로 생성한 컨테이너의 5678 포트를 연결한다.
MySQL 실행하기
docker run -d -p 3306:3306 ^
-e MYSQL_ALLOW_EMPTY_PASSWORD=true ^
--name mysql ^
mysql:5.7
- –name 명령어를 통하여 컨테이너의 이름을 지정하였다.
docker exec -it mysql mysql
create database wp CHARACTER SET utf8;
grant all privileges on wp.* to wp@'%' identified by 'wp';
flush privileges;
quit
- exec 명령어를 통하여 mysql 컨테이너를 실행하고 wp 테이블을 생성하였다.
exec
실행 중인 도커 컨테이너에 접속할 때는 보안 등의 문제로 인하여 컨테이너 안에 ssh server 등을 설치하지 않고 exec 명령어를 사용한다.
워드프레스 블로그 실행하기
docker run -d -p 8080:80 ^
-e WORDPRESS_DB_HOST=host.docker.internal ^
-e WORDPRESS_DB_NAME=wp ^
-e WORDPRESS_DB_USER=wp ^
-e WORDPRESS_DB_PASSWORD=wp ^
wordpress
- 워드프레스 이미지로 컨테이너를 생성하였다.
ps
docker ps
실행중인 도커 컨테이너 목록을 확인하는 명령어이다.
docker ps -a
중지된 컨테이너도 확인하려면 -a 옵션을 붙인다.
stop
docker stop [OPTIONS] CONTAINER [CONTAINER...]
실행 중인 컨테이너를 하나 또는 여러 개 중지할 수 있는 명령어이다.
rm
docker rm [OPTIONS] CONTAINER [CONTAINER...]
종료된 컨테이너를 완전히 제거하는 명령어이다.
logs
docker logs [OPTIONS] CONTAINER
컨테이너가 정상적으로 동작하는지 확인하기 위하여 로그를 확인할 수 있다.
-f: 실시간으로 로그를 확인할 수 있다.
-tail: 마지막부터 원하는 만큼만의 로그를 확인할 수 있다.
images
docker images [OPTIONS] CONTAINER
도커가 다운로드한 이미지 목록을 보는 명령어이다.
pull
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
이미지를 다운로드하는 명령어이다.
rmi
docker rmi [OPTIONS] IMAGE [IMAGE...]
이미지를 삭제하는 명령어이다. images 명령어를 통해 얻는 이미지 목록에서 이미지 ID를 입력하면 된다. 단, 컨테이너가 실행중인 이미지는 삭제하지 않는다.
network create
docker network create [OPTIONS] NETWORK
도커 컨테이너끼리 이름으로 통신할 수 있는 가상 네트워크를 만드는 명령어이다.
network connect
docker network connect [OPTIONS] NETWORK CONTAINER
기존에 생성된 컨테이너에 네트워크를 추가하는 명령어이다.
network 예제
docker network create app-network
docker network connect app-network mysql
docker run -d -p 8080:80 ^
--network=app-network ^
-e WORDPRESS_DB_HOST=mysql ^
-e WORDPRESS_DB_NAME=wp ^
-e WORDPRESS_DB_USER=wp ^
-e WORDPRESS_DB_PASSWORD=wp ^
wordpress
- app-network 라는 이름으로 wordpress와 mysql이 통신할 네트워크를 만든다.
- mysql 컨테이너에 네트워크를 추가한다.
- wordpress를 app-network에 속하게 하고 mysql 컨테이너 이름으로 접근한다.
volume mount (-v)
docker stop mysql
docker rm mysql
docker run -d -p 3306:3306 ^
-e MYSQL_ALLOW_EMPTY_PASSWORD=true ^
--network=app-network ^
--name mysql ^
-v /Users/subicura/Workspace/github.com/subicura/docker-guide/ch02/mysql:/var/lib/mysql ^
mysql:5.7
-v 옵션을 이용하여 컨테이너 종료 후에도 보존해야 하는 파일을 저장할 수 있다.
docker-compose
- 도커 컨테이너를 생성, 조회 및 삭제하는 작업을 간편하게 할 수 있는 프로그램으로 docker 설치 시 기본적으로 설치됨
docker-compose.yml
version: '2'
services:
db:
image: mysql:5.7
volumes:
- ./mysql:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: wordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
image: wordpress:latest
volumes:
- ./wp:/var/www/html
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_PASSWORD: wordpress
YAML (yml)
xml, json과 같이 타 시스템과 데이터를 주고 받기 위한 데이터 직렬화 양식
- 공백 문자를 이용한 들여쓰기로 구조체를 구분한다. (탭문자를 사용하지 않는다)
- 리스트 요소는 하이픈(-)으로 시작하는 한 줄에 하나의 요소를 표현하며, 한 줄에 모아 쓸 때에는 대괄호([])를 이용한다.
up
docker-compose up -d
docker compose를 이용하여 docker-compose.yml 파일을 detached mode로 실행한다.
down
docker-compose down
docker compose를 이용하여 mysql과 wordpress를 종료한다.
이미지 만들고 배포하기
도커 이미지 만들기
이미지?
- 프로세스가 실행되는 파일들의 집합(환경)
- 도커는 이미지를 만들기 위해 컨테이너의 상태를 그대로 이미지로 저장한다.
Git 설치한 이미지 생성 예제
docker run -it --name git ubuntu:latest bash
Ubuntu base image를 이용한 컨테이너를 생성한다.
apt-get update
apt-get install -y git
git --version
컨테이너에 git을 설치한다.
docker commit git ubuntu:git
commit 명령어를 이용하여 git 컨테이너를 ubuntu:git이라는 이미지로 생성하였다.
TDD (Test Driven Develop)
- 한 번에 성공하는 빌드는 없음
- 파란불(빌드 성공)이 뜰 때까지 많은 빨간불(빌드 실패)를 경험함
- 일단 파란불이 켜져도 리팩토링을 통해 더 최적화된 이미지 생성
build 명령어를 이용할 경우 Dockerfile
Dockerfile
명령어 | 기능 |
---|---|
FROM | 기본 이미지 |
RUN | 쉘 명령어 실행 |
CMD | 컨테이너 기본 실행 명령어 (Entrypoint의 인자로 사용) |
EXPOSE | 오픈되는 포트 정보 |
ENV | 환경변수 설정 |
ADD | 파일 또는 디렉토리 추가. URL/ZIP 사용 가능 |
COPY | 파일 또는 디렉토리 추가 |
ENTRYPOINT | 컨테이너 기본 실행 명령어 |
VOLUME | 외부 마운트 포인트 생성 |
USER | RUN, CMD, ENTRYPOINT를 실행하는 사용자 |
WORKDIR | 작업 디렉토리 설정 |
ARGS | 빌드타임 환경변수 설정 |
LABEL | key-value 데이터 |
ONBUILD | 다른 빌드의 베이스로 사용될 때 사용하는 명령어 |
이미지 빌드하기
docker build -t {이미지명:이미지 태그} {빌드 컨텍스트}
현재 디렉토리의 Dockerfile로 빌드
- -f
옵션을 사용해 다른 위치의 Dockerfile 파일 사용 가능 - -t 명령어로 도커 이미지 이름을 지정
- {네임스페이스}/{이미지이름}:{태그} 형식
마지막에는 빌드 컨텍스트 위치를 지정
- 현재 디렉터리를 의미하는 점(.)을 주로 사용
- 필요한 경우 다른 디렉터리를 지정할 수도 있음
.dockerignore
.gitignore와 비슷한 역할
도커 빌드 컨텍스트에서 지정된 패턴의 파일을 무시
- .git이나 민감한 정보를 제외하는 용도로 주로 사용
- .git이나 에셋 디렉터리만 제외시켜도 빌드 속도 개선
- 이미지 빌드 시에 사용하는 파일은 제외시키면 안 됨
도커 이미지 만들기 - 웹 애플리케이션 (node.js)
app.js
// Require the framework and instantiate it
const fastify = require('fastify')({
logger: true
})
// Declare a route
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
// Run the server!
fastify.listen(3000, '0.0.0.0', function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
fastify.log.info(`server listening on ${address}`)
})
Dockerfile
# 1. node 설치
FROM ubuntu:20.04
RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs npm
# 2. 소스 복사
COPY . /usr/src/app
# 3. Nodejs 패키지 설치
WORKDIR /usr/src/app
RUN npm install
# 4. WEB 서버 실행 (Listen 포트 정의)
EXPOSE 3000
CMD node app.js
.dockerignore
node_modules/*
이미지 빌드하기
docker build -t subicura/app .
컨테이너 실행하기
docker run --rm -d -p 3000:3000 subicura/app
Dockerfile 최적화 (빌드타임 628.2s -> 12.3s)
# 1. node 이미지 사용: 사용하지 않는 파일들을 제거한 alpine 이미지 사용
FROM node:12-alpine
# 2. 패키지 우선 복사: 캐시 기능 활용하여 변경되지 않는 부분을 우선 빠르게 복사
COPY ./package* /usr/src/app/
WORKDIR /usr/src/app
RUN npm install
# 3. 소스 복사: 자주 변경되는 부분을 나중에 복사
COPY . /usr/src/app
# 4. WEB 서버 실행 (Listen 포트 정의)
EXPOSE 3000
CMD node app.js
도커 허브 (docker hub) 이미지 관리
docker login
ID와 패스워드를 입력하여 로그인이 되면 ~/.docker/config.json
에 인증정보가 저장된다.
docker push {ID}/example
docker pull {ID}/example
이미지를 무료로 저장할 수 있지만 한 개의 이미지를 제외한 모든 이미지가 공개되기 때문에 비공개로 사용하려면 유료 서비스를 이용해야 한다.
도커 배포 기본
docker run --rm -d -p 3000:3000 subicura/app
컨테이너를 실행하면 이미지 pull과 컨테이너 start가 자동으로 진행된다. 즉, 서버에 이미지를 push하면 배포가 완료된 것이다.
더보기
이미지를 만들기 위한 다양한 쉘 스크립트 & 환경변수 사용
- CI/CD 자동빌드, 자동배포, blue & green 배포 / 무중단 배포하기
- 모니터링, 로그
- 가상 네트워크
- 보안
- 쿠버네티스 (kubernetes)
- 이스티오 서비스매시 (istio)