본문 바로가기
docker-kub

docker - introduction

by 오우지 2022. 9. 8.

docker란?

docker는 컨테이너의 생성 및 관리 프로세스를 단순화하는 도구이다.

 

도커를 가장 최근에 프로젝트를 실행하는데 사용했는데

제공자분이 다음과 같은 docker-compose.yml 파일을 만들어서 주셨고

나는 로컬에서 docker run을 통해 동일한 mysql 환경을 구축해 코드로 구현된 직접 JDBC 접속을 해야하는 실습을 예외 없이 진행할 수 있었다.

version: "3.9"
services:
  db:
    image: mysql:8.0.30
    platform: linux/x86_64
    restart: always
    ports:
      - "3308:3306"
    environment:
      MYSQL_ROOT_PASSWORD: mypass1
      MYSQL_DATABASE: springbook
      MYSQL_USER: spring
      MYSQL_PASSWORD: book
      TZ: Asia/Seoul
    volumes:
      - ../db/mysql/data:/var/lib/mysql
      - ../db/mysql/config:/etc/mysql/conf.d
      - ../db/mysql/init:/docker-entrypoint-initdb.d

 

왜 독립적이고 표준화된 애플리케이션 패키지가 필요할까?

 

1. 다른 개발 제품 환경을 가질 수 있기 때문에 배포 환경과 버전이 다르면 불편하다.

예를 들어 javascrip의 await를 사용하려면 특정 버전 이상의 node.js 버전이 필요하다. 만약 서버에 배포하는데 내 로컬 버전과 다른 node.js버전을 가지고 있다면 그것에 대한 대응을 해줘야 하는데

이런 문제를 해결하는데 드는 시간이 생각보다 많이 든다.

 

2. 팀이나 회사에서 다른 개발환경을 가져갈 수 있다.

동료와 같은 개발 환경을 가져가는데 편하다.

 

3. 여러 프로젝트를 실행하는데 특정 프로젝트가 낮은 버전의 언어로 프로그래밍 됐다면 매번 환경변수를 바꿔줘야 한다.

 

 


Virtual Machines and Container

출처: https://cloudacademy.com/blog/docker-vs-virtual-machines-differences-you-should-know/#:~:text=Docker%20containers%20are%20considered%20suitable,to%20run%20on%20different%20OS.

가상 머신은 로컬 머신의 OS 위에 가상 OS를 띄워 도커에 비해 오버헤드가 발생한다.

가상머신은

1. OS와 메모리에 대한 점유가 커진다.

2. 다른사람과 공유하고 다시 빌드하는 과정이 도커에 비해 어렵다.

3. 캡슐화할 수 있지만 app이나 환경뿐만 아닌 전체 머신을 캡슐화한다.

 

도커로 대표되는 컨테이너는 OS에 내장되어있거나 컨테이너 에뮬레이트를 지원하는 내장 컨테이너를 사용한다.

컨테이너는 위의 단점을 보완해준다.


간단한 도커 컨테이너를 띄워보자.

 

다음과 같은 express 서버를 띄우기 위해선 npm install을 통해 자바스크립트 라이브러리들을 다운받고 , node app.mjs를 통해 실행할 수 있다. 하지만 그건 로컬에서 띄우는 것을 의미하고 컨테이너는 다르다.

 

컨테이너에서 빌드하려면 

위와 같이 Dockerfile을 생성하고 docker build .를 실행하면 된다.

그럼 이미지가 생성되고 ID를 얻을 수 있다.

 

docker run -p 3000:3000 4456053814e7f98164b59f7c6e8ed097f0eb41f2421f7eee2d209335753ea3dc

다음과 같이 로컬 호스트를 지정하고 실행 가능하다.

애플리케이션의 정지는 새로운 터미널을 실행한 후

docker ps

를 실행하면 실행중인 컨테이너들이 표시되는데

docker stop {이름}

으로 종료할 수 있다.


컨테이너와 이미지

컨테이너는 이미지의 한 인스턴스라고 볼 수 있다. 

이미지는 코드, 설정등 여러것이 포함되며 컨테이너는 그 이미지의 실행 인스턴스이다.

docker run -it node

를 입력하면 컨테이너 안에서 노드 이미를 이용해 실행된 터미널에 접속해서 본 OS의 터미널과 통신할 수 있다.

여기서 사용한 이미지는 도커 허브에 node 제작사가 저장해놓은 node 이미지다.

 

이미지를 가져오는 방법은 크게 두가지로 볼 수 있다. 도커 허브같은 곳에 위치한 pre-built 이미지를 가져오던가 커스텀 이미지를 만들어서 가져올 수 있다. 

 

이제 Dockerfile을 이용한 도커 이미지를 만들어보자.

# 1. 도커 허브나 로컬의 이미지 이름을 넣는다.
FROM node

WORKDIR /app

# 2. 첫 번째 항목은 로컬 파일 시스템에서 도커 이미지화 할 디렉토리. 지금은 Dockerfile이 포함된 디렉토리를 의미한다.
# 3. 두 번째 항목은 이미지 내부의 경로를 의미한다. 로컬에 있는 파일들이 이미지 내부의 /app으로 복사된다.
COPY . /app
# 5. WORKDIR에 의해 작업 루트가 재설정됐기 때문에 아래와 같은 상대경로로 적어도 된다. 하지만 절대경로가 선호된다.
# COPY . ./

# 4. 복사된 이미지 안에서 실행할 명령어들을 적는데 기본 디렉토리가 root이므로 /app으로 이동해야 한다. 그게 두번째 줄의 WORKDIR이다.
RUN npm install

# 7. 도커 컨테이너는 격리돼 있기 때문에 이전에 사용한 docker run -p 3000:3000 처럼 로컬 머신에서 포트를 이어줘야 한다.
EXPOSE 80

# 6. RUN node server.js로 할 수도 있지만 우리는 이미지에서 node server를 실행하고 싶은 것이 아니라 컨테이너를 실행할 때만 실행하고 싶기 때문에 CMD 명령어를 사용한다.
CMD [ "node", "server.js" ]

 

docker build .

이미지를 빌드하고

이미지 코드를 run 하면 된다. 참고로 -p 80:80을 추가해줘야 정상 작동한다.

 

이미지는 닫힌 템플릿이다.

지금까지 작성된 소스 코드를 변경하고 이미 빌드된 이미지를 재실행하면 소스코드의 변경사항은 적용되지 않는다.

이미지는 읽기 전용이기 때문이다. 나중에 코드에서 변경사항을 선택하는 기능을 배우긴 하지만 우선 기본 개념은 읽기 전용이다. 따라서, 코드 변경 사항을 적용하기 위해선 이미지를 다시 만들어야 한다.

 

레이어 기반 아키텍쳐

이미지를 빌드하다 보면 이미 한번 빌드된 이미지는 다시 빌드하면 매우 빨리 빌드되는 것을 알 수 있다. 이것이 도커가 레이어 기반인 것을 알려주는데

도커는 모든 명령어를 캐싱하고 이미지를 다시 실행할 때 다시 실행할 필요가 없으면 캐싱된 결과를 사용한다.

이를 레이어 기반 아키텍쳐라고 한다. 레이어는 위의 Dockerfile의 라인 by 라인을 뜻한다.

 

위의 Dockerfile을 보면 여러 명령어가 존재하는데 명령어가 순차적으로 실행되면서 이전에 빌드했던 이미지에서 바뀐 부분이 없으면 캐싱된 데이터를 사용하고 특정 라인에서 데이터의 변경이 감지되면 그 이후의 레이어는 모두 다시 빌드된다.

 

이 뜻은 도커파일도 최적화가 가능하다는 뜻이다.

FROM node

WORKDIR /app

# 아래의 COPY와 위치를 바꿨다.
RUN npm install

COPY . /app

EXPOSE 80

CMD [ "node", "server.js" ]

개발자는 단순한 코드 변화에 로는 package.json을 리빌드 할 필요가 없다는 것을 알고있지만 docker는 모른다.

따라서 소스코드를 복사하기 전에 설정파일을 복사하면 코드 변경시마다 package.json을 빌드할 필요, npm install할 필요가 없다. 더 빠른 실행을 보장할 수 있다.