반응형

2018년에 Kubernetes 상에서 돌아가는 ML Platform 인 Kubeflow 를 처음 알게된 후로 관심을 갖고 재미삼아 하다가 2022년에는 업무와 연관되어 일을 해야하기 때문에 중점으로 살펴보고자 한다. (최근에는 ML Platform 보다는 AI Platform 이라고 말하는 사람들이 많은 듯)

 

Kubeflow 를 KubeCon 에서 만나다

Kubeflow 개발자들을 처음 만난 것은 2019년 KubeCon in Europe (Barcelona) 이다. 초기 Kubeflow 는 Goolge 과 Arrito 가 주축이 되어 개발하고 있었는데 마침 초기 버전이 나오면서 Kubeflow 홍보를 시작하던 때 였었다.

Kubeflow 의 주 개발자는 화면에 보이는 구글의 Senior S/W Engineer 인 Jeremy Lewi 다. 당시 저 발표는 간단한 설명후에 데모 형태로 발표가 되었는데 Kubernetes 위에 Kubeflow 를 설치한 상태에서 Jupyter Notebook 을 띄어 Model 을 개발하고, Fairing 으로 Model Training 후 Model Serving 하는 것을 시연으로 보여주었다.

 

영상은 아래를 클릭하면 볼 수 있다.

https://www.youtube.com/watch?v=-GYiatVNemY 

 

저녁에 Arrito 가 주최하는 Kubeflow 개발자 / 사용자와의 저녁 만남이 있었는데 거기서 Jeremy 와 이야기를 해봤을 때는 전형적인 내성적이고 약간은 고지식한 개발자라는 느낌을 받았던 기억이(물론 이건 주관적인 생각). 시간이 좀 지나니 혼자 따로 앉아 있던데. 

 

 

Kubeflow 를 왜 써야 하나?

AI Platform Developer 라면 아래의 그림은 많이 봐왔을 것이다. 정 가운데에 있는 ML Code 는 데이터 사이언티스트들이 모델을 개발하는 코드인데, 이런 개발을 도와주거나 실제 서비스로 구축하기 위해서는 코드 이외에도 많은 Tool / System 들이 필요하다는 의미를 보여준다.  

https://papers.nips.cc/paper/5656-hidden-technical-debt-in-machine-learning-systems.pdf

 

또한 위의 동영상에서도 아래와 같이 설명하고 있다.

 

 

간단하게 살펴보는 Kubeflow Architecture

아키텍처에 대한 설명은 Kubeflow document 사이트를 참고하여 간단히 설명하고자 한다.

https://www.kubeflow.org/docs/started/architecture/

 

 

 

ML 개발 프로세스는 Experimental Phase (실험 단계) 와 Production Phase (운영 단계) 로 나눌 수 있으며 전체 워크플로우는 아래와 같다.

실험 단계

1. 문제를 인식하고 데이터를 수집/분석

2. ML 알고리즘을 선택하여 모델을 코딩

3. 데이터를 가지고 모델 트레이닝

4. 하이퍼 파라미터 튜닝

 

운영 단계

1. 데이터 변환

2. 모델 트레이닝

3. 온라인/배치 예측을 위한 모델 서빙

4. 모델 성능 측정 (결과를 가지고 다시 트레이닝하거나 튜닝)

 

 

단계에 적용되는 Kubeflow 컴포넌트는 아래와 같다. 

 

  • Fairing: 위성발사를 생각해보면 로켓 맨 상단에 중요 내용물인 위성을 보호하기 위해서 Fairing 으로 감싸고 있다. 모델을 안전하게 다른 시스템으로 전달하는 기능으로 python library 를 제공한다.
  • Pipelines: Argo workflow 기반으로 python library 를 제공하여 pipeline 을 구축할 수 있다. 예를 들면 데이터 전처리 등 반복되는 작업을 자동으로 수행할 수 있다. 
  • Katib: 하이퍼 파라미터 튜닝을 자동으로 해주는 기능이다. 아래 그림과 같이 하이퍼 파라미터를 바꾸면서 결과를 그래프와 표로 보여준다.
  • KFServing:  Kubeflow 에서 제공해주는 Model Serving 기능이다.

 

마치며

현재 Kubeflow 의 최신 버전은 v1.4 이다. 2019년 Arrito 개발자들과 만나서 논의했을 때 (당시에 v0.3 인걸로 기억한다) v1.0 으로 올리는 것이 의미있냐고 질문을 받은 적이 있다. 고객이 오픈소스의 안정성을 의심하기 때문에 기능이 조금 미흡하더라도 v1.0 으로 올리는 것이 좋겠다는 의견을 준적이 있었는데 어느덧 버전이 v1.4 까지 나왔다.(물론 그 때 이후 한참 지나서야 v1.0 이 나왔지만)

많은 기업에서 자체 AI Platform 을 만들어서 사용하는 것으로 알고 있는데, 제 개인적인 생각으로는 지금이라도 Kubeflow 로 바꾸는 것이 좋다는 생각이다. 그 이유는 이미 모두 알고 있을 것이다.

반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

일반적으로 go 실행 프로그램을 도커 이미지를 만드는 경우는 많은데 plugin 을 이미지로 만드는 경우는 드문 것 같다. 이번에는 kustomize 의 plugin 을 go 로 작성하여 plugin 이미지를 만들어 본다. (kustomize plugin 을 만드는 방법이 아니라 kustomize plugin 을 도커 이미지로 만드는 내용이다.)

kustomize plugin 소스 다운로드

TACO 에서는 Kubernetes Resource yaml 파일을 kustomize 를 사용하여 만든다. 일반 kubernetes resource type (정확히는 HelmRelease type 임)이 아니라서 이를 위한 plugin 을 만들어 제공하고 있다.

github 으로 부터 소스를 다운 받는다.

$ git clone https://github.com/openinfradev/kustomize-helm-transformer.git

2 step building Dockerfile 작성

이미지 사이즈를 줄이기 위해서 2 step 으로 빌드하는 Dockerfile 을 작성한다.

아래는 Dockerfile2 파일의 첫번째 부분이다.

FROM golang:1.14-alpine3.13 AS builder
LABEL AUTHOR Seungkyu Ahn (seungkyua@gmail.com)

RUN apk update && apk add build-base

ENV HOME /root
ENV GO111MODULE on
ENV GOROOT /usr/local/go
ENV GOPATH $HOME/golang

RUN mkdir -p $HOME/.config/kustomize/plugin/openinfradev.github.com/v1/helmvaluestransformer

WORKDIR $HOME
COPY . $HOME/kustomize-helm-transformer

RUN cat kustomize-helm-transformer/README.md | grep -m 1 "* kustomize" | sed -nre 's/^[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/p' > .kustomize_version
RUN go get sigs.k8s.io/kustomize/kustomize/v3@v$(cat $HOME/.kustomize_version)
RUN mv $GOPATH/bin/kustomize /usr/local/bin/

WORKDIR $HOME/kustomize-helm-transformer/plugin/openinfradev.github.com/v1/helmvaluestransformer/
RUN GOOS=linux GOARCH=amd64 go build -buildmode plugin -o HelmValuesTransformer.so
RUN mv HelmValuesTransformer.so $HOME/
  1. go build 를 위해서 build-base 를 설치한다.
  2. kustomize plugin 을 위해서 $HOME/.config/kustomize/plugin 디렉토리 아래 module 명 디렉토리를 만든다.
  3. kustomize version 을 알아내기 위해서 README.md 파일을 파싱한다.
  4. go get 으로 해당 버전의 kustomize 를 다운받아 /usr/local/bin 에 설치한다.
  5. go build -buildmode plugin 으로 파일을 빌드한다. -o 옵션을 사용하면 원하는 파일명으로 만들 수 있다. go build 명령어 외에 go test 를 통해 unit test 를 수행하면서 so 파일을 생성할 수 도 있다.

다음은 Dockerfile2 의 두번째 부분이다.

FROM alpine:edge
LABEL AUTHOR Seungkyu Ahn (seungkyua@gmail.com)

RUN mkdir -p $HOME/.config/kustomize/plugin/openinfradev.github.com/v1/helmvaluestransformer
COPY --chown=0:0 --from=builder /root/*.so /root/.config/kustomize/plugin/openinfradev.github.com/v1/helmvaluestransformer/
COPY --chown=0:0 --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
WORKDIR /

CMD ["/usr/local/bin/kustomize"]

base 이미지를 scratch 로 할 수 도 있지만 scratch 는 shell 명령을 쓸 수 없기 때문에 alpine 이미지를 사용하였다.

  1. kustomize plugin 을 위해서 $HOME/.config/kustomize/plugin 디렉토리 아래 module 명 디렉토리를 만든다.
  2. 빌드된 kustomize plugin so 파일을 복사한다.
  3. 다운받은 kustomize 파일을 복사한다.

추가 확인 사항

  1. kustomize 를 go get 으로 다운받으면 잘 동작하나 curl 로 바이너리만 받아서 실행하면 에러가 난다. 실제로 이 둘의 binary 사이즈에도 차이가 있는데 이는 추후 원인을 파악해 봐야 겠다.
  2. go build 에 앞서 go mod tidy 와 go mod vender 를 수행하면 이미지를 실행할 때 앞의 1번과 같은 에러가 난다. 이 부분도 추후 확인해야 할 사항이다.
반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

최근 github action 을 workflow CI/CD 로 활용하는 사례가 점점 늘어나고 있다. 이전 글인 "10분만에 만드는 Docker image 저장 자동화"도 github action 을 활용하여 docker registry 에 컨테이너 이미지를 빌드하고 push 하는 방법을 설명하였다.

이번에는 github action 을 client cli 를 활용하여 원하는 시점에 manual 로 호출하는 방법을 알아보자.

1. 새로운 github repository 만들기

먼저 github repository 를 만들어 clone 한다.

$ git clone https://github.com/seungkyua/github-action-sample.git
$ cd github-action-sample

2. workflow action 파일 만들기

github action 을 위한 .github/workflows 디렉토리를 생성하고 action 파일인 hello.yaml 파일을 만든다. 디렉토리 명이 workflow 가 아닌 workflows 이니 실수하지 않게 조심하자.

$ mkdir -p .github/workflow
$ touch .github/workflows/hello.yaml

hello.yaml 은 다음과 같다.

name: Hello

on: 
  workflow_dispatch:
    inputs:
      greeting:
        required: false
        default: 'Hello'
        type: string
      name:
        required: false
        default: 'Seungkyu'
        type: string

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      - name: Run hello.sh
        run: |
          set -xe
          chmod +x .github/workflows/hello.sh
          .github/workflows/hello.sh ${{ github.event.inputs.greeting }} ${{ github.event.inputs.name }}

workflow 에서 parameter 를 받기 위해서는 workflow_dispatch: 를 명시적으로 선언하고 parameter 는 inputs 아래에 기술한다. inputs 아래 나오는 이름이 parameter 명이 되며 필수 여부, 기본값, 타입 등을 지정할 수 있다.

여기서는 'greeting' 과 'name' 이라는 2개의 parameter 를 받는 다고 선언하였다.

다음은 workflow step 인데 첫번째 actions/checkout@v2 는 현재의 github repository 소스를 다운 받는 다는 의미이다. 두번째 name: Run hello.sh 는 run: 으로 shell 을 실행하겠다는 의미이며, 앞 단계에서 소스를 다운받았으니 hello.sh 을 실행할 수 있게 되었으며, 실제로 hello.sh 을 실행하면서 parameter 를 argument 로 호출한다.

parameter 는 ${{ github.event.inputs.파라미터명 }} 과 같이 활용할 수 있다.

다음은 간단산 hello.sh 쉡스크립트 이다.

#!/bin/bash

if [ $# -eq 1 ]; then
  greeting=$1
elif [ $# -eq 2 ]; then
  greeting=$1
  name=$2
fi

echo "[hello.sh] ${greeting} ${name}\n"

이제 해당 파일들을 github 에 push 한다.

3. workflow 호출하기

github workflow client 를 다운 받는다. mac 에서는 brew 로 간단히 설치할 수 있다.

$ brew install gh

github-action-sample 소스를 clone 한 디렉토리에서 gh auth login 으로 github 에 로그인 한다.

$  gh auth login
? What account do you want to log into?  [Use arrows to move, type to filter]
> GitHub.com
  GitHub Enterprise Server

GitHub.com 을 선택하고 엔터를 치면 아래의 내용이 나온다.

? What account do you want to log into? GitHub.com
? You're already logged into github.com. Do you want to re-authenticate? (y/N) y

이미 로그인을 했기 때문에 다시 로그인 하겠다는 의미이며 처음이면 로그인 하라는 메세지가 나온다. y 로 선택하고 로그인을 하자.

? What account do you want to log into? GitHub.com
? You're already logged into github.com. Do you want to re-authenticate? Yes
? What is your preferred protocol for Git operations?  [Use arrows to move, type to filter]
> HTTPS
  SSH

HTTPS 를 선택한다.

? What account do you want to log into? GitHub.com
? You're already logged into github.com. Do you want to re-authenticate? Yes
? What is your preferred protocol for Git operations? HTTPS
? Authenticate Git with your GitHub credentials? (Y/n) y

y 로 Github credentials 로 로그인을 한다.

? What account do you want to log into? GitHub.com
? You're already logged into github.com. Do you want to re-authenticate? Yes
? What is your preferred protocol for Git operations? HTTPS
? Authenticate Git with your GitHub credentials? Yes
? How would you like to authenticate GitHub CLI?  [Use arrows to move, type to filter]
  Login with a web browser
> Paste an authentication token

token 으로 로그인을 한다.

? What account do you want to log into? GitHub.com
? You're already logged into github.com. Do you want to re-authenticate? Yes
? What is your preferred protocol for Git operations? HTTPS
? Authenticate Git with your GitHub credentials? Yes
? How would you like to authenticate GitHub CLI? Paste an authentication token
Tip: you can generate a Personal Access Token here https://github.com/settings/tokens
The minimum required scopes are 'repo', 'read:org', 'workflow'.
? Paste your authentication token: ****************************************

정상적으로 login 이 되면 아래와 같이 표시된다.

- gh config set -h github.com git_protocol https
✓ Configured git protocol
✓ Logged in as seungkyua

로그인 후에 gh workflow list 명령어로 workflow 를 조회하면 아래와 같이 Hello 란 workflow 가 있음을 알 수 있다.

➜ github-action-sample git:(main) gh workflow list
Hello  active  14639329

이를 실행해 본다.

$ gh workflow run hello.yaml -f greeting=Welcome -f name=Seungkyu

실행 중인 workflow 를 조회할 수 있다.

$ gh run list --workflow=hello.yaml
STATUS  NAME            WORKFLOW  BRANCH  EVENT              ID          ELAPSED  AGE
*       Initial commit  Hello     main    workflow_dispatch  1390917347  7s       0m

완료된 후의 workflow 를 조회하면 STATUS 가 완료로 체크되어 있다.

$ gh run list --workflow=hello.yaml
STATUS  NAME            WORKFLOW  BRANCH  EVENT              ID          ELAPSED  AGE
✓       Initial commit  Hello     main    workflow_dispatch  1390917347  15s      2m

웹 브라우저로 repository https://github.com/seungkyua/github-action-sample 에 접속하여 Action 탭의 build 를 클릭해보면 아래의 화면 처럼 '[hello.sh] Welcome Seungkyu\n' 가 출력되어 있음을 알 수 있다.

 

 

 

 

4. 활용법

manual 하게 github action 을 호출하는 부분은 다른 workflow tool 과 같이 사용할 때가 많다. 예를 들면 argo workflow 에서 하나의 단계로 github action 을 필요한 시점에 호출하여 결과를 활용하는 등으로 사용할 수 있다.

github action 은 github 의 리소스를 활용하므로 CI 에서 많은 리소스가 필요할 때는 github action 을 같이 활용하는 것도 좋은 대안이 될 것이다.

반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

애플리케이션을 개발하면서 원하는 시점에 자동으로 Docker image 를 build 하고 이를 docker hub 저장소에 push 할 수 있는 방법을 소개한다. 보통은 이를 CI 로 구축하는데 CI 시스템을 따로 구축하기에는 시간과 비용이 들고 이를 유지보수 하기에도 귀찮은 면이 있다. 만약 Github 을 사용하고 있다면 10분 만에 Github Action 으로 이를 쉽게 구현할 수 있다.

 

1. Dockerfile 만들기

가장 먼저 해야할 일은 도커 이미지 빌드를 위한 Dockerfile 을 만드는 것이다. golang 으로 개발하고 있다면 아래와 같이 만들 수 있을 것이다. 어느 정도 최적화를 고려했지만 완전히 최적화한 부분은 아니니 자신의 환경에 맞게 변경해야 한다. Dockerfile 의 위치는 $PROJECT_HOME 디렉토리 바로 아래이다.

FROM golang:1.16.3-stretch AS builder
LABEL AUTHOR Seungkyu Ahn (seungkyua@gmail.com)

RUN mkdir -p /build
WORKDIR /build

COPY . .
RUN go mod tidy && go mod vendor
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/server ./cmd/server

RUN mkdir -p /dist
WORKDIR /dist
RUN cp /build/bin/server ./server


FROM golang:alpine3.13

RUN mkdir -p /app
WORKDIR /app

COPY --chown=0:0 --from=builder /dist /app/
EXPOSE 9111

ENTRYPOINT ["/app/server"]
CMD ["-port", "9110"]

 

2. Github Action 파일 만들기

Github Action 은 규칙에 맞게 yaml 을 파일은 정해진 위치에 넣으면 Github 에서 알아서 실행시켜 준다. Action 을 실행시키는 시점은 소스가 github 에 push 되는 순간이며, 어느 branch 에 push 되었는지, Pull Request 를 했을 때 등의 실행 조건을 정의 할 수 있다.

$PROJECT_HOME 디렉토리에서 다음과 같이 디렉토리를 만든다.

$ mkdir -p .github/workflows

 

github action yaml 은 아래와 같다.

$ vi .github/workflows/deploy-image.yml

name: Build and Push Docker Image
on:
  push:
    branches:
      - main
jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v1

    - name: Login to DockerHub
      uses: docker/login-action@v1
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    - name: Build and push
      id: docker_build
      uses: docker/build-push-action@v2
      with:
        push: true
        tags: seungkyua/tks-contract:latest

처음의 on 은 해당 action 이 수행될 조건을 의미한다. push - branches - main 은 main branch 에 push 가 일어나면 수행된다는 의미이다. 그렇기 때문에 요청된 PR 을 merge 하거나 main branch 에 직접 push 하면 해당 action 이 trigger 된다.

모든 branch 의 push 에 대해서 동작하게 하려면 '*' 로 하면 된다.

 

job 은 pipeline 을 구성하는 부분이다. steps 처음을 보면 "name: Checkout" 부분에 uses: actions/checkout@v2 로 되어 있는데 이는 github action 으로 정의된 모듈이다. 실체 소스는 https://github.com/actions/checkout 으로 접속하면 볼 수 있으며 해당 기능은 디폴트로 action 이 수행되는 github repository 의 소스 (현재 project 의 소스)를 내려받는 기능이다. 내려 받을 repository 를 바꿀려면 변경 가능하다. (action/checkout 에 접속하여 사용법을 확인하자)

 

"name: Set up Docker Buildx" 부분은 builder driver 나 platform 등을 세팅하는 부분이다. 역시 자세한 부분은 https://github.com/docker/setup-buildx-action 를 확인하자.

 

"name: Login to DockerHub" 는 docker hub 에 접근하기 위해서 id 와 password 를 지정하는 부분이다. 이를 위해서는 github repository 의 settings 로 들어가서 좌측 하단의 secrets >> Actions 에서 Action 용 secret 을 만들어야 한다.

 

 

 

 

 

 

 

docker hub 에 접속할 id 와 password 에 대한 secret 을 만들고 이를 github action 에서 ${{ secrets.DOCKERHUB_USERNAME }} 와 같이 변수로 가져다 쓰는 방식이다.

 

마지막으로 "name: Build and push" 부분은 docker image 를 build 하고 push 하는 부분이다. 이미지 name 과 tag 는 tags 로 지정했다. Dockerfile 의 위치 및 파일명은 file 키로 변경할 수 있다. 아무런 지정을 하지 않으면 해당 프로젝트 디렉토리에 있는 Dockerfile 을 찾는다. 자세한 내용은 https://github.com/docker/build-push-action 를 참조하자.

 

최종적으로 파일들은 아래와 같이 존재하게 된다.

 

 

 

보너스로 한가지 더.

 

main branch 로 merge 될 때 외에도 PR 을 할 때 docker image 를 build 하고 push 할 수 는 없을까? 이미지의 tag 는 latest 가 아니라 PR 을 요청한 source 의 branch 명으로 만들고 싶을 수 있다.

 

이럴 때는 새로운 파일을 만들고 github 의 내장 환경 변수를 사용하면 된다. 앞부분의 on 부분과 마지막 Build and push 부분의 tags 를 다음과 같이 수정하면 된다.

$ vi .github/workflows/deploy-image-with-branch.yml

name: Create and publish a package
on:
  pull_request:
    branches:
      - main
...
...
  - name: Build and push
      id: docker_build
      uses: docker/build-push-action@v2
      with:
        push: true
        tags: seungkyua/tks-contract:${{ github.head_ref }}
반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

Kaggle 을 통해서 Machine Learning 을 배우고 Kaggle Competition 에 참여하는 방법을 알 수 있습니다.

 

https://www.youtube.com/playlist?list=PL98nY_tJQXZnP-k3qCDd1hljVSciDV9_N 

 

Kaggle's 30 Days of ML

 

www.youtube.com

 

반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

Helm chart 의 Life cycle 을 이해하면 조금 더 고급스러운 chart 를 만들 수 있다. 예를 들면 이전 chart 의 애플리케이션에서 postgresql DB를 사용하기 때문에 사용자, 데이터베이스, 테이블을 생성해야 하는 경우를 생각해 보자. 차트가 배포될 때 애플리케이션이 실행되기 이전에 해당 작업들을 할 수 있다면 chart 배포 한 번으로 모든 배포를 끝낼 수 있으니 멋진 일이다.

Helm 에서는 chart 가 실행되기 이전에 혹은, 실행된 이후에 작업을 정의할 수 있도록 hook 기능을 제공한다. 이러한 hook 의 종류는 아래와 같다.

 

Annotation 값 설 명
pre-install Executes after templates are rendered, but before any resources are created in Kubernetes
post-install Executes after all resources are loaded into Kubernetes
pre-delete Executes on a deletion request before any resources are deleted from Kubernetes
post-delete Executes on a deletion request after all of the release's resources have been deleted
pre-upgrade Executes on an upgrade request after templates are rendered, but before any resources are updated
post-upgrade Executes on an upgrade request after all resources have been upgraded
pre-rollback Executes on a rollback request after templates are rendered, but before any resources are rolled back
post-rollback Executes on a rollback request after all resources have been modified
test Executes when the Helm test subcommand is invoked ( view test docs)

 

hook 을 정의하는 것은 Annotation 으로 하며, 앞에서 설명한 시나리오의 경우에는 pre-install hook 을 사용하는 것이 적절하기 때문에 "helm.sh/hook": pre-install 로 설정한다. 또한 이런 단발성 호출의 경우에는 Job 으로 설정하는 것이 좋다.

apiVersion: batch/v1
kind: Job
metadata:
...
  annotations:
    "helm.sh/hook": pre-install
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation

 

hook-weight 는 낮을 수록 우선순위가 높으며, hook-delete-policy 의 before-hook-creation 은 hook 으로 실행된 리소스를 다음 hook 이 실행될 때 까지 지우지 않고 남겨둔다는 의미이다. policy 는 아래와 같이 3가지가 있다.

Annotation Value Description
before-hook-creation Delete the previous resource before a new hook is launched (default)
hook-succeeded Delete the resource after the hook is successfully executed
hook-failed Delete the resource if the hook failed during execution

그럼 postgresql 을 설치하고 사용자, 데이터베이스, 테이블을 생성하는 pre-install hook 을 작성해 보자.

 

 

1. postgresql 설치

bitnami chart repo 에서 postgresql 을 다운받아 설치한다.

$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm repo update
$ helm upgrade -i postgresql bitnami/postgresql --version 10.8.0 -n decapod-db --create-namespace \
--set postgresqlPassword=password \
--set persistence.enabled=true \
--set persistence.storageClass=rbd \
--set persistence.size=10Gi

chart 의 value 값을 필요한 부분만 override 하였다. db user 는 postgres 이며 db password는 password 로 설치했다. 테스트 환경에서는 외부 스토리지를 제공하고 있어 10Gi 로 설정하였다.

$ kubectl get pods -n decapod-db
NAME                      READY   STATUS    RESTARTS   AGE
postgresql-postgresql-0   1/1     Running   0          35h


$ kubectl get svc -n decapod-db
NAME                  TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
postgresql            ClusterIP   10.233.46.14   <none>        5432/TCP   35h
postgresql-headless   ClusterIP   None           <none>        5432/TCP   35h

 

2. pre-install hook 만들기

chart 가 설치될 때 한 번 실행되면 되므로 Job 타입으로 리소스를 작성한다. psql 명령을 사용할 수 있는 컨테이너 이미지를 활용하고 수행할 명령어들은 쉘 스크립트로 작성하였다.

{chart_source_home}/templates/pre-install-job.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "tks-contract.fullname" . }}
  namespace: {{ .Values.namespace }}
  labels:
    {{- include "tks-contract.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": pre-install
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    metadata:
      name: {{ include "tks-contract.fullname" . }}
    spec:
      restartPolicy: Never
      containers:
      - name: pre-install-job
        image: "bitnami/postgresql:11.12.0-debian-10-r44"
        env:
        - name: DB_ADMIN_USER
          value: {{ .Values.db.adminUser }}
        - name: PGPASSWORD
          value: {{ .Values.db.adminPassword }}
        - name: DB_NAME
          value: {{ .Values.db.dbName }}
        - name: DB_USER
          value: {{ .Values.args.dbUser }}
        - name: DB_PASSWORD
          value: {{ .Values.args.dbPassword }}
        - name: DB_URL
          value: {{ .Values.args.dbUrl }}
        - name: DB_PORT
          value: "{{ .Values.args.dbPort }}"
        command:
        - /bin/bash
        - -c
        - -x
        - |
          # check if ${DB_NAME} database already exists.
          psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -lqt | cut -d \| -f 1 | grep -qw ${DB_NAME}
          if [[ $? -ne 0 ]]; then
            psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -c "CREATE DATABASE ${DB_NAME};"
          fi

          # check if ${DB_USER} user already exists.
          psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -tc '\du' | cut -d \| -f 1 | grep -qw ${DB_USER}
          if [[ $? -ne 0 ]]; then
            psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -c "create user ${DB_USER} SUPERUSER password '${DB_PASSWORD}';"
          fi

          # check if contracts table in tks database already exists.
          psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER} -d ${DB_NAME} -tc '\dt' | cut -d \| -f 2 | grep -qw contracts
          if [[ $? -ne 0 ]]; then
            echo """
              \c ${DB_NAME};
              CREATE TABLE contracts
              (
                  contractor_name character varying(50) COLLATE pg_catalog."default",
                  id uuid primary key,
                  available_services character varying(50)[] COLLATE pg_catalog."default",
                  updated_at timestamp with time zone,
                  created_at timestamp with time zone
              );
              CREATE UNIQUE INDEX idx_contractor_name ON contracts(contractor_name);
              ALTER TABLE contracts CLUSTER ON idx_contractor_name;
              INSERT INTO contracts(
                contractor_name, id, available_services, updated_at, created_at)
                VALUES ('tester', 'edcaa975-dde4-4c4d-94f7-36bc38fe7064', ARRAY['lma'], '2021-05-01'::timestamp, '2021-05-01'::timestamp);

              CREATE TABLE resource_quota
              (
                  id uuid primary key,
                  cpu bigint,
                  memory bigint,
                  block bigint,
                  block_ssd bigint,
                  fs bigint,
                  fs_ssd bigint,
                  contract_id uuid,
                  updated_at timestamp with time zone,
                  created_at timestamp with time zone
              );
            """ | psql -h ${DB_URL} -p ${DB_PORT} -U ${DB_ADMIN_USER}
          fi
{chart_source_home}/values.yaml

args:
  port: 9110
  dbUrl: postgresql.decapod-db.svc
  dbPort: 5432
  dbUser: tksuser
  dbPassword: password

db:
  adminUser: postgres
  adminPassword: password
  dbName: tks

 

컨테이너에서 필요한 값들(db superuser admin 과 password, db url, db port 등)은 env 로 전달한다. 참고로 postgresql 은 psql 로 접속할 때 패스워드를 전달하는 아규먼트가 없다. 대신 환경 변수로 다음과 같이 설정하면 패스워드를 사용하여 접속할 수 있다. ($ export PGPASSWORD=password)

  1. 먼저 DB 에 해당 database 가 존재하는 조회하고 없으면 database 를 생성한다.
  2. DB User 가 존재하는지 조회하고 없으면 새로운 DB User 를 생성한다.
  3. Table 이 존재하는지 조회하고 없으면 Table 을 생성한다. Table 을 생성하기 위해 psql 로 접속 시에 해당 database 에 접속할 수 있지만 (-d database명 옵션 사용), "\c database명" 으로 database 를 선택할 수 있다는 것을 보여주기 위해 이 부분을 추가하였다.

 

chart 를 배포하면 다음과 같이 job 이 실행됨을 알 수 있다.

$ kubectl get pods -n tks
NAME                               READY   STATUS      RESTARTS   AGE
tks-contract-h5468                 0/1     Completed   0          31h


$ kubectl logs tks-contract-h5468 -n tks
+ psql -h postgresql.decapod-db.svc -p 5432 -U postgres -lqt
+ cut -d '|' -f 1
+ grep -qw tks
+ [[ 0 -ne 0 ]]
+ psql -h postgresql.decapod-db.svc -p 5432 -U postgres -tc '\du'
+ cut -d '|' -f 1
+ grep -qw tksuser
+ [[ 0 -ne 0 ]]
+ psql -h postgresql.decapod-db.svc -p 5432 -U postgres -d tks -tc '\dt'
+ cut -d '|' -f 2
+ grep -qw contracts
+ [[ 0 -ne 0 ]]
반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

쿠버네티스에 서비스를 배포하기 위해 사용하는 대표적인 방법중에 하나가 바로 Helm chart 이다. 한마디로 말해서 Helm chart 는 쿠버네티스 용도의 패키징된 s/w 라 할 수 있다. Helm chart 문법은 go template 을 활용하였기 때문에 go template 을 안다면 조금 더 쉽게 이해할 수 있다.

여기서 설명하는 내용은 사전에 쿠버네티스 클러스터가 있으며 kubectl 로 api 에 접근할 수 있는 환경이 있다는 것을 가정한다.

그럼 도커 이미지를 가지고 쿠버네티스에 Helm 으로 배포할 수 있는 Helm chart 를 만들어 보자.

먼저 Helm (v3)을 설치한다.

$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh

$ helm version
version.BuildInfo{Version:"v3.4.0", GitCommit:"7090a89efc8a18f3d8178bf47d2462450349a004", GitTreeState:"clean", GoVersion:"go1.14.10"}

Helm 버전은 틀릴 수 있는데 v.3.0.0 이상이면 상관없다.

 

1. 기본 코드 생성하기

아무 디렉토리로 이동해서 아래의 명령어로 기본 코드를 생성한다. (여기서 예제는 gRpc 기반의 tks-contract 이라는 프로그램을 기반으로 했다)

$ helm create tks-contract

helm create 명령어는 tks-contract 디렉토리를 만들고 아래와 같은 구조로 디렉토리와 샘플 코드를 자동으로 만들어 준다. 그 구조는 다음과 같다.

$ tree tks-contract    
tks-contract
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 10 files

Chart.yaml 은 chart 에 대한 기본적인 정보가 있는 파일이다. chart 명, chart 버전, chart 설명 등을 적을 수 있으며, 지금은 별로 바꿀 내용이 없다.

charts 디렉토리는 의존성을 관리한다. 예를 들면 DB를 이용하는 웹 애플리케이션 chart 를 만들 때 DB 가 설치되어야 한다면 여기에 chart 를 넣거다 chart repo 에 존재하는 chart 의 링크를 기술 할 수 있다. 이 것도 지금은 바꿀 내용이 없다.

templates 디렉토리는 가장 중요한 디렉토리다. 이 디렉토리 안에는 쿠버네티스 리소스 yaml 파일들이 (예를 들면, Deployment, Service 와 같은 리소스 정의 파일) 위치한다.

values.yaml 파일은 한마디로 변수들을 정의한 파일이다. templates 디렉토리 안에 있는 yaml 파일들의 특정 변수 값을 치환하고 싶을 때 값을 선언해 놓는 곳이다. 일반적으로 key: value 형태로 기술한다.

 

2. Template 작성 방법

먼저 간단한 templates 디렉토리 안의 service.yaml 을 변경해 보자.

apiVersion: v1
kind: Service
metadata:
  name: {{ include "tks-contract.fullname" . }}
  namespace: {{ .Values.namespace }}
  labels:
    {{- include "tks-contract.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.args.port }}
      protocol: TCP
  selector:
    {{- include "tks-contract.selectorLabels" . | nindent 4 }}

go 템플릿을 사용하기 때문에 {{ }} 기호로 값을 치환한다는 것을 알 수 있다. 일반적으로 values.yaml 파일에 정의된 값을 치환할 수 있게 적혀있다. 가령 {{ .Values.namespace }} 는 values.yaml 파일의 namespace 를 키로 하는 값을 가져와서 치환하라는 의미이다. 실제 values.yaml 에는 아래와 같이 되어 있다.

namespace: tks
service:
  type: LoadBalancer
  port: 9110

values.yaml 파일의 service 아래의 type 을 키로 하는 값, 즉 LoadBalancer 라는 값을 가져오기 위해서는 {{ .Values.service.type }} 으로 적어주면 된다.

마지막 라인의 {{- include "tks-contract.selectorLabels" . | nindent 4 }} 와 같이 include 는 정의된 템플릿 호출을 의미한다. 즉 tks-contract.selectorLabels 라는 정의된 템플릿을 호출했다고 할 수 있다. 템플릿 정의들은 보통 templates 디렉토리 아래 _helpers.tpl 파일에 정의하며 define 으로 선언되어 있다. _helpers.tpl 파일의 일부를 살펴보자.

{{/*
Selector labels
*/}}
{{- define "tks-contract.selectorLabels" -}}
app.kubernetes.io/service: tks
app.kubernetes.io/name: {{ include "tks-contract.name" . }}
{{- end }}

{{/* */}} 은 주석처리이다. 그 아래에 {{- define "tks-contract.selectorLabels" -}} ... {{- end }} 은 템플릿 정의이다.

{{- include "tks-contract.selectorLabels" . | nindent 4 }} 에서 {{- 는 맨앞에 쓰였을 때 {{ }} 코드가 차지하는 영역의 줄바꿈과 공백을 없애라는 뜻이며 . 은 values.yaml 에 있는 모든 변수들을 아규먼트로 넘긴다는 뜻이다. 마지막으로 | 는 shell 에서의 파이프라인과 동일하고 nindent 4 는 결과를 프린트 할 때 공백 4개를 멀티라인으로 계속 들여쓰라는 의미이다.

정리해 보면 service.yaml 과 _helper.tpl 파일을 이 활용되어 아래와 같이 작동된다는 것을 알 수 있다.

## service.yaml
  selector:
    {{- include "tks-contract.selectorLabels" . | nindent 4 }}

+

## _helpers.tpl
{{- define "tks-contract.selectorLabels" -}}
app.kubernetes.io/service: tks
app.kubernetes.io/name: {{ include "tks-contract.name" . }}
{{- end }}


================ 결과 ===============
  selector:
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract

app.kubernetes.io/service: tks 와 app.kubernetes.io/name: tks-contract 가 들여쓰기 4 만큼 프린트 됐다. 물론 _helpers.tpl 내의 템플릿 정의를 찾아보면 {{ include "tks-contract.name" . }} 의 결과 값은 tks-contract 이다.

그리고 NOTES.txt 에 적힌 내용은 chart 가 배포되고 나서 터미널에 프린트된다.

 

3. 전체 소스 작성

코드를 원하는 값으로 수정하고 배포될 때의 최종 yaml 이 어떻게 될지 확인해보자.

먼저 Chart.yaml 이다.

$ vi tks-contract/Chart.yaml
apiVersion: v2
name: tks-contract
description: A Helm chart for tks-contract
type: application
version: 0.1.0
appVersion: 0.1.0

_helpers.tpl 은 selectorLabels 를 원하는 값을 넣기 위해서 49 라인만 수정하였다.

$ vi tks-contract/_helpers.tpl
...
{{/*
Selector labels
*/}}
{{- define "tks-contract.selectorLabels" -}}
app.kubernetes.io/service: tks
app.kubernetes.io/name: {{ include "tks-contract.name" . }}
{{- end }}
...

deployment.yaml 은 container 의 args 값을 추가했다.

$ vi tks-contract/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "tks-contract.fullname" . }}
  namespace: {{ .Values.namespace }}
  labels:
    {{- include "tks-contract.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "tks-contract.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      labels:
        {{- include "tks-contract.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ include "tks-contract.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: tks-contract
              containerPort: {{ .Values.args.port }}
              protocol: TCP
          command:
            - /app/server
          args: [
            "-port", "{{ .Values.args.port }}",
            "-dbhost", "{{ .Values.args.dbUrl }}",
            "-dbport", "{{ .Values.args.dbPort }}",
            "-dbuser", "{{ .Values.args.dbUser }}",
            "-dbpassword", "{{ .Values.args.dbPassword }}",
            "-info-address", "{{ .Values.args.tksInfoAddress }}",
            "-info-port", "{{ .Values.args.tksInfoPort }}"
          ]
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}

조건절은 {{- if }} 혹은 {{- if not }} {{ end }} 로 사용할 수 있고 {{- with 키 }} {{ end }} 는 with 절 안에서는 구조상 해당 키 아래의 값들을 사용하겠다는 의미이다. {{- toYaml 키 }} 는 키의 값을 그대로 Yaml 로 프린트 해 준다.

nodeSelector 를 예를 들어 보자.

## deployment.yaml
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}

+

## values.yaml
nodeSelector:
  taco-tks: enabled


============ 결과 =================
      nodeSelector:
        taco-tks: enabled

values.yaml 에 nodeSelector 키 아래에 taco-tks: enabled 라는 값을 그대로 toYaml 로 프린트 한다.

service.yaml 전체는 다음과 같다.

$ vi tks-contract/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "tks-contract.fullname" . }}
  namespace: {{ .Values.namespace }}
  labels:
    {{- include "tks-contract.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.args.port }}
      protocol: TCP
  selector:
    {{- include "tks-contract.selectorLabels" . | nindent 4 }}

values.yaml 에 전체 값을 넣는다.

$ vi tks-contract/values.yaml

replicaCount: 1

namespace: tks

image:
  repository: docker.io/seungkyu/tks-contract
  pullPolicy: Always
  tag: "latests"

imagePullSecrets: []
nameOverride: "tks-contract"
fullnameOverride: "tks-contract"

serviceAccount:
  create: true
  annotations: {}
  name: "tks-info"

args:
  port: 9110
  dbUrl: postgresql.decapod-db.svc
  dbPort: 5432
  dbUser: tksuser
  dbpassword: tkspassword
  tksInfoAddress: tks-info.tks.svc
  tksInfoPort: 9110

podAnnotations: {}

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  type: LoadBalancer
  port: 9110

ingress:
  enabled: false
  annotations: {}
  hosts:
    - host: chart-example.local
      paths: []
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  # targetMemoryUtilizationPercentage: 80

nodeSelector:
  taco-tks: enabled

tolerations: []

affinity: {}

마지막으로 배포후 프린트될 내용은 NOTES.txt 을 수정한다.

$ vi tks-contract/templates/NOTES.txt
TKS-Contract
{{- if contains "LoadBalancer" .Values.service.type }}
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get --namespace {{ .Values.namespace }} svc -w {{ include "tks-contract.fullname" . }}'
  export SERVICE_IP=$(kubectl get svc --namespace {{ .Values.namespace }} {{ include "tks-contract.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
  gRPC Call => $SERVICE_IP:{{ .Values.service.port }}
{{- end }}

 

4. 결과 확인

작성된 chart 는 아래 명령어로 최종 yaml 이 어떻게 변환되어 배포되는지 dry-run 으로 확인할 수 있다.

$ helm upgrade -i tks-contract ./tks-contract --dry-run --debug

---
# Source: tks-contract/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tks-info
  labels:
    helm.sh/chart: tks-contract-0.1.0
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract
    app.kubernetes.io/version: "0.1.0"
    app.kubernetes.io/managed-by: Helm
---
# Source: tks-contract/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: tks-contract
  namespace: tks
  labels:
    helm.sh/chart: tks-contract-0.1.0
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract
    app.kubernetes.io/version: "0.1.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: LoadBalancer
  ports:
    - port: 9110
      targetPort: 9110
      protocol: TCP
  selector:
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract
---
# Source: tks-contract/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tks-contract
  namespace: tks
  labels:
    helm.sh/chart: tks-contract-0.1.0
    app.kubernetes.io/service: tks
    app.kubernetes.io/name: tks-contract
    app.kubernetes.io/version: "0.1.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/service: tks
      app.kubernetes.io/name: tks-contract
  template:
    metadata:
      labels:
        app.kubernetes.io/service: tks
        app.kubernetes.io/name: tks-contract
    spec:
      serviceAccountName: tks-info
      securityContext:
        {}
      containers:
        - name: tks-contract
          securityContext:
            {}
          image: "docker.io/seungkyu/tks-contract:latests"
          imagePullPolicy: Always
          ports:
            - name: tks-contract
              containerPort: 9110
              protocol: TCP
          command:
            - /app/server
          args: [
            "-port", "9110",
            "-dbhost", "postgresql.decapod-db.svc",
            "-dbport", "5432",
            "-dbuser", "tksuser",
            "-dbpassword", "",
            "-info-address", "tks-info.tks.svc",
            "-info-port", "9110"
          ]
          resources:
            {}
      nodeSelector:
        taco-tks: enabled

NOTES:
TKS-Contract
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get --namespace tks svc -w tks-contract'
  export SERVICE_IP=$(kubectl get svc --namespace tks tks-contract --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
  gRPC Call => $SERVICE_IP:9110
반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

이전 글은 Cluster API 가 어떤 것이고 어떻게 동작하는지를 알아봤다면 이번에는 AWS 에 Cluster API를 이용하여 Kubernetes 를 쉽게 설치하는 방법을 설명한다. 사실 AWS를 어느 정도 알고있어야 (특히 VPC, Subnet, Route Table, Nat Gateway, Internet Gateway 같은 네트워크) 해당 내용들을 이해하기가 쉽기 때문에 Cluster API 로 구현되는 최종 Kubernetes Cluster 구성에 대해서 먼저 설명하겠다.

 

 

특정 리전 (여기서는 서울)에 3개의 가용존(AZ1~3)에 Kubernetes Master 와 Node 가 설치되게 된다. 하나의 AZ에는 public subnet 과 private subnet 이 만들어지며 public subnet은 인터넷과 통하는 Internet Gateway 와 연결되어 있다. 네트워크 통신을 위해서는 Route Table 을 통해서 default gateway (0.0.0.0/0)를 설정해야 하는데 public subnet 은 Internet Gateway 를 private subnet 은 EIP 가 할당되어 있는 NAT gateway 를 설정한다.

 

AWS 에서는 Internet Gateway 와 연결되어 있는데 subnet 을 public subnet 으로 명명하며 public IP 가 할당되는 자원들은 모두 public subnet 에 생성한다. 그 외의 subnet 은 private subnet 이라 명명하는데, 이 때 외부 outbound 연결을 하기 위해서는 public subnet 에 NAT Gateway 를 만들어 활용한다. 보라색 점선이 Internet Gateway 와 NAT Gateway 로 default gateway 를 설정한 내용을 보여준다.

 

요약하면 AWS 에서의 네트워크는 Subnet, Route table, Gateway (Internet 혹은 NAT) 로 구성된다.

 

모든 VM (Control plane과 Node) instance는 private subnet에 생성되어 외부에서 직접 VM 으로 접속하는 경로를 차단한다. 필요에 따라 외부의 Inbound 를 트래픽을 가능하게 하려면 Load Balancer 를 통해서 연결한다(물론 Internal LB도 가능하다). Load Balancer 가 private subent 의 VM 에 연결되기 위해서는 public subnet 이 LB에 등록되어야 한다. 다이어그램에서 이를 표현한 것이 초록색 점선이다. LB 와 Control plane 을 연결한 것은 API Server 가 LB 에 등록되어 로드밸랜싱된다는 의미이며, 고객 서비스의 연결은 Node VM 에 연결될 수 있다. 이 때 Service 리소스의 Type 을 LoadBalancer 로 지정하면 자동으로 연결된다.

 

이 기능들이 가능한 이유는 Kubernetes 에 Cloud provider controller 가 내장되어 있기 때문이다. 하지만 이 기능은 deprecated 될 예정으로 버전 1.23 부터는 소스가 분리될 예정이다.

 

또 한가지 일반적인 방법으로 설치하면 그림의 내용과 같이 모든 Node VM 이 AZ1 의 private subnet 에 몰려서 생성된다. 이는 가용성에 문제가 될 수 있으며 이를 해결하기 위해서는 아직은 experimental 버전인 MachinePool 기능을 사용해야 한다.

이제 본격적으로 Cluster API 로 Kubernetes Cluster 를 설치해 보자.

 

이전 글을 보면 Management cluster 에 Cluster API Controller 를 설치한 후 Custom Resource (CR) 를 생성하면 자동으로 Workload cluster 가 생성된다는 것을 설명하였다. 이를 잘 기억해 두고 Management cluster 는 존재한다는 가정하에 시작한다(인터넷에서 조회해보면 Kind 로 쉽게 Management cluster 를 구성하는 방법을 찾을 수 있다).

 

1. clusterctl 설치

clusterctl 은 Managed cluster 에 Cluster API controller 를 설치하고 Workload cluster 를 위한 CR 생성을 도와주는 도구이다.

$ curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v0.3.20/clusterctl-linux-amd64 -o clusterctl
$ chmod +x ./clusterctl
$ mv ./clusterctl /usr/local/bin/clusterctl
$ clusterctl version
=== output ===
clusterctl version: &version.Info{Major:"0", Minor:"3", GitVersion:"v0.3.20", GitCommit:"ea9dc4bdc2a9938325aab3817ece3e654873aaab", GitTreeState:"clean", BuildDate:"2021-06-30T22:10:51Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}

 

2. clusterawsadm 설치

clusterawsadm 은 aws 에 필요한 Role 과 Policy 를 자동으로 생성해 주는 도구이다.

$ curl -L https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/download/v0.6.6/clusterawsadm-linux-amd64 -o clusterawsadm
$ chmod +x clusterawsadm
$ mv ./clusterawsadm /usr/local/bin/clusterawsadm
$ clusterawsadm version
=== output ===
clusterawsadm version: &version.Info{Major:"0", Minor:"6", GitVersion:"v0.6.6-4-d4593daa95fb96-dirty", GitCommit:"d4593daa95fb961be91dc6db869f26ca4359ebc0", GitTreeState:"dirty", BuildDate:"2021-06-01T20:05:33Z", GoVersion:"go1.13.15", AwsSdkVersion:"v1.36.26", Compiler:"gc", Platform:"linux/amd64"}

 

3. awscli 및 jq 설치

$ curl -L https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.2.13.zip -o awscliv2.zip
$ unzip awscliv2.zip
$ ./aws/install
$ aws --version
=== output ===
aws-cli/2.2.13 Python/3.8.8 Linux/4.4.0-87-generic exe/x86_64.ubuntu.16 prompt/off

$ mkdir -p ~/.aws
$ vi ~/.aws/credentials
[default]
aws_access_key_id=<< access_key_id >>
aws_secret_access_key=<< secret_access_key >>

$ vi ~/.aws/config
[default]
region = ap-northeast-2

 

4. 사전 작업

clusterawsadm 을 활용하여 IAM Role 과 Policy 를 생성한다.

## 설정이 안되어 있을 때 환경 변수로 설정한다.
$ export AWS_REGION=ap-northeast-2
$ export AWS_ACCESS_KEY_ID=<< access_key_id >>
$ export AWS_SECRET_ACCESS_KEY=<< secret_access_key >>

$ clusterawsadm bootstrap iam create-cloudformation-stack

Workload cluster VM 에 접속할 key pair 를 Import 한다.

$ aws ec2 import-key-pair \
  --key-name capi-seungkyu \
  --public-key-material fileb://~/.ssh/id_rsa.pub

 

5. Management cluster 생성

clusterctl 생성에 필요한 환경 변수를 다음과 같이 설정한다.

$ clusterawsadm bootstrap credentials encode-as-profile
=== output ===
<< crendentials >>

$ vi env.sh
export AWS_REGION=ap-northeast-2
export AWS_ACCESS_KEY_ID=<< access_key_id >>
export AWS_SECRET_ACCESS_KEY=<< secret_access_key >>
export AWS_B64ENCODED_CREDENTIALS=<< crendentials >>

$ source ./env.sh

clusterctl init 명령어를 사용하여 Management cluster 를 생성한다.

$ clusterctl init --core cluster-api:v0.3.20 --infrastructure aws:v0.6.6 --bootstrap kubeadm:v0.3.20 --control-plane kubeadm:v0.3.20 -v5
=== output ===
...
Fetching providers
Installing cert-manager
Waiting for cert-manager to be available...
...
Installing Provider="cluster-api" Version="v0.3.20" TargetNamespace="capi-system"
...
Installing Provider="bootstrap-kubeadm" Version="v0.3.20" TargetNamespace="capi-kubeadm-bootstrap-system"
...
Installing Provider="control-plane-kubeadm" Version="v0.3.20" TargetNamespace="capi-kubeadm-control-plane-system"
...
Installing Provider="infrastructure-aws" Version="v0.6.6" TargetNamespace="capa-system"

Your management cluster has been initialized successfully!

You can now create your first workload cluster by running the following:

  clusterctl config cluster [name] --kubernetes-version [version] | kubectl apply -f -

 

6. Workload cluster 생성

필요한 환경변수를 설정하고 clusterctl config 명령어를 이용하여 CR 파일을 생성한다.

$ vi env-workload.sh
export AWS_CONTROL_PLANE_MACHINE_TYPE=t3.large
export AWS_NODE_MACHINE_TYPE=t3.large
export AWS_SSH_KEY_NAME=capi-seungkyu

$ source ./env-workload.sh

$ clusterctl config cluster capi-quickstart \
  -n capi-quickstart \
  --kubernetes-version v1.20.5 \
  --control-plane-machine-count=3 \
  --worker-machine-count=3 \
  > capi-quickstart.yaml

capi-quickstart.yaml 을 확인하면 아래와 같다.

$ cat capi-quickstart.yaml
---
apiVersion: cluster.x-k8s.io/v1alpha3
kind: Cluster
metadata:
  name: capi-quickstart
  namespace: capi-quickstart
spec:
  clusterNetwork:
    pods:
      cidrBlocks:
      - 192.168.0.0/16
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
    kind: KubeadmControlPlane
    name: capi-quickstart-control-plane
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
    kind: AWSCluster
    name: capi-quickstart
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSCluster
metadata:
  name: capi-quickstart
  namespace: capi-quickstart
spec:
  region: ap-northeast-2
  sshKeyName: capi-seungkyu
---
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: KubeadmControlPlane
metadata:
  name: capi-quickstart-control-plane
  namespace: capi-quickstart
spec:
  infrastructureTemplate:
    apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
    kind: AWSMachineTemplate
    name: capi-quickstart-control-plane
  kubeadmConfigSpec:
    clusterConfiguration:
      apiServer:
        extraArgs:
          cloud-provider: aws
      controllerManager:
        extraArgs:
          cloud-provider: aws
    initConfiguration:
      nodeRegistration:
        kubeletExtraArgs:
          cloud-provider: aws
        name: '{{ ds.meta_data.local_hostname }}'
    joinConfiguration:
      nodeRegistration:
        kubeletExtraArgs:
          cloud-provider: aws
        name: '{{ ds.meta_data.local_hostname }}'
  replicas: 3
  version: v1.20.5
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSMachineTemplate
metadata:
  name: capi-quickstart-control-plane
  namespace: capi-quickstart
spec:
  template:
    spec:
      iamInstanceProfile: control-plane.cluster-api-provider-aws.sigs.k8s.io
      instanceType: t3.large
      sshKeyName: capi-seungkyu
---
apiVersion: cluster.x-k8s.io/v1alpha3
kind: MachineDeployment
metadata:
  name: capi-quickstart-md-0
  namespace: capi-quickstart
spec:
  clusterName: capi-quickstart
  replicas: 3
  selector:
    matchLabels: null
  template:
    spec:
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3
          kind: KubeadmConfigTemplate
          name: capi-quickstart-md-0
      clusterName: capi-quickstart
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
        kind: AWSMachineTemplate
        name: capi-quickstart-md-0
      version: v1.20.5
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3
kind: AWSMachineTemplate
metadata:
  name: capi-quickstart-md-0
  namespace: capi-quickstart
spec:
  template:
    spec:
      iamInstanceProfile: nodes.cluster-api-provider-aws.sigs.k8s.io
      instanceType: t3.large
      sshKeyName: capi-seungkyu
---
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3
kind: KubeadmConfigTemplate
metadata:
  name: capi-quickstart-md-0
  namespace: capi-quickstart
spec:
  template:
    spec:
      joinConfiguration:
        nodeRegistration:
          kubeletExtraArgs:
            cloud-provider: aws
          name: '{{ ds.meta_data.local_hostname }}'

 

해당 CR 은 아래 다이어그램 구조로 매칭된다.

 

 

Cluster API 는 공통이 Abstract Class 와 같이 기본을 정의해 놓았으며, Provider 는 실제 Cloud 에 맞는 구현을 정의하였다. AWSCluster 에는 VPC, subnet, Route table 등과 같은 인프라 생성 정보를 가지고 있고 AWSMachineTemplate 은 Kubernetes Control Plane 에 대한 정보를 AWSMachineTemplate 에는 Node 정보를 갖고 있다.

 

CR 을 배포하여 Workload cluster 를 생성한다.

$ kubectl create ns capi-quickstart

$ kubectl apply -f capi-quickstart.yaml
=== output ===
cluster.cluster.x-k8s.io/capi-quickstart created
awscluster.infrastructure.cluster.x-k8s.io/capi-quickstart created
kubeadmcontrolplane.controlplane.cluster.x-k8s.io/capi-quickstart-control-plane created
awsmachinetemplate.infrastructure.cluster.x-k8s.io/capi-quickstart-control-plane created
machinedeployment.cluster.x-k8s.io/capi-quickstart-md-0 created
awsmachinetemplate.infrastructure.cluster.x-k8s.io/capi-quickstart-md-0 created
kubeadmconfigtemplate.bootstrap.cluster.x-k8s.io/capi-quickstart-md-0 created

kubectl 명령어를 위해 아래와 같이 kubeconfig 를 생성한다.

$ clusterctl get kubeconfig capi-quickstart -n capi-quickstart > capi-quickstart.kubeconfig

한가지 중요한 부분은 Cluster API 는 Network 모듈이나, CSI 를 설치해 주지는 않는다. 이는 추가로 설치해야 한다.

$ kubectl --kubeconfig=./capi-quickstart.kubeconfig \
  apply -f https://docs.projectcalico.org/v3.19/manifests/calico.yaml

네트워크 모듈까지 다 설치하면 정상적으로 Kubernetes Cluster 가 설치된 것을 볼 수 있다.

$ kubectl get kubeadmcontrolplane -n capi-quickstart
=== output ===
NAME                            INITIALIZED   API SERVER AVAILABLE   VERSION   REPLICAS   READY   UPDATED   UNAVAILABLE
capi-quickstart-control-plane   true          true                   v1.20.5   3          3       3


$ kubectl --kubeconfig=mycluster.kubeconfig get nodes
=== output ===
NAME                                              STATUS   ROLES                  AGE   VERSION
ip-10-0-143-5.ap-northeast-2.compute.internal     Ready    control-plane,master   23h   v1.20.5
ip-10-0-164-198.ap-northeast-2.compute.internal   Ready    <none>                 23h   v1.20.5
ip-10-0-222-84.ap-northeast-2.compute.internal    Ready    control-plane,master   23h   v1.20.5
ip-10-0-255-19.ap-northeast-2.compute.internal    Ready    <none>                 23h   v1.20.5
ip-10-0-68-113.ap-northeast-2.compute.internal    Ready    <none>                 23h   v1.20.5
ip-10-0-80-79.ap-northeast-2.compute.internal     Ready    control-plane,master   23h   v1.20.5

 

반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

AWS 에서 Kubernetes 사용하는 방법은 Managed Service 인 EKS 서비스를 신청/설치 방법과 일반 VM 에 Kubernetes 를 설치하는 방법이 있다.

VM 에 Kubernetes 를 설치하는 방법은 VM 을 생성하고 필요한 Security Group 을 지정하고 Muti Control plane 에 ELB 를 연결하여 다중 Master 로 사용가능 하도록 세팅해야할 내용이 제법 있다. Kubernetes 자체 설치도 kubeadm 이나 kubespray 같은 툴을 사용해서 설치한다. - 역시 쉽지 않은 내용임에 분명하다.

 

Cluster API 란 ?

지금 소개하는 방법은 Kubernetes Cluster Lifecycle SIG 에서 개발하고 있는 Cluster API 로 Operator Pattern 을 활용하여 Kubernetes 를 설치하는 방법이다. Cluster API 란 Management Kubernetes Cluster 에 Custom Controller 를 설치하고 Custom Resource 를 생성하면 자동으로 Cloud 에 VM 을 생성하고 Kubernetes 를 설치하는 방법이다. Controller 로 관리하기 때문에 VM 이 죽으면 다시 살려주는 장점이 있다. Kubernetes 가 설치된 VM 이 다운되었을 때 Self healing 으로 Recovering 해준다니 정말 멋진 아이디어인 것 같다.

Cluster API 의 시작은 2년 정도 전부터 시작되었는데 초장기에는 PoC 정도였다면 지금은 어느 정도 안정화 되어 사용하기에 문제가 없다.

개념을 보면 Management Kubernetes Cluster 를 설치(Kind, Minikube 등도 가능)하고 여기에 필요한 여러 Custom Controller 를 실행시킨다. 그리고 나서 Custom Resource 를 생성하면 지정된 Cloud 에 VM 을 생성하고 Workload Kubernetes Cluster (고객이 사용할 클러스터) 를 자동으로 생성해 주는 방법이다.

 

Cluster API 구성도

 

그림에서 보면 좌측 맨 위에 Cluster API Controller 가 있다. Cluster API Controller 는 마치 java class 로 치면 Abstraction Class 의 역활과 동일하다. Cluster Pod Network, Control Plane VM 갯수, Node VM 갯수 등을 관리한다.

이렇게 정의를 하면 Boostrap provider Controller 에서 kubeadm 으로 어떻게 Kubernetes 를 설치할 지를 관리한다.

Infrastructure provider Controller 에서는 AWS, Azure, GCP 와 같이 Public Cloud 의 자원 생성을 관리한다.

마지막으로 Control Plane provider Controller 특별하게 Control Plane 의 설치를 관리한다.

우측 위는 Custom Resource 를 보여주고 있다.

 

Cluster API Provider Controller 와 Infrastructure Provider Controller 의 상관 관계를 보면 다음과 같다.

 

Cluster API 는 Infrastructure Provider 에게 이런 식으로 구현을 해야 한다고 알려주는 가이드라고 보면 된다.

이제 세부적으로 하나씩 살펴보자.

 

Cluster API Controller

Cluster API Controller 는 cluster 의 기본 정보를 관리하는 Controller 이다. 이 Controller 가 관리하는 Custom Resource 는 다음과 같이 4개가 있다.

  • clusters
  • machinedeployments
  • machinehealthchecks
  • machines

이 중에서 machinehealthchecks 는 AWS Provider 에서는 사용하고 있지 않으므로 이를 제외하고 나머지 3가지만 살펴보겠다.

clusters CR(Custom Resource) 는 다음과 같은 정보를 가지고 있다.

  • Cluster Pod Network 정보
  • Kubernetes API url 정보
  • Control Plane 을 생성하는 CR 정보
  • Cloud Infrastructure 로 사용되는 CR 정보
  Cluster Network:
    Pods:
      Cidr Blocks:
        192.168.0.0/16
  Control Plane Endpoint:
    Host:  capi-quickstart-apiserver-479433786.ap-northeast-2.elb.amazonaws.com
    Port:  6443
  Control Plane Ref:
    API Version:  controlplane.cluster.x-k8s.io/v1alpha3
    Kind:         KubeadmControlPlane
    Name:         capi-quickstart-control-plane
    Namespace:    capi-quickstart
  Infrastructure Ref:
    API Version:  infrastructure.cluster.x-k8s.io/v1alpha3
    Kind:         AWSCluster
    Name:         capi-quickstart
    Namespace:    capi-quickstart

 

machinedeployments CR 의 정보는 Kubernetes Worker Node 에 대한 일반적인 내용이며 아래의 내용을 포함한다.

  • Bootstrap 으로 사용될 Kubeadm Config Template
  • Infrastructure Provider 에 대한 VM 정보 Template
  • Cluster name 및 Kubernetes Version 정보
  Cluster Name:               capi-quickstart
  Min Ready Seconds:          0
  Progress Deadline Seconds:  600
  Replicas:                   3
  Revision History Limit:     1
  Selector:
    Match Labels:
      cluster.x-k8s.io/cluster-name:     capi-quickstart
      cluster.x-k8s.io/deployment-name:  capi-quickstart-md-0
  Strategy:
    Rolling Update:
      Max Surge:        1
      Max Unavailable:  0
    Type:               RollingUpdate
  Template:
    Metadata:
      Labels:
        cluster.x-k8s.io/cluster-name:     capi-quickstart
        cluster.x-k8s.io/deployment-name:  capi-quickstart-md-0
    Spec:
      Bootstrap:
        Config Ref:
          API Version:  bootstrap.cluster.x-k8s.io/v1alpha3
          Kind:         KubeadmConfigTemplate
          Name:         capi-quickstart-md-0
      Cluster Name:     capi-quickstart
      Infrastructure Ref:
        API Version:  infrastructure.cluster.x-k8s.io/v1alpha3
        Kind:         AWSMachineTemplate
        Name:         capi-quickstart-md-0
      Version:        v1.20.5

 

machines CR 은 Kubernetes Control Plane VM 별 정보와 Kubernetes Worker Node VM 별 정보를 갖고 있는데 둘다 아래의 정보를 동일하게 갖고 있다.

  • Boostrap 으로 KubeadmConfig CR 정보
  • Infrastructure 로서 AWSMachine CR 정보
  Bootstrap:
    Config Ref:
      API Version:     bootstrap.cluster.x-k8s.io/v1alpha3
      Kind:            KubeadmConfig
      Name:            capi-quickstart-md-0-7f4xk
      Namespace:       capi-quickstart
      UID:             7b095ef9-3380-45cc-950e-9bce8602f954
    Data Secret Name:  capi-quickstart-md-0-7f4xk
  Cluster Name:        capi-quickstart
  Infrastructure Ref:
    API Version:  infrastructure.cluster.x-k8s.io/v1alpha3
    Kind:         AWSMachine
    Name:         capi-quickstart-md-0-dcdcx
    Namespace:    capi-quickstart
    UID:          3383bd5b-1040-4577-a690-741c556d1076
  Provider ID:    aws:///ap-northeast-2a/i-090230ea1d12065ce
  Version:        v1.20.5

 

Bootstrap Provider Controller

Bootstrap provider controller 는 Kubernetes 를 설치하는 kubeadm 관련 정보를 가지고 있다.

  • kubeadmconfigs
  • kubeadmconfigtemplates

kubeadmconfigs 는 kubeadm 으로 kubernetes 를 설치할 때 사용하는 정보로서 Control Plane VM 별 정보와 Work Node VM별 정보를 갖고 있다.

  • Cluster Configuration 으로 provider 정보를 갖고 있다.
  • Join Configuration 으로 Token 정보를 갖고 있다.
  Cluster Configuration:
    API Server:
      Extra Args:
        Cloud - Provider:  aws
    Controller Manager:
      Extra Args:
        Cloud - Provider:  aws
    Dns:
    Etcd:
    Networking:
    Scheduler:
  Join Configuration:
    Control Plane:
      Local API Endpoint:
        Advertise Address:
        Bind Port:          0
    Discovery:
      Bootstrap Token:
        API Server Endpoint:  capi-quickstart-apiserver-479433786.ap-northeast-2.elb.amazonaws.com:6443
        Ca Cert Hashes:
          sha256:de8ef58efd55489531f681066eb77cec21a7d7f8e2a04eb41a06ad38b18d066e
        Token:                        xxxx.xxxxxxxxxxx
        Unsafe Skip CA Verification:  false
    Node Registration:
      Kubelet Extra Args:
        Cloud - Provider:  aws
      Name:                {{ ds.meta_data.local_hostname }}

 

kubeadmconfigtemplates 는 Worker Node 가 join 한 template 정보로 Node 명을 가진다.

  Template:
    Spec:
      Join Configuration:
        Node Registration:
          Kubelet Extra Args:
            Cloud - Provider:  aws
          Name:                {{ ds.meta_data.local_hostname }}

 

Infrastructure Provider Controller

Infrastructure Provider Controller 는 각각의 Cloud 의 API 를 활용하여 리소스를 관리하는 Controller 로 aws 의 경우에는 아래의 정보를 가진다.

  • awsclustercontrolleridentities
  • awsclusterroleidentities
  • awsclusters
  • awsclusterstaticidentities
  • awsfargateprofiles
  • awsmachinepools
  • awsmachines
  • awsmachinetemplates
  • awsmanagedclusters
  • awsmanagedmachinepools

이 중에서 awsclusters, awsmachines, awsmachinetemplate 3가지만 살펴본다.

awsclusters 는 aws 에 설치된 Kubernetes workload cluster 에 대한 정보를 갖고 있다.

  Bastion:
    Allowed CIDR Blocks:
      0.0.0.0/0
    Enabled:  false
  Control Plane Endpoint:
    Host:  capi-quickstart-apiserver-479433786.ap-northeast-2.elb.amazonaws.com
    Port:  6443
  Identity Ref:
    Kind:  AWSClusterControllerIdentity
    Name:  default
  Network Spec:
    Cni:
      Cni Ingress Rules:
        Description:  bgp (calico)
        From Port:    179
        Protocol:     tcp
        To Port:      179
        Description:  IP-in-IP (calico)
        From Port:    -1
        Protocol:     4
        To Port:      65535
    Subnets:
      Availability Zone:  ap-northeast-2a
      Cidr Block:         10.0.0.0/20
      Id:                 subnet-04e972e6bfcb98336
      Is Public:          true
      Nat Gateway Id:     nat-0d456fe6c4dd2ce3d
      Route Table Id:     rtb-0dfccca96ffc00732
      Tags:
        Name:                                                          capi-quickstart-subnet-public-ap-northeast-2a
        kubernetes.io/cluster/capi-quickstart:                         shared
        kubernetes.io/role/elb:                                        1
        sigs.k8s.io/cluster-api-provider-aws/cluster/capi-quickstart:  owned
        sigs.k8s.io/cluster-api-provider-aws/role:                     public
      Availability Zone:                                               ap-northeast-2a
      Cidr Block:                                                      10.0.64.0/18
      Id:                                                              subnet-06f2be3eedb46d622
      Is Public:                                                       false
      Route Table Id:                                                  rtb-00090ad5d1de60b20
      Tags:
        Name:                                                          capi-quickstart-subnet-private-ap-northeast-2a
        kubernetes.io/cluster/capi-quickstart:                         shared
        kubernetes.io/role/internal-elb:                               1
        sigs.k8s.io/cluster-api-provider-aws/cluster/capi-quickstart:  owned
        sigs.k8s.io/cluster-api-provider-aws/role:                     private
      Availability Zone:                                               ap-northeast-2b
      Cidr Block:                                                      10.0.16.0/20
      Id:                                                              subnet-02a9eed4c602fd3a2
      Is Public:                                                       true
      Nat Gateway Id:                                                  nat-050244cb7c9cff368
      Route Table Id:                                                  rtb-020e9366a2da25625
      Tags:
        Name:                                                          capi-quickstart-subnet-public-ap-northeast-2b
        kubernetes.io/cluster/capi-quickstart:                         shared
        kubernetes.io/role/elb:                                        1
        sigs.k8s.io/cluster-api-provider-aws/cluster/capi-quickstart:  owned
        sigs.k8s.io/cluster-api-provider-aws/role:                     public
      Availability Zone:                                               ap-northeast-2b
      Cidr Block:                                                      10.0.128.0/18
      Id:                                                              subnet-03880f2f847b3c21e
      Is Public:                                                       false
      Route Table Id:                                                  rtb-06b52ab77da5c5f2c
      Tags:
        Name:                                                          capi-quickstart-subnet-private-ap-northeast-2b
        kubernetes.io/cluster/capi-quickstart:                         shared
        kubernetes.io/role/internal-elb:                               1
        sigs.k8s.io/cluster-api-provider-aws/cluster/capi-quickstart:  owned
        sigs.k8s.io/cluster-api-provider-aws/role:                     private
      Availability Zone:                                               ap-northeast-2c
      Cidr Block:                                                      10.0.32.0/20
      Id:                                                              subnet-09ccabe6dd862df6a
      Is Public:                                                       true
      Nat Gateway Id:                                                  nat-030068539013d5c64
      Route Table Id:                                                  rtb-0973a222fc772ff76
      Tags:
        Name:                                                          capi-quickstart-subnet-public-ap-northeast-2c
        kubernetes.io/cluster/capi-quickstart:                         shared
        kubernetes.io/role/elb:                                        1
        sigs.k8s.io/cluster-api-provider-aws/cluster/capi-quickstart:  owned
        sigs.k8s.io/cluster-api-provider-aws/role:                     public
      Availability Zone:                                               ap-northeast-2c
      Cidr Block:                                                      10.0.192.0/18
      Id:                                                              subnet-0be3353fd3b6f1637
      Is Public:                                                       false
      Route Table Id:                                                  rtb-0854effe818750d33
      Tags:
        Name:                                                          capi-quickstart-subnet-private-ap-northeast-2c
        kubernetes.io/cluster/capi-quickstart:                         shared
        kubernetes.io/role/internal-elb:                               1
        sigs.k8s.io/cluster-api-provider-aws/cluster/capi-quickstart:  owned
        sigs.k8s.io/cluster-api-provider-aws/role:                     private
    Vpc:
      Availability Zone Selection:    Ordered
      Availability Zone Usage Limit:  3
      Cidr Block:                     10.0.0.0/16
      Id:                             vpc-070a545cb4b967dd7
      Internet Gateway Id:            igw-03a4ee0ca0f8d0f72
      Tags:
        Name:                                                          capi-quickstart-vpc
        sigs.k8s.io/cluster-api-provider-aws/cluster/capi-quickstart:  owned
        sigs.k8s.io/cluster-api-provider-aws/role:                     common
  Region:                                                              ap-northeast-2
  Ssh Key Name:                                                        capi-seungkyu

 

awsmachinetemplates 은 aws vm spec 에 대한 정보를 template 으로 갖고 있다. template 은 control plane 과 worker node 각 1개씩 가진다.

 

control plane template

  Template:
    Spec:
      Iam Instance Profile:  control-plane.cluster-api-provider-aws.sigs.k8s.io
      Instance Type:         t3.large
      Ssh Key Name:          capi-seungkyu

worker node template

  Template:
    Spec:
      Iam Instance Profile:  nodes.cluster-api-provider-aws.sigs.k8s.io
      Instance Type:         t3.large
      Ssh Key Name:          capi-seungkyu

 

awsmachines 은 awsmachinetemplates 으로 연결된 control plane 과 worker node 별 aws VM 에 대한 정보를 갖는다.

 

contol plane vm1

  Ami:
  Cloud Init:
    Secure Secrets Backend:  secrets-manager
  Iam Instance Profile:      control-plane.cluster-api-provider-aws.sigs.k8s.io
  Instance ID:               i-0433d589b68e42cd0
  Instance Type:             t3.large
  Provider ID:               aws:///ap-northeast-2c/i-0433d589b68e42cd0
  Ssh Key Name:              capi-seungkyu

 

Control plane Provider Controller

Control plane provider controller 는 Kubernetes control plane 에 대한 정보를 관리하는 Controller 이다.

  • kubeadmcontrolplanes

kubeadmcontrolplanes 은 말그래도 control plane 의 정보를 담고 있으며 앞에서 살펴본 정보들을 활용한다.

  • Infrastructure Template 으로 AWSMachineTemplate 을 사용
  • Kubeadm Config 로 Init Configuration 과 Join Configuration 정보
  • Replicas 개수 정보
  • Kubernetes version 정보
  Infrastructure Template:
    API Version:  infrastructure.cluster.x-k8s.io/v1alpha3
    Kind:         AWSMachineTemplate
    Name:         capi-quickstart-control-plane
    Namespace:    capi-quickstart
  Kubeadm Config Spec:
    Cluster Configuration:
      API Server:
        Extra Args:
          Cloud - Provider:  aws
      Controller Manager:
        Extra Args:
          Cloud - Provider:  aws
      Dns:
      Etcd:
      Networking:
      Scheduler:
    Init Configuration:
      Local API Endpoint:
        Advertise Address:
        Bind Port:          0
      Node Registration:
        Kubelet Extra Args:
          Cloud - Provider:  aws
        Name:                {{ ds.meta_data.local_hostname }}
    Join Configuration:
      Discovery:
      Node Registration:
        Kubelet Extra Args:
          Cloud - Provider:  aws
        Name:                {{ ds.meta_data.local_hostname }}
  Replicas:                  3
  Rollout Strategy:
    Rolling Update:
      Max Surge:  1
    Type:         RollingUpdate
  Version:        v1.20.5

 

반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요

반응형

 

https://www.youtube.com/watch?v=lIzAMFUKxBE&t=4s

반응형
Posted by seungkyua@gmail.com

댓글을 달아 주세요