반응형

쿠버네티스에 서비스를 배포하기 위해 사용하는 대표적인 방법중에 하나가 바로 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
,
반응형

Argo Rollout 은 new version pods 와 old version pods 를 어떻게 구분하는 것일까? Rollout 리소스로 생성하면 ReplicaSet 의 형태인 Pod 템플릿 영역인 (spec.template) 과 동일하다. 즉 ReplicaSet 이 Selector 로 지정한 label 로 Pod 를 찾아가는 것이다.

아래 Rollout 1개와 Service 2개를 가지는 sample yaml 파일을 배포하여 동작 방법을 살펴보자.

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollout-bluegreen
spec:
  replicas: 2
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: rollout-bluegreen
  template:
    metadata:
      labels:
        app: rollout-bluegreen
    spec:
      containers:
      - name: rollouts-demo
        image: argoproj/rollouts-demo:blue
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
  strategy:
    blueGreen:
      activeService: rollout-bluegreen-active
      previewService: rollout-bluegreen-preview
      autoPromotionEnabled: false

---
kind: Service
apiVersion: v1
metadata:
  name: rollout-bluegreen-active
spec:
  selector:
    app: rollout-bluegreen
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

---
kind: Service
apiVersion: v1
metadata:
  name: rollout-bluegreen-preview
spec:
  selector:
    app: rollout-bluegreen
  ports:
  - protocol: TCP
    port: 80

Rollout 은 strategy 만 제외하면 ReplicaSet 혹은 Deployment 와 동일하다. 그래서 Rollout 을 배포하면 ReplicaSet 과 Pod 는 Kubernetes Core Resource 타입으로 동일하게 생성되고 조회할 수 있다.

 

.spec.selector.matchLabels 와 .spec.template.metadata.labels 에서 지정한 label 은 동일해야 한다. 이 부분은 ReplicaSet 에서 요구하는 spec 과 같다.

 

strategy 의 blueGreen 에는 rollout-bluegreen-active 와 rollout-bluegreen-preview 라는 service 가 2개가 지정되어 있다. Service 는 DNS 명 처럼 이름으로 IP 를 매칭하는 값을 가지는 기능과 Pod 를 연결하여 load balancing 해주는 대표적인 기능이 있다. rollout-bluegreen-active Service 에는 selector 로 "app: rollout-bluegreen" 값이 지정되어 있는데 이는 Rollout Template 에서 지정된 Pod 의 labels 를 가리킨다.

 

그런데 rollout-bluegreen-active Service 와 rollout-bluegreen-preview 2개 모두 같은 selector 로 같은 Pod 를 보고 있는데 어떻게 new version 과 old version 을 구분할까?

 

배포한 후에 설정들이 어떻게 바뀌는지 살펴보자.

$ kubectl apply -f rollout-blue-green.yaml
$ kubectl argo rollouts get rollout rollout-bluegreen --watch

 

 

rollout 명이 rollout-bluegreen-[해시코드값] 으로 되어 있는데 여기서 해시코드 값이 6565b74f44 은 ReplicaSet 의 해시코드와 동일하다.

$ kubectl get rs rollout-bluegreen

 

 

이 해시코드 값은 Service 에서도 찾아 볼 수 있다.

$ kubectl get svc rollout-bluegreen-active -o yaml
...
apiVersion: v1
kind: Service
metadata:
  name: rollout-bluegreen-active
  namespace: default
spec:
  clusterIP: 10.233.59.133
  clusterIPs:
  - 10.233.59.133
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: rollout-bluegreen
    rollouts-pod-template-hash: 6565b74f44
  sessionAffinity: None
  type: ClusterIP
...

selector 의 label 에 자동으로 rollouts-pod-template-hash: 6565b74f44 이 추가되어 rollout-bluegreen-active Service 는 현재 version 의 rollout 으로 생성된 Pod 를 가리키는 것을 알 수 있다.

이제 이미지를 업데이트 하여 upgrade 배포를 해보자.

$ kubectl argo rollouts set image rollout-bluegreen rollouts-demo=argoproj/rollouts-demo:yellow
$ kubectl argo rollouts get rollout rollout-bluegreen --watch

 

 

revision:2 아래에 rollout-bluegreen-6b5dc99488 이라는 새로운 해시코드 값으로 ReplicaSet 이 생성되었다.

rollout-bluegreen-preview Service 의 selector 로 지정된 labels 을 보면 rollouts-pod-template-hash:6b5dc99488 가 생성되어 new version 의 pod 를 가리키는 것을 알 수 있다.

$ kubectl get svc rollout-bluegreen-preview -o yaml
...
apiVersion: v1
kind: Service
metadata:
  name: rollout-bluegreen-preview
  namespace: default
spec:
  clusterIP: 10.233.44.227
  clusterIPs:
  - 10.233.44.227
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: rollout-bluegreen
    rollouts-pod-template-hash: 6b5dc99488
  sessionAffinity: None
  type: ClusterIP
...

배포를 완료하고 ReplicaSet 을 조회해 보면 이전 해시코드 값을 갖는 ReplicaSet 의 DESIRED, CURRENT, READY 가 0 으로 세팅되어 있는 것을 알 수 있다.

$ kubectl argo rollouts promote rollout-bluegreen
$ kubectl get rs -l app=rollout-bluegreen
NAME                           DESIRED   CURRENT   READY   AGE
rollout-bluegreen-6565b74f44   0         0         0       5h35m
rollout-bluegreen-6b5dc99488   2         2         2       10m

이 방식을 응용하면 Deployment 로도 쉽게 배포 전략을 활용할 수 있다.

반응형
Posted by seungkyua@gmail.com
,
반응형

KubeCon 에서 다른 건 몰라도 이 내용은 꼭 들어야 한다.

 

Kubernetes Cluster 에서 Networking 관점에서 문제가 생길 수 있는 부분들을 설명했다.

 

docs.cilium.io/en/v1.9/intro/#what-is-hubble

 

Introduction to Cilium & Hubble — Cilium 1.9.6 documentation

Protect and secure APIs transparently Ability to secure modern application protocols such as REST/HTTP, gRPC and Kafka. Traditional firewalls operates at Layer 3 and 4. A protocol running on a particular port is either completely trusted or blocked entirel

docs.cilium.io

editor.cilium.io/

 

Network Policy Editor for Kubernetes

editor.cilium.io makes it easy to build, visualize, and make sense of Network Policies, which can then be downloaded as YAML and run in any Kubernetes cluster with a Network Policy-aware CNI.

editor.cilium.io

20210506_2120_How to Break your Kubernetes Cluster with Networking - Thomas Graf, Isovalent.pdf
3.32MB

반응형
Posted by seungkyua@gmail.com
,
반응형

Contour, A High Performance Multitenant Ingress Controller for Kubernetes - MICHAEL MICHAEL, Steve Sloka, Nick Young, & James Peach, VMware

Kubernetes 에서 north-south 네트워크로 Ingress Controller 는 필수 입니다.

대부분은 Nginx Ingress Controller 를 사용할 텐데 CNCF Incubating Project 로 Contour 를 사용할 수 있습니다.

Contour 는 Envoy 를 중심으로 개발되었네요.

 

Craig 가 구글에서 나와서 만든 Heptio 에서 시작한 프로젝트인데 지금은 bitnami 가 지원하고 있는 것 같습니다.

 

 

아키텍처는 다음과 같습니다.

 

 

세부적인 내용은 https://projectcontour.io/ 를 확인하세요.

소스 Contributing 은 https://github.com/bitnami/charts/tree/master/bitnami/contour  이곳입니다. ^^

 

20201119_0500 Contour, A High Performance Multitenant Ingress Controller for Kubernetes - MICHAEL MICHAEL, Steve Sloka, Nick Young, &amp; James Peach, VMware.pptx
4.24MB



반응형
Posted by seungkyua@gmail.com
,
반응형

Istio 에 대한 문의도 있었고 또 모르시는 분들이 계셔서 글을 올립니다.

 

Istio 는 Kubernetes 에만 올릴 수 있고 Control plane 이 여러 pod 로 구성되어 있습니다.

1.4 버전까지는 아래 다이어그램에서 보이듯이 Control 에 해당하는 컴포넌트들이 각각의 프로세스로 실행되었습니다.  

 

하지만 1.5 버전 부터는 (현재는 1.6 버전이 최신입니다) 아래와 같이 New Architecture 로 변경되었습니다.

 

 

 

모든 컴포넌트가 istiod 라는 하나의 프로세스로 합쳐지고 Mixer 가 없어지고 Pilot 이 Mixer 의 기능까지도 함께 수행하는 것으로 되어 있습니다.

 

Istio 에서는 각 서비스를 관리하는 것도 중요하지만 사실 더 중요한 것은 여기 다이어그램에는 표시되지 않은 Ingress traffic 을 처리하는 Gateway 서버입니다. 모든 트래픽을 받아는 주는 관문 역할을 해야 하고, 백엔드의 Envoy Proxy 와도 연계되어야 하기 때문에 Isito 를 도입한다면 중요하게 고려되어야 할 서비스입니다. 

 

Istio 를 설치하는 방법은 아래 두가지 방법으로 하는 것이 편리합니다.

1. Download Istio

https://istio.io/latest/docs/setup/getting-started/

Istio 를 다운받아 istioctl 로 Configure Profile 을 활용하여 설치하는 방법입니다.

 

2. Standalone Operator Install

https://istio.io/latest/docs/setup/install/standalone-operator/

Istio Operator 를 설치하고 이를 기반으로 Istio 를 설치하는 방법입니다.

 

 

반응형
Posted by seungkyua@gmail.com
,
반응형

Kubernetes 를 가르친다고 하면 어떻게 교육을 구성하면 효과적일지 생각해 봤다.

모든 과정이 그렇겠지만 가장 기본적인 내용을 먼저 설명하고 응용, ecosystem 까지 확대하는 순으로 계획해 보았는데, 최소한 5일이 필요한 것 같다.

사실 5일은 부족하지만 그래도 최소한 이정도는 알아야지... 하는 정도가 5일이다.

실습 환경으로 VM (4core, mem 8G, disk 300G)은 4대 (master 1대, node 2대, 스토리지 1대) 가 적절하다.

 

[ Kubernetes 교육 과정 ]

[ 1 day ]
▪ Container 설명 (4H)
  - Container 의 개념 설명
  - Docker, Docker Registry 설치, 실습
  - Dockerfile 작성 실습, 명령어 실습

▪ Kubernetes 설명 (2H)
  - Kubernetes 배경, 아키텍처 설명

▪ Kubernetes 설치 실습 (2H)
  - Kubespray 설명
  - Kubespray 를 활요한 Kubernetes 설치

[ 2 day ]
▪ Kubernetes 활용 (4H)
  - Kubernetes Cli 실습
  - Kubernetes Resource 활용 실습
    - Pod, ReplicaSet, Deployment, StatefulSet, DaemonSet, Job

▪ Kubernetes 네트워크 활용 (4H)
  - Calico 네트워크의 이해
  - Cilium 네트워크의 이해

[ 3 day ]
▪ Kubernetes 고급 활용 (2H)
  - 스토리지 PV (Persistent Volumen) 활용
  - Kubernetes Auto scaling 설치

▪ Helm 설명 (2H)
  - Helm 의 개념, 구조 설명
  - Helm 기본 패키지 생성 실습

▪ DevOps 구축 실습 (4H)
  - Kubernetes 기반 Jenkins 설치
  - Jenkins 를 활용한 Pipeline 구성 실습

[ 4 day ]
▪ Kubernetes Pattern 설명 (4H)
  - Kubernetes 에서 활용될 수 잇는 Pattern 을 설명
    - Init Container, Resource Request/Limit, Scheduling

▪ Kubernetes Programming 실습 (4H)
  - Golang 설명
  - Operator 설명
  - Custom Controller 개발 실습
  - Admission Webhook 설명

[ 5 day ]
▪ Kubernetes Monitoring 실습 (4H)
  - Prometheus / Grafana 설명, 설치, 실습
  - exporter 설명

▪ Kubernetes Logging 실습 (4H)
  - Elastic Search, FluentBit, Kibana 설명, 설치, 실습

 

 

------------------------- 보너스 ----------------------------------


[ 6 day - 한 분야당 2H 보다는 최소 하루가 적절할 듯 ]
▪ Kubernetes Ecosystem 설명 (8H)

  - Cluster-API 설명, 실습
  - Machine Learning Infra - Kubeflow 설명, 실습
  - MSA(Microservice Architecture) - Istio 설명, 실습
  - Continuous Delivery 설명 - ArgoCD 설명, 실습

 

 

반응형
Posted by seungkyua@gmail.com
,
반응형

Kubernetes 는 RBAC 이 디폴트 접근 제어 권한으로 User account 와 RBAC 을 적절히 사용하면 효과적인 접근 제어를 할 수 있습니다.

예를 들어, Kubernetes Cluster 에 접근할 수 있는 사용자 seungkyua 를 새로 만들고 해당 사용자는 seungkyua 네임스페이스에만 접근 가능하게 설정하고 싶은 경우가 있을 수 있습니다.

또한 seungkyua 네임스페이스 접근 안에서도 pods, replicasets, deployments, services 만 생성, 조회, 수정, 삭제권한만 주고 싶을 때도 있습니다.

 

이러한 세팅은 아래와 같은 절차를 거쳐야 합니다. 물론 Kubernetes 는 RBAC 사용으로 세팅되어 있다고 가정합니다.

  1. 신규 namespace 생성
  2. User account 생성 (client key 와 client crt 생성)
  3. 새로운 namespace 에 제한적으로 접근할 수 있는 권한 생성 (role 생성)
  4. 3번의 새로운 User account 와 3 번의 role 을 연결 (rolebinding 생성)
  5. kubectl 이 사용할 새로운 config context 를 만들고 연결

 

1. 신규 naemspace 생성

# kubectl create namespace seungkyua

 

2. 새로운 User account 생성

  • client private key 생성 
# openssl genrsa -out seungkyua.key 2048

 

  • crt 생성을 위한 csr 생성
# openssl req -new -key seungkyua.key -subj "/CN=seungkyua/O=Kubernetes Korea Group" -out seungkyua.csr

 

  • client cert 파일 생성
# openssl x509 -req -in seungkyua.csr -CA /etc/kubernetes/ssl/ca.crt -CAkey /etc/kubernetes/ssl/ca.key -CAcreateserial -out seungkyua.crt -days 10000

 

3. 새로운 namespace 로 접근 role 설정

# vi role.yaml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: seungkyua-manager
  namespace: seungkyua
rules:
  - apiGroups:
      - ""            # core api
      - "extensions"
      - "apps"
    resources:
      - "deployments"
      - "replicasets"
      - "pods"
      - "services"
    verbs:
      - "get"
      - "list"
      - "watch"
      - "create"
      - "update"
      - "patch"
      - "delete"


# kubectl create -f role.yaml

 

4. User 와 Role 을 연결 (rolebinding 생성)

# vi rolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: seungkyua-manager-rolebinding
  namespace: seungkyua
subjects:
  - kind: User         # User or ServiceAccount
    name: seungkyua
roleRef:
  kind: Role
  name: seungkyua-manager
  apiGroup: rbac.authorization.k8s.io
  

# kubectl create -f rolebinding.yaml

 

5. Kubectl config context 생성

  • 인증을 위한 사용자 정보 등록 (key, crt 로 접속)
# kubectl config set-credentials seungkyua --client-certificate=/root/ahnsk/private-certs/seungkyua.crt --client-key=/root/ahnsk/private-certs/seungkyua.key --embed-certs=true

 

  • context 생성
# kubectl config get-clusters NAME cluster.local # kubectl config set-context seungkyua@k1 --cluster=cluster.local --user=seungkyua --namespace=seungkyua

 

  • kubernetes 접속
# kubectl config get-contexts
CURRENT   NAME                             CLUSTER         AUTHINFO           NAMESPACE
*         kubernetes-admin@cluster.local   cluster.local   kubernetes-admin   
          seungkyua@k1                     cluster.local   seungkyua 


# kubectl config use-context seungkyua@k1


# kubectl config get-contexts
CURRENT   NAME                             CLUSTER         AUTHINFO           NAMESPACE
          kubernetes-admin@cluster.local   cluster.local   kubernetes-admin   
*         seungkyua@k1                     cluster.local   seungkyua          seungkyua

 

Kubernetes 에 접속하여 해당 계정이 해당 네임스페이스에만 접속 권한이 있는지 확인

# kubectl get pods

No resources found.



# kubectl get pods -n defaults

Error from server (Forbidden): pods is forbidden: User "seungkyua" cannot list resource "pods" in API group "" in the namespace "defaults"

 

 

반응형
Posted by seungkyua@gmail.com
,