1. What is Kind?

Kind ๋Š” ๋กœ์ปฌ ์ปดํ“จํ„ฐ ํ™˜๊ฒฝ์— ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ์†์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ์„ค์น˜ ํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ๋„๊ตฌ ์ž…๋‹ˆ๋‹ค.

Kind๋Š” Go ์–ธ์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด ์กŒ์œผ๋ฉฐ, Docker ์ด๋ฏธ์ง€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ kubeadm์„ ์ด์šฉํ•˜์—ฌ ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๋ฐฐํฌ ํ•ฉ๋‹ˆ๋‹ค.

kind ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€ : kind.sigs.k8s.io

kind์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋ฉ€ํ‹ฐ๋…ธ๋“œ ๊ธฐ๋ฐ˜ ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋กœ์ปฌ ํด๋Ÿฌ์Šคํ„ฐ ๊ตฌ์ถ• ๋„๊ตฌ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋„๊ตฌ๋ช…๊ณต์‹ URL
minikubehttps://minikube.sigs.k8s.io
k3shttps://k3s.io
MicroK8shttps://microk8s.io
k3dhttps://k3d.io

2. Kind ์„ค์น˜ ํ•˜๊ธฐ

์„ค์น˜ ๊ฐ€์ด๋“œ ์›๋ณธ URL : https://kind.sigs.k8s.io/docs/user/quick-start/#installation

- MacOS

  brew install kind
  

- Windows

  choco install kind
  

- Linux

  curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.15.0/kind-windows-amd64
Move-Item .\kind-windows-amd64.exe c:\some-dir-in-your-PATH\kind.exe
  

- Kubectx, Kubens ์„ค์น˜

  choco install kubectx
choco install kubens
  

3. Kind ๋กœ ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ (์ดˆ๊ฐ„๋‹จ)

- ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ

  kind create cluster # Default cluster context ์ด๋ฆ„์€ 'kind' ๋กœ ์ƒ์„ฑ
kind create cluster --name dangtong # cluster context ์ด๋ฆ„์„ 'dangtong' ์œผ๋กœ ์ง€์ •
  

- ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ ํ™•์ธ

  kind get clusters
kubectl cluster-info --context dangtong
  

- ํด๋Ÿฌ์Šคํ„ฐ ์‚ญ์ œ

  kind delete cluster

kind delete clusters kind-local-cluster
  

4. ์„ค์ • ํŒŒ์ผ์„ ์ด์šฉํ•œ Kind ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ

- ์„ค์ • ํŒŒ์ผ์„ ์ด์šฉํ•œ ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ

์„ค์ •ํŒŒ์ผ์„ ์ด์šฉํ•ด์„œ kind ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  kind create cluster --config kind-example-config.yaml
  

- 3๊ฐœ ๋…ธ๋“œ ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ ์˜ˆ์‹œ

3๊ฐœ ๋…ธ๋“œ(1 controller, 2worker) ํด๋Ÿฌ์Šคํ„ฐ ์„ค์ •

  # three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
  

- 6๊ฐœ ๋…ธ๋“œ ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ ์˜ˆ์‹œ

  kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: control-plane
- role: control-plane
- role: worker
- role: worker
- role: worker
  

- ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฒ„์ „ ์„ค์ •

์ฟ ๋ฒ„๋„คํ‹ฐ์Šค ๋ฒ„์ „์— ๋”ฐ๋ฅธ ์ด๋ฏธ์ง€๋Š” ๋งํฌ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ : https://github.com/kubernetes-sigs/kind/releases

  kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  image: kindest/node:v1.16.4@sha256:b91a2c2317a000f3a783489dfb755064177dbc3a0b2f4147d50f04825d016f55
- role: worker
  image: kindest/node:v1.16.4@sha256:b91a2c2317a000f3a783489dfb755064177dbc3a0b2f4147d50f04825d016f55
  

- ๋„คํŠธ์›Œํฌ ์„ค์ •

  • Pod Subnet ์„ค์ •
  kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  podSubnet: "10.244.0.0/16"
  
  • Service Subnet ์„ค์ •
  kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  serviceSubnet: "10.96.0.0/12"
  
  • Default CNI ์„ค์ •

Caliaco ์™„ ๊ฐ™์€ 3rd party CNI ์‚ฌ์šฉ์„ ์œ„ํ•ด์„œ๋Š” default CNI ์„ค์น˜๋ฅผ ํ•˜์ง€ ๋ง์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  # default CNI๊ฐ€ ์„ค์น˜ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  disableDefaultCNI: true
  
  • kube-proxy ๋ชจ๋“œ ์„ค์ •

iptables ๋˜๋Š” IPVS ์ค‘์— ์„ ํƒํ•ด์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ. default ๋Š” iptables

  kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  kubeProxyMode: "ipvs"
  

- ์ตœ์ข… ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ

  kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: cwave-cluster
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  image: kindest/node:v1.32.5
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
- role: worker
  image: kindest/node:v1.32.5
- role: worker
  image: kindest/node:v1.32.5
networking:
  serviceSubnet: "10.120.0.0/16"
  podSubnet: "10.110.0.0/16"
  
  • ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ
  # ์ƒ์„ฑ
kind create cluster  --config ./3-node-cluster.yml

# ์‚ญ์ œ
kind delete cluster --name cwave-cluster
  
  • ํด๋Ÿฌ์Šคํ„ฐ ์ ‘์† ์ •๋ณด ํ™•์ธ
  kind get kubeconfig --internal --name cwave-cluster
  
  • IDE ์ปจํ…Œ์ด๋„ˆ์— Kind ๋„คํŠธ์›Œํฌ ์ฃผ์„์ œ๊ฑฐ
  name: "aws-cicd-practice"
services:
  code-server:
    image: dangtong76/cicd-devops-ide:arm64-v2 
    container_name: "ide"
    networks: # ๋„คํŠธ์›Œํฌ ํ•ญ๋ชฉ ์ฃผ์„์ œ๊ฑฐ
      - kind_network 
    environment:
      AUTH: none
      #FILE__PASSWORD: /run/secrets/code-server-password
    env_file:
      - .env
    working_dir: /code
    ports:
      - "8080:8080" # istory-web
      - "1314:1314" # hugo port 1
      - "1315:1315" # hugo port 2
      - "8444:8443" # vscode service port
      - "5500:5500"
    # secrets:s
    #   - code-server-password
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - devops-cicd-apps:/code/devops-cicd-apps
      - devops-cicd-vscode:/config
networks: # ๋„คํŠธ์›Œํฌ ํ•ญ๋ชฉ ์ฃผ์„์ œ๊ฑฐ
  kind_network:
    name: kind
    external: true
volumes:
  devops-cicd-apps:
    external: true
    name: devops-cicd-apps
  devops-cicd-vscode:
    external: true
    name: devops-cicd-vscode
  
  services:
  code-server:
    image: dangtong76/cicd-devops-ide:arm64-v2
    container_name: ide
    networks:
      - kind_network
    environment:
      AUTH: none
    env_file:
      - .env
    working_dir: /code
    ports:
      - "8080:8080"
      - "8444:8443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - devops-cicd-apps:/code/devops-cicd-apps
      - devops-cicd-vscode:/config

  nfs-server:
    image: itsthenetwork/nfs-server-alpine:latest
    container_name: nfs-server
    networks:
      - kind_network
    privileged: true
    environment:
      - SHARED_DIRECTORY=/exports
      # ์•„๋ž˜๋Š” ์‹ค์Šต์šฉ(์ ‘๊ทผ ์‰ฌ์›€). ์šด์˜์—์„  ์ œํ•œ ํ•„์š”
      - PERMITTED="172.21.0.0/16"
    volumes:
      - nfs-server-volume:/exports
    # NFS ํฌํŠธ (๊ฐ™์€ docker ๋„คํŠธ์›Œํฌ๋ฉด ์—†์–ด๋„ ๋˜์ง€๋งŒ, ๋””๋ฒ„๊น…์šฉ์œผ๋กœ ์—ด์–ด๋‘๋ฉด ํŽธํ•จ)
    ports:
      - "2049:2049"
      - "111:111"
      - "20048:20048"
    restart: unless-stopped
    

networks:
  kind_network:
    name: kind
    external: true

volumes:
  nfs-server-volume:
    external: true
    name: nfs-server-volume
  
  • ~/.kube/config ํŒŒ์ผ์˜ API ์ ‘์† ์ •๋ณด ์ˆ˜์ •
  docker network inspect kind # cwave-cluster-control-plane ์˜ IP์ฃผ์†Œ ํ™•์ธ

vi ~/.kube/config
  

์•„๋ž˜์™€ ๊ฐ™์ด kubectl ์ ‘์† ์ •๋ณด ์ˆ˜์ •

  apiVersion: v1
clusters:
- cluster:
    server: https://<cwave-cluster-control-plane ์˜ IP์ฃผ์†Œ>:6443
  name: kind-cwave-cluster
  

API ์„œ๋ฒ„๊ฐ€ ์ ‘์† ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ

  kubectl get no 

### ์ถœ๋ ฅ์ด ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๋‚˜์˜ค๋ฉด ์ •์ƒ
root@85bed0161b08:~/.kube# kubectl get no
NAME                          STATUS   ROLES           AGE   VERSION
cwave-cluster-control-plane   Ready    control-plane   30m   v1.32.5
cwave-cluster-worker          Ready    <none>          30m   v1.32.5
cwave-cluster-worker2         Ready    <none>          30m   v1.32.5
  

5. MetalLB ์„ค์น˜

- MetalLB ์„ค์น˜

  # kubectl ์ด์šฉ
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.15.2/config/manifests/metallb-native.yaml

# helm ์ฐจํŠธ ์ด์šฉ
helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb
  

- kind network์˜ IP ๋Œ€์—ญ ํ™•์ธ

  docker network inspect kind --format '{{(index .IPAM.Config 0).Subnet}}'
  

์ถœ๋ ฅ ์˜ˆ์‹œ : 172.20.0.0/16

- MetalLB ์„ค์ •

  apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: cwave-pool
  namespace: metallb-system
spec:
  addresses:
  - 172.20.100.100-172.20.100.200 # Docker kind ๋„คํŠธ์›Œํฌ์˜ IP ๋Œ€์—ญ์œผ๋กœ ๊ตฌ๊ฐ„ ํ• ๋‹น
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: cwave-loadbalancer-advertisement
  namespace: metallb-system
spec:
  ipAddressPools:
  - cwave-pool
  

- MetalLB ์ •์ƒ ํ™•์ธ

  apiVersion: apps/v1
kind: Deployment
metadata:
  name: lb-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: lb-test
  template:
    metadata:
      labels:
        app: lb-test
    spec:
      containers:
        - name: app
          image: hashicorp/http-echo:0.2.3
          args:
            - "-text=hello-metallb"
          ports:
            - containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
  name: lb-test
spec:
  type: LoadBalancer
  selector:
    app: lb-test
  ports:
    - port: 80
      targetPort: 5678
  
  curl http://172.21.100.100
  

hello-metallb ๋ผ๊ณ  ๋‚˜์˜ค๋ฉด ์ •์ƒ

6. Ingress ๋ฐ LoadBalancer ์„ค์ •

Ingress ๋ฐ Loadbalancer ๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” KIND ๋ฅผ ์ด์šฉํ•œ ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ์‹œ extraPortMapping ์„ค์ •์„ ํ•˜๊ณ , kubeadmํˆด์„ ํ†ตํ•ด custom node label ์„ ๋…ธ๋“œ์— ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

- Ingress ๊ฐ€๋Šฅํ•œ ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ

kind ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ extraPortMappings ๋ฐ node-lables ์„ค์ •๊ณผ ํ•จ๊ป˜ ์ƒ์„ฑ ํ•ฉ๋‹ˆ๋‹ค.

  • ExtreaPortMappings : ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ๊ฐ€ 80 ๋ฐ 443 ํฌํŠธ๋ฅผ ํ†ตํ•ด Ingress Controller๋กœ ์š”์ฒญ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • node–labels : Ingress Controller ๊ฐ€ ํŠน์ • ๋ผ๋ฒจ์„ ๊ฐ€์ง„ ๋…ธ๋“œ์—์„œ๋งŒ ์ˆ˜ํ–‰ ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
EOF
  

- Countour Ingress ์ƒ์„ฑ

  • Contour ์„ค์น˜
  kubectl apply -f https://projectcontour.io/quickstart/contour.yaml
  
  • Contour ์„ค์ • ์—…๋ฐ์ดํŠธ
  kubectl patch daemonsets -n projectcontour envoy -p '{"spec":{"template":{"spec":{"nodeSelector":{"ingress-ready":"true"},"tolerations":[{"key":"node-role.kubernetes.io/control-plane","operator":"Equal","effect":"NoSchedule"},{"key":"node-role.kubernetes.io/master","operator":"Equal","effect":"NoSchedule"}]}}}}'
  

- Kong Ingress ์ƒ์„ฑ

  • Kong ์„ค์น˜
  kubectl apply -f https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/master/deploy/single/all-in-one-dbless.yaml
  
  • Kong ์„ค์ • ์—…๋ฐ์ดํŠธ
  kubectl patch deployment -n kong ingress-kong -p '{"spec":{"template":{"spec":{"containers":[{"name":"proxy","ports":[{"containerPort":8000,"hostPort":80,"name":"proxy","protocol":"TCP"},{"containerPort":8443,"hostPort":443,"name":"proxy-ssl","protocol":"TCP"}]}],"nodeSelector":{"ingress-ready":"true"},"tolerations":[{"key":"node-role.kubernetes.io/control-plane","operator":"Equal","effect":"NoSchedule"},{"key":"node-role.kubernetes.io/master","operator":"Equal","effect":"NoSchedule"}]}}}}'
  

- Nginx Ingress ์ƒ์„ฑ

  • Nginx ์„ค์น˜
  kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
  
  • Nginx ์„ค์ • ์—…๋ฐ์ดํŠธ
  kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=90s
  

- Ingress ์‚ฌ์šฉ ์˜ˆ์ œ

  kind: Pod
apiVersion: v1
metadata:
  name: foo-app
  labels:
    app: foo
spec:
  containers:
  - name: foo-app
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=foo"
---
kind: Service
apiVersion: v1
metadata:
  name: foo-service
spec:
  selector:
    app: foo
  ports:
  # Default port used by the image
  - port: 5678
---
kind: Pod
apiVersion: v1
metadata:
  name: bar-app
  labels:
    app: bar
spec:
  containers:
  - name: bar-app
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=bar"
---
kind: Service
apiVersion: v1
metadata:
  name: bar-service
spec:
  selector:
    app: bar
  ports:
  # Default port used by the image
  - port: 5678
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  rules:
  - http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: foo-service
            port:
              number: 5678
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: bar-service
            port:
              number: 5678
---
  

7. Geteway API ๊ธฐ๋Šฅ ์„ค์ •

- Gateway API CRD ์„ค์น˜

  kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml
  

- Contour Gateway Provisioner ์„ค์น˜

  kubectl apply -f https://projectcontour.io/quickstart/contour-gateway-provisioner.yaml
  
  kubectl get pod -n projectcontour -w
  

- ์ •์ƒ ์„ค์ • ํ™•์ธ ํ…Œ์ŠคํŠธ

  apiVersion: v1
kind: Namespace
metadata:
  name: tcp-lab
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: tcp-lab
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:7.2
          ports:
            - containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  name: redis-svc
  namespace: tcp-lab
spec:
  selector:
    app: redis
  ports:
    - name: redis
      port: 6379
      targetPort: 6379
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: contour-gwclass
spec:
  controllerName: projectcontour.io/gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: tcp-gateway
  namespace: tcp-lab
spec:
  gatewayClassName: contour-gwclass
  listeners:
    - name: tcp-redis
      protocol: TCP
      port: 6379
      allowedRoutes:
        namespaces:
          from: Same
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TCPRoute
metadata:
  name: redis-tcproute
  namespace: tcp-lab
spec:
  parentRefs:
    - name: tcp-gateway
      sectionName: tcp-redis
  rules:
    - backendRefs:
        - name: redis-svc
          port: 6379
  
  kubectl apply -f tcp-contour-lab.yaml
  
  kubectl get gateway -n tcp-lab
kubectl get svc -n tcp-lab
  
  redis-cli -h <GATEWAY-ADDRESS> -p 6379 ping
# PONG ๋‚˜์˜ค๋ฉด ์„ฑ๊ณต
  

8. Storage ๊ธฐ๋Šฅ ์„ค์ •

- NFS ์ปจํ…Œ์ด๋„ˆ๊ฐ€ kind ๋„คํŠธ์›Œํฌ์— ๋ถ™์–ด ์žˆ๋Š”์ง€ ํ™•์ธ

  docker inspect cwave-nfs-container --format '{{json .NetworkSettings.Networks.kind}}'
  

nfs ๋‚ด๋ถ€์—์„œ ํ™•์ธ

  docker exec -it cwave-nfs-container exportfs -v
  

kind ๋…ธ๋“œ์—์„œ ํ™•์ธ

  docker exec -it kind-control-plane bash -lc '
mkdir -p /mnt/test &&
mount -t nfs -o vers=4 cwave-nfs-container:/ /mnt/test &&
echo ok-$(date) > /mnt/test/ok.txt &&
cat /mnt/test/ok.txt
'
  

- NFS ํ…Œ์ŠคํŠธ

  • nfs-pvc-lab.yaml
  apiVersion: v1
kind: Namespace
metadata:
  name: storage-lab
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs-local
  nfs:
    # โœ… ํ•ต์‹ฌ: docker network(kind) ์•ˆ์—์„œ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„์œผ๋กœ ์ ‘๊ทผ
    server: cwave-nfs-container
    path: /exports
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
  namespace: storage-lab
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: nfs-local
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: pvc-test
  namespace: storage-lab
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh","-c","echo $(date) from $(hostname) >> /data/out.txt; sleep 3600"]
      volumeMounts:
        - name: nfs-vol
          mountPath: /data
  volumes:
    - name: nfs-vol
      persistentVolumeClaim:
        claimName: nfs-pvc
  

- ์Šคํ† ๋ฆฌ์ง€ ํ™•์ธ

  # kubernetes ๋‚ด์—์„œ ํ™•์ธ
kubectl apply -f nfs-pvc-lab.yaml
kubectl get pv
kubectl get pvc -n storage-lab
kubectl get pod -n storage-lab
kubectl exec -n storage-lab -it pvc-test -- cat /data/out.txt
kubectl exec -n storage-lab -it pvc-test -- ls /data

# Desktop ํ™˜๊ฒฝ์˜ nfs ์ปจํ…Œ์ด๋„ˆ ์„œ๋น„์Šค์—์„œ ํ™•์ธ
docker ps -a # nfs ์ปจํ…Œ์ด๋„ˆ ์ปจํ…Œ์ด๋„ˆ ID ํ™•์ธ
docker exec -it 699a618f12ca ls /exports
docker exec -it rm -f /exports/out.txt
  

opencode ์„ค์น˜ ๋ฐ ์‚ฌ์šฉ๋ฒ•

opencode ์„ค์น˜

๋ฐฑ์•ค๋“œ LLM ์„ค์ •

  • ๋กœ์ปฌ LLM ์„ค์ • ์˜ˆ์ œ
  {
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "lmstudio": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "LM Studio (local)",
      "options": {
        "baseURL": "http://127.0.0.1:1234/v1"
      },
      "models": {
        "google/gemma-3-12b": {
          "name": "Gemma 3-12b (local)"
        },
        "qwen/qwen3-vl-4b": {
          "name": "Qwen3-VL-4b (local)"
        },
      }
    }
  }
}
  

Agent ์ƒ์„ฑ ๋ฐ ์‚ฌ์šฉ ๋ฐฉ๋ฒ• ๋ถ„์„

1. Agent ์ƒ์„ฑ ๋ฐฉ๋ฒ•๋“ค

๋ฐฉ๋ฒ• A: ๋ช…๋ น์–ด๋กœ ์ƒ์„ฑ
opencode agent create

  • ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•˜๊ฒŒ Agent ์ƒ์„ฑ
  • ์ „์—ญ/ํ”„๋กœ์ ํŠธ๋ณ„ ์ €์žฅ ์œ„์น˜ ์„ ํƒ ๊ฐ€๋Šฅ
    ๋ฐฉ๋ฒ• B: JSON ์„ค์ •
    opencode.json์— ์ง์ ‘ ์„ค์ •:
  {
  agent: {
    custom-agent: {
      description: ์ „๋ฌธ๊ฐ€ Assistant,
      mode: primary,
      model: opencode/big-pickle,
      prompt: ๋‹น์‹ ์€... ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
    }
  }
}
  

๋ฐฉ๋ฒ• C: Markdown ํŒŒ์ผ
~/.config/opencode/agent/ ๋˜๋Š” .opencode/agent/์— .md ํŒŒ์ผ๋กœ ์ƒ์„ฑ
2. Agent ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
Primary Agent ์ „ํ™˜

  • Tab ํ‚ค๋กœ Build โ†” Plan โ†” Custom Agent ์ˆœํ™˜
  • ํ˜„์žฌ ํ™œ์„ฑํ™”๋œ Agent๊ฐ€ ์‘๋‹ต
    Subagent ํ˜ธ์ถœ
  • ๋ฉ”์‹œ์ง€์—์„œ @agent-name์œผ๋กœ ์ง์ ‘ ํ˜ธ์ถœ
  • ์˜ˆ: @custom-agent React ์ปดํฌ๋„ŒํŠธ ์ตœ์ ํ™” ๋ฐฉ๋ฒ• ์•Œ๋ ค์ค˜