본문 바로가기
DevOps

Skaffold와 Cloud Code

by NEMNE 2022. 7. 4.

일반적으로 쿠버네티스를 포함한 환경에서 개발을 한다면 수정된 코드를 테스트하기란 복잡한 절차가 따른다.

그러나 이를 효과적으로 단축하여 개발 효율성을 향상할 수 있는 방법이 있는데 바로 Skaffold이다.

 

이번 글에서는 Skaffold와 추가적으로 Skaffold를 효과적으로 사용할 수 있게 도와주는 IDE Extension인 Cloud Code에 대해서도 소개해보려고 한다.

 

Skaffold란?

Skaffold는 구글에서 개발한 Command Line Tool로 쿠버네티스 어플리케이션을 위한 지속적인 배포를 쉽게 가능하게 해 준다. 즉, 내가 개발한 애플리케이션의 빌드, 푸시, 배포 등의 워크로드를 다루는 툴이며, 코드를 작성하는 것만 집중할 수 있도록 도와준다.

 

Skaffold와 일반 개발과정 차이

일반적인 K8s 포함 어플리케이션 개발 과정

  1. 소스코드를 추가/수정한다.
  2. Dockerfile를 통해 도커 이미지를 생성한다.
  3. K8s YAML 파일을 생성하여 적절한 워크로드를 작성한다.
  4. kubectl apply -f ~ 를 통해서 워크로드를 실행시킨다.
  5. K8s 클러스터에 등록된 어플리케이션을 확인 후 수정 사항이 생기면 1번 과정부터 다시 반복한다.

⇒ 변경된 소스코드를 확인하기까지 거쳐야할 작업들이 많다.

 

 

skaffold를 사용한 K8s 포함 어플리케이션 개발 과정

  1. 소스코드를 추가/수정한다.
  2. skaffold dev를 실행한다.
    • 사전에 K8s manifest, skaffold manifest가 구성되어 있음
    • 수정사항이 생기면 해당 소스코드를 변경하고 저장만 하면 반영이 된다.

⇒ nodemon처럼 코드 수정만 하면 자동으로 빌드를 해준다.

 

Skaffold의 장점

  • 가볍다.
    • 별도로 install를 해서 사용하기 때문에 클라이언트 사이드에 존재하므로 클러스터에는 아무런 영향이 없다.
  • 프로젝트 편리성
    • 별도의 실행 가이드라인 문서를 작성하지 않아도 git clone → skaffold run을 통해서 실행할 수 있다.
    • Profiles, Local User Config, Environment Variables, Flags을 통해 쉽게 다른 크로스 환경을 포함할 수 있다.
  • 프로젝트를 위한 Pluggable 방식의 선언적인 설정
    • skaffold init 명령어로 내 레포지토리에 있는 여러 설정 파일(Dokcerfile, Manifests)들을 탐지하여 쉽게 만들 수 있다.
    • 여러 컴포넌트로 구성된 어플리케이션을 지원한다.
    • 어떤 빌드나 배포 툴에 동합할 수 있는 Pluggable 아키텍처를 지원한다.
  • 개발에 최적화 되어 있다.
    • 수정에 따른 즉각적인 피드백 가능
  • 구글이 관리하며 커뮤니티가 활성화되어 있다.

Github Star 수만 보더라도 생각보다 많은 개발자의 관심을 받고 있다.

 

 

파이프라인

  1. 소스코드 변경을 탐지한다.
  2. 변경된 소스코드를 반영한 새로운 아티팩트로 빌드한다.
  3. 새로운 아티팩트를 테스트한다.
  4. 새로운 아티팩트를 설정한 정책에 맞게 태그를 추가해준다.
  5. 새로운 아티팩트가 적용된 메니페스트를 렌더링 한다.
  6. 매니페스트들을 배포한다.
  7. 워크로드부터 log를 관찰하며 리소스들을 포트포워딩 해준다.
  8. 작업이 끝났으면 매니페스트와 이미지들을 클린업 해준다.

 

Skaffold 사용법

E2E 기본 명령어

  • skaffold dev : 코드 변화에 따른 지속적인 빌드 & 배포 (K8s까지 적용되는 nodemon이라고 생각하면 된다.)
    • 어플리케이션 소스코드에 변화가 발생한다면 새로운 이미지로 재 빌드하고 이 것을 다시 내 클러스터에 배포한다.
    • File Sync(변경된 파일 반영), Build(아티팩트 빌드), Test(커스텀 테스트, 컨테이너 구조 텍스트), Deploy 작업들이 수행된다.
    • 변경된 어플리케이션을 담고 있는 컨테이너 이미지는 새로운 태그로 생성되어 저장된다. (dev 환경 종료 후 삭제하고 싶다면 사전에 Image pruning 설정을 하면 된다.)

  • skaffold run : 가장 간단한 Continuous Delivery Setup으로 build, deploy, render, apply 작업을 실행한다.
  • skaffold debug : 어플리케이션 내 소스코드를 디버깅할 수 있다.
    • Skaffold에서는 해당 명령어를 사용하는 방법보다는 cloud code extension을 사용하는 방법을 권장한다. (일반적으로 IDE를 통해서 디버깅을 하는 것이 더 편하기 때문이다.)
  • skaffold init : 명령어를 실행한 위치를 기반으로 하위 디렉터리들을 다 조회하여 적절한 skaffold.yaml 파일들 생성한다

 

Skaffold.yaml 작성

기본 구조

처음부터 작성하기보다는 skaffold init 명령어를 사용하여 수정하는 방식을 추천한다.

 

skaffold init 생성 예시

apiVersion: skaffold/v2beta28
kind: Config
metadata:
  name: so-s-poc-nemne
build:
  artifacts:
  - image: dlatqdlatq/poc-api
    context: api
    docker:
      dockerfile: Dockerfile
deploy:
  kubectl:
    manifests:
    - api/kubernetes-manifests/api.cluster-role.yaml
    - api/kubernetes-manifests/api.deployment.yaml
    - api/kubernetes-manifests/api.service-account.yaml
    - api/kubernetes-manifests/api.service.yaml
    - kubernetes-manifests/test.ingress.yaml

 

 

예제

structure test 적용

apiVersion: skaffold/v2beta29
kind: Config
build:
  artifacts:
  - image: skaffold-example
test:
  - image: skaffold-example
    structureTests:
      - ./test/*
deploy:
  kubectl:
    manifests:
      - k8s-*
profiles:
  - name: test
    test:
      - image: skaffold-example
        structureTests:
          - ./test/profile_structure_test.yaml

---
# profile_structure_test.yaml

schemaVersion: 2.0.0

fileExistenceTests:
  - name: 'no go binary'
    path: '/usr/bin/go'
    shouldExist: false

 

profiles 적용 → skaffold dev --profile staging-profile로 실행 가능

apiVersion: skaffold/v2beta29
kind: Config
build:
  # only build and deploy "world-service" on main profile
  artifacts:
  - image: skaffold-world
    context: world-service
deploy:
  kubectl:
    manifests:
      - 'world-service/*.yaml'

profiles:
  - name: minikube-profile
    # automatically activate this profile when current context is "minikube"
    activation:
      - kubeContext: minikube
    build:
      # only build and deploy "hello-service" on minikube profile
      artifacts:
        - image: skaffold-hello
          context: hello-service
    deploy:
      kubectl:
        manifests:
          - 'hello-service/*.yaml'

  - name: staging-profile
    build:
      # build and deploy both services on "staging"
      artifacts:
        - image: skaffold-hello
          context: hello-service
        - image: skaffold-world
          context: world-service
    deploy:
      # use context "staging" for staging-profile
      kubeContext: staging
      kubectl:
        manifests:
          - '**/*.yaml'

 

lifecycle hook 적용(베타, 알파 버전이므로 사용 시 주의)

apiVersion: skaffold/v2beta29
kind: Config
build:
  artifacts:
  - image: hooks-example
    hooks:
      before:
        - command: ["sh", "-c", "./hook.sh"]
          os: [darwin, linux]
          dir: .
        - command: ["cmd.exe", "/C", "hook.bat"]
          os: [windows]
          dir: .
      after:
        - command: ["sh", "-c", "docker images $SKAFFOLD_IMAGE --digests"]
          os: [darwin, linux]
        - command: ["cmd.exe", "/C", "docker images %SKAFFOLD_IMAGE% --digests"]
          os: [windows]
    sync: 
      manual:
        - src: 'hello.txt'
          dest: .
      hooks:
        before:
          - host:
              command: ["sh", "-c", "echo file changes detected: $SKAFFOLD_FILES_ADDED_OR_MODIFIED"]
              os: [darwin, linux]
          - host:
              command: ["cmd.exe", "/C", "echo file changes detected: %SKAFFOLD_FILES_ADDED_OR_MODIFIED%"]
              os: [windows]
        after:
          - container:
              command: ["sh", "-c", "set -x; kill -HUP 1"]
deploy:
  kubectl:
    manifests:
      - deployment.yaml
    hooks:
      before:
        - host:
            command: ["sh", "-c", "echo pre-deploy host hook running on $(hostname)!"]
            os: [darwin, linux]
        - container:
            # this will only run when there's a matching container from a previous deploy iteration like in `skaffold dev` 
            command: ["sh", "-c", "echo pre-deploy container hook running on $(hostname)!"]
            containerName: hooks-example*
            podName: hooks-example-deployment*
      after:
        - host:
            command: ["sh", "-c", "echo post-deploy host hook running on $(hostname)!"]
        - container:
            command: ["sh", "-c", "echo post-deploy container hook running on $(hostname)!"]
            containerName: hooks-example* # use a glob pattern to prefix-match the container name and pod name for deployments, stateful-sets, etc.
            podName: hooks-example-deployment*

 

helm 적용

apiVersion: skaffold/v2beta29
kind: Config
build:
  artifacts:
  - image: skaffold-helm
deploy:
  helm:
    releases:
    - name: skaffold-helm
      chartPath: charts

 

Skaffold를 사용한다면

  • 사실 Skaffold만을 사용하는 것보다는 Cloud Code와 함께 사용하는 것이 좋다.
    • 간단한 빌드, 수정, 디버깅
    • 클러스터 상태 확인
    • 유저 친화적인 파드 로깅 UI
    • IDE Extension으로 편리한 설치
    • Local, GKE, EKS 등 다양한 클라우드 환경 및 로컬 환경 클러스터 지원
  • Skaffold는 빠른 CD를 할 수 있다는 장점이 있지만 버전 관리나 동적 manifest를 만들지 못한다. 따라서 이러한 단점을 보완할 helm과 같이 사용하는 것이 좋다.

 

Cloud Code

구글에서 제공하는 IDE 플러그인(인텔리제이, VSCode 등에서 지원)으로 IDE에서 쉽게 쿠버네티스를 실행, 디버깅, 로그 분석, 워크로드 조작 등을 지원한다.

또한 구글에서 제공하는 만큼 Google Cloud에 있는 서비스들을 쉽게 이용할 수 있다. (반대로 Google Cloud 외 클라우드 서비스들은 이용하기 어렵다.)

 

Skaffold와 Cloud Code가 함께 사용하는 것이 좋은 이유는 한 번의 클릭으로 Skaffold를 실행시키고 그것을 시각화하여 볼 수 있기 때문에 관리가 용이하다.

 

 

위 Run On Kubernetes 버튼만 누르면 자동으로 왼쪽에 있는 Skaffold.yaml이 실행되고 왼쪽에서 어떤 리소스가 존재하고 있는지 쉽게 확인할 수 있다.

 

Debug on Kubernetes 버튼을 누르면 어플리케이션 레벨에서 디버깅을 할 때 처럼 소스코드에 중단점을 걸어놓기만 하면 동일하게 디버깅을 할 수 있다.

 

+) Create a Kubernetes Sample을 보면 구글에서 제공하는 다양한 Kubernetes 어플리케이션 샘플들을 볼 수 있다. 이를 통해 어느 정도의 폴더 구조나 네이밍 컨벤션도 배울 수 있으니 참고하면 좋다.

 

파드에서 Logs를 직관적으로 보여줄 수도 있고 Metric API를 설정만 하면 메트릭 정보도 받아올 수 있다.

 

 

Skaffold를 아는 개발자는 많지만 Cloud Code을 아는 개발자는 적다. (글쓴이도 구글에 재직 중인 멘토님의 소개로 알게 됐다.)

Cloud Code를 Skaffold와 함께 사용한다면 Skaffold의 핵심 기능들을 쉽게 사용할 수 있을 것이며 커맨드 라인으로 불편하게 리소스를 관리했던 것과는 다르게 쉽게 리소스를 관리할 수 있으므로 전체적인 K8s 개발 속도 역시 향상할 것이다.