Composeからk3sに移行してみる
公開日:
カテゴリ: ソフトウェア
ラズパイに乗っけたDocker Composeをk3sに移行してみたので、その覚書です。
といってもラズパイは1台しかないのでクラスタは組めずにシングルノード構成となるため、Dockerの構成とさほど変わりませんが・・・。
インストール
インストール方法は、公式のドキュメントにかなり丁寧な説明が載っています。
準備
Raspberry Piにインストールする場合はcgroupsの設定が必要です。また、ホストOSがRaspberry Pi OSではなくUbuntuの場合は追加のパッケージのインストールも必要のようです。
クラスタを組む場合かつファイアウォール(ufwやfirewalldなど)を使用している場合は追加設定が必要になるようです(ラズパイで手動で導入している場合もUbuntuタブ)。今回はシングルノード構成のためスキップしています。
インストール
公式サイトのセットアップスクリプトを実行するのみでインストールできます。そのまま実行すると特権ユーザしかkubectlできなくなる(一般ユーザは毎回sudoが必要になる)ため、--write-kubeconfig-mode 644オプションを付けておきます。
curl -sfL https://get.k3s.io | sh - --write-kubeconfig-mode 644ちなみにk3sはシングルバイナリのため、バイナリを直接ダウンロードして実行しても使えます。インストールスクリプトはハッシュの検証やサービス登録の他、後述する内包コマンドへのリンクが作成されます(kubectl → k3s kubectlなど)。競合するコマンドが既にインストールされている場合、リンクは作成されません。
マニフェスト
k8s/k3sはmanifestファイルと呼ばれるyamlファイルに構築する内容を記述して適用します。目的はcomposeファイルと同じですが、内容や書式は全く異なります。
基本的にリソースごとにmanifestファイルを書くことになるため、複数のmanifestファイルで構成されますが、---(半角ハイフン3つ)だけの行で区切って複数のmanifestファイルをまとめることもできます。
Helmと呼ばれるmanifestファイルを自動で構築するツールもありますが、今回は扱いません。Helm利用が推奨の構築方法ではありますが、manifestファイルを理解できていないと辛いので。。。
Namespace
名前空間を定義するmanifestです。
apiVersion: v1
kind: Namespace
metadata:
name: my-namespacemetadata.nameに指定した値でkubernetes.io/metadata.nameラベルが自動で設定されます。
Deployment
Pod定義と展開方法を定義するmanifestです。Podを定義するPodというmanifestの種類もありますが、k8sの自動スケーリング機能の性質上、基本的にあるべき状態を定義するこちらのmanifestが使われます。
書式こそ異なりますが、ネットワーク周りを除き記載内容はComposeファイルとあまり変わりません。イメージをビルドする環境はありませんので、基本的にどこかのリポジトリに登録してPullする方式になります。コンテナイメージの指定時にアドレスを指定しない場合、Composeと同じくdocker.ioとなります。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: image-url:tag
env:
- name: TZ
value: Asia/Tokyo
ports:
- containerPort: 8080
protocol: TCP
volumeMounts:
- name: config-volume
mountPath: /etc/my-config
- name: data-volume
mountPath: /data
volumes:
- name: config-volume
configMap:
name: my-config
- name: data-volume
persistentVolumeClaim:
claimName: my-pvcspec.templateでPodの定義を作成し、spec.selectorで対象の定義を選択してspec.replicasの数だけPodを立ち上げるイメージです。
containers[].ports[]でアプリの公開ポートを明示できます(上の例では8080)が、この時点では同一Pod内からしかアクセスできません。Dockerでいうところのexposeのようなイメージです。
Service
Pod外にアプリケーションを公開するためのmanifestです。Pod間での通信は、基本的にここで定義したサービス名を用いて名前解決します。
Serviceは自身が内部IPを持ちます。Pod内での公開ポートとは別のポートで待ち受けることができます。
公開したサービスはサービス名.名前空間名.svc.cluster.localで名前解決・アクセスできます。ndotsの関係で、同一名前空間内ではサービス名のみ、同一クラスタ内ではサービス名.名前空間名で名前解決・アクセスできます。
apiVersion: v1
kind: Service
metadata:
name: my-app-service
namespace: my-namespace
spec:
type: ClusterIP
selector:
app: my-app
ports:
- port: 80
targetPort: 8080spec.typeでServiceの種類を指定します。ClusterIPではクラスタ内部に、NodePortではノード内部に専用ポート帯(30000-32767)で、LoadBalancerではクラスタ外部にそれぞれサービスを公開できます。
spec.ports[].targetPortにPod側のポート、spec.ports[].portに公開側のポートを指定するとポートが変換されます。上の例ではmy-apps-service.my-namespace.svc.cluster.local:80へのアクセスがPodの8080番ポートへのアクセスに変換されます。
Ingress
クラスタ外からServiceへのHTTP/HTTPSアクセスを定義するためのmanifestです。
別途Infress Controllerが必要ですが、k3sでは標準組み込みのTraefikのInfress Controllerが立っているので、別のIngress Controllerを使いたい等がなければ別途対応する必要なく使えます。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
namespace: my-namespace
spec:
ingressClassName: traefik
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80spec.rules[].hostに公開するサーバのホスト名、spec.rules[].http.paths[]にパスの設定を定義します。このあたりの設定は一般的なリバースプロキシの設定と大差ありません。
パスの設定内のbackendに、プロキシ先のServiceとポート番号を指定します。
ConfigMap
環境変数や設定ファイルを定義するmanifestです。ファイルの内容はブロックスカラーで記述します。
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
namespace: my-namespace
data:
config.yml: |
title: my-title
body: Sample-content
date: 2026-07-03
sample.txt: |
サンプルテキスト
テキストデータ2行目上のDeploymentと組み合わせると、data."config.yml"の記述内容が/etc/my-config/config.ymlに、data."sample.txt"の記述内容が/etc/my-config/sample.txtにマウントされます。
PVC (PersistentVolumeClaim)
永続保存領域を定義するmanifestです。PVCに定義した内容でPVが自動で生成されます。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
namespace: my-namespace
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 1GistorageClassNameで、どのStorageClassを使用するかを指定します。k3sの標準ではlocal-pathというStorageClassが定義されていて、ホストの/var/lib/rancher/k3s/storage内にPVが作成されます。
クラウドなどにPVを置く場合、予めStorageClassを作成しておき、そのStorageClass名を指定することになります。
NetworkPolicy
Podのネットワークアクセス(内向き・外向き両方)の制御ポリシーを設定するmanifestです。同一名前空間内に、そのPodが対象となるNetworkPolicyが1つも無い場合は全許可、それ以外の場合は全拒否がデフォルト設定です。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-policy
namespace: my-namespace
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ports:
- protocol: TCP
port: 8080
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
ports:
- protocol: TCP
port: 443サンプルがちょっと長いですが、やっていることはかなりシンプルです。
spec.ingress[]で内向き、spec.egress[]で外向きのポリシーを設定します。上のサンプルでは、
- 組み込みTraefikからPodへの
8080番ポートへのアクセス - PodからKubernetes DNSへのアクセス(TCP/UDP
53番ポート) - PodからプライベートIPを除く全IPの
443番ポート(https)へのアクセス
を許可しています。許可ルールを記述していく(拒否ルールは書けない)ため、競合することはありません。
ちなみに各種selectorは空({})にすると、すべてに適用されます。spec.podSelector: {}と指定すると、同一名前空間内のすべてのPodにポリシーが適用されます。
基本操作
作成したmanifestファイルは以下のコマンドで適用/削除できます。
# マニフェストを適用する場合
kubectl apply -f manifestファイル
# 適用したマニフェストを削除する場合
kubectl delete -f manifestファイル適用後のリソースのステータス一覧は以下のコマンドで取得できます。名前空間がdefault以外の場合は-n 名前空間名のオプションが必要です。リソースの種類を短縮形で書いていますが、フルで書いても動作します(po→pods、ns→namespaceなど)。
コマンドの実行結果で該当PodのステータスがRunningになっていれば、Podは正常に稼働しています。
# Podの一覧を表示
kubectl get po
# Namespaceの一覧を表示
kubectl get ns
# Deploymentの一覧を表示
kubectl get deploy
# Serviceの一覧を表示
kubectl get svc
# Ingressの一覧を表示
kubectl get ing
# ConfigMapの一覧を表示
kubectl get cm
# PVCの一覧を表示
kubectl get pvc
# NetworkPolicyの一覧を表示
kubectl get netpolリソースの状態を詳細表示するにはdescribeサブコマンドを使用します。
kubectl describe リソース種類 [リソース名]
# 例: Pod名"test1"のリソース詳細を表示
kubectl describe po test1その他
ConfigMapやSecretsはコマンドでファイルから作成することも可能です。ディレクトリを指定すると、そのディレクトリ内のファイルをすべてConfigMapに取り込みます(サブディレクトリは取り込まれません)。
この方法ではmanifestファイルは使えません。
# ファイルまたはディレクトリからConfigMapを作成
kubectl create cm ConfigMap名 --from-file=ファイルまたはディレクトリ
# ConfigMapの削除
kubectl delete cm ConfigMap名カテゴリ: ソフトウェア