sehyun.dev
k8s스터디

WSL2 + k3s로 쿠버네티스 연습 환경 구성하기

2026년 2월 7일

0. 왜 WSL에서 하냐

  • Windows에서 쿠버네티스를 “로컬 서버처럼” 연습하려면 리눅스 환경이 필요함
  • WSL2는 Windows 안에 리눅스 VM을 제공하고, 그 위에 Ubuntu를 올려서 k3s를 돌릴 수 있음

✅ 중요한 개념

  • WSL = 리눅스를 돌리는 “플랫폼(그릇)”
  • Ubuntu = WSL 위에서 실행되는 “리눅스 배포판(내용물)”
  • k3s는 Ubuntu(WSL) 안에 설치해야 정상적으로 동작함
  • /mnt/c(윈도우 파일 시스템)에서 작업하면 권한/성능 문제가 생길 수 있어 /home/<user>****에서 작업하는 것을 권장

1. k3s 설치

image.png

1-1) k3s 설치

  • k3s(경량 Kubernetes) 설치
  • control-plane + worker가 단일 노드로 같이 구성됨
curl -sfL https://get.k3s.io | sh -

1-2) k3s 서비스 상태 확인

  • Active: active (running)이면 정상
sudo systemctl status k3s

2. kubectl 설정 (sudo 없이 사용하기)

k3s가 설치돼도 kubectl은 기본 설정이 없으면 localhost:8080으로 붙으려다 실패할 수 있음.

2-1) kubeconfig를 내 계정으로 복사

mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
 
kubectl get nodes

출력 예시:

NAME   STATUS   ROLES           AGE   VERSION
osh    Ready    control-plane   11m   v1.34.3+k3s1

3. kubectl 단축어(k) + 자동완성

3-1) kubectl을 k로 변경해보기 + 자동완성

echo "alias k='kubectl'" >> ~/.bashrc
source ~/.bashrc
 
sudo apt-get update
sudo apt-get install -y bash-completion
 
cat <<'EOF' >> ~/.bashrc
export KUBECONFIG="$HOME/.kube/config"
alias k='kubectl'
if [ -f /usr/share/bash-completion/bash_completion ]; then
  . /usr/share/bash-completion/bash_completion
fi
source <(kubectl completion bash)
complete -o default -F __start_kubectl k
EOF
 
source ~/.bashrc

4. Ingress란? 왜 Controller가 필요한가?

  • Ingress: “도메인/경로 → 어떤 서비스로 보낼지” 규칙(라우팅 규칙 객체)
  • Ingress Controller: Ingress 규칙을 실제로 읽고 트래픽을 처리하는 엔진
  • 우리는 여기서 회사에서 가장 흔한 Ingress NGINX Controller(ingress-nginx) 를 설치한다.

✅ 핵심

  • Ingress 리소스만 만들면 아무 일도 안 일어남
  • Ingress Controller가 있어야 Ingress 규칙이 실제로 동작함
  • ConfigMap은 “컨트롤러를 만드는 것”이 아니라 컨트롤러(NGINX)의 설정을 조정하는 것

image.png

5. Ingress NGINX Controller 설치

  • ingress-nginx 네임스페이스 생성
  • RBAC 생성
  • ingress-nginx-controller Deployment 생성
  • Service 생성 (WSL에서는 LoadBalancer가 <pending>이어도 정상)
k apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.14.3/deploy/static/provider/cloud/deploy.yaml

설치 확인:

k -n ingress-nginx get pods
k -n ingress-nginx get svc
 
ingress-nginx-controller   LoadBalancer   ...   <pending>   80:<>/TCP,443:<>/TCP

✅ WSL에서는 EXTERNAL-IP가 <pending>인 것이 정상

대신 NodePort(예: 80:<포트>****) 로 접근한다.

구성 :

[ Browser ]
   |
   |  http://hello.local / api.local
   |  Host 헤더 기반
   v
[ NodePort ]
(ingress-nginx-controller Service)
   |
   v
[ Ingress NGINX Controller ]
(NGINX + Ingress rules)
   |
   +--> hello-svc  --> hello Pod
   |
   +--> admin-svc  --> admin Pod
   |
   +--> api-svc    --> api Pod

6. hello 앱 배포 (Deployment / Service / Ingress 분리)

연습이므로 리소스 이름을 명확하게:

  • Deployment: hello
  • Service: hello-svc
  • Ingress: hello-ing

6-1) 작업 폴더 구성

mkdir -p ~/hello
cd ~/hello

6-2) Deployment (deployment.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello -> Deployment 이름
    spec:
      containers:
        - name: hello
          image: hashicorp/http-echo:1.0
          args:
            - "-text=hello from ingress-nginx"
          ports:
            - containerPort: 5678

6-3) Service (service.yaml)

apiVersion: v1
kind: Service
metadata:
  name: hello-svc
spec:
  selector:
    app: hello
  ports:
    - port: 80
      targetPort: 5678

6-4) Ingress (ingress.yaml)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-ing
spec:
  ingressClassName: nginx
  rules:
    - host: hello.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello-svc
                port:
                  number: 80

host: hello.local 의미

  • 요청의 Host 헤더가 hello.local일 때만 이 규칙이 적용됨
  • 회사에서는 api.company.com, admin.company.com처럼 도메인 기준 분기를 하기 때문에 이 구조를 최소 단위로 연습한 것

6-5) 적용

k apply -f .
 
or
# 순서는 deployment -> service -> ingress 순으로
k apply -f deployment.yaml
k apply -f service.yaml
k apply -f ingress.yaml
 
or
k apply -f deployment.yaml -f service.yaml -f ingress.yaml

상태 확인:

k get deploy,svc,ingress

7. 접속(라우팅) 확인 (WSL 환경)

7-1) ingress-nginx NodePort 확인

k -n ingress-nginx get svc ingress-nginx-controller
  • 예: 80:<포트>/TCP

7-2) WSL IP 확인

ip -4 addr show eth0 | grep inet

7-3) Windows hosts 설정

파일:

C:\Windows\System32\drivers\etc\hosts

추가:

<WSL_IP> hello.local

7-4) 브라우저 접속

http://hello.local:<NodePort>

정상 결과:

hellofrom ingress-nginx

8. 최종 구조(요청 흐름) 요약

Browser
 → hello.local (hosts로 WSL IP 매핑)
   → <WSL_IP>:<NodePort>
     → ingress-nginx-controller Service
       → ingress-nginx-controllerPod(NGINX)
         → Ingress 규칙(host=hello.local)
           →Service(hello-svc)
             →Pod(hello, replicas=2)

9. 하나의 Ingress NGINX Controller로 여러 Service를 Host / Path 기준으로 분기 라우팅

  • “Ingress 하나로 서비스 여러 개 물려놨어요”
  • “Host는 도메인 분기고, Path는 앱 내부 분기예요”
  • “Nginx 단에서 라우팅돼서 앱은 몰라요”

image.png

hello.local/
  ├─ / hello-svc
  └─ /admin admin-svc
 
api.local/
  └─ / api-svc
  
👉 같은 포트, 같은 Ingress Controller, 규칙만 다름

9-1) admin 서비스 추가

  • admin Deployment (admin-deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: admin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: admin
  template:
    metadata:
      labels:
        app: admin
    spec:
      containers:
        - name: admin
          image: hashicorp/http-echo:1.0
          args:
            - "-text=admin page"
          ports:
            - containerPort: 5678
  • admin Service (admin-service.yaml)
apiVersion: v1
kind: Service
metadata:
  name: admin-svc
spec:
  selector:
    app: admin
  ports:
    - port: 80
      targetPort: 5678
k apply -f admin-deployment.yaml
k apply -f admin-service.yaml
 
k get deploy,svc

9-2) Path 기반 라우팅 추가

  • vi ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-ing
spec:
  ingressClassName: nginx
  rules:
    - host: hello.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello-svc
                port:
                  number: 80
          - path: /admin
            pathType: Prefix
            backend:
              service:
                name: admin-svc
                port:
                  number: 80
k apply -f ingress.yaml
 
http://hello.local:<NodePort>/
 hello from ingress-nginx
 
http://hello.local:<NodePort>/admin
 admin page

9-3) Host 기반 라우팅 추가 (api.local) - api 서비스 생성

  • api Deployment (api-deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 1
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: hashicorp/http-echo:1.0
          args:
            - "-text=api response"
          ports:
            - containerPort: 5678
  • api Service (api-service.yaml)
apiVersion: v1
kind: Service
metadata:
  name: api-svc
spec:
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 5678

적용

k apply -f api-deployment.yaml
k apply -f api-service.yaml

9-4) Ingress에 Host 추가

  • ingress.yaml 다시 수정
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-ing
spec:
  ingressClassName: nginx
  rules:
    - host: hello.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello-svc
                port:
                  number: 80
          - path: /admin
            pathType: Prefix
            backend:
              service:
                name: admin-svc
                port:
                  number: 80
 
    - host: api.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 80

적용:

k apply -f ingress.yaml

9-5) hosts 파일 추가

<WSL_IP> hello.local
<WSL_IP> api.local

9-6) 최종 확인

http://hello.local:<NodePort>/        → hello
http://hello.local:<NodePort>/admin   →admin
http://api.local:<NodePort>/          → api

🎉 Ingress 고급 라우팅 완성

10. 연습 끝나면 끄는 방법 (권장)

k3s만 중지

sudo systemctl stop k3s

WSL까지 완전 종료(리소스 회수)

Windows PowerShell:

wsl--shutdown

🔜 다음에 다뤄볼 내용

이번 글에서는 WSL2 + k3s 환경에서 Ingress-NGINX Controller를 설치하고, Host / Path 기반 라우팅까지 직접 구성하며 Ingress의 기본 개념과 트래픽 흐름을 확인하는 데 초점을 맞췄습니다.

다음과 같은 주제로 아래와 같은 과정으로 심화 과정을 통해 전반적인 CI/CD의 Flow을 익혀보려고 합니다.

1️⃣ Ingress 운영 설정(ConfigMap)

실무에서는 단순히 Ingress를 “붙이는 것”보다, 운영 중 발생하는 이슈를 어떻게 제어하느냐가 훨씬 중요합니다. 다음 단계에서는 Ingress-NGINX ConfigMap과 Ingress annotation의 역할 차이를 중심으로, 운영 환경에서 자주 마주치는 설정들을 직접 다뤄볼 예정입니다.


2️⃣ Git으로 Kubernetes 매니페스트 관리하기 (GitOps의 출발점)

이번 글에서는 로컬에서 kubectl apply로 직접 배포했지만, 실제 업무 환경에서는 매니페스트를 Git으로 관리하는 것이 기본이 됩니다.

  • Kubernetes YAML을 “코드”로 관리하는 방식
  • 디렉토리 단위 배포 전략
  • 변경 이력과 롤백을 고려한 구조

이 과정은 이후 ArgoCD를 통한 GitOps 배포로 자연스럽게 이어집니다.


3️⃣ ArgoCD로 자동 배포 흐름 구성하기

Ingress 구조와 매니페스트 관리가 준비되면, 이제 배포 방식도 다음 단계로 넘어갈 수 있습니다.

  • Git 변경 → 클러스터 자동 반영
  • 선언적 배포와 Drift 관리
  • “kubectl을 직접 치지 않는 배포” 경험

🧩 마무리

이번 글은 쿠버네티스를 ‘설치해본다’는 수준을 넘어서,실제로 외부 트래픽이 클러스터 내부로 어떻게 흘러가는지를 직접 구성하고 확인해보는 데 목적이 있었습니다.

특히,

  • Ingress는 단순한 리소스가 아니라 규칙(선언) 이라는 점
  • 실제 트래픽 처리는 Ingress-NGINX Controller가 담당한다는 점
  • 하나의 진입점에서 Host / Path 기준으로 여러 서비스를 분기할 수 있다는 점

을 직접 손으로 구성해보면서, 문서로만 접했을 때보다 훨씬 명확하게 이해할 수 있었습니다.

WSL2 + k3s 환경은 이러한 개념을 부담 없이 실험해보기에 충분히 좋은 선택지였고, 로컬 환경에서도 실제 운영 환경과 유사한 Ingress 구조를 경험할 수 있다는 점에서 의미 있는 연습이었습니다.

다음 글에서는 이번에 구성한 Ingress-NGINX를 운영 관점에서 다뤄보는 단계로 확장해, 설정 튜닝과 GitOps 흐름까지 자연스럽게 이어가 볼 예정입니다.