레이블이 pod인 게시물을 표시합니다. 모든 게시물 표시
레이블이 pod인 게시물을 표시합니다. 모든 게시물 표시

prometheus "found duplicate series" error

# pod 기준으로 network 트래픽 쿼리를 다음과 같이 실행하면
avg_over_time(container_network_transmit_bytes_total{pod=~"ysoftman-.*", interface="eth0"}[1w:1m]) + on(pod) group_left avg_over_time(container_network_receive_bytes_total{pod=~"ysoftman-.*", interface="eth0"}[1w:1m])

# 특정 pod series 가 중복되어 하나로 그룹핑 되지 않아 다음과 같은 에러를 발생한다.
Error executing query: found duplicate series for the match group {pod="ysoftman-123"} on the right hand-side of the operation:

# ysoftman-123 pod 의 id 가 다르게 3개가 나와서 문제였다.
# 해당 series 는 데이터는 무의미한것으로 없어도 된다.

# 해결방법1
# prometheus 의 admin api가 활성화(--web.enable-admin-api) 되어 있다면 다음과 같이 삭제할 수 있다.
# 바로 삭제되지는 않고 다음 compaction 시 적용된다.
curl -X POST -g 'http://localhost:8090/api/v1/admin/tsdb/delete_series?match[]=container_network_transmit_bytes_total{pod=~"ysoftman-.*"}[1w]'
# 바로 삭제를 위해선 다음을 api 한번더 호출해 준다. 
curl -X POST -g 'http://localhost:8090/api/v1/admin/tsdb/clean_tombstones'

# 해결방법2
# on(pod, id) 로 pod, id 로 그룹핑되도록 한다.
avg_over_time(container_network_transmit_bytes_total{pod=~"ysoftman-.*", interface="eth0"}[1w:1m]) + on(pod, id) group_left avg_over_time(container_network_receive_bytes_total{pod=~"ysoftman-.*", interface="eth0"}[1w:1m])

ssh 로그인 후 바로 종료 문제

# k8s pod 내에 sshd -D 를 기동 후
# 다음과 같이 ssh 연결을 하면 바로 연결이 close 된다.
ssh root@localhost -vvv

... 생략 ...
Last login: Sat Apr 29 07:38:26 2023 from 127.0.0.1
debug3: receive packet: type 96
debug2: channel 0: rcvd eof
... 생략 ...
Connection to localhost closed.
debug1: Exit status 255

# receive packet : type 96(SSH_MSG_CHANNEL_EOF)으로
# 보통 연결된 ssh 에서 exit/logout 하면 받는 패킷이다.

# lastlog(/var/log/lastlog) 로 마지막 로그를 봐도 로그인 된 흔적이 있다.
Username         Port     From             Latest
root             pts/3    127.0.0.1        Sat Apr 29 07:38:28 +0000 2023
... 생략 ...

# lastb(/var/log/btmp)로 실패한 로그인 기록은 보면 없다고 나오고
# last(/var/log/wtmp) | head -2 로 성공한 로그인 기록을 보면 내역에도 기록 되어 있다.
# 그런데 로그인 후 바로 로그아웃되었다.
root     pts/3        127.0.0.1        Sat Apr 29 07:38 - 07:38  (00:00)
root     pts/3        127.0.0.1        Sat Apr 29 07:38 - 07:38  (00:00)

# 혹시 root 기본 쉘을 확인해보면 bash 정상
cat /etc/passwd | grep groot
root:x:0:0:root:/root:/bin/bash

# ssh closed 시 verbose 내용에 255 종료상태가 있는것을 보고 
# 체크1
# 로그인시 ~/.profile, ~/.bashrc 를 rename 으로 로딩을 하지 않아도 closed 된다.

# 체크2
# root/.ssh/ 에 있는 environment 파일이 다른 정상 container 와 달리 꽤 커서 살펴봤다.
# pod(container)의 파일을 로컬로 복사
kubectl cp {namespace}/{pod_name_ssh_접속정상}:/root/.ssh/environment ./environment1
kubectl cp {namespace}/{pod_name_ssh_접속문제}:/root/.ssh/environment ./environment2

# environment1 파일은 500라인인데, environment2 파일은 1000라인이다.
# 문제가 있는 container /root/.ssh/environment 의 내용을 라인을 지워가면서 접속 여부를 확인해봤다.
# 확인결과 environment 내용에 상관없이 라인수를 991로 줄이면 접속이 성공한다.
# 접속시 너무 많은 environment 설정(현재 client 와 중복도 많이 되고 있었음) 문제였음...

k8s pod log tool stern

# stern 커맨드를 사용하면 k8s 의 n 개의 pod 로그를 한번에 볼수 있다.
# ysoftman 네임스페이스의 aaa 이름(정규표현 가능) 로그 보기
stern "aaa" -n ysoftman

# 전체 네임스페이스에서 aaa 이름(정규표현 가능) 로그 보기
stern "aaa" -A

# 전체 네임스페이스에서 aaa 이름(정규표현 가능) 로그 보기
# 로그 중 json 포맷만 보기, jq 로 포맷팅해서 보면 좋다.
stern "aaa" -A -o json | jq

k8s pod ephemeral storage

# 운영중인 pod 다음과 에러로 evicted(pod 중지) 됐다.
# evicted pod event log describe
Type    Reason   .. From .... Message
Warngin evicted ... kubelet ... Pod ephemeral local storage usage exceeds the total limit of containers 5000Mi
Normal killing ... kubelet ... Stopping containers ysoftman_pod

# 원인은 서버가 pod 내 /tmp/xxx  업로드한 파일을 쓰고 있어 발생했다.

# 테스트를 위해 container 접속해서
# ysoftman_10GB 크기가 커지는것으로 모니터링 하고
watch -n 1 ls -ahl ysoftman_10GB

# container 접속 터미널을 하나 더 열고
# ysoftman_10GB 를 1GB 씩 늘려 본다.
rm -rf ysoftman_10GB
touch ysoftman_10GB
for ((i=1;i<=10;i++)); do
    echo $i
    sleep 1
    dd if=/dev/urandom bs=1000000 count=1000 >> ysoftman_10GB
done

# 참고로 한번에 쓸 경우
# dd if=/dev/urandom of=ysoftman_10GB bs=1000000 count=10000

# 이러면 1GB write 이후 pod 가 evicted 된다.
# ephemeral-storage 값을 늘리면 된다.
pod > spec > containers >  ... 
resources
  requests:
    ephemeral-storage: 10Gi
  limits:
    ephemeral-storage: 10Gi


##########


# 참고
# ephemeral-storage 리소스 종류에는
# [emptydir]
# pod 시작시 생서 pod 내 모든 container 들은 emptydir 볼륨에 동일한 파일을 읽고 쓴다.(컨테이너들간 공유)
# 로컬의 kubelet 베이스 디렉터리(보통 루트 디스크) 또는 램에서 제공
# 외 configmap 등이 있다.
# 다음과 같이 명시해 적용하면 /cache 를 emptyDir 로 사용할 수 있다.
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

# emptydir 는 pod 삭제되면 사라지는 휘발성이지만
# hostpath 를 이용하면 실제 호스트 노드에 마운트해서 pod 가 내려가도 노드에 데이터가 남는다.
# 하지만 pod 다른 호스트 노드에 뜰 수 있기 때문에 완벽한 데이터 보존 방법은 아니다.
# 그리고 hostpath 은 호스트 노드를 접근하는거라 보안 위험이 있어 되도록이면 사용하지 않는것이 좋다.
# 다음과 같이 명시해 적용하면 /test-pd 로 호스트의 /data 에 접근해 사용할 수 있다.
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

docker 로 pod 따라하기

# docker 로 pod(컨테이너들이 mount,pid,network 네임스페이스 공유)따라하기
# pod 는 다음 컨테이너들로 구성한다.
pause
nginx
ghost (nginx proxy_pass 를 받아 처리할 컨테이너, 참고:https://github.com/TryGhost/Ghost)

# pause(pod 내 컨테이너들의 부모 컨테이너로, ipc(pid), network 네임스페이스를 공유할 수 있도록) 컨테이너 생성
docker run -d --name pause -p 8080:80 --ipc="shareable" k8s.gcr.io/pause:3.2

# 생성된 pause 확인
# sandboxkey 의 network 네임스페이스 들어가서
sandboxkey=$(docker inspect pause | grep -i sandboxkey | awk '{print $2}' | tr -d ,\")
nsenter --net=$sandboxkey

# network 네임스페이스 확인(호스트의 값과 같다.)후 종료
lsns -p $$ -t net
ip a
exit

# 이제 nginx
# nginx.conf 파일 생성
# / 요청은 localhost:2368(ghost)로 프록시 패스한다.
cat > nginx.conf << zzz
error_log stderr;
events { worker_connections 1024; }
http {
  access_log /dev/stdout combined;
  server {
    listen 80 default_server;
    location / {
      proxy_pass http://127.0.0.1:2368;
    }
  }
}
zzz
# nginx 컨터이너 생성
# 위에서 만든 nginx.conf 를 볼륨 마운트하고
# pause 컨테이너의 ipc, network 를 사용(공유)
docker run -d --name nginx -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf --network=container:pause --ipc=container:pause nginx

# nginx 컨테이너에 ip 명령 설치
docker exec nginx apt update
docker exec nginx apt install -y iproute2

# nginx ip 확인
docker exec nginx ip a

# ghost 컨테이너 생성
# pause 컨테이너의 ipc, network 를 사용(공유)
docker run -d --name ghost --network=container:pause --ipc=container:pause ghost

# ghost 컨테이너에 ip 명령 설치
docker exec ghost apt update
docker exec ghost apt install -y iproute2

# ghost ip 확인
docker exec ghost ip a

# 이제 nginx 에서 / 로 접속하면 ghost 결과가 나온다.
docker exec nginx curl -I localhost

pid 네임스페이스에 관한 이슈와 해결책

[들어 가기전]
고아(orphan) process : 부모 프로세스가 자식 프로세스보다 먼저 종료되면, 자식 프로세스는 고아 프로세스가 된다. 고아 프로세스는 init(pid 1)에서 거둬 들여, 고아의 부모 프로세스는 init(pid 1)이 된다. 고아 프로세스가 종료되면 init 이 종료 상태를 파악해 좀비 프로세스가 되는것을 막는다.

좀비(zombie/defunct) process : 자식 프로세스는 종료되었지만 부모 프로세스가 wait()등으로 자식 프로세스의 종료 상태를 파악하지 못해 자식 프로세스를 거둬 드릴 수 없는 경우로 process table 에만 남아 있어 kill -9 좀비프로세스PID 로도 삭제되지 않는다.
좀비 프로세스를 삭제 방법
- kill -CHLD PPID (부모 프로세스가 CHLD 시그널을 처리할 수 있는 경우에만 가능)
- kill -KILL PPID (좀비 프로세스의 부모프로세스를 제거한다. 모든 child 가 죽기 때문에 신중해야함)

pid 네임스페이스에 관한 이슈와 해결책을 보고 정리해 봤다

!!! Caution 잘못된 번역이나 이해로 틀릴 수 있음!!! ^^;;


[linux 의 pid(process identifier)]

user space 의 root 프로세스는 init (pid 1, 이것의 부모는 pid 0 으로 커널)이다.
1 번 프로세스가 죽으면 커널이 패닉을 발생해 시스템을 재부팅해야 한다.


[linux namespace 생성]

리눅스 네임스페이스들(uts(host, domain 구분), network, mount, ipc, cgroup(cpu,memory,disk,network 등 리소스 구분), pid)는 unshare 시스템콜로 생성한다. unshare 하면 바로 새로운 네임스페이스로 들어간다.
하지만 pid 네임스페이스의 경우 unshare 로 바로 pid 네임스페이스로 들어가지 않고, fork 로 pid 네임스페이스에 pid 1(init)번를� 만든다. 이 1번 프로세스는 pid 네임스페이스 밖(호스트)에서는 다른 pid 값이다.


[네임스페이스에서의 pid 1]

1. pid 네임스페이스의 init 프로세스는 기본 signal 핸들러가 없어, HUP, INT, QUIT, TERM, KILL .. 등의 시그널 처리를 하지 못한다.(별다른 옵션 없이 docker 컨테이너를 수행하면 ctrl-c 가 동작하지 않는다. 그래서 docker kill 등을 사용한다.)

2. pid 네임스페이스의 init 외 다른 프로세스가 자식 프로세스보다 먼저 죽으면 그 자식 프로세스의 부모는 init (pid 1)이 되고, init 은 프로세스들이 exit 상태를 수집해 커널이 process table 에서 정리할 수 있도록 한다.

3. pid 네임스페이스의 init(pid 1)이 죽으면, 같은 네임스페이스의 모든 프로세스가 강제로 제거되며, pid 네임스페이스는 clean up 된다.


[docker 의 실수]

docker(runc:lightweight docker runtime)는 컨테이너의 entrypoint 를 pid 1 로 하는 새로운 pid 네임스페이스를 만든다. pid 1번에서 signal 처리가 되어 있지 않으면 ctrl-c 와 같은 signal 이 동작하지 않는다. 손자 프로세스가 종료(exit)되기 전에 죽은 자식 프로세스를 fork 하게 되면 zombie(좀비) 프로세스가 컨테이너에 쌓이게 되고, 결국 process table 을 꽉 채우게되는 상황이 발생할 수 있다.
이런 문제를 피하기 위해 fork-exec 를 할 수 있는 특별할 application process 를 실행할 수 있다. 이는 컨테이너가 복잡해지지만 실제 호스트(컨테이너가 아닌)의 init 처럼 만들 수 있다. 사람들은 종속성 격리의 이점을 희생하더라도 여러개의 apt 를 실행(dockerfile 에서 apt install a b c 컨테이너 내에서 패키지를 설치)을 포함시키는 경향이 있다.


[Rkt 해결책]

Rkt(rkt is an application container engine) 로 위 문제를 다소 해결할 수 있다. Rkt 는 사용자가 만든 시작 프로세스(container process)가 init 이 아니라고 가정하고 init (systemd) 를 생성하고 systemd 로 컨테이너에서 동작하는 filesystem 네임스페이스를 만들고 구동시킨다. systemd 는 pid 1번이 되고, (사용자가 만든) container process 는 pid 2번으로 동작한다. 만약 container process 가 init 이라면 이것도 pid 2 로 동작해 또 다른 이슈가 될 수 있다.


[간단한 대체 방법]

fork 후 pid 네임스페이스에서 container process 를 바로 실행하는 대신, spawner 가 다시 fork 한다. 이 두번째 fork 는 container spawner 가 pid 1이 되도록 하고, 모든 child 프로세스들에 signal을 전달할 수 있는 signal handler 를 구성한다. 이렇게 하면 child 프로세스가 죽을때 까지 좀비 프로세스를 거둬들일 수 있고, 이 시점에 container process 의 종료 상태를 수집해 컨테이너화 시스템에 전달한다. 이것은 ctrl-c 와 같은 signal 을 처리할 수 있고 zombie 프로세스도 적절히 거둬들 수 있다는 의미다.
docker 1.13 부터 가능하며 --init 플래그를 사용하면 된다.


[하나의 pod 내에서의 다중 컨테이너]

pod 는 네임스페이스를 공유 하는 컨테이너들의 집합이다. rkt 에서는 파일시스템 네임스페이스를 제외한 모든 네임스페이스들이 공유된다.
앞서 pid 네임스페이스 이슈로 kubernetes(k8s) 는 pod 내 컨테이너들간에 pid 네임스페이스를 공유 하지 않는다. 때문에 같은 pod 내의 container 들 끼리 signal 주고 받을 수 없고, 앞서 언급한 init 문제를 가지고 있어 container processor 는 pid 1로 실행된다.
rkt 방법을 사용하면 컨테이너 안에 init 프로세스를 만들지 않아도 통신이나 signal 을 주고받을 수 있는 컨테이너들을 쉽게 생성할 수 있다. 하지만 이미 존재하는 pod 에 컨테이너를 추가하는 상황에서는 바로 되지는 않는다.


[pod 에 컨테이너 추가하기]

k8s 는 pod sandbox 라는 개념이 있다. k8s 컨테이너 런타임은 컨테이너 시작에 앞서 리소스를 할당하는데, 이는 특히 네트워킹에 유용하고 존재하는 pod 에 컨테이너를 추가하는데도 적용할 수 있다. sandbox pod 를 먼저 생성 후 컨테이너를 하나씩 추가하는 경우, 왜 나중에 컨테이너 추가하는게 허용되지 않을까? 이는 데이터베이스 백업이나 로그 수집과 같은 주기적인 작업에 특히 유용할 것이다.
rkt(에서는 container 를 app 으로 부른다.)는 실험적으로 컨테이너와 독립적으로 pod 생성을 허용하여, 추후에라도 pod 에서 컨테이너를 추가,삭제할 수 있다. 별도 실행되는 유닛(프로세스?)없이 systemd 로 가능하게 한다. 새로운 app(container) 시작 요청이 있으면 pod 의 systemd 와 통신한다. 이러한 모델로 init 프로세스는 다음과 같은 추가적인 특권(privilege)을 가진다.
- 호스트 파일시스템 네임스페이스에 접근할 수 있어서 컨테이너 시작을 위한 파일시스템 네임스페이스를 생성할 수 있다.
- 각 app 시작에 필요한 privilege 들을 알 수 없어 전체 privilege 을 유지해야 한다.
- 컨테이너상의 모든 실행중인 프로세스를 볼 수 있다.
non-sandbox 모델이라면 init 프로세스는 child 프로세스를 시작한 다음 compromise 영향을 최소화 하기 위해 privilege 를 삭제한다.


[sandboxes 와 pid namespaces]

init, sandboxes, pid namespace 를 이용한 방법들 각각 약간의 결점(단점)이 있고, 다음 옵션을 사용할 수 있다.
1. pid namespace 가 sandbox 와 같이 생성되지 않을때, 각 컨테이너는 자신의만의 pid 네임스페이스를 가진다. k8s 에서 사용하는 방식이며, signal 처리를 위한 간단한 init 전략이다. 중요 단점으로는 pod 내 프로세스들간 signal 을 통신을 할 수 없다.
2. pid namespace 가 sandbox 와 같이 생성되지 않을때, sandbox 에서 처음으로 시작되는 컨테이너에 pid namespace 가 생성된다. 이 상태에서는 프로세스간 signal을 주고 받을 수 있다. 단점은 첫번째 프로세스가 pod 의 master 가 되고 이 master 프로세스가 죽으면 컨테이너 내 모든 프로세스는 커널에 의해 종료된다. 때문에 master 프로세스는 pod lifecycle 동안 살아 있어야 한다.
3. pid namespace 가 sandbox와 함께 생성될때, 다른 프로세스를 실행할 수 있는 smart init 프로세스를 포함한다. 이것은 rkt app sandbox 가 동작하는 방식이다. 단점으로는 init 프로세스가 너무 많은 privilege 를 가져 그 만큼 보안 공격에 취약할 수 있다.
4. pid namespace 가 sandbox와 함께 생성될때, sandbox 는 signal을 처리하고 zombie 프로세스를 회수하는 간단한 init 프로세스를 포함한다. 다른 프로세스들은 pid namespace 로 들어갈 수 있지만 부모 프로세스들은 네임스페이스 밖에 있다. init 프로세스는 새로운 컨테이너를 시작하지 않기 때문에, privilege 나 호스트 파일시스템 액세스를 유지할 필요가 없다. 단점은 네임스페이스 내의 각 프로세스들은 pid 0인 부모를 가져 일반적인 process tree 가 깨진다.
5. pid namespace 와 init 이 정확히 위 4번 옵션과 같을때, 다른 프로세스들은 pid namespace 에 들어가고 daemonize 된다. 커널은 위 4번에 pid 0으로 깨진 process tree 에서의 부모를 init 으로 돌릴 것이다. 단점은 daemonize 후 밖에 있는 새로운 프로세스를 모니터링 하기 어렵다. containerizationo system 은 간단하게 프로세스를 waiting 하는 대신 pid 를 통해 프로세스를 추적하도록 강요된다.

(필자는) 위 옵션 중 4번, 5번을 선호한다. 사실 기대하는 프로세스의 lifetime 에 따라 하나를 선택하면 된다. 위 옵션들은 프로세스를 오래 실행하기 위한 좋은 방법들이고 특히 프로세스를 spawner 로 daemonize 프로세스를 종료하는 docker 환경에 좋은 솔루션이다. 만약 간단하게 끝나는 프로세스라면 4번 옵션을 사용하고 프로세스를 pid process tree 와 분리해 유지하면 아주 쉽다. 
어떤 작업들은 k8s 가 init 처럼 동작하는 pause container를 생성하는것과 비슷하다. k8s 에 pid namespace 공유가 지원되면 5번 옵션도 가능할것이다.


[결론]

pid namespace 에는 꽤 숨겨진 복잡도가 존재한다. 오늘날 containerization 시스템은 중요한 단점과 이걸 피할 수 있는 방법들에 따라 선택한다. docker 의 싱글 컨테이너의 단점은 잘 이해되고 적당한 해결방법이 있는 반면, 컨테이너 spawner 가 init 처럼 동작하게 허용되면 컨테이너 빌더 작업이 단순화된다.
그룹 컨테이너의 경우 init 분리 방식이 rkt 가 docker 보다 우수하다. rkt 방식은 현재 k8s pod 모델에서 가능하지 않는 signal 을 통한 프로세스간 통신을 허용한다. 하지만 지연된 시작 컨테이너가 포함되기 때문에 rkt 방식도 몇가지 단점을 보인다.
지연된 시작 컨테이너에 가장 강력한 해결 방법은 pid namespace 와 함께 간단한 init 프로세스를 시작하는것이다. 하지만 새로운 컨테이너를 생성하려면 container spawner 를 통해야 한다. 이는 init 프로세스가 privileges 삭제를 허용하여, 공격을 차단한다. spawner 는 새 프로세스를 daemonize 하고 process tree 를 일관되게 유지하거나 새 프로세스의 부모로 남아 프로세스 관리를 단순화할 수 있다.


k8s sandbox OCI runtime create failed 에러

# k8s cronjob 수행으로 jobs -> pod 생성시 가끔 pod warning 이 발생한다.
# pod 내 container command(작업)는 정상적으로 수행되지만 아래와 같은 에러가 간혹 발생한다.
# 그런데 같은 클러스터에서 수행되는 다른 cronjob 은 에러가 발생하지 않는다.
# 참고로 kubernetes 버전은 1.15.5 이다.
# describe 로 pod Event 를 보면
kubectl describe pod ysoftman-pod-xxx(가칭)

... 생략 ...

Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  3m54s  default-scheduler  Successfully assigned ysoftman-xxx/ysoftman-pod-xxx to ysoftman-worker-1
  Normal  Pulling    3m51s  kubelet,ysoftman-worker-1            Pulling image "ysoftman-test:test"
  Normal  Pulled     3m51s  kubelet,ysoftman-worker-1            Successfully pulled image "ysoftman-test:test"
  Normal  Created    3m51s  kubelet,ysoftman-worker-1            Created container ysoftman-pod
  Normal  Started    3m51s  kubelet,ysoftman-worker-1            Started container ysoftman-pod
  Normal  SandboxChanged 3m50s  kubelet,ysoftman-worker-1          Pod sandbox changed, it will be killed and re-created.
  Warning  FiledCreatePodSandBox 3m50s  kubelet,ysoftman-worker-1  Failed created pod sandbox... rpc error: code = Unknown desc = failed to start sandbox container for pod "ysoftman-pod-xxx": Error response from daemon: 
  OCI runtime create failed: container_linux.go:367: starting container process caused process_linux.go:495: container init caused: Running hook #0:: error running hook: exit status 1,stdout: , stderr: time="xxx" level=fatal msg="no such file or directory": unknown
  # 또는
  OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:319: getting the final child's pid from pipe caused \"EOF\"": unknown

# 위와 같은 이슈가 moby(docker 에서 만든 컨테이너 open source)에
# https://github.com/moby/moby/issues/40835 로 등록되어 있고 아직 리졸브되지 않았다.

# sandbox는 gke(goole kubernetes engin)가 pod내에서 실행되는 컨테이너가 보안의 위협이 되지 않도록 k8s 가 생성해 관리하는 컨테이너라고 한다.
# 에러 메시지(getting the final child's pid from pipe caused \"EOF\"": unknown. )의 뜻을 보면
# OCI(Open Container Initiative)를 준수하는 runC(OCI 를 따르는 컨테이너 런타임)이 pod->container 생성시
# 어떤 이유로 pid 를 할당받지 못하는건데, moby 이슈에 제기된 원인을 보면 크게 다음과 같다.

# 1. cpu, memory 리소스 부족 -> 노드별로 cpu, mem 상태는 넉넉한것으로 보인다.
# 2. 커널 파라메터 max pid, namespace 수 늘리기 -> 요것도 아직 그리 부족해 보이진 않고, 커널 파라메터까지 수정하는 경우는 거의 없어야 할것 같다.

# 테스트1
# 1번을 보면 현재 ysoftman-pod 의 cpu, mem 사용량은 크지 않은것 같고,
# 혹시 잦은 pod 생성 에 따른 문제는 아니진 해서 테스트로 1분 간격으로 실행하는 크론잡을 돌려 봤다.
# 하루동안 돌려봤지만, interval 1분이라 빨리 재현될줄 알았는데 pod 내 sandbox 생성 에러가 없다.

# 테스트2
# pod -> container 도 잘 수행되지만, sandbox 생성시 사용자의 컨테이너를 찾을 수 없어 발생할 수 있을것 같다.
# ysoftman-pod 의 컨테이너 커맨드가 빨리 끝나서 sandbox 컨테이너 생성시 참고하는 ysoftman-pod 컨테이너가 없어
# no such file or directory 또는 다른 에러(pid EOF 로 찾을 수 없다등)이 발생하는 것 같아
# 다음과 같이 1초내로 빨리 종료되는 크론잡 컨테이너를 설정했다.

... 생략 ...

containers:
- name: ysoftman-cronjob-test
  image: busybox
  imagePullPolicy: Always
  command:
  - /bin/sh
  - -c
  - date; echo "wait for 0s"; i=1; while [ $i -le 1 ]; do echo $i; sleep 1; i=$((i+1)); done; echo "job completed\!"
  resources:

# 위와 같이 설장하고 1분마다 크론잡이 실행되면, 100%는 아니지만 에러가 재현됐다.
# 실제 ysoftman-pod 는 사용자 업데이트를 가져오는 잡으로 대부분 업데이트 내용이 없어서 1~2초내로 바로 종료되는 컨테이너였다.

# 해결방법
# ysoftman-pod container 에 sleep 10초 주어 sandbox 생성시 사용자 컨테이너를 참조할 수 있는 시간을 주니 더이상 발생하지 않았다.