연재 시리즈

테라폼 스터디 졸업과제 - kind cluster + argocd

악분 2022. 12. 11. 14:32
반응형

1. 선수지식

이 글은 쿠버네티스 그리고 helm, argocd, minio, argoworkflow, cnpg operator, terraform을 사용할 수 있다는 전제로 설명합니다.

 

2. 프로젝트 작업분리

프로젝트의 작업은 테라폼파트와 argocd파트로 나눠집니다. 테라폼이 쿠버네티스 필수 리소스와 argocd설치 작업을 관리합니다. argocd는 나머지 쿠버네티스 리소스를 관리합니다.

 

2.1 테라폼 파트

  • 테라폼 kind provider로 쿠버네티스를 구축합니다.
  • 테라폼 kubectl proivder로 필수 쿠버네티스 리소스를 설치(예: nginx ingress)합니다.
  • 테라폼 helm provider로 argocd를 설치합니다.

 

2.2 Argocd 파트

  • argocd를 app of apps패턴을 이용하여 나머지 쿠버네티스 리소스를 생성합니다.

 

3. 테라폼에서 Argocd 설정을 관리하지 않는 이유

테라폼 스터디에서 기억에 남는 것 중에 하나가 모든 것을 자동화하는 것은 고민해봐야 한다였습니다. 그래서 테라폼이 어느범위까지 자동화를 다룰 것인가를 생각했고 argocd의 테라폼으로 다루지 않기로 결정했습니다.

 

테라폼 코드로 argocd application 생성과 sync작업을 실행 할 수 있습니다. 하지만, 매번 argocd를 수정하면 테라폼 상태파일도 동기화해줘야 하는 애로사항이 있을 거라고 생각했습니다.

 

테라폼은 한번 쿠버네티스를 구축하면 수정할 일이 없지만 argocd는 전체 쿠버네티스 리소스를 관리하기 때문에 수정할 일이 많습니다. 그러므로 테라폼이 자주 변경되는 argocd내역을 관리하는건 무리가 있다고 생각합니다.

 

4. 1단계: 테라폼 파트 - 쿠버네티스 클러스터 생성

kind provider를 사용하면 쉽게 테라폼에서 kind를 다룰 수 있습니다.

 

4.1 provider 설정

provider.tf파일을 생성하고 provider 코드를 작성합니다.

terraform {
  required_providers {
    kind = {
      source  = "tehcyx/kind"
      version = "0.0.15"
    }
  }
}

provider "kind" {}

 

4.2 클러스터 생성 코드 작성

kubernetes.tf파일을 생성하고 쿠버네티스 클러스터 생성 코드를 작성합니다.

 

kind_cluster 리소스를 사용하여 쿠버네티스 클러스터를 쉽게 생성할 수 있습니다. 가장 최소 argument는 control-plane node, worker node설정입니다. control-plane노드의 도커이미지와 node노드의 도커이미지는 꼭 같아야 오류가 발생하지 않습니다.

resource "kind_cluster" "default" {
  name           = "cluster-1"
  wait_for_ready = true

  kind_config {
    kind        = "Cluster"
    api_version = "kind.x-k8s.io/v1alpha4"

    node {
      role  = "control-plane"
      image = "kindest/node:v1.23.4"
    }

    node {
      role  = "worker"
      image = "kindest/node:v1.23.4"
    }
  }
}

 

terraform apply명령어를 실행하여 쿠버네티스 클러스터를 생성합니다.

terraform apply

 

terraform apply명령어가 성공 한 후, 도커 컨테이너 목록을 확인해보세요. control-plane노드 컨테이너와 worker노드 컨테이너가 생성되어 있습니다.

docker ps

 

쿠버네티스 클러스터 생성되면서 같이 생성된 context는 테라폼 명령어를 실행한 root경로에 생성됩니다.

 

context는 자동으로 $HOME/.kube디렉터리에 복사되므로 kubectl명령어를 바로 사용할 수 있습니다.

kubectl get no

 

1단계까지 파일구조는 아래와 같습니다.

 

5. 2단계: 테라폼 파트 - 필수 쿠버네티스 리소스 설치

필수 쿠버네티스 리소스는 argocd와 argocd설치에 필요한 리소스를 의미합니다. ingress controller가 대표적인 예입니다.

 

5.1 yaml로 ingress controller설치

테라폼 코드를 작성하기 이전 단계에서 반영한 인프라를 삭제합니다.

terraform destroy

 

테라폼에서는 kubectl provider를 사용하여 kind_ingress_controller를 설치합니다. 기존에 있던 proivder.tf에 kubectl provider를 설정합니다. kind로 생성했던 클러스터 context정보를 provider에 설정합니다.

terraform {
  required_providers {
    ...

    kubectl = {
      source  = "gavinbunney/kubectl"
      version = ">= 1.7.0"
    }
  }
}

provider "kind" {}

provider "kubectl" {
  host = kind_cluster.default.endpoint
  cluster_ca_certificate = kind_cluster.default.cluster_ca_certificate
  client_certificate = kind_cluster.default.client_certificate
  client_key = kind_cluster.default.client_key
}

 

yaml.tf파일을 생성합니다. 그리고 ingress controller를 설치하는 코드를 작성합니다. yaml파일은 kind 공식문서에서 제공하는 것을 그대로 사용했습니다.

출처: https://kind.sigs.k8s.io/docs/user/ingress/#ingress-nginx
data "kubectl_file_documents" "nginx_ingress" {
    content = file("yaml/ingress-nginx.yaml")
}

resource "kubectl_manifest" "nginx_ingress" {
    for_each  = data.kubectl_file_documents.nginx_ingress.manifests
    yaml_body = each.value
}

 

kind nginx ingress에는 node selector가 설정되어 있어 node라벨 설정이 필요합니다.

 

기존 kubernetes.tf에 node설정을 수정해봅시다. controlplane node에 node label과 nodeport(80, 443)포워딩 설정을합니다. node label은 nginx ingress설치를 위한 설정입니다. 그리고 nginx ingress에 접근하기 위해 nodeport 포워딩 설정이 필요합니다.

resource "kind_cluster" "default" {
  name           = "cluster-1"
  wait_for_ready = true

  kind_config {
    kind        = "Cluster"
    api_version = "kind.x-k8s.io/v1alpha4"

    node {
      role  = "control-plane"
      image = "kindest/node:v1.23.4"

      # reference: https://kind.sigs.k8s.io/docs/user/using-wsl2/#accessing-a-kubernetes-service-running-in-wsl2
      kubeadm_config_patches = [
        "kind: InitConfiguration\nnodeRegistration:\n  kubeletExtraArgs:\n    node-labels: \"ingress-ready=true\"\n"
      ]

      # nodeport port-forward
      extra_port_mappings {
        container_port = 80
        host_port      = 80
      }

      # nodeport port-forward
      extra_port_mappings {
        container_port = 443
        host_port      = 443
      }
    }

    node {
      role  = "worker"
      image = "kindest/node:v1.23.4"
    }
  }
}

 

지금까지 진행한 내용은 git commit에서 확인할 수 있습니다.

commit 링크: https://github.com/sungwook-practice/terraform-study/commit/4679d61d7e4049a0a76a70e7c66f04524c81ccef

 

terraform apply로 테라폼 코드를 반영합니다.

terraform init && terraform apply

 

ingress controller 리소스가 잘 생성되었는지 확인합니다.

kubectl get po,svc

 

ingress controller이 잘 동작하는지 테스트 리소스를 생성합니다. 테스트 리소스는 kind 공식문서에서 제공합니다.

문서 링크: https://kind.sigs.k8s.io/docs/user/ingress/
kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/usage.yaml

 

localhost/foo, localhost/bar를 호출하면 pod의 응답을 받을 수 있습니다.

# should output "foo"
curl localhost/foo
# should output "bar"
curl localhost/bar

 

지금까지 진행한 파일구조는 아래와 같습니다.

 

5.2 helm provider로 argocd 설치

테라폼 코드를 작성하기 이전 단계에서 반영한 인프라를 삭제합니다.

terraform destroy

 

기존 provider.tf파일에 helm provider를 설정합니다.

# 이전 코드
...

provider "helm" {
  alias = "cluster-1"

  kubernetes {
    host                   = kind_cluster.default.endpoint
    cluster_ca_certificate = kind_cluster.default.cluster_ca_certificate
    client_certificate     = kind_cluster.default.client_certificate
    client_key             = kind_cluster.default.client_key
  }
}

 

helm.tf파일을 생성합니다. 그리고 argocd helm chart를 릴리즈하는 코드를 작성합니다. helm values는 제가 커스텀으로 정의한 values.yaml파일을 사용했습니다. values.yaml파일은 helm/argocd폴더에 존재합니다.

resource "helm_release" "argocd" {
  # reference: https://github.com/hashicorp/terraform-provider-helm/issues/400#issuecomment-583561090
  provider = helm.cluster-1

  name = "argocd"

  repository       = "https://argoproj.github.io/argo-helm"
  chart            = "argo-cd"
  namespace        = "argocd"
  version          = "5.14.2"
  create_namespace = true

  values = [
    file("helm/argocd/values.yaml")
  ]

  depends_on = [
    kubectl_manifest.nginx_ingress
  ]
}

 

helm_release 리소스에서 가장 중요한 필드는 depends_on입니다. 저는 argocd values.yaml에 ingress를 설정했기 때문에 ingress controller가 설치된 이후에 argocd가 설치되야 합니다.

server:
  extraArgs:
    - --insecure
  ingress:
    enabled: true
    ingressClassName: nginx
    hosts:
      - argocd.localtest

 

지금까지 작업한 내용은 github commit에서 확인할 수 있습니다.

github 링크: https://github.com/sungwook-practice/terraform-study/commit/f4d94aa589ae1caee9ad2b0a1876ad1263b609e9

 

terraform apply명령어로 코드를 반영합니다.

terraform init && terraform apply

 

argocd namespace를 조회하면 argocd가 잘 설치된 거시을 확인할 수 있습니다.

kubectl -n argocd get po,ingress

 

이제 직접 argocd 대시보드에 접속해보겠습니다. hosts파일을 수정해서 argocd.localhost를 접속할 수 있게 설정해야 합니다.

# linux, mac -> /etc/hosts
# windows -> C:\Windows\System32\drivers\etc\hosts
127.0.0.1 argocd.localtest

 

hosts파일을 수정한 후 https://argocd.localtest에 접속하면 argocd로그인페이지가 잘 뜹니다.

 

ID는 admin이고 password는 secret에 있습니다.

kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo

 

지금까지 작업한 파일구조는 아래와 같습니다.

 

6.  3단계: ArgoCD 파트 - app of apps 패턴

3단계에서는 app of apps패턴을 사용하여 나머지 쿠버네티스 리소스를 배포합니다. 아직 최적화가 안되어 있습니다. 양해부탁드립니다.

 

6.1 개요

argoCD에서는 최대한 app of apps패턴으로 필요한 쿠버네티스 리소스들을 배포합니다. 배포할 리소스를 application이라는 단위로 그룹화하여 관리하는 패턴입니다. app of apps패턴의 자세한 설명은 전 글(https://malwareanalysis.tistory.com/478)을 참고하시면 좋을 것 같습니다.

 

6.2 목표

이 글에서는 argo workflow를 배포합니다. argo workflow는 minio, postgresql가 필요합니다. 그러므로 설치할 준비작업이 많이 필요한데 app of apps패턴을 사용하여 그룹으로 관리했습니다. minio는 standalone, postgres는 cnpg operator를 사용했습니다.

 

6.3 manifest repo생성

argocd가 사용할 리소스를 git에 push해야 합니다. 저는 apps라는 폴더에 리소스를 정의했습니다. apps폴더에는 argocd application과 설정파일이 있습니다.

github 링크: https://github.com/sungwook-practice/terraform-study/tree/main/final_subject

 

app of apps패턴 application sepc은 아래와 같이 정의되어 있습니다.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: argowf
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  ...

 

각 application에 필요한 helm차트는 charts라는 폴더에 관리했습니다. 단, default vaules를 사용하는 경우 chart를 별도로 관리하지 않았습니다.

 

6.4 app of apps argocd application 생성

생성한 repo를 이용하여 app of apps argocd application을 생성합니다. source.path는 apps폴더가 있는 경로를 설정합니다. 주의할 점은 destination.namespace가 꼭 argocd여야 합니다. argocd디폴트 설정으로 argocd namespace에만 application을 생성할 수 있기 때문입니다.

 

applicatino을 생성하면 아래 그림과 같이 의도한 application목록이 보입니다.

 

6.5 postgres operator application sync

postgres는 cnpg-operator를 사용하여 설치합니다. 그러므로 먼저 cnpg-operator설치부터 해야합니다.

 

cnpg-operator application을 sync합니다. sync가 성공하면 application이 생성됩니다. 링크버튼을 클릭하면 application페이지로 이동됩니다.

 

application은 수동sync설정이 되어있어서 아직 쿠버네티스 리소스가 배포되지 않았습니다. sync버튼을 눌러 수동sync을 합니다.

 

sync가 성공하여 healthy로 되었습니다.

 

직접 kuebctl로 조회하면 cnpg-operator pod가 정상적으로 실행하고 있습니다.

kubectl -n cnpg get po

 

6.6 postgresl application sync

이제 postgres를 설치해봅시다. postgres application를 sync합니다.

 

postgresql application이 sync가 되었는지 확인합니다. 그리고 application상세페이지로 이동합니다.

 

sync버튼을 눌러 postgres를 생성합니다.

 

postgres pod가 정상적으로 생성되고 running상태인지 확인합니다.

 

kubectl로 직접 조회하면 postgres pod가 정상적으로 실행되고 있습니다.

k -n argowf get po

 

postgres에 연결되는지 테스트해볼게요. 먼저 postgres 서비스를 port-forward합니다.

kubectl -n argowf port-forward svc/argowf-postgres-rw 5432:5432

 

DB이름은 postgres, 계정은 postgres입니다. 이 설정은 spec에 설정되어 있습니다.

계정설정 github 링크: https://github.com/sungwook-practice/terraform-study/blob/main/final_subject/apps/postgres/argowf-postgres.yaml#L20

 

비밀번호는 operator가 자동으로 생성하여 secret에 저장합니다.

# 비밀번호 조회
kubectl -n argowf get secret argowf-postgres-app -o jsonpath={.data.password}  | base64 -d ; echo

 

postgres client로 127.0.0.1:5432로 연결합니다. 저는 Dbeaver를 사용했습니다. 잘 접속된다면 아래 그림 6번처럼 postgres DB가 보입니다.

 

6.7 minio application sync

minio는 standalone모드를 사용했습니다.

 

argo workflow에서 사용할 access_key를 추가하기 위해 argocd application을 생성합니다.

github 링크: https://github.com/sungwook-practice/terraform-study/tree/main/final_subject/apps/minio-users

 

생성한 argocd application을 sync하여 minio access_key를 추가합니다.

 

app of app application에서 minio application을 sync합니다.

 

minio application 상세페이지로 이동합니다.

 

sync버튼을 눌러 minio리소스를 동기화합니다.

 

sync상태가 healthy상태인지 확인합니다. 그리고 네트워크 트래픽이 정상적으로 잘 흐르는지 확인합니다.

 

minio홈페이지에 접속하기 위해 hosts파일을 수정합니다.

# linux, mac -> /etc/hosts
# windows -> C:\Windows\System32\drivers\etc\hosts
127.0.0.1 minio.localtest

 

웹 브라우저에서 https://minio.localtest로 접속하면 minio로그인 페이지에 접속됩니다. 계정은 argowf이고 비밀번호는 password입니다. 계정과 비밀번호는 secret에 설정되어 있습니다.

계정/비밀번호 github 링크: https://github.com/sungwook-practice/terraform-study/blob/main/final_subject/apps/minio-users/argwf-user.yaml

 

로그인에 성공하면 bucket목록이 보입니다. helm values에 argo-workflow bucket은 helm values에 생성설정이 되어 있기 떄문에 목록에 보입니다.

 

6.8 argo workflow application sync

드디어 마지막 application입니다. 구축한 minio, postgres를 이용하여 argo workflow를 설치합니다.

 

argo workflow application을 sync합니다.

 

argo workflow application상세페이지로 이동합니다.

 

sync버튼을 눌러 argo workflow리소스를 동기화합니다.

 

sync가 healthy상태인지 확인합니다. 그리고 네트워크 트래픽이 잘 흐르는지 확인합니다.

 

argo workflow홈페이지에 접속하기 위해 hosts파일을 수정합니다.

# linux, mac -> /etc/hosts
# windows -> C:\Windows\System32\drivers\etc\hosts
127.0.0.1 argowf.localtest

 

https://argowf.localtest에 접속하면 argo workflow로그인 페이지가 나옵니다.

 

6.8 argo workflow admin 토큰 생성

argo workflow에 로그인하려면 토큰을 생성해야 합니다. 토큰생성가이드는 argo workflow공식홈페이지에 설명되어 있습니다. 이 글에서는 argo workflow에 대한 내용이 아니므로 자세한 설명은 생략합니다.

argo workflow토큰생성 공식문서 링크: https://argoproj.github.io/argo-workflows/access-token/#token-creation

 

저는 설명을 참고하여 미리 yaml파일을 정의했습니다. yaml파일을 argocd로 동기화하면 모든 권한을 갖는 admin토큰이 생성됩니다.

github 링크: https://github.com/sungwook-practice/terraform-study/tree/main/final_subject/apps/argowf-users

 

admin토큰은 secret에서 admin에 저장되어 있습니다.

kubectl -n argowf get secret admin

 

admin토큰은 base64인코딩되어 있으므로 base64디코딩을 해줘야합니다. 그리고 토큰타입 Bearer키워드를 붙여줘야 합니다.

ARGO_TOKEN="Bearer $(kubectl -n argowf get secret admin -o=jsonpath='{.data.token}' | base64 --decode)"
echo $ARGO_TOKEN

 

로그인페이지에 조회한 토큰을 입력하면 로그인을 성공합니다.

 

7. workflow 테스트 - minio, postgres연동 확인

argo workflow 2번째 메뉴에서 샘플 workflowtemplate를 생성합니다. namespace와 serviceaccount를 꼭 설정해야 합니다.

 

생성한 workflowtemplate에서 Submit버튼을 클릭하여 workflow을 생성합니다.

 

postgres, minio설정이 잘 되었다면 아래 그림처럼 workflow가 잘 실행됩니다.

 

workflow 아티팩트(로그 등)는 minio에 저장되어 있습니다.

 

minio bucket에 접속하면 workflow 아티팩트가 존재합니다.

 

postgres에 접속하면 argo workflow에 관련된 table이 생성되어 있습니다. 

 

8. 마치며

작업을 끝나고 보니 리팩토링할게 많아 보이네요. ㅜ.ㅜ

 

테라폼 모듈분리 리팩토링

지금은 모든 테라폼 코드가 root모듈에 있습니다. 모든 코드가 root모듈에 있으면 terraform apply시간이 오래걸리고 코드수정 불편해집니다. 그래서 모듈단위로 분리해야 하는 작업이 필요합니다.

 

argocd 수동 sync작업 리팩토링

현재 argo workflow 유저, minio user등 app of apps패턴을 사용하기 위한 수동작업이 필요합니다. 이러한 작업을 app of apps패턴을 다단계로 분리, sync wave로 자동화하거나 applicationset등을 이용하여 리팩토링이 필요해보입니다.

 

argcod project설정

현재 모든 argocd application은 default project에 생성됩니다. 사용목적에 따라 project를 분류해야 유지보수가 쉽습니다.

 

cert-manager 기능 추가

인증서 설정때문에 cert-manager를 추가가 필요합니다. 이건 고민인데요. 로컬 테스트인데 굳이 인증서가 필요한지 생각해야할 것 같습니다.

 

argocd target rivision 리팩토링

현재 모든 argocd application이 최신 commit을 바라보고 diff를 수행합니다. 다양한 application이 같은 commit을 바라보고 diff하는 것은 바람직하지 않다고 생각합니다. git tag로 target rivision을 수정한다면 각 application은 diff target을 다르게 가져갈 수 있습니다.

 

중앙 집중식 민감정보 관리

민감정보가 현재 yaml또는 helm에 하드코딩되어 있습니다. 운영에서는 민감정보를 암호화해서 관리해야하므로 vault를 연동해서 관리하려고 합니다.

반응형