DevOps/Docker

[시작하세요! 도커/쿠버네티스] 도커 엔진 ①

Grace 2022. 11. 21. 18:30

도커 이미지와 컨테이너

도커 엔진에서 사용하는 기본 다위는 이미지와 컨테이너이며, 이 두 가지가 도커 엔진의 핵심입니다.

도커 이미지

이미지는 컨테이너를 생성할 때 필요한 요소이며, 가상 머신을 생성할 때 사용하는 iso 파일과 비슷한 개념입니다. 이미지는 여러 개의 계층으로 된 바이너리 파일로 존재하고, 컨테이너를 생성하고 실행할 때 읽기 전용으로 사용됩니다. 이미지는 도커 명령어로 내려받을 수 있으므로 별도로 설치할 필요는 없습니다.

도커에서 사용하는 이미지의 이름은 기본적으로 [저장소 이름]/[이미지이름]:[태그] 의 형태로 구성됩니다.

  • 저장소(Repository) 이름은 이미지가 저장된 장소를 의미합니다. 저장소 이름이 명시되지 않은 이미지는 도커에서 기본적으로 제공하는 이미지 저강소인 도커 허브의 공식 이미지를 뜻합니다. 그러나 이미지를 생성할 때 저장소 이름을 명시할 필요는 없으므로 생략하는 경우도 있습니다.
💡 도커 허브에 저장될 이미지라는 것을 명시하기 위해서 이미지 이름 최상위에 [docker.io](http://docker.io)를 붙여 [docker.io/alicek106/ubuntu:14.04](http://docker.io/alicek106/ubuntu:14.04)와 같이 나타내기도 합니다. 이미지 저장소를 나타내는 최상위 접두어는 docker.io 외에도 [gcr.io](http://gcr.io) 등이 있습니다.
  • 이미지 이름은 해당 이미지가 어떤 역할을 하는지 나타냅니다. 위 예시는 우분투 컨테이너를 생성하기 위한 이미지라는 것을 알 수 있습니다. 이미지 이름은 생각할 수 없으며 반드시 설정해야 합니다.
  • 태그는 이미지의 버전 관리, 혹은 리비전 관리에 사용합니다. 일반적으로 14.04와 같이 버전을 명시하지만 태그를 생략하면 도커 엔진은 이미지의 태그를 latest로 인식합니다.

도커 컨테이너

앞에서 설명한 도커 이미지는 우분투, CentOS 등 기본적인 리눅스 운영체제부터 아파치 웹 서버, MySQL 데이터베이스 등의 각종 애플리케이션, 하둡이나 스파크, 스톰 등의 빅데이터 분석 도구까지 갖가지 종류가 있습니다. 이러한 이미지로 컨테이너를 생성하면 해당 이미지의 목적에 맞는 파일이 들어 있는 파일시스템과 격리된 시스템 자원 및 네터워크를 사용할 수 있는 독립된 공간이 생성되고, 이것이 바로 도커 컨테이너가 됩니다. 대부분의 도커 컨테이너는 생성될 때 사용된 도커 이미지의 종류에 따라 알맞은 설정과 파일을 가지고 있기 때문에 도커 이미지의 목적에 맞도록 사용되는 것이 일반적입니다.

컨테이너는 이미지를 읽기 전용으로 사용하되 이미지에서 변경된 사항만 컨테이너 계층에 저장하므로 컨테이너에서 무엇을 하든지 원래 이미지는 영향을 받지 않습니다. 또한 생성된 각 컨테이너는 각기 독립된 파일시스템을 제공받으며 호스트와 분리돼 있으므로 특정 컨테이너에서 어떤 애플리케이션을 설치하거나 삭제해도 다른 컨테이너와 호스트는 변화가 없습니다.

도커 컨테이너 다루기

💡 도커를 편하게 쓰기 위해 GUI를 지원하는 Kitematic이라는 도구가 있지만 우리는 CLI를 기준으로 공부합니다. 윈도우, 맥 OS X, 리눅스 등 운영체제에 관계없이 도커의 CLI를 사용하는 방법은 전부 동일하기 때문입니다.

컨테이너 생성

설치된 도커 엔진의 버전을 확인합니다.

# docker -v
Docker version 20.10.5, build 55c4c88

도커는 다양한 기능이 빠르게 업데이트되고 새로운 버전이 배포되므로 설치된 도커 엔진의 버전을 확인하는 것은 매우 중요합니다. 사소한 버전 차이로 도커의 중요한 기능을 사용하지 못할 수도 있기 때문입니다.

Ubuntu 컨테이너 생성

버전을 확인했다면 다음 명령어를 입력해 첫 번째 컨테이너를 생성합니다. docker run 명령어는 컨테이너를 생성하고 실행하는 역할을 합니다. ubuntu:14.04는 컨테이너를 생성하기 위한 이미지의 이름이며, -i -t 옵션은 컨테이너와 상호(interactive) 입출력을 가능하게 합니다.

# docker run -i -t ubuntu:14.04

docker run 명령어를 입력하면 다음과 같은 내용이 출력됩니다. ubuntu:14.04 이미지가 로컬 도커 엔진에 존재하지 않으므로 도커 중앙 이미지 저장소인 도커 허브에서 자동으로 이미지를 내려받습니다.

Unable to find image 'ubuntu:14.04' locally
14.04: Pulling from library/ubuntu
862a3e9af0ae: Pull complete
...
7a1f7116d1e3: Pull complete
Digest: sha256: 5b5d48912298191c3c8006e7d3982029b288678fccabf2265899199c24d7f89
Status: Downloaded newer image for ubuntu:14.04
root@0f4c5b8203f:/#

단 한 줄의 docker 명령어로 컨테이너를 생성 및 실행과 동시에 컨테이너 내부로 들어왔습니다. 셸의 사용자와 호스트 이름이 변경된 것이 컨테이너 내부에 들어와 있다는 것을 나타냅니다. 컨테이너에서 기본 사용자와 호스트 이름은 무작위의 16진수 해시값입니다. 무작위의 16진수 해시값은 컨테이너의 고유한 ID의 앞 일부분입니다.

docker run 명령어로 컨테이너를 생성할 때 -i 옵션으로 상호 입출력을, -t 옵션으로 tty를 활성화해서 배시(bash) 셸을 사용하도록 컨테이너를 설정했습니다. docker run 명령어에서 이 두 옵션 중 하나라도 사용하지 않으면 셸을 정상적으로 사용할 수 없습니다.

컨테이너와 호스트의 파일시스템은 서로 독립적이므로 ls 명령어로 파일시스템을 확인해보면 아무것도 설치되지 않은 상태임을 확인할 수 있습니다.

root@0f4c5b8203f:/# ls
bin   dev  home  lib64  mnt  proc  run   src  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

컨테이너 내부에서 호스트의 도커 환경으로 돌아옵니다. 컨테이너 내부에서 빠져나오는 방법은 두 가지가 있습니다. 첫 번째 방법은 컨테이너 셸에서 exit를 입력하거나 Ctrl + D를 동시에 입력하는 것입니다. 그러나 이 방법은 컨테이너 내부에서 빠져나오면서 동시에 컨테이너를 정지시킵니다. 여기서는 exit 명령어를 사용합니다.

root@0f4c5b8203f:/# exit
exit

다른 방법은 컨테이너를 정지하지 않고 빠져나오는 것으로 Ctrl + P, Q를 입력하는 것입니다. exit 명령어는 배시 셸을 종료함으로써 컨테이너를 정지시킴과 동시에 컨테이너를 빠져나오지만 Ctrl + P, Q를 사용하면 단순히 컨테이너의 셸에서만 빠져나오기 때문에 컨테이너 애플리케이션을 개발하는 목적으로 컨테이너를 사용할 때는 이 방법을 많이 씁니다.

CentOs 컨테이너 생성

docker pull 명령어를 입력해 도커 공싱 이미지 저장소에서 centos:7 이미지를 내려받습니다. docker pull 명령어는 이미지를 내려받을 때 사용합니다.

# docker pull centos:7
7: Pulling from library/centos
3d8673bd162a: Pull complete
Digest: sha256:a66ffcb73930584413de83311ca11a4cb4938c9b2521d331026dad970c19adf4
Status: Downloaded newer image for centos:7

이미지를 정상적으로 내려받았는지 확인합니다. docker images 명령어는 도커 엔진에 존재하는 이미지의 목록을 출력합니다.

방금 내려받은 centos:7 이미지와 이전에 내려받은 ubuntu:14.04 이미지가 존재하는 것을 알 수 있습니다.

REPOSITORY     TAG             IMAGE ID          CRETED            SIZE
**ubuntu         14.04**           4a725d3b3b1c      9 days ago        187.9 MB
**centos         7**               970633036444      5 weeks ago       196.7 MB

컨테이너를 생성할 때는 run 명령어가 아닌 create 명령어를 사용할 수도 있습니다. 다음 명령어를 입력해 centos:7 이미지로 컨테이너를 생성합니다. --name 옵션에는 컨테이너의 이름을 설정합니다.

# docker create -i -t --name mycentos centos:7
dd06c5cbf48843920ba00ca8b32318d019817f2b6d345299665f32b9d8c059
💡 `create` 명령어의 결과로 출력된 무작위의 16진수 해시값은 컨테이너의 고유 ID입니다. 그러나 너무 길어 일반적으로 앞 12자리만 사용합니다. `docker inspect` 명령어로 컨테이너의 ID를 다시 확인할 수 있습니다.

그런데 이번에는 run 명령어를 실행했을 때와 달리 컨테이너 내부로 들어가지 않습니다. create 명령어는 컨테이너를 생성만 할 뿐 컨테이너로 들어가지 않기 때문입니다.

이번에는 docker start 명령어와 docker attach 명령어를 써서 컨테이너를 시작하고 내부로 들어갑니다. attach는 컨테이너의 내부로 들어가는 명령어입니다.

# docker start **mycentos**
mycentos

#docker attach **mycentos**
[root#dd06c6cb6f4 /]#

이번에는 exit가 아닌 Ctrl+P,Q를 입력해 컨테이너에서 빠져나옵니다.

지금까지 컨테이너를 생겅하기 위해 run, create, start 명령어를 사용했습니다. run 명령어는 pull, create, start 명령어를 일괄적으로 실행한 후 attach가 가능한 컨테이너라면 컨테이너 내부로 들어갑니다.

그러나 create명령어는 도커 이미지를 pull한 뒤에 컨테이너를 생성만 할 뿐, start, attach를 실행하지 않습니다. 보통은 컨테이너를 생성함과 동시에 시작하기 때문에 run 명령어를 더 많이 사용합니다. 따라서 앞으로 컨테이너를 생성할 때 run 명령어를 쓰겠습니다.

💡 컨테이너를 대상으로 하는 모든 명령어는 컨테이너의 이름 대신 ID를 쓸 수 있습니다. 그러나 ID의 이름이 너무 길 때는 앞의 2~3자만 입력해도 됩니다.
그러나 ID의 앞자리인 dd0인 다른 컨테이너가 이미 존재한다면 어느 컨테이너를 가리키는지 알 수 없으므로 에러가 발생합니다. 따라서 ID의 앞자리를 사용해 컨테이너를 제어할 때는 적절히 3~4자를 입력하는 것이 좋습니다.

컨테이너 목록 확인

CentOS 컨테이너에서 호스트로 되돌아와 지금까지 생성한 컨테이너의 목록을 확인합니다.

# docker ps

docker ps 명령어는 정지되지 않은 컨테이너만 출력합니다. 즉, exit를 사용해 빠져나온 컨테이너는 저지 상태이기 때문에 컨테이너 목록에 출력되지 않지만 Ctrl+P,Q를 입력해 빠져나온 컨테이너는 실행 중이기 때문에 컨테이너 목록에 출력됩니다.

CONTAINER ID  IMAGE      COMMAND      CREATED       STATUS       PORTS  NAMES
5cc815c9e796  centos:7   "/bin/bash"  16 hours ago  **Up 3 seconds**        mycentos

정지된 컨테이너를 포함한 모든 컨테이너를 출력하려면 -a 옵션을 추가합니다. 컨테이너의 상태는 STATUS 항목에서 확인합니다. Exited … 는 정지된 상태, Up .. seconds는 실행 중인 상태임을 의미합니다.

# docker ps -a
CONTAINER ID  IMAGE         COMMAND      CREATED       STATUS              PORTS  NAMES
5cc815c9e796  centos:7      "/bin/bash"  16 hours ago  **Up 3 seconds**               mycentos
0f4c5b82c03f  ubuntu:14.04  "/bin/bash"  20 hours ago  Exited (1) 3 minuates ago  angry_morse

docker ps 명령어의 출력에 대한 설명은 아래와 같습니다.

  • CONTAINER ID: 컨테이너에게 자동으로 할당되는 고유한 ID입니다. 위 출력 결과에서는 ID의 일부분밖에 확일할 수 없지만 컨테이너의 정보를 확인하기 위해 docker insepect 명령어를 사용하면 전체 ID를 확인할 수 있습니다.
  • # docker inspect mycentos | grep Id "Id": "5cc815c9e796bff447c5a7746871e10a884506da560d83bf2f7777aa815508eaebaa2d",
  • IMAGE: 컨테이너를 생성할 때 사용된 이미지의 이름입니다. 위 출력 결과의 컨테이너는 centos:7, ubuntu:14.04 이미지로부터 생성됐음을 알 수 있습니다.
  • COMMAND: 커맨드는 컨테이너가 시작될 떄 실행될 명령어입니다. 커맨드는 대부분의 이미지에 미리 내장돼있기 때문에 별도로 설정할 필요는 없습니다. 위에서 생성한 centos:7, ubuntu:14.04 이미지에는 /bin/bash라는 커맨드가 내장돼 있기 때문에 컨테이너를 생성할 떄 별도의 커맨드를 설정하지 않았습니다. 컨테이너가 시작될 때 /bin/bash 명령어가 실행됐으므로 상호 입출력이 가능한 셸 환경을 사용할 수 있었습니다.
    이미지에 내장된 커맨드는 docker run이나 create 명령어의 맨 끝에 입력해서 컨테이너를 생성할 때 덮어쓸 수 있습니다.
  • CREATED: 컨테이너가 생성되고 난 뒤 흐른 시간을 나타냅니다.
  • STATUS: 컨테이너의 상태를 나타내며, 컨테이너가 실행 중임을 나타내는 ‘Up’, 종료되니 상태인 ‘Exited’. 일시 중지된 상태인 ‘Pause’ 등이 있습니다.
  • PORTS: 컨테이너가 개방한 포트와 호스트에 연결한 포트를 나열합니다. 앞에서 컨테이너를 생성할 때는 외부에 노출하도록 설정하지 않았으므로 PORTS 항목에는 아무것도 출력되지 않습니다.
  • NAMES: 컨테이너의 고유한 이름입니다. 컨테이너를 생성할 때 --name 옵션으로 이름을 설정하지 않으면 도커 엔진이 임의로 형용사와 명사를 무작위로 조합해 이름을 설정하기 때뭉네 우분투 컨테이너의 이름이 angry_morse로 설정돼 있습니다. 컨테이너의 이름은 ID와 마찬가지로 중복될 수는 없지만 docker rename 명령어를 사용하면 컨테이너의 이름을 변경할 수 있습니다.
  • # docker rename angry_morse my_container

컨테이너 삭제

더 이상 사용하지 않는 컨테이너를 삭제할 때는 docker rm 명령어를 사용합니다. 한 번 삭제한 컨테이너는 복구할 수 없으므로 삭제할 때는 신중을 기해야 합니다.

다음 명령어를 입력해 컨테이너를 삭제합니다. 컨테이너의 이름은 생성한 컨테이너의 이름에 맞게 적절히 변경합니다.

# docker rm angry_morse

컨테이너가 삭제됐는지 확인하려면 docker ps -a 명령어를 입력합니다.

# docker ps -a
CONTAINER ID  IMAGE     COMMAND      CREATED       STATUS         PORTS  NAMES
5cc815c9e796  centos:7  "/bin/bash"  16 hours ago  **Up 29 seconds**         mycentos
...

이번에는 mycentos 컨테이너를 삭제합니다.

# docker rm mycentos

그런데 아래와 같은 에러가 발생합니다.

Error response from daemon: You cannot remove a running container 5cc815c9e796bff
447c5a7746871e10a884506da560d83bf2f7777aa815508eaebaa2d. Stop the container before
attempting removal or force remove

실행 중인 컨테이너는 삭제할 수 없으므로 컨테이너를 정지한 뒤 삭제하거나 강제로 삭제할 수 있는 옵션을 추가하라는 내용입니다. 첫 번째로 삭제한 angry_morse 컨테이너는 docker ps에서 상태가 Exited(1)…, 즉 정지된 상태였기 때문에 삭제가 가능했습니다. mycentos 컨테이너를 삭제하려면 다음과 같이 stop 명령어로 컨테이너를 정지한 후에 삭제해야 합니다.

# docker stop mycentos
# docker rm mycentos

실행 중인 컨테이너를 삭제하는 다른 방법은 rm 명령어에 -f 옵션을 추가하는 것입니다.

# docker rm -f mycentos

도커를 사용하다 보면 연습용으로 생성한 컨테이너가 너무 많아 일일이 삭제하기 귀찮은 경우가 있습니다. 이런 경우에는 prune 명령어를 입력해서 모든 컨테이너를 삭제할 수 있습니다.

# docker container prune
WARING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y

또는 docker ps 명령어의 -a 옵션과 -q 옵션을 조합해 컨테이너를 삭제할 수도 있습니다. -a는 컨테이너 상태와 관계 없이 모든 컨테이너를, -q는 컨테이너의 ID만 출력하는 역할을 합니다.

# docker ps -a -q
5cc815c9e796
...

이 옵션들을 사용해 출력된 컨테이너 리스트를 변수로 컨테이너를 삭제하려면 모든 컨테이너를 간단히 삭제할 수 있습니다. 즉, 다음 명령어는 컨테이너의 실행 상태와 관계없이 모든 컨테이너를 정지하고 삭제합니다.

# docker stop $(docker ps -a -q)
# docker rm $(docker ps -a -q)

컨테이너를 삭제하면 컨테이너의 데이터를 모두 잃게 되므로 연습용으로 도커를 활용한 경우에만 위 명령어를 사용할 것을 권장합니다.

'DevOps > Docker' 카테고리의 다른 글

[시작하세요! 도커/쿠버네티스] 도커란?  (1) 2022.11.14