Docker 간단하게 알아보기

rvlwldev·2023년 3월 27일
0

CS

목록 보기
7/12
post-thumbnail

Docker의 탄생

기존의 VMWare, Virtual Box 등의 가상화 기술은 많은 컴퓨터 자원을 필요로 하고 시스템 운영과 소프트웨어 개발의 지식을 모두 필요로 했기에 관리적인 측면에서 부담이 컸다.

이러한 부담을 줄이기 위해 도커(Docker)가 개발되고 2013년 3월 26일에 출시되었으며
2008년에 개발된 리눅스 컨테이너(LXC) 기반으로 애플리케이션의 개발, 배포, 실행을 더 쉽고 간편하게 할 수 있도록 개발된 오픈소스 플랫폼이다.

도커 첫 시연 영상

도커 소개영상

도커의 발표 이후,
당시 다양한 이유로 서버 환경과 개발 환경이 바뀌는 부담을 획기적으로 줄일 수 있어서 각광받기 시작했으며 본격적으로 컨테이너 기술의 확산에 기폭제 역할을 했다.

동작방식

리눅스 컨테이너(LXC) 기술을 기반으로 하며, 애플리케이션을 개발, 배포, 실행할 때 격리된 환경을 제공하여 도커가 실행되는 기반 시스템과의 간섭을 최소화된 상태로 동작한다.

리눅스 컨테이너를 기술을 기반으로 하기 때문에 초기에는 리눅스에서만 사용이 가능했으나
맥에서는 이전에 VirtualBox 또는 Xhyve 가상화 기술을 사용하여 리눅스에서 실행되었으며 현재는 내장된 Hypervisor를 사용하고, 윈도우에서는 Hyper-V를 사용하여 도커를 사용할 수 있다.

도커로 가상화 기술을 구현하기 위해 크게 3가지로 나눌 수 있다.

Dockerfile

도커파일은 이미지가 빌드되기 위한 스크립트파일이다.
이미지를 빌드할 때 필요한 설정과 명령어등의 생성과정을 담고 있다.
기본적인 구조는 아래와 같다.

  • FROM
    빌드할 이미지의 베이스 이미지를 지정
  • RUN
    새로운 레이어에서 명령어를 실행
  • CMD
    컨테이너가 시작될 때 실행될 기본 명령어를 설정
  • COPY
    호스트 파일을 컨테이너 내부로 복사
  • ADD
    호스트 파일을 컨테이너 내부로 복사하며, 압축 파일의 경우 자동으로 압축을 푼다.
  • WORKDIR
    작업 디렉토리를 설정
  • EXPOSE
    컨테이너가 사용하는 포트를 설정

Docker Image

dockerfile에 의해 빌드된 이미지는 컨테이너를 생성하기 위해 사용되며, 이미지는 해당 프로그램이 실행되기 위한 파일, 코드, 라이브러리 등을 포함한다.

이미지는 레이어(Layer)라는 개념을 사용하여 구성되며, 같은 계층의 레이어는 이미지들 사이에서 공유되기 때문에 이미지의 용량이 커지는 것을 방지하고 이미지의 빌드, 배포 시간을 단축시킬 수 있다.

예를 들어,

위 그림처럼 같은 계층의 레이어 정보를 nginx, webapp 이미지가 저장하고 있는 것이 아닌 ubuntu 이미지의 A,B,C 레이어들을 상속받아 사용하고 있는것이다.

때문에 용량을 절약할 수 있을 뿐만 아니라, 레이어를 공유하는 이미지를 더 빠르게 공유할 수 있고, 더 쉽게 이미지의 정보를 업데이트 할 수 있다.

출처 : https://www.ahnlab.com/kr/site/securityinfo/secunews/secuNewsView.do?seq=30533

컨테이너

독립된 실행환경을 제공하는 도커의 핵심적인 기능이다.
위에서 설명한 이미지를 로드해서 독립된 환경에서 간섭없이 실행시켜준다.
가상 머신(VM)과는 달리 커널을 공유하기 때문에 비교적 가볍고 빠르게 실행될 수 있다.

가상머신과의 차이점

기존의 VM은 보통 하나의 가상화를 위해 위 그림처럼 게스트 OS를 각각 가상화 대상에 설치해줘야 한다.
각각의 게스트 OS에 사용될 자원을 미리 정해놓고 동작하기 때문에 효율적으로 동작되기 힘들고 곧 확장성(Scalability)의 문제로 이어진다.

Namespace, cgroups(Control Group)

도커의 가상화 기술은 위에서 말한대로 리눅스 컨테이너 기반으로 개빌되었다.

  • Namespace
    프로세스들을 격리시키는 리눅스의 가상화 기술이다.
    이 기술을 사용하여 실제로 도커의 컨테이너가 동작한다.

  • cgroups
    프로세스들의 자원 사용량을 제한, 제어하는 리눅스의 기술이다.
    도커에서는 가상머신과는 다르게 처음부터 사용될 자원을 할당하지 않고 컨테이너에서 사용될 메모리, 네트워크 대역폭 등의 자원을 동적으로 설정하기 위해서 사용된다.

출처
https://velog.io/@geunwoobaek/컨테이너-및-도커-개념정리
https://imjeongwoo.tistory.com/106

사용 예시

어플리케이션 예시

public class Main {
    public static void main(String[] args) {
        String osName = System.getProperty("os.name");

        while (true) {
            System.out.println("Hello, Docker!");
            System.out.println("현재 운영체제 : " + osName + "\n");
            Thread.sleep(1000);
        }
    }
}
실행결과

Hello, Docker!
현재 운영체제 : Mac OS X

Hello, Docker!
현재 운영체제 : Mac OS X

간단하게 자바로 현재 운영체제의 이름을 반복적으로 출력하는 코드를 작성 후
dockertest.jar 파일로 만들었다.

Dockerfile 작성 예시

# 자바11 이미지 기반 
FROM openjdk:11 

# 작업 경로 설정
WORKDIR /app

# 이미지 안에서 복사될 실행파일과 경로 설정
COPY dockertest.jar /app

# java -jar dockertest.jar 로 실행하라고 명령어 설정
CMD ["java", "-jar", "dockertest.jar"]

위와 같이 Dockerfile을 작성하고 해당 디렉토리에서 docker build -t docker_test . 명령어를 실행해서 이미지를 빌드한다.
(-t docker_test 부분을 이미지의 이름을 지정하며 . Dockerfile의 위치를 가르킨다.)

베이스 이미지로 사용하는 jdk이미지가 없다면 도커허브(docker.io/Library/)에서 다운로드 받아오기 때문에, 조금 오래 걸릴 수 있으며 jdk를 포함한 해당 이미지는 약 650MB 의 용량을 가졌다.

빌드 완료 후 docker images 명령어로 이미지 목록을 확인할 수 있다.

ex)

REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
docker_test   latest    dfea1eb2a2f8   2 minutes ago   645MB

실행 및 확인

기본적으로 docker run docker_test 명령어를 통해서 해당 이미지를 실행시킬 수 있다.
이 경우 자동으로 컨테이너가 생성되고 자동으로 컨테이너의 이름도 지정해주고 바로 출력값을 확인할 수 있다.

이름을 지정해주고 싶다면 --name 명령어를, 백그라운드로 실행시키고 싶다면 -d 명령어를 추가할 수 있다.
출력 결과를 확인하기 위해 docker logs 컨테이너이름 명령어를 사용할 수 있다.

실행 예시)

~/dockertest$ docker run --name test-container -d docker_test
9b69006b570c9de25c4a8d17489927fb313aef5ea4eeb5542daaaa0b3532a30f

~/dockertest$ docker logs test-container
Hello, Docker!
현재 운영체제 : Linux

Hello, Docker!
현재 운영체제 : Linux

Hello, Docker!
현재 운영체제 : Linux

~/dockertest$ docker ps
CONTAINER ID   IMAGE         COMMAND                  CREATED              STATUS              PORTS     NAMES
9b69006b570c   docker_test   "java -jar dockertes…"   About a minute ago   Up About a minute             test-container

docker logs test-container 명령어에 -f를 추가하여 도커 첫 시연 영상과 비슷하게 실시간으로 출력결과를 확인할 수 있다.

또한 도커는 리눅스 기반으로 동작하기에 System.getProperty("os.name"); 의 값이 Mac OS X에서 Linux로 변경된것도 확인할 수 있다.

도커를 잘 활용하는 방법

Docker Hub (https://hub.docker.com)

Docker Hub는 Github처럼 도커 이미지를 저장하고 공유하는 데 사용되는 공식 레지스트리이다. Docker Hub에 이미지를 저장하고 관리하면 다른 사람들과 공유할 수 있다.

터미널에서 docker search '이미지명' 명령어로 간단하게 검색하고
docker pull '이미지명'로 간단하게 받아올 수 있다.

멀티스테이지 빌드

이미지를 더 효율적으로 빌드하기 위해 고안된 방법 중 하나이다.
여러 스테이지로 이미지를 빌드하면 최종 이미지의 용량을 줄일 수 있으며 곧 더 빠른 배포로 이어지게 된다.
예를들어 빌드과정에서 필요한 이미지는 빌드과정에서만 사용하고 컨테이너로 실행될 때는 제외시킬 수 있다.

아래는 멀티스테이지로 빌드하는 예시이다.

# 빌드환경
FROM gradle:7.3.3-jdk11 AS build
WORKDIR /app
COPY build.gradle settings.gradle ./
COPY src ./src
RUN gradle build --no-daemon

# 런타임환경
FROM openjdk:11-slim
WORKDIR /app
COPY --from=build /app/build/libs/dockertest-0.0.1-SNAPSHOT.jar ./dockertest.jar
CMD ["java", "-jar", "dockertest.jar"]

위 예시는 빌드환경에서 Gradle을 사용하여 빌드된 dockertest-0.0.1-SNAPSHOT.jar 파일을 실행환경을 openjdk:11-slim 베이스이미지만을 사용하여 실행시키는 간단한 예시이다.

각각의 환경을 스테이지라고 부른다.
이 스테이지들은 독립적으로 실행되며 이전 단계의 스테이지에서 파일을 복사해올 수도 있다.
(위 예시의 /app/build/libs/dockertest-0.0.1-SNAPSHOT.jar 파일)

이미지를 빌드하면 Gradle 이미지를 포함하고 있지 않기에 더 가벼워진다.

참고

여러 빌드환경을 사용할때?

또한 하나의 프로그램을 서로 다른 환경에서 지원하기 위해 사용되기도 한다.

이를 위해 인수(argument)를 사용할 수 있다.
예시로 위 Dockerfile을 빌드환경에서 Maven 또는 Gradle을 사용할지 인수로 판단할 수 있다.

수정된 Dockerfile 예시)

ARG BUILD_TOOL

# 빌드환경
FROM gradle:7.3.3-jdk11 AS build
WORKDIR /app
COPY build.gradle settings.gradle ./
COPY src ./src
# BUILD_TOOL 인수에 따른 다른 명령어 실행
RUN if [ "$BUILD_TOOL" = "gradle" ]; then \
        ./gradlew build --no-daemon; \
    else \
		apt-get update && apt-get install -y maven && mvn package ; \
    fi

# 런타임환경
FROM openjdk:11-slim
WORKDIR /app
COPY --from=build /app/build/libs/dockertest-0.0.1-SNAPSHOT.jar ./dockertest.jar
CMD ["java", "-jar", "dockertest.jar"]

위 수정된 예시는 BUILD_TOOL 인수를 받아 빌드도구를 선택한다.
gradle로 인수를 설정했으면 ./gradlew build --no-daemon 명령어를,
아니면 apt-get update && apt-get install -y maven && mvn package 명령어로 maven을 설치하고 빌드한다.

이미지로 빌드할때 인수를 넘기는 방법은 다음과 같다.

docker build --build-arg BUILD_TOOL=gradle -t docker_test .

Volume 사용

도커에서의 Volume이란, 컨테이너와 호스트 운영체제가 파일을 공유하는 공간이다.
이 볼륨을 잘 활용하면 데이터의 지속성과 유연성을 보장할 수 있다.

예를 들어 Mysql을 볼륨없이 사용하고 있을 때, 컨테이너를 중지하거나 삭제하면 테이블과 컬림의 정보가 모두 날아갈 것이다.

하지만 볼륨을 사용하여 컨테이너의 파일 시스템과 호스트의 파일 시스템을 공유하게 되면, 어떤 이유로 컨테이너가 중지되었어도 데이터를 유지할 수 있다.

볼륨 사용 예시)

// 볼륨 생성
~$ docker volume create test-mysql-vol
test-mysql-vol

// 생성한 볼륨과 mysql의 컨테이너의 디렉토리 공유한 상태로 실행
~$ docker run -d --name mysql-container -v test-mysql-vol:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=1234 mysql

// 실행이 성공했다면 컨테이너의 ID값이 출력됨!
f3a38df6ff17b5641decc1ef27c03ad5749a4145663a1863bf427d0d9ae7f2e5

공유된 도커 볼륨을 직접 접근하려면?

inspect 명령어로 test-mysql-vol의 경로를 확인해보면

~$ docker inspect volume test-mysql-vol
[
    {
        "CreatedAt": "2023-03-30T04:07:40Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/test-mysql-vol/_data",
        "Name": "test-mysql-vol",
        "Options": {},
        "Scope": "local"
    }
]

Mountpoint 정보에서 해당 경로를 확인할 수 있는데 아마 해당 경로는 눈을 씻고 찾아봐도 없을 것이다.

이유는 위에서 서술한대로 맥 OS환경에서는 Xhyve, 또는 Hypervisor를 사용하여 리눅스 운영체제 환경에서 도커가 실행되기 때문에 맥에 내장된 가상화 환경에 존재하는 디렉토리이다.
즉, docker volume create로 생성된 볼륨은 VM환경에서 존재한다.

이 내장된 가상환경에 엑세스하기 위해서는 복잡할 뿐더러 권장되지 않는 방법이기에 리눅스 환경이 아닐 경우 보통 docker volume create 명령어를 사용하는 대신 직접 경로를 입력해주는게 여러모로 편하다.

직접 디렉토리를 설정하는 예시)

~/$ docker run -d --name mysql-container -v $(pwd)/test-vol:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=your_password mysql
5cd3d1fe614fd2a2561b2efb4be7d60d065c9614e970607373f674bc5de7069f

명령어에 -v $(pwd)/test-vol:/var/lib/mysql 부분은
volume을 현재경로에서(pwd) test-vol 디렉토리와 컨테이너의 /var/lib/mysql와 공유하겠다는 의미이다.
(:으로 구분하며 현재경로란 해당 이미지를 빌드하는 Dockerfile의 위치이다.)


위 명령어로 컨테이너를 실행하면 test-vol 폴더와 mysql의 파일들이 생성된걸 확인할 수 있다.

이 방식으로 컨테이너가 날아가도 데이터의 지속성을 보장할 수 있다.

Docker Swarm & Kubernates

도커 컴포즈와 쿠버네티스는 모두 컨테이너 오케스트레이션 도구이다.

컨테이너 오케스트레이션이란? 참고

개별적으로 컨테이너화 되는 MSA 아키텍쳐로 설계된 서비스나 그의 준하는 설계의 서비스의 경우 다수의 컨테이너를 정리하는 프로그램, 프로세스 등을 뜻한다.
대규모 서비스의 경우 단일 호스트 운영체제로만 컨테이너를 실행시키는 경우는 드물뿐더러 각기 다른 호스트 위에서 상호작용하는 컨테이너들의 관리가 어려워질 수 있기에 사용되는 도구이다.

기본적으로 컨테이너들의 스케일링, 모니터링, 스케쥴링 등의 기능을 지원한다.

Docker Swarm

도커 1.12 버전이라면 (2016년) 기본적으로 내장되어있는 컨테이너 오케스트레이션이며 쉽게 사용할 수 있다. 때문에 가장 진입장벽이 낮다.

참고

Kubernates

K8s라는 약어를 사용하며 Docker Swarm보다 확장된 기능을 지원하며 대규모 서비스의 컨테이너 오케스트레이션에서 거의 표준으로 사용되는 구글에서 개발한 오픈소스 도구이다.

참고

Docker의 자주 사용되는 명령어 정리

맨 앞 docker 명령어는 생략됨

-d, --detach, -p, --publish 와 같은 옵션은
각각의 명령어에 -h 또는 --help 옵션으로 사용가능한 옵션을 조회할 수 있다.

이미지 관련

  • build : 이미지를 빌드
  • pull : 이미지를 Docker Hub 등에서 다운로드
  • push : 이미지를 Docker Hub 등에 업로드
  • images : 이미지 목록 조회
  • history : 이미지 생성 과정을 보여줌
  • save : 이미지를 아카이브로 저장
  • tag : 이미지에 태그를 추가 또는 변경
  • inspect : 이미지에 대한 정보를 상세하게 출력
  • import : 파일에서 이미지를 생성
  • rmi : 도커 이미지 삭제

컨테이너 관련

  • run : 컨테이너를 생성하고 실행
  • start : 컨테이너를 실행
  • stop : 컨테이너를 정지
  • pause : 컨테이너 일시 정지
  • unpause : 일시 정지된 컨테이너 재개
  • rm : 컨테이너를 삭제
  • ps : 컨테이너 목록을 조회
  • exec : 도커 컨테이너 내부에서 명령어를 실행
  • logs : 도커 컨테이너의 로그를 출력
  • cp : 도커 컨테이너와 로컬 파일 시스템 사이에서 파일을 복사
  • top : 컨테이너에서 실행 중인 프로세스 목록 표시
  • stats : 컨테이너의 CPU, 메모리, 네트워크 등의 성능을 실시간 모니터링
  • attach : 실행 중인 컨테이너에 접속
  • wait : 컨테이너가 종료될 때까지 대기
  • diff : 컨테이너 내부에서 변경된 파일 목록 출력
  • rename : 컨테이너 이름 변경
  • export : 컨테이너 파일 시스템 내용을 tar 압축으로 출력

네트워크 관련 명령어

  • network ls : 네트워크 목록을 조회
  • network create : 네트워크 생성
  • network connect : 네트워크와 컨테이너를 연결
  • network disconnect : 네트워크와 컨테이너 연결해제

볼륨 관련

  • volume ls : 볼륨 목록 조회
  • volume create : 볼륨 생성
  • volume rm : 볼륨 삭제
  • volume inspect : 볼륨 상세 정보 조회

시스템 관련

  • version: 클라이언트와 서버의 도커 버전 확인
  • info: 도커 서버의 정보 확인
  • events : 도커에서 발생하는 이벤트를 실시간으로 조회
  • system df: 도커에서 사용 중인 자원들의 사용량 확인
  • system events: 도커에서 발생하는 이벤트들을 실시간 조회
  • inspect : 도커 자원의 상세 정보를 조회
  • system prune: 사용하지 않는 자원들 정리
  • login: 도커 레지스트리(Docker Hub)에 로그인
  • logout: 도커 레지스트리(Docker Hub)에서 로그아웃
  • search: 도커 허브에서 이미지를 검색
  • update : 컨테이너나 서비스의 설정 업데이트
  • wait : 컨테이너가 종료될 때까지 대기

서비스 관련

  • service ls : 서비스 목록 조회
  • service create : 서비스 생성
  • service scale : 서비스의 레플리카 수 조정
  • service update : 서비스 설정 업데이트
  • service rm : 서비스 삭제

0개의 댓글