개념

Edit This Page

구성 모범 사례

이 문서는 사용자 가이드, 시작하기 문서 및 예제들에 걸쳐 소개된 구성 모범 사례를 강조하고 통합한다.

이 문서는 지속적으로 변경 가능하다. 이 목록에 없지만 다른 사람들에게 유용할 것 같은 무엇인가를 생각하고 있다면, 새로운 이슈를 생성하거나 풀 리퀘스트를 제출하는 것을 망설이지 말기를 바란다.

일반적인 구성 팁

  • 구성을 정의할 때, 안정된 최신 API 버전을 명시한다.

  • 구성 파일들은 클러스터에 적용되기 전에 버전 컨트롤에 저장되어 있어야 한다. 이는 만약 필요하다면 구성의 변경 사항을 빠르게 되돌릴 수 있도록 해준다. 이는 또한 클러스터의 재-생성과 복원을 도와준다.

  • JSON보다는 YAML을 사용해 구성 파일을 작성한다. 비록 이러한 포맷들은 대부분의 모든 상황에서 통용되어 사용될 수 있지만, YAML이 좀 더 사용자 친화적인 성향을 가진다.

  • 의미상 맞다면 가능한 연관된 오브젝트들을 하나의 파일에 모아 놓는다. 때로는 여러 개의 파일보다 하나의 파일이 더 관리하기 쉽다. 이 문법의 예시로서 guestbook-all-in-one.yaml 파일을 참고한다.

  • 많은 kubectl 커맨드들은 디렉터리에 대해 호출될 수 있다. 예를 들어, 구성 파일들의 디렉터리에 대해 kubectl apply를 호출할 수 있다.

  • 불필요하게 기본 값을 명시하지 않는다. 간단하고 최소한의 설정은 에러를 덜 발생시킨다.

  • 더 나은 인트로스펙션(introspection)을 위해서, 어노테이션에 오브젝트의 설명을 넣는다.

“단독(Naked)” 파드 vs 레플리카 셋, 디플로이먼트, 그리고 잡

  • 가능하다면 단독 파드(즉, 레플리카 셋이나 디플로이먼트에 연결되지 않은 파드)를 사용하지 않는다. 단독 파드는 노드 장애 이벤트가 발생해도 다시 스케줄링되지 않는다.

명백하게 restartPolicy: Never를 사용하는 상황을 제외한다면, 의도한 파드의 수가 항상 사용 가능한 상태를 유지하는 레플리카 셋을 생성하고, 파드를 교체하는 전략(롤링 업데이트와 같은)을 명시하는 디플로이먼트는 파드를 직접 생성하기 위해 항상 선호되는 방법이다. 또한 적절할 수 있다.

서비스

  • 서비스에 대응하는 백엔드 워크로드(디플로이먼트 또는 레플리카 셋) 또는 서비스 접근이 필요한 어떠한 워크로드를 생성하기 전에 서비스를 미리 생성한다. 쿠버네티스가 컨테이너를 시작할 때, 쿠버네티스는 컨테이너 시작 당시에 생성되어 있는 모든 서비스를 가리키는 환경 변수를 컨테이너에 제공한다. 예를 들어, foo 라는 이름의 서비스가 존재한다면, 모든 컨테이너들은 초기 환경에서 다음의 변수들을 얻을 것이다.

    FOO_SERVICE_HOST=<서비스가 동작 중인 호스트>
    FOO_SERVICE_PORT=<서비스가 동작 중인 포트>

이는 순서를 정하는 일이 요구됨을 암시한다 - 파드가 접근하기를 원하는 어떠한 서비스파드 스스로가 생성되기 전에 미리 생성되어 있어야 하며, 그렇지 않으면 환경 변수가 설정되지 않을 것이다. DNS는 이러한 제한을 가지고 있지 않다.

  • 선택적인(그렇지만 매우 권장되는) 클러스터 애드온은 DNS 서버이다. DNS 서버는 새로운 서비스를 위한 쿠버네티스 API를 Watch하며, 각 서비스를 위한 DNS 레코드 셋을 생성한다. 만약 DNS가 클러스터에 걸쳐 활성화되어 있다면, 모든 파드서비스의 이름을 자동으로 해석할 수 있어야 한다.

  • 반드시 필요한 것이 아니라면 파드에 hostPort 를 명시하지 않는다. <hostIP, hostPort, protocol> 조합은 유일해야 하기 때문에, hostPort로 바인드하는 것은 파드가 스케줄링될 수 있는 위치의 개수를 제한한다. 만약 hostIPprotocol을 뚜렷히 명시하지 않으면, 쿠버네티스는 hostIP의 기본 값으로 0.0.0.0를, protocol의 기본 값으로 TCP를 사용한다.

만약 오직 디버깅의 목적으로 포트에 접근해야 한다면, apiserver proxy 또는 kubectl port-forward를 사용할 수 있다.

만약 파드의 포트를 노드에서 명시적으로 노출해야 한다면, hostPort에 의존하기 전에 NodePort 서비스를 사용하는 것을 고려할 수 있다.

  • hostPort와 같은 이유로, hostNetwork를 사용하는 것을 피한다.

  • kube-proxy 로드 밸런싱이 필요하지 않을 때, 쉬운 서비스 발견을 위해 헤드리스 서비스(ClusterIP의 값을 None으로 가지는)를 사용한다.

레이블 사용하기

  • { app: myapp, tier: frontend, phase: test, deployment: v3 }처럼 애플리케이션이나 디플로이먼트의 속성에 대한 의미를 식별하는 레이블을 정의해 사용한다. 다른 리소스를 위해 적절한 파드를 선택하는 용도로 이러한 레이블을 이용할 수 있다. 예를 들어, 모든 tier: frontend 파드를 선택하거나, app: myapp의 모든 phase: test 컴포넌트를 선택하는 서비스를 생각해 볼 수 있다. 이 접근 방법의 예시는 방명록 앱을 참고한다.

릴리스에 특정되는 레이블을 서비스의 셀렉터에서 생략함으로써 여러 개의 디플로이먼트에 걸치는 서비스를 생성할 수 있다. 디플로이먼트는 생성되어 있는 서비스를 다운타임 없이 수정하기 쉽도록 만든다.

오브젝트의 의도한 상태는 디플로이먼트에 의해 기술되며, 만약 그 스펙에 대한 변화가 적용될 경우, 디플로이먼트 컨트롤러는 일정한 비율로 실제 상태를 의도한 상태로 변화시킨다.

  • 디버깅을 위해 레이블을 조작할 수 있다. (레플리카 셋과 같은) 쿠버네티스 컨트롤러와 서비스는 셀렉터 레이블을 사용해 파드를 선택하기 때문에, 관련된 레이블을 파드에서 삭제하는 것은 컨트롤러로부터 관리되거나 서비스로부터 트래픽을 전달받는 것을 중단시킨다. 만약 이미 존재하는 파드의 레이블을 삭제한다면, 파드의 컨트롤러는 그 자리를 대신할 새로운 파드를 생성한다. 이것은 이전에 “살아 있는” 파드를 “격리된” 환경에서 디버그할 수 있는 유용한 방법이다. 레이블을 상호적으로 추가하고 삭제하기 위해서, kubectl label를 사용할 수 있다.

컨테이너 이미지

imagePullPolicy와 이미지의 태그는 kubelet이 명시된 이미지를 풀(pull) 하려고 시도할 때 영향을 미친다.

  • imagePullPolicy: IfNotPresent: 이미지가 로컬에 이미 존재하지 않으면 이미지가 풀(Pull) 된다.

  • imagePullPolicy: Always: 파드가 시작될 때마다 이미지가 풀(Pull) 된다.

  • imagePullPolicy가 생략되어 있고, 이미지 태그가 :latest 이거나 생략되어 있다면 Always가 적용된다.

  • imagePullPolicy가 생략되어 있고, 이미지 태그가 존재하지만 :latest가 아니라면 IfNotPresent가 적용된다.

  • imagePullPolicy: Never: 이미지가 로컬에 존재한다고 가정한다. 이미지를 풀(Pull) 하기 위해 시도하지 않는다.

참고: 컨테이너가 항상 같은 버전의 이미지를 사용하도록 만들기 위해, sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2와 같은 이미지의 다이제스트를 명시할 수 있다. 다이제스트는 특정 버전의 이미지를 고유하게 식별하며, 다이제스트 값을 변경하지 않는 한 쿠버네티스에 의해 절대로 변경되지 않는다.
참고: 운영 환경에서 컨테이너를 생성할 때 :latest 태그의 사용을 피하는 것이 좋은데, 이는 어떠한 버전의 이미지가 실행 중인지 추적하기가 어렵고, 적절히 롤백하기가 더 어려워지기 때문이다.
참고: 기반이 되는 이미지 제공자의 캐시 방법은 imagePullPolicy: Always를 효율적으로 만든다. 예를 들어, 도커에서는 이미지가 이미 존재한다면 풀(Pull) 시도는 빠르게 진행되는데, 이는 모든 이미지 레이어가 캐시되어 있으며 이미지 다운로드가 필요하지 않기 때문이다.

kubectl 사용하기

피드백