쿠버네티스를 이루는 컨테이너 도우미, 도커

노하람·2021년 9월 9일
0

쿠버네티스를 이루는 컨테이너 도우미, 도커

도커를 알아야 하는 이유

이전 포스팅에서 쿠버네티스는 컨테이너를 오케스트레이션하며, 오케스트레이션하는 기본 단위는 파드라고 배웠습니다.
그리고 파드는 컨테이너로 이루어져 있다는 것도 배웠습니다.
이번 포스팅에선 파드를 이루는 컨테이너를 알아보고 컨테이너를 다루는 도구인 도커의 개념과 사용법을 배워봅시다!

쿠버네티스를 말할 때 도커를 자주 이야기하곤 합니다.
시중의 책도 대부분 도커를 다루고 나서 쿠버네티스를 다룹니다.
쿠버네티스를 이루는 기본 오브젝트가 파드고, 파드는 컨테이너로 이루어져 있으며, 컨테이너를 만들고 관리하는 도구가 도커이기 때문입니다.
그래서 도커를 배우고 나서 쿠버네티스를 배우는 것이 흐름에 더 맞습니다.

그런데 최근엔 도커를 몰라도 쿠버네티스를 배우고 사용하는 것이 가능해졌습니다.
이는 여러 공급사에서 만들어 둔 컨테이너 이미지로 쿠버네티스에 컨테이너 인프라 서비스를 만들 수 있다는 뜻입니다.
컨테이너 관리 기술이 발전했고 여러 애플리케이션이 이미 도커 이미지로 배포되고 있어서 배포된 이미지를 사용하면 도커를 몰라도 쿠버네티스의 환경을 만들고 사용할 수 있습니다.

하지만 쿠버네티스를 이루고 있는 기술 자체는 컨테이너를 벗어날 수 없습니다.
따라서 트러블 슈팅을 제대로 하려면 컨테이너를 잘 알아야합니다.
상황에 따라서는 직접 만든 소스 코드를 빌드해 컨테이너로 만들고 이를 쿠버네티스에서 사용할 수도 있습니다.
또한 CI/CD, 모니터링도 모두 컨테이너로 관리됩니다.
따라서 컨테이너와 이를 다루는 도커를 자세히 알아야 컨테이너 인프라 환경을 깊게 이해할 수 있습니다!

파드, 컨테이너, 도커, 쿠버네티스의 관계

쿠버네티스 시스템을 기술적으로 정의해 봅시다.
파드들은 워커 노드라는 노드 단위로 관리하며,
워커 노드와 마스터 노드가 모여 쿠버네티스 클러스터가 됩니다.
그리고 파드는 1개 이상의 컨테이너로 이루어져 있습니다.

파드는 쿠버네티스로부터 IP를 받아 컨테이너가 외부와 통신할 수 있는 경로를 제공합니다.(이미 여러번 서비스를 통해 실습해봤죠?)
그리고 컨테이너들이 정상적으로 작동하는지 확인하고 네트워크나 저장 공간을 서로 공유하게 합니다.
파드가 이러한 환경을 만들기 때문에 컨테이너들은 마치 하나의 호스트에 존재하는 것처럼 작동할 수 있습니다.(파드는 1개 이상의 컨테이너라고 했죠?)
장리하면, 컨테이너를 돌보는 것이 파드고, 파드를 돌보는 것이 쿠버네티스 워커 노드이며, 워커 노드를 돌보는 것이 쿠버네티스 마스터 노드입니다.
그런데 쿠버네티스 마스터 역시 파드(컨테이너)로 이루어져 있습니다.

이 구조를 이루는 가장 기본인 컨테이너는 하나의 운영 체제 안에서 커널을 공유하며 개별적인 실행 환경을 제공하는 격리된 공간입니다.
여기서 개별적인 실행 환경이란 CPU, 네트워크, 메모리와 같은 시스템 자원을 독자적으로 사용하도록 할당된 환경을 말합니다.
개별적인 실행 환경에서는 실행되는 프로세스를 구분하는 ID도 컨테이너 안에 격리돼 관리됩니다.
그래서 각 컨테이너 내부에서 실행되는 애플리케이션들은 서로 영향을 미치지 않고 독립적으로 작동할 수 있습니다.

예를들어 워커 노드1의 HOST OS(Linux Kernel)에서 시스템 자원(CPU, 네트워크, 메모리, PID, 파일시스템) 등을 각 컨테이너로 독립적으로 할당하는 것과 같습니다.

각 컨테이너가 독립적으로 작동하기 때문에 여러 컨테이너를 효과적으로 다룰 방법이 필요해졌습니다.
오래전부터 유닉스나 리눅스는 하나의 호스트 운영 체제 안에서 자원을 분리해 할당하고, 실행되는 프로세스를 격리해서 관리하는 방법을 제공했습니다.
하지만 파일 시스템을 설정하고 자원과 공간을 관리하는 등의 복잡한 과정을 직접 수행해야 해서 일부 전문가만 할 수 있다는 단점이 있었습니다.
이런 복잡한 과정을 쉽게 만들어 주는 도구로 등장한 것이 도커이빈다.
도커는 컨테이너를 사용하는 방법을 명령어로 정리한 것이라고 보면 됩니다.
도커는 사용하면 사용자가 따로 신경쓰지 않아도 컨테이너를 생성할 때 개별적인 실행 환경을 분리하고 자원을 할당합니다!

다양한 컨테이너 관리 도구

컨테이너 관리 도구는 다음과 같이 도커 외에도 여러 가지가 있습니다.

  1. 컨테이너디(Containerd)

    • Docker사에서 컨테이너 런타임 부분을 분리하여 만든 오픈 소스 컨테이너 관리 도구
    • 쿠버네티스와의 통신에 필요한 CRI(Container Runtime Interface) 규격에 맞춰 구현한 플러그인을 사용해 쿠버네티스와 통합
    • 다른 시스템과 통합해 컨테이너를 관리하는 기능 제공 -> 컨테이너 관리 도구를 직접 개발하려는 개발자에게 적합
  2. 크라이오(CRI-O)

    • 레드햇에서 개발해 2019년 클라우드 네이티브 컴퓨팅 재단에 기부한 오픈 소스 프로젝트, 현재 인큐베이팅 단계
    • 범용적인 컨테이너 관리 도구인 도커나 컨테이너디와 달리 쿠버네티스와 통합하는 것을 주목적으로 함
    • 다른 도구보다 가볍고 단순하며, CRI 규격을 자체적으로 구현하고 있어서 별도의 구성 요소나 플러그인 없이 쿠버네티스와 통합 가능
    • 역사가 짧고 아직 대중적이지 않아 관리와 구성에 관한 자료는 도커보다 부족
  3. 카타 컨테이너(Kata Containers)

    • 오픈스택 재단에서 후원하는 오픈 소스 컨테이너 관리 도구
    • 컨테이너마다 독립적인 커널을 제공한다는 점에서 기존 컨테이너 방식과 큰 차이
    • 카타 컨테이너를 실행하면 개별 컨테이너를 위한 가벼운 가상 머신을 생성하고 그 위에서 컨테이너가 작동
    • 따라서 모든 컨테이너는 독립적인 커널을 사용하므로 다른 컨테이너의 영향을 받지 않음
    • 기술적으로 보면 기존 컨테이너 방식과 가상화 방식의 중간 영역에 존재
    • 가상 머신을 통해 컨테이너를 격리하므로 기술적으로는 보안에 좀 더 강하지만, 필요한 CPU나 메모리의 크기가 기존 컨테이너 방식보다 큼
    • 아직 사용자 층이 넓지 않아 자료가 부족
  4. 도커

    • Docker사에서 2013년에 만든 컨테이너 관리 도구
    • 컨테이너 관리 기능 외에도 컨테이너를 실행하는 데 필요한 이미지를 만들거나 공유하는 등의 다양한 기능을 제공
    • 사용자가 명령어를 입력하는 명령어 도구(CLI)와 명령을 받아들이는 도커 데몬으로 구성돼 있음
    • 도커 1.11 이전에는 도커가 직접 컨테이너를 제어했으나, 이후부터는 컨테이너를 제어하기 위해 별도의 컨테이너디를 포함
    • 도커와 쿠버네티스를 함께 설치할 경우 쿠버네티스는 컨테이너 오케스트레이션을 위해 도커에 포함된 컨테이너디를 활용
    • 네트워크를 통한 호출로 작동하므로 구조적으로는 다소 복잡한 편이나 이를 모두 도커에서 관리하기 때문에 사용자는 신경쓰지 않아도 됌
구분컨테이너디크라이오카타 컨테이너도커
명령어 도구별도 지원타 도구 사용자체 지원자체 지원
내부 구조단순매우 단순복잡복잡
확장성좋음좋지 못함좋지 못함매우 좋음
컨테이너 관리좋음좋음좋음매우 좋음
이미지 관리좋음좋음좋음매우 좋음
보안성좋은좋음매우 좋음좋음
자원 사용량매우 좋음매우 좋음좋지 못함좋음
정보량적음거의 없음거의 없음거의 없음

특징을 살펴보고, 현재 쿠버네티스 인프라를 구성하는 데 가장 적합한 도구인 도커에 대해 더 자세히 살펴보도록 합시다.


도커로 컨테이너 다루기

도커는 앞서 베이그런트를 실행할 때 자동으로 설치되었습니다. 여기서는 도커로 컨테이너를 다루는 기본 명령들을 실습해 보겠습니다.
도커 이미지를 내려받아 컨테이너로 실행하고 도커 이미지와 컨테이너를 삭제하는 방법까지 배웁니다.

배울 내용을 간단히 정리하면 이미지 찾기 -> 실행하기 -> 디렉터리와 연결하기 -> 삭제하기 입니다.

먼저 컨테이너 이미지와 컨테이너의 관게를 정리해 봅시다.
컨테이너 이미지는 베이그런트 이미지와 유사합니다.
베이그런트 이미지는 이미지 자체로는 사용할 수 없고 베이그런트를 실행할 때 추가해야만 사용할 수 있습니다.
이와 마찬가지로 컨테이너 이미지도 그대로는 사용할 수 없고 도커와 같은 CRI로 불러들여야 컨테이너가 실제로 작동합니다.
이는 실행 파일과 실행된 파일 관계로 볼 수 잇습니다.
따라서 컨테이너를 삭제할 때는 내려받은 이미지와 이미 실행된 컨테이너를 모두 삭제해야만 디스크의 용량을 온전히 확보할 수 있습니다.(이미지도 용량을 차지하고, 이미지로 컨테이너를 실행했을 때도 별도의 공간을 또 사용합니다)

컨테이너 이미지 알아보기

먼저 컨테이너를 만들 이미지가 있어야 합니다. 이미지를 검색해서 내려받고 구조를 살펴보겠습니다.

이미지 검색하고 내려받기

이미지는 레지스트리(registry) 라고 하는 저장소에 모여 있습니다. 레지스트리는 도커 허브처럼 공개된 유명 레지스트리(저장소, DB라고 생각하시면 됩니다)일 수도 있고, 내부에 구축한 레지스트리일 수도 있습니다.
이미지는 레지스트리 웹 사이트에서 직접 검색해도 되고, Terminus(본인이 사용하는 ssh client) 명령 창에서 쿠버네티스 마스터 노드에 접속해 검색할 수도 있습니다. 이때 별도의 레지스트리를 지정하지 않으면 기본으로 도커 허브에서 이미지를 찾습니다.

  1. docker search <검색어> 를 입력하면 특정한 이름(검색어)을 포함하는 이미지가 있는지 찾습니다.
    이미지는 애플리케이션, 미들웨어 등 고유한 목적에 맞게 패키지돼 있습니다.
    예를 들어 docker search nginx 명령어로 현재 사용할 수 있는 nginx 이미지를 찾아보겠습니다.
    STARS로 가장 많이 사용되는 이미지가 정렬되어 있어서 편하군요!

    표시되는 각 열의 의미는 다음과 같습니다.

    • INDEX : 이미지가 저장된 레지스트리의 이름입니다. (docker.io)
    • NAME : 검색된 이미지 이름입니다. 공식 이미지를 제외한 나머지는 '레지스트리 주소/저장소 소유자/이미지 이름'의 형태입니다. (docker.io/jwilder/nginx-proxy)
    • DESCRIPTION : 이미지에 대한 설명입니다 (official build of Nginx)
    • STARS : 해당 이미지를 내려받은 사용자에게 받은 평가 횟수입니다. 시용자가 좋은 평가를 주고 싶을 떄 스타(STAR)를 추가합니다. 숫자가 큰 이미지일수록 신뢰성이 높은 이미지겠죠? 깃허브와 비슷한 기능이라고 생각하시면 됩니다.
    • OFFICIAL : [OK] 표시는 해당 이미지에 포함된 애플리케이션, 미들웨어 등을 개발한 업체에서 공식적으로 제공한 이미지라는 의미입니다.
    • AUTOMATED : [OK] 표시는 도커 허브에서 자체적으로 제공하는 이미지 빌드 자동화를 활용해 생성한 이미지를 의미합니다.
  2. docker search로 찾은 이미지는 docker pull로 내려받을 수 있습니다. 앞에서 찾은 nginx 이미지를 내려받아 자세히 살펴봅시다!

    • docekr pull nginx

    이미지를 내려받을 때 사용하는 태그, 레이어, 이미지의 고유 식별 값 등을 볼 수 있습니다.

    • 태그(tag) : Using default tag와 함께 뒤에 따라오는 태그 이름을 통해 이미지를 내려받을 때 사용한 태그를 알 수 있습니다. 아무런 조건을 주지 않고 이미지 이름만으로 pull을 수행하면 기본으로 latest 태그가 적용됩니다. latest는 가장 최신 이미지를 의미합니다. 따라서 내려받는 버전이 다를 수도 있습니다.

    • 레이어(layer) : a330b6cecb98, e0ad2c0621bc, 9e56c3e0e6b7, 09f31c94adc6, 32b26e9cdb83, 20ab512bbb07는 pull을 수행해 내려받은 레이어입니다. 하나의 이미지는 여러 개의 레이어로 이루어져 있어서 레이어마다 Pull complete 메시지가 발생합니다.

    • 다이제스트(digest) : 이미지의 고유 식별자(sha256:853b221d3341add7aaadf5f81dd088ea943ab9c918766e295321294b035f3f3e)로 이미지에 포함된 내용과 이미지의 생성 환경을 식별할 수 있습니다. 식별자는 해시(hash) 함수로 생성되며 이미지가 동일한지 검증하는 데 사용합니다. 이름이나 태그는 이미지를 생성할 때 임의로 지정하므로 이름이나 태그가 같다고 해서 같은 이미지라고 할 수 없습니다. 그러나 다이제스트는 고유한 값이므로 다이제스트가 같은 이미지는 이름이나 태그가 다르더라도 같은 이미지입니다.

    • 상태(Status) : 이미지를 내려받은 레지스트리, 이미지, 태그 등의 상태 정보를 확인할 수 있습니다. 형식은 '레지스트리 이름/이미지 이름:태그'입니다. 여기서 내려받은 이미지는 docker.io 레지스트리에서 왔으며, 이미지 이름은 nginx이고, 태그는 앞서 설명한 것처럼 기본 태그인 latest입니다.

이미지의 태그와 레이어 구조는 컨테이너를 이해하는 데 매우 중요한 부분이므로 좀 더 자세히 살펴봅시다!

이미지 태그

태그는 이름이 동일한 이미지에 추가하는 식별자입니다. 이름이 동일해도 도커 이미지의 버전이나 플랫폼(CPU 종류나 기본 베이스를 이루는 운영 체제 등)이 다를 수 있기 떄문에 이를 구분하는 데 사용합니다.
이미지를 내려받거나 이미지를 기반으로 컨테이너를 구동할 때는 이미지 이름만 사용하고 태그를 명시하지 않으면 latest 태그를 기본으로 사용합니다. 이미지 태그와 관련된 정보는 해당 이미지의 도커 허브 메뉴 중 Tags 탭에서 확인할 수 있습니다.

  • 현재(21/09/10) 기준으로 nginx 이미지의 태그는 1.21.3입니다.
    • 따라서 docker pull nginx로 내려받는 latest는 docker pull nginx:1.21.3으로 내려받는 이미지와 동일합니다. 만약 안정화 버전(stable, 1.18.0)을 사용하고 싶으면 docker pull nginx:stable 명령을 사용합니다.
    • 이미지 버전이 올라가면서 추가 또는 변경되는 기능에 따라 예상치 못한 오류가 생길 수 있습니다. 따라서 컨테이너를 배포할 때는 latest 태그가 아닌 검증된 버전으로 배포해야 문제가 생기지 않습니다.

이미지의 레이어 구조

앞에서 컨테이너 이미지는 실행 파일이라고 했는데, 사실 이미지는 애플리케이션과 각종 파일을 담고 있다는 점에서 ZIP 같은 압축 파일에 더 가깝습니다.

그런데 앞축 파일은 압축한 파일의 개수에 따라 전체 용량이 증가합니다. 하지만 이미지는 같은 내용일 경우 여러 이미지에 동일한 레이어를 공유하므로 전체 용량이 감소합니다.

ZIP 같은 압축파일은 내부에 동일한 파일이 포함된 압축 파일과 이미지를 같은 파일이지만 각각 독립적으로 저장하고 공간을 점유합니다. 그에 반해 이미지는 내용이 같은 레이어들을 공유하기 때문에 전체 공간에서 봤을 때 상대적으로 용량을 적게 차지합니다.

두 개의 nginx 이미지(latest와 stable)를 비교해 차이점을 좀 더 살펴봅시다!

이미지의 레이어 구조 확인하기

  1. docker pull nginx:stable 명령으로 stable 이미지를 내려받습니다.
    • a330b6cecb98 레이어는 Already exists라고 이미 존재하는 레이어라는 사실을 확인할 수 있습니다.
    • 앞에서 내려받은 nginx:latest 이미지에 해당 레이어가 있기 때문입니다. 따라서 해당 레이어를 내려받지 않고 재사용합니다!
  2. docker images <이미지 이름> 명령을 실행해 내려받은 이미지를 조회합니다.
    • docker images nginx
    • 두 개의 이미지가 검색됩니다. 각각 SIZE가 133, 133MB입니다. 두 이미지의 용량을 합치면 266MB입니다. 하지만 두 이미지가 공유하는 레이어가 있다면 실제로 차지하는 용량은 더 적습니다. 확인해봅시다.
  3. stable 이미지가 어떤 과정을 거쳐 생성됐는지 docker history nginx:stable 명령을 실행해 확인합니다. 생성 과정에서 단계별로 용량을 얼마나 차지하는지 자세한 이력이 나오므로 이미지가 차지하는 실제 용량을 확인할 수 있습니다.
    • docker history nginx:stable
    • SIZE 열의 용량을 모두 더하면 133MB의 이미지가 생성된 것을 알 수 있습니다. 이때 마지막 ADD file:4ff85d9f6aa2467...69.3 MB를 기억해 두세요.
  4. 이번에는 docker history nginx:latest 명령으로 latest 이미지의 생성 과정과 용량을 확인해봅시다.
    • docker history nginx:latest
    • ADD file:4ff85d9f6aa2467...69.3 MB 부분이 nginx:stable과 같습니다. 두 이미지가 레이어를 공유하고 있음을 알 수 있습니다!

두 이미지가 각각 133MB, 133MB이지만 실제로는 69.3MB에 해당하는 레이어를 두 이미지가 공유하고 있습니다. 따라서 실제로 두 이미지 크기를 합하면 266MB가 아닌 196.7MB가 됩니다.

이처럼 도커로 작성된 컨테이너는 레이어를 재사용하기 때문에 여러 이미지를 내려받더라도 디스크 용량을 효율적으로 사용할 수 있습니다.
참고로 docker history에서 나오는 내용들은 도커 컨테이너 이미지 자체를 만드는 명령을 보여주는 것입니다.
이러한 내용이 담긴 도커파일(Dockerfile)은 잠시 후에 살펴보겠습니다.

컨테이너 실행하기

컨테이너의 이미지 구조를 살펴봤으니 이제 컨테이너를 생성하겠습니다.

컨테이너 단순히 실행하기

먼저 내려받은 이미지를 기반으로 새로운 컨테이너를 실행해 어떻게 구성돼 있는지 살펴봅시다.

  1. 새로운 컨테이너를 실행합니다.
    • docker run -d --restart always nginx
    • docker run으로 컨테이너를 생성하면 결과값으로 fe07... 과 같은 16진수 문자열이 나옵니다. 이런 문자열은 컨테이너를 식별할 수 있는 고유한 ID입니다. ID는 실행할 때마다 다르게 표시되므로 여러분이 실행한 결과는 이와 다를 수 있습니다. 고유한 ID가 생성된다는 것을 기억하고 진행을 위해 ID를 기록해둡니다.(fe0703fa797efe2c9feaefbfb6bbcf771b62b105c2a5f7564b2cd0d768b49806)
    • 컨테이너를 생성하는 명령 형식은 docker run [옵션] <사용할 이미지 이름>[:태그 | @다이제스트]이고, 태그와 다이제스트는 생략할 수 있습니다. 여기서 사용된 옵션은 다음 두 가지 입니다.
      - -d(--detach) : 컨테이너를 백그라운드에서 구동한다는 의미입니다. 옵션을 생략하면 컨테이너 내부에서 실행되는 애플리케이션의 상태가 화면에 계속 표시됩니다. 이 상태에서 빠져나오려고 Ctrl+C를 누르면 애플리케이션뿐만 아니라 컨테이너도 함께 중단됩니다. 따라서 계속 작동해야 하는 서버나 DB같은 프로그램은 -d 옵션을 붙여 백그라운드에서 작동하게 합니다.
      • --restart always : 컨테이너의 재시작과 관련된 정책을 의미하는 옵션입니다. 프로그램에 예상하지 못한 오류가 발생하거나 리눅스 시스템에서 도커 서비스가 중지되는 경우에 컨테이너도 작동이 중지됩니다. 이때 중지된 컨테이너를 즉시 재시작하거나 리눅스 시스템에서 도커 서비스가 작동할 때 컨테이너를 자동으로 시작하도록 설정할 수 있습니다. 앞으로는 가상 머신을 중지한 후 다시 실행해도 자동으로 컨테이너가 기존 상태를 이어 갈 수 있게 --restart always 옵션을 사용하겠습니다.

옵션에 따라 달라지는 컨테이너 시작 방법
컨테이너가 비정삭적으로 종료되거나 리눅스 시스템에서 도커 서비스가 시작될 때 --restart 옵션 값에 따라 일어나는 일을 다음과 같습니다.

컨테이너 비정상 종료 시도커 서비스 시작 시
no(기본값)컨테이너를 재시작하지 않음컨테이너를 시작하지 않음
on-failure컨테이너를 재시작함컨테이너를 시작함
always컨테이너를 재시작함컨테이너를 시작함
unless-stopped컨테이너를 재시작함사용자가 직접 정지하지 않은 컨테이너만 시작함
  1. docker ps 명령으로 생성한 컨테이너 상태를 확인합니다. 여기서 ps는 프로세서 상태(process status)를 의미합니다. 도커의 많은 명령어는 리눅스의 기본 명령어에서 가져왔습니다.

    • docker ps

      조회 결과에서 각 열의 의미는 다음과 같습니다.

    • CONTAINER ID : 컨테이너를 식별하기 위한 고유 ID입니다. ID에는 docker run을 실행한 결과가 일부 표시되는데, 이를 통해 이미지나 컨테이너를 식별할 수 있습니다. (fe0703fa797e)

    • IMAGE : 컨테이너를 만드는 데 사용한 이미지로, 여기서는 nginx를 사용했습니다. (nginx)

    • COMMAND : 컨테이너가 생성될 때 내부에서 작동할 프로그램을 실행하는 명령어입니다. 여기서는 /docker-entrypoint.sh이며 해당 셸이 nginx 이미지로 컨테이너가 생성될 때 nginx 프로그램을 호출해서 서비스를 할 수 있도록 해줍니다. ("/docker-entrypoin...")

    • CREATED : 컨테이너가 생성된 시각을 표시합니다. (14 minutes ago)

    • STATUS : 컨테이너가 작동을 시작한 시각을 표시합니다. CREATED와 달리 컨테이너를 중지했다가 다시 시작할 경우 초기화 됩니다. (Up 14 minutes)

    • PORTS : 컨테이너가 사용하는 포트와 프로토콜을 표시합니다. 80/tcp는 컨테이너 내부에서 80번 포트와 TCP 프로토콜을 사용한다는 뜻입니다. 현재는 해당 포트로 컨테이너에 접속할 수 없습니다. 이 내용은 나중에 설명하겠습니다. (80/tcp)

    • NAMES : 컨테이너 이름을 표시합니다. 이름은 docker run--name <이름> 옵션으로 직접 지정할 수도 있지만, 지정하지 않으면 컨테이너가 시작될 때 도커가 임의로 부여한 값이 나타나빈다. 여기서는 keen_rosalind라는 이름이 임의로 부여됐습니다.

  2. docker ps -f id=fe07 명령으로 컨테이너를 지정해 검색합니다(앞에서 기록해둔 16진수 ID를 넣어야합니다)

    • docker ps -f id=fe07
    • docker ps-f(--filter) <필터링 대상> 옵션을 주면 검색 결과를 필터링할 수 있습니다.
      - 필터링 대상을 지정할 때는 key(대상)=value(값) 형식으로 입력합니다. 이떄 value와 정확하게 일치하지 않더라도 value에 해당하는 문자열을 포함하는 경우는 필터링 됩니다.
    • 자주 사용하는 필터링 키
      - id : 컨테이너 아이디
      • name : 컨테이너 이름
      • label : 컨테이너 레이블
      • exited : 컨테이너가 종료됐을 때 반환하는 숫자 코드
      • status : 컨테이너의 작동 상태
      • ancestor : 컨테이너가 사용하는 이미지
      • 더 많은 키는 도커 공식 문서의 filtering section에서 확인할 수 잇습니다!
  3. 생성된 nginx 컨테이너는 마스터 노드 내부에 존재하므로 curl 127.0.0.1 명령으로 컨테이너가 제공하는 nginx 웹 페이지 정보를 가지고 오게 합니다.

    • curl 127.0.0.1
    • 생성한 컨테이너는 80번 포트를 사용하므로 HTTP의 기본 포트인 80번 포트로 요청을 보냈습니다. 그런데 정상적으로 응답이 돌아오지 않고 Connection refused 에러가 발생합니다. docker ps에서 설명한 80번 포트로 컨테이너에 접속할 수 없다는 문제가 왜 발생하는지 알아봅시다.

    앞에서 컨테이너의 PORTS 열에 표시되는 80/tcp는 컨테이너 내부에서 TCP 프로토콜의 80번 포트를 사용한다는 의미라고 했습니다.
    하지만 curl로 전달한 요청은 로컬호스트(127.0.0.1)의 80번 포트로 전달만 될 뿐 컨테이너까지는 도달하지 못합니다.(호스트 네트워크의 80번 포트까지는 전달됐는데, 도커 네트워크의 80번 네트워크로는 전달되지 않습니다.)
    즉, 호스트에 도달한 후 컨테이너로 도달하기 위한 추가 경로 설정이 돼 있지 않은 것입니다.
    현재 상태로는 가상 머신 호스트에서 컨테이너의 80번 포트에 접속할 수 업습니다. 이를 좀 더 쉽게 이해하려면 마스터 노드의 주소인 192.168.1.10으로 요청하고 동일하게 아무것도 표시되지 않는 것을 확인해 보면 됩니다.

    즉, 마스터 노드의 입장에서는 웹 브라우저를 통한 접속은 기본 포트인 80번에서 처리하려고 하나 이에 대해 응답해 줄 주체가 없는 것입니다.

    따라서 응답을 컨테이너에서 처리해주기를 원한다면 80번으로 들어온 것을 컨테이너에서 바로 받아줄 수 있는 포트로 연결해 주는 설정이 추가로 필요합니다. 추가 설정하는 방법은 바로 다음 실습에서 확인해봅시다.

    작동 중인 컨테이너의 설정을 변경할 수 있나요?
    컨테이너는 변경 불가능한 인프라(immutable infastructure)를 지향합니다.
    변경 불가능한 인프라는 초기에 인프라를 구성하면 임의로 디렉토리 연결이나 포트 노출과 같은 설정을 변경할 수 없습니다.
    따라서 컨테이너에 적용된 설정을 변경하려면 새로운 컨테이너를 생성해야 합니다.
    이러한 특성 덕분에 컨테이너로 배포된 인프라는 배포된 상태를 유지한다는 장점이 있습니다.

추가로 경로를 설정해 정상적으로 컨테이너 실행하기

컨테이너 외부에서도 컨테이너 내부에 접속할 수 있게 새로운 컨테이너를 구동해보겠습니다!

  1. -p 8080:80 옵션을 추가해 새로운 컨테이너(nginx-exposed)를 실행합니다.
    • docker run -d -p 8080:80 --name nginx-exposed --restart always nginx
    • 여기서 -p(--publish)는 외부에서 호스트로 보낸 요청을 컨테이너 내부로 전달하는 옵션으로, -p <요청 받을 호스트 포트>:<연결할 컨테이너 포트> 형식으로 사용합니다.(호스트 네트워크가 8080으로 요청을 받고 도커 네트워크의 80번 포트로 전달)
  2. 컨테이너가 제대로 작동하는지 확인합니다. 필터링 키 중에서 이름을 사용해보겠습니다. -f name=nginx-exposed 옵션을 추가해 필터링합니다.
    • docker ps -f name=nginx-exposed

      앞에서 간단히 만든 컨테이너를 조회한 결과와 달라진 점은 다음과 같습니다.
    • 0.0.0.0:8080->80/tcp : 0.0.0.0의 8080번 포트로 들어오는 요청을 컨테이너 내부의 80번 포트로 전달한다는 의미입니다. 0.0.0.0은 존재하는 모든 네트워크 어댑터를 의미합니다. m-k8s 호스트는 자기 자신을 나타내는 127.0.0.1과 외부에 노출된 192.168.1.10 등의 IP를 가지고 있는데, 요청이 호스트에 할당된 어떤 IP의 8080번 포트로 들어오더라도 컨테이너 내부의 80번 포트로 전달됩니다.
    • nginx-exposed : 현재 작동 중인 컨테이너의 이름입니다. 이전에는 컨테이너 이름을 지정하지 않아 NAMES에 도커가 임의로 부여한 이름이 표시됐지만, 이번에는 --name 옵션으로 지정한 이름이 표시됩니다.
  3. 웹 브라우저에서 192.168.1.10:8080을 입력해 가상 머신을 호스팅하는 노트북에서 컨테이너로 접근할 수 있는지 확인합니다.
    • 192.168.1.10:8080

      모든 주소의 8080번 포트로 들어오는 요청을 컨테이너 내부의 80번 포트로 보내도록 설정했기 때문에 컨테이너의 nginx 접속 초기 화면이 문제 없이 출력됩니다.
    • kubectl get nodes -o wide
      이번에는 마스터 노드(m-k8s)의 INTERNAL-IP인 192.168.1.10이 아닌 워커 노드의 IP인 192.168.1.101에서 8080번 포트로 접근해볼까요?

      - 192.168.1.101:8080

      워커 노드엔 nginx-exposed 컨테이너를 실행해놓지 않았기 때문에 오류가 나는 것을 확인할 수 있습니다!

현재 nginx 내부에는 따로 작성한 파일이 없기 때문에 기본 페이지를 보여줍니다.
따라서 사용자가 원하는 페이지를 출력하기 위해서는 웹 페이지와 관련된 화면을 별도로 작성해야 합니다.
컨테이너 내부에서 웹 페이지 파일을 변경할 수 있지만 이런 경우, 컨테이너를 다시 생성하게 되면 매번 웹 페이지 파일을 전송해야 합니다.
그러므로 영속적으로 웹 페이지 파일을 사용하기 위해서는 특정 디렉토리와 컨테이너 내부의 디렉토리를 연결하는 것이 효과적인 사용법입니다!

컨테이너 내부 파일 변경하기

도커는 컨테이너 내부에서 컨테이너 외부의 파일을 사용할 수 있는 방법으로 크게 4가지를 제공합니다.

  • docker cp
    - docker cp <호스트 경로> <컨테이너 이름>:<컨테이너 내부 경로> 형식으로 호스트에 위치한 파일을 구동 중인 컨테이너 내부에 복사

    • 따라서 컨테이너에 임시로 필요한 파일이 있는 경우 단편적으로 전송하기 위해서 사용
    • 또는 컨테이너에 저장돼 있는 설정 및 로그를 추출해 확인하는 목적으로도 사용(SFTP처럼)
  • Dockerfile ADD
    - 이미지는 Dockerfile을 기반으로 만들어지는데, 이때 Dockerfile에 ADD라는 구문으로 컨테이너 내부로 복사할 파일을 지정하면 이미지를 빌드할 때 지정한 파일이 이미지 내부로 복사됌

    • 이후 해당 이미지를 기반으로 구동한 컨테이너에서는 복사한 파일을 사용할 수 있음
    • 그러나 사용자가 원하는 파일을 선택해 사용할 수 없다는 약점이 존재
  • 바인드 마운트
    - 호스트의 파일 시스템과 컨테이너 내부를 연결해 어느 한쪽에서 작업한 내용이 양쪽에 동시에 반영되는 방법

    • 새로운 컨테이너를 구동할 때도 호스트와 연결할 파일이나 디렉터리 경로만 지정하면 다른 컨테이너에 있는 파일을 새로 생성한 컨테이너와 연결 가능
    • 데이터베이스의 데이터 디렉토리나 서버의 첨부 파일 디렉터리처럼 컨테이너가 바뀌어도 없어지면 안되는 자료는 이 방법으로 보존 가능
      • 딥러닝 업무를 진행할 때 필요한 DB를 이렇게 바인드, 마운트하여 사용하면 편리합니다.
  • 볼륨
    - 호스트의 파일 시스템과 컨테이너 내부를 연결하는 것은 바인드 마운트와 동이랗지만, 호스트의 특정 디렉터리가 아닌 도커가 관리하는 볼륨을 컨테이너와 연결

    • 여기서 말하는 볼륨은 쿠버네티스에서 살펴본 볼륨 구조와 유사
    • 따라서 도커가 관리하는 볼륨 공간을 NFS와 같은 공유 디렉토리로 생성한다면, 다른 호스트에서도 도커가 관리하는 볼륨을 함께 사용할 수 있음
      • 협업을 위해선 이 방법이 좋을 듯 하죠?

사용할 수 있는 방법이 총 4가지가 있지만, 현재 웹 페이지를 연결하는 것처럼 오랫동안 고정된 내용을 각 사용자마다 다르게 취하는 경우에는 바인드 마운트 또는 볼륨이 효과적인 방법입니다.

컨테이너 내부에서 외부 파일을 사용하는 방법을 정리하면 다음과 같습니다.

구분docker cpDockerfile ADD바인드 마운트볼륨
컨테이너 적용구동 중 복사이미지 생성 시 복사구동 시 디렉터리 연결구동 시 도커의 볼륨 연결
파일 보관 위치컨테이너 내부컨테이너 내부호스트(디렉토리)호스트(도커 볼륨)
주 활용 용도임시 파일컨테이너 생성 시 필요한 파일보존이 필요한 파일보존이 필요한 파일
관리 편의성좋지 못함좋음좋음매우 좋음
파일 보존성좋지 못함좋음매우 좋음매우 좋음

4가지 방법 중 nginx 웹 페이지를 사용자가 변경하기에 가장 용이한 바인드 마운트와 볼륨을 실습을 통해 확인해 보겠습니다!

바인드 마운트로 호스트와 컨테이너 연결하기

호스트와 컨테이너를 연결하려면 연결 대상이 되는 컨테이너 내부의 디렉토리 구조를 먼저 알아야 합니다.
현재 정상적으로 노출된 nginx 컨테이너의 구조를 살펴보면 처음 접속할 때 노출되는 페이지는 /usr/share/nginx/html/index.html입니다. 따라서 우리가 수정해야 하는 파일이 index.html이며, 이러한 경로 설정은 /etc/nginx/nginx.conf에 존재합니다.

  1. 컨테이너 내부에 연결할 /root/html/ 디텍토리를 호스트에 생성합니다.

    • mkdir -p /root/html
  2. docker run 명령으로 nginx-bind-mounts라는 이름의 컨테이너를 구동하고, 컨테이너의 /usr/share/nginx/html/ 디렉토리와 호스트의 /root/html/ 디텍터리를 연결합니다.

    • -v(--volume)는 호스트 디렉토리와 컨테이너 디렉토리를 연결하는 옵션이로, -v <호스트 디렉토리 경로>:[컨테이너 디텍토리 경로] 형식으로 사용합니다.
    • 이때 앞서 사용한 8080번 포트와 중복되지 않게 호스트의 포트 번호는 8081번으로 지정해 컨테이너 내부의 80번 포트와 연결합니다.
    • 여기서 한 가지 중요한 바인드 마운트의 특성을 짚고 넘거가자면, 호스트 디렉토리의 내용을 그래도 컨테이너 디렉토리에 덮어쓴다는 점입니다. 따라서 컨테이너 디렉토리에 어떠한 내용이 있더라도 해당 내용은 삭제된다는 것에 유의합니다!!
    • docker run -d -p 8081:80 -v /root/html:/usr/share/nginx/html --restart always --name nginx-bind-mounts nginx
  3. nginx-bind-mounts 컨테이너를 조회해 STATUS가 정상(Up n minutes)인지 확인합니다.

    • docker ps -f name=nginx-bind-mounts
  4. 컨테이너가 정상적으로 구동했음을 확인하면 컨테이너 내부와 연결된 /root/html/ 디렉토리를 확인합니다. 이 디렉토리는 사용자가 호스트에 생성한 빈 비렉토리인데, 조회하면 여전히 비어있습니다.

    • 빈 디렉토리가 컨테이너와 연결됐기 때문에 현재 컨테이너의 nginx는 초기 화면으로 보여 줄 파일이 없습니다.
    • ls /root/html
  5. 웹 브라우저에서 192.168.1.10:8081에 연결해 nginx-bind-mounts 컨테이너에서 실행되는 nginx에 접속되는지 확인합니다. 이 때 nginx의 기본 화면이 아닌 오류 화면이 표시됩니다.

    • 사용자가 nginx에 접속하면 index.html을 읽어서 화면에 표시해 주도록 설계돼 있습니다.
      - 따라서 바인드 마운트 설정에 따라 호스트 디렉터리의 /root/html에 있는 index.html을 노출하려고 하지만 파일이 없기 때문에 403 Forbidden 오류가 발생하는 것입니다. 403 에러는 일반적으로 권한이 존재하지 않을 때 표시되는 에러이나, 파일이 존재하지 않을 때도 표시 됩니다. 자세한 것은 html 상태 코드를 참고하세요!
  6. cp 명령어로 호스트 디렉터리와 컨테이너 디렉터리를 연결할 때 사용할 index.html을 /root/html/에 복사합니다. 저자가 미리 작성해 둔 파일을 사용하겠습니다. 그리고 제대로 저장되었는지 확인합니다.

    • cp ~/_Book_k8sInfra/ch4/4.2.3/index-BindMount.html /root/html/index.html
    • ls /root/html
    • 마스터 노드에 있는 디렉터리 내의 파일을, 같은 마스터 노드의 /root/html로 복사하는 것입니다.(~/_어쩌구 -> /root/html)
  7. 브라우저에서 192.168.1.10:8081로 다시 접속해 index.html이 정상적으로 표시되는지 확인합니다.

컨테이너 내부 확인하기
호스트 디렉터리와 컨테이너 디렉터리가 연결된 경우에 컨테이너 내부를 보려면 어떻게 할까요?
kubectl과 동일하게 docker에도 컨테이너에 접속하거나 내부에 명령을 전달하고 결괏값을 반환하는 exec라는 명령어가 있습니다.
docker exec <컨테이너 ID | 이름> <명령어> 형식으로 실행하면 컨테이너에서 명령을 실행하고 결과를 보여 줍니다. 결과를 보면 바인드 마운트를 통해 호스트와 ㅇ녀결된 index.html이 확인됩니다.
docker exec nginx-bind-mounts ls /usr/share/nginx/html

docker의 많은 기능과 명령이 kubectl과 동일하거나 비슷합니다!

볼륨으로 호스트와 컨테이너 연결하기

볼륨(volume)은 도커가 직접 관리하며 컨테이너에 제공하는 호스트의 공간입니다.
앞서 배운 바인드 마운트와는 어떤 차이가 있는지 실습으로 확인해봅시다!
볼륨은 바인드 마운트와 다르게 덮어쓰기가 아니고 동기화됩니다(호스트 디렉토리가 비어있을 경우에)!

  1. 볼륨을 생성합니다. 이때 nginx-volume은 생성할 볼륨의 이름입니다.

    • docker volume create nginx-volume
  2. 생성된 볼륨을 조회합니다. 볼륨에 적용된 드라이버 종류와 실제 호스트와 연결된 디렉토리, 볼륨 이름 등을 조회할 수 있습니다.

    • docker volume inspect nginx-volume
    • Mountpoint 행의 /var/lib/docker/volumes/nginx-volume/_data/ 디렉토리가 볼륨 디렉토리임을 확인할 수 있습니다. 컨테이너 내부와 연결할 때 전체 디렉토리 경로를 사용하지 않고 nginx-volume이라는 볼륨 이름만으로 간편하게 연결할 수 있습니다.
  3. 볼륨으로 생성된 디렉터리를 확인합니다.

    • ls /var/lib/docker/volumes/nginx-volume/_data
      디렉토리는 현재 비어 있습니다. 디렉토리를 컨테이너와 연결해 봅시다. 앞서 바인드 마운트에서 빈 디렉토리를 컨테이너 내부 디렉토리에 덮어쓰는 것과 어떤 차이가 있는지 살펴봅니다.
  4. 컨테이너에 연결할 볼륨을 호스트에 생성했으니 호스트와 컨테이너의 디렉토리를 연결할 컨테이너를 구동합니다.

    • 기존 컨테이너는 설정을 바꿀 수 없으므로 새로운 컨테이너를 구동해야 합니다.
    • nginx-volume이라는 이름의 컨테이너를 구동하고 컨테이너 내부의 /usr/share/nginx/html/ 디렉토리와 호스트의 nginx-volume 볼륨을 연결합니다.
    • 사용하는 옵션은 -v [볼륨 이름]:[컨테이너 디렉토리]입니다.
    • 앞서 사용한 포트와 중복되지 않게 호스트의 포트 번호는 8082번으로 컨테이너 내부의 80번 포트와 연결합니다.
    • docker run -d -v nginx-volume:/usr/share/nginx/html -p 8082:80 --restart always --name nginx-volume nginx
  5. 볼륨 디렉토리의 내용을 다시 확인합니다.

    • 바인드 마운트와 달리 볼륨은 빈 디렉토리를 덮어 쓰지 않고 컨테이너 내부에 있는 50x.html과 index.html 파일을 보존하는 것을 확인할 수 있습니다.(아까는 빈 디렉토리가 덮어써져 ~/_어쩌구 디렉토리에서 파일을 복사해왔던 것 기억 나시죠?)
    • 참고로 50x.html 파일은 웹사이트 내부 오류가 발생했을 때 오류 화면으로 노출하는 파일입니다.
    • ls /var/lib/docker/volumes/nginx-volume/_data
  6. 웹 브라우저로 192.168.1.10:8082를 연결해 nginx-volume 컨테이너의 nginx에 접속합니다.

    • 볼륨은 바인드 마운트와 다르게 index.html을 삭제하지 않았기 때문에 index.html의 내용이 그대로 표시됩니다.
    • 볼륨은 바인드 마운트와 달리 호스트 디렉토리를 컨테이너 디렉터리에 덮어쓰는 구조가 아니라 양쪽을 서로 동기화시키는 구조이기 때문에, 비어 있는 볼륨을 연결하는 경우에는 컨테이너 디렉터리에 있는 파일이 보존됩니다.
    • 하지만 볼륨에 컨테이너 디렉터리와 동일한 파일이 존재한 상태로 연결하는 경우에는 덮어쓰기가 되니 이 점에 대해 유의할 필요가 있습니다!!
    • 볼륨에도 파일이 있고 컨테이너 디렉터리에도 파일이 있는데, 각각 다른 파일이 존재할 경우 파일들이 합쳐진(각각 추가된) 상태로 동기화 되는지도 궁금하군요. 기회가 되면 시도해보겠습니다.
  7. nginx-volume에 cp 명령어로, 바꿀 파일을 볼륨 디렉터리로 복사해 볼륨에서 변경한 내용이 컨테이너 디렉터리에 동기화 되는지 테스트합니다.

    • 외부(볼륨) 파일이 변경되면, 자동으로 내부(컨테이너 디렉토리)가 동기화 되는지 확인하는 것입니다!
    • cp ~/_Book_k8sInfra/ch4/4.2.3/index_Volume.html /var/lib/docker/volumes/nginx-volume/_data/index.html
  8. 웹 브라우저에서 다시 192.168.1.10:8082에 접속하면 바뀐 index.html이 표시됩니다(동기화 되었습니다!).

    • 저는 쿠키 문제가 있는지, 변경된 파일이 적용되는데 시간이 조금 걸렸습니다. 안되면 cp 명령을 재시도하고 재접속 해보세요!

이처럼 볼륨을 사용하면 컨테이너에 존재하는 파일을 그대로 보존할 수도 있고, 필요할 때 변경해서 사용할 수도 있습니다.
또한, 사용 중인 볼륨을 docker volume ls 명령으로 조회할 수 있고, docker volume rm 명령으로 삭제할 수도 있어서 바인드 마운트보다 관리하기가 더 쉽습니다!

볼륨 경로는 고정돼 있나요?
실습에서 볼륨은 /var/lib/docker/volumes/ 디렉터리 안에 생성됐습니다.
리눅스의 시스템 디렉터리 정의에 따르면 var 디렉터리는 시스템이 사용하는 파일 중 로그, 캐시, 상태 정보 등을 저장합니다.
docker volume create를 실행할 때 기본으로 설정된 /var/lib/docker/volumes 디렉터리를 그대로 사용하면 도커의 볼륨을 사용하는 데이터가 늘어날수록 시스템의 다른 기능이 사용해야 하는(로그, 캐시 등) 용량까지 차지하는 문제가 생길 수 잇습니다.
리눅스 시스템을 운영할 때 큰 용량의 데이터를 저장하는 경로는 별도로 분리해 충분한 용량을 할당하는 경우가 많습니다.
따라서 대량의 데이터를 저장하고 연결하려는 목적이면 기본 디렉토리가 아닌 충분한 용량이 확보된 디렉토리 경로를 설정해야 할 필요가 있습니다.
도커 버전에 따라서 경로를 분리하는 기능의 지원 여부가 다른데, 예제에서 사용힌 버전은 1.13.1입니다.
docker-ce 17.05 버전부터는 도커의 데몬 설정에서 볼륨이 저장되는 경로를 지정하는 --data-root 옵션이나 호스트 경로와 컨테이너 경로를 명확하게 연결하는 --mount 옵션을 제공합니다.

사용하지 않는 컨테이너 정리하기

사용이 끝나고 더 이상 사용하지 않을 컨테이너라면 공간을 확보히기 위해 삭제하는 것이 좋습니다.
그래서 이번에는 컨테이너를 안전하게 삭제하는 방법을 알아봅시다!

컨테이너 정지하기

컨테이너나 이미지를 삭제하기 전에 먼저 컨테이너를 정지해야 합니다!
삭제할 때 말고도 동일한 호스트의 포트를 사용하는 컨테이너를 배포하거나 작동 중인 컨테이너 사용 자체를 종료할 때도 먼저 컨테이너를 정지해야 합니다.

  1. docker ps -f ancestor=nginx 명령으로 nginx 이미지를 기반으로 생성된 컨테이너를 조회합니다.

    • ancestor 키는 컨테이너를 생성하는 데 사용한 이미지를 기준으로 필터링합니다.
    • docker ps -f ancestor=nginx
  2. 컨테이너를 정지하는 명령은 docker stop <컨테이너 이름 | ID> 입니다. 4번째 행에 표시된 컨테이너인 keen_rosalind를 정지해보겠습니다. 컨테이너가 정지되면 입력한 컨테이너 이름이 결과로 표시됩니다.

    • docker stop keen_rosalind
  3. 3번째 행에 표시된 nginx-exposed 컨테이너를 컨테이너 ID로 정지해 보겠습니다. 해당 컨테이너의 ID는 cccc6fa9409b이므로 docker stop cccc으로 정지합니다. 컨테이너 정상적으로 정지되면 입력한 컨테이너 이름이 결과로 표시됩니다.

    • docker stop cccc
  4. 컨테이너를 하나씩 정지하면 번거로울 수 있으니 nginx 이미지를 사용하는 모든 컨테이너를 한꺼번에 정지해 보겠습니다. 컨테이너 조회 명령에 -q(--quite) 옵션을 추가해 컨테이너 ID만 출력합니다.

    • docker ps -q -f ancestor=nginx
  5. 앞서 사용한 명령을 docker stop에서 인자로 사용하도록 $()안에 넣습니다. 실행하면 nginx를 이미지로 사용하는 모든 컨테이너가 정지됩니다.

    • docker stop $(docker ps -q -f ancestor=nginx)
  6. 모든 컨테이너가 정지됐는지 다시 조회해봅시다!

    • docker ps -q -f ancestor=nginx
  7. 컨테이너가 모두 정지됐으나 삭제된 것은 아닙니다. 정지된 컨테이너를 포함해 모든 컨테이너 목록을 조회합니다.

    • docker ps-a(--all) 옵션을 주면 정지된 컨테이너를 포함해 nginx 이미지를 기반으로 생성한 컨테이너가 모두 조회됩니다.
    • 이때 정지된 컨테이너를 다시 구동하고 싶다면 docker start <컨테이너 이름 | ID>를 실행합니다.
    • docker ps -a -f ancestor=nginx

컨테이너와 이미지 삭제하기

정지한 컨테이너가 더 이상 필요 없으면 삭제해 사용 중인 컨테이너 목록을 관리하고, 사용하지 않는 컨테이너 이미지를 삭제해 저장 공간을 확보합니다.

  1. 컨테이너는 docker rm <컨테이너 이름 | ID> 명령으로 삭제합니다. 이 명령은 한꺼번에 컨테이너를 중지할 때 사용했던 방법을 그대로 사용합니다. 기존 옵션과 조합해 현재 정지된 모든 컨테이너를 삭제해보겠습니다.
    • docker rm $(docker ps -aq -f ancestor=nginx)

정지하지 않은 컨테이너 삭제 시 발생하는 오류와 해결책
컨테이너를 정지하지 않았거나 컨테이너의 자동 재시작 옵션 때문에 컨테이너가 구동 중일 때 삭제를 진행하면 'You cannot remove a running container' 라는 오류가 발생합니다. 이런 경우에는 docker stop 명령으로 컨테이너를 다시 정지한 후 삭제하거나, docker rm-f(--force) 옵션을 붙여 실행 중인 컨테이너를 강제로 삭제합니다. 하지만 강제 삭제는 의도하지 않은 삭제가 일어날 수 있으니 주의해야 합니다.

  1. 컨테이너가 정상적으로 삭제됐는지 docker ps -a -f ancestor=nginx 명령으로 확인합니다. 모든 컨테이너가 삭제되면 조회되는 컨테이너가 없는게 정상입니다.

    • docker ps -a -f ancestor=nginx
  2. 컨테이너는 모두 삭제했지만, 내려받은 이미지는 아직도 남아서 공간을 차지하고 있습니다. 용량 확보를 위해 더 이상 필요없는 이미지를 삭제하겠습니다.

    • nginx:latest 이미지와 nginx:stable 이미지를 하나씩 삭제할 수도 있지만, docker rmi $(docker images -q nginx) 명령으로 이미지를 한번에 삭제해보겠습니다.
    • 여기서 rmi는 이미지를 지운다는 의미입니다.(rm + i)
    • 참고로 이미지 컨테이너가 정지 상태가 아닌 삭제 상태일 때 삭제할 수 있습니다. 따라서 이미지를 삭제하기 전에 먼저 컨테이너를 삭제해야 합니다.
    • docker rmi $(docker images -q nginx)

이로써 도커로 컨테이너를 다루는 방법을 모두 살펴봤습니다. 이제 다음 포스팅에선 직접 컨테이너 이미지를 만들어 쿠버네티스에서 사용하는 방법을 알아봅시다!


본 게시물은 "컨테이너 인프라 환경 구축을 위한 쿠버네티스/도커 - 조훈,심근우,문성주 지음(2021)" 기반으로 작성되었습니다.

profile
MLOps, MLE 직무로 일하고 있습니다😍

0개의 댓글