学んだことや苦労したことを書いていきます。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。
なお本記事は、KubernetesとHelmをある程度触ったことがある方向けの内容です(kubectl や helm install、values.yamlのイメージがある前提で進めます)。
0. はじめに
皆さんKubernetesはご利用でしょうか。バニラのKubernetesをインストールするのは骨が折れるので、minikubeやMicroK8sをご利用の方もいらっしゃると思いますが、自分の推しはk3sです。
簡単インストールでコンポーネントが揃っているのでめっちゃ便利です。
今回の本題ではないので、インストールは以下を参照ください。
https://github.com/alexellis/k3sup?tab=readme-ov-file#k3sup-community-edition-ce
1. k3sのコンポーネントの設定を変更したい
kubernetesを利用していると、コンポーネントの設定を変えたい時があると思います。
k3s(RKE2)は、少し独特な設定変更方法となっています。
k3sは以下のようなコンポーネントを自動で入れてくれます(バージョン、オプションによって多少違います)。
- Traefik Ingress Controller
- ServiceLB
- Network Policy Controller
- CoreDNS など
k3sは各コンポーネントをHelmChartや通常のマニフェストで管理しています。
各ファイルは以下のディレクトリにまとまっています。
ls /var/lib/rancher/k3s/server/manifests/
ccm.yaml coredns.yaml local-storage.yaml metrics-server rolebindings.yaml runtimes.yaml traefik.yaml
このうちTraefikなど一部はHelmChartとしてデプロイされており、HelmChartConfigという専用のCRDを使って設定を上書きできます。
一方でCoreDNSはHelmChartではなく通常のマニフェストでデプロイされているため、別の方法で上書きをします。
なぜこのような構造かというと、コンポーネントの設定(ConfigmapやSecret)を手作業で直接変更できる形では、設定の一貫性や再現性・保守性が崩れてしまうからです。
設定は全てYAMLファイルとして残しておこう、ってことですね。
2. HelmChartConfigとは
普通のHelmの話を少しだけおさらいします。
Helmでアプリケーションをデプロイする場合、だいたいこんな感じで使うと思います。
# リポジトリ追加
helm repo add example https://example.com/charts
# values.yaml を用意して…
helm install my-app example/my-chart -f values.yaml
values.yamlはこんな感じですね。
replicaCount: 2
image:
repository: my-app
tag: "1.0.0"
ingress:
enabled: true
className: "traefik"
k3s では、これとよく似たことを クラスター内で自動的にやってくれる仕組み が入っています。
- HelmChart … 「どのチャートを、どのリポジトリから、どの namespace にインストールするか」を表す CRD
- HelmChartConfig … そのチャートに渡す values.yaml(設定)を上書きするための CRD
という二段構えになっています。
k3s の組み込み Traefik などは、内部的にはだいたい次のような HelmChart リソースで管理されています(実物はもっと項目があります)
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: traefik-crd
namespace: kube-system
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: traefik
namespace: kube-system
spec:
chart: traefik
repo: https://helm.traefik.io/traefik
valuesContent: |-
deployment:
podAnnotations:
prometheus.io/port: "8082"
prometheus.io/scrape: "true"
...
ここに対して、後からvaluesContentを上書きしたい場合に使うのがHelmChartConfigです。
HelmChartConfig の基本形
HelmChartConfig は「対象の HelmChart と同じ name / namespace を持つリソース」として作成します。
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik # 対象の HelmChart と同じ name
namespace: kube-system # 対象の HelmChart と同じ namespace
spec:
valuesContent: |-
# ここに values.yaml と同じ書き方で上書きしたい設定を書く
logs:
access:
enabled: true
k3s 側のコントローラが、HelmChart と HelmChartConfig を合わせて解釈し、
values.yaml 相当の値としてマージ → Helm を実行 → 既存のリソースを再デプロイ
という流れを自動でやってくれます。
3. 実際に設定してみる
ここからは、実際に k3s 組み込みの Traefik の設定を、HelmChartConfig で変更してみます。
例として、Traefik の Gateway API provider を有効化する設定を入れてみます。
やることはざっくりこの4ステップです。
- HelmChart の存在確認
- 対象の HelmChart(Traefik)の YAML を確認する
- HelmChartConfig の YAML を書く (Gateway API を有効化)
- 適用して、設定が反映されたか確認する
3.1 Traefik の HelmChart を確認する
まずは、本当に Traefik が HelmChart として管理されているのかを確認します。(環境によって列は多少異なります)
kubectl get helmchart -A
NAMESPACE NAME REPO CHART VERSION TARGETNAMESPACE BOOTSTRAP FAILED
kube-system traefik https://%{KUBERNETES_API}%/static/charts/traefik-37.1.1+up37.1.0.tgz False
kube-system traefik-crd https://%{KUBERNETES_API}%/static/charts/traefik-crd-37.1.1+up37.1.0.tgz False
ここで重要なのは NAME と NAMESPACE で、
後述する HelmChartConfig も 同じ name / namespace で作成する必要があります。
3.2 Traefik の YAML を確認する
次に、現在デプロイされているYAMLを確認します
sudo cat /var/lib/rancher/k3s/server/manifests/traefik.yaml
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: traefik-crd
namespace: kube-system
spec:
chart: https://%{KUBERNETES_API}%/static/charts/traefik-crd-37.1.1+up37.1.0.tgz
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: traefik
namespace: kube-system
spec:
chart: https://%{KUBERNETES_API}%/static/charts/traefik-37.1.1+up37.1.0.tgz
set:
global.systemDefaultRegistry: ""
valuesContent: |-
deployment:
podAnnotations:
prometheus.io/port: "8082"
prometheus.io/scrape: "true"
providers:
kubernetesIngress:
publishedService:
enabled: true
priorityClassName: "system-cluster-critical"
image:
repository: "rancher/mirrored-library-traefik"
tag: "3.5.1"
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
service:
ipFamilyPolicy: "PreferDualStack"
この内容に追加する形でHelmChartConfigを作成します。
3.3 HelmChartConfig の YAML を書く(Gateway API を有効化)
/var/lib/rancher/k3s/server/manifests/ 配下に、traefik-custom.yaml を作成します。
ここでは、さきほど確認した traefik.yaml の valuesContent をそのままコピーし、
そのうえで providers.kubernetesGateway.enabled: true を追記する形にしています。
sudo vi /var/lib/rancher/k3s/server/manifests/traefik-custom.yaml
---
apiVersion: helm.cattle.io/v1
# 必ずkind をHelmChartConfigにする
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
deployment:
podAnnotations:
prometheus.io/port: "8082"
prometheus.io/scrape: "true"
providers:
kubernetesIngress:
publishedService:
enabled: true
# GatewayAPIを追加
kubernetesGateway:
enabled: true
priorityClassName: "system-cluster-critical"
image:
repository: "rancher/mirrored-library-traefik"
tag: "3.5.1"
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
service:
ipFamilyPolicy: "PreferDualStack"
配置後、k3sを再起動することで新しいtraefikのPodがデプロイされます。
sudo systemctl restart k3s.service
kubectl get pods -n kube-system | grep traefik
helm-install-traefik-9dnkh 0/1 Completed 0 13s
helm-install-traefik-crd-9vfwr 0/1 Completed 0 24m
traefik-57c6d4db56-2ns4f 1/1 Running 0 119s
traefik-865bd56545-gfr8v 0/1 Running 0 8s # 新しいPodが起動中
3.4 変更後の動作を確認する
HelmChartConfigがデプロイされたかを確認しましょう。
kubectl get helmchartconfig -n kube-system
NAME AGE
traefik 99s
GatewayAPI providerが有効化されたか確認しましょう。
kubectl logs -n kube-system deploy/traefik
…
2025-12-12T07:21:21Z INF Starting provider *gateway.Provider
…
実際に有効化されましたね。
4. 注意点
4-1. HelmChartConfig を使うときの注意点
-
name / namespace を必ず揃える
metadata.name は 対象の HelmChart の metadata.name と一致している必要があります。
metadata.namespace も 対象の HelmChart と同じ namespace である必要があります。 -
values の優先順位に注意
公式ドキュメントでは一部の設定のみ記載して、値を追加するケースを記載していますが、map 単位で上書きされるので、deployment: や service: を丸ごと書くと他のサブキーを潰す可能性があります。
心配な方は、valuesContentをコピーして設定を追記する方法(今回提示した方法)を取りましょう。
4-2. 上書き用マニフェストを使うパターン
HelmChartConfig ではなく、「マニフェスト自体を上書きする」パターンもあります。
CoreDNSやlocal-path-provisionerなどは、通常のマニフェストで書かれているため、設定を上書きしたければコピーして設定を追加したYAMLを用意しましょう。
CoreDNSの例を見てみます。
sudo cat /var/lib/rancher/k3s/server/manifests/coredns.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
…
ご覧の通り、HelmChartではないため通常のマニフェストを上書き用で用意します。
念のためオリジナルをコピーして、そこに設定を追加するといいでしょう。
sudo cp -ip /var/lib/rancher/k3s/server/manifests/coredns{,-custom}.yaml
sudo vi /var/lib/rancher/k3s/server/manifests/coredns-custom.yaml
5. まとめ
k3sは簡単にインストールできますが、コンポーネント設定には癖があります。
ただ、それはKubernetes運用の思想がはっきりと表れた結果だと思うので、背景を理解して適切にk3sを利用していきましょう。
6. おまけ
k3sのCoreDNSはマニフェストでデプロイしてるけど、RKE2はHelmChartでデプロイされてるんですよねぇ。(統一してくれないかなぁ)