본문 바로가기

개발 관련/Docker

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

✏️ 도커란?

도커와 쿠버네티스는 자주 엮어서 언급되곤 한다. 이는 쿠버네티스를 이루는 기본 오브젝트가 파드고, 파드는 컨테이너로 이루어져 있으며, 컨테이너를 만들고 관리하는 도구가 도커이기 때문이다.

파드는 워커 노드라는 노드 단위로 관리하며, 워커 노드와 마스터 노드가 모여 쿠버네티스 클러스터가 된다. 또한 파드는 1개 이상의 컨테이너로 이루어져 있다.

파드는 쿠버네티스로부터 IP를 받아 컨테이너가 외부와 통신할 수 있는 경로를 제공하기도 한다. 파드가 컨테이너들이 정상적으로 작동하는지 확인하고 네트워크나 저장 공간을 서로 공유하므로 컨테이너들은 마치 하나의 호스트에 존재하는 것처럼 작동할 수 있다.

출처 - https://twofootdog.tistory.com/7

위 그림과 같이 컨테이너를 돌보는 것이 파드고, 파드를 돌보는 것이 쿠버네티스 워커 노드이며, 워커 노드를 돌보는 것이 쿠버네티스 마스터이다. 

 

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

 

이렇게 각자 독립적으로 작동하는 컨테이너를 효과적으로 다루는 도구가 도커이다. 오래전부터 유닉스나 리눅스에서 제공해 온 파일 시스템을 설정하고 자원과 공간을 관리하는 등의 복잡한 과정을 직접 수행해야 해서 일부 전문가만 사용할 수 있었던 방법과는 다르다. 도커는 컨테이너를 사용하는 방법을 명령어로 정리한 것인데, 이를 사용하면 사용자가 신경쓰지 않아도 컨테이너 생성 시 개별적인 실행 환경을 분리하고 자원을 할당한다.

 

컨테이너 관리 도구는 도커 외에도 컨테이너디, 크라이오, 카타 컨테이너 등이 있는데, 아래 표를 통해 짧게 보고 지나가겠다.

구분 컨테이너디 크라이오 카타 컨테이너 도커
명령어 도구 별도 지원 타 도구 사용 자체 지원 자체 지원
내부구조 단순 매우 단순 복잡 복잡
확장성 좋음 좋지 못함 좋지 못함 매우 좋음
컨테이너 관리 좋음 좋음 좋음 매우 좋음
이미지 관리 좋음 좋음 좋음 매우 좋음
보안성 좋음 좋음 매우 좋음 좋음
자원 사용량 매우 좋음 매우 좋음 좋지 못함 좋음
정보량 적음  거의 없음 거의 없음 매우 많음

✏️ 컨테이너 이미지

컨테이너 이미지는 베이그런트 이미지와 유사하다. 이미지 자체로 사용할 수 없고, 베이그런트를 실행할 때 추가해야만 사용할 수 있는 베이그런트 이미지와 마찬가지로 컨테이너 이미지도 그래도 사용할 수 없고 도커와 같은 CRI(Container Runtime Interface)로 불러들여야 컨테이너가 실제로 작동한다. 또한 컨테이너를 삭제할 때는 내려받은 이미지와 이미 실행된 컨테이너를 모두 삭제해야만 디스크의 용량을 온전히 확보할 수 있다.

 

이미지는 레지스트리(registry)라는 저장소에 모여있다. 이는 도커 허브처럼 공개된 레지스트리이거나 내부에 구축된 레지스트리일 수도 있다. 아래와 같은 명령어로 검색어를 포함하는 이미지가 있는지 찾는다.

docker search <검색어>

nginx

  • INDEX - 이미지가 저장된 레지스트리의 이름. 위의 사진에서는 보이지 않음.
  • NAME - 검색된 이미지 이름. 공식 이미지를 제외한 나머지는 '레지스트리 주소/저장소 소유자/이미지 이름' 형태
  • DESCRIPTION - 이미지 설명
  • STARS - 해당 이미지를 내려받은 사용자에게 받은 평가 횟수. 사용자가 좋은 평가를 주고 싶을 때 스타 추가. 숫자가 클수록 신뢰성 높은 이미지를 뜻함.
  • OFFICIAL - [OK] 표시는 해당 이미지에서 포함된 애플리케이션, 미들웨어 등을 개발한 업체에서 공식적으로 제공한 이미지라를 의미
  • AUTOMATED - [OK] 표시는 도커 허브에서 자체적으로 제고하는 이미지 빌드 자동화 기능을 활용해 생성한 이미지를 의미

 

찾은 이미지는 docker pull로 내려받을 수 있다.

  • 태그(tag) - Using default tag 뒤에 따라오는 태그 이름을 통해 내려받을 때 사용한 태그를 알 수 있다. 아무런 조건을 주지 않고 pull을 수행하면 기본으로 latest가 적용되며, 가장 최신 이미지를 뜻한다. 내려받는 이미지 버전이 다를 수 있다.
  • 레이어(layer) - pull을 수행해 내려받은 레이어로, 하나의 이미지는 여러 개의 레이어로 이루어져있어 레이어마다 Pull Complete 메시지가 발생한다.
    • 이름이나 태그는 임의로 생성되므로 이름이나 태그가 같다고 해서 같은 이미지라고 할 수 없다.
  • 다이제스트(digest) - 이미지의 고유 식별자. 이미지에 포함된 내용과 이미지의 생성 환경을 식별할 수 있다. 식별자는 해시(hash) 함수로 생성되며, 이미지가 동일한지 검증하는 데 사용된다.
    • 다이제스트는 고유한 값이므로 다이제스트가 같은 이미지는 이름이나 태그가 다르더라도 같은 이미지다.
  • 상태(status) - 이미지를 내려받은 레지스트리, 이미지, 태그 등의 상태 정보를 확인할 수 있다. 형식은 '레지스트리 이름/이미지 이름:태그'이다. 위의 사진을 보면, 내려받은 이미지는 docker.io의 레지스트리에서 왔으며, 이미지의 이름은 nginx이고, 태그는 앞서 설명한 것처럼 기본 태그인 latest이다.

📌 이미지

1) 이미지 태그

태그는 이름이 동일한 이미지에 추가하는 식별자로, 이름이 동일해도 도커 이미지의 버전이나 플랫폼이 다를 수 있기 때문에 이를 구분하는데 사용한다.

nginx 이미지의 latest 버전은 1.23.1로, docker pull nginx로 내려받는 latest는 docker pull nignx:1.23.1 과 동일하다.

안정화 버전을 사용하고 싶다면 docker pull nginx:stable 명령을 실행한다.

컨테이너를 배포할 때는 latest가 아닌 검증된 버전으로 배포해야 문제가 생기지 않는다.

 

2) 이미지 레이어 구조

이미지는 애플리케이션과 각종 파일을 담고 있다는 점에서 실행 파일보다는 zip 같은 압축 파일에 더 가깝다. 파일 개수에 따라 전체 용량이 증가하는 압축 파일에 비해, 이미지는 같은 내용일 경우 여러 이미지에 동일한 레이어를 공유하므로 전체 용량이 감소한다.

위의 사진을 보면 앞에서 내려받은 nginx:latest 이미지에 해당 레이어가 있기 때문에, 5b1423465504 레이어가 이미 존재한다는 뜻의 Already exists 실행결과를 볼 수 있다. 

 

docker images nginx

docker images <image name>

내려받은 이미지를 조회한다. stable 태그와 latest 태그 두 개 이미지가 검색되며, 각각 134MB, 133MB이다.

 

stable 이미지가 어떤 과정을 거쳐 생성됐는지는 docker hisgory nginx:stable 명령을 실행해 확인한다.

 

이번에는 docker history nginx:latest로 latest 이미지의 생성 과정과 용량을 확인한다.

Ad file: ~ 부분과 74.3MB부분이 stable과 같다. stable과 latest 이미지의 크기는 각각 134, 133MB이지만 실제로는 74.3MB에 해당하는 레이어를 두 이미지가 공유하고 있다. 즉 두 이미지의 크기를 합하면 192.7MB이다. 


✏️ 컨테이너 실행하기

내려받은 이미지를 기반으로 새로운 컨테이너를 실행하자. 다음의 명령어를 입력한다.

docker run -d --restart always nginx

c8d3~과 같은 16진수 문자열을 볼 수 있는데, 이는 컨테이너를 식별할 수 있는 고유한 ID다.

컨테이너를 생성하는 명령 형식은 다음과 같다.

docker run [option] <사용할 이미지 이름>[:태그 | @다이제스트]

맨 뒤의 태그와 다이제스트는 생략 가능하며, 사용할 수 있는 옵션은 다음과 같다.

  • -d(--detach) - 컨테이너를 백그라운드에서 구동한다는 의미로, 옵션 생략시 컨테이너 내부에서 실행되는 애플리케이션의 상태가 화면에 계속 표시됨
  • --restart always - 컨테이너의 재시작과 관련된 정책을 의미하는 옵션으로, 오류에 의해 도커 서비스가 중지되는 경우 컨테이너를 즉시 재시작하거나 리눅스 시스템에서 도커 서비스가 작동할 때 컨테이너를 자동으로 시작하도록 설정 가능
  • no(기본값) - 컨테이너 시작 또는 비정상 종료 시 컨테이너를 시작하지 않음

 

다음은 ps로 프로세스 상태(process status)를 확인한다.

docker ps

  • CONTAINER ID - 컨테이너를 식별하기 위한 고유 ID
  • IMAGE - 컨테이너를 만드는데 사용하는 이미지
  • COMMAND - 컨테이너가 생성될 때 내부에서 작동할 프로그램을 실행하는 명령어로, /docker-entrypoint.sh이 해당 셀이 nginx 이미지로 컨테이너가 생성될 때 nginx 프로그램을 호출해서 서비스를 할 수 있도록 해준다.
  • CREATED - 컨테이너가 생성된 시각을 표시한다.
  • STATUS - 컨테이너가 작동을 시작한 시각으로, 중지 후 재시작한 경우 초기화된다.
  • PORTS - 컨테이너가 사용하는 포트와 프로토콜을 표시한다. 80/tcp는 컨테이너 내부에서 80번 포트와 TCP 프로토콜을 사용한다는 뜻이다.
  • NAMES - 컨테이너 이름이며, docker run 명령어 실행 시, --name <name> 옵션으로 직접 지정하지 않는다면 도커가 임의로 부여한 값을 배정받는다. 여기서는 flamboyant_pascal 이라는 이름이 임의로 부여됐다.

위와 같이 docker ps에 -f(--filter) 옵션을 주면, 검색 결과를 필터링할 수 있다. id=c8d3을 지정해 conatiner id에 c8d3이라는 문자열이 포함된 컨테이너를 출력한다.


다음은 curl 127.0.0.1 명령으로 컨테이너가 제공하는 nginx 웹 페이지 정보를 가지고 오게 한다.

이 요청이 실패하는 이유는 로컬호스트(127.0.0.1)의 80번 포트로 전달은 되지만, 컨테이너까지는 도달하지 못한다. 즉 호스트에 도달한 후 컨테이너로 도달하기 위한 추가 경로 설정이 돼 있지 않기 때문에 실패한 것이다.

떄문에 응답을 컨테이너에서 처리해주길 원한다면 80번으로 들어온 것을 컨테이너에서 받아줄 수 있는 포트로 연결해 주는 추가 설정이 필요하다. 컨테이너 외부에서도 컨테이너 내부에 접속할 수 있게 새로운 컨테이너를 구동한다. 다음과 같이 설정한다.

-p는 publish 옵션으로, 외부에서 호스트로 보낸 요청을 컨테이너 내부로 전달하는 옵션이다. 다음과 같이 사용한다.

-p <요청받을 호스트 포트>:<연결한 컨테이너 포트>

 

docker ps로 컨테이너가 제대로 작동하는지 확인한다. 이 때 필터링은 이름으로 사용한다.

  • 0.0.0.0:8080->80/tcp - 0.0.0.0의 8080번 포트로 들어오는 요청을 컨테이너 내부의 80번 포트로 전달한다는 의미
  • nginx-exposed - 현재 작동 중인 컨테이너의 이름으로, 임의로 부여된 이름이 아닌 --name 옵션으로 지정한 nginx-exposed 이름이 부여됨

 

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

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

  • docker cp - 호스트에 위치한 파일을 구동 중인 컨테이너 내부에 복사한다. 파일, 설정 및 로그를 추출해 확인하는 목적으로 사용
docker cp <host 경로> <container name>:<container 내부 경로>
  • Dockerfile ADD: 컨테이너 내부로 복사할 파일을 지정하면 이미지를 빌드 시 지정한 파일이 이미지 내부로 복사된다. 사용자가 원하는 파일을 선택해 사용할 수 없다는 약점이 있다.
  • 바인트 마운트 - 호스트의 파일 시스템과 컨테이너 내부를 연결해 어느 한쪽에서 작업한 내용이 양쪽에 동시에 반영되는 방법이다.
  • 볼륨 - 호스트의 파일 시스템과 컨테이너 내부를 연결하는 것은 바인트 마운트와 동일하지만, 호스트의 특정 디렉터리가 아닌 도커가 관리하는 볼륨을 컨테이너와 연결한다.

웹 페이지를 연결하는 것처럼 오랫동안 고정된 내용을 각 사용자마다 다르게 취하는 경우에는 바인드 마운트나 볼륨이 효과적이다.

책에서는 다음과 같이 실습했다.

mkdir -p /root/html

docker run -d -p 8081:80 \

ls /root/html

 

볼륨으로 호스트와 컨테이너 연결하는 방법은 다음과 같다. 볼륨은 도커가 직접 관리하며 컨테이너에게 제공하는 호스트의 공간이다.

docker volume create nginx-volume

docker volume inspect nginx-volume

ls /var/lib/docker/volumes/nginx-volume/_data

docker -d -v nginx-volume:/usr/share/nginx/html \

ls /var/lib/docker/voluems/nginx-volume/_data

cp ~/_Book_k8sInfra/ch4/4.2.3/index-Volume.html /var/lib/docker/volumes/nginx-volume/_data/index.html

 


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

컨테이너나 이미지 삭제 전에는 먼저 컨테이너를 정지한다.

일단 다음의 명령어로 nginx 이미지를 기반으로 생성된 컨테이너를 조회한다. ancestor는 컨테이너를 생성하는 데 사용한 이미지 기준으로 필터링한다. 

컨테이너를 정지하는 방법은 다음과 같다.

docker stop <컨테이너 이름 | ID>

 

이번에는 ID로 정지한다.

 

한꺼번에 정지하는 방법은 다음과 같다. 컨테이너 조회 명령에 -q(--quite) 옵션을 추가해 컨테이너 ID만 출력하고, docker stop을 통해 nginx를 이미지로 사용하는 모든 컨테이너를 정지시킨다.

docker ps -q -f ancestor=nginx

docker stop $(docker ps -q -f ancestor=nginx)

모든 컨테이너가 정지됐다.

 

정지된 컨테이너를 포함해 모든 컨테이너를 조회한다. 컨테이너가 정지는 됐으나 삭제된 것은 아니기 때문이다.


📌 컨테이너 삭제

컨테이너 삭제는 다음과 같은 명령어로 실행한다.

docker rm <컨테이너 이름 | ID>

다음과 같이 현재 정지된 모든 컨테이너를 삭제한다.

삭제가 제대로 되었는지 docker ps -a -f ancestor=nginx 명령으로 확인한다.

컨테이너는 모두 삭제했지만, 내려받은 이미지는 아직도 남아 공간을 차지하고 있다. 필요 없는 이미지를 삭제하자. docker rmi $(docker image -q nginx) 로 이미지를 한 번에 삭제하자.


✏️ 4가지 방법으로 컨테이너 이미지 만들기

컨테이너 이미지를 만드는 방법은 다음과 같이 4가지이다.

  1. 기본적인 빌드
  2. 용량 줄이기
  3. 컨테이너 내부 빌드
  4. 멀티 스테이지
FROM openjdk:8
LABEL description="Echo IP Java Application"
EXPOSE 60431
COPY ./target/app-in-host.jar /opt/app-in-image.jar
WORKDIR /opt
ENTRYPOINT ["java", "-jar", "app-in-image.jar"]

이 부분부터는 실습이 제대로 되지 않아, Dockerfile만 보고 지나간다. 각 줄의 내용은 다음과 같다.

  • FROM <이미지 이름>:[태그] 형식으로 이미지를 갖고 온다. 가져온 이미지 내부에서 컨테이너 이미지를 빌드하며 여기서는 openjdk를 기초 이미지로 사용한다.
  • LABEL <레이블 이름>=<값> 형식으로 이미지에 부가적인 설명을 위한 레이블을 추가할 때 사용한다.
  • EXPOSE <숫자> 형식으로 생성된 이미지로 컨테이너를 구동 시 어떤 포트를 사용할지 알려준다. EXPOSE를 사용한다고 해서 컨테이너 구동 시 자동으로 해당 포트를 호스트 포트와 연결하지 않는다. 외부와 연결하려면 지정한 포트를 호스트 포트와 연결해야한다는 정보를 제공할 뿐이다.
  • COPY~ 는 호스트에서 새로 생성하는 컨테이너 이미지로 필요한 파일을 복사한다.
  • 이미지의 현재 작업 위치를 opt로 변경한다.
  • ENTRYPOINT ["명령어", "옵션" ... "옵션"]의 형식으로, 컨테이너 구동 시 java -jar app-in-image.jar이 실행된다는 의미다.

이렇게 컨테이너 이미지를 빌드하는 방법을 알아본 것은 직접 만든 컨테이너 이미지를 쿠버네티스에서 사용하기 위함이다.

쿠버네티스에서 이미지를 사용하려면 쿠버네티스가 이미지를 불러올 수 있는 공간에 이미지를 넣어 두어야 한다.

직접 만든 이미지를 kubectl 명령으로 쿠버네티스 클러스터에서 바로 구동 가능하다.


✏️ 도커 레지스트리

호스트에서 생성한 이미지를 쿠버네티스에서 사용하려면 모든 노드에서 공통으로 접근하는 레지스트리(저장소)가 필요하다.

구분 하버 넥서스 리포지터리 도커 레지스트리
가격 유/무료 무료 유/무료 무료
저장 형식 도커 이미지, 헬름 도커 이미지, 헬름 다양함 도커 이미지
설치 방법 직접 설치, 클라우드 직접 설치 직접 설치 직접 설치
기능 부가기능 있음 부가기능 있음 매우 많음 최소
관련 자료 적음 보통 많음 많음

테이너 인프라 환경 구축을 위한 쿠버네티스/도커 책 4장을 보고 정리한 내용이다.

더 자세한 내용은 다음의 책을 구입하길 바란다.

http://www.yes24.com/Product/Goods/102099414

 

컨테이너 인프라 환경 구축을 위한 쿠버네티스/도커 - YES24

실무에 바로 적용할 수 있는 컨테이너 인프라 환경 기술!IT 자원을 효율적으로 빠르게 사용할 수 있는 방법으로 컨테이너 환경이 거론되었으나 그동안 관리가 어렵고 복잡해서 상용되기 어려웠

www.yes24.com