요약


 

RandomForest 의 Feature importance은 여러가지가 있을 수 있지만 크게는 대표적으로 1) 불순도 기반 방식(Impurity based): tree을 나눌 때, 쓰이는 특징값으로 나눴을 때, 클레스가 잘 나뉘는 가 얼마나 변화하는지 측정하는 방식. 불순도의 변화가 크면 클수록 트리를 잘 나눴다는 것을 의미중요한 특징 값이라는 것, 2) 순열 기반 방식(Permutation based): 각 특징값을 데이터 포인터에 대해서 랜덤하게 순서를 바꿔서, 정확도가 떨어지는지 확인하는 방식. 정확도가 크게 떨어질 수록 중요한 특징값임을 가정하는 방식이 있다.

 

선행


랜덤포레스트는 이 포스팅에서 깊게 다루지는 않지만, 트리구조를 만들 때, 배깅(bagging) 중복을 포함해서 임의로 데이터를 N개씩 뽑고, Feature bagging (속성을 뽑을 때도 랜덤하게 K)만큼 뽑아서 각각의 트리를 만든다. 그렇게 만들어진 약한 분류기(Tree)을 여러개 만들고 분류모델이면 Voting하여 얻은 값을 반환한다.

https://medium.com/analytics-vidhya/random-forest-classifier-and-its-hyperparameters-8467bec755f6

 

 

Gini importance / Mean Decrease in Impurity (MDI)


이 방식은 Gini importance라고도 불리고, MDI라고도 불리는 방식이다. 의미하는 것은 모든 랜덤포레스트의 각각의 트리에서 해당 특징값으로 트리를 나눌 때, 감소한 불순도의 평균을 의미한다(For each feature we can collect how on average it decreases the impurity. The average over all trees in the forest is the measure of the feature importance[1]).

Gini impurity은 아래와같이 계산한다 (주의: Gini coefficient와 다른 내용이다).

$Gini impurity = 1 - Gini$

$Gini = p_{1}^{2}+p_{2}^{2}+...+p_{n}^{2}$

n은 데이터의 각 클레스를 의미한다. 가령 아래와 같이 주황색 데이터와, 파란색데이터를 구분하는 문제라면 n은 2가된다. 노드를 나누기전을 부모노드라고하면, 부모노드에서의 Gini은 파란색공이 5개중에 3개, 노란색이 5개중에 2개이므로 $Gini=(3/5)^{2}+(2/5)^{2}$가 된다. 따라서, Gini impurity은 $Gini impurity=1-(3/5)^{2}+(2/5)^{2}$이다. 그리고 나눈 후를보면, 좌측은 $Gini imputiry = 1- ((2/3)^{2}+(1/3)^{2}$. 우측은 $Gini imputiry = 1-((1/2)^{2}+(1/2)^{2})$ 이다. Gini impurity의 변화량이 얼마나 변화했는지를 파악하는 것이기 때문에, 다음과 같이 부모의 impurity - (자식노드의 imputiry의합)과 같이 계산한다.

$1-(3/5)^{2}+(2/5)^{2} - [(1- ((2/3)^{2}+(1/3)^{2}) - 1-((1/2)^{2}+(1/2)^{2})]$

위의 계산에서 중요한 것을 빠뜨렸는데 특징값 A에 대해서 위와 같은 과정을 진행했다고하면, 모든 노드 중에서 A을 이용한 분기에를 모든 트리에서 찾은다음에 평균낸 값이 Gini importance이다. 혹은, 어떤 방식에서는 나누려는 노드의 가중치를 곱해서 많이 나눴던 노드에서는 더 가중치를 주기도 한다.

Gini importance 예시

 

아래의 그림을 보면 feature importances (MDI)라는 것이 표시되어있다. 이는 위의 과정(Gini importance)을 이용해서 각 노드를 나눴을때 불순도가 크게 변화하는 정도의 평균을 계산했다는 의미이다. (이 데이터셋은 타이타닛 데이터셋). 생존자와 사망자를 구분할 때, sex을 이용해서 구분했을 때, sex을 안썼을 때보다 평균적으로 더 트리구조가 깔끔하게 나뉘어져나갔다는 것을 의미한다. 

sklearn에서의 feature importance 예시: https://scikit-learn.org/stable/auto_examples/inspection/plot_permutation_importance.html

 

[1] https://mljar.com/blog/feature-importance-in-random-forest/#:~:text=Random%20Forest%20Built%2Din%20Feature%20Importance&text=It%20is%20a%20set%20of,sets%20with%20similars%20responses%20within.

 

Permutational importance / Mean Decrease in Accuracy (MDA)


Permutation importance은 트리를 만들때 사용되지 않았던 데이터들 OOB(Out of bagging, 랜덤포레스트의 각 트리를 만들 때, 랜덤으로 N개의 데이터를 중복을 포함하여 뽑는데, 그 때 사용되지 않았던 데이터들)을 이용하여, OOB인 데이터들에 대해서 해당 특징값을 랜덤하게 섞은후에 예측력을 비교한다.

아래와 같이 a,b,c,d,e,의 특징값을 가진 1,2,3,4,5번 데이터가 있다고하자. 모델을 빌딩할 때, 랜덤으로 뽑은 데이터가 1,3,5였고, 안타깝게 2,4번은 중복을 포함혀어 뽑았지만 뽑히지 않았던 데이터라고 하자. 

OOB인 데이터포인트 2,4가 있고, a,b,c,d,e 특징값이 있는 데이터의 예시

이 때, 특징값 a에 대해서 1,2,3,4,5번의 a특징값의 순서를 랜덤하게 바꿔버리고 원래 데이터셋으로 만든 모델과 성능을 비교한다. 성능차이가 크면 클수록 랜덤으로 돌리지 말았어야할 중요한 별수라는 것이다. 또는 2번과 4번의 데이터 또는 바꿔서 차를 계산해도 이론상 큰 차이는 안날 것이다. 이렇게 특징값a에 대해서, 순서변경 전후의 성능을 비교하고, b,c,d,e,f에 대해서도 비교하면 permutation feature importance가 계산된다.

 

아래와 같이 permuation importance을 계산하면 성별이 나온다. boxplot으로 나온 이유는 random forest의 각 tree에서의 틀린정도의 분포를 그렸기 때문이다. 해석은 다음과 같다. 테스트 세트에서 생존여부를 맞추는 정도는 sex을 랜덤하게 바꿀 경우 가장 많이 성능이 떨어졌다는 것을 의미한다. 각 트리별로 고려했을 때, 평균적으로 0.25정도 성능이 떨어졌음을 의미한다.반대로 random_num, age등은 어떤 tree에서는 빼나 안빼나 구별력차이가 없었던 것을 의미하기도 한다 (box plot에 0에 맞닿아있다 (이는 랜덤으로 뽑은 데이터셋이 운이 안좋게 구별을 못한 경우일 수도 어서 0이 나왔을 수도있다.

https://scikit-learn.org/stable/auto_examples/inspection/plot_permutation_importance.html

반응형

쿠버네티스에서 kubectl get pods 등 여러 명령어를 치더라도 아래와 같이 에러를 반환하는 경우가 있다. 

The connection to the server localhost:8080 was refused - did you specify the right host or port 

 

원인: 마스터노드에서의 kubectl 관련 config가 설정이 되지 않았기 때문


쿠버네티스 초기화 내 config가 설정이 안되어있는 경우로 생각된다. 실제로 master 노드에서의 config을 확인해보면 아래와 같다.

 

 

해결방법1: (마스터노드만) kubeadm을 이용한 마스터노드의 초기화


쿠버네티스 kubeadm을 설치가 안되어있다면 설치가 필요하고, 설치했음에도 에러가 떴다면 초기화 해주는 과정이 필요하다.

설치를 한적이 없으면, 여기 URL 에서 kubeadm을 설치하는 과정을 따라하여 설치한다. 아래의 리눅스 운영체제 또는 패키지관리도구를 이용해서 설치할 것인지 말것인지 결정해서 1~4번까지 복붙하여 따라한다. 설치되어있으면 kubeadm을 초기화한다.

 

아래의 명령어로 쿠버네티스 마스터노드의 kubeadm을 초기화해준다.

sudo kubeadm init // 처음 설치한 경우
sudo kubeadm reset // 이미 설치한 경우

 

위의 명령어로도 쿠버네티스 마스터노드가 초기화 안되고 에러가 뜨는 경우가 발생했는데, 필자의 경우는 도커의 설치가 되지않아 초기화되지 않았다. 마찬가지로 도커설치도 진행한다. docker.io라고 할지 어떤 종류를 할지는 sudo apt-get install docker을 한번 실행해보면, 가이드를 해주는 설명이 출력된다.

sudo apt-get install docker.io

 

그리고나서 다시 kubeadm으로 마스터노드를 초기화 한다. 그러면, 아래와 같이 정살정으로 kubectl 관련 서비스들이 생성된다.

그림. 마스터노드에서의 쿠버네티스 초기화 작업. 자세히보면 마스터노드에서 필요한 서비스들이 생성된다. 예를 들어 etcd와 같은 클라이언트 노드들의 상태를 보관하는 저장소들도 하나씩 생성해나간다.

 

관련 리소스들이 제대로 설치가 되었는지 아래와 같이 namespace을 검색해본다.

# kubectl get pods --all-namespaces

 

해결방법2: WorkerNode인경우


마스터노드와의 Join이 안되는 경우에 이런 문제가 발생 수 있다. 마스터노드에서 다음의 명령어로 해당 노드가 제대로 참여중인지 확인한다. 아래의 명령어로 control-plane과 해당 worker노드가 확인되어야한다. 확인되지 않으면, 워커노드가 클러스터로 join이 안된것이다.

# kubectl get nodes

 

워커노드가 클러스터에 조인이 안되었음을 확인했다면, 일단 워커노드를 초기화한다.

# kubeadm reset

그리고 마스터노드에 join에 필요한 hash, cluster API및 포트, token정보를 받아온다.

// 마스터노드에서
root@k8s-master:/home/ubuntu# kubeadm token create --print-join-command
kubeadm join 172.16.2.231:6443 --token 8yxfot.ktr4irill5hqw6h7 --discovery-token-ca-cert-hash sha256:bd91d60981b5774add11106cd42e1f8d6db18b241b5fd529b0d638e71

다시 워커노드에서 위의 명령어를 복붙하여 조인한다.

root@k8s-test:/home/ubuntu# kubeadm join 172.16.2.231:6443 --token 8yxfot.ktr4irill5hqw6h7 --discovery-token-ca-cert-hash sha256:bd91d60981b5774add11106cd42e1f8d6db18b241b5fd529b0d638e713522494
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

 

반응형

쿠버네티스의 컨트롤러는 특정 Pod의 수를 보장해주는 역할을 하는 자원을 의미한다. 아래의 쿠버네티스의 마스터노드(컨트롤플레인)가 어플리케이션을 각 노드에서 실행하고, 이를 각 etcd에서 제대로 수행되고 있는지 모니터링하다가 부족한 Pod은 더 실행해주고, 많은 Pod은 종료시켜주는 역할을 한다.

그림. 쿠버네티스 동작원리

 

쿠버네티스의 컨트롤러의 종류


쿠버네티스 컨트롤러는 Replication controller, replicaset, deployment, daemonset등 여러가지가 있어서 헷갈린다. 이 자원들은 모두 다 컨트롤로에 속한다. 다만 각각의 자원들이 쿠버네티스가 발전해나감으로서 생긴 디테일의 차이(대동소이)한 것들이 있어서 몇가지 대표적으로 필요한 것들만 알아보면 좋다. 사용목적에 따른 대표적인 것들은 아래와 같다.

  • 오랜시간 켜두어야할 어플리케이션이 파드로 존재하는 경우: Replication contoller, Replicaset, Deploment
  • 클러스터에 속한 전체 노드에 같은 파드를 실행해야하는 경우: Daemonset
  • 일회성 작업을 해야할 때: Job
  • 주기적인 배치작업을 해야하는 경우: Cronjob

자세히는 아래와 같다. 리플리케이션 컨트롤러 (Replication controller)은 쿠버네티스 초기부터 있던 컨트롤러로, 지정된 숫자만큼 파드의 개수를 항상 보장해줄 수 있도록 제어해주는 컨트롤러이다. 요즘은 이 리플리케이션 컨트롤러보다 리플리카셋(replicaset)을 주로 이용하거나, 앱 배포면 디플로이먼트(deployment)을 이용한다고 한다.

사용방법은 아래와 같다. 1) Kind에 replicationContoller라고 적는다. 2) spec.replicas내에 몇개의 파드를 유지할 것인지를 명시한다. 3) selector은 레이블이 동일한 어떤 파드를 관찰할 것인지를 명시하는 것이다. 여기서는 app: my-ngnix라는 라벨을 가진 파드를 바라보고 관찰한다. 4) template은 명시한 파드가 개수가 부족한 경우, 어떤 스펙으로 다시 만들지를 정의한다. *주의할 것은 selector이하의 속성값과 template.labels의 속성값은 동일해야 에러가 안난다.

아래와 같은 rc의 실행결과를 확인할 수 있다.

PS C:\Users\PC\Documents\repository\kube> kubectl get rc
NAME    DESIRED   CURRENT   READY   AGE
my-rc   3         0         0       24s

 

리플리카셋(Replicaset, RC): ReplicationContorller의 확장판(+POD 검색기능)


ReplicationContoller은 POD의 개수를 보장한다고 했다. 리플리카셋도 마찬가지로 POD의 개수를 보장한다. 하지만, 리플리카셋이 추가적으로 해주는 기능은,

 "="의 selector외에 집합연산자 (in, notin)등을 지원해서 저 쉽게 사용할 수 있다. 리플리카셋의 spec에는 matchLabels과 matchExpressions라는 것을 사용할 수 있는데, matchLabels은 리플리케이션컨트롤러가 해주는 기능과 비슷하다. 다만, matchExpressions으로 좀더 상세하게 POD의 선정을 할 수 있다. 가령 아래와 같이 matchExpression을 써주면 key의 In조건에 맞는 POD들만 선택해서 관리할 수 있다.

# replicaset.yaml

...
spec:
    replicas:3
    selector:
         matchLabels:
             app: my-app
         matchExpression:
             {key: verison, operator: In, value:["3.5", "2.1"]}

 

하지만 matchExpressions을 꼭 써야하는 것은 아니다. 다음과 같이 작성해도 충분히 동작한다.

위와 같이 파드의 개수를 3개로 유지하게끔 선언했는데, 아래와 같이 파드가 1개 생성된 경경우는 2개가 아직 모종의 이유로 생성되지 안음을 확인할 수 있다. cpu 요구량을 못맞춰서 중단된것을 알 수 있다.

> kubectl get pods
NAME                  READY   STATUS              RESTARTS   AGE
my-replicaset-q42qp   0/1     ContainerCreating   0          3s

> kubectl describe replicaset my-replicaset
....
Events:
  Type     Reason            Age                 From                   Message
  ----     ------            ----                ----                   -------
  Normal   SuccessfulCreate  2m7s                replicaset-controller  Created pod: my-replicaset-q42qp
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-86kwg" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-n4rkz" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-jg6xc" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-66pht" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-5zg4z" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-8ns6f" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-w5gsb" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m6s                replicaset-controller  Error creating: pods "my-replicaset-8j4dh" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m6s                replicaset-controller  Error creating: pods "my-replicaset-vtqgv" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      24s (x8 over 2m6s)  replicaset-controller  (combined from similar events): Error creating: pods "my-replicaset-fx7rt" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1

 

디플로이먼트(Deployment)


컨트롤러기보다는 컨트롤러인 레플리카셋(RS)을 제어해주는 역할이다. 디플로이먼트의 가장 큰 목적은 롤링업데이트(Rolling update) 또는 롤링백(Rolling back)을 하기위함이다. 롤링업데이트의 자세한 설명은 링크와 같다. 소프트웨어를 업데이트를 위한 것인데 요약하면 "롤링 업데이트는 파드가 여러 개 있다고 했을 때, 한 번에 모든 파드를 동시에 버전업하는 것이 아닌, 파드를 하나씩 업데이트해서 무중단 업데이트를 해주는 과정"이라고 생각하면 된다. 핵심은 "무중단 + 업데이트"이다.  

Deployment은 정확히는 컨트롤러는 아니고, 컨트롤러인 Replicaset을 제어하는 자원이다. Replicaset은 POD을 제어한다.

아래와 같이 대화형 튜토리얼을 이용하면 예시를 직접 해볼 수 있다(링크). 아래의 이미지를 보면, 이름이 "kubernetes-bootcamp"인 deployment가 하나가 실행됨을 확인할 수 있다. 위에서도 디플로이먼트가 레플리카셋을 제어한다고하니, 레플리카셋이 하나 있을 것이고, 레플리카셋은 또 파드를 제어하니, 파드의 생성도 함께 확인할 수 있을 것이다.

그러면, 디플로이먼트를 사용하려면 어떻게 yaml파일을 작성해야하나? 일단, deployment의 버전을 확인하기위해서 아래와 같이 확인한다. deployment은 apps/v1의 APIVERSION에 있다.

PS C:\Users\PC> kubectl api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
...
deployments                       deploy       apps/v1                                true         Deployment

그리고 아래와 같이 스펙에 맞춰 yaml파일을 작성한다. 형태는 replciaset과 동일하다.

apiVersion: apps/v1
kind: Deployment
metadata:
    name: my-deployment
spec:
    replicas: 2
    selector:
        matchLabels:
            app: my-nginx
    template:
        metadata:
            name: nginx-pod
            labels: 
                app: my-nginx
        spec:
            containers:
            -   name: nginx-container
                image: nginx:latest
                resources:
                    limits:
                        memory: 500Mi
                        cpu: 1
                    requests:
                        memory: 500Mi
                        cpu: 1

 

그러면 아래와같이 동작하는 것을 확인할 수 있다. (POD 하나는 리소스 이유 때문에 동작은 하지 않았다.)

PS C:\Users\PC\Documents\repository\kube> kubectl get all -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP          NODE             NOMINATED NODE   READINESS GATES
pod/my-deployment-cff6c8d86-jgb5g   1/1     Running   0          12s   10.1.0.25   docker-desktop   <none>           <none>

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   44d   <none>

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS        IMAGES         SELECTOR
deployment.apps/my-deployment   1/2     1            1           12s   nginx-container   nginx:latest   app=my-nginx

NAME                                      DESIRED   CURRENT   READY   AGE   CONTAINERS        IMAGES         SELECTOR
replicaset.apps/my-deployment-cff6c8d86   2         1         1       12s   nginx-container   nginx:latest   app=my-nginx,pod-template-hash=cff6c8d86

 

 

Deployment을 이용하여 pod을 스케일업하기: kubectl scale deployment [deployment명] --replicas=3

아래의 그림과 같이 deployment은 Replica Set(RS)을 선언적으로 지정해서 관리한다. 또한, Replica Set은 pod을 선언적으로 관리한다.(선언적= 지정한대로 개수, 자원등을 보장해줌). 따라서, deployment을 생성해놓고, replicaset 또는 pod을 직접 관리하면, 삭제되고 재생산된다. 따라서, pod수나 replicas을 변경하고싶으면 deployment을 이용해서 조작해야한다.

Deployment, Replica Set, Pod의 관계예시: 자료(https://theithollow.com/2019/01/30/kubernetes-deployments/)

아래와같이 쿠버네티스 공식홈페이지에 있는 것과 동일하게 웹서버인 nginx을 서비스하는 deployment을 정의했다.

 

이를 이용해서 Deployment 및 스케일업을 아래와같이 진행해볼 수 있다. 아래의 코드블럭처럼 replicas을 조절하면 replicas에 딸려있는 pod수가 조절되므로 replicas을 조정해도 된다. replicas을조정하기위해서는 deployment을 조정한다. 

// deployment 자원의 생성
$ kubectl create -f nginx-deployment.yaml
deployment.apps/nginx-deployment created

// 자원이 생성되었는지 확인
$ kubectl get deployment
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           18s

// replicas을 5개로 스케일업
$ kubectl scale deployment nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled


// 스케일업 되었는지 pod수를 확인
$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7fb96c846b-88ctt   1/1     Running   0          52s
nginx-deployment-7fb96c846b-dqbhk   1/1     Running   0          7s
nginx-deployment-7fb96c846b-jhgq2   1/1     Running   0          52s
nginx-deployment-7fb96c846b-m2bbg   1/1     Running   0          7s
nginx-deployment-7fb96c846b-xj74r   1/1     Running   0          52s

그리고, kubectl descirbde 을 이용하면 아래와같이 스케일업된것을 확인할 수 있다.

 

 

롤링업데이트의 사용은 어떻게 해야하나? 예시로 

# set 명령어 인자
kubectl set image deployment <디플로이먼트 이름> <컨테이너이름>=<새 버전>

# 디플로이먼트 내 컨테이너 ngnix 을 1.9.1로 업데이트
kubectl set image deployment/nginx nginx=nginx:1.9.1

# 모든 디플로이먼트와 RC의 컨테이너 내에 있는 ngnix이미지를 1.9.1로 업데이트
kubectl set image deployments,rc nginx=nginx:1.9.1 --all

*references:https://www.macstadium.com/blog/how-to-k8s-pods-replicasets-and-deployments

 

Deployment의 정지: kubectl delete deployment [deployment명]

// deployment 상태확인
$ kubectl get deployment
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
simple-app-deployment   3/3     3            3           11m


$ kubectl delete deployment simple-app-deployment
deployment.apps "simple-app-deployment" deleted

 

 

스테이트풀세트(Stateful set)


작성중

 

데몬세트(Daemon set)


작성중

 

잡 컨트롤러(Job contoller)


작성중

 

크론잡(Cronjob)


작성중

 

기타(트러블 슈팅)


이슈1: error: resource mapping not found for name: "my-rc" namespace: "" from ".\\my_rc.yaml": no matches for kind "ReplicationContoller" in version "v1" ensure CRDs are installed first

해결방법: 아래와 같이 쿠버네티스에서 지원하는 자원들과 APIVERSION을 확인한다. 참고로 해당 이슈는 "ReplicationContoller"을 kind에 썼는데, 아래와 같이 "ReplicationController"에 오타가 난 것을 알 수 있다. 즉, 아래의 리스트의 KIND와 동일한 KINDS을 써주어야한다.

$ kubectl api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
bindings                                       v1                                     true         Binding
componentstatuses                 cs           v1                                     false        ComponentStatus
configmaps                        cm           v1                                     true         ConfigMap
endpoints                         ep           v1                                     true         Endpoints
events                            ev           v1                                     true         Event
limitranges                       limits       v1                                     true         LimitRange
namespaces                        ns           v1                                     false        Namespace
nodes                             no           v1                                     false        Node
...

 

이슈 2: 리플리카셋에서 아래와 같이 메시지가 나온 경우(failed quota) Error creating: pods "my-replicaset-vvh6n" is forbidden: failed quota: mem-cpu-demo: must specify limits.cpu for: nginx-container; requests.cpu for: nginx-container

해결방법: 리플리카셋 내 template.spec.container.resources 내에 limit 또는 reqeusts을 명시한다.

반응형

의외로 다른 IT 프로젝트와 달리, 특히 AI 프로젝트의 성공율이 15%정도라고 한다. 왜 15%밖에 안되는 것인가? 해당 보고서에는 아래와 같이 설명하고 있다.

  • ML은 아직 리서치 단계(연구단계)의 작업들이다. 따라서, 애초에 프로젝트 시에 무조건 된다는 버려야 한다.
  • 기술적을 불가능한 문제를 푸려고 한다.
  • 프로덕션 단계를 고려하지 않는다.
  • ML 프로젝트의 성공여부가 불분명하다.
  • 팀 관리가 안된다.

 

Lifecycle (생애주기)


ML project의 생애주기는 ML 프로젝트에 각각 무엇이 수행될 수 있는지에 관한 내용이다. 크게는 프로젝트 계획하기-> 데이터 수집 및 라벨링 -> 모델 학습 및 디버깅 -> 모델 적용 및 테스트로 구분된다. 이 단계가 무조건 1, 2,3,4의 순차적인 단계로 실행되는 것은 아니고, 진행하던 단계가 문제에 봉착했는데 풀 수 없을 경우는 이전, 또는 더 이전단계로 돌아가서 계획부터 다시 할 수 있다는 것을 알아야 한다.

ML 프로젝트의 생애주기

- 프로젝트 계획하기(Planning and Project setup): 실제 문제를 풀어 나갈 것인지, 목표는 무엇인지, 리소스는 어떤 것들이 있는지 파악하는 단계이다.

- 데이터 수집 및 라벨링(data collection and labeling): 훈련용 데이터를 수집하고, ground truth(실제 참이라고 할 수 있는 라벨)을 이용하여 데이터를 라벨한다. 만일 이 단계에서 뭔가 문제가 있거나 데이터를 구하기 어려우면 프로젝트 계획부터 다시 실행하는 것을 권장한다.

- 모델 학습 및 디버깅(Model traning and debugging): 이 단계에서는 베이스 라인 모델을 빨리 적용해놓고, 가장 좋은 모델들을 찾아서 성능을 높혀나가는 것을 의미한다. 그리고 이 단계에서는 뭔가 라벨링이 이상하거나 데이터가 더 필요한 경우는 이전 단계인 "데이터 수집 및 라벨링" 단계로 돌아간다. 혹은 문제가 너무 어려워서 풀지 못할 것 같거나, 프로젝트 요구사항에서 밀릴 경우는 프로젝트 게획하기 단계부터 다시 실행한다.

- 모델 적용 및 테스트(ML Deploying and Model testing): 이 단계에서는 파일럿 모델을 연구실 환경에서 한번 돌려보고, 잘되는지 확인하고, 실제 프로덕션 단계로 적용한다. 만일, 성능이 애초에 안나오는 경우, 모델 학습 및 디버깅 단계로 돌아간다. 또는 데이터가 더 필요하거나, 훈련데이터랑 실제 데이터랑 너무 안맞는 경우 데이터 수집 및 라벨링 단계로 간다. 마지막으로 실제 환경에서 잘 동작하지 않거나, 사용자의 사용 환경과 너무 안맞는 결과들이 나올 경우에는 계획부터 다시해야할 수도 있다.

 

프로젝트의 우선순위 결정하기(Priortizing projects)


ML프로젝트를 실행하기전에, 이 프로젝트가 얼마나 비지니스 모델에 영향을 줄 것인지? 아래의 그림처럼, 가능한 실현가능성도 높고, 실제 비지니스에 영향을 줄만한 프로젝트를 하는 것이 중요하다. 

영향도 평가: 니즈(Needs). 니즈를 파악하는 것은 얼마나 의사결정 과정에서 마찰을 경험하고 있는지를 파악해보는 것이다. ML의 장점(Strength)은 Software 2.0(데이터가 많아질 수록 성능이 강력해지는 소프트웨어)과 데이터가 실제 복잡한 규칙기반의 소프트웨어이며, 이 규칙이 데이터로 학습할 수 있는 지를 의미한다. 

실현가능성 평가(Feasibiltiy): 실현 가능성을 평가하는 것들중에 비용이 많이 드는 순서로 문제의 난이도 -> 요구되는 모델의 정확성 -> 사용가능한 데이터순으로 높다. 첫 째로 가장 문제가되는 데이터는 "데이터가 많은지", "얻기 쉬운지?", "얼마나 많이 필요할지" 등이다. 흔히 생각할 수 있는 내용이다. 둘 째로, 모델의 정확도 요구사항은 혹시 모델이 틀렸을 때 얼마나 비용이 많이 드는지, 어느정도 정확해야 쓸만한지, 윤리적인 이슈는 없는지에 관한 내용이다. 마지막으로 문제의 난이도는 "문제가 잘 정의도리 수 있는지", "이전에 해결한 문제들은 없는지" 컴퓨팅 요구사항"은 되는지, 사람이 할 수 있는일인지에 관한 내용이다.

 

아키텍처(Archetypes)


ML 프로젝트의 메인 카테고리로, 프로젝트 관리 및 구현을 어떻게 할 것인지에 관한 내용이다. 이 챕터는 그 원형(Archetypes)에 대한 얘기로, 크게 3가지로 분류되며, Sofware 2.0, HITL(Human in the loop), Autonomous system이다. 본인이 풀고자하는 문제들이 어느부분에 해당하는지, 어떻게 구현되는지 개념적으로 명확히 이해하는데 도움을 줄 수 있다.

  • Software 2.0: 소프트웨어의 성능이 데이터가 많아질 수록 강력해지는 것을 의미한다. 기존의 규칙기반 시스템을 능가하는 것을 의미한다.
  • HITL: 사람의 컨펌하에 쓰는 소프트웨어이다. 가령, 구글 이메일 작성시, 오토 컴플릿되는 것을 의미한다.
  • 자동화 시스템: 사람 손을 안타는 시스템이다.

 

평가지표(metrics)


위의 내용처럼 어떻게 구현하겠다라는 가이드가 잡히면, 실제로 어떤 지표를 삼아야 성공여부를 달성했는지, 효율적으로 평가할지를 알 수 있다. 당연하겠지만 평가도구는 모델의 성능을 평가할 수 있는 지표를 삼아야한다. 처음에는 모델을 성능을 단순히 평가할만한 하나의 지표로 삼다는다. 그 후에, 안정화되면, 여러 평가지표를 복합적으로 쓸 수 있다. 가령, 평균적인 모델의 성능은 어느정도가 되어야하는지지표(average)와 지표를 넘는지 안넘는지 임계치(Threshold)로 계산해볼 수 있다. 가령 임계치는 모델 몇 MB이하여야한다 정도로 써볼 수 있다. 

 

베이스라인 모델 구하기


실제 ML프로젝트를 하다보면 무조건 SOTA(State of the Art)부터 가져와서 적용할라다보니까, 어렵다. 그리고, SOTA을 적용하다가 너무어려워서 다 따라가지도 못하고 접는 경우도 생긴다. 그리고 SOTA부터 적용하니까 어느정도가 적정한 모델 성능인지 파악하기 어렵다. 베이스라인 모델을 구해서 적용해보는 것은 위의 내용을 파악하는데 도움을 준다. 베이스라인 모델은 대강 우리의 모델이 어느정도 성능이 나올 것이다하는 바로미터로 써볼 수 있다. 예를 들어, 질병 추천시스템의 Top 5 Recall이 대충 선형회귀로 해도 80%가나오면 이문제는 어느정도 풀만한 모델이고, 잘만하면 90%도 나올 수 있다는 것을 알 수 있다. 

반응형

 

Grad-CAM의 FeatureMap의 기여도를 찾는 방법은 각 FeatureMap의 셀로판지가 최종 색깔에 어느정도 기여하는지와 동일하다.

 

요약


Grad-CAM은 CNN 기반의 모델을 해석할 때 사용되는 방법이다. 인공지능의 해석방법(XAI)에서는 Grad-CAM은 흔히 Post-hoc으로 분류되고(일단, 모델의 결과(Y)가 나오고 나서 다시 분석사는 방법)으로 취급된다. 또한, 딱 CNN에서만 사용되기 때문에 Model-specific 방법이다. 이 Grad-CAM의 가장 큰 쉬운 마지막 CNN이 나오고 나서 반환되는 Feature Map(본문에서:A)이 평균적으로 Y의 분류에 어느정도 되는지를 계산하고, 각 픽셀별로 이를 선형으로 곱하는 방법이다.

 

 

상세 내용


Grad-CAM은 아래와 같이 계산할 수 있다. 

식1: Grad-CAM의 핵심적인 계산과정

 

각, 기호에 대한 설명은 아래와 같다. y은 모델이 내뱉는 확률이며, c는 특정 라벨을 의미한다. 개 vs 고양이의 분류기이면 c의 최대값은 2이 된다(=1, 2의 분류). 그 다음, $A^k$은 각 Feature Map을 의미한다. Feature Map은 이미지가 CNN을 통과한 결과를 의미한다. $k$가 붙는 이유는 CNN을 통과하고나서 CNN의 필터의 개수(k)만큼 반환되는 값(feature map)의 k채널이 생기기 때문이다. 일종의 채널과 같다. 

마지막으로 $a_{k}^{c}$은 $A^{k}$의 필터맵이 이미지와 유사한데, 이 필터맵의 각 픽셀들이 분류에 어느정도 기여했는지를 의미한다. 아래의 그림(Figure 1)을 보자. Feature maps이 4개가 있으니 $k=4$인 예시이다. 녹색의 feature maps을 보면, 5x5로 이미지가 이루어져 있는데, 각 $i, j$에 해당하는 픽셀이 $y^{c}$에 어느정도 기여했는지를 구한 것이다.

 

Figure 1. GAP의 그림

즉, Grad-CAM은 각 feature Map이 어느정도 모델의 결과값($y$)에 기여했는지($A^{k}$)와 각 픽셀이 모델의 결과값에 어느정도 기여했는지($a_{k}^{c}$)을 곱하는 것이다. 결국, 각 픽셀이 feature maps을 고려하였을 때(가중하였을 때) 결과값에 어느정도 영향을 미쳤는지를 계산할 수 있다.

 

구현:


torch에서는 register_foward_hook과 register_backward_hook을 이용하여 grad cam을 계산할 수 있습니다. register_forward_hook과 register_backward_hook은 PyTorch에서 모델의 레이어에 대한 forward pass와 backward pass 중간에 호출되는 함수를 등록하는 메서드입니다. 이를 통해 레이어의 활성화 맵이나 그래디언트를 추출하거나 조작할 수 있습니다.

register_forward_hook:
이 메서드는 모델의 레이어에 대해 forward pass가 수행될 때 호출되는 함수를 등록합니다. 등록된 함수는 해당 레이어의 출력을 인자로 받아 다양한 작업을 수행할 수 있습니다. 주로 활성화 맵 등 중간 결과를 추출하거나 조작하는 데 사용됩니다.

def forward_hook(module, input, output):
    # module: 레이어 인스턴스
    # input: forward pass의 입력
    # output: forward pass의 출력
    pass

target_layer.register_forward_hook(forward_hook)

 

register_backward_hook:
이 메서드는 모델의 레이어에 대해 backward pass가 수행될 때 호출되는 함수를 등록합니다. 등록된 함수는 해당 레이어의 그래디언트를 인자로 받아 다양한 작업을 수행할 수 있습니다. 주로 그래디언트를 조작하거나 특정 그래디언트 정보를 추출하는 데 사용됩니다.

def backward_hook(module, grad_input, grad_output):
    # module: 레이어 인스턴스
    # grad_input: 입력 그래디언트
    # grad_output: 출력 그래디언트
    pass

target_layer.register_backward_hook(backward_hook)

 

위의 두 함수를 이용하여 아래와 같이 구현할 수 있습니다.

def grad_cam(
    model: torch.nn.Module,
    image: np.ndarray,
    target_layer: torch.nn.Module,
) -> np.ndarray:
    """
    Args:
        model (torch.nn.Module): Grad-CAM을 적용할 딥러닝 모델.
        image (np.ndarray): Grad-CAM을 계산할 입력 이미지.
        target_layer (Type[torch.nn.Module]): Grad-CAM을 계산할 대상 레이어.

    Returns:
        np.ndarray: Grad-CAM 시각화 결과.
    """

    def forward_hook(module, input, output):
        grad_cam_data["feature_map"] = output

    def backward_hook(module, grad_input, grad_output):
        grad_cam_data["grad_output"] = grad_output[0]

    grad_cam_data = {}
    target_layer.register_forward_hook(forward_hook)
    target_layer.register_backward_hook(backward_hook)

    output = model(image)  # 모델의 출력값을 계산합니다. y_c에 해당
    model.zero_grad()

    # 가장 예측값이 높은 그레디언트를 계산합니다. output[0,]은 차원을 하나 제거
    output[0, output.argmax()].backward()

    feature_map = grad_cam_data["feature_map"]
    grad_output = grad_cam_data["grad_output"]
    weights = grad_output.mean(dim=(2, 3), keepdim=True)
    cam = (weights * feature_map).sum(1, keepdim=True).squeeze()
    cam = cam.detach().cpu().numpy()

    return cam
반응형

Pod(파드): 여러 컨테이너를 묶어 놓은 논리적인작업 단위이다. 파드 내에는 1개의 컨테이너가 존재할 수 있고, 여러 개의 컨테이너가 존재할 수도 있다. 1개만 존재하면 싱글 컨테이너 파드, 2개 이상 존재하면 멀티 컨테이너파드라고 부른다. 그럼 컨테이너는 무엇인가? 하나의 "어플리케이션"이라고 생각하면 된다. 파드에는 여러 개의 컨테이너가 존재할 수도 있기 때문에, 웹 서버 컨테이너, 로그 수집기, 볼륨(=디스크)을 묶어 하나의 파드로 구성할 수도 있다. 

 

파드 사용하는 방법

 


파드를 사용하는 방법은 CLI(커멘드 명령의 인터페이스)로 실행시키는 방법이 있고, 아래의 그림과 같이 YAML포맷의 파일을 이용해서 생성하는 방법이 있다.

  • metdata.name: 값에 해당하는 필드 및 필드명은 이 파드의 이름을 설정하기 위함이다. 
  • spec.containers: 이 필드아래에 container을 적으면 하나씩 컨테이너가 생긴다. 아래의 예시는 컨테이너 1개를 생성하는 예시이다. "-"(하이픈)은 하위필드를 배열로 묶겠다는 것이다. 즉, 파이썬의 딕셔너리로 생각하면 {"spec": {"container": [{"image":"nginx":"latest"}, {"name":"my-nginx"}, {"resources": {}}]와 같이 속성들을 containers의 하위 필드 배열로 취급하겠다는 것이다.
  • spec.containers.image: 컨테이너에 사용될 이미지명을 적는다, ":"앞은 어플리케이션 명,  뒤는 어플리케이션의 버전정보이다. 사용자가 다운로드 받지 않아도, 이 컨테이너를 실행하는 순간에 컨테이너 허브에서 이 이미지를 다운로드(PULL)하여 가져온다.

 

Pod 생명주기


파드 생명주기는 파드 생성부터 파드의 삭제, 실패까지의 과정 중에 각각 어느단계에 있는지에 대한 개념이다. 만일 파드가 러닝중인지? 생성중인지? 실패했는지에 관한 내용을 알 수 있다. 일단 파드가 한번 생성되면 유니크안 식별자(UID, Unique ID)을 갖는다. 그리고, 삭제되기 전까지는 유지된다. 파드 생명주기 중에 어느 상태인지는 status의 필드로 확인할 수 있다.

이름이 mywebserver인 STATUS가 실행중인 파드. 어플리케이션은 1개중에 1개가 구동중

각 단계별로 의미하는 것은 아래와 같다.

상태(STATUS) 의미
Pending 쿠버네티스 클러스에서 일단 실행이 가능한 상태라고 판명된 상태. 하지만, 컨테이너중에 몇개는 아직 구동을 준비중인 상태를 의미한다. (=즉, "만들어지는 중이에요" 라는 상태)
Running 파드 내에 모든 컨테이너가 구동중인 상태
Succeeded 파드 내 모든 컨테이너가 구동에 성공된 상태. 
Failed 파드 내 모든 컨테이너가 종료된 상태. 정상 실행이 아닌 (non-zero status) 컨테이너가 존재하는 상태. 
Unknown 모종의 이유로 컨테이너 상태를 얻을 수 없는 경우

 

LivenessProbe 사용하기: 자동으로 컨테이너가 죽는 경우, 재시작 하기


kubernetes의 livnessProbe의 동작 방식 그림 (참조: https://komodor.com/learn/kubernetes-liveness-probes-a-practical-guide/)

 

  • livenessProbe을 이용해서 컨테이너 sefl-healing 이용하기

livenessProbe가 동작하는 방식은 httpget probe, tcpSocket probe, exec probe라는 것을 이용해서 각각의 목적에 맞는 일부 기능들을 주기적으로 체크를 해서 컨테이너가 정상적으로 동작 중인지를 확인한다. 예를 들어, http 웹서버라면, httpget probe은 주기적으로, 지정한 IP, port, path에 대해서 HTTP get요청을 보내서 응답이 잘 못나오는 경우 컨테이너를 다시 시작한다. tcpSocket probe은 ssh로 접속해보고 접속이 몇 번 실패하면, 해당 컨테이너를 다시 시작하는 것이다. exec probe은 명렁을 전달하고 명령의 종료코드가 0 (정상종료)가 아니면, 컨테이너를 다시 시작하는 것이다. 중요한 것은 Pod을 재시작하는 것이 아니라 Container을 재시작하는 것이다. 따라서, Pod은 그대로 이기 때문에, IP을 동일하게 사용할 수 있다.

livenessProbe을 사용하기 위해서는 몇 가지 전달해야하는 인자가 있는데, 아래와 같다. 자세한 인자들(Configuration)은 여기에서도 확인이 가능하다. initialDelaySeoncds은 컨테이너 실행 후 몇초 뒤에 probe을 실행할 것인지에 대한 속성이다. periodSeoconds은 몇초 주기로 프로브를 실행할 것지에 대한 속성, timeoutSeconds은 프로브를 실행하고나서 몇초 내에 명령들이 종료되어야하는지에 대한 속성, seccessThreshold은 이 프로브가 몇 번 연속 성공하면 성공이라고 볼것인가에 대한 것이다. failureThreshold은 프로브가 몇 번 실패해야 실패로 볼 것인지에 대한 기각역이다.

아래의 예시는 buxybox라는 이미지를 이용해서 컨테이너 1개짜리를 담은 "liveness-exam"이라는 Pod을 생성한다. 추가로, 컨테이너를 실행할 때, 컨테이너 상의 '/tmp/healthy'라는 파일을 만들고 30초 후에 삭제하는 명렁도 같이 넣어놓았다. 즉, 컨테이너가 실행되고나서 30초 동안만 파일이 존재할 것이다.

exec livenessProbe을 사용하기위한 yaml 예시. 컨테이너 실행할 때 초기에 30초동안만 파일을 생성하고, 그 뒤에 지우는 커멘트가 들어가있다. 마찬가지로 이 파일을 cat으로 확인하는 커멘드도 포함되어 있다.

 

아래와 같이 exec livenessProbe가 실행을 몇 번해보다가, failthrehold만큼 실패하면 해당 컨테이너를 재시작한다.

위의 yaml파일에서 failureThrehold가 3번이 지정되었기 때문에, 3번 검사하고 (x3 over 11s) 실패했기 때문에, 해당 컨테이너를 삭제하고 재시작하는 것이다.

 

초기화 컨테이너(Init container): 초기화 컨테이너는 파드에 앱 컨테이너가 실행되기 전에, 실행되는 특수한 컨테이너이다. 이 초기화 컨테이너로, 앱 이미지(도커이미지)에 없는 설정, 유틸리티 스크립트를 실행하여 일부 변형을 할 수 있다.


초기화 컨테이너(init container)은 Pod내에서 어플리케이션이 동작하기전에 실행되는 특별한 컨테이너를 의미한다[ref]. 주로, 만들어진 이미지를 컨테이너로 실행하면서 추가사항을 실행하기 위한 목적으로 쓰인다. 예를 들어, 구동하고자하는 앱은 RestAPI + 딥러닝추론엔진이라면, 컨테이너에는 FastAPI이미지만 있고, 초기화 컨테이너가 실행될 때, 딥러닝추론엔진만 다운로드 받아오는 식이다. 이렇게하면 딥러닝 추론엔진이 사이즈가 엄청 크더라도(>10Gb) 한 이미지에 담을 필요없이, 이미지 + 추가실행사항 정도로 이해하면된다.

일반적인 컨테이너랑 거의 유사(리소스 제한, 볼륨 사용, 보안세팅)한데, 다른 몇 가지가 있다, 대표적으로 lievnessProbe을 지원하지 않는 것이다. 그리고 초기화 컨테이너도 일반 컨테이너(regular container)와 마찬가지로 다수의 초기 컨테이너를 사용할 수 있다. 초기 세팅이 2~3개 이상 필요한 경우, 초기화 컨테이너를 2~3개 이상 명시하는 것이다. 이 때, yaml파일에 적인 순서대로 동작한다. 먼저 선언된 초기화 컨테이너가 동작하지 않으면, 뒤의 초기화 컨테이너가 동작하지 않는다. 초기화 컨테이너의 실행이 실패하면, 쿠버네티스는 실행 될 때까지 초기화 컨테이너를 구동해본다. (=쿠버네티스의 "선언적 특징"). 그리고, 초기화 컨테이너가 다 실행되어야, 메인 컨테이너를 동작한다(=초기화 컨테이너가 실행이 안되면 메인컨테이너가 구동이 안됨)

 

아래는 일반 컨테이너를 1개 구성하고 초기 컨테이너를 2개 구성한 것이다.

# myapp.yaml
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

 

실제 파드를 쿠버네티스에 띄워보면 아래와 같아. myapp-pod가 초기화되려고할 때, STATUS은 Init:0/2상태로 2개의 초기화 컨테이너가 파드내에서 동작해야함을 의미하고 있다.

 

그리고, kubectl desribe pod myapp-pod을 찍어보면, 첫 번째 초기화 컨테이너(init-myservice)가 실행중임을 알 수 있다.

kubectl desribe pod myapp-pod

# std out
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  13m   default-scheduler  Successfully assigned default/myapp-pod to docker-desktop
  Normal  Pulling    13m   kubelet            Pulling image "busybox:1.28"
  Normal  Pulled     13m   kubelet            Successfully pulled image "busybox:1.28" in 5.871629222s
  Normal  Created    13m   kubelet            Created container init-myservice
  Normal  Started    13m   kubelet            Started container init-myservice

 

조금 더 자세히, 첫 번째 컨테이너의 로그를 보면(kubectl logs myapp-pod -c init-myservice) 계속 실패중이다. 

$kubectl logs myapp-pod -c init-myservice | tail

# std out
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'myservice.default.svc.cluster.local'
waiting for myservice
nslookup: can't resolve 'myservice.default.svc.cluster.local'
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

waiting for myservice

위와 마찬가지로 두 번째 컨테이너의 로그를 보면, 첫 번째 컨테이너가 실패했기 때문에, 구동을 시도조차 하지 않았을 것이다.

 

 

Infra container 사용하기


싱글 컨테이너로 파드를 구동하면 일반 컨테이너(regular)가 1개 돌아가는데, 사실은 아무일도 하지 않는 Infra container (pause) 컨테이너가 숨어있다.파드 생성될 때, 1개씩 생성된다. 파드에 대한 인프라 IP, HOSTNAME 을 관리한다. 이 파드는 식별자(UID)가 1이다. 다른 컨테이너의 부모 컨테이너로 사용(다른 컨테이너를 생성할 때, 상속받듯이 사용)이 된다. 그래서, 파드 안에 새로운 컨테이너 생성되면, Infra contatiner을 참조하여 만드니까, IP, hostname이 동일하게 생성된다. 혹시나, 이 infra conatiner가 재시작이 된다면, 파드 내에 네트웍구성이(IP, hostname)가 동일해야되기때문에, 모든 컨테이너가 다시 재시작이 된다.

 

 

스태틱 파드(Static Pod)


각 워커노드에서는 kublect 이라는 데몬이 돌아이 돌아가는데, kubelet은 파드에서 컨테이너가 확실하게 돌아가게끔 관리해주는 역할을 한다. 여태까지는 컨테이너를 띄울 때, Kubectl을 통해서, 각 노드에게 명령을 전달했다. 반면, 스태틱 파드는 직접 데몬에서 실행하는 (노드에 직접 들어가서 파드를 정의하는 디렉토리(/etc/kubernetes/manifests, kubelete configuration file)에서 직접 설정하게끔 파일을 설정하면, 자동으로 kubelet이 이 파일을 참조해서 컨테이너를 만드는 데, 이것이 스태틱파드이다. 즉, API서버 없이 특정노드에 있는 kubelet에 의해 직접 실행되는 파드를 의미한다. 쉽게 말해, 수동으로 돌리는 파드이다.

# 특정 노드로 접속
$ ssh alchemist@node1

# kubelet 설정 파일을 확인
$ docker exec -it 0a5f48991df2 /bin/bash # minikube을 docker에서 띄운 경우
$ cat /var/lib/kubelet/config.yaml  | grep staticPodPath
staticPodPath: /etc/kubernetes/manifests

# PodPath에 지금 kubelet이 실행하고 있는 자원들을 확인
$ cd /etc/kubernetes/manifests
$ ls /etc/kubernetes/manifests
etcd.yaml  kube-apiserver.yaml  kube-controller-manager.yaml  kube-scheduler.yaml

# nginx을 예시로 실행하기 
$ vi nginx-pod
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: nginx-pod
  name: nginx-pod
spec:
  containers:
  - image: nginx:latest
    name: nginx-pod
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

# control-plane으로 들어와서
$ kubectl get pods
NAME                 READY   STATUS    RESTARTS   AGE
nginx-pod-minikube   1/1     Running   0          14s

 

Pod 생성시 컨테이너에서의 자원 제한하기(requests, limits)


Pod을 생성할 때, 각 컨테이너가 어느 노드에 실행될지는 컨트롤플래인(control-plane)의 스케쥴러가 확인해준다고 했다. 사용자가 지정으로 "특정 컨테이너를 돌릴 때, 적어도 이정도 자원이 필요해요", 또는 "특정 컨테이너를 돌릴 때, 다른 컨테이너에 영향안받게 최대 여기까지만 자원을 할당해주세요" 와 같이 요구사항/제한을 둘 수 있다. 전자(특정 컨테이너를 돌릴 때, 적어도 이정도 자원이 필요해요)는 requests, 후자(특정 컨테이너를 돌릴 때, 다른 컨테이너에 영향안받게 최대 여기까지만 자원을 할당)은 limits을 의미한다. 혹여나 컨테이너가 limits의 자원을 넘어서 할당되면, 해당 파드가 죽게되고, 다시 스케쥴링이 된다. Requests은 container을 생성할 때, 어느 최소요구사항을 만족하는 node을 찾아달라는 요청이다. 신기하게도? limits을 넣으면 requests도 같은 크기로 함께 자동으로 갖게된다.

쿠버네티스에서는 메모리의 크기는 Mi단위로 표현하고, CPU은 숫자(코어수), 또는 1000mc(밀리코어, 1CPU)로 표현한다.

 

# ngnix-pod.yaml 작성
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - image: nginx:latest
    name: nginx-container
    ports:
    - containerPort: 80
      protocol: TCP
    resources:
      requests:
        memory: 500Mi
        cpu: 1
        
# Pod생성
kubectl create -f ngnix-pod.yaml
  
# 파드 확인
kubectl describe pod <파드명>
 Containers:
  nginx-container:
    Container ID:   docker://8cb4710e30e4ac033276b51361791de5ff9b39edfdc84ff6e315ab71f8af8d38
    Image:          nginx:latest
    Image ID:       docker-pullable://nginx@sha256:db345982a2f2a4257c6f699a499feb1d79451a1305e8022f16456ddc3ad6b94c
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Tue, 19 Jul 2022 14:20:39 +0000
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:        1
      memory:     500Mi
    Environment:  <none>
    Mounts:

 

 

환경변수 설정하기


Kubernetes에서 컨테이너를 실행할 때, 컨테이너에  내에서 쓰이는 환경변수를 설정할 수 있다. 아래와 같이 컨테이너(Containers)이하에 env라는 속성명 이하에 담으면 된다.언제 쓰느냐? 도커 컨테이너에서 정의된 환경변수외에 추가로 필요하거나, 변경할 때 필요하다. 

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - image: nginx:latest
    name: nginx-container
    ports:
    - containerPort: 80
      protocol: TCP
    env:  # 환경변수의 설정
    - name: env_name
      value: "test_env_name"

 

Pod 디자인 패턴


파드 디자인패턴은 파드 내에 컨테이너를 어떻게 구성해서 실행할 것인지에 대한 설계도를 의미한다.  파드가 2개 이상의 컨테이너로 이루어져있기 때문에, 여러 파드가 있거나 의존성이 어떤지에 따라서 구성이 각가 다를 수 있다. 크게는 1) 사이드카(Side car), 2) 엠베서더(Ambassador), 3) 어뎁터(Adaptor) 패턴이 있다. 자세한 내용은 여기서도 확인할 수 있다.

1) 사이드 카 패턴: 사이드카는 아래의 그림과 같이 오토바이에 옆에 타는 부분을 의미한다. 사이드 카라는 의미는 여기서 가져왔다. 사이드카 패턴은 이미 기능적으로 잘 실행 될 수 있는 컨테이너에 뭔가 기능을 덧댄(Add on) 이미지 패턴을 의미한다. 본디 실행하고자하는 컨테이너 자체가 잘 동작하는 경우를 의미한다. 주로 로그를 쌓거나, 모니터링하는 기능을 더해서 사용하기도한다. 

Side car

2) 엠베서더(Ambassodor): 파드 외부와 내부를 연결해주는 역할을하는 것이다. 엠베서더는 대사를 의미한다. 외교사절단 대표정도로 의미가되는데, 쿠버네티스에서는 외부 TCP-Proxy로 DB을 연결해주는 역할을할수 있다. 주로 로드밸런서 역할을

3) 어뎁터 패턴(Adaptor): 어뎁터패턴은 어뎁터 용도의 컨테이너가 있어서, 각 컨테이너에서 생성하는 정보들을 모아서 관리해주거나, 또는 실제 물리적 DB에 인터페이스해서(또는 외부 데이터를 가져오는 역할), 어플리케이션에 다시 전달해주거나 해주는 역할로 사용된다. 가령 A, B, C 어플리케이션(각 컨테이너)의 로그가 제각각으로 저장되면, 이를 전처리해서 모아주는 역할을 하는 컨테이너가 있으면, 이 컨테이너가 어뎁터가 된다.

반응형

+ Recent posts