雑記帳

整理しない情報集

更新情報

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はシングルバイナリのため、バイナリを直接ダウンロードして実行しても使えます。インストールスクリプトはハッシュの検証やサービス登録の他、後述する内包コマンドへのリンクが作成されます(kubectlk3s kubectlなど)。競合するコマンドが既にインストールされている場合、リンクは作成されません。

マニフェスト

k8s/k3sはmanifestファイルと呼ばれるyamlファイルに構築する内容を記述して適用します。目的はcomposeファイルと同じですが、内容や書式は全く異なります。

基本的にリソースごとにmanifestファイルを書くことになるため、複数のmanifestファイルで構成されますが、---(半角ハイフン3つ)だけの行で区切って複数のmanifestファイルをまとめることもできます。

Helmと呼ばれるmanifestファイルを自動で構築するツールもありますが、今回は扱いません。Helm利用が推奨の構築方法ではありますが、manifestファイルを理解できていないと辛いので。。。

Namespace

名前空間を定義するmanifestです。

apiVersion: v1
kind: Namespace
metadata:
  name: my-namespace

metadata.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-pvc

spec.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: 8080

spec.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: 80

spec.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: 1Gi

storageClassNameで、どの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/UDP53番ポート)
  • PodからプライベートIPを除く全IPの443番ポート(https)へのアクセス

を許可しています。許可ルールを記述していく(拒否ルールは書けない)ため、競合することはありません。

ちなみに各種selectorは空({})にすると、すべてに適用されます。spec.podSelector: {}と指定すると、同一名前空間内のすべてのPodにポリシーが適用されます。

基本操作

作成したmanifestファイルは以下のコマンドで適用/削除できます。

# マニフェストを適用する場合
kubectl apply -f manifestファイル
# 適用したマニフェストを削除する場合
kubectl delete -f manifestファイル

適用後のリソースのステータス一覧は以下のコマンドで取得できます。名前空間がdefault以外の場合は-n 名前空間名のオプションが必要です。リソースの種類を短縮形で書いていますが、フルで書いても動作します(popodsnsnamespaceなど)。

コマンドの実行結果で該当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名

カテゴリ: ソフトウェア