도커는 docker engine 위에서 '컨테이너'라는 단위로 가상 실행 환경을 제공해주는 오픈 소스 플랫폼이다. 도커는 리눅스 커널 위에서 동작하여 가볍고 빠르다는 특징과 이식성, 보안 등 여러가지 장점을 가지고 있는 기술이다.
하지만, 도커는 CRI (Container Runtime Interface) 라는 표준 인터페이스를 지원하지 때문에 쿠버네티스에서 도커에 대한 지원을 중단하는 등 그 위상이 예전같지 않다는 것은 사실이다. 하지만, 현재까지도 많은 서비스와 개발자 사이에서는 도커를 적극적으로 사용하고 있다는 것을 다음의 통계를 통해 확인할 수 있다.
평소, 도커에 대한 관심이 많았기 때문에 관련 문서나 강의를 통해 지속적으로 학습을 이어나가고 있었지만 제대로 정리해야할 필요성을 느끼게 되어 이번 포스팅을 통해 도커에 대한 기초적인 지식부터 정리하고자 한다.
📑 개요
도커 (Docker)는 가상실행 환경을 제공해주는 오픈소스 플랫폼이다. 도커에서는 이러한 가상실행 환경을 '컨테이너(container)'라고 부른다. '컨테이너'는 도커에서 다루는 실행 단위이며 각 컨테이너는 실행환경이 독립적으로 분리되어 있는 형태이기 때문에 여러 애플리케이션을 각자의 독립적인 환경에서 구동시킬 수 있다.
장점
표준화 | 도커를 사용하면 프로세스가 어떠한 방식으로 작동하든 상관없이 동일한 형식으로 프로세스를 실행하고 관리할 수 있다. |
이식성 | 프로그램이 어디서 어떻게 만들어졌는지 상관 없이 도커를 플랫폼 위에서 실행한다면 동일한 실행 환경으로 프로세스를 작동시킬 수 있다. |
가벼움 | 도커를 통해 실행된 컨테이너는 Host OS의 커널을 공유하기 때문에 다른 가상화 제품에 비해 가볍다. |
보안 | 컨테이너라는 고립된 환경에서 실행되므로 보안 측면에서 유리하다. |
특징
도커는 컨테이너라는 가상 환경을 제공하기 때문에 그 특징을 잘 알고 가는 것이 좋다.
확장성 | 도커는 Docker Engine이라는 프로세스위에서 동작한다. Java의 JVM처럼 도커도 Docker Engine이 있으면 컨테이너를 구동시킬 수 있다. |
표준성 | 컨테이너라는 표준으로 애플리케이션을 배포하므로 모든 서비스들의 배포 과정이 동일해진다. |
이미지 | 컨테이너는 '이미지'라는 단위를 통해 생성한다. Dockerfile 이라는 특수한 파일을 작성하거나, 실행되고 있는 컨테이너를 이미지로 만들수도 있다. 이러한 이미지들은 Docker Hub나 ECR 과 같은 이미지 저장소를 통해 관리한다. |
설정 | 컨테이너에 대한 설정은 일반적으로 환경 변수를 통해 이루어진다. MYSQL_PASS=password 와 같이 컨테이너를 띄울 때 환경 변수를 같이 지정한다. |
자원 | 컨테이너를 삭제하고 새로 만들면 기존 컨테이너 내부에 저장되었던 모든 데이터가 초기화된다. 따라서, 외부 스토리지와 링크하거나 Host 서버와 저장소를 공유할 수 있는 볼륨 마운트 기술을 제공한다 |
컨테이너는 일반 서버와 달리, 한 번 구동시킨 이후 오래 구동시키는 개념이 아니라 휘발성을 가진 실행 환경이다. 따라서 언제든지 컨테이너가 삭제될 수 있으며, 삭제된 컨테이너에 저장된 데이터는 모두 삭제된다는 것을 기억하자.
운영 환경에서는 컨테이너 내부에 중요 데이터를 저장하지 않고, 볼륨 마운트 기술을 사용하거나 외부 스토리지에 저장한다.
VM (가상 머신)과 도커의 차이
도커는 독립적인 가상실행 환경을 제공하는 기술이라는 점에서 가상화 기술(VM) 과 유사한 점이 있다. 하지만 내부 동작 방식에는 분명한 차이점이 있다. 도커를 공부할 때 항상 등장하는 내용이므로 확실하게 짚고 넘어가자.
위 이미지는 VM과 Container 의 차이를 그림으로 나타낸 것이다. 우선, VM은 Hypervisor 이라는 가상 엔진 위에 Guest OS와 App을 패키징한 가상 머신을 만들어 실행하는 방식으로 '하드웨어 레벨의 가상화'를 지원하는 기술이다.
반면, Container 는 Host OS에서 실행되는 Container Engine 위에서 운영 체제를 제외한 나머지 App을 패키징한다는 점에서 'OS 레벨의 가상화'를 지원하는 기술이다.
컨테이너는 Guest OS와 Hypervisor 가 없기 때문에 이로 인한 오버헤드를 줄임으로써 더욱 가볍게 프로세스를 실행할 수 있고 컨테이너에 대한 복제와 배포가 훨씬 용이하다는 차별점이 있다.
도커는 Docker engine 이라는 Container Engine이 있고, 이는 Linux 커널에서 동작하기 때문에 Host OS에 리눅스를 실행할 수 있는 환경이 있어야한다. Window 환경에서는 WSL2 또는 Docker Desktop을 설치하면 되고 Mac 환경에서는 Docker Desktop 을 설치하여 도커를 실행할 수 있다.
도커 용어
도커에서 사용되는 핵심적인 용어들은 다음과 같다.
- docker image
- 컨테이너를 실행시키기 위한 바이너리 파일
- layer 형식으로 되어있음
- docker container
- 도커 이미지가 메모리에 상주하여 실제 코드가 수행되는 프로세스
- docker daemon
- 컨테이너가 정상적으로 수행될 수 있도록 실행 환경을 제공
- Rest API로 client와 통신
- Registry
- 도커 이미지를 저장할 수 있는 원격 저장소
- Docker Hub, AWS에서는 ECR이 대표적
도커 설치
Ubuntu 환경에서는 다음과 같이 도커를 바로 설치할 수 있다.
sudo apt update
sudo apt install -y docker.io net-tools
# 도커 실행 권한 추가
sudo usermod -aG docker $USER
# 서버 재시작
sudo reboot
Window 나 Mac 사용자들은 Docker Desktop을 설치하여 간단하게 도커 실행 환경을 구축할 수 있다. docker 명령을 실행할 수 있도록 Window는 WSL2나 powershell을, Mac 유저는 터미널을 띄워 docker 명령을 실행해보자.
자세한 내용은 공식 문서를 참고하면 된다.
https://docs.docker.com/get-docker/
docker -v 와 docker ps 명령어를 입력했을 때 다음과 같이 도커 버전과 컨테이너에 대한 정보가 출력되면 도커를 사용할 준비가 된 것이다.
도커 기본 명령어
도커의 모든 명령어는 docker 로 시작한다. docker 명령어를 실행하면 Rest API를 통해 docker daemon에 명령이 전달되고, docker daemon이 해당 작업을 수행하게 된다.
docker <COMMAND> (option) <TARGET> (argument)
도커의 기본적인 명령어는 다음과 같은 형태를 띈다. COMMAND 에는 기본 명령어인 container, image, volume, network 같은 명령어들이 있으며 각 명령어들은 도커에서 사용하는 리소스인 컨테이너, 이미지 등에 대한 작업을 수행하게 된다.
특정 작업을 실행하는데에 필요한 옵션들은 -d, -p, --name 등과 같이 커맨드 뒤에 붙여서 사용하며 컨테이너 내부에 인자를 전달할수도 있다.
🚋 컨테이너 관련 명령어
컨테이너 관련 명령어는 기본적으로 docker container <COMMAND> (option) 으로 이루어져 있다. 하지만, 컨테이너 명령은 정말 많이 사용되므로 container 입력을 생략하여 docker <COMMAND> (option) 으로 사용할 수 있다.
Container 명령어
COMMAND | 내용 | 주요 옵션 |
start | 컨테이너 실행 | -i |
stop | 컨테이너 정지 | 거의 사용하지 않음 |
create | 도커 이미지로부터 컨테이너 생성 | --name, -e, -p, -v |
run | 도커 이미지로부터 컨테이너를 실행. (이미지가 없을 경우 registry로부터 이미지를 다운 받아옴) docker image pull, docker container create, docker container start 명령어를 합친 것과 같다. |
--name, -e, -p, -v, -d, -i, -t |
rm | 정지 상태의 컨테이너를 삭제 | -f, -v |
exec | 실행중인 컨테이너 속에서 프로그램을 실행 | -i, -t |
ps | 컨테이너 목록을 출력 | -a |
cp | 도커 컨테이너와 도커 호스트 간 파일을 복사 | 거의 사용되지 않음 |
commit | 도커 컨테이너를 이미지로 변환 | 거의 사용되지 않음 |
컨테이너 실행
도커 컨테이너는 주로 run 명령어를 사용하여 실행한다. run 명령어에 지정한 이미지가 없을 때 자동으로 도커 허브에서 이미지를 다운받고 실행시키는 과정을 자동으로 수행한다.
docker run <IMAGE>:<TAG> [args]
# nginx:latest 버전 이미지를 컨테이너로 실행
# -d: 백그라운드에서 실행
# -p: 포트 포워딩
docker run -d -p 8080:80 --name mynginx nginx
# 컨테이너 목록 조회
docker ps
# nginx 접속
curl localhost:8080
- docker run 을 사용하여 nginx 이미지를 실행시키는 명령어다. run 에는 다양한 옵션들이 있는데, -d 는 백그라운드에서 실행시키는 옵션, -p 는 포트포워딩 옵션이다.
- 컨테이너는 docker 가 자체적으로 부여하는 해시값인 CONTAINER ID와 NAME으로 식별할 수 있다. run 명령어에 --name 옵션으로 NAME을 부여해주어 식별 값을 설정할 수 있다.
- nginx는 기본적으로 80 포트를 사용하는데, -p 옵션을 통해 Host의 8080 포트를 nginx 의 80포트와 연결했다. 따라서, Host IP에서 8080 포트로 요청을 전송하면 nginx 컨테이너에 전달된다.
컨테이너 조회
ps 명령을 통해 생성한 컨테이너 목록을 조회할 수 있다.
docker ps
docker ps -a
docker ps 명령은 실행중인 컨테이너의 목록을 조회하고, -a 옵션을 붙이면 실행중이지 않은 컨테이너까지 모두 조회한다.
해당 명령어를 통해 컨테이너의 상태, 포트, ID, NAME 등을 알 수 있다.
컨테이너 상세 정보 확인
ps 명령어로도 기본적인 정보들은 확인할 수 있지만, 더 자세한 정보를 확인해야할 때가 있다. 이 때 inspect 명령어를 사용해 컨테이너의 컨테이너의 네트워크, 볼륨 등 상세 정보를 확인할 수 있다.
docker inspect mynginx
컨테이너 명령 전달
docker exec <CONTAINER ID> <CMD>
docker exec mynginx sh -c 'apt update && apt install -y wget'
docker exec mynginx wget localhost
컨테이너 / 호스트 간 파일 복사
실행한 컨테이너와 호스트 서버간에 파일을 주고 받을 수 있다.
docker cp <HOST_PATH> <CONTAINER_ID>:<CONTAINER_PATH>
# Host의 /etc/passwd 파일을 mynginx 컨테이너의 /usr/share/nginx/html/. 경로로 복사
docker cp /etc/passwd mynginx:/usr/share/nginx/html/.
# Host의 mynginx 컨테이너의 /usr/share/nginx/html/index.htm 파일을 Host의 현재 디렉터리로 복사
docker cp mynginx:/usr/share/nginx/html/index.html .
컨테이너 내부 접속
-it 옵션을 이용하여 컨테이너 내부로 접속할 수 있다.
# 컨테이너 내부 접속
docker run -it nginx bash
docker exec -it mynginx bash
볼륨 마운트
docker run -v <HOST_DIR>:<CONTAINER_DIR> <IMAGE_NAME>
컨테이너는 휘발성 프로세스이기 때문에 컨테이너 내부에 데이터를 저장할 경우 손실 위험이 크다. 따라서 호스트 저장소와 마운트할 수 있는 기능을 제공한다.
# 현재 디렉터리를 컨테이너의 nginx 디렉터리와 연결
docker run -p 7010:80 -v $(pwd):/usr/share/nginx/html/ -d nginx
# 현재 디렉터리에 hello.txt 파일을 생성
echo "hello!!" >> $(pwd)/hello.txt
# nginx 내부에서 해당 파일이 보이는지 확인
curl localhost:6000/hello.txt
- 다음과 같이 Host 의 현재 디렉토리와 docker container 의 /usr/share/nginx/html/ 디렉토리가 마운트되어, 파일을 저장공간을 공유할 수 있다.
- 이를 통해 컨테이너 내부에서 발생한 파일들 (log) 등을 Host 서버에 안전하게 저장할 수 있다.
실행중인 컨테이너 이미지로 만들기
docker commit <EXIST CONTAINER NAME> <NEW IAMGE NAME>
docker commit mynginx special_nginx
- 실행중인 컨테이너를 이미지로 만들 수 있다.
컨테이너 중단 & 재개 & 삭제
# 컨테이너 중단
docker stop mynginx
docker ps -a
# 컨테이너 재개
docker start mynginx
# 컨테이너 삭제
docker rm mynginx
docker ps -a
# 컨테이너 강제 삭제
docker rm -f mynginx
stop, start 명령어로 컨테이너를 중단하고 다시 재개시킬 수 있다. rm 명령은 기본적으로 컨테이너가 중단되어있는 상태여야 실행된다. -f 옵션으로 실행중인 도커 컨테이너를 강제로 삭제할 수 있다.
🎨 이미지 관련 명령어
이미지는 도커에서 컨테이너를 실행시키는 하나의 리소스 단위이다. 이미지는 Layer 되어있으며 이에 따라 컨테이너도 계층화되어 실행된다.
모든 이미지들은 각 프로세스 실행에 필요한 기반 프로세스들을 차곡차곡 쌓아올리는 형태로 구성되어 있다. 따라서, 하나의 이미지는 여러개의 계층화된 이미지를 패키징 한 것이라 볼 수 있다.
Redis 이미지를 다운받는 모습을 보자. 다음과 같이 하나의 이미지가 다운받아지는 것이 아니라, Redis를 구동하는데에 있어 기반이 되는 여러개의 이미지들이 함께 다운로드 되는 모습을 볼 수 있다. Redis 이미지는 6개의 이미지를 패키징 했다는 것을 알 수 있다.
이미지를 다운받을 때, 가장 처음에 있는 이미지는 Already exists 문구와 함께 다운을 건너 뛰는 모습을 볼 수 있다. 이처럼 이미 존재하는 Layer를 캐싱하고 필요한 이미지만 다운받는다는 것을 확인할 수 있다.
최신 이미지들에는 대부분 Alphine 이라는 태그가 붙어있다. alphine은 단순성, 자원 효율성을 위해 필수적인 기능들만을 포함하여 프로세스를 경량화 시킨 것을 말한다. (alphine linux, alphine nginx ..)
alphine 이미지는 기존 이미지와 비교하여 훨씬 가벼우므로 운영 환경에서 alphine 이미지를 사용하는 것을 권장한다.
Image 명령어
COMMAND | 내용 | 주요 옵션 |
images | 이미지 목록 출력 | 거의 사용하지 않음 |
rmi | 이미지 삭제 | 거의 사용하지 않음 |
pull | 도커 허브 등의 리포티토리에서 이미지를 내려받음 | 거의 사용하지 않음 |
build | 도커 이미지를 생성 | -t |
이미지 목록 출력
docker image ls
docker images
도커 이미지 목록을 확인하는 명령어는 다음과 같다. 보통 images를 많이 사용한다.
이미지 삭제
docker image rm special_nginx
docker rmi special_nginx
- 이미지 삭제도 다음과 같이 두가지의 명령어를 사용한다.
- 단, 선택한 이미지가 컨테이너로 실행중일때는 삭제가 불가능하다.
- 컨테이너를 지울때와 마찬가지로 -f 명령어를 통해 강제로 삭제할 수 있다.
이미지 내려받기
docker pull <IMAGE>:<TAG>
# TAG 생략시 latest 버전의 이미지를 내려받는다.
docker pull redis
docker pull redis:latest
docker pull redis:alpine
- pull 명령을 통해 이미지를 내려받을 수 있다.
- TAG 명 생략시 latest 버전의 이미지를 내려받는다.
- 이미지 이름은 기본적으로 '발급자/이름' 을 적어주어야한다. 하지만, Docker Hub가 인정한 공식 이미지는 '발급자'를 적어주지 않아도 된다. docker pull redis
- 이미지는 이름 뿐만 아니라 TAG로도 구분할 수 있다. 어떤 태그가 있는지는 Docker Hub에서 확인할 수 있다.
💦 도커 허브에 이미지 올리기
깃허브가 소스코드를 저장하는 곳이라면, 도커 허브는 이미지를 저장하는 공간이다. 이 곳에서 이미지를 자유롭게 내려받을 수 있으며 개인 사용자도 이미지를 올리고 내려받을 수 있다.
Docker Hub 홈페이지에서 계정을 생성한다.
이미지 Tag 달기
tag 명령어를 사용하여 기존 이미지에서 새로운 이름을 부여할 수 있다.
docker tag <OLD_NAME>:<TAG> <NEW_NAME>:<TAG>
# 도커 허브에 등록한 사용자 닉네임을 USERNAME에 삽입
docker tag nginx <USERNAME>/myimage:v1
docker tag nginx kaispread/myimage:v1
- tag 로 생성한 이미지들은 사실상 이름만 다를 뿐 같은 이미지이다.
도커 허브 로그인
docker login
# Username: <USERNAME>
# Password:
# WARNING! Your password will be stored unecryped in ...
# Configure a credential helper to remove this warning. See
# https://docs.docker.com/engine/reference/commandline/login/#credentials-store
# Login Succeeded
- Username 과 Password 를 입력하고 도커 허브에 로그인
- Login Succeeded 문구가 출력되면 로그인에 성공한 것임
도커 허브에 이미지 업로드
docker push <USERNAME>/<NAME>
docker push kaispread/myimage:v1
- push 명령어를 통해 도커 허브에 이미지를 업로드할 수 있다.
본인 도커 허브 계정에 이미지가 업로드된 것을 확인할 수 있다.
도커 허브에서 이미지 내려받기
docker pull kaispread/myimage:v1
- 도커 허브로부터 방금 업로드했던 이미지를 받아오기 위해서 pull 명령어를 사용한다.
- 우리가 업로드한 이미지는 공식 이미지가 아니기 때문에 이미지 명 앞에 <USERNAME>을 적어주어야 함
Clean Up
모든 이미지, 컨테이너 강제 삭제
docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)
도커 이미지와 컨테이너는 사용하지 않는다면 바로 지워주는 것이 좋다. 다음의 명령어로 이미지와 컨테이너를 모두 삭제하자
참고
- 핵심만 콕! 쿠버네티스 - 유홍근 저
- 그림과 실습으로 배우는 도커 & 쿠버네티스 - 오가사와라 시게타카 저
- https://docs.docker.com/reference/