도커 컨테이너를 실행할 때 리소스에 제한을 걸 수 있다.

 

예를 들면, redis 컨테이너의 메모리 사용을 제한해보도록 하자.

sudo docker run --name my-redis -d -p 6379:6379 --memory="1g" --memory-swap="1g" --restart=always redis

 

--memory : The maximum amount of memory the container can use. If you set this option, the minimum allowed value is 4m (4 megabyte).

-- memory-swap : The amount of memory this container is allowed to swap to disk

 

--memory-swap details

--memory-swap is a modifier flag that only has meaning if --memory is also set. Using swap allows the container to write excess memory requirements to disk when the container has exhausted all the RAM that is available to it. There is a performance penalty for applications that swap memory to disk often.

Its setting can have complicated effects:

  • If --memory-swap is set to a positive integer, then both --memory and --memory-swap must be set. --memory-swap represents the total amount of memory and swap that can be used, and --memory controls the amount used by non-swap memory. So if --memory="300m" and --memory-swap="1g", the container can use 300m of memory and 700m (1g - 300m) swap.
  • If --memory-swap is set to 0, the setting is ignored, and the value is treated as unset.
  • If --memory-swap is set to the same value as --memory, and --memory is set to a positive integer, the container does not have access to swap. See Prevent a container from using swap.
  • If --memory-swap is unset, and --memory is set, the container can use as much swap as the --memory setting, if the host container has swap memory configured. For instance, if --memory="300m" and --memory-swap is not set, the container can use 600m in total of memory and swap.
  • If --memory-swap is explicitly set to -1, the container is allowed to use unlimited swap, up to the amount available on the host system.
  • Inside the container, tools like free report the host’s available swap, not what’s available inside the container. Don’t rely on the output of free or similar tools to determine whether swap is present.

 

어떻게 컨테이너 마다 독립된 환경으로 리소스를 제한할 수 있는 것일까?

 

먼저 도커 구조를 살펴보고 이해하는데 도움을 얻자.

도커 구조

https://betterprogramming.pub/docker-for-front-end-developers-c758a44e622f

- 도커는 containerd를 활용하여 컨테이너를 생성, 관리하는 서비스로 docker.sock을 통해 통신한다.

- 도커 서비스는 docker cli를 docker.sock을 통해 받아서 containerd로 요청을 보내주는 역할을 한다.

 

아래 명령어를 통해 --containerd를 명시하고 docker.sock을 통해서 API를 리스닝 하는 것을 살펴볼 수 있다.

$ sudo service docker status
● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2021-07-26 07:43:57 UTC; 1 months 10 days ago
     Docs: https://docs.docker.com
 Main PID: 1314 (dockerd)
    Tasks: 89
   CGroup: /system.slice/docker.service
           ├─ 1314 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
$ journalctl -xu docker.service
-- Logs begin at Mon 2021-07-26 05:40:51 UTC, end at Sun 2021-09-05 06:52:46 UTC. --
Jul 26 06:32:36 xxx systemd[1]: Starting Docker Application Container Engine...
-- Subject: Unit docker.service has begun start-up
-- Defined-By: systemd
-- Support: http://www.ubuntu.com/support
-- 
-- Unit docker.service has begun starting up.
...
Jul 26 06:32:37 xx dockerd[3815]: time="2021-07-26T06:32:37.322270147Z" level=info msg="API listen on /var/run/docker.sock"
Jul 26 06:32:37 xx systemd[1]: Started Docker Application Container Engine.

 

결론적으로 containerd는 docker로 부터 요청을 받아서 runc를 통해 컨테이너(프로세스)를 생성하는 구조가 된다.

$ sudo service containerd status
● containerd.service - containerd container runtime
   Loaded: loaded (/lib/systemd/system/containerd.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2021-07-26 07:43:55 UTC; 1 months 10 days ago
     Docs: https://containerd.io
 Main PID: 1153 (containerd)
    Tasks: 115
   CGroup: /system.slice/containerd.service

하지만 containerd가 여러가지 이유로 재시작될 수 있는데 직접 runc와 소통을 하게되면 runc 프로세스까지 모두 재시작 될 수 있기 때문에 shim을 중간 매개체로 넣어서 관리하게된다. 즉, shim은 컨테이너 runtime과 containerd 사이에서 소통을 담당하고, runtime이 독립적으로 돌 수 있도록 한다.

 

실습을 위해 메모리 제한을 주고 redis container 실행해보자

sudo docker run -it --name test-redis -d -p 6399:6399 --memory="1g" --memory-swap="1g" --restart=always redis /bin/bash

이미지는 redis를 사용하였고, init 커맨드로 /bin/bash를 사용했다. 즉, 해당 컨테이너 내부로 들어가면 /bin/bash가 init 프로세스가 되는 것이다. 

 

생성 프로세스(/bin/bash)의 부모 프로세스가 containerd-shim(25228)임을 확인할 수 있다.

$ ps -ef | grep container
root     25220  1314  0 06:40 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 6399 -container-ip 172.17.0.5 -container-port 6399
root     25228  1153  0 06:40 ?        00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424 -address /run/containerd/containerd.sock -c


$ ps -ef | grep /bin/bash
root     25254 25228  0 06:40 pts/0    00:00:00 /bin/bash
...

 


도커는 독립된 환경과 리소스 제한을 위해 namespace와 cgroup을 활용한다.

cgroup은 가용할 수 있는 자원의 제한을 설정하는 것이고, namespace는 볼 수 있는 자원의 범위를 의미한다.

 

Namespace

리눅스에서는 프로세스별로 독립적인 공간을 제공하고 서로가 충돌하지 않도록 하는 기능을 제공한다. 

mnt 홈스트 파일시스템에 구애받지 않고 독립적으로 파일시스템을 마운트하거나 언마운트 가능
pid 독립적인 프로세스 공간을 할당
net namespace간에 네트워크 충돌방지
ipc 프로세스간의 독립적인 통신통로 할당
uts 독립적인 hostname 할당
user 독립적인 사용자 할당

 

컨테이너는 네임스페이스가 분리되어 있는 독립적인 프로세스이고, 실제 아래 명령어로 확인가능하다.

$ sudo ls -al /proc/25254/ns
total 0
dr-x--x--x 2 root root 0 Sep  5 06:40 .
dr-xr-xr-x 9 root root 0 Sep  5 06:40 ..
lrwxrwxrwx 1 root root 0 Sep  5 07:17 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Sep  5 07:17 ipc -> 'ipc:[4026532883]'
lrwxrwxrwx 1 root root 0 Sep  5 07:17 mnt -> 'mnt:[4026532881]'
lrwxrwxrwx 1 root root 0 Sep  5 06:40 net -> 'net:[4026532886]'
lrwxrwxrwx 1 root root 0 Sep  5 07:17 pid -> 'pid:[4026532884]'
lrwxrwxrwx 1 root root 0 Sep  5 07:17 pid_for_children -> 'pid:[4026532884]'
lrwxrwxrwx 1 root root 0 Sep  5 07:17 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Sep  5 07:17 uts -> 'uts:[4026532882]'

 

Cgroup

cgroup은 자원에 대한 제어를 가능하게 해주는 리눅스 커널의 기능이다. 다음의 리소스를 제어할 수 있다.

- memory

- cpu

- I/O

- network

- device 노드

 

컨테이너를 띄우면 각각 고유한 컨테이너 ID를 부여받는다. runc는 이 ID를 기반으로 cgroup내의 디렉토리를 구성한다.

(/sys/fs/cgroup/memory/docker/container ID)

 

$ sudo cat /proc/25254/cgroup 
12:rdma:/
11:memory:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
10:hugetlb:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
9:perf_event:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
8:cpu,cpuacct:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
7:devices:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
6:pids:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
5:freezer:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
4:cpuset:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
3:net_cls,net_prio:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
2:blkio:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
1:name=systemd:/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
0::/system.slice/containerd.service

 

이제 redis에 메모리 제한을 주어 cgroup내에 디렉토리가 어떻게 구성되는지 살펴보자.

- memory: 1G

- memory-swap: 0

$ sudo docker run -it --name test-redis -d -p 6399:6399 --memory="1g" --memory-swappiness 0 --restart=always redis /bin/bash
bf5560cc4fc5975594016f028ca07ec435202cb276ca2f7f2f95f4f13346d5c1
$ cat /sys/fs/cgroup/memory/docker/bf5560cc4fc5975594016f028ca07ec435202cb276ca2f7f2f95f4f13346d5c1/memory.limit_in_bytes 
1073741824

$ cat /sys/fs/cgroup/memory/docker/bf5560cc4fc5975594016f028ca07ec435202cb276ca2f7f2f95f4f13346d5c1/memory.swappiness 
0

이번에는 

- memory : 100M

- memory-swap: 10

$ sudo docker run -it --name test-redis -d -p 6399:6399 --memory="200M" --memory-swappiness 10 --restart=always redis /bin/bash
2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424
$ cat /sys/fs/cgroup/memory/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424/memory.limit_in_bytes 
209715200
$ cat /sys/fs/cgroup/memory/docker/2035a84b0f9540774fa05c0e8c920c398fe9ddeefd1e54389dceb36fd9e90424/memory.swappiness 
10

 

docker -> (docker.sock) -> containerd -> (shim) -> runc을 통해 cgroup을 만들고 값을 넣어주어서 메모리 제한이 적용되는 것을 확인했다.

 

 

ps aux | grep docker 명령으로 확인해보면

- docker

- docker-containerd

- docker-containred-shim

- docker-runc

 

4개의 프로세스가 돌아가는 것을 확인할 수 있다.


참고

# 커널 swap 지원 불가

WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.

# /etc/default/grub
...
...GRUB_CMDLINE_LINUX_DEFAULT="cgroup_enable=memory swapaccount=1"
# update-grub 
# reboot

 

 

 


출처

- https://tech.ssut.me/what-even-is-a-container/

- https://betterprogramming.pub/docker-for-front-end-developers-c758a44e622f

- https://tech.kakao.com/2020/06/29/cgroup-driver/

- https://brunch.co.kr/@jehovah/35

- https://docs.docker.com/config/containers/resource_constraints/#--memory-swap-details

'클라우드 컴퓨팅 > 쿠버네티스' 카테고리의 다른 글

CKA(Certified Kubernetes Administrator) 시험 비중  (0) 2021.09.11
Kubernetes TroubleShooting  (0) 2021.09.11
docker 기본  (0) 2021.09.05
Kubernetes Pod Scheduling  (0) 2021.08.13
Kubernetes Workloads  (0) 2021.08.08

도커(docker)는 컨테이너를 실행하기 위한 실행 환경(컨테이너 런타임) 및 툴킷이다. 쿠버네티스는 도커 이외의 컨테이너 런타임도 지원하도록 개발되어있다. 

 

쿠버네티스 사용에 앞서 도커 이미지를 빌드하고, 이미지 보관/배포 서버인 도커 레지스트리로 푸시하는 방법을 알아 두어야 한다. 또한, 반드시 알아두면 좋은 지식을 간단히 알아볼 것이다.

 

도커 컨테이너

도커 컨테이너는 도커 이미지를 기반으로 실행되는 프로세스다. 도커 이미지만 있다면 환경의 영향을 받지 않고 다양한 환경에서 컨테이너를 기동시킬 수 있기 때문에 이식성이 높다. 

도커 컨테이너는 가상 머신에 비해 '가볍다', '시작과 중지가 빠르다' 등과 같은 장점이 있다.

그 이유는 가상 머신은 하이퍼바이저를 이용하여 게스트 OS를 동작시키지만, 도커 컨테이너는 호스트 머신의 커널을 이용하면서 네임스페이스 분리와 cgroups를 이용한 제어를 통해 독립적인 OS와 같은 환경을 만들 수 있기 때문이다. 

 

https://www.docker.com/blog/containers-replacing-virtual-machines/

 

도커 컨테이너 설계

도커 컨테이너를 생성할 때 주로 주의해야할 점은 아래와 같다.

- 1 컨테이너당 1 프로세스

여러 프로세스를 기동하도록 만들면 주변 에코시스템과 맞지 않거나 관리가 힘들어진다.

- 변경 불가능한 인프라 이미지로 생성

컨테이너 이미지안에 애플리케이션 실행 바이너리나 관련 리소스를 가능한 한 포함시켜 컨테이너 이미지를 변경불가능한 상태로 만든다.

- 경량의 도커 이미지로 생성

경량 배포판으로는 알파인 리눅스(Alpine Linux)나 Distroless가 있다.

- 실행 계정은 root 이외의 사용자로 한다.

 

도커 파일 작성법

# Alpine 3.11 버전 golang 1.14.1 이미지를 기반 이미지로 사용
From golang:1.14.1-alpine3.11

# 빌드할 머신에 있는 main.go 파일을 컨테이너에 복사
COPY ./main.go ./

# 빌드 시 컨테이너 내부에서 명령어 실행
RUN go build -o ./go-app ./main.go

# 실행 계정을 nobody로 지정
USER nobody

# 컨테이너가 기동할 때 실행할 명령어 정의
ENTRYPOINT ["./go-app"]

도커 파일에서 사용할 수 있는 명령

명령 요약
FROM 기반 이미지를 지정
MAINTAINER 컨테이너 이미지 관리자 정보를 기입(현재는 비추천, 아래 LABEL 명령을 사용함)
LABEL 컨테이너 이미지의 메타데이터를 키:밸류 형식으로 지정(예: LABEL maintainer="XX<xxx@xxx.com>")
USER 명령어 실행 계정 지정
WORKDIR 명령어를 실행할 작업 디렉터리 지정(디렉터리가 없을 경우 생성)
EXPOSE 컨테이너 실행 시 Listen할 포트 지정
COPY 로컬에 있는 파일을 컨테이너로 복사
ADD 로컬에 있는 tar.gz 파일의 압축을 풀고 파일을 컨테이너로 복사
RUN 컨테이너 안에서 명령을 실행
ENTRYPOINT 컨테이너 기동 시에 실행할 명령어
CMD 컨테이너 기동 시에 실행할 명령어 인수

 

ENTRYPOINT와 CMD의 관계

ENTRYPOINT CMD 실행되는 명령어
"/bin/ls" "-a" /bin/ls -a
지정안함 /bin/ls /bin/ls
"/bin/sh", "-c" "ls -a" /bin/sh -c "ls -a"

ex) docker run ubuntu-sleeper 10 -> 10초간 sleep

FROM ubuntu
ENTRYPOINT ["sleep"]


ex) docker run --entrypoint sleep ubuntu-sleeper 10

--entrypoint : it will override entrypoint

FROM ubuntu
ENTRYPOINT ["sleep"]
CMD["5"] #파라미터가 없을 때 default 값을 지정하고자 할 경우, 파라미터 전달시 overrided 됨.

도커 실행시  Environment 전달

docker run -e APP_COLOR=pink simple-webapp-color

 

도커 이미지 빌드 및 확인

docker image build -t sample-image:0.1 (-f DockerFile) .

docker images

 

멀티 스테이지 빌드

기동용 컨테이너에 불필요한 소프트웨어를 설치하지 않는 것은 보안 측면에서도 좋을 뿐만아니라 이미지 사이즈를 줄일 수 있다. 이를 위해 멀티 스테이즈 빌드를 활용하자.

# Stage 1 컨테이너(애플리케이션 컴파일)
FROM golang:1.14.1-alpine3.11 as builder
COPY ./main.go ./
RUN go build -o /go-app ./main.go

# Stage 2 컨테이너(컴파일한 바이너리를 포함한 실행용 컨테이너 생성)
FROM alpine:3.11
# Stage 1에서 컴파일한 결과물 복사
COPY --from=builder /go-app .
ENTRYPOINT ["./go-app"]
# Stage 1 컨테이너(애플리케이션 컴파일)
FROM golang:1.14.1-alpine3.11 as builder
COPY ./main.go ./
RUN CGO_ENABLED=0 go build -o /go-app ./main.go

# Stage 2 컨테이너(컴파일한 바이너리를 포함한 실행용 컨테이너 생성)
FROM scratch
# Stage 1에서 컴파일한 결과물 복사
COPY --from=builder /go-app .
ENTRYPOINT ["./go-app"]

예제는 2단계 구성이지만 3단계 이상의 구성도 가능하다!

 

alpine 이미지 대신 scratch 이미지를 기반으로 사용하면 이미지 사이즈를 더 줄일 수 있는 것을 확인할 수 있다.

(base) jds@jdsui-MacBookPro chapter01 % docker images

REPOSITORY                           TAG                                                     IMAGE ID       CREATED          SIZE

smaple-image                         0.3                                                     a96b26cc1aa5   16 seconds ago   7.41MB

smaple-image                         0.2                                                     72e55cd3def7   46 seconds ago   13.1MB

 

도커 허브에 이미지 푸시

docker login

option 1) docker image build -t DOCKERHUB_USER/sample-image:0.x .

option 2)docker image tag sample-image:0.1 DOCKERHUB_USER/sample-image:0.x

docker image push DOCKERHUB_USER/sample-image:0.x

docker logout

 

컨테이너 기동

docker container run -d -p 1234:8080 sample-image:0.1

(base) jds@jdsui-MacBookPro chapter01 % docker container run -d -p 1234:8080 smaple-image:0.2
7edb8b08e3c4f6385ec814b9157063bbd0fea3da8baed6b61e8c7ee85afee769

(base) jds@jdsui-MacBookPro chapter01 % curl http://localhost:1234
Hello, Kubernetes% 

 


Docker Storage

Docker Storage drivers

도커 엔진은 OS에 따라서 적절한 storage driver를 선택한다.

- AUFS, ZFS, BTRFS, Device Mapper, Overlay, Overlay2

 

 

Docker volume drivers

- local, Azure File Storage, Convoy, AWS EBS, ...

$ docker run -it \
      --name mysql
      --volume-driver rexray/ebs
      --mount src=ebs-vol,target=/var/lib/mysql
      mysql

 

 

도커 파일 시스템

/var/lib/docker# ls
builder   containers  network   plugins   swarm  trust
buildkit  image       overlay2  runtimes  tmp    volumes

 

Docker Layer

- Image Layers

$ docker build -t test/my-app:0.0.1

- Container Layers

$ docker run test/my-app

출처 : https://docs.docker.com/storage/storagedriver/

Docker Volumes & Usage

- volume mount : 볼륨을 생성하지 않은 상태에서 도커 run 명령을 사용해서 볼륨지정을 했다면 자동생성된다.

$ docker volume create data_volume
$ docker run -v data_volume:/var/lib/mysql mysql

- bind mount : any location on the docker host

$ docker run -v ./current-dir:/var/lib/mysql mysql

출처 : https://docs.docker.com/storage/tmpfs/

참고) Container Interface

- CRI(Container Runtime Interface)

- CNI(Container Network Interface)

- CSI(Container Storage Interface)

 

 

다음 포스팅에서 도커 namespace와 cgroup에 대해서 살펴보도록 하자.
https://espossible.tistory.com/19

 


출처

- https://www.docker.com/blog/containers-replacing-virtual-machines/

- [책]쿠버네티스완벽가이드 마사야 아오야마 지음, 박상욱 옮김(길벗)

'클라우드 컴퓨팅 > 쿠버네티스' 카테고리의 다른 글

Kubernetes TroubleShooting  (0) 2021.09.11
docker 리소스 제한  (0) 2021.09.05
Kubernetes Pod Scheduling  (0) 2021.08.13
Kubernetes Workloads  (0) 2021.08.08
Kubernetes JSONPATH 사용법  (0) 2021.08.08

쿠버네티스에서 Pod 생성을 요청 했을 때, 적당한 node에 배치하는 것을 스케쥴링이라고 한다. 이때 쿠버네티스 스케줄러는 아래 내용을 살펴보고 Pod가 배포될 수 있는 Node를 선정하는 작업을 하게된다.

 

- 충분한 리소스가 있는가?

- 디스크 볼륨을 사용할 경우 node에서 접근이 가능한가?

- HA 구성을 위해 다른 node, 다른 rack, 다른 zone에 배포 가능한가?

- 기타..

 

Resource

충분한 리소스가 있는지 없는지 판단하기 위해서는 기준이 있어야한다. 컨테이너에 대해서 리소스 정의를 할 수 있고, 이 정의된 리소스를 기준으로 Node에 충분한 리소스가 있는지 판단할 수 있는 것이다.

# Requests, limits

If the node where a Pod is running has enough of a resource available, it's possible (and allowed) for a container to use more resource than its request for that resource specifies. However, a container is not allowed to use more than its resource limit.

For example, if you set a memory request of 256 MiB for a container, and that container is in a Pod scheduled to a Node with 8GiB of memory and no other Pods, then the container can try to use more RAM.

If you set a memory limit of 4GiB for that Container, the kubelet (and container runtime) enforce the limit. The runtime prevents the container from using more than the configured resource limit. For example: when a process in the container tries to consume more than the allowed amount of memory, the system kernel terminates the process that attempted the allocation, with an out of memory (OOM) error.

Limits can be implemented either reactively (the system intervenes once it sees a violation) or by enforcement (the system prevents the container from ever exceeding the limit). Different runtimes can have different ways to implement the same restrictions.

Note: If a Container specifies its own memory limit, but does not specify a memory request, Kubernetes automatically assigns a memory request that matches the limit. Similarly, if a Container specifies its own CPU limit, but does not specify a CPU request, Kubernetes automatically assigns a CPU request that matches the limit.
# Pod resource
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
        - containerPort: 8080
      resources:
        requests:
          memory: "1Gi"
          cpu: 1
        limits:
          memory: "2Gi"
          cpu: 2

# Resource Quota

A resource quota, defined by a ResourceQuota object, provides constraints that limit aggregate resource consumption per namespace. It can limit the quantity of objects that can be created in a namespace by type, as well as the total amount of compute resources that may be consumed by resources in that namespace.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-quota
spec:
  hard:
    pods: "10"
    requests.cpu: "4"
    requests.memory: 5Gi
    limits.cpu: "10"
    limits.memory: 
  scopeSelector:
      matchExpressions:
      - operator : In
        scopeName: PriorityClass
        values: ["high"]
apiVersion: v1
kind: Pod
metadata:
  name: high-priority
spec:
  containers:
  - name: high-priority
    image: ubuntu
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo hello; sleep 10;done"]
    resources:
      requests:
        memory: "10Gi"
        cpu: "500m"
      limits:
        memory: "10Gi"
        cpu: "500m"
  priorityClassName: high
Resource Name Description
pods The total number of Pods in a non-terminal state that can exist in the namespace. A pod is in a terminal state if .status.phase in (Failed, Succeeded) is true.
limits.cpu Across all pods in a non-terminal state, the sum of CPU limits cannot exceed this value.
limits.memory Across all pods in a non-terminal state, the sum of memory limits cannot exceed this value.
requests.cpu Across all pods in a non-terminal state, the sum of CPU requests cannot exceed this value.
requests.memory Across all pods in a non-terminal state, the sum of memory requests cannot exceed this value.
cpu Same as requests.cpu
memory Same as requests.memory

- kubectl describe quota

Name:       pods-high
Namespace:  default
Resource    Used  Hard
--------    ----  ----
cpu         0     4
memory      0     5Gi
pods        0     10

 

# Limit Range

By default, containers run with unbounded compute resources on a Kubernetes cluster. With resource quotas, cluster administrators can restrict resource consumption and creation on a namespace basis. Within a namespace, a Pod or Container can consume as much CPU and memory as defined by the namespace's resource quota. There is a concern that one Pod or Container could monopolize all available resources. A LimitRange is a policy to constrain resource allocations (to Pods or Containers) in a namespace.

A LimitRange provides constraints that can:

  • Enforce minimum and maximum compute resources usage per Pod or Container in a namespace.
  • Enforce minimum and maximum storage request per PersistentVolumeClaim in a namespace.
  • Enforce a ratio between request and limit for a resource in a namespace.
  • Set default request/limit for compute resources in a namespace and automatically inject them to Containers at runtime.
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container

고급 스케줄링 정책

Pod를 배포할 때, 특정 Node를 선택할 수 있도록 정의할 수 있다. 예를 들어 Redis master와 slave들이 같은 Node에 배포되지 않도록 Pod의 스케줄링 정책을 인위적으로 조정할 수 있다. 즉, taint/toleration과 affinity with NodeSelector의 조합으로 특정 노드에 특정 포드가 스케줄링 되도록 할 수 있다.

# Taint/Toleration

출처 : https://livebook.manning.com/#!/book/kubernetes-in-action/chapter-16/section-16-3-1

Taints

Taint는 Node에 정의할 수 있고, Toleration은 Pod에 정의할 수 있다.

- Taints & Tolerations does not tell the part to go to a particular node.
- It tells the node to only accept parts with certain toleration

 

Taint는 특정 Node에 일반적인 pod가 배포되는 것을 막을 수 있다. 즉, 알맞은 toleration을 가지고 있는 Pod만 배포될 수 있다.

Taint Effect

Taint에 적용할 수 있는 effect는 아래와 같이 3가지가 있다.

  • NoSchedule : if there is at least one un-ignored taint with effect NoSchedule then Kubernetes will not schedule the pod onto that node(기존에 노드에 머물러 있던 Pod는 그대로 stay!)
  • PreferNoSchedule : if there is no un-ignored taint with effect NoSchedule but there is at least one un-ignored taint with effect PreferNoSchedule then Kubernetes will try to not schedule the pod onto the node. the system will try to avoid placing a pod that does not tolerate the taint on the node, but it is not required. 
  • NoExecute : if there is at least one un-ignored taint with effect NoExecute then the pod will be evicted from the node (if it is already running on the node), and will not be scheduled onto the node (if it is not yet running on the node). a toleration with NoExecute effect can specify an optional tolerationSeconds field that dictates how long the pod will stay bound to the node after the taint is added. pods that tolerate the taint without specifying tolerationSeconds in their toleration specification remain bound forever(돌고 있던 Pod도 evit하고, 다른 node로 옮긴다.)

NoExecute > NoSchedule > PreferNoSchedule 순으로 Pod에 영향을 많이 준다.

# Taint on Nodes
kubectl taint nodes node01 spray=mortein:NoSchedule
kubectl describe nodes node-1 | grep -i taint

# taint 제거
kubectl taint node master/controlplane node-role.kubernetes.io/master:NoSchedule-

kubectl explain pod --recursive | grep -A5 tolerations

# NoSchedule / PreferNoSchedule / NoExecute(killed)

Toleration

Taint 처리가 되어있는 Node에 Pod를 배포하기 위해서는 Toleration을 사용해야한다. 일종의 입장권이라고 생각하면 편할 것 같다. 다만 주의해야될 점은 배포되는 것이아니라 배포될 수 있다라는 점이다. 즉, 다른 조건에 맞는 Node가 있다면 해당 Node에 배포될 수도있다.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
        - containerPort: 8080
  nodeSelector:
    size: Large # node labeling first!

  tolerations:
    - key: app
      operator: "Equal" / "Exists"
      value: "blue"
      effect: "NoSchedule"

 

 

# Affinity

Taint가 Pod가 배포되지 못하도록 하는 정책이라면, affinity는 Pod가 특정 노드로 배포되도록 하는 기능이다. affinity는 Node를 기준으로 하는 Node affinity와 다른 Pod가 배포된 노드를 기준으로 하는 Pod affinity가 있다.

Node Affinity

Node affinity는 Hard affinity와 soft affinity가 있다.

- Hard affinity는 조건이 딱 맞는 node에만 pod를 배포하는 기능을 하고(node selector와 같은 기능을 한다.) 예를들면, node selector

- Soft affinity는 Pod가 조건에 맞는 node에 되도록이면 배포되도록 하는 기능이다 

 

Node Affinity Types

Node Affinity에 적용할 수 있는 effect는 아래와 같이 2가지가 있다.

- requiredDuringSchedulingIgnoredDuringExecution(hard) : similar to nodeSelector but using a more expressive syntax. If labels on a node change at runtime such that the affinity rules on a pod are no longer met, the pod continues to run on the node.

- preferredDuringSchedulingIgnoredDuringExecution(soft), not guaranteed

 

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
        - containerPort: 8080
  # schedule a Pod using required node affinity
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: size
                operator: In #NotIn, Exists
                values:
                  - Large

  tolerations:
    - key: app
      operator: "Equal"
      value: "blue"
      effect: "NoSchedule"

Pod affinity

Pod Affinity Types

Pod Affinity에 적용할 수 있는 effect는 아래와 같이 2가지가 있다.

- requiredDuringSchedulingIgnoredDuringExecution(hard) : similar to nodeSelector but using a more expressive syntax. If labels on a node change at runtime such that the affinity rules on a pod are no longer met, the pod continues to run on the node.

- preferredDuringSchedulingIgnoredDuringExecution(soft), not guaranteed

 

예시는 아래와 같다.

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

 

클러스터 구성을 할때 1개의 노드에 모든 파드가 배포되었다고 가정해보자. 노드가 갑자기 이상상황이 발생하여 정상 작동하지 않는다면 더이상 서비스를 제공할 수 없을 것이다. 이런 경우에 Pod anti-affinity를 활용할 수 있다.

 

예를 들어 Redis Cluster 구성한다고 가정해보자. 쿠버네티스 노드가 6개 이상이고, Redis cluster에 필요한 Pod도 6개라고 할때 노드마다 1개의 Redis Pod가 배치된다면 HA 구성을 할 수 있다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
  labels:
    app: redis-cluster
spec:
  selector:
    matchLabels:
      app: redis-cluster
  serviceName: redis-cluster
  replicas: 6
  template:
    metadata:
      labels:
        app: redis-cluster
      annotations:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
                - key: "app"
                  operator: In
                  values:
                  - redis-cluster
            topologyKey: "kubernetes.io/hostname"
            
      ...

 


Multiple Scheduler

# Scheduler

static pod로 배포된다. 따라서 포드명은 kube-scheduler-{node_name} 다음과 같은 형식이다.

apiVersion: v1
kind: Pod
metadata:
   name: kube-scheduler
   namespace: kube-system
spec:
   containers:
   - command:
      - kube-scheduler
      - --address=127.0.0.1
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
     image: k8s.gcr.io/kube-scheduler-amd64:v1.11.3
     name: kube-scheduler

# Custom Scheduler 

--learder-elect=false를 이용하여 기존 leader를 유지

--lock-object-name=my-custom-scheduler을 이용하여 새로운 스케줄러가 leader eletion 중에 default가 되는 것을 막음

apiVersion: v1
kind: Pod
metadata:
   name: my-custom-scheduler
   namespace: kube-system
spec:
   containers:
   - command:
      - kube-scheduler
      - --address=127.0.0.1
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=false (for single master node)
      - --scheduler-name=my-custom-scheduler
      - --lock-object-name=my-custom-scheduler (for multiple master nodes)
     image: k8s.gcr.io/kube-scheduler-amd64:v1.11.3
     name: kube-scheduler
apiVersion: v1
kind: Pod
metadata:
   name: nginx
spec:
  containers:
  -  image: nginx
     name: nginx
  schedulerName: my-custom-scheduler

 

 

참고

- container resource default : 1 cpu, 512 Mi

1 cpu
- 1 AWS vCPU
- 1 GCP core
- 1 Azure core
- 1 Hyperthread

1 Gi(Gibibyte) like 1 G
1 Mi(Mebibyte) like 1 M
1 Ki(Kibibyte) like 1 K

 

- The status 'OOMKilled' indicates that the pod ran out of memory.

- debugging

kubectl get events

kubectl logs my-custom-scheduler -n=kube-system

kubectl pod kube-scheduler-master -n kube-system | grep Image

cd /etc/kubernetes/manifests
# 노드 테인트 정보가져오기
kubectl describe node kubemaster | grep -i taint

# 노드 라벨링(Node selector와 연동!)
kubectl label nodes node01 size=Large
kubectl label nodes node01 color=blue

# 노드 with label
kubectl get nodes node01 --show-labels
apiVersion: v1
kind: Binding
metadata:
  name: nginx
target:
  apiVersion: v1
  kind: Node
  name: node02

 


출처

- https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/

- https://thenewstack.io/implementing-advanced-scheduling-techniques-with-kubernetes/

- https://bcho.tistory.com/1344?category=731548

- https://bcho.tistory.com/1346?category=731548

'클라우드 컴퓨팅 > 쿠버네티스' 카테고리의 다른 글

docker 리소스 제한  (0) 2021.09.05
docker 기본  (0) 2021.09.05
Kubernetes Workloads  (0) 2021.08.08
Kubernetes JSONPATH 사용법  (0) 2021.08.08
Kubernetes Network Model & Policy  (0) 2021.07.08

쿠버네티스는 가장 기본적인 구성단위가 되는 오브젝트와, 이 기본 오브젝트를 생성하고 관리하는 추가적인 기능을 가진 컨트롤러(Controller)로 이루어진다. 컨트롤러는 다양한 형태의 워크로드를 지원하고, 다양한 시나리오에 맞게 Pod를 운영할 수 있도록 지원한다.

 

기본 오브젝트 

쿠버네티스에 의해서 배포 및 관리되는 가장 기본적인 오브젝트는 컨테이너화되어 배포되는 애플리케이션의 워크로드를 기술하는 오브젝트로 Pod, Service, Volume, Namespace 4가지가 있다.

 

# Pod

- Pod는 쿠버네티스에서 가장 기본적은 배포 단위로, 하나 이상의 컨테이너를 포함하는 단위이다. Pod 안의 컨테이너들은 IP와 Port를 공유하고, 디스크 볼륨을 공유하는 특징을 갖는다.

- 실행중인 포드의 환경설정, service account, resource limit을 수정할 수 없다.

- Pod는 재시작하면 기록이 모두 지워지기 때문에 --restart=Never 로 실행한다.

 

참고) 애플리케이션 배포시에 항상 함께 사용하는 프로그램을 같이 배포할 수 있는데 이런 경우에 Pod가 유용하게 활용될 수 있다.

 

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
        - containerPort: 8080
# 포드 생성
kubectl run nginx --image=nginx --labels=tier=db --port=8080 (container port)
kubectl run httpd --image=httpd:alpine --port 80 --expose --dry-run-client -o yaml (cluster ip)

# 포드 이미지
kubectl describe pod pod_name | grep -i image

# 포드의 노드 이름
kubectl get pods -o wide --show-labels
kubectl get pods --selector app=App1
kubectl get pods -l env=dev,bu=finance,tier=frontend --no-headers | wc -l
kubectl get all 

# 특정 포드 조회
kubectl get pods webapp

# 포드 삭제
kubectl delete pod webapp

# 포드 YAML 및 실행
kubectl run redis --image=redis --dry-run=client -o yaml > pod.yaml
kubectl run mosquito --image nginx --restart=Never
kubectl apply -f pod.yaml

# 포드 Config 수정
kubectl edit pod redis

kubectl explain pod --recursive | less

 

# Service & Service Type

- 지정된 IP로 생성 가능(Pod의 경우에 지정되는 IP가 랜덤하게 지정이 되고, 리스타트 때마다 변함)

- 여러 Pod에 같은 애플리케이션을 운용할 경우 이 Pod 간의 LB를 지원

- 고유한 DNS 이름을 가질 수 있음

ClusterIP

Exposes the Service on a cluster-internal IP. Choosing this value makes the Service only reachable from within the cluster. This is the default ServiceType.

apiVersion: v1
kind: Service
metadata:
  name: back-end
spec:
  type: ClusterIP
  ports:
    - protocol: TCP
    - targetPort: 80 #default(same as port)
      port: 80
  selector: #labels
    app: myapp
    type: back-end
# Create a Service named redis-service of type ClusterIP to expose pod redis on port 6379
kubectl expose pod redis --name redis-service --port=6379 --target-port 6379
kubectl expose pod redis --port=6379 --name redis-service --dry-run=client -o yaml
kubectl create service clusterip redis --tcp=6379:6379 --dry-run=client -o yaml

LoadBalancer

Exposes the Service externally using a cloud provider's load balancer. NodePort and ClusterIP Services, to which the external load balancer routes, are automatically created.

 

apiVersion: v1
kind: Service
metadata:
  name: back-end
spec:
  type: LoadBalancer
  ports:
    - targetPort: 80 #default(same as port)
      port: 80
      nodePort: 30008 #range(30000 ~ 32767)
  selector: #labels
    app: myapp
    type: back-end

 

참고) AWS EKS lb

apiVersion: v1
kind: Service
metadata:
  name: web-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: xxx
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"
  namespace: default
spec:
  ports:
  - name: http
    port: 80
    targetPort: 80
  - name: https
    port: 443
    targetPort: 80
  selector:
    run: web-app
  type: LoadBalancer

 

NodePort

Exposes the Service on each Node's IP at a static port (the NodePort). A ClusterIP Service, to which the NodePort Service routes, is automatically created. You'll be able to contact the NodePort Service, from outside the cluster, by requesting :<NodeIP>:<NodePort>

apiVersion: v1
kind: Service
metadata:
  name: hello-node-svc
spec:
  selector:
    app: hello-node
  type: NodePort
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 8080
      nodePort: 30036

출처 : ttps://bcho.tistory.com/1262

# service 생성
kubectl expose deploy simple-webapp-deployment --name=webapp-service --target-port=8080 --type=NodePort --port=8080 --dry-run=client -o yaml > test.yaml

# Create a Service named nginx of type NodePort to expose pod nginx port 80 on port 30080 on the nodes
kubectl expose pod nginx --port=80 --name nginx-service --type=NodePort --dry-run=client -o yaml (recommended)
kubectl create service nodeport nginx --tcp=80:80 --node-port=30080 --dry-run=client -o yaml

# Namespace

네임스페이스는 쿠버네티스 클러스터내의 논리적인 분리단위라고 할 수 있다.

- 네임스페이스별로 리소스를 나눠서 관리할 수 있다.(Pod, Service)

- 사용자별로 네임스페이스별 접근 권한을 다르게 운영할 수 있다.

- 네임스페이스별로 리소스의 할당량을 지정할 수 있다.

 

네임스페이스는 논리적인 분리이기 때문에 분리 정책을 적용하지 않는 이상 네임스페이스간의 통신이 가능하다.

apiVersion: v1
kind: Namespace
metadata:
  name: dev
# namespace 생성
kubectl create namespace dev

# default namespace 변경
kubectl config set-context $(kubectl config current-context) --namespace=dev

# namespace 카운트
k get ns --no-headers | wc -l

# namespace 포드 생성
k run redis -n xxx --image=redis --dry-run=client -o yaml > test.yaml

# pod namespace 조회
k get pods --all-namespaces | grep blue

# fqdn(fully qualified domain name)
serviceName.ns.svc.cluster.local

 

컨트롤러

# Replicaset(Previous name : Replication Controller)

- High Availability

- Load Balancing & Scaling

- Self-healing

 

Kubernetes supports self-healing applications through ReplicaSets and Replication Controllers. The replication controller helps in ensuring that a POD is re-created automatically when the application within the POD crashes. It helps in ensuring enough replicas of the application are running at all times.

 

Kubernetes provides additional support to check the health of applications running within PODs and take necessary actions through Liveness and Readiness Probes.

 

apiVersion: v1
kind: ReplicationController
metadata:
  name: myapp-rc
  labels:
    app: myapp
    type: front-end
  annotations:
    buildversion: 1.34
spec: Pod 영역
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: simple-webapp
  labels:
    app: myapp1
    type: front-end
spec:
  replicas: 3
  selector:
    matchLabels: #important!
      app: myapp
  template:
    metadata:
      labels:
        app: myapp #important!
        type: front-end
    spec:
      containers:
        - name: simple-webapp
          image: simple-webapp
# replicaset의 개수
kubectl get replicasets.apps

# replicaset 이미지
kubectl describe replicasets.apps new-replica-set

# replicaset 삭제
kubectl delete replicasets.apps replicaset-1

# replicaset 수정
kubectl edit replicasets.apps replicaset-1

# replicaset replica 수정
kubectl scale replicaset --replicas=5 new-replica-set

 

# Deployment Controller

Deployment is an object which can own ReplicaSets and update them and their Pods via declarative, server-side rolling updates. While ReplicaSets can be used independently, today they're mainly used by Deployments as a mechanism to orchestrate Pod creation, deletion and updates. When you use Deployments you don't have to worry about managing the ReplicaSets that they create. Deployments own and manage their ReplicaSets. As such, it is recommended to use Deployments when you want ReplicaSets.

 

With Deployments you can easily edit any field/property of the POD template.

# deploy의 개수
kubectl get deployments.apps

# deploy 이미지
kubectl describe deployments.apps new-deploy | grep -i image

kubectl create deployment httpd-frontend --image=xxxx

kubectl scale deploy httpd-frontend --replicas=3 

kubectl get deployments.apps httpd-frontend

kubectl create deploy --image=nginx nginx --dry-run=client -o yaml > test.ayml

kubectl create deploy --image=nginx nginx --replicas=4

 

참고) rolling updates

롤링 업데이트 방식은 Pod를 하나씩 업그레이드 해가는 방식이다.

출처 : https://bcho.tistory.com/1256

롤링 업데이트 실패시 수동으로 롤백해주어야한다.

# you can rollback to a specific revision by specifying it with --to-revision
kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2

 

고급 컨트롤러

# DaemonSets

출처 : https://bcho.tistory.com/1257

Pod가 각각의 노드에서 하나씩만 돌게 하는 형태로 Pod를 관리하는 컨트롤러. 모니터링 용도(예를들면 fluentd로 많이 사용된다.)로 많이 쓰임.

- kube-proxy

- network

DaemonSets은 특정 노드에만 Pod를 배포할 수 있도록, node selector를 이용해서 특정 노드만을 선택할 수 있게 지원한다.

 

apiVersion: apps/v1
kind: DaemonSet
metadata:
	name: monitoring-daemon
spec:
	selector:
    	matchLabels:
        	app: monitoring-agent
    template:
    	metadata:
        	labels:
            	app: monitoring-agent
        spec:
        	containers:
            - name: monitoring-agent
              image: monitoring-agent
kubectl get ds --all-namespaces
kubectl -n kube-system describe ds weave-net | grep -i image
# DaemonSet 생성시 deploy로 생성하고, 필요없는 부분 삭제
# Deploy -> DaemonSet 변경 / replicas, strategy 제거
kubectl create deploy elasticsearch --image=xxx --dry-run -o yaml > elastic.yaml

 

# StatefulSet

데이터베이스와 같이 상태를 가지는 애플리케이션을 관리하기 위한 컨트롤러.

 

기존 컨트롤러는 아래와 같은 문제점이 있음.

- Pod의 이름이 불규칙적이다.

마스터/슬레이브 구조를 가지는 데이터베이스 등에서 마스터 서버의 이름을 특정 이름으로 지정할 수가 없다.

- Stateless Pod들은 기동이 될때 병렬로 동시에 기동된다.

데이터베이스의 경우에는 마스터 노드가 기동된 다음 스레이브 노드가 순차적으로 기동되어야하는 순차성을 가지고 있는 경우가 있음.

- 볼륨 마운트

맨 처음 생성된 Pod가 PVC와 PV에 연결이 되기 때문에 뒤에 생성되는 Pod들은 PVC를 얻지 못해서 디스크를 사용할 수 없음.

출처 : https://bcho.tistory.com/1306

Statefulset은 앞서 설명한 stateless 애플리케이션이 관리하는 컨트롤러로 할 수 없는 기능을 제공한다. 대표적인 기능들은 아래와 같다.

- Pod 이름에 대한 규칙성을 부여

생성된 Pod들은 {Pod name}-{num} 식으로 이름이 정해진다. 예를들면 cassandra-0, cassandra-1 … 가 된다.

- 배포시 순차적인 기동과 업데이트

동시에 모든 Pod를 생성하지 않고, 0, 1, 2 … 순서대로 하나씩 Pod를 생성한다. 이러한 순차기동은 데이터베이스에서 마스터 노드가 기동된 후에, 슬레이브 노드가 기동되어야 하는 조건등에 유용하게 사용될 수 있다.

- 개별 Pod에 대한 디스크 볼륨 관리

Pod 마다 각각 PVC와 PV를 생성하여 관리할 수 있도록한다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: cassandra
  labels:
    app: cassandra
spec:
  serviceName: cassandra
  replicas: 3
  selector:
    matchLabels:
      app: cassandra
  template:
    metadata:
      labels:
        app: cassandra
    spec:
      terminationGracePeriodSeconds: 1800
      containers:
      - name: cassandra
        image: gcr.io/google-samples/cassandra:v13
        imagePullPolicy: Always
        ports:
        - containerPort: 7000
          name: intra-node
        - containerPort: 7001
          name: tls-intra-node
        - containerPort: 7199
          name: jmx
        - containerPort: 9042
          name: cql
        resources:
          limits:
            cpu: "500m"
            memory: 1Gi
          requests:
            cpu: "500m"
            memory: 1Gi
        securityContext:
          capabilities:
            add:
              - IPC_LOCK
        lifecycle:
          preStop:
            exec:
              command: 
              - /bin/sh
              - -c
              - nodetool drain
        env:
          - name: MAX_HEAP_SIZE
            value: 512M
          - name: HEAP_NEWSIZE
            value: 100M
          - name: CASSANDRA_SEEDS
            value: "cassandra-0.cassandra.default.svc.cluster.local"
          - name: CASSANDRA_CLUSTER_NAME
            value: "K8Demo"
          - name: CASSANDRA_DC
            value: "DC1-K8Demo"
          - name: CASSANDRA_RACK
            value: "Rack1-K8Demo"
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
        readinessProbe:
          exec:
            command:
            - /bin/bash
            - -c
            - /ready-probe.sh
          initialDelaySeconds: 15
          timeoutSeconds: 5
        # These volume mounts are persistent. They are like inline claims,
        # but not exactly because the names need to match exactly one of
        # the stateful pod volumes.
        volumeMounts:
        - name: cassandra-data
          mountPath: /cassandra_data
  # These are converted to volume claims by the controller
  # and mounted at the paths mentioned above.
  # do not use these in production until ssd GCEPersistentDisk or other ssd pd
  volumeClaimTemplates:
  - metadata:
      name: cassandra-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: fast
      resources:
        requests:
          storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
  type: pd-ssd

 

이때, PVC의 이름은 {StatefulSet}-{Pod name}으로 생성된다.

 

Static Pods

- kube-apiserver와 kube-sceduler 등 클러스터에서 지원하는 기능(control plane) 없이 Pod를 관리하고 싶은 경우

- 노드명이 suffix로 붙음(kubectl get pods --all-namespaces | grep "\-master")

- manifests 폴더에 있는 yaml 파일들이 static pod 정의(해당 위치에있는 yaml 파일들은 자동으로 만들어짐)

Static pods DaemonSets
Created by the Kubelet Created by Kube-API server(DaemonSet Controller)
Deploy Control plane components as static pods deploy monitoring agents, logging agents on nodes
lgnored by the kube-scheduler

- how to kill delete static pod

root@controlplane:~# kubectl get pods --all-namespaces -o wide  | grep static-greenbox
default       static-greenbox-node01                 1/1     Running   0          19s     10.244.1.2   node01       <none>           <none>
root@controlplane:~#

root@controlplane:~# ssh node01(172.17.0.41) 

root@node01:~# ps -ef |  grep /usr/bin/kubelet   (ps -ef | grep kubelet | grep "\--config")
root       752   654  0 00:30 pts/0    00:00:00 grep --color=auto /usr/bin/kubelet
root     28567     1  0 00:22 ?        00:00:11 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2
root@node01:~# grep -i staticpod /var/lib/kubelet/config.yaml
staticPodPath: /etc/just-to-mess-with-you
root@node01:~# 

root@node01:/etc/just-to-mess-with-you# ls
greenbox.yaml
root@node01:/etc/just-to-mess-with-you# rm -rf greenbox.yaml 
root@node01:/etc/just-to-mess-with-you#

# Exit out of node01
root@controlplane:~# kubectl get pods --all-namespaces -o wide  | grep static-greenbox
root@controlplane:~#

참고) static pod 실행시 --restart=Never를 잊지말자.

 

 

다음 포스팅에서는 쿠버네티스 스케줄러에 대해서 살펴보도록 하겠습니다.

 


출처

- https://kubernetes.io/docs/tutorials/stateful-application/cassandra/

- https://bcho.tistory.com/1306

- https://bcho.tistory.com/1262?category=731548

- https://www.weave.works/blog/optimizing-cluster-resources-for-kubernetes-team-development

- https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/

- https://bcho.tistory.com/1257?category=731548

- https://v1-18.docs.kubernetes.io/docs/concepts/overview/components/

- https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/

 

'클라우드 컴퓨팅 > 쿠버네티스' 카테고리의 다른 글

docker 기본  (0) 2021.09.05
Kubernetes Pod Scheduling  (0) 2021.08.13
Kubernetes JSONPATH 사용법  (0) 2021.08.08
Kubernetes Network Model & Policy  (0) 2021.07.08
Kubeadm Swap Disable  (0) 2021.07.04

+ Recent posts