Docker

실습을 통해 전반적인 도커(Docker) 개념 알아보기

kym8821 2025. 1. 10. 18:38

Docker란?

우선 도커에 대한 개념을 먼저 읽어보자

Docker는 리눅스 응용 프로그램들을 소프트웨어 컨테이너에 배치하는 일을 자동화하는 오픈소스 프로젝트이다. 

솔직히 말해서 정말 어려운 문장이다... 이 문장을 이해하기 위해서는 컨테이너와 가상화에 대한 기초 개념이 필요하다.

 

가상화

가상화는 하드웨어를 추상화하는 기술이다.

하나의 하드웨어 자원을 여러대인 것처럼 운용하거나, 여러개의 장치를 하나의 장치인 것처럼 제공해준다.

간단한 예시를 들자면, 전자는 클라우드 서비스이고 후자는 클러스터링이 있다.

아래 그림은 어떤 방식으로 가상화가 이루어지는지를 그림으로 나타낸 것이다. 가상화 방식 또한 여러 가지가 있으므로 더 깊은 공부를 원한다면 추가적으로 알아보는 것을 추천한다.

우선 위 그림에 나와있는 각 요소들에 대해서 알아보자

  • 가상 머신 : 하나의 물리적 장치를 여러개의 가상의 장치로 나눌 때, 각각의 가상의 장치를 지칭한다.
  • GuestOS : 가상 머신이 자신이 구동중인 하드웨어 자원에 접근하기 위해 사용하는 운영체제이다.
  • 하이퍼바이저 : 단일 물리적 장치에서 여러개의 가상 머신을 실행시킬 수 있도록 하는 소프트웨어이다.  
  • 하드웨어 : 물리적인 장치를 의미한다. 가상화를 통해 이 하드웨어를 여러개의 가상 머신으로 나누어 사용할 수 있다.

운영체제에 대해서 더 이야기해보자면, 하드웨어는 하드웨어에서 기존에 동작하던 호스트 운영체제가상 머신에서 구동하는 게스트 운영체제로 나눌 수 있다.

예를 들자면, 내 컴퓨터의 윈도우 운영체제가 호스트 운영체제이고, 따로 설치한 Linux OS가 게스트 운영체제이다.

가상 머신은 하드웨어 자원에 접근하기 위해 게스트 운영체제를 사용하므로, 게스트 운영체제를 따로 준비해야 한다.

가상화의 단점과 보완

가상화는 정말 놀라운 기술임은 확실하다.

하지만, GuestOS를 따로 준비해야 하기에 용량과 가상환경 생성 속도가 상대적으로 느리다.

이러한 단점은 컨테이너 기술을 통해서 보완할 수 있다.

 

컨테이너 기술

컨테이너 기술은 호스트 운영체제 수준에서 가상화가 가능하도록 해주는 기술이다.

리눅스 운영체제는 리눅스 네임스페이스리눅스 컨트롤 그룹이라는 기능을 제공하여 호스트 운영체제 레벨에서 가상화가 가능하도록 해주었다.

  • 리눅스 네임스페이스 : 리눅스 운영체제의 사용자 공간을 여러개로 나누고 독립적으로 동작할 수 있도록 함
  • 리눅스 컨트롤 그룹 : 각 프로세스가 사용할 수 있는 자원을 제한함.

정리하자면 컨테이너는 독립적이고 분할된 사용자 공간에서 제한된 자원으로 동작하는 프로세스라고 볼 수 있다.

 

아래 그림은 컨테이너의 구조를 그림으로 나타낸 것이다.

간단하게 각 요소들에 대해서 알아보자.

  • 컨테이너 : 호스트 운영체제 수준에서 가상화된 환경
  • 컨테이너 엔진 : 컨테이너 이미지를 기반으로 컨테이너를 생성하는 소프트웨어
  • 호스트 운영체제 : 기존에 하드웨어에서 동작하던 운영체제
  • 하드웨어 : 물리적인 장치

이 내용을 풀어 설명하면 아래와 같다.

  • 호스트 운영체제가 커널 기능(리눅스 네임스페이스 등)을 활용하여 논리적으로 분할된 실행 환경을 만들어준다.
  • 각 컨테이너는 기존의 호스트 운영체제를 공유한다.
  • 각각의 컨테이너는 일반적인 리눅스 프로세스처럼 보이고 작동한다

GuestOS를 따로 준비할 필요 없이 호스트 운영체제를 공유하므로 자원과 실행 시간 측면에서 유리함을 알 수 있다.

그렇다면, Docker는 무엇인가? 이 부분에 대해서 알아보자

 

Docker와 Docker 이미지

이전에 이야기했던 Docker의 정의를 다시 갖고왔다.

  • Docker는 리눅스 응용 프로그램들을 소프트웨어 컨테이너에 배치하는 일을 자동화하는 오픈소스 프로젝트이다. 

정리하자면, 도커는 리눅스 응용 프로그램을 기준으로 설계된 프로그램이고, 컨테이너 기술을 쉽게 사용할 수 있도록 도와주는 기술임을 알 수 있다.

지금부터는 Docker의 핵심 기술인 Docker 이미지에 대해서 알아보자

Docker 이미지

Docker 이미지는 컨테이너 실행에 필요한 모든 파일과 설정값을 포함하고 있는 변하지 않는 것이다.

즉 Docker 이미지는 특정 프로그램을 실행하기 위해 필요한 모든 것(의존성)들을 갖고 있는 것이라고 보면 된다.

 

그런데 Docker 이미지가 모든 의존성을 갖고 있다면, 매번 모든 의존성들을 준비해야 하니 비효율적이지 않을까?

Docker는 이러한 문제를 레이어 저장 방식으로 해결한다.

 

레이어 저장 방식은 여러개의 레이어를 합쳐서 하나의 파일 시스템으로 만드는 방식이다.

쉽게 말하자면 도커 이미지는 여러개의 불변 레이블로 구성되어 있고, 만약 새로운 이미지를 다운로드해야 한다면, 기존에 갖고 있던 불변 레이블을 재사용하고 필요한 레이블만 따로 다운로드한다는 것이다.

위 그림은 간단한 예시를 나타냈다. Layer2와 Layer3를 갖고 있는 상황에서 새로운 이미지를 다운로드하는 상황이다.이 때, Layer2와 Layer3는 재사용하고 Layer1만 따로 다운로드하는 방식을 채택하여 효율적으로 이미지를 관리한다.

 

Docker 아키텍처

Docker는 기본적으로 클리아언트-서버 구조를 갖고 있다.

구체적으로 설명하자면, Docker Client(클라이언트), Docker Daemon(서버), Docker Registry(저장소)로 나뉜다.

  • Docker Daemon(dockerd) : 클라이언트 명령을 REST API로 받아서 컨테이너, 이미지, 네트워크, 볼륨을 관리
  • Docker Client : dockerd에 명령어를 전달하기 위한 인터페이스
  • Docker Registry : 도커 이미지를 관리하는 저장소 (ex. dockerhub)

이 것만 봐서는 좀 어렵다... 우선 아래 그림을 통해서 보다 구체적으로 동작 방식에 대해서 알아보자

Docker Client와 Docker Daemon은 아래와 같은 순서로 동작한다.

  1. Docker Client에서 REST API 방식으로 dockerd에 명령어 전달
  2. dockerd에서 명령어 수행
  3. 명령어 수행 결과를 Docker Client에게 전달

또한, Docker Host와 Docker Registry는 아래와 같이 동작한다.

  1. Docker Registry로부터 원하는 이미지를 pull
  2. 해당 이미지를 run해서 container 생성

이렇게 보니 크게 어려울 것은 없다. 이제부터는 배운 내용을 바탕으로 간단한 환경 설정과 실습을 해보자

 

Docker 환경 설정

ubuntu 환경에서 실습을 진행했다.

스크립트를 따라쳐도 작동을 하지만, 각 스크립트의 의미를 이해하면서 넘어갈 수 있도록 자료를 준비했다.

Docker 다운로드하기

사실 다운로드는 정말 간단하다. 아래 명령어를 차례로 실행하면 된다.

sudo apt-get update
sudo apt-get install curl
curl https://get.docker.com | sudo sh

위 쉘 스크립트는 아래와 같은 과정을 담고 있다.

  1. 패키지 관리 시스템을 업데이트한 후, curl 명령어 다운로드
  2. curl 명령어를 통해서 docker가 제공하는 최신 버전의 docker 설치 스크립트를 받아옴
  3. sudo sh 명령어를 통해서 해당 명령어를 실행

만약 위 명령어를 실행했을 때 아래와 같은 화면이 나타난다면, 정상적으로 작동중이니 기다리면 다운로드가 완료된다.

 

2025년 1월 10일 기준, docker 설치 명령어를 실행했을 때 아래와 같은 패키지가 설치되었음을 확인할 수 있다.

설치된 패키지는 dpkg --get-selections | grep docker 명령어를 통해 확인할 수 있다.

패키지 이름 기능
docker-buildx-plugin 고급 빌드 기능과 멀티 플랫폼 이미지 빌드 지원
docker-ce docker 엔진 실행 및 컨테이너 관리
docker-ce-cli docker cli(command line interface)에 대한 명령어 제공
docker-ce-rootless-extras 루트리스 모드 실행을 위한 구성
docker-compose-plugin docker compose 기능을 위한 cli 제공

Docker 명령어 실행을 위한 권한 부여하기 (필요한 경우 진행)

처음 Docker를 다운로드 했을 때, Docker에 루트 사용자만 접근할 수 있도록 설정되어 있다.

하지만, 자주 사용하는 명령어들이기에 현재 사용자도 sudo 명령어 없이 접근할 수 있도록 수정할 것이다.

sudo usermod -aG docker $USER
sudo su - $USER

스크립트를 실행 시 아래와 같은 과정을 진행한다.

  1. 현재 사용자($USER)를 docker라는 그룹에 추가한다.
  2. 로그인 쉘 환경을 초기화 후 현재 사용자로 다시 로그인한다.

위 스크립트를 통해 현재 사용자를 docker 그룹에 추가했고, 자유롭게 docker 명령어에 접근할 수 있도록 했다.

Docker 이미지 받아오기

위 과정을 통해 실습을 위한 환경은 모두 갖췄다.

이제는 이미지를 도커 레지스트리(도커 허브)에서 받아온 후 컨테이너로 만들어보는 실습을 할 것이다.

 

이미지는 docker pull 명령어로 받아올 수 있고, 구조를 확인하면 아래와 같다.

docker pull {이미지 이름} : {태그}

 

우선 간단하게 레지스트리에서 Ubuntu 이미지를 받아오는 실습을 해보자.

docker pull ubuntu:latest

위 스크립트를 통해 latest라는 태그를 갖는 ubuntu 이미지를 받아왔다. 즉 최신 버전 우분투 이미지이다.

 

정상적으로 받아왔는지 확인해보자. docker images 명령어를 활용하면 현재 이미지 목록들을 확인할 수 있다.

이러한 결과가 나타난다면 정상적으로 이미지를 받아왔다는 뜻이다. 

Docker 이미지를 컨테이너로 실행하기

이제는 받아온 우분투 이미지를 컨테이너로 실행시킬 것이다.

docker run 명령어를 통해서 이미지를 컨테이너로 실행할 수 있다. 구조는 아래와 같다

docker run -d -p host_port : container_port -it --rm {이미지명}:{태그} {컨테이너 실행 후 실행할 명령어}

상당히 복잡하다. 각 속성들의 의미를 알고 넘어가자

  • -d : 컨테이너를 백그라운드에서 실행
  • -p host_port:container_port : 호스트와 컨테이너 간 네트워크 포트를 연결 (포트 매핑)
  • -i : 표준 입력을 활성화하여 컨테이너 내부에서 입력을 받을 수 있도록 함
  • -t : 가상 터미널을 할당하여 사용자가 명령어를 입력할 수 있도록 함
  • --rm : 컨테이너 종료 시 자동으로 삭제. 임시 작업 컨테이너에 유용함

우리는 우분투 컨테이너를 실행 후, 터미널로 접속할 것이고 컨테이너 종료 후 해당 컨테이너를 삭제할 것이다.

따라서 -i, -t, --rm 옵션을 사용하고, bash 쉘을 사용하기 위해 컨테이너 실행 후 bash 명령어가 실행되도록 한다.

docker run -it --rm ubuntu:latest bash

 

아래와 같은 화면이 나타난다면 성공이다.

 

docker ps 명령어를 실행하면 현재 실행중인 컨테이너 목록을 확인할 수 있다. 

기존 ubuntu를 유지한 채 새로운 ubuntu 환경을 열어서 확인해보자

현재 ubuntu:latest 이미지를 활용한 컨테이너가 실행되고 있음을 확인할 수 있다.

 

exit 명령어를 작성하면 컨테이너를 종료할 수도 있다. --rm 옵션을 사용했기에 컨테이너 종료 후 컨테이너는 삭제된다.

Docker 컨테이너로 이미지 만들기

도커 컨테이너를 통해 이미지를 생성할 수 있다. docker commit 명령어를 사용한다.

docker commit {컨테이너 id} {이미지명}:{태그}

 

이젠 docker commit 명령어를 활용하여 git이 설치되어 있는 ubuntu:git 이미지를 생성해보자

 

우선 ubuntu:latest 이미지를 컨테이너로 실행 후 git을 설치할 것이다.

apt-get update
apt-get -y install git
git --version

 

 

위 코드 실행 시 git 설치 후 현재 설치된 git의 버전을 보여준다. 아래와 같이 버전 정보가 나타난다면 성공이다.

 

docker diff를 활용하여 원본 이미지인 ubuntu:latest 이미지와 현재 컨테이너의 차이를 확인해보자

여러 변경사항들을 확인할 수 있다 (A : Add, C : Change, D : Delete)

 

이젠 현재 컨테이너를 이미지로 만들 것이다. 우분투 환경을 하나 더 열어서 아래 명령어를 작성한다.

docker commit {컨테이너 id} ubuntu:git

컨테이너 id에 해당하는 부분은 docker ps로 확인 후 작성하면 된다.

 

정상적으로 실행되었다면, docker images 명령어 실행 시 아래와 같이 ubuntu:git 이미지가 생성되었다.

 

실제 git이 설치되어 있는지 확인하기 위해 ubuntu:git 이미지를 통해 컨테이너를 생성해보자.

정상적으로 설치되었다면, git --version 명령어가 정상적으로 작동할 것이다.

해당 컨테이너의 원본 이미지는 ubuntu:git이다. 따라서 docker diff 실행 시 변경 사항이 없을 것이다.

Docker 이미지와 컨테이너 삭제

실습이 모두 종료되었으니 이미지와 컨테이너를 모두 삭제하자.

이미지는 docker rmi, 컨테이너는 docker rm으로 삭제할 수 있다.

docker rm {컨테이너 id}
docker rmi {이미지명}:{태그}

위 명령어 실행 시 아래와 같은 제약사항이 있다.

  1. 실행중인 컨테이너는 삭제 불가
  2. 생성된 컨테이너가 존재하는 이미지는 삭제 불가

컨테이너를 삭제하기 위해 컨테이너를 모두 종료해야 하고, 이미지를 삭제하기 위해 컨테이너를 모두 삭제해야 한다.

 

마무리

정말 긴 내용이었다!

Docker에 대한 전반적인 개념에 대해서 알아보았고, 이를 기반으로 간단한 실습을 진행했다.

다음에는 dockerfile과 docker-compose에 대해서 다루어볼 예정이다.