스타일 가이드

이 문서는 GitLab Helm 차트 개발에 대한 다양한 지침과 모범 사례를 설명합니다.

네이밍 규칙

함수 이름과 values.yaml에서 사용되는 속성에 대해 camelCase를 사용하고 있습니다.

예: gitlab.assembleHost

템플릿 함수는 해당하는 설정 파일에서 영향을 받는 값을 일치시키기 위해 차트와 관련된 네임스페이스에 배치됩니다. 차트 global 함수는 일반적으로 gitlab.* 네임스페이스에 속합니다.

예시:

  • gitlab.redis.host: gitlab 차트의 일부로 Redis 서버의 호스트 이름을 제공합니다.
  • registry.minio.url: registry 차트의 일부로 MinIO 호스트의 URL을 제공합니다.

values.yaml의 공통 구조

많은 차트가 동일한 정보를 제공해야 하는데, 예를 들어 Redis 및 PostgreSQL 연결 설정을 여러 차트에 제공해야 할 수 있습니다. 여기서 해당 설정의 표준 네이밍과 구조를 기술합니다.

다른 서비스에 연결하기

redis:
  host: redis.example.com
  serviceName: redis
  port: 8080
  sentinels:
    - host: sentinel1.example.com
      port: 26379
  password:
    secret: gitlab-redis
    key: redis-password
  • redis - 현재 차트가 연결해야 하는 항목의 이름
  • host - serviceName의 사용을 무효화하고 기본적으로 0.0.0.0을 사용하는 주석 처리의 대체입니다. Redis Sentinels를 사용하는 경우 host 속성은 sentinel.conf에서 지정된 클러스터 이름으로 설정되어야 합니다.
  • serviceName - 기본적으로 host 대신 사용되도록 의도된 것으로 Kubernetes 서비스 이름을 사용하여 연결합니다.
  • port - 연결할 포트. 기본적으로 주석 처리되고 기본 포트를 예제로 사용합니다.
  • password - 암호를 포함하는 Kubernetes Secret의 설정을 정의합니다.
  • sentinels.[].host - Redis HA 설정을 위한 Redis Sentinel 서버의 호스트 이름을 정의합니다.
  • sentinels.[].port - Redis Sentinel 서버에 연결할 포트를 정의합니다. 기본값은 26379입니다.

참고: 현재 Redis Sentinel 지원은 별도로 배포된 Sentinels만 지원합니다. 따라서 GitLab 차트를 통한 Redis 배포는 redis.install=false로 비활성화되어야 합니다. Redis 암호를 포함하는 Secret은 GitLab 차트를 배포하기 전에 매뉴얼으로 생성되어야 합니다.

비밀 정보 공유

비밀 정보(예: 암호)를 저장하고 다른 차트/pod간에 공유하기 위해 비밀 정보를 사용합니다.

우리가 이를 사용하는 일반적인 필드는:

  • TLS/SSL 인증서 - TLS/SSL 인증서 공유
  • 암호 - Redis 암호 공유
  • Auth 토큰 - 다른 서비스 간의 인증 토큰 공유
  • 기타 비밀 정보 - JWT 인증서 및 서명 키와 같은 기타 비밀 정보 공유

TLS/SSL 인증서

TLS/SSL 인증서는 유효한 Kubernetes TLS Secret로 예상됩니다.

예를 들어, 레지스트리를 설정하는 경우:

registry:
  tls:
    secretName: <TLS 비밀 이름>

차트 간에 TLS 인증서를 공유할 때, 전역 값으로 선언되어야 합니다.

global:
  ingress:
    tls:
      secretName: <TLS 비밀 이름>

암호

예를 들어, redis가 소유한 차트이고 다른 차트가 redis 암호를 참조해야 하는 경우입니다.

소유 차트는 다음과 같이 암호 Secret을 정의해야 합니다:

password:
  secret: <비밀 이름>
  key: <가져올 Secret 내부의 키 이름>

다른 차트는 다음과 같이 동일한 암호 Secret을 공유해야 합니다:

redis:
  password:
    secret: <비밀 이름>
    key: <가져올 Secret 내부의 키 이름>

Auth Tokens

소유 차트는 다음과 같이 authToken Secret을 정의해야 합니다:

authToken:
  secret: <비밀 이름>
  key: <가져올 Secret 내부의 키 이름>

다른 차트는 다음과 같이 동일한 암호 Secret을 공유해야 합니다:

gitaly:
  authToken:
    secret: <비밀 이름>
    key: <가져올 Secret 내부의 키 이름>

예를 들어 gitaly가 소유한 차트이고 다른 차트가 gitaly의 authToken을 참조해야 하는 경우입니다.

기타 비밀 정보

registrygitaly GPG 서명 키를 위한 JWT 서명 인증서와 같은 기타 비밀 정보는 authTokenpassword Secret와 동일한 형식을 사용합니다.

한 차트에서 다른 차트로 이러한 비밀 정보를 공유하려면, registry JWT 서명 인증서가 다른 차트와 공유되도록 다음 예제와 유사한 구성을 제공합니다.

소유 차트는 다음과 같이 Secret을 정의해야 합니다:

certificate:
  secret: <비밀 이름>
  key: <가져올 Secret 내부의 키 이름>

다른 차트는 다음과 같이 동일한 Secret을 공유해야 합니다:

registry:
  certificate:
    secret: <비밀 이름>
    key: <가져올 Secret 내부의 키 이름>

함수 사용 선호도

우리는 gotmpl, Sprig 및 Helm에서 사용 가능한 다양한 함수에 대해 차트 개발을 위한 선호도 집합을 발전시켰습니다. 다음 섹션에서 이러한 함수 사용에 대한 일부 설명과 이유를 설명합니다.

indent 대신 nindent 사용하기

가능한 경우, indent 함수 대신 nindent 함수를 사용하도록 하세요. 이러한 선호도는 가독성을 기반으로 하며, 특히 우리 차트의 복잡성을 고려할 때입니다. nindent의 선호도는 커뮤니티 전체에 퍼졌으며, 이제는 helm create 명령에서 기본값으로 설정되어 있습니다.

이를 보여주는 두 스니펫 예제를 살펴보겠습니다.

가독성이 좋은 예시

  gitlab.yml.erb: |
    production: &base
      gitlab:
        host: {{ template "gitlab.gitlab.hostname" . }}
        https: {{ hasPrefix "https://" (include "gitlab.gitlab.url" .) }}
        {{- with .Values.global.hosts.ssh }}
        ssh_host: {{ . | quote }}
        {{- end }}
        {{- with .Values.global.appConfig }}
        max_request_duration_seconds: {{ default (include "gitlab.appConfig.maxRequestDurationSeconds" $) .maxRequestDurationSeconds }}
        impersonation_enabled: {{ .enableImpersonation }}
        application_settings_cache_seconds: {{ .applicationSettingsCacheSeconds | int }}
        usage_ping_enabled: {{ eq .enableUsagePing true }}
        username_changing_enabled: {{ eq .usernameChangingEnabled true }}
        issue_closing_pattern: {{ .issueClosingPattern | quote }}
        default_theme: {{ .defaultTheme }}
        {{- include "gitlab.appConfig.defaultProjectsFeatures.configuration" $ | nindent 8 }}
        webhook_timeout: {{ .webhookTimeout }}
        {{- end }}
        trusted_proxies:
        {{- if .Values.trusted_proxies }}
          {{- toYaml .Values.trusted_proxies | nindent 10 }}
        {{- end }}
        time_zone: {{ .Values.global.time_zone | quote }}
        {{- include "gitlab.outgoing_email_settings" . | nindent 8 }}
      {{- with .Values.global.appConfig }}
      {{- if .incomingEmail.enabled }}
      {{- include "gitlab.appConfig.incoming_email" . | nindent 6 }}
      {{- end }}
      {{- include "gitlab.appConfig.cronJobs" . | nindent 6 }}
      gravatar:

가독성이 나쁜 예시

  gitlab.yml.erb: |
    production: &base
      gitlab:
        host: {{ template "gitlab.gitlab.hostname" . }}
        https: {{ hasPrefix "https://" (include "gitlab.gitlab.url" .) }}
{{- with .Values.global.hosts.ssh }}
        ssh_host: {{ . | quote }}
{{- end }}
{{- with .Values.global.appConfig }}
        max_request_duration_seconds: {{ default (include "gitlab.appConfig.maxRequestDurationSeconds" $) .maxRequestDurationSeconds }}
        impersonation_enabled: {{ .enableImpersonation }}
        usage_ping_enabled: {{ eq .enableUsagePing true }}
        username_changing_enabled: {{ eq .usernameChangingEnabled true }}
        issue_closing_pattern: {{ .issueClosingPattern | quote }}
        default_theme: {{ .defaultTheme }}
{{- include "gitlab.appConfig.defaultProjectsFeatures.configuration" $ | indent 8 }}
        webhook_timeout: {{ .webhookTimeout }}
{{- end }}
        trusted_proxies:
{{- if .Values.trusted_proxies }}
{{- toYaml .Values.trusted_proxies | indent 10 }}
{{- end }}
        time_zone: {{ .Values.global.time_zone | quote }}
{{- include "gitlab.outgoing_email_settings" . | indent 8 }}
{{- with .Values.global.appConfig }}
{{- if .incomingEmail.enabled }}
{{- include "gitlab.appConfig.incoming_email" . | indent 6 }}
{{- end }}
{{- include "gitlab.appConfig.cronJobs" . | indent 6 }}
      gravatar:

관련된 이슈: #729 리팩터링: Helm 템플릿

템플릿에서 toYaml을 사용하는 시점

템플릿 파일에서 toYaml을 기본으로 사용하는 것은 권장되지 않습니다. 이는 Kubernetes 및 원하는 커뮤니티 구성의 모든 기능을 지원하는 데 부담을 줄 수 있기 때문입니다. 우리의 주요 초점은 최소 구성을 사용하여 합리적인 기본값을 제공하는 데 있습니다. 우리의 보조 초점은 Kubernetes의 고급 사용자를 위해 기본값을 재정의할 수 있는 기능을 제공하는 것입니다. 이는 각각의 경우에 따라 수행되어야 합니다. 왜냐하면 어떤 경우에는 두 옵션 모두 지원하기에는 부담스러울 수도 있고, 유지 관리할 템플릿이 불필요하게 복잡한 경우도 있기 때문입니다.

합리적인 기본값과 재정의할 수 있는 능력의 좋은 예로는 레지스트리 서브차트의 수평 파드 오토스케일러 구성이 있습니다. 우리는 지원이 쉬운 최소한의 구성을 제공하기 위해 기본값으로 CPU 이용률을 통제하는 특정 구성을 노출시켜 커뮤니티에게 targetAverageUtilization이라는 유일한 구성 옵션을 노출시킵니다. HPA는 훨씬 더 유연성을 제공할 수 있기 때문에 고급 사용자들은 다양한 지표를 대상으로 설정하고 싶을 수 있습니다. 그러므로 여기에서 우리는 사용자가 더 복잡한 HPA 구성을 제공할 수 있도록 하는 if 문을 사용하는 좋은 예가 됩니다.

  metrics:
  {{- if not .Values.hpa.customMetrics }}
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          targetAverageUtilization: {{ .Values.hpa.cpu.targetAverageUtilization }}
  {{- else -}}
    {{- toYaml .Values.hpa.customMetrics | nindent 4 -}}
  {{- end -}}

위의 예에서 최소 구성은 values.yaml에서 targetAverageUtilization을 업데이트하는 간단한 변경일 것입니다.

더 나은 지표를 식별한 고급 사용자들은 .customMetrics를 설정하여 이 과도하게 단순한 HPA 구성을 재정의할 수 있습니다. 이는 HPA 메트릭스 배열에 대한 Kubernetes API와 호환되는 특정 구성을 포함하는 배열로 설정됩니다.

고급 사용자들이 자신의 구성 파일을 번거롭지 않게 최소화할 수 있도록 유지하는 것이 중요합니다.

템플릿 도우미 개발

차트 템플릿 도우미는 templates/_helpers.tpl에 위치해 있습니다. 이것들은 차트 내에서 사용되는 이름이 지정된 템플릿(named templates)을 포함합니다.

이러한 템플릿을 사용할 때는 Go 템플릿 구문에 관련하여 몇 가지 사항을 주의해야 합니다.

동작으로부터 출력되지 않는 값을 포착

Go 템플릿 구문에서 모든 동작({{ }}로 표시됨)은 문자열을 출력해야 하지만, 변수 할당을 제외한 제어 구조(정의, if, with, range)를 비롯한 동작은 문자열을 출력해선 안 됩니다.

이는 때로 출력되면 안 되는 출력물을 포착하기 위해 변수 할당을 사용해야 하는 경우가 있음을 의미합니다.

예를 들어:

{{- $details := .Values.details -}}
{{- $_ := set $details "serviceName" "example" -}}
{{ template "serviceHost" $details }}

위의 예에서 우리는 출력을 하기 전에 맵에 추가 데이터를 추가하기 위해 템플릿 함수에 넘기기 전에 set 함수의 출력물을 포착하기 위해 $_ 변수에 할당했습니다. 이 할당을 하지 않으면 템플릿은 set 함수의 결과물(수정된 맵)을 문자열로 출력하려고 시도할 것입니다.

제어 구조 간에 변수 전달

Go 템플릿 구문은 변수 초기화(:=)와 할당(=)을 명확히 구분하며, 이는 스코프에 영향을 받습니다.

결과적으로 제어 구조(if/with/range) 밖에서 존재하던 변수를 다시 초기화할 수 있지만, 제어 구조 내에서 선언된 변수는 외부에서 사용할 수 없음을 명심해야 합니다.

예를 들어:

{{- define "exampleTemplate" -}}
{{- $someVar := "default" -}}
{{- if true -}}
{{-   $someVar := "desired" -}}
{{- end -}}
{{- $someVar -}}
{{- end -}}

위의 예에서 exampleTemplate을 호출하면 항상 default를 반환합니다. 왜냐하면 desired를 포함하고 있는 변수는 if 제어 구조 내에서만 접근 가능하기 때문입니다.

이 문제를 피하기 위해 여러 스코프에서 변경하고 싶은 값을 보관하기 위해 사전을 사용하거나, 명확하게 할당 연산자(= vs :=)를 사용하여 문제를 피하려고 시도합니다.

문제를 피하는 예:

{{- define "exampleTemplate" -}}
{{- if true -}}
{{-   "desired" -}}
{{- else -}}
{{-   "default" -}}
{{- end -}}

사전 사용 예:

{{- define "exampleTemplate" -}}
{{- $result := dict "value" "default" -}}
{{- if true -}}
{{-   $_ := set $result "value" "desired" -}}
{{- end -}}
{{- $result.value -}}
{{- end -}}

초기화와 할당의 예 (잘 보세요!):

{{- define "exampleTemplate" -}}
{{- $someVar := "default" -}}
{{- if true -}}
{{-   $someVar = "desired" -}}
{{- end -}}
{{- $someVar -}}
{{- end -}}

템플릿 사용 예:

{{- define "exampleTemplate" -}}
foo:
  bar:
   baz: bat
{{- end -}}

그리고 위의 내용을 변수와 구성으로 가져오는 예:

{{- $fooVar := include "exampleTemplate" . | fromYaml -}}
{{- $barVar := merge $.Values.global.some.config $fooVar -}}
config:
{{ $barVar }}

템플릿 구성 파일

이 차트는 클라우드 네이티브 GitLab 컨테이너를 사용합니다. 이러한 컨테이너는 ERB 또는 gomplate을 지원합니다.

지침:

  1. ConfigMap 내에서 템플릿 파일을 사용합니다(예: gitlab.yml.erb, config.toml.tpl)
    • 항목은 템플릿으로 처리되기 위해 예상되는 확장자를 사용해야 합니다.
  2. 마운트된 파일 위치에서 시크릿 내용을 채우기 위해 템플릿을 사용합니다. (예: GitLab Pages config)
  3. Ruby를 사용하는 모든 컨테이너에 대해 ERB(.erb)를 사용할 수 있습니다.
  4. 어떤 컨테이너에든 gomplate(.tpl)을 사용할 수 있습니다.

ERB 사용법:

우리는 표준 ERB를 사용하며, jsonyaml 모듈이 사전로드되었을 것이라고 기대할 수 있습니다.

gomplate 사용법:

우리는 컨테이너 내의 Ruby 크기와 표면을 줄이기 위해 gomplate을 사용합니다. {% %} 대체 구분자로 gomplate 구문을 구성하므로 Helm의 {{ }} 구분자와 충돌하지 않도록합니다.

민감한 내용 템플릿화

시크릿은 적절하게 인코딩되거나 따옴표로 묶이지 않으면 YAML을 잘못된 상태로 만들 수 있는 문자를 포함할 수 있습니다. 특히 복잡한 암호에 대한 경우, 이러한 문자열이 다양한 구성 형식으로 추가되는 방식에 주의해야 합니다.

지침:

  1. ERB 또는 Gomplate 출력에 따옴표를 사용하지 말고 따옴표로 감싸십시오.
  2. 최대한 형식에 맞는 인코더를 사용하십시오.
    • 렌더링된 YAML의 경우 YAML이 JSON의 하위 집합이기 때문에 JSON 문자열을 사용하십시오.
    • 렌더링된 TOML의 경우 TOML 문자열이 유사하게 이스케이핑하므로 JSON 문자열을 사용하십시오.
  3. 데이터베이스 연결 문자열과 같은 따옴표로 묶인 문자열 내부에 따옴표로 묶인 문자열과 같은 복잡성을 주의하십시오.

암호 인코딩 예시

Gitaly의 클라이언트 시크릿 토큰을 예로 들어보겠습니다. 이 값은 gitaly_token이며, YAML 및 TOML 둘 다에 템플릿화됩니다.

예시로 my"$pec!@l"p#assword%'를 사용해봅시다:

# YAML
gitaly:
  token: "<%= File.read('gitaly_token').strip =>"

# TOML
[auth]
token = "<%= File.read('gitaly_token').strip %>"

위 코드는 유효하지 않은 YAML 및 TOML로 렌더링됩니다.

# YAML
gitaly:
  token: "my"$pec!@l"p#assword%'"

(<unknown>): did not find expected key while parsing a block mapping at line 3 column 3

[auth]
token = "my"$pec!@l"p#assword%'"

Error on line 2: Expected Comment, Newline, Whitespace, or end of input but "$" found.

이를 <%= File.read('gitaly_token').strip.to_json %>으로 변경하면 유효한 YAML 및 TOML 형식으로 변환됩니다. <% %> 바깥의 "가 제거되었다는 점에 주목해야 합니다.

gitaly:
  token: "my\"$pec!@l\"p#assword%'"

이와 같은 변환이 gomplate로도 가능합니다: {% file.Read "gitaly_token" | strings.TrimSpace | data.ToJSON %}

gitaly:
  # gomplate
  token: {% file.Read "./token" | strings.TrimSpace | data.ToJSON %}
  # ERB
  token: <%= File.read('gitaly_token').strip.to_json %>

템플릿 차트 메모 (NOTES.txt)

Helm의 차트 메모 기능은 차트 설치 및 업그레이드 후 유용한 정보와 후속 지침을 제공합니다.

이러한 메모는 templates/NOTES.txt에 위치합니다.

이러한 메모 처리 시, 출력이 가독성 있고 실행 가능하도록 스타일에 대해 몇 가지 사항을 염두에 두어야 합니다.

메모 카테고리 선택

WARNINGNOTICE 두 카테고리는 메모 출력의 각 유형을 나타냅니다.

  • WARNING은 설치를 최적화하기 위해 추가 작업이 필요함을 나타냅니다
  • NOTICE는 추가 조치가 필요하지 않은 중요한 알림을 강조합니다

NOTES.txt의 각 항목은 이 두 카테고리 중 하나로 시작해야 합니다. 예를 들어:

{{- if eq true .Values.some.setting }}
{{ $WARNING }}
This message is a warning.
{{- end }}

{{- if eq true .Values.some.other.setting }}
{{ $NOTICE }}
This message is a notice.
{{- end }}

이러한 예시는 출력의 각 항목 간 일관된 제목과 간격을 보장하는 파일 상단에 포함된 두 가지 미리 정의된 변수 중 하나를 사용합니다.