Error rendering WebPanel: No renderer found for resource type: velocity Template contents: <meta name="ajs-keyboardshortcut-hash" content="$keyboardShortcutManager.shortcutsHash">
메타 데이터의 끝으로 건너뛰기
메타 데이터의 시작으로 이동

쿠버네티스 

컨테이너 오케스트레이션 중 사실상 표준

여러 서버에 걸쳐있는 컨테이너를 효율적으로 분산/배치/관리하는 기법으로 유용한 기능들을 제공

작은 컨테이너라면 직접 배포하여 사용하면 되지만, 컨테이너 수가 많아지면서 해당 컨테이너를 어디에 배포해야 될지에 대한 결정이 필요해짐

  • CPU, memory resource 고려해야하는 경우
  • 동일 물리서버에 배포해야하는 경우
  • 가용성을 위해 다른 물리서버에 배포해야 하는 경우

이러한 컨테이너를 적절하게 배포하는 스케줄링 역할을 한다. 이 외 정상 작동 체크하고 문제 시 재기동, 모니터링, 삭제등 컨테이너 종합 관리를 해주는 환경이 필요하다. 쿠버네티스는 컨테이너 운영환경 솔루션 중 하나이다.

Object 

쿠버네티스는 Basic Object와 이를 생성하고 관리하는 추가적인 기능을 가진 Controller로 이루어 진다. 이러한 오브젝트의 스펙(설정)이외에 추가적인 메타 정보들로 구성된다.

Basic Object :  k8s에서 배포 및 관리되는 기본적인 구성단위 (기본 오브젝트 만으로 설정, 배포 가능)

  • Pod : 컨테이너화된 애플리케이션
  • Service : 로드밸런서
  • NameSpace : 패키지명 
  • Volume : 디스크

Controller : 기본 오브젝트를 생성하고 관리하는 역할 (편리성)

  • ReplicaSet : Pod 관리 (replica 수 만큼 pod를 배포, 관리) 

  • Deployment : ReplicaSet 추상화된 개념. 실제 운영에서 ReplicatSet보다 Deployment를 사용 

  • StatefulSet  : 상태를 가지는 애플리케이션 운용 시 적합

  • Job, CronJob : 하나 이상의 pod를 생성해 지정된 pod가 정상 종료될 때까지 관리하는 리소스 (batch)
  • ...

Volume : PersistentVolume, PersistentVolumeClaim, StorageClass



파드(Pod)

  • 기본적인 배포 단위(컨테이너가 모인 집합체)
  • 쿠버네티스는 하나의 컨테이너를 개별적으로 배포하지 않고, pod 단위로 배포한다
  • 결합(정합도, 결합도)이 강한 컨테이너를 pod로 묶어 일괄 배포한다 ex) web server + was 
  • 같은 pod를  한 node에 여러개 배치가 가능하고, 여러 노드에 배치가 가능하지만 한 pod가 2개이상의 node에 배치될 수 없다. 

         

참고)https://matthewpalmer.net/kubernetes-app-developer/articles/kubernetes-networking-guide-beginners.html


Pod의 특징

  • Pod 내의 컨테이너는 IP, port를 공유한다 (localhost를 통해 통신 가능)

  • Pod 내의 배포된 컨테이너간에는 디스크 볼륨을 공유할 수 있다.

  • 파드에는 고유한 가상 IP 주소가 할당되고 이 주소를 컨테이너가 공유하므로 컨테이너간 통신이 가능하다. 
    이 가상 IP로 다른 파드와의 통신도 가능하다.

         

[참고]

(실습) Pod 배포하고 상태 확인 및 삭제 해본다.

실습 시 사용되는 동일 및 주요 명령어 

simple-pod.yaml
# 매니페스트 배포 
kubectl apply -f [매티페스트 파일] 

# 매니페스트 삭제 
kubectl delete -f  [매니페스트 파일]

# 클러스터 상태 확인 
kubectl get pod[,replicaset,deployment]

1) Pod 생성 및 배포하기

simple-pod.yaml
apiVersion: v1
kind: Pod      # 쿠버네티스 리소스 유형
metadata:	# 리소스의 각종 메타데이터.(라벨, 리소스 이름)
  name: simple-echo
spec:	# 상세한 스펙
  containers:  # pod에 포함할 컨테이너 list
  - name: nginx
    image: gihyodocker/nginx:latest
    env:
    - name: BACKEND_HOST
      value: localhost:8080 # 파드의IP주소:컨테이너의_포트 
    ports:
    - containerPort: 80		# 컨테이너 접근 시 사용될 포트  
  - name: echo
    image: gihyodocker/echo:latest
    ports:
    - containerPort: 8080
pod 매니페스트 배포
# pod 배포 
kubectl apply -f simple-pod.yaml
파드 안 컨테이너 출력 보기
# pod echo 컨테이너 logs 확인 
kubectl logs -f simple-echo -c echo


2) Pod 상태 확인

파드 상태 확인
# Pod 상태 확인
kubectl get pod
  • 실행화면 ( pod 생성 중 및 생성 되었을 때의 상황이다 )

          

    • READY : 실행 중 컨테이너 수 / 총 컨테이너 수 
    • STATES : ContainerCreating, Running, Terminating


3) Pod 삭제

파드 삭제
# pod 삭제
kubectl delete pod simple-echo
# 매니페스트 파일로 삭제
kubectl delete simple-pod.yaml




서비스(Service)

  • 랜덤으로 IP주소가 생성되는 Pod에 고정적인 엔드포인트로 호출할 수 있는 기능을 제공하는 리소스.

  • 여러 pod에 같은 애플리케이션 운용 시 pod간의 로드밸런싱이 필요한데 이를 서비스가 해결한다.

  • 파드의 집합(replicaset)에 대한 경로, 서비스 디스커버리를 제공하는 리소스

  • 서비스의 역할

    • 지정된 IP 생성한다.  

    • 여러 Pod를 묶어 로드밸런싱을 한다. 

    • 고유한 DNS 이름을 가질 수 있음

       


Controller

레플리카세트(ReplicaSet)

  • 동일한 pod를 여러개로 복제하여 생성/관리하는 리소스 (가용성 확보를 위해)
  • 매니페스트 설정 파일 하나로 여러 파드를 관리할 수 있다.
  • 파드명무작위 접미사가 붙으므로 삭제된 파드는 복원 불가
    → 때문에 stateless application에 적절하다 (webserver, was)

!! state application은 어떻게 처리하는가?


(실습) 

simple-replicaset.yaml
apiVersion: apps/v1
kind: ReplicaSet    # resource type 
metadata:
  name: echo 
  labels:
    app: echo 
spec:
  replicas: 3		# 복제한 pod의 개수 
  selector:		
    matchLabels:
      app: echo 
  template: 		# template 아래는 파드 리소스 정의와 같음
    metadata:
      labels:
        app: echo
    spec:
      containers:
      - name: nginx 
        image: gihyodocker/nginx:latest 
        env: 
        - name: BACKEND_HOST
          value: localhost:8080
        ports:
        - containerPort: 80 
      - name: echo
        image: gihyodocker/echo:latest
        ports:
        - containerPort: 8080
레플리카세트 배포
# 레프리카세트 배포
kubectl apply -f simple-replicaset-yaml

# 생성된 파드 확인
kubectl get pod

# 레프리카세트 삭제
kubectl delete -f simple-replicaset.yaml



디플로이먼트(Deployment)

  • 레플리카세트를 관리하고 다루기 위한 상위 리소스로 애플리케이션 배포(deploy)의 단위
  • 디플로이먼트는 레플리카세트를 제어하여 파드의 개수 증감, 버전 교체가 가능하며, 추가로 이전 버전으로 롤백등 기능을 가진 리소스
  • 레플리카세트의 리비전 관리 가능 (–record)

  • 실제 운영에서는 레플리카세트를 직접 다르기 보다는 디플로이먼트 매니페스트 파일을 통해 운영

       

[이미지 참고]

(실습)

1) 디플로이먼트 생성

simple-deployment.yaml
apiVersion: apps/v1
kind: Deployment		# resource type  
metadata:
  name: echo 
  labels:
    app: echo 
spec:
  replicas: 3 			# replicaset의 개수
  selector:
    matchLabels:
      app: echo 
  template: 			# template 아래는 파드 리소스 정의와 같음
    metadata:
      labels:
        app: echo
    spec:
      containers:
      - name: nginx 
        image: gihyodocker/nginx:latest 
        env: 
        - name: BACKEND_HOST
          value: localhost:8080
        ports:
        - containerPort: 80 
      - name: echo
        image: gihyodocker/echo:latest
        env: 
        - name: HOGE 
          value: fuga 
        ports:
        - containerPort: 8080


디플로이먼트 배포
# 옵션으로 기록을 남기며 클러스터에 반영
kubectl apply -f simple-deployment.yaml --record
# 상태 확인
kubectl get pod,replicaset,deployment --selector app=echo
# 리비전 확인
kubectl rollout history deployment echo

2) 파드 개수 수정

  • simple-deployment.yaml replicas 3→4 수정
  • 수정된 매니페스트 반영
  • 늘어난 파드 갯수 확인 

  

       (수정 전)                                                    (수정 후)


리비전 확인
kubectl rollout history deployment echo

 → 파드의 개수 수정 시 레플리카세트가 새로 생성되지 않으므로 리비전 번호가 변경되지 않음

3) 컨테이너 정의 수정

  • simple-deployment.yaml 파일의 echo 컨에니어 이미지를 gihyodocker/echo:patched로 수정

    디플로이먼트 배포
    # 디플로이먼트 배포
    kubectl apply -f simple-deployment.yaml --record 
    # 리비전 확인
    kubectl rollout history deployment echo
  • NEW ReplicaSet를 생성하여 새로운 이미지의 컨테이너가 있는 Pod를 배치, Old ReplicaSet의 Pod를 제거 (반복)

      >>

4) 롤백

  • 디플로이먼트는 리비전을 관리하므로 특정 버전의 내용을 확인하거나 디플로이먼트 문제 시 이전버전으로 롤백 가능하다.

    디플로이먼트 롤백
    # 리비전 확인 option: --reversion
    kubectl rollout history deployment echo
    # 직전 버전 롤백
    kubectl rollout undo deployment echo
    디플로이먼트 삭제
    # 매니페스트를 이용해 삭제 시 디플로이먼트, 관련된 레플리카세트, 파드 함께 삭제
    kubectl delete --f simple-deployment.yaml

서비스(Service)

  • 파드의 집합(replicaset)에 대한 경로, 서비스 디스커버리를 제공하는 리소스

    서비스의 역할

    • 지정된 IP 생성한다.  

    • 여러 Pod를 묶어 로드밸런싱

      • default) 랜덤으로 부하 분산
      • 특정 클라이언트가 특정 pod로 지속적으로 연결 가능하다  (session affinity)
    • 고유한 DNS 이름을 가질 수 있음

    • 멀티 포트 지원 : 서비스는 하나의 포트가 아닌 여러개의 포트를 동시에 지원 할 수 있다. ex) HTTP, HTTPS

(실습) 3개의 pod(summer, summer, spring) 존재한다. summer인 pod에만 접근 가능한 서비스를 만들어본다.

1. release 속성값이 spring과 summer인 레플리카세트를 정의하고 배포

simple-replicaset-with-label.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: echo-spring
  labels:
    app: echo 
    release: spring 
spec:
  replicas: 1
  selector:				 # 어떤 pod를 묶을 것인지 정의한다 (label selector)
    matchLabels:
      app: echo 
      release: spring    # spring pod 1
  template:
    metadata:
      labels:
        app: echo
        release: spring
    spec:
      containers:
      - name: nginx 
        image: gihyodocker/nginx:latest 
        env: 
        - name: BACKEND_HOST
          value: localhost:8080
        ports:
        - containerPort: 80 
      - name: echo
        image: gihyodocker/echo:latest
        ports:
        - containerPort: 8080
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: echo-summer
  labels:
    app: echo 
    release: summer 
spec:
  replicas: 2
  selector:
    matchLabels:
      app: echo 
      release: summer   # summer pod 2
  template:
    metadata:
      labels:
        app: echo
        release: summer 
    spec:
      containers:
      - name: nginx 
        image: gihyodocker/nginx:latest 
        env: 
        - name: BACKEND_HOST
          value: localhost:8080
        ports:
        - containerPort: 80 
      - name: echo
        image: gihyodocker/echo:latest
        ports:
        - containerPort: 8080
매니페스트 반영
# pod 3개 배포 summer(1), spring(2) 
kubectl apply -f simple-replicaset-with-label.yaml

# pod 확인
kubectl get pod -l app=echo -l release=spring
kubectl get pod -l app=echo -l release=summer


2. release=summer인 파드만 접근할 수 있는 서비스 생성하고 확인

sumple-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: echo 
spec:
  selector: 				# 서비스 대상으로 삼을 파드의 label selector 설정
    app: echo
    release: summer
  ports:                    # multi port 설정 
    - name: http
      port: 80
서비스 생성,확인
# 서비스 생성
kubectl apply -f simple-service.yaml

# 서비스 확인
kubectl get svc echo


3. 트래픽 전달 확인

클러스터 내부에서 요청을 보내 summer pod에 도달하였는지 확인한다

서비스의 default는 clusterIP 이다.

쿠버네티스 클러스터 안에서만 접근할 수 있으므로 쿠버네티스 클러스터에서 디버깅용 임시 컨테이너를 배포하고 확인한다.

HTTP 요청
# 디버깅용 임시 컨테이너 배포
kubectl run -i --rm --tty debug --image=gihyodocker/fundamental:0.1.0 --restart=Never -- bash -il
# HTTP 요청
curl http://echo/


4. 레이블이 summer인 파드에 해당 파드에 접근하였는지 로그 확인

로그 확인
# summer pod명 복사하여 확인
kubectl logs -f [echo summer pod name] -c echo 


서비스의 종류

IP 주소 할당 방식, 연동 서비스등에 따라 크게 4가지로 구분된다.

1) ClusterIP 서비스 (Default)

쿠버네티스 클러스 내부 IP주소에 서비스를 공개하여, 쿠버네티스 클러스터 내에서는 서비스 접근이 가능하나 외부 IP를 할당 받지 못해 외부로부터 접속이 불가능하다.

서비스명으로 네임 레졸루션이 가능 

2) NodePort 서비스 (L4)

  • 클러스터 외부에서 접속할 수 있는 서비스
  • ClusterIP를 만들지만 각 노드에서 서비스 포트로 접속하기 위한 글로벌 포트를 개방한다는 차이점이 있음

        

  • 노드포트 서비스 생성

    simple-service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: echo 
    spec:
      type: NodePort
      selector:
        app: echo
      ports:
        - name: http
          port: 80
    NodePort 서비스로 외부에서 접근
    # 서비스 확인
    kubectl get svc echo
    
    
    # HTTP 요청
    curl http://localhost:30725

    실행결과

          

3) LoadBalancer 서비스

일반적으로 클라우드 벤더에서 제공하는 설정 방식으로 플랫폼에서 제공하는 로드밸런서와 연동하기 위해 사용한다.

외부 IP를 가지는 로드밸런서를 할당하여 외부 접근 가능하다.

  • GCP Cloud Load Balancing, AWS Elastic Load Balancing 지원

4) ExternalName 서비스

  • 쿠버네스트 클러스터에서 외부 호스트를 네임 레졸루션 하기 위한 별명을 제공한다.(셀렉터/포트정의 없음)
    외부 서비스를 쿠버네티스 내부에서 호출하고자 할 때 사용할 수 있다. (RDS, CloudSQL등 사용 시 이용)



인그레스(Ingress) 

외부로 서비스 공개 시 서비스를 NodePort로 노출 시 L4에 TCP단에서 pod를 밸런싱한다

HTTP/HTTPS 경로 기반으로 서비스 제어 필요 시 L7 레벨의 Ingress 사용하여 해결 가능 ( + 로드밸런싱, 인증서 처리등 가능)

[참고]

Ingress의 구현체

  • GKE : 글로벌 로드밸런서
  • NginX ingress controller
  • Kong ingress controller


(상황)

외부에서 쿠버네티스 클러스터 내부로 들어오는 네트워크 요청 처리를 정의한다 

MSA 경우 서비스 하나가 MSA 서비스로 표현되는 경우가 많고, 하나의 URL로  대표되는 경우가 많다. 

MSA 라우팅하기 위해 API Gateway를 사용하는 경우가 많은데 사용 시 관리 포인트가 늘어난다. URL기반의 라우팅 기능만 사용한다면 ingress를 사용하여 처리할 수 있다. 

                     

(실습) Ingress controller를 이용하여 URI, Host 기반의 라우팅을 해본다

1. Ingress controller 배포

ingress-nginx를 사용하며 'ingress-nginx'라는 namespace로 생성 

Ingress controller 배포
# Ingress controller 배포
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

# 실행결과 
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
...


2. Ingress service 생성

외부에서 접근 가능하도록 Loadbalancer 타입의 Ingress service 생성하고 확인한다

Ingress service 생성 및 로드밸런서 확인
# Ingress service 생성
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
# 로드밸런서 생성 여부 실시간 확인 (External IP 확인)
kubectl get service -n ingress-nginx --watch


3. Application 및 서비스를 배포
다른 정보를 가진 deployment, service를 생성한다.

예제용 deployment는 자신의 host 정보를 보여주는 nginx container이다. (각 서비스는 Cluster IP로 외부접근 불가)

 -  deployment : apps/a, apps/b

 -  service : service/a-svc, service/b-svc 

예제 애플리케이션 및 서비스 배포
kubectl apply -f https://gist.githubusercontent.com/NaverCloudPlatformDeveloper/c47620d8d25b2a0e08648f225043adf6/raw/675addde0a56ba727824f30f92a73b40550fb73c/nks-tutorial-hello-service.yaml


4. URI 기반 라우팅 

Path별로 라우팅하여 /a, /b 요청 시 각각 다른 서비스로 연결되도록 설정한다.

4-1) Ingress 생성 ( sample_ingress_uri.yaml 배포 )

Ingress URI 라우팅 설정파일
# sample_ingress_uri.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ab-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
  - http:
      paths:
      - path: /a				# Path(/a) 시 a-svc 접근 
        backend:
          serviceName: a-svc
          servicePort: 80
      - path: /b				# Path(/b) 시 b-svc 접근
        backend:
          serviceName: b-svc
          servicePort: 80


4-2. Ingress 생성 확인 후 URI 라우팅 테스트 

Ingress 생성 확인, 서비스 접속확인
# Ingress 생성 확인
kubectl get service -n ingress-nginx --watch


# 서비스 접속확인
curl http://localhost/a
curl http://localhost/b

로드밸런서 생성 확인 결과 (External IP 확인)

테스트 결과


5. 호스트기반 라우팅

Host 기반 라우팅을 테스트 한다.

동일한 External-IP에 각각 다른 도메인으로 요청 시 다른 서비스로 연결되도록 설정한다

  • a.svc.com으로 요청 시  → a-svc 서비스로 접근
  • b.svc.com으로 요청 시→ b-svc 서비스로 접근

5-0. 이전 URI Ingress 설정 삭제

Ingress 설정 제거
kubectl delete ingress ab-ingress


5-1. Ingress 생성 ( sample_ingress_host.yaml 배포 )

Ingress Host 라우팅 설정파일
# sample-ingress-host.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ab-host-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: a.svc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: a-svc
          servicePort: 80
  - host: b.svc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: b-svc
          servicePort: 80


5-2. Ingress 생성 확인 후 URI 라우팅 테스트 

Ingress 생성 확인, 서비스 접속확인
# Ingress 생성 확인
kubectl get ingress


# 서비스 접속확인
curl -H host:a.svc.com http://localhost
curl -H host:b.svc.com http://localhost

테스트 결과



잡(Job)

  • 하나 이상의 pod를 생성해 지정된 pod가 정상 종료될 때까지 관리하는 리소스

  • 배치 작업 위주의 애플리케이션에 적합  

    • ex1) 원타임으로 파일 변환

    • ex2) 주기적으로 배치 작업하는 경우

  • Job에서 관리되는 pod는 job 종료 시 pod 같이 종료한다. 

  • Job이 생성한 pod는 정상 종료 후에도 삭제되지 않고 남아있어 pod의 로그나 실행 결과를 분석 할 수 있다.

Job 특징

  • completion : 데이터가 커 범위를 나눠 작업해야하는 경우 pod를 순차적으로 여러번 실행할 수 있다. 

  • parallelism : 여러 작업 처리 중 순차적 처리가 필요없는 경우 병렬처리가 가능하다.

  • restartPolicy : Job 결과가 실패 일 경우 설정에 따라 재실행 할 지 설정할 수 있다.
    • always : Pod가 종료하면 항상 재실행하여 실행 상태 유지 (pod는 이 값이 기본)
    • Never : 실패 시 pod를 재생성해 실행
    • OnFailure : 실패 시 실패한 pod를 재실행
  • restartPolicy : 파드 종료 후 재실행 여부로 Job 리소스 Always로 설정불가능(Never 혹은 OnFailure 설정)

simple-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pingpong 
  labels:
    app: pingpong
spec:
  parallelism: 3 # 동시에 실행하는 파드 수 지정 (병렬 실행)
  template:
    metadata:
      labels:
        app: pingpong 
    spec:
      containers:
      - name: pingpong 
        image: gihyodocker/alpine:bash
        command: ["/bin/sh"]
        args:
          - "-c" 
          - |
            echo [`date`] ping!
            sleep 10
            echo [`date`] pong!
      restartPolicy: Never # 파드 종료 후 재실행 여부
매니페스트 적용
# 파드 실행
kubectl apply -f simple-job.yaml
# 로그 확인
kubectl logs -l app=pingpong
# 파드 상태 확인 (Completed: 종료됨)
kubectl get pod -l app=pingpong



크론잡(CronJob)

  • 배치성 작업은 주기적으로 자동화하여 실행이 필요하다.
  • Job은 단 한번만 실행되는데 반해 CronJob 스케줄을 지정해 정기적으로 pod를 실행할 수 있다.
  • 컨테이너에 친화적인 특성을 유지하며 이벤트 애플리케이션을 따로 사용하지 않으며 스케줄에 따른 작업을 수행할 수 있다.
  • 아래의 예제처럼 알파인 리눅스 등 경량 이미지 컨테이너에 작업을 매니페스트 파일로 기술하거나 구현을 매니페스트 파일 대신 도커 이미지에 포함시키는 방법 사용

(실습) 위 실습과 동일하며 1초마다 실행한다

simple-cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: pingpong 
spec:
  schedule: "*/1 * * * *" # Cron과 같은 포맷으로 실행할 스케줄 정의
  jobTemplate: # 파드 정의
    spec:
      template:
        metadata:
          labels:
            app: pingpong 
        spec:
          containers:
          - name: pingpong 
            image: gihyodocker/alpine:bash
            command: ["/bin/sh"]
            args:
              - "-c" 
              - |
                echo [`date`] ping!
                sleep 10
                echo [`date`] pong!
          restartPolicy: OnFailure
크론잡 실행
# cronjob 배포
kubectl apply -f simple-cronjob.yaml
# job 확인
kubectl get job -l app=pingpong
# 로그 확인
kubectl logs -l app=pingpong




참고자료

구글 클라우드 스터디잼

Naver cloud platform Ingress 튜토리얼

조대협님 blog

alice blog


  • 레이블 없음

1 개의 댓글

  1. 실습자료 다운로드

    (위 실습과 파일명이 다를 수 있습니다. README 참고하세요)

    https://github.com/juyoungyoo/slipp-k8s-sample.git