Use Cases (更新: 2026/6/7)

Kubernetesデプロイ入門:kubectl applyからローリングアップデートとロールバックまで手で動かす

Deployment/Service/Ingressの役割、マニフェストの書き方、kubectl apply、ローリングアップデートとロールバック、liveness/readinessを最小YAMLで手を動かして覚える入門。

Kubernetesデプロイ入門:kubectl applyからローリングアップデートとロールバックまで手で動かす

「Kubernetesにデプロイしといて」

そう頼まれて、Claude Codeに「KubernetesのYAML作って」と投げた僕は、出てきた150行をそのままクラスタに apply しました。動いた。やった、と思った瞬間、Podが2分おきに再起動を始めたんです。

原因は、コピペしたマニフェストの livenessProbe が、まだ起動中のアプリを「死んでる」と判定して殺し続けていたこと。賢いAIが書いたYAMLでも、こうなる。理由はシンプルで、動くYAMLと、安全に動かせるYAMLは別物だからです。

この記事では、その「別物」の境目を手で確かめます。題材はローカルでも貼り付けて試せる小さなNGINXアプリ。自社アプリのDockerイメージがまだなくても、Deployment・Service・Ingressの役割から、kubectl apply、ローリングアップデート、ロールバックまでひと通り体で覚えられます。Dockerイメージ作りから見直したい人は Claude CodeとDocker連携 を先に読むとつながります。

この記事の要点

  • Kubernetesの主役は3つ。Deployment(Podを何個・どう更新するか)/ Service(安定した宛先)/ Ingress(外からの入口) の役割分担を最初に押さえる。
  • マニフェストは1ファイルに全部書いて kubectl apply -f で流せる。コピペで動く最小YAMLを下に置いた。
  • ローリングアップデートは「少しずつ入れ替える」、ロールバックは kubectl rollout undo で一発で戻す。 わざと壊して戻す練習を先にやる。
  • readinessProbe(宛先に入れていいか)と livenessProbe(再起動すべきか)は別物。 混同するとPodが再起動ループに入る。
  • K8sは万能ではない。1〜2台で足りるなら入れない。いつ要るかの判断基準も最後に書いた。

まず役者を3人だけ覚える

Kubernetesは用語が多すぎて、最初の壁はそこです。でも、最小構成で必要な役者は3人だけ。残り(ConfigMap、Secret、probe、resources)は脇役なので、まず主役から覚えます。

役者役割身近な例え
Deploymentアプリのコピー(Pod)を何個動かし、更新時にどう入れ替えるか管理する店長。バイトを何人シフトに入れ、入れ替えも仕切る
Serviceバラバラに増減するPodに、変わらない宛先(社内向けの固定窓口)を与える代表電話。誰が出ても同じ番号でつながる
Ingress外からのアクセスを、ドメインやパスごとに正しいServiceへ振り分ける受付。来訪者を行き先の部署へ案内する

ここで大事なのは、Podは使い捨てという前提です。落ちたら新しいのが立ち上がるし、更新のたびに作り直される。だからPodのIPは毎回変わる。その「変わるもの」に「変わらない宛先」をかぶせるのがServiceで、その外側の入口がIngress。この3層を分けて考えると、あとで「外からつながらない」ときの切り分けがラクになります。

Podは個別に手で作ることもできますが、入門では基本やりません。Deployment経由で作れば、台数管理も更新もロールバックも全部まとめて面倒を見てくれるからです。

Claude Codeに丸投げしない前提メモ

最初に正直に言うと、Claude Codeは便利です。でも「KubernetesのYAML作って」だけだと、僕が冒頭でやらかしたような事故りやすいYAMLが出てきます。勝手にLoadBalancerを作る、Secretを平文で書く、クラスタ固有のIngress Controller名を決め打ちする——AIはこっちのクラスタの事情を知らないので当然です。

なので、頼むときは「何を作るか」だけでなく「何を作らないか」も渡します。コードフェンスの中が、僕が実際に使っている指示文です。

Kubernetes初心者向けに、ローカルでも検証できるDeployment一式を作ってください。

前提:
- namespaceは claude-k8s-demo
- private registryは使わず nginx:1.27-alpine を使う
- ConfigMapでHTMLを1つ差し替える
- Deploymentはreplicas 2、RollingUpdate、readinessProbe、livenessProbe、resourcesを含める
- ServiceはClusterIPにする
- Ingressは任意利用として claude-k8s.local を使う
- Secretの実値はGitにコミットしない方針だけ説明する
- kubectl apply、rollout status、port-forward、rollbackの確認コマンドも書く

制約:
- NodePortやLoadBalancerは使わない
- Secretの実値をYAMLに書かない
- クラスタ全体に影響するClusterRoleやNamespace削除コマンドは出さない

namespace、公開方法、イメージ、probe、resources、Secret方針を先に決める。それだけで危ない自動生成がごっそり減ります。同じプロジェクトで何度も使うなら、この前提を CLAUDE.md に移しておくと毎回書かずに済みます。考え方は Claude.mdベストプラクティス に整理しました。

コピペで動く最小マニフェスト

理屈より動かしたほうが早いので、まず手元で動くものを置きます。次の内容を k8s/claude-k8s-demo.yaml として保存してください。kind、minikube、Docker Desktop Kubernetes、検証用クラスタのどれでも試せます。Namespace、ConfigMap、Deployment、Service、Ingressを1ファイルに --- で区切って入れてあります。

apiVersion: v1
kind: Namespace
metadata:
  name: claude-k8s-demo
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-page
  namespace: claude-k8s-demo
data:
  APP_ENV: "demo"
  index.html: |
    <!doctype html>
    <html lang="ja">
      <head>
        <meta charset="utf-8" />
        <title>Claude Code Kubernetes Demo</title>
      </head>
      <body>
        <h1>Claude Code Kubernetes Demo</h1>
        <p>Deployment, Service, ConfigMap, probes are running.</p>
      </body>
    </html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-web
  namespace: claude-k8s-demo
  labels:
    app: demo-web
spec:
  replicas: 2
  revisionHistoryLimit: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: demo-web
  template:
    metadata:
      labels:
        app: demo-web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
          env:
            - name: APP_ENV
              valueFrom:
                configMapKeyRef:
                  name: demo-page
                  key: APP_ENV
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              cpu: "250m"
              memory: "128Mi"
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 3
            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 2
            failureThreshold: 3
          volumeMounts:
            - name: demo-page
              mountPath: /usr/share/nginx/html/index.html
              subPath: index.html
              readOnly: true
      volumes:
        - name: demo-page
          configMap:
            name: demo-page
---
apiVersion: v1
kind: Service
metadata:
  name: demo-web
  namespace: claude-k8s-demo
spec:
  type: ClusterIP
  selector:
    app: demo-web
  ports:
    - name: http
      port: 80
      targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-web
  namespace: claude-k8s-demo
spec:
  ingressClassName: nginx
  rules:
    - host: claude-k8s.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: demo-web
                port:
                  name: http

初心者が一番ハマるのは、selector.matchLabels とPod templateの labels、そしてServiceの selector の3つを app: demo-web で一致させることです。ここがズレると、Deploymentは自分のPodを見つけられず、Serviceも宛先を見失います。僕も最初、コピペ中にラベルを1文字だけ変えてしまって「Podは動いてるのにつながらない」で30分溶かしました。Claude Codeにレビューさせるなら「selector、template labels、Service selectorが一致しているか確認して」と一言添えてください。

kubectl applyで流して、port-forwardで覗く

マニフェストを保存したら、クラスタにつながっているか先に確認してから流します。kubectl apply -f は「このファイルに書いた状態に合わせて」という指示で、何度実行しても同じ結果になります(差分だけ反映される)。これが手順書より楽な理由です。

kubectl cluster-info
kubectl apply -f k8s/claude-k8s-demo.yaml
kubectl -n claude-k8s-demo rollout status deployment/demo-web
kubectl -n claude-k8s-demo get pods -l app=demo-web
kubectl -n claude-k8s-demo get service demo-web
kubectl -n claude-k8s-demo port-forward service/demo-web 8080:80

最後の port-forward は、Ingressがまだ無くてもServiceまで届いているかを確かめる近道です。別のターミナルでアクセスします。

curl http://localhost:8080/

<h1>Claude Code Kubernetes Demo</h1> が返ってくれば、Deployment → Service → Pod の経路は生きています。Ingress Controllerが入っている環境なら、claude-k8s.local をローカルのhostsファイルに向けて確認します。割り当てIPはクラスタの種類で違うので、まず現物を見てから決めてください。

kubectl -n claude-k8s-demo get ingress demo-web
kubectl -n claude-k8s-demo describe ingress demo-web

ここを曖昧にしたままClaude Codeに任せると、環境に合わないIngress注釈ばかり増えていきます。Ingressは「最後に疑う」のがコツです。外からつながらないときは、Pod → Service → port-forward → Ingress の順に切り分ける。Serviceで届かないものはIngressをいじっても直りません。

ローリングアップデートとロールバックを、わざと壊して覚える

Deploymentの本当の価値は、台数を増やすことより「安全に入れ替えて、ダメなら戻せる」ことにあります。最初に置いたYAMLで maxUnavailable: 0maxSurge: 1 を指定しているので、更新は新しいPodを1個立ててから古いのを1個落とす形で進みます。これがローリングアップデートで、途中で全部止まる瞬間が生まれません。

まずは正常な更新を流して、履歴が残ることを見ます。

kubectl -n claude-k8s-demo set image deployment/demo-web nginx=nginx:1.27-alpine
kubectl -n claude-k8s-demo rollout status deployment/demo-web
kubectl -n claude-k8s-demo rollout history deployment/demo-web

次が本番の練習。あえて存在しないタグに更新して、壊します。

kubectl -n claude-k8s-demo set image deployment/demo-web nginx=nginx:not-a-real-tag
kubectl -n claude-k8s-demo rollout status deployment/demo-web --timeout=30s
kubectl -n claude-k8s-demo get pods
kubectl -n claude-k8s-demo describe deployment demo-web

ImagePullBackOff が見えるはずです。でも、ここで気づいてほしいのは——サイトはまだ落ちていないこと。新しいPodが立ち上がれないので、Kubernetesは古いPodを残したままにしています。maxUnavailable: 0 が効いている証拠です。慌てずに戻します。

kubectl -n claude-k8s-demo rollout undo deployment/demo-web
kubectl -n claude-k8s-demo rollout status deployment/demo-web

この「わざと壊して戻す」を一度やっておくかどうかで、本番でのメンタルが全然違います。本番で初めて rollout undo を打つチームは、どのrevisionに戻るのか、ConfigMapやSecretの変更も一緒に戻るのか(戻りません。Deploymentのテンプレートしか戻らない)、監視アラートは止まるのか、で必ず迷います。Claude Codeにロールバック手順を書かせるときは「戻る対象と戻らない対象も書いて」と頼むと、この落とし穴を先に潰せます。本番反映までまとめて自動化したくなったら Claude CodeでCI/CDパイプラインを構築 が続きになります。

probeとSecret、ここだけは事故りやすい

脇役と言いましたが、事故率が高いのはむしろこの2つです。冒頭の再起動ループも、ここでやらかしました。

readinessProbe と livenessProbe は意味が違います。

  • readinessProbe:このPodを「Serviceの宛先に入れていいか」の判定。失敗してもPodは殺されず、宛先から外れるだけ。
  • livenessProbe:このPodを「再起動すべきか」の判定。失敗が続くとコンテナが殺されて作り直される。

僕の事故は、起動に時間のかかるアプリに対して livenessProbe の initialDelaySeconds が短すぎて、起動中を「死んでる」と誤判定し、永遠に再起動し続けたケースでした。起動が遅いアプリには startupProbe を足すか、initialDelaySecondsfailureThreshold を現実に合わせます。DBの一時障害で liveness を落とす設計にすると、Pod再起動が連鎖して傷口が広がるので注意してください。

Secretは「Secretにしたから安全」ではありません。 標準のSecretはbase64で保存されるだけで、暗号化ではない。Gitにコミットすればそのまま漏えいです。ローカルで形だけ作るなら、ファイルを .gitignore に入れたうえでこうします。

kubectl -n claude-k8s-demo create secret generic demo-api-secret \
  --from-literal=API_TOKEN='replace-me-locally' \
  --dry-run=client \
  -o yaml > secret.local.yaml

kubectl apply -f secret.local.yaml

実務ではクラウドのSecret Manager、External Secrets、Sealed Secrets、SOPSあたりから方針をチームで1つ選び、リポジトリには実値を置きません。一方で、DBパスワードやAPIキーをConfigMapに入れるのは論外です。ConfigMapは「秘密ではない設定」専用と割り切ってください。

いつKubernetesを入れるべきか(入れない判断も大事)

ここまで書いておいてなんですが、全部のアプリにK8sが要るわけではありません。 むしろ入門者が一番やりがちな過剰投資が「とりあえずKubernetes」です。1台のVPSで月数千リクエストを捌くだけのサイトに、3ノードのクラスタとIngress ControllerとSecret運用を持ち込むと、アプリより運用に時間を取られます。

僕の中のざっくりした判断基準はこうです。

状況向いている選択
1〜2台で足りる / トラフィックが安定素のサーバー or PaaS(Render、Fly.io等)。K8sは不要
コンテナを数個、たまに更新Docker ComposeやマネージドコンテナでもOK
複数サービスを安全に更新・自動回復・水平スケールしたいKubernetesが効いてくる
チームが増え、デプロイの手順を全員でそろえたいK8s + CI/CDで標準化する価値が出る

判断軸はシンプルで、「ローリングアップデート・自動回復・水平スケール・宣言的な管理」のうち2つ以上が今すぐ欲しいかどうか。欲しくないなら、まだ早い。K8sは強力ですが、強力なぶん運用コストも乗ります。先にDockerで1コンテナをきれいに動かす経験を積むほうが、結局は近道です(Claude CodeとDocker連携 参照)。

よくある質問

Q. DeploymentとPodは何が違うの? A. Podが実際に動く最小単位(コンテナの入れ物)で、Deploymentはそのお世話係です。Deploymentに「2個動かして」と書けば、1個落ちても自動で補充し、更新やロールバックもまとめて面倒を見ます。入門ではPodを直接作らず、Deployment経由にするのが基本です。

Q. ServiceとIngressはどちらか片方でいい? A. 役割が別なので、外部公開するなら両方使うのが普通です。Serviceはクラスタ内に「変わらない宛先」を作る役、Ingressはその外側で「どのドメイン・パスをどのServiceへ」振り分ける入口役。ローカル検証だけなら、Ingressを後回しにして kubectl port-forward でServiceに直接つないで確認できます。

Q. ローリングアップデートとロールバックの違いは? A. ローリングアップデートは「新しい版を少しずつ入れ替える」前進、ロールバックは「前の版に戻す」後退です。戻すのは kubectl rollout undo deployment/<name> の一発。ただし戻るのはDeploymentのテンプレートだけで、ConfigMapやSecretの変更は戻らない点に注意してください。

Q. kubectl applykubectl create はどっちを使う? A. 入門なら apply 一本でいいです。apply はファイルに書いた状態へ寄せる宣言的なコマンドで、何度流しても安全(差分だけ反映)。create は新規作成専用で、すでにある物に対して流すとエラーになります。

Q. probeは省略してもいい? A. 動きはしますが、本番では入れてください。readinessProbeが無いと、まだ準備できていないPodにもアクセスが流れて一瞬エラーが出ます。livenessProbeが無いと、ハングしたPodが永遠に再起動されません。最初の initialDelaySeconds だけ現実に合わせれば、冒頭の僕のような再起動ループは避けられます。

実際に試した結果

この記事の流れは、ローカルクラスタで「apply → rollout status → port-forward → curl → 存在しないタグへ更新 → rollout undo」の順に手で確かめる前提で組みました。実際にやってみて一番効いたのは、maxUnavailable: 0 を入れた状態でわざと壊す練習です。Podが ImagePullBackOff になっても旧Podが残ってサイトが落ちない、という挙動を一度自分の目で見ると、ローリングアップデートの意味が腹落ちします。

そして、初心者が本当につまずくのはIngressではありませんでした。selectorとlabelの不一致、probeのpath・遅延の設定、Secretサンプルの扱い——この3つです。Claude Codeに最初から「安全な公開範囲・Secret禁止・ロールバック確認」を渡しておくほうが、後からYAMLを直すより速く、レビューもしやすい。賢いAIに完璧なYAMLを期待するより、壊しても戻せる小さな構成を手で回す。遠回りに見えて、これがK8sを怖くなくする一番の近道でした。

公式の一次情報も必ず当たってください。Kubernetes DeploymentsLiveness/Readiness/Startup probes は特にブックマーク推奨です。チームでこの手順を実リポジトリに落とし込むなら Claude Code研修・導入相談 で、マニフェストの棚卸しからCIレビュー、ロールバック演習までまとめて整えられます。

#Kubernetes #kubectl #Deployment #ローリングアップデート #Claude Code
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。