スタイルガイド

このドキュメントでは、GitLab Helmチャート開発のための様々なガイドラインとベストプラクティスについて説明します。

命名規則

values.yaml で使用する関数名やプロパティ名には、キャメルケースを使用しています。

例: gitlab.assembleHost

例: テンプレート関数は、関連付けられているチャートに従って名前空間に配置され、ターゲット・ファイル内の影響を受ける入力値と一致する名前が付けられます。チャート・_グローバル_関数は通常、gitlab.* 名前空間の下にあることに注意してください。

例:

  • gitlab.redis.host: Redisサーバーのホスト名をgitlab Chartの一部として提供します。
  • registry.minio.url: :registry チャートの一部として、MinIOホストへのURLを提供します。

共通構造values.yaml

例えば、RedisとPostgreSQLの接続設定を複数のChartに提供する必要があります。ここでは、これらの設定の標準的な命名と構造の概要を説明します。

他のサービスへの接続

redis:
  host: redis.example.com
  serviceName: redis
  port: 8080
    sentinels:
    - host: sentinel1.example.com
      port: 26379
  password:
    secret: gitlab-redis
    key: redis-password
  • redis - 現在のChartが接続する必要のあるサービスの名前。
  • host - はserviceNameの使用を上書きします。デフォルトでは0.0.0.0 を例にコメントアウトしてください。Redis Sentinelsを使用している場合は、host 属性にsentinel.conf で指定したクラスター名を設定する必要があります。
  • serviceName - ホストの代わりにデフォルトで使用されることを意図して、Kubernetesサービス名を使用して接続します。
  • port - 接続するポート。デフォルトでコメントアウトし、デフォルトのポートを例として使用します。
  • password- はパスワードを含むKubernetesシークレットの設定を定義します。
  • sentinels.[].host - は、Redis HAセットアップ用のRedis Sentinelサーバーのホスト名を定義します。
  • sentinels.[].port - は Redis Sentinel サーバーに接続するポートを定義します。デフォルトは26379 です。

注意: 現在のRedis Sentinelサポートは、GitLabチャートとは別にデプロイされたSentinelのみをサポートしています。そのため、GitLabチャートによるRedisのデプロイは、redis.install=false で無効にする必要があります。Redisのパスワードを含むシークレットは、GitLabチャートをデプロイする前に手動で作成する必要があります。

シークレットの共有

私たちはシークレットを使ってパスワードのような機密情報を保存し、異なるチャート/ポッド間で共有しています。

私たちがシークレットを使用する一般的なフィールドは以下の通りです:

  • TLS/SSL証明書- TLS/SSL証明書の共有
  • パスワード- Redisパスワードの共有。
  • 認証トークン- サービス間認証トークンの共有
  • その他のシークレット- JWT証明書や署名キーなどのその他のシークレットを共有します。

TLS/SSL 証明書

TLS/SSL証明書は、有効なKubernetes TLS Secretであることが期待されます。

例えば、レジストリを設定する場合:

registry:
  tls:
    secretName: <TLS secret name>

TLS証明書をChart間で共有する場合、グローバル値として定義する必要があります。

global:
  ingress:
    tls:
      secretName: <TLS secret name>

パスワード

例えば、redis 自分のChartは redisどこにredis あったのか、他のChartは redisパスワードをredis 参照する必要が redisあります。

所有するチャートは、以下のようにパスワードのシークレットを定義する必要があります:

password:
  secret: <secret name>
  key: <key name inside the secret to fetch>

他のChartは以下のように同じパスワードのシークレットを共有する必要があります:

redis:
  password:
    secret: <secret name>
    key: <key name inside the secret to fetch>

認証トークン

所有するChartは以下のようにauthTokenシークレットを定義しなければなりません:

authToken:
  secret: <secret name>
  key: <key name inside the secret to fetch>

他のChartは以下のように同じパスワードのシークレットを共有する必要があります:

gitaly:
  authToken:
    secret: <secret name>
    key: <key name inside the secret to fetch>

例えば、gitaly 所有するチャートは gitalyどこでgitaly 、他のチャートは gitalyauthTokengitaly を参照する必要が gitalyあります。

その他のシークレット

registry の JWT 署名証明書やgitaly の GPG 署名鍵など、その他のシークレットもauthTokenpassword のシークレットと同じ書式を使用します。

あるChartから他のChartへこのようなシークレットを共有するには、registry JWT署名証明書を他のChartと共有する以下の例のような設定を用意してください。

所有するChartは、そのシークレットを以下のように定義する必要があります:

certificate:
  secret: <secret name>
  key: <key name inside the secret to fetch>

他のChartも同じシークレットを共有する必要があります:

registry:
  certificate:
    secret: <secret name>
    key: <key name inside the secret to fetch>

機能使用に関する好み

gotmpl、Sprig、Helmで使用できる様々な関数について、これらのChartを開発する上での好みを進化させました。以下のセクションで、その一部と理由を説明します。

インデントよりもnindentを使用

可能であれば、indent 関数の代わりにnindent 関数を使用してください。これは読みやすさを考慮したもので、特に私たちのような複雑なHelmチャートには適しています。nindent を使用することが推奨されるようになり、helm create コマンドで生成されるテンプレートのデフォルトにもなっています。

その理由を簡単に説明する2つのスニペット例を見てみましょう:

読みやすい

  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 を利用するタイミング。

Kubernetesと希望するコミュニティ設定の両方のすべての機能をサポートする上で過度な負担をかけることになるため、テンプレートファイルでtoYaml をデフォルトで利用することは嫌われます。私たちは、最小限の設定を使用して合理的なデフォルトを提供することに第一に重点を置いています。私たちの二次的な焦点は、Kubernetesのより高度なユーザーのためにデフォルトをオーバーライドする機能を提供することです。これはケースバイケースで行うべきで、どちらのオプションを選んでもサポートが面倒になったり、不必要に複雑なテンプレートをメンテナーに提供したりするシナリオがあるためです。

オーバーライド可能な合理的なデフォルトの良い例が、レジストリ・サブチャートのHorizontal Pod Autoscaler設定にあります。デフォルトでは、CPU使用率を通じてHPAを制御する特定の設定を公開し、1つの設定オプションのみをコミュニティに公開することで、簡単にサポートできる最低限のものを提供しています(targetAverageUtilization )。HPAははるかに柔軟性を提供できるため、より高度なユーザーは異なるメトリクスをターゲットにしたいと思うかもしれません。そのため、エンドユーザーがより複雑なHPA設定を提供できるようにするステートメントがあれば、それを利用できる完璧な例です。

  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 -}}

上記の例では、targetAverageUtilization を更新す る ためのvalues.yaml の単純な変更が最小限の設定です。

より良いメトリクスを特定した上級ユーザーは、HPAメトリクス配列のKubernetes API互換の設定を正確に含む配列に.customMetrics を設定することで、この単純すぎるHPA設定を上書きすることができます。

より高度なユーザーが、面倒にならずに独自の設定ファイルを最小化できるよう、使いやすさを維持することが重要です。

テンプレートヘルパーの開発

Chartのテンプレートヘルパーはtemplates/_helpers.tpl の内部にあります。これには、Chart内で使用される名前付きテンプレートがコンテナとして格納されています。

これらのテンプレートを使用する際には、Goのテンプレート構文に関していくつか注意すべき点があります。

アクションから出力されない値のトラップ

goテンプレート構文では、制御構造(define、if、with、range)と変数代入を除いて、すべてのアクション({{ }})は文字列を表示します。

つまり、印刷されることを意図していない出力をトラップするために変数代入を使用する必要がある場合があります。

使用例:

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

上記の例では、出力のためにテンプレート関数に渡す前に、Mapにいくつかの追加データを追加したいとします。$_ 変数に代入することで、set 関数の出力をトラップしました。この代入がなければ、テンプレートはset (変更したMapを返す)の結果を文字列として出力しようとするでしょう。

制御構造間の変数の受け渡し

go のテンプレート構文は、初期化 (:=) と代入 (=) を強く区別しており、これはスコープの影響を受けます。

その結果、制御構造(if/with/range)の外部に存在する変数を再初期化することができますが、制御構造の内部で宣言された変数は外部では使用できないことを知っておいてください。

使用例:

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

上記の例では、desired を含む変数は、if 制御構造内でのみアクセス可能であったため、exampleTemplate を呼び出すと、常にdefault が返されます。

このイシューを回避するには、複数のスコープで変更したい値を保持するDictionaryを使用するか、_代入演算_子を明示的に使用することで問題を回避します (= 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 }}

テンプレート設定ファイル

これらのChartはクラウドネイティブのGitLabコンテナを利用しています。これらのコンテナはERBか gomplateの使用をサポートしています。

ガイドライン

  1. ConfigMaps内部でテンプレートファイルを使用してください(例:gitlab.yml.erb,config.toml.tpl )。
    • テンプレートとして扱われるためには、エントリーは期待される拡張子を使用_しなければ_なりません。
  2. テンプレートを使って、マウントされたファイルの場所からシークレットの内容を入力します。(例:GitLab Pagesconfig)
  3. ERB (.erb) は、実行時に Ruby を使用するコンテナに使用できます。
  4. gomplate (.tpl) はどんなコンテナにも使えます。

ERBの使い方

標準的な ERB を使用しており、jsonyaml のモジュールはあらかじめロードされています。

gomplateの使用法:

コンテナ内のRubyのサイズと表面を取り除くためにgomplateを利用しています。gomplateのシンタックスは、Helmの{{ }}の使用と衝突しないように、{% %} の代替区切り文字で設定しています。

センシティブなコンテンツのテンプレート化

シークレットは適切にエンコードもしくはクォートされなければ無効な YAML になる可能性のある文字を含みます。とりわけ複雑なパスワードの場合、これらの文字列がさまざまな設定フォーマットに追加される方法に注意しなければなりません。

ガイドライン

  1. ERB / Gomplate 出力を囲む_のではなく_、引用符で囲んでください。
  2. 可能な限りフォーマットネイティブのエンコーダーを使用してください。
    • YAML は JSON のスーパーセットなので、レンダリングされた YAML には JSON 文字列を使います。
    • 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 の2つのカテゴリーが、ノート出力の各タイプのエントリーを示します。

  • WARNING は、インストールを最適化するためにさらなるアクションが必要であることを示します。
  • NOTICE 必ずしも追加アクションを必要としない重要な注意事項を強調表示します。

NOTES.txt の各項目は、これら2つのカテゴリのいずれかで始まる必要があります。例えば

{{- 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 }}

これらの例では、NOTES.txt ファイルの先頭に含まれている 2 つの定義済み変数のうちの 1 つを使用して、出力内の各エントリ間の一貫したタイトルと間隔を確保しています。