k8s 로 인프라를 구축하여도 일반적으로 docker hub 에서 이미지를 가져와 사용하기 때문에 k8s 내부적으로 docker 가 컨테이너를 관리하는 것으로 오해할 수 있다. 실제로 kubernetes 1.24 버전 미만에서는 k8s 가 내부적으로 dockershim -> dockerd( daemon)을 통해 컨테이너를 실행/관리하였다. 하지만 dockerd 는 인프라 환경에서 필요 없는 GUI, CLI, 빌드 기능 등이 포함되어있기 때문에 비효율적이었고, 따라서 k8s 는 1.24 버전부터 컨테이너 런타임으로 containerd 을 사용하게 되었다.

 

1. k8s 에서 컨테이너를 구동하는 주체

현재 k8s 는 CRI(Container Runtime Interface)라는 표준 규격을 만족하는 가벼운 컨테이너 런타임을 사용한다. 가장 대표적인 것이 containerd 와 CRI-O 인데, 대부분의 클라우드 서비스나 최신 클러스터는 containerd 를 기본으로 한다.

 

2. contauner registry 에 올라가는 이미지 규격

docker hub 에 올리던, 사내 nexus registry 나 harbor 에 올리던간에 그 이미지는 docker 로 빌드했더라도 docker 전용 규격이 아니라, 표준 규격인 OCI(Open Container Initiative) 규격을 따르는 이미지이다. 따라서 docker 데몬뿐 아니라 containerd 같은 다른 컨테이너 런타임도 이미지를 컨테이너화 할 수 있는 것이다.

 

3. k8s 에서의 컨테이너 실행흐름

k8s 에서 파드를 띄울 때 실제 일어나는 일은 다음과 같다.

  1. kubelet 의 요청: 해당 노드의 관리자인 kubelet 이 CRI 규격(gRPC)을 통해 containerd(고수준 런타임)에게 컨테이너 실행을 요청한다.
  2. 이미지 다운로드: containerd 가 container image registry 에서 OCI 규격의 이미지를 pull 한다.
  3. 실제 컨테이너 생성: containerd 가 직접 컨테이너를 실행하지 않고, 저수준 런타임인 runc 에게 컨테이너 실행을 요청한다. runc 는 리눅스 커널과 직접 소통한다.
  4. 리눅스 커널 격리: runc 가 리눅스 커널의 namespace, cgroups 기능을 활용해 격리된 프로세스로 컨테이너를 구동시킨다.

 

4. docker 로 컨테이너 구동시 dockerd 가 죽으면 어떻게 될까?

dockerd 가 죽으면 실행중이던 컨테이너들도 종료된다. 이는 dockerd 가 컨테이너 프로세스의 직속 부모 프로세스이기 때문이다.(Live Restore 라는 기능을 사용하면 dockerd 가 죽어도 컨테이너를 유지할 수 있긴 하다)

[ systemd ]
   │
   └─ [ dockerd (Docker 데몬) ] ── (부모-자식 관계) ──> [ 컨테이너 프로세스 ]

 

 

 

5. k8s 에서 컨테이너 구동시 containerd 가 죽으면 어떻게 될까?

containerd 가 죽어도 실행중이던 컨테이너들은 종료되지 않는다. 이는 containerd 가 컨테이너 프로세스의 직속 부모가 아니기 때문이다. containerd 가 컨테이너 생성을 요청받으면 containerd-shim 이라는 프로세스를 컨테이너 갯수만큼 생성하고, 각각의 프로세스 하위로 컨테이너 프로세스를 두게 된다. containerd 자신은 부모가 아닌 관리자 프로세스로 남게 된다.

[ systemd ]
   │
   ├─ [ containerd (데몬) ]  ── (독립된 관계)
   │
   ├─ [ containerd-shim ] ── (실질적 부모) ──> [ 컨테이너 프로세스 ]
   └─ [ containerd-shim ] ── (실질적 부모) ──> [ 컨테이너 프로세스 ]

 

단, containerd가 죽었을 때 일어나는 일은 아래와 같다.

  • 상태 모니터링 불가: kubelet 이 containerd 를 통해 파드의 상태를 확인(Health Check)할 수 없으므로, 쿠버네티스 마스터 노드 입장에서는 해당 노드의 파드들이 일시적으로 Unknown 상태로 보일 수 있다.
  • 새로운 작업 불가: 새로운 파드를 띄우거나, 기존 파드를 삭제하는 등의 제어 명령이 동작하지 않는다.
블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,
  1. 컨테이너 런타임이 사용하는 리눅스 커널 기술
    결국 컨테이너 런타임은 이 기술을 사용하여 컨테이너를 관리한다.
    • chroot: 디렉토리 격리
    • cgroup: 리소스(CPU, memory) 격리
    • namespace: 프로세스, 네트워크 격리
  2. 대표적인 container runtime 종류
    • High Level
      • docker: mirantis 가 소유.
      • container-d: docker 에서 컨테이너 생성 기능만 분리. CNCF에 기부됨
      • cri-o: RedHat 이 만듦. CNCF에 기부됨
    • Low Level
      • LXC: 리눅스 컨테이너 기술
      • rkt: 컨테이너 전용 OS인 리눅스 CoreOS(=Fedora CoreOS) 가 런타임으로 사용됨
      • runC: cri-o, container-d 가 호출하는 low level 컨테이너
      • libcontainer: runC가 호출하는 low level 컨테이너(docker -> container-d -> runC -> libcontainer 순서로 호출됨)
  3. k8s 의 kublet 컴포넌트와 컨테이너 런타임의 관계
    • kube-apiserver: k8s 를 작동하는 모든 커맨드를 받는 게이트워이 서버 역할. pod 생성과 같이 컨테이너 관련된 작업이 요구되면 kubelet 을 호출함.
    • kubelet: 노드 내 파드를 관리. 컨테이너 런타임을 호출하는 주체.
      • 1.27 미만 버전: 사용하는 컨테이너 런타임이 무엇인지에 따라 맞춤형으로 API 호출함. CRI 인터페이스를 준수하는 컨테이너 런타임은 모두 사용가능. 단, 컨테이너 런타임 호출 구현부 소스코드도 k8s 에 있고, 컨테이너 런타임 개발사들이 기고하는 형식이였음
      • 1.27 이상 버전: 컨테이너 런타임 호출 구현부를 k8s 에서 제거하고, 각 런타임 개발사가 플러그인 형태로 제공함
  4. kubelet 의 컨테이너 런타임 호출  CRI 구현부(1.27 미만 버전)
    • docker: dockershim
    • container-d: CRI-Containerd
    • rkt: rktshim
  5. container runtime 별 kubelet 용 endpoint 플러그인(1.27 이상 버전)
    • docker: cri-dockerd
    • container-d: CRI-Plugin
    • cri-o: 애초에 kubelet 이 바로 호출 가능함
  6. OCI(Open Container Initiative)
    컨테이너 런타임의 컨테이너 표준 규약을 정의하는 단체
    따라서, 표준 컨테이너(OCI 규약에 맞는)라면, 다른 컨테이너 런타임에서도 호환된다.
블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

docker pull 로 이미지를 docker hub 에서 받아올 때 아래와 같이 에러가 나며 실패하는 경우가 있다.

$ docker pull centos:6
Error response from daemon: no matching manifest for linux/arm64/v8 in the manifest list entries: no match for platform in manifest: not found

 

해당 태그(6)의 이미지가 없기 때문인데, 사용 가능한 태그 목록을 조회하기 위해 docker hub 웹사이트에 접속한다.

docker hub 웹사이트에서 원하는 이미지를 찾아 들어가면 Tag summary 에서 인기있는 주요 태그들을 볼 수있다.(권장하는 태그들)

 

그리고, Tag 탭을 통해 들어가면 docker hub 에 push 된 모든 태그를 볼 수 있다. 하지만 페이지네이션 때문에 한번에 모든 태그를 볼 수가 없다.

 

(Tag summary 에 노출되는 기준은 Tags 에서 확인할 수 있는 태그 목록의 newest 내림차순 10개인 것 같다.)

 

웹사이트에 방문하지 않고 다음 커맨드로도 조회할 수 있다.

$ curl -s "https://registry.hub.docker.com/v2/repositories/library/centos/tags/" | jq '.results[].name'

 

하지만, page_size 파라미터 기본값이 10이고, 최대값이 100 이기 때문에, nginx 나 ubuntu 와 같이 태그가 엄청나게 많은 경우에는 최대 100개까지밖에 조회가 안된다.(docker 의 정책이다) 따라서, 모든 태그를 조회하기 위해 아래 스크립트를 실행하면 된다. 각 fetch 의 응답필드 중 next 에 명시된 url 을 추가 호출하는 방식이다.

 

fetch-docker-hub-all-tags.sh

#!/bin/bash

# 인자 확인
if [ -z "$1" ]; then
  echo "사용법: $0 <repository-name>"
  echo "예시: $0 ubuntu"
  exit 1
fi

REPO="$1"
URL="https://registry.hub.docker.com/v2/repositories/library/${REPO}/tags?page_size=100"

while [ "$URL" != "null" ]; do
  RESP=$(curl -s "$URL")
  echo "$RESP" | jq -r '.results[].name'
  URL=$(echo "$RESP" | jq -r '.next')
done

 

이를 다음과 같이 이미지명을 파라미터로 주입하여 실행하면 모든 태그를 조회할 수 있다.

$ sh fetch-docker-hub-all-tags.sh centos
$ sh fetch-docker-hub-all-tags.sh ubuntu
$ sh fetch-docker-hub-all-tags.sh nginx

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

다른 블로그 글들을 보면 host 의 timezone 파일을 도커 컨테이너에 연결해서 timezone 을 일치시키는 방법을 많이 쓴다.

하지만 윈도우에서 어떻게 하는지 몰라 그냥 쉽고 편한 방식인 tzdata 를 이용한 방법을 썼다.

 

우분투 컨테이너에서 아래 명령어를 차례로 실행

 

apt update

apt install tzdata

dpkg-reconfigure tzdata

위와 같이 대륙?과 도시를 선택하면 된다.

로컬타임과 유니버셜타임이 각각 설정되며,

date 명령어로 확인하면 KST 타임존이 적용된 것을 볼 수 있다.

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

- search 키워드

: 설치가능한 이미지 검색

 

- pull 이미지명

: 이미지 다운로드

 

- images [이미지명]

: 다운로드 받은 이미지 목록

 

- create [옵션] --name 컨테이너명 이미지명

: 이미지를 컨테이너로 만들기

: 옵션은 보통 -i -t 를 주며, -i는 상호입출력, -t는 tty를 사용하여 bash 쉘로 입출력

 

- ps [옵션]

: 현재 컨테이너 목록보기

: 옵션을 안주면 실행중인 컨테이너만 보이며, -a 옵션을 주면 모든 컨테이너를 보여줌

 

- start 컨테이너명

: 컨테이너를 실행(이미 create 로 만들어져 있어야 함)

 

- attach 컨테이너명

: 컨테이너에 연결(이미 start 로 실행되어 있어야 함)

 

- run [옵션] --name 컨테이너명 이미지명 이미지실행파일경로

: 생성(create) + 실행(start) + 연결(attach)까지 한번에 하는 명령어

 

- stop 컨테이너명

: 컨테이너 종료

 

 

* 컨테이너 종료

exit

* 컨테이너 실행상태에서 빠져나오기(백그라운드 실행)

ctrl + p, ctrl + q

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,