kind로 쿠버네티스 개발환경 만들기
KinD (Kubernetes in Docker)
여러가지 MSA 패턴들을 구현해보기 위해 간단하게 띄워볼 수 있는 쿠버네티스 클러스터를 찾다가 알게 되었다. 본 포스팅을 위한 예시 코드 저장소를 참고해도 좋다.
맥이라면 간단한게 brew로 kind를 사용할 수 있다. (Docker Desktop에서도 사용할 수 있지만, CLI 를 추천한다.)
brew install kind
Provisioning KinD Cluster
아래처럼 yaml 파일로 클러스터를 정의할 수 있다. 컨트롤 플레인과 워커 노드를 여러 대 구성할 수도 있지만, 로컬 자원 절약을 위해 컨트롤 플레인 1대만 구성했다.
# cluster.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30080
hostPort: 30080
protocol: TCP
- containerPort: 30443
hostPort: 30443
protocol: TCP
기본적으로 디폴트로 사용되는 아주 미니멀한 CNI(kindnetd)가 존재한다. 이번엔 로컬에서 MSA를 서빙하는 게 목적이라 Gateway 어플리케이션만 NodePort로 노출시키는 방식으로 구성했다.
kind create cluster --config=cluster.yaml
이렇게 간단한 명령으로 로컬 테스트용 쿠버네티스를 올려볼 수 있다.
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kind-control-plane Ready control-plane 15d v1.32.2 172.18.0.2 <none> Debian GNU/Linux 12 (bookworm) 6.12.5-linuxkit containerd://2.0.2
Ingress Nginx
# ingress-nginx-values.yaml
controller:
service:
type: NodePort
nodePorts:
http: 30080
https: 30443
아주 간단하게 http, https를 노출시키는 인그레스를 정의했다. 공식 문서 상에서는 Kong 등 다양한 인그레스를 사용할 수 있다고 되어있지만, 매우 간단하게 익숙한 것으로 진행했다.
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace \
-f ingress-nginx-values.yaml
ArgoCD
Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.
아마 요즘 쿠버네티스를 사용하는 많은 조직이 사용하는 툴인데, 깃옵스(GitOps) 의 예시를 만들기 위해서 설치했다. 아래의 yaml 파일과 helm으로 배포했다.
global:
domain: argo.example.com
configs:
params:
server.insecure: true
cm:
exec.enabled: true
certificate:
enabled: false
server:
ingress:
enabled: true
ingressClassName: nginx
paths:
- /
pathType: Prefix
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
tls: []
설치하면 바로 접근할 수 있다. (설정에 따라 포트포워딩을 해야할 수도 있겠지만.)
예시에서는 Argo가 깃헙 저장소에 접근해야 하기 때문에, 깃헙의 Personal Access Token을 발급받아서 설정해두어야 한다. 토큰은 repository 읽기 권한과 package 읽기 권한을 포함해야 한다. 설정 방법은 이 문서를 참조한다.
github의 컨테이너 레지스트리에 접근하기 위해서는 아래처럼 시크릿도 생성해줘야 한다. (원한다면 위 토큰과 분리하셔도 좋다.)
kubectl create secret docker-registry ghcr-secret --docker-server=ghcr.io --docker-username={USER-NAME} --docker-password={ghp_xxx} --docker-email={EMAIL-ADDRESS} -n default
Github Packages - Container Registry
이 프로젝트에서는 지출없는 환경 설정을 위해 Github 에서 제공하는 Container Registry를 사용한다. 아래는 Github Actions 를 위한 스크립트다.
name: Build
on:
push:
branches:
- main
- develop
paths:
- 'gateway/**'
- 'inventory-api/**'
- 'order-api/**'
jobs:
build:
runs-on: ubuntu-24.04-arm
strategy:
matrix:
app: [
'gateway',
'order-api',
'inventory-api',
]
permissions:
contents: read
packages: write
outputs:
image_tag_matrix: ${{ steps.set-matrix.outputs.result }}
steps:
- uses: actions/checkout@v4
name: Checkout
with:
ref: ${{ github.head_ref }}
fetch-depth: 0
- name: Determine Version
id: version
run: bash .github/workflows/version.bash
- name: SetUp to Docker Buildx
uses: docker/setup-buildx-action@v3
with:
buildkitd-config: .github/buildkitd.toml
platforms: linux/amd64,linux/arm64
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build And Push
uses: docker/build-push-action@v6
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}-${{ matrix.app }}
IMAGE_TAG: '${{ steps.version.outputs.semVer }}-${{ steps.version.outputs.shortSha }}'
with:
file: Dockerfile
context: .
push: true
platforms: linux/arm64
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
APP_NAME=${{ matrix.app }}
여기서 약간 특이한 것은, Apple Silicon 환경에서 작업하다보니, arm 기반의 클러스터가 만들어졌다는 점이다. 여기서 앱들을 돌리려면 이미지 빌드도 arm 을 지원해야 한다.
As a result
kind
└── apps
├── charts
│ ├── app
│ │ └── templates
│ ├── mysql-arm64
│ │ └── templates
│ └── redis-arm64
│ └── templates
├── configs
│ ├── gateway
│ ├── inventory-api
│ ├── mysql-inventory-api
│ ├── mysql-order-api
│ ├── order-api
│ └── redis
└── values
├── gateway
├── inventory-api
├── mysql-inventory-api
├── mysql-order-api
├── order-api
└── redis
이 Helm Chart를 기반으로 앱을 나누고, 설정과 값을 분리해서 관리하한다.
charts
: Helm 차트들이 위치해 있다. 각 앱 혹은 서비스(mysql, redis 등)의 템플릿 리소스를 정의해두는 공간이다. app, mysql-arm64, redis-arm64 각각은 독립적인 차트로 구성되어 있다.configs
: 각 서비스별 Kubernetes 리소스들을 정의한 manifests 들이 들어있다. 예를 들면, Deployment, Service, Ingress 같은 것들. Helm 차트로 템플릿화하기 전 명시적으로 리소스를 정의해두고 실험하거나, ArgoCD AppSet과 연계하기 위한 베이스 구성을 포함한다.values
: Helm 차트에 넘겨줄 실제 값들 (values.yaml) 이 저장되는 폴더다. 각 앱마다 별도 설정값을 줄 수 있도록 분리해두었다.
이렇게 디렉토리를 나눈 이유는, Helm 템플릿 구조와 실제 값들을 명확히 구분해서 관리하고, ArgoCD나 CI 파이프라인 등과 연동할 때 각각의 책임을 분리하기 위함이다. 예를 들어 동일한 차트를 여러 서비스에 재활용하면서 값만 바꿔서 배포하고 싶을 때 매우 유용하다.
원하는대로 helm 차트를 정의하고, appset을 생성하면 된다.
kubectl apply -f apps/appset.yaml -n argocd
초기 환경이 약간 복잡하지만… https://github.com/kubernetes-sigs/kind/issues/3508 같은 스냅샷 기능이 잘 나오기만 하면 여기저기서 쓸 수 있는 걸 볼 수 있지 않을까?