오토마우스 


보건의료인이라면 의무적으로 듣는 보수교육이 있습니다. 이 보수교육을 주로 온라인으로 듣는데요. 온라인으로 듣는 보수교육으로부터 해방시켜줄 오토마우스를 소개합니다.

 

오토 마우스 다운로드


auto_mouse.exe
11.71MB

 

사용법


1. 첨부파일에 걸어둔 auto_mouse.exe을 다운로드 받습니다.

 

2. 자동 클릭할 위치에 커서를 올려두고, 키보드에서 "Enter"을 누릅니다. 그러면, 커서의 현 위치가 프로그램에 등록됩니다.

3. 몇초마다 클릭할 것인지 세팅합니다. 보통 10초 또는 5초정도 맞춰넣고, 적용을 누릅니다.

 

4. 보수교육 창(브라우저)를 띄워놓고 "자동 클릭"버튼을 눌러, 자동으로 클릭되도록합니다.

5. 중지를 시키려면 "멈춤"버튼을 누르고, "간격" 만큼 기다립니다.

 

2개의 창을 동시에 2개를 클릭하는법


오토마우스 프로그램을 2개를 켜두고, 웹브라우저도 2개를 켜놓고, 각각에 대해서 좌표설정(Enter), 간격설정(적용), 시작(클릭)을 하면됩니다.

 

개발과정


pyinstaller [python_file_path]--noconsole --onefile

 

반응형

요약


LCA(Latent class analysis)은 클러스터링 분석과 유사하게 어느 집단에서 하위그룹이 있는지 알아보는 통계적 방법론이다. 이 하위그룹을 LCA에서는 latent group(=subgroup, 또는 class)라고 한다.

 

언제사용하나? 내가 분석하고자하는 군을 더 나누고자할 때,


LCA은 k-means clustering과 유사하게 연구대상자(cases)을 더 하위 그룹으로 쪼개어 분석하고자할 때, 시행하는 분석방법이다.[1]

 

 

모형(LCA model)의 품질평가. 잠재계층의 수를 어떻게 결정하는가?


LCA의 모형이 얼마나 데이터셋을 잘 평가하는지는 the likelihood-ratio statistic (G2), Akaike information criterion (AIC), Bayesian information criterion (BIC), Adjusted BIC, the log-likelihood, Entropy가 있다. Entropy 값은 클수록, G2 , AIC, BIC, Adjusted BIC와 the log-likelihood 값은 작을수록 모형 이 자료에 적합하다는 것을 의미한다.

K-means clsutering에 보면 inertia라는 값이 있다. Inertia은 클러스터 내의 l2 distnace의 합계이다. 클러스터링의 품질을 평가할 때, 결정요소 K을 inertia로 계산하고, 이 inertia가 툭툭 떨어지다가 잘 안떨어지는 구간을 통상 K로 결정한다. AIC, BIC도 유사하다. AIC, BIC 값이 크게 떨어지다가 감소폭이 떨어지는 잠재계층 수를 연구자가 결정하면된다.

 

 

 

클러스터 분석(Cluster analysis)와의 차이/공통점은 무엇인가?


클러스터 분석과 대표적인 차이를 이해하는 것은 잘 알려진 k-means 알고리즘과 LCA의 차이를 생각해보면 직관적으로 이해가 가능하다.

  LCA K-means
공통점 Person-orient analysis Person-orient analysis
차이점    
  Membership 각 군에 속할 확률이 계산 지정됨.
각 연구대상자가 2군이상 중복되지 않음
  변수 연속형 외 다 가능 연속형
  결측치 결측치에 영향이 별로 없다고 알려짐 많이 영향받음
이상치가 있으면 centroid에 영향줄 수 있음.
  잠재계층의수 Likelihood을 이용하여 AIC, BIC 측정 클러스터링 품질지표(예, 군내 L2거리의 합, 실루엣 계수 등)

 

공통점:

1)     두 분석 모두 “Person-oriented analysis”이다. 각 연구대상자들의 패턴을 비교해서 각 연구대상자들이 어느 그룹에 묶일지를 식별하는 분석방법이다. 이와 반대로, variable centered approach가 있는데, 이건 변수들이 어떤 관계가 있을거다라고 가정하고, 변수들간의 해석을 목적으로하는 분석방법이다. 두 분석 모두 “연구대상자가 어느 그룹으로 할당될지”에 대한 초점이므로 Person-oriented analysis에 해당된다.

차이점:

1.     가정이 다르다. 같은 클러스터(=군)에 있는 연구참여자들이 매우 유사한 변수들을 가지고 있을 것이라 가정한다. 반면, LCA은 잠재계층(latent classes)가 존재하며, 잠재계층의 각 군으로 전 연구대상자들을 설명할 수 있을거라고 가정한다.

2.     클러스터 분석에서는 변수들의 평균을 구하면, 각 연구참여자들의 변수들이 얼마나 평균에 가까운지 측정할 수 있다(=편차). 그렇기에, 분석할 변수들이 연속형이어야한다. 클러스터 분석에 대표적인 사례인 K-means을 생각해보면, 범주형변수로 k-means을 돌리면, 각 범주형변수들의 L2 distance가 잘 측정이 안될 수도 있다. L2은 유클리디언 공간에서의 크기를 의미하기 때문이다[3]. 반면 LCA은 분석할 변수가 범주형이다(최근엔 범주/연속 둘다 쓸 수 있는 모형도 있다).

3.     클러스터분석은 각 연구대상자들이 분석후에 A군인지 ,B군인지, C군인지 등 명확하게 그룹이 결정된다. 반면에 LCA은 확률이 주어지기에 명확히 A,B,C인지는 알수없다. 다만, 각 군에 확률만 알 수 있을 뿐이다.

4.     LCA은 모형학적 군집방식이기에, 잠재계층의 군집수를 알아서 정해준다. 자세히는 군집방식에 parameter로 군집 수가 있으며, 데이터(x)을 이용해 파라미터(Θ)을 찾는 likelihood을 이용해서, 잠재계층의 수를 찾는다. K-means은 반면 K을 연구자가 지정해야한다.

 

Case study


1.     아래는 LCA분석의 결과이다. LCA 분석에서 얼마나 모형이 품질이 있는지 AIC, BIC 등으로 평가한다고 했다. 이 중에 군집이 3개까지 AIC, BIC도 낮아지는 폭이 크다. 이 중에 군집이 3개까지 AIC, BIC도 낮아지는 폭이 크다. 군집에 3개라고 가정하고 분석하면된다. latent class 수를 더 키우면 무조건 낮아진다. 직관적으로 N=100일떄, 각 100개가 독립적인 subgroup이라고 생각하면 제일 잘 맞는 모형이다. 우리는 다 각자의 인생을 살듯이?

https://jkan.or.kr/pdf/10.4040/jkan.2019.49.6.701

 

2. 세 그룹으로 나뉘어진 것을 알았으니, 각 군에 이름을 붙여본다. 이 간호학과 선생님들께서는 Current non-drinkers, Binge drinkers, Problem drinkers 라고 네이밍을 줬다. 첫 행은 각 군에 대한 membership에 대한 확률을 나타낸다. Current non drinker은 대부분 24,417명중에 69%정도는 이 군에 할당될 확률이 있단 말이다. 그리고, 이 군에 있는 사람들은Drink during the pas 30 days할 확률은 0.12라는 말이다. 즉 이 변수("Drink during the pas 30 days")인 카테고리컬이다. 이 변수에 해당되냐 안되냐를 보여준다. 반면 Binge drinkers, Problem drinkers에서는 1.00, 1.00이니 무조건 이 군에 속할려면 최근 30일 이내 술마셔야하는 사람이어야 한다는 것이다.

 

[1] Latent Class Analysis: A Guide to Best Practice

[3] 잠재계층분석기법(Latent Class Analysis)을 활용한 영화 소비자 세분화에 관한 연구

반응형

요약


Morphological transformation (형태변형)은 이미지의 형태를 변형하는 작업들 중 하나를 말한다. 이미지의 형태를 변형하는 하는 방법은 여러가지가 있는데, 주로 "입력 이미지"와 "구조적 원소(=커널이미지)"을 이용한 경우를 말한다. 그리고 형태변형은 주로 이진화 이미지(Binary image, 흑백이미지으로 0(흑), 1(백)으로만 이뤄져있는 이미지)를 연산하는 것을 의미한다[1]*. 주로 Erosion, Dilation이 주로 연산이며 Open, closing은 이것들의 변형들이다 . 

*주로라는 것은 이진화 이미지가 아닌 경우도 쓸 수 있다(예, grayscale)

 

 

주로 아래와 같은 연산들이 대표적으로 쓰인다. 아래의 글을 봐도 잘 이해가 안되는데, 예시와 함께 보는 것을 권장한다.

  1. Erosion (부식, 침식, Shrink, Reduce): 이미지의 boundary(가장자리)를 부식시키는 작업. 각 오브젝트의 두께가 줄여진다. [2]
  2. Dilation (확장):이미지의 boundary(가장자리)를 확장시키는 작업. 각 오브젝트의 두께가 커진다.
  3. Opening (오프닝): Erosion후에 Dilation을 적용하는 연산. Erosion으로 노이즈 같은 점을 없애주고 이미지를 확장하기에 noise reduction용으로도 쓰는듯하다.
  4. Closing (클로징): Dilation 후에 Erosion을 적용하는 연산. 반면에 Closing은 Object내에 이미지를 패딩해주는효과가 있다.

위의 모든 단계는 커널(kernel, structuring element 또는 probe 라고도 함)을 이용하는데, 이는 입력이미지와 연산할 매개체를 의미한다. 예를 들어, 입력이미지가 A, 연산이 @, 커널이 B면, A@B의 연산을 한다. 주로 @은 위와 같이 종류가 다양하고, B에 해당하는 커널을 바꾸면서도 여러 종류의 연산이 가능하다.

 

 

알아야할 개념: Kernel, Fit, Hit, Miss. 위의 알고리즘을 설명하기 위해서는 아래의 이미지 처리시 개념을 선행해야한다.


  • Kernel: 이미지 연산에 필요한 구조적인 요소(Structuring element). 이미지인데, 연산을 해줄 0또는 1로만 이뤄진 이미지를 의미한다. 입력이미지를 덮어가면서 연산할 이미지를 의미한다[3].
  • Fit: Kernel 이미지의 픽셀이 모든 입력이미지에 커버가 되는 경우.
  • Hit: Kernel 이미지의 픽셀 중 하나라도, 입력이미지에 커버가 되는 경우.
  • Miss: Kernel 이미지의 픽셀 중 하나라도 입력이미지에 커버가 되지 않는 경우.

Input 이미지와 Kernel이 주어진 경우, Hit, Fit, Miss의 예시

 

 

Erode (침식): 주로 오브젝트의 사이즈를 조금 줄일 때 사용


침식은 수학적인 표현으로는 주로 "$\ominus $"라는 표현을할 수 있다. 침식(Erode)은 모든 형태변형 알고리즘 연산과 같이 2개의 값을 요구한다. 하나는 입력이미지(침식 시킬 이미지, $A$), 또 하나는 커널(Kernel, $B$)이다. A를 커널 B로 침식시킨다는 표현은 $A\ominus B$로 표현할 수 있다.

erosion은 다음의 규칙으로 연산한다.

$A\ominus B$: A와 커널 B가 fit이면 1, 아니면 0을 채운다. 이 과정을 kernel의 중앙부가 0인 지점에따라 계산한다.

import cv2 as cv
import numpy as np

image = np.array(
    [
        [0,0,0,0,0],
        [0,1,1,1,0],
        [0,1,1,1,0],
        [0,1,1,0,0],
        [0,1,0,0,0],
    ],
    np.uint8
)

kernel = np.array(
    [[0,1,0],
     [1,1,1],
     [0,1,0]
     ],
     np.uint8
)

cv.erode(image, kernel)

그림으로 표현하면 다음과 같다. 커널의 중심부가 배경(0)인 지점을 모두 순회하면서 hit인 부분이 있으면 모두 0으로 바꿔준다.

그러면 가운데 하나만 1만 남게된다.

array([[0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

응용 예) 주로 두꺼운 펜으로 작성한 글씨나, 번져서온 이미지인 경우 Erode을 시키면 세밀해진 이미지를 얻을 수 있다.

 

erosion은 다음의 규칙으로 연산한다.

$A\ominus B$: A와 커널 B가 fit이면 1, 아니면 0을 채운다. 이 과정을 kernel의 중앙부가 0인 지점에따라 계산한다.

import cv2 as cv
import numpy as np

image = np.array(
    [
        [0,0,0,0,0],
        [0,1,1,1,0],
        [0,1,1,1,0],
        [0,1,1,0,0],
        [0,1,0,0,0],
    ],
    np.uint8
)

kernel = np.array(
    [[0,1,0],
     [1,1,1],
     [0,1,0]
     ],
     np.uint8
)

cv.erode(image, kernel)

그림으로 표현하면 다음과 같다. 커널의 중심부가 배경(0)인 지점을 모두 순회하면서 hit인 부분이 있으면 모두 0으로 바꿔준다.

그러면 가운데 하나만 1만 남게된다.

array([[0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

응용 예) 주로 두꺼운 펜으로 작성한 글씨나, 번져서온 이미지인 경우 Erode을 시키면 세밀해진 이미지를 얻을 수 있다.

 

 

Dilation (팽창): 주로 오브젝트보다 약간 큰 사이즈의 이미지를 얻기 위함.


방법: Dliation도 erosion과 마찬가지로, kernel에 hit인 부분을 0으로 바꾸는 대신에, 1로 채워준다. 좀 더 큰 이미지의 영역을 확보하기 위함이다.

응용사례: Pill(알약)의 이미지보다 약간 큰 이미지 영역을 확보하기위해서, 이미지를 이진화(Binarization)시킨 다음, Dilation시켜서 좀더 큰 사이즈의 영역을 확보한다. 이는 약의 그림자 등 조금 큰 영역을 확보해서 Dection하는게 더 성능에 유리하기 때문에 이렇게 진행한다 

Pill Detection Model for Medicine Inspection Based on Deep Learning, ref : https://www.mdpi.com/2227-9040/10/1/4/pdf

 

 

Opening:주로 노이즈를 지우기 위해 사용.


응용 예:  이진화한 이미지에서 작은 점(노이즈)를 지울 때 사용한다. 또는 인공지능이 Segmentation한 영역에서 Background이미지에서 지저분하게 잘못 예측한 영역들을 오프닝으로 지울 수 있다.

 

 

 

Closing: 이미지 내 작은 구멍 및 점들은 채우기 위한 방법으로 사용.


의료영역에서 Closing 사용

Unet의 후속모델인 ResnetUnet을 이용하여 심장부와 폐부를 Segmentation했다. 그리고 그 예측결과 중에, predicted Mask처럼 구멍뚤린 부위를 후처리(Closing)을 이용해서 매꿔주어 성능을 향상시킬 수 있다.

 

 

[1] https://en.wikipedia.org/wiki/Binary_image

[2] https://homepages.inf.ed.ac.uk/rbf/HIPR2/erode.htm 

[3] https://www.cs.auckland.ac.nz/courses/compsci773s1c/lectures/ImageProcessing-html/topic4.htm

[4] https://docs.opencv.org/4.x/d9/d61/tutorial_py_morphological_ops.html

 

 

 

 

 

반응형

요약


확률 보정(Probability calibration)은 실제 데이터로 사건이 발생할(=분류할) 이벤트가 확률값처럼나오도록 하는 과정을 의미한다. 가령, 기상청에서 쓰는 강수확률모델이 강수확률이 80%이면, 기상조건이 같은 날짜만 모아서 예측한 경우, 80%만 비가왔어야한다. 이렇듯, 모델이 반환하는 확률값이 (정확히는 확률은 아니지만, 일종의 신뢰도 역할을 한다), 신뢰도 역할을 잘 잘 할 수 있도록 하는 작업을 확률 보정(Probability calibration)이라고 한다.

서론


Sklearn 공식 도큐먼트를보면 1.16에 Probability calibartion에 대해 소개가 되어있다 [1]. ML모델로 얻은 확률(.predict_proba로 얻은 확률)은 우리가 실제로 확률처럼 쓸수 있냐면? 그건 아니다. 이진분류라면, 모델이 반환한 확률값이 0.8이었다면(1=positive case), 실제 positive class중에 80%정도만 모델이 맞추었어야한다. 모델이 반환한 확률이 1이라면? 모델이 1이라고 찍은 데이터들은 100% positive case여야한다.

 

 

켈리브리에이션 커브(Calibation curves): 얼마나 확률이 신뢰도를 잘 대변하고 있는지 파악할 수 있는 다이어그램


아래의 그림을 Calibration curves (=reliabiltiy diagrmas)이라고 한다. 이는 확률 값이 실제로 이진분류상에서 얼마나 신뢰도를 대변할 수 있는지를 보여주는지를 알려주는 그림이다. X축은 모델이 각 평균적으로 내뱉는 확률값(구간화한값인듯), y축은 그 중에 실제 positive가 얼마나 있었는지를 의미한다. 확률이 잘 보정되었다면, 실제 positive case중에 모델이 예측한 케이스의 비율과 동일할 것이다. 즉 이 선은 기울기가 1인 점선처럼 나와야 한다.

각 4가지 모델에 대한 probabiltiy calibaration을 파악하기 위한 비교.

 

위의 그래프를보면, logistic 모델의 경우(녹색) 잘 보정이 되어있고, 다른 방법론들은 그러하지 못하다. GaussianNB은 대다수가 0, 또는 1에 가까운 확률을 내뱉는다. Random Forest의 경우에는 히스토그램이 0.2, 0.9에서 피크치고, Calibration curve에서도 S형을 보인다. 이에 대해서 한 연구자(Niculescu-Mizil and Caruana)가 다음과 같은 설명을 했다. Bagging 또는 random forest의 경우는 확률이 0 이나 1이 나오는 경우는, 유일하게 한가지로, 모든 random forest의 tree(앙상블방법들의 weak learner)가 다 0의 확률을 예측해야 한다. 사실상 조금 어려운 경우이고, 우리가 일부 RF에 노이즈를 주더라도 0에서 멀어질 수밖에 없다[2].

 

 

어떻게 확률를 조정(calibartion)할 수있나? 매핑함수를 하나 더 씌워본다


수식이 직관적이어서 바로 서두에 작성한다.
$p(y_{i}=1|f_{i})$ 을 해줄 후처리 함수하나만 만들면 된다.
국룰이 없다. 단, 모델을 훈련할 때 사용했던, 데이터로는 사용하지 말아야한다. 왜냐하면, 훈련데이터로하면 성능이 워낙 좋기때문에, test 데이터로 찍은 확률과 차이가 있을 수 있기 때문이다. 즉, 본인이 테스트할 데이터를 가지고 예측기에, 각 확률만큼 positive 케이스를 맞추도록 보정하여야한다. 이 보정은 어떤 함수여도 상관이 없다. Sklearn에서는 CalibratiedClassifierCV라는 툴을 제공해서 모델의 확률이 calibration되도록 만들어준다. 대표적으로 쓰이는 방법은 2가지인데, isotonic과 sigmoid 방법이다.


Isotonic: $\sum_{i=1}^{n}(y_{i}-\hat{f_{i})^{2}}$ where $ \hat{f_{i}^{2}} = m(f_{i}) + e_{i} $
- Isotonic 방법은 non-parametric 방법(학습할 파라미터가 없이)으로 확률값을 보정하는 방법이다. $y_{i}$은 $i$번째 샘플의 실제 라벨을 의미하고, $\hat{f_{i})^{2}}$은 calibration된 분류기의 출력을 의미한다. 즉, calibration 시키기위해서 단조증가 함수를 하나 씌워서 만드는 것이다.[3]. 여기에 해당하는 $m$을 하나 학습해야하는데, 이거는 iteration돌면서 윗 식을 최소화할 수 있는 m을 찾는다. 전반적으로 Isotonic이 아래의 Platt calbiration보다 성능이 좋다고 알려져있다.

Sigmoid (=Platt calibration) : $ P(y=1|f) = \frac{1}{1+exp(Af+b)}$
- 확률값(f)을 다시 A, B을 학습시켜서 학습할 수 있도록 하는 방법이다. 이 방법에서는 A, B을 찾기위해서 Gradient descent을 사용한다고 한다.


예제코드


아래와 같이 Calibration plot으로 실제 예측치 중에 몇의 positive case가 있는지 파악할 수 있다. prob_pred은 각 확률의 구간들을 평균낸 것이다. 예를들어 n_bins에 따라서, 하위구간의 확률을 가진 샘플 10개가 평균적으로 얼마의 확률을 가졌는지를 의미한다. 이에 해당하는 실제 positive data point의 수가 prob_true이다.

 

그렇게해서 아래와 같이 CalibrationDisplay함수를 이용해서 plot으로 확률이 어느정도 신뢰도를 대변할 수 있는지 파악할 수 있다 [3].

import sklearn
import numpy as np

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC

x, y = make_classification(
    n_samples=10000, n_features=20, n_informative=2, n_redundant=10, random_state=42
)

x_train, x_test, y_train, y_test = train_test_split(x, y)

base_clf = SVC(probability=True)
base_clf.fit(x_train, y_train)


# Calibriation plot 그리기
from sklearn.calibration import calibration_curve, CalibrationDisplay

y_prob = base_clf.predict_proba(x_test)[:, 1]
prob_true, prob_pred = calibration_curve(y_test, y_prob, n_bins=10)

disp = CalibrationDisplay(prob_true, prob_pred, y_prob)
disp.plot()

 

Calibration은 아래와 같이 진행한다.

from sklearn.calibration import CalibratedClassifierCV
calibriated_clf = CalibratedClassifierCV(base_estimator=base_clf, method="isotonic", cv=2)
calibriated_clf.fit(x_test, y_test)

pred_y = calibriated_clf.predict_proba(x_test)[:, 1]
prob_true, prob_pred = calibration_curve(y_test, pred_y, n_bins=10)

disp = CalibrationDisplay(prob_true, prob_pred, pred_y)
disp.plot()

 

[1] https://scikit-learn.org/stable/modules/calibration.html#probability-calibration
[2] Predicting Good Probabilities with Supervised Learning, A. Niculescu-Mizil & R. Caruana, ICML 2005
https://www.cs.cornell.edu/~alexn/papers/calibration.icml05.crc.rev3.pdf

[3] https://scikit-learn.org/stable/modules/generated/sklearn.calibration.CalibrationDisplay.html#sklearn.calibration.CalibrationDisplay.from_estimator

반응형

쿠버네티스에서 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을 명시한다.

반응형

+ Recent posts