docker를 사용하면서 그 편리함을 느끼고 있었다. 그리고 요 근래 it회사에서 docker와 kubernets를 이용하여 인프라를 운영을 하는 것을 많이 들었다.

나는 그런 환경을 접해보지는 못했기 때문에 정확하게 kuberntes가 무엇인지 잘 모른다. 그래서 이번 기회에 kubernets(이하 쿠버네티스)에 대한 기본 개념을 정리하고 설치해서 공부를 위한 초석을 닦아보자.

 

쿠버네티스 (kubernetes)

쿠버네티스는 도커 컨테이너 운영을 자동화 하기위한 오케스트레이션 도구이다. 구글에서 만들었으며 컨테이너를 운영하고 다루기 위한 api와 cli등을 제공한다. 컨테이너 배포 이외에도 효율적인 컨테이너 배치 및 스케일링, 로드밸런싱, 헬스 체크, secure등의 기능을 제공한다.

AWS ECS 와 GClound에서 도커 관리 기능을 제공하면서 컨테이너를 사용한 애플리케이션 개발이 점차 보급 되었다.  기존에도 docker에 스웜(swarm)이라는 기능이 있었으나 사용해 봤을 때 불편한 점이 많았는데 쿠버네티스가 더 편리하게 이 점이 보완되었다. 

 

쿠버네티스 설치 (MacOs)

실습을 진행할 컴퓨터가 mac os이기 때문에 mac에서 설치하는 방법을 알아보자. 우선 docker에서 preference에 들어가면 kubernetes 탭이 존재한다. 여기서 아래와 같이 체크하고 Apply를 누르면 설정이 우선 완료된다.

그리고 cli로 쿠버네티스를 다루기 위한 도구인 kubectl을 설치한다.

curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl
chmod +x kubectl
mv kubectl /usr/local/bin

위에 명령어를 통해 kubectl을 명령어로 등록해주는데 자세한 방법은 홈페이지에 잘 나와있다.

https://kubernetes.io/docs/tasks/tools/install-kubectl/

설치가 완료된 후 버전을 확인해보면 출력되면 정상적으로 설치가 된 것 이다.

 

단일 노드 쿠버네티스 클러스터 Minikube 설치

쿠버네티스 클러스터에는 여러 노드들이 존재하지만 실습을 위해서 단일 노드로 되어 있는 Minikube를 설치해서 사용한다. 맥 기준으로 brew로 설치하면 되고 자세한 방법은 아래를 참조하면 된다.

brew cask install minikube

https://kubernetes.io/ko/docs/tasks/tools/install-minikube/

 

쿠버네티스 구성요소

쿠버네티스로 실행되는 애플리케이션은 노드, 네임스페이스, 파드, 레플리카세트, 디플로이먼트 등 다양한 리소스가 함께 연동해 동작한다. 

그 구성 요소들에 대해 간단하게 정리해보자.

1. 노드

-> 노드는 쿠버네티스 클러스터의 관리 대상으로 등록된 도커 호스트로 컨테이너가 배치되는 대상이다. 노드는 마스터와 일반 노드들로 구성되는데 쿠버네티스 클러스터 전체를 관리하는 서버에는 마스터가 적어도 하나 이상 있어야 한다. 마스터는 클러스터를 상호 조정하는 장치이고 노드는 애플리케이션이 실제로 돌아가는 곳이다. 노드에는 여러 파드들이 위치할 수 있고 그 파드에는 컨테이너들이 존재한다.

구글 이미지 출처

또한 쿠버네티스는 노드의 리소스 사용 현황 및 배치 전략을 근거로 컨테이너를 적절하게 배치한다. 클러스터에 배치된 노드 수, 노드의 사양 등에 따라서 노드에 배치할 수 있는 컨테이너의 수를 결정한다. 클러스터의 처리 능력은 노드에 의해서 결정된다.

kubectl get nodes

 

 현재 노드는 아까 설치한 단일 노드 Minikubes가 마스터로 위치해 있는 걸 볼 수 있다.

 

2. 네임스페이스

쿠버네티스 클러스터 내부에는 여러개의 가상 클러스터를 만들 수 있는데 이 네임스페이스는 하나의 공간으로써 사람또는 상황에 따라서 나눠서 사용할 수 있다. 기본적으로는 default로 되어있다.

 

3. 파드 (pod)

 pod는 컨테이너가 모인 집합체로써 하나 이상의 컨테이너로 이루어 진다. nginx, was처럼 서로간의 연결고리가 있는 컨테이너들은 하나로 묶어 일괄배포한다. pod는 노드내에 배치되고 같은 파드는 여러 노드에 배치할 수도 있고 한 노드에 여러개 배치할 수도 있다. pod는 하나 또는 그 이상의 애플리케이션 컨테이너 그룹을 나타내는 쿠버네티스의 추상적 개념으로 컨테이너에 대해 서로 자원을 공유한다.

  • 볼륨과 같은 공유 스토리지
  • 클러스터 IP 주소와 같은 네트워킹
  • 컨테이너 이미지 버전 또는 사용할 특정포트와 같은 각 컨테이너가 동작하는 방식에 대한 정보

 

apiVersion: v1
kind: Pod
metadata:
  name: sample-echo
spec:
  containers:
  - name: nginx
    image: ddd/nginx:latest
    env:
    - name: BACKEND_HOST
      value: localhost:8080
    ports:
    - containerPort: 80
  - name: echo
    image: dd/echo:latest
    ports:
    - containerPort: 8080
    
 # 파드 yaml 예제

 

4. 레플리카 세트

파드를 하나만 사용하여 가동할 경우에 실제 가용성이 떨어지기 때문에 레플리카 세트를 만들어서 여러 파드를 함께 구성해준 것이다.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: echo
  labels:
    app: echo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: echo
  template: # template하단은 pad.yaml과 같음

 

5. 서비스 (servce, svc)

서비스를 이용해서 각 Pod에 있는 애플리케이션을 외부에서 접근하게 할 수있다.

쿠버네티스에서 서비스는 하나의 논리적인 파드 셋과 그 파드들에 대한 접근할 수 있는 정책을 정의하는 추상적인 개념이다. 서비스는 종속적인 파드들 사이를 느슨하게 결합되도록 도와준다. 서비스는 모든 쿠버네티스 오브젝트들과 같이 yaml로써 정의 할 수있다. 각 서비스가 되는 대상을 labelSelector를 통해 지정할 수 있다.

각 파드들이 고유의 IP를 갖고 있기는 하지만, 그 IP들은 서비스의 도움없이 클러스터의 외부로 노출될 수 없다. 서비스들은 애플리케이션들에 트래픽이 실릴 수 있도록 허용해준다. 서비스들은 ServiceSpec에서 type을 지정함으로써 다양한 방식들로 노출시킬 수 있다.

clusterIp(기본값) - 클러스터 내에서 내부 IP에 대해 서비스를 노출해준다. 이 방식은 오직 클러스터 내에서만 서비스가 접근될 수 있도록 해준다.

NodePort - NAT가 이용되는 클러스터 내에서 각각 선택된 노드들의 동일한 포트에 서비스를 노출시켜준다.
<NodeIP>:<NodePort>를 이용하여 클러스터 외부로부터 서비스가 접근할 수 있도록 해준다. CluserIP의 상위 집합이다.

LoadBalancer - (지원 가능한 경우) 기존 클라우드에서 외부용 로드밸런서를 생성하고 서비스에 고정된 공인 IP를 할당해준다. NodePort의 상위 집합이다.

ExternalName - 이름으로 CNAME 레코드를 반환함으로써 임의의 이름(스펙에서 externalName으로 명시)을 이용하여 서비스를 노출시켜준다. 프록시는 사용되지 않는다. 이 방식은 kube-dns 버전 1.7 이상에서 지원 가능하다.

 

6. 디플로이먼트 (deployment)

서비스, 파드, 레플리카세트의 집합체로써 애플리케이션의 기초가 되는 단위이다. 디플로이먼트 내부에는 서비스, 파드, 레플리카세트 등을 한번에 구성하여 적용 시킬 수 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo
  labels:
    app: echo
spec:
  replicas: 3
  selector:
    matchLabels
      app: echo
  template: # template는 pod와 동일

 

서비스와 디플로이먼트에 차이는 서비스는 내부에 여러 pod들을 외부와 연결해주는 역할을 담당하고 디플로이먼트는 쿠버네티스에서 돌아가는 pod들의 상태를 확인하면서 재시작 등등을 진행하는 담당을 한다.

다음 시간에는 실제 애플리케이션 하나를 이미지로 빌드하고 그것을 minikube에 디플로이먼트를 통해 파드로 배포를 진행해본다. 그리고 스케일적용과 이미지 변경 시 자동으로 새로 배포되는 블루그린 배포도 적용해보자.

 

참고 싸이트

https://kubernetes.io/ko/docs/tasks/administer-cluster/highly-available-master/
https://kubernetes.io/ko/docs/tutorials/kubernetes-basics/deploy-app/deploy-intro/
https://kubernetes.io/ko/docs/concepts/overview/working-with-objects/labels/
https://devopscube.com/kubernetes-deployment-tutorial/

Multer는 파일 업로드를 위해서 사용되는 multipart/form-data를 다루기 위한 node.js 미들웨어이다. busyboy를 기반으로 하고 있다.
자세한 내용은 https://github.com/expressjs/multer/blob/master/doc/README-ko.md 이곳에서 참고하면 된다.

그럼 간단하게 multipart/form-data로 올린 이미지 파일과 텍스트파일을 request post로 받아서 처리하는 코드를 만들어보자.

우선 multer를 설치한다.
npm i multer

그리고 이미지 파일을 특정 경로에 저장해놓고 사용할 수 있지만 나는 메모리 스토리지를 사용해서 조작하는 방식으로 진행해보겠다.

multer 라이브러리를 선언하고 memoryStorage를 사용할 수 있도록 추가적으로 선언해준다.

1
2
3
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage });
cs

그리고 router에 미들웨어로 upload.single('productImage')를 넣어서 productImage 필드로 넘어온 값을 버퍼로 가져오도록 한다. array, fields 등등 다른 메소드들도 있으나 나는 이미지가 하나라서 single을 사용했다.

1
2
3
4
5
6
7
8
9
router.post('/test', upload.single('productImg'), async (req, res, next) => {
    try {
      console.log(req.file);
      res.json(req.body);
    } catch (e) {
      next(e);
    }
  });
 
cs

그럼 postman을 통해서 데이터를 보내보자.


전송정보

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"method": "POST",
"url": "/info/test",
"headers": {
"authorization": "Bearer 8c6076013db8af716df89b1b48b90c9b2b0fad6c",
"content-type": "multipart/form-data; boundary=--------------------------820616490467091968093229",
"cache-control": "no-cache",
"postman-token": "217b466f-1f40-402e-b2cd-153426c61cc7",
"user-agent": "PostmanRuntime/7.6.0",
"accept": "*/*",
"host": "127.0.0.1:8081",
"accept-encoding": "gzip, deflate",
"content-length": "97764",
"connection": "keep-alive"
},
"body": {},
"query": {},
"level": "info",
"message": "Access"
}
 
cs

이렇게 전송하고 debug로 전송된 정보를 체크해보면 다음과 같이 출력된다.

이 정보를 이용해서 text 파일들은 파일대로 다루고 이미지 파일은 버퍼를 사용해서 s3에 저장을 하던지 여러가지 동작을 진행할 수 있다.

AWS에서 제공하는 S3 스토리지는 다양한 파일을 bucket에 보관할 수 있다. 자세한 내용은 생활코딩 강의 참고하면 좋다. https://opentutorials.org/course/608/3006


그럼 우선 aws sdk를 설치하여야 한다.

1
npm i aws-sdk --save
cs


그리고 AWS IAM에서 생성한 사용자가 있어야 한다. 사용자는 S3에 대한 권한을 가지고 있어야 한다.


사용자를 생성하고 권한을 부여하는 기능은 어렵지 않기때문에 검색해서 적용하면 된다.

그리고 해당 사용자를 생성하면 csv 파일로 secretkey를 받을 수 있다. 이를 AWS S3에 접근하여 사용하기 위해서 사용된다.




기본 정보가 담긴 config.json 생성

우선 부여 받은 사용자 accessKeyId와 secretAccessKey 그리고 지역정보가 담긴 config.json 파일을 만든다.

1
2
3
4
5
{
  "accessKeyId""<accessKeyId>",
  "secretAccessKey""<secretAccessKey>",
  "region""ap-northeast-2"
}
cs


그리고 aws-sdk 라이브러리를 로드하면서 위에 생성한 config.json 파일을 추가해준다. 그리고 S3 객체의 경우 로드한 aws-sdk에서 new와 함께 s3() 메소드를 실행하여 만들 수 있다.

1
2
3
4
5
6
'use strict';
 
const AWS = require('aws-sdk');
const path = require('path');
AWS.config.loadFromPath(path.join(__dirname, 'config.json'));
const S3 = new AWS.S3();
cs


그리고 이미지 파일을 업로드 하기위해서 제공하는 메소드인 upload에는 bucket 이름, 파일의 고유 키, 파일이 담긴 Body, Body의 contentType 정보가 필요하다. 나는 이미지를 올리려고 하기 때문에 contentType은 image/jpg로 하였다.

1
2
3
4
5
   const params = {};
    params.Key = `image/${md5(url)}.jpg`;
    params.Bucket = bucket;
    params.Body = await request(url);
    params.contentType = 'image/jpg';
cs

생성된 최종 코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
'use strict';
 
const AWS = require('aws-sdk');
const path = require('path');
AWS.config.loadFromPath(path.join(__dirname, 'config.json'));
const S3 = new AWS.S3();
const md5 = require('md5');
const request = require('request-promise');
const bucket = 's3-buckets';
 
class AwsS3 {
 
  async upload(url)  {
    const params = {};
    params.Key = `image/${md5(url)}.jpg`;
    params.Bucket = bucket;
    params.Body = await request(url);
    params.contentType = 'image/jpg';
 
    S3.upload(params, function (err, data) {
      console.log(err);
      console.log(data);
    });
  }
 
}
 
module.exports = new AwsS3();
 
cs


그리고 테스트 코드를 작성하여 동작 시켜보면 정상적으로 파일이 올라간것을 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
'use strict';
 
const s3 = require('../../lib/s3');
 
describe('s3 test', () => {
  it('upload', async () => {
    await s3.upload('http://ticketimage.interpark.com/Play/image/large/18/18009670_p.gif');
  });
});
cs


 이런 문제가 발생하면 config.json에 기재한 키 정보를 다시한번 확인해봐야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{ InvalidAccessKeyId: The AWS Access Key Id you provided does not exist in our records.
    at Request.extractError (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/services/s3.js:580:35)
    at Request.callListeners (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:109:20)
    at Request.emit (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:81:10)
    at Request.emit (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:685:12)
    at Request.callListeners (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:119:18)
    at Request.emit (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:81:10)
    at Request.emit (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/request.js:685:12)
    at Request.callListeners (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:119:18)
    at callNextListener (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/sequential_executor.js:99:12)
    at IncomingMessage.onEnd (/Users/wedul/Documents/repository/nodejs/node_modules/aws-sdk/lib/event_listeners.js:294:13)
    at emitNone (events.js:111:20)
    at IncomingMessage.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:139:11)
    at process._tickDomainCallback (internal/process/next_tick.js:219:9)
  message: 'The AWS Access Key Id you provided does not exist in our records.',
  code: 'InvalidAccessKeyId',
  region: null,
  time: 2018-10-01T15:53:23.570Z,
  cfId: undefined,
  statusCode: 403,
  retryable: false,
  retryDelay: 32.0943451721504 }
 
cs


참고 URL
https://www.npmjs.com/package/aws-sdk
https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-creating-buckets.html

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-json-file.html

Docker의 컨테이너를 실행시키기 위해서는 다음가 같은 명령어를 사용할 수 있다.

 

docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]
옵션 설명
-d detached mode 흔히 말하는 백그라운드 모드
-p 호스트와 컨테이너의 포트를 연결 (포워딩)
-v 호스트와 컨테이너의 디렉토리를 연결 (마운트)
-e 컨테이너 내에서 사용할 환경변수 설정
–name 컨테이너 이름 설정
–rm 프로세스 종료시 컨테이너 자동 제거
-it -i와 -t를 동시에 사용한 것으로 터미널 입력을 위한 옵션
–link 컨테이너 연결 [컨테이너명:별칭]
/bin/bash bash 쉘 접속 가능


그럼 한번 mysql을 실행 시켜보자.기본적으로 docker run 이미지를 입력하여 컨테이너를 실행시킬 경우에 이미지가 다운받아진 경우가 없는경우에는 Docker 레퍼지토리에서 pull 한후에 컨테이너를 생성하고 시작한다.

-p 옵션의 경우에는 2222:3112로 설정할 수 있다. 이럴경우 호스트에서 2222로 접속하면 내부에서 컨테이너에 6379로 매핑하여 실행한다.

그럼 이 옵션을 이용해서 2개의 mysql을 실행시켜보자.

 

Mysql 이미지 사용하기

기본적인 설명은 Mysql의 Docker 문서를 참고하면 된다.

간편하게 사용테스트를 진행하기 위해서 비밀번호가 없고 보트를 3306과 3310 두개를 사용하는 mysql을 만들고 docker container가 종료되면 바로 삭제하는 옵션을 사용해서 진행해보자. 두 개 모두 서로 다른 방식으로 진행해보자.

먼저 첫번째는 컨테이너를 명령어를 실행시켜서 진행하는 방식을 사용해보자.

docker run -d --rm -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true --name mysql_test1 mysql:5.7

-e 옵션에 MYSQL_ALLOW_EMPTY_PASSWORD의 경우 패스워드 없이 사용할 수 있는 옵션이다.


명령어를 실행하면 기존에 Mysql의 이미지가 없었기 때문에 이를 다운로드 받는다.

그리고 모든 컨테이너 적제가 완료되면 컨테이너 내부에 mysql을 제어하기 위해 docker 명령어를 사용하여 들어가보자.

docker exec -i -t mysql_test1 bash

 

 

성공적으로 접속한 것을 확인할 수 있다. 접속해서 User를 만들고 접속권한을 부여한다.

그 다음 쿼리박스를 이용해서 접속을 시도해보자. 성공적으로 접속을 완료했다.

 

그럼 이제 3307 포트를 사용하는 다른 Mysql을 docker에서 제공하는 docker-compose를 사용하여 만들어보자.

docker-compose란 무엇인가?

우선 기존에는 docker에 필요한 이미지를 받고 실행 방법을 설정하기 위해서 일일히 명령어에 추가하기에는 어려움이 있다. 방금 전 까지만 해도 비밀번호 없게 하라, 포트는 무엇으로 하라 등등 많은 설정을 명령어 라인으로 실행했었다. 

하지만 이를 yml 문법에 맞게 설정파일을 작성한 뒤에 docker 레포지토리에서 다운을 요청하면 그에 맞게 실행파일 및 설정이 적용되어 컨테이너에 이미지를 적재 시켜준다. 훨씬 간편하다.

그럼 우선 yml 문법으로 mysql 이미지를 실행하는 문법을 만들어보자.

version: '3.1'
services:
  db:
    image: mysql:5.5
    container_name: mysql5.5_test2
    ports:
      - "3307:3306"
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=dbsafer00
      - MYSQL_USER=wedul
      - MYSQL_PASSWORD=dbsafer00
      - MYSQL_DATABASE=wedul

문법은 생각보다 간단하다. image 버전과 container_name을 적고 포트의 인바운드 아웃바운드를 적어준다. 그리고 추가적인 환경으로 mysql의 정보를 기재할 수 있다. 

그리고 docker-compse 명령어로 컨테이너에 등재시킬 수 있다.

docker-compose up -d

도커 프로세스를 확인해보면 두 개의 mysql 컨테이너가 실행중인 것을 확인할 수 있다.

 

두 번째에 실행시킨 Mysql도 동일하게 QueryBox에서 사용이 가능하다.

 

기존에는 DBMS가 필요하거나 할 때, VM을 사용하거나 실 서버를 사용하였지만 도커를 이용하면 아주 간단하게 사용할 수 있어서 좋은 것 같다. 이번에는 Mysql만 해보았지만, Repository에 있는 워드프레스나 레디스 등등도 사용할 수 있따. 실무에서도 시간이 나면 환경을 만들어서 써봐야겠다. 

 

 


 

 

마이크로서비스를 사용하고 배포를 자동화하고 하면서 유동적인 컨테이너인 Docker가 많은 인기를 얻고 여기저기 회사에서 많이 사용되고 있다. 실제로 백엔드 개발자에게는 거의 필수로 알고 가야하는 툴중에 하나가 되어버렸다.

 

Docker란 무엇인가?

도커는 컨테이너 기반의 오픈소스 가상화 플랫폼 이라고 한다. 여기서 컨테이너는 다양한 프로그램, 실행환경을 컨터이너에  동일한 인터페이스로 제공하여 여러 프로그램의 배포와 관리를 단순하게 해준다.

 

컨테이너 (Container)

컨테이너는 OS 와 격리된 공간으로서 별도의 프로세스가 동작하는곳을 말한다. 그러면 기존에 OS에서 VM을 설치하여 사용하던 방식과 무엇이 다른지 알아보자.

 


VM과 Docker의 차이


기존의 VMware는 HostOS에 GuestOS가 올라가는 방식으로 추가로 OS 파일이 올라가야 하기 때문에 부담이 된다. 하지만 컨테이너에 경우 별도의 OS를 또 올릴 필요없이 프로세스를 격리하는 방식으로 이러어져있다. 그렇기 때문에 훨씬 가볍고 빠르며 CPU, 메모리 프로세스가 필요한 만큼 추가할 수 있다.

그리고 서로 다른 컨테이너는 서로 영향을 주지 않고 각각의 컨터이너에 접속하여 명령어도 별도로 사용할 수 있고 여러개의 프로세스를 백그라운드로 실행할 수도 있다.

컨터이너에는 보안적인 문제가 있다. 컨테이너는 host와 커널을 서로 공유하기 때문에 container가 뚫리게 되면 host 커널에도 영향이 갈 수있다. 또한 다른 컨터이너도 같이 공유하고 있기 때문에 문제가 될 수있다.

그렇다면 이런 컨터이너에 실행되는 파일과 설정값을 가지고 있는것이 무엇인가? 바로 이미지(Image)이다. 

 


이미지 (Image)


이미지는 컨터이너에 실행에 필요한 파일과 설정등이 다 들어있다. 컨테이너는 이미지를 실행한 상태라고 볼 수 있고, 추가되거나 변하는 값은 컨터이너에 저장된다. 같은 이미지에서 여러 컨테이너를 생성하더라도 수정, 삭제 하더라도 이미지의 값이 바뀌지는 않는다. 이미지는 Docker hub에 이미 등록되어 있고 추가적으로 등록할수도 있다. 개인의 Docker Registry 저장소를 직접 만들어 관리할 수 있다.

 

 


이미지 경로는 다음과 같이 되어있는데 모두 사용할 필요없이 간단하게 ubuntu:14.04와 같이 사용할 수 있다.

 



레이어 저장방식



도커 이미지는 실행하는 정보를 가지고 있기 때문에 용량이 크다. 그래서 이미지에 파일이 추가되었다고 파일 하나를 다시 받는다면 문제가 된다. 그래서 도커는 레이어라는 개념을 사용하였다. 기본적인 레이어는 동일하게 유지하며(읽기모드) 나머지 부분에 대해서만 쓰기 레이어를 만들어 사용한다. 그렇기 때문에 이미지 레이어는 그대로 사용되고 실행중에 생성하는 파일, 변경된 내용은 읽기/쓰기 레이어에 저장되므로 여러 컨터이너를 생성해도 최소한에 용량만 사용된다.

 


다음시간에는 Mac에 Docker를 설치해보자.


 출처 : https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html

+ Recent posts