M1 (Apple Silicon) 맥북에서 Kubernetes(이하 k8s)의 로컬 개발환경을 구축하기 위한 방법을 찾기 위해 정리를 시작하였습니다.
Docker Desktop
의 라이선스 정책이 곧(2022-02-01) 변경(자세한 사항은 링크 참조 - https://www.ciokorea.com/news/206529)된다고 알고지만, 익숙하지 않은 환경을 기반으로 하기에는 너무 많은 삽질이 필요할 듯하여 우선적으로 한 사이클을 진행해보고 다른 방법을 시도하고자 Docker Desktop을 기반으로 진행했습니다.
Docker Desktop with Kubernetes
: Minikube
를 활용하는 경우도 있으나, 저는 Docker Desktop
의 k8s
를 사용하였습니다.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기반의 MacTerraform
: 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 테마 설치 # https://github.com/romkatv/powerlevel10k#installation
$> brew install romkatv/powerlevel10k/powerlevel10k
echo "source $(brew --prefix)/opt/powerlevel10k/powerlevel10k.zsh-theme" >>~/.zshrc
# wizard를 통해서 다시 설정하려면 다음과 같이 입력한다.
$> p10k configure
nginx controller
설치ingress
설정 - host 기반의 routing 테스트postgresql ha
with pgpool-II 설치postgresql proxy
설정아직 terraform 이 익숙하지 않아 파일을 main.tf 작성하였습니다.
추후 directory 구성에 대해서는 별도로 분리를 할 예정입니다.. └── local ├── main.tf └── values ├── nginx-controller_values.yaml ├── pgadmin_values.yaml └── postgresql_values.yaml
k8s
및 helm
을 사용하기 위한 provider
설정local
namespace 생성 provider "kubernetes" {
config_path = "~/.kube/config"
}
provider "helm" {
kubernetes {
config_path = "~/.kube/config"
}
}
resource "kubernetes_namespace" "local" {
metadata {
name = "local"
}
}
terraform
을 사용하고, 그 안에 탑재되는 application은 가급적 helm
을 기반으로 관리할 예정입니다. 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
이제 특정호스트(k8s.localdev.me
)로 접속 가능한 간단한 웹서비스(nginx
)를 하나 띄워보겠습니다.
main.tf
파일을 열어 다음과 같은 내용을 다시 추가합니다.
deployment
설정service
설정ingress
설정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 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
$> 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
)로 라우팅되었습니다.
이젠 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")}"
]
}
방금 설치한 postgresql
은 ha
(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 처리해주어야 합니다.
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로 처리하지 않았습니다.
5432
port로 접속하면 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:5432
로 postgresql
접속이 가능한것을 확인할 수 있습니다.
미래의 상황을 예상하여 간단히만 만들어봐서 실제로 사용하기엔 몇가지 추가적인 설정이 필요합니다.
오작동
(제 실수일 수 있으나,) 여러가지의 상황적인 문제일 수 있으나, terraform
의 apply
/destroy
가 제대로 이루어지지 않은 경우도 발생했었고, (설정한뒤 예상대로 동작하지 않아 제가 잘못 설정한 줄 알았으나, docker desktop의 k8s cluster reset
후 정상동작하는 경우 발생)정리필요
아직 terraform이나 helm 에 익숙하지 않아서 제대로 정리되지 않은 파일의 문제도 있고, 미래예측한계
로컬 개발 환경의 use case에 맞춘 파일의 분리도 고려되지 않았고, 정리하다보니 비교적 짧게 정리해봤지만, 원하는 설정까지는 꽤 많은 삽질을 했네요.
그리고, 생각보다도 할일이 더 많이 남아 있다는 것도 알게 되었네요.
아무래도 포스팅에 일련번호를 붙여야 할 것 같습니다.
EOD.