Kubernetes for M1 with Docker Desktop 로컬 환경 설정 #1

jude Kim·2022년 1월 22일
3

배경

M1 (Apple Silicon) 맥북에서 Kubernetes(이하 k8s)의 로컬 개발환경을 구축하기 위한 방법을 찾기 위해 정리를 시작하였습니다.

Docker Desktop의 라이선스 정책이 곧(2022-02-01) 변경(자세한 사항은 링크 참조 - https://www.ciokorea.com/news/206529)된다고 알고지만, 익숙하지 않은 환경을 기반으로 하기에는 너무 많은 삽질이 필요할 듯하여 우선적으로 한 사이클을 진행해보고 다른 방법을 시도하고자 Docker Desktop을 기반으로 진행했습니다.


Prerequisite

  • Docker Desktop with Kubernetes : Minikube를 활용하는 경우도 있으나, 저는 Docker Desktopk8s를 사용하였습니다.
  • Kubernetes - Client - v1.23.1, Server - v1.22.5
  # 다음 명령으로 확인
  $> kuberctl version
  Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.1", GitCommit:"86ec240af8cbd1b60bcc4c03c20da9b98005b92e", GitTreeState:"clean", BuildDate:"2021-12-16T11:33:37Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"darwin/arm64"}
  Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.5", GitCommit:"5c99e2ac2ff9a3c549d9ca665e7bc05a3e18f07e", GitTreeState:"clean", BuildDate:"2021-12-16T08:32:32Z", GoVersion:"go1.16.12", Compiler:"gc", Platform:"linux/arm64"}
  
  # 클러스터 정보 확인 # k8s의 컨트롤 패널의 정보를 가져올 수 있는 endpoint를 알려줍니다. 
  # kubernetes.docker.internal 은 k8s 설치시 hosts 파일에 자동으로 등록된 정보입니다. 127.0.0.1 kubernetes.docker.internal 
  $> kubectl cluster-info 
  Kubernetes control plane is running at https://kubernetes.docker.internal:6443
  CoreDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

  $> kubectl get nodes 
  NAME             STATUS   ROLES                  AGE   VERSION
  docker-desktop   Ready    control-plane,master   14h   v1.22.5
  • Mac on Apple Silicon : M1기반의 Mac
  • Terraform : version 1.1.3 for arm64
  # tfenv 패키지 설치
  $> brew install tfenv
  
  # tfenv 를 통한 특정 버전 설치
  $> tfenv use 1.1.3
  
  # terraform 버전은 1.1.3을 사용한다. 
  $> terraform version
  Terraform v1.1.3
  on darwin_amd64 

  # 다만, 이렇게 설치하면 arm64가 아닌 x86기반 amd64로 설치되어 재설치가 필요합니다. 
  # 기존 버전 삭제
  $> tfenv uninstall 1.1.3 
  # 다시 arm64 버전으로 설치합니다.
  $> TFENV_ARCH=arm64 tfenv install 1.1.3

  # 정상적으로 설치 확인
  $> terraform version
  Terraform v1.1.3
  on darwin_arm64
  • Helm 설치 - v3.7.2
  $> brew install helm
  • 간단한 쉘 설정 : 자주쓰는 명령어는 단축어로 만들어둡니다.
  $> vi ~/.zshrc
  # alias 
  alias k='kubectl'
  alias tf='terraform'

추가적으로 zsh 기반의 설정을 해두면 도움이 됩니다.

  • oh-my-zsh 설치
  $> sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
  • powerlevel10k oh-my-zsh 테마 설치
    설정도 wizard 방식으로 손쉽게 진행된다.
    아래 과정으로 설치한 뒤에 terminal 을 종료하고 재시작하면 터미널 wizard 가 실행된다.
  # https://github.com/romkatv/powerlevel10k#installation
  $> brew install romkatv/powerlevel10k/powerlevel10k
echo "source $(brew --prefix)/opt/powerlevel10k/powerlevel10k.zsh-theme" >>~/.zshrc
  # wizard를 통해서 다시 설정하려면 다음과 같이 입력한다.
  $> p10k configure

플랜

  1. nginx controller 설치
  2. sample nginx ingress 설정 - host 기반의 routing 테스트
  3. postgresql ha with pgpool-II 설치
  4. postgresql proxy 설정

아직 terraform 이 익숙하지 않아 파일을 main.tf 작성하였습니다.
추후 directory 구성에 대해서는 별도로 분리를 할 예정입니다.

.
└── local
    ├── main.tf
    └── values
        ├── nginx-controller_values.yaml
        ├── pgadmin_values.yaml
        └── postgresql_values.yaml

common 설정

  • k8shelm 을 사용하기 위한 provider 설정
  • local 환경을 위한 local namespace 생성
  provider "kubernetes" {
    config_path = "~/.kube/config"
  }
  provider "helm" {
    kubernetes {
      config_path = "~/.kube/config"
    }
  }
  resource "kubernetes_namespace" "local" {
    metadata {
      name = "local"
    }
  }
  • 기본적으로 infra 설정에는 terraform을 사용하고, 그 안에 탑재되는 application은 가급적 helm을 기반으로 관리할 예정입니다.

nginx controller 설치

nginx controller 를 ingress controller로 사용하고, helm을 기반으로 설치할 예정입니다.

아래 링크의 helm chart를 활용할 예정입니다.
https://github.com/kubernetes/ingress-nginx/tree/main/charts/ingress-nginx

main.tf 파일을 열어 다음과 같은 내용을 추가해줍니다.

  resource "helm_release" "nginx_controller" {
    name = "nginx-controller"
    namespace = kubernetes_namespace.local.metadata[0].name

    repository = "https://kubernetes.github.io/ingress-nginx"
    chart = "ingress-nginx"
    values = [
      "${file("values/nginx-controller_values.yaml")}"
    ]
  }

values 폴더내에 https://github.com/kubernetes/ingress-nginx/blob/main/charts/ingress-nginx/values.yaml 이 파일을 저장하여 nginx-controller_values.yaml 이름으로 변경합니다.

특별히 설정을 변경할 사항은 없습니다.

  • nginx-controller를 설치합니다.
  $> terraform apply
  
  $> kubectl get all -n local 
  NAME                                                             READY   STATUS    RESTARTS   AGE
  pod/nginx-controller-ingress-nginx-controller-78f676d8db-9xgvv   1/1     Running   0          52s

  NAME                                                          TYPE             CLUSTER-IP      EXTERNAL-IP   PORT(S)                                     AGE
  service/nginx-controller-ingress-nginx-controller             LoadBalancer   10.97.122.144   localhost     80:30162/TCP,443:32137/TCP                  52s
  service/nginx-controller-ingress-nginx-controller-admission   ClusterIP      10.106.20.31    <none>        443/TCP                                     52s

  NAME                                                        READY   UP-TO-DATE   AVAILABLE   AGE
  deployment.apps/nginx-controller-ingress-nginx-controller   1/1     1            1           52s

  NAME                                                                   DESIRED   CURRENT   READY   AGE
  replicaset.apps/nginx-controller-ingress-nginx-controller-78f676d8db   1         1         1       52s  

sample nginx ingress 설정

이제 특정호스트(k8s.localdev.me)로 접속 가능한 간단한 웹서비스(nginx)를 하나 띄워보겠습니다.

main.tf 파일을 열어 다음과 같은 내용을 다시 추가합니다.

  • deployment 설정
  • service 설정
  • ingress 설정
  • 모두 namespace를 local 로 설정합니다.
  # create deployment
  resource "kubernetes_deployment" "nginx" {
    metadata {
      name = "nginx"
      namespace = kubernetes_namespace.local.metadata[0].name
    }
    spec {
      replicas = 2
      selector {
        match_labels = {
          "app" = "nginx"
        }
      }
      template {
        metadata {
          labels = {
            app = "nginx"
          }
        }
        spec {
          container {
            image = "nginx"
            name = "nginx-container"
            port {
              container_port = 80
            }
          }
        }
      }
    }
  }

  # create an service
  resource "kubernetes_service" "nginx" {
    metadata {
      name = "nginx"
      namespace = kubernetes_namespace.local.metadata[0].name
    }
    spec {
      selector = {
        app = kubernetes_deployment.nginx.spec[0].template[0].metadata[0].labels.app
      }
      type = "NodePort"
      port {
        port = 8080
        target_port = 80
        protocol = "TCP"
      }
    }
  }
  
  # create ingress
  resource "kubernetes_ingress_v1" "nginxingress" {
    metadata {
      name = "nginxingress"
      annotations = {
        "nginx.ingress.kubernetes.io/rewrite-target" = "/"
      }
      namespace = kubernetes_namespace.local.metadata[0].name
    }
    spec {
      ingress_class_name = "nginx"
      rule {
        host = "k8s.localdev.me"
        http {
          path {
            path_type = "Prefix"
            path      = "/*"
            backend {
              service {
                name = kubernetes_service.nginx.metadata[0].name
                port {
                  number = kubernetes_service.nginx.spec[0].port[0].target_port
                }
              }
            }
          }
        }
      }
    }
  }
  
  • terraform 에 반영합니다.
  • nginx pod, service, deployment, replicaset 이 추가된 것을 확인할 수 있습니다.
  $> terraform apply 
  
  $> kubectl get all -n local 
  NAME                                                             READY   STATUS    RESTARTS   AGE
  pod/nginx-7d7dc86f65-76s67                                       1/1     Running   0          98s
  pod/nginx-7d7dc86f65-xfw7q                                       1/1     Running   0          98s
  pod/nginx-controller-ingress-nginx-controller-78f676d8db-9xgvv   1/1     Running   0          19m

  NAME                                                          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                     AGE
  service/nginx                                                 NodePort       10.96.105.197   <none>        8080:32679/TCP                              90s
  service/nginx-controller-ingress-nginx-controller             LoadBalancer   10.97.122.144   localhost     80:30162/TCP,443:32137/TCP                  19m
  service/nginx-controller-ingress-nginx-controller-admission   ClusterIP      10.106.20.31    <none>        443/TCP                                     19m

  NAME                                                        READY   UP-TO-DATE   AVAILABLE   AGE
  deployment.apps/nginx                                       2/2     2            2           98s
  deployment.apps/nginx-controller-ingress-nginx-controller   1/1     1            1           19m

  NAME                                                                   DESIRED   CURRENT   READY   AGE
  replicaset.apps/nginx-7d7dc86f65                                       2         2         2       98s
  replicaset.apps/nginx-controller-ingress-nginx-controller-78f676d8db   1         1         1       19m
  • ingress 는 all 명령으로는 보이지 않아 별도의 명령어로 확인합니다.
  $> kubectl get ingress -n local 
  NAME           CLASS   HOSTS             ADDRESS     PORTS   AGE
  nginxingress   nginx   k8s.localdev.me   localhost   80      4m37s
  • 아래와 같이 k8s.localdev.me 로 접속했을때 다음 화면을 확인할 수 있습니다.

nginx controller를 거쳐 아까 설정한 ingress의 route 설정을 통해 k8s.localdev.me 에서 서비스(backend)로 라우팅되었습니다.


postgresql ha with pgpool-II 설치

이젠 DB를 설치하겠습니다. 여러가지 사유로 postgreSQL을 설치하기로 결정했고, 버전은 아직 확정하지 못했으나, 우선 설치를 진행해보기로 했습니다. 설치할 버전은 11.14.0 입니다.

postgresql-ha 의 설치 버전은 https://github.com/bitnami/charts/blob/master/bitnami/postgresql-ha/Chart.yaml 에서 체크가 확인가능합니다.

annotations:
  category: Database
apiVersion: v2
appVersion: 11.14.0

values 폴더내에 https://github.com/bitnami/charts/blob/master/bitnami/postgresql-ha/values.yaml 이 파일을 저장하여 postgresql_values.yaml 이름으로 변경합니다.

이번에는 postgresql_values.yaml 파일을 열어 간단히 수정합니다.

  # 접속 비밀번호 변경
  username: "postgres"
  password: "postpassword!@#"

다시 main.tf를 열어 다음 항목을 추가합니다.

  # postgresql ha
  resource "helm_release" "postgresql_ha" {
    name = "postgresql-ha"
    namespace = kubernetes_namespace.local.metadata[0].name

    repository = "https://charts.bitnami.com/bitnami"
    chart = "postgresql-ha"

    values = [
      "${file("values/postgresql_values.yaml")}"
     ]
  }

방금 설치한 postgresqlha(High Availability) 버전입니다.
즉, postgresql + replication + pgpool-II(https://www.pgpool.net/mediawiki/index.php/Main_Page) 이 동시에 설치된것을 아래에서 확인할 수 있습니다.

pgpool-II를 간단히 설명하면 read-only 쿼리는 read reaplica 서버에 그외에 쿼리는 master 에 알아서 분배된다. (대충 알아본것은 이정도)

terraform 에 수정된 항목을 반영합니다.

  $> terraform apply
  $> kubectl get all -n local
  NAME                                                             READY   STATUS    RESTARTS   AGE
  pod/nginx-7d7dc86f65-76s67                                       1/1     Running   0          39m
  pod/nginx-7d7dc86f65-xfw7q                                       1/1     Running   0          39m
  pod/nginx-controller-ingress-nginx-controller-78f676d8db-9xgvv   1/1     Running   0          57m
  pod/postgresql-ha-pgpool-5d8557bf7b-ckj5c                        1/1     Running   0          7m22s
  pod/postgresql-ha-postgresql-0                                   1/1     Running   0          7m22s
  pod/postgresql-ha-postgresql-1                                   1/1     Running   0          7m22s

  NAME                                                          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                     AGE
  service/nginx                                                 NodePort       10.96.105.197   <none>        8080:32679/TCP                              39m
  service/nginx-controller-ingress-nginx-controller             LoadBalancer   10.97.122.144   localhost     80:30162/TCP,443:32137/TCP                  57m
  service/nginx-controller-ingress-nginx-controller-admission   ClusterIP      10.106.20.31    <none>        443/TCP                                     57m
  service/postgresql-ha-pgpool                                  ClusterIP      10.102.102.56   <none>        5432/TCP                                    7m22s
  service/postgresql-ha-postgresql                              ClusterIP      10.102.56.105   <none>        5432/TCP                                    7m22s
  service/postgresql-ha-postgresql-headless                     ClusterIP      None            <none>        5432/TCP                                    7m22s

  NAME                                                        READY   UP-TO-DATE   AVAILABLE   AGE
  deployment.apps/nginx                                       2/2     2            2           39m
  deployment.apps/nginx-controller-ingress-nginx-controller   1/1     1            1           57m
  deployment.apps/postgresql-ha-pgpool                        1/1     1            1           7m22s

  NAME                                                                   DESIRED   CURRENT   READY   AGE
  replicaset.apps/nginx-7d7dc86f65                                       2         2         2       39m
  replicaset.apps/nginx-controller-ingress-nginx-controller-78f676d8db   1         1         1       57m
  replicaset.apps/postgresql-ha-pgpool-5d8557bf7b                        1         1         1       7m22s

  NAME                                        READY   AGE
  statefulset.apps/postgresql-ha-postgresql   2/2     7m22s    

현재 설정은 DB가 private subent 형태로 즉, 다시 말하면 외부에서 접속이 허용되지 않는 상태로 설치가 됩니다.
하지만, local 환경에서 DB를 직접 제어하지 못하면 개발할때 상당히 불편합니다.
그래서 보통 pgadmin을 설치하곤 하지만, 여기에선 postgresql proxy를 설정하여 접근해보겠습니다.


postgresql proxy 설정

앞서 말한것처럼 postgresql 의 접속을 proxy 처리해주어야 합니다.

localhost:5432 로 접속하면 service/postgresql-ha-pgpool 로 접속하도록 하겠습니다. (보통 master 서버의 접속이 맞으나, 결과적으로 같은 결과를 얻을 수 있을 듯 하여 postgresql-ha-pgpool 로 연결합니다.)

이때는 처음에 설치한 nginx-ingress-controller에 설정을 변경하여 반영합니다.

values/nginx-controller_values.yaml 파일을 열어 tcp: {}를 아래와 같이 수정합니다.

  # -- TCP service key:value pairs
  ## Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/exposing-tcp-udp-services.md
  ##
  tcp: {
    5432: "local/postgresql-ha-pgpool:5432"
  }

다만, 좀 아쉬운 점은 아직 helm 이 익숙하지 않아서 value로 처리하지 않았습니다.
5432port로 접속하면 local namespace 내의 postgresql-ha-pgpool 서비스 5432 port로 proxy 하도록 하겠다는 의미입니다.

https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/ 여기를 참고

설정후 terraform을 통해 반영합니다.

  $> terraform apply 
  $> kubectl get services
  NAME                                                  TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
  nginx                                                 NodePort       10.96.105.197   <none>        8080:32679/TCP               72m
  nginx-controller-ingress-nginx-controller             LoadBalancer   10.97.122.144   localhost     80:30162/TCP,443:32137/TCP,5432:30459/TCP   90m
  nginx-controller-ingress-nginx-controller-admission   ClusterIP      10.106.20.31    <none>        443/TCP                      90m
  postgresql-ha-pgpool                                  ClusterIP      10.102.102.56   <none>        5432/TCP                     40m
  postgresql-ha-postgresql                              ClusterIP      10.102.56.105   <none>        5432/TCP                     40m
  postgresql-ha-postgresql-headless                     ClusterIP      None            <none>        5432/TCP                     40m

바뀐점은 딱 하나 5432:30459/TCP 가 nginx-controller 에 추가되었습니다.

이젠 localhost:5432postgresql 접속이 가능한것을 확인할 수 있습니다.


결론

미래의 상황을 예상하여 간단히만 만들어봐서 실제로 사용하기엔 몇가지 추가적인 설정이 필요합니다.

  • 오작동 (제 실수일 수 있으나,) 여러가지의 상황적인 문제일 수 있으나, terraformapply/destroy가 제대로 이루어지지 않은 경우도 발생했었고, (설정한뒤 예상대로 동작하지 않아 제가 잘못 설정한 줄 알았으나, docker desktop의 k8s cluster reset 후 정상동작하는 경우 발생)
  • 정리필요 아직 terraform이나 helm 에 익숙하지 않아서 제대로 정리되지 않은 파일의 문제도 있고,
  • 미래예측한계 로컬 개발 환경의 use case에 맞춘 파일의 분리도 고려되지 않았고,

정리하다보니 비교적 짧게 정리해봤지만, 원하는 설정까지는 꽤 많은 삽질을 했네요.

그리고, 생각보다도 할일이 더 많이 남아 있다는 것도 알게 되었네요.

아무래도 포스팅에 일련번호를 붙여야 할 것 같습니다.

EOD.

profile
씨봉봉이

0개의 댓글