Kubernetesで自動復旧するWebサーバの構築

Kubernetesで自動復旧するWebサーバを構築することがこの記事の目標です。Kubernetesについては概要がわかった状態で見ていただけると理解しやすいと思います。

特に、Kubernetesを学ぶうえで重要なReplicaSet、Deploymentについても解説し、それらを使ったWebサーバ構築を行っています。実際に手を動かしながら理解できる内容となっておりますので、見ていっていただけると幸いです。

この記事で学べる内容
  • マニフェストの書き方
  • ReplicaSet、Deploymentの概要
  • Kubernetesを用いたWebサーバの構築方法

前提

  • 筆者のPCはM1 Macobook Pro、macOS 15.6.1
  • Kubernetesクラスタは kind を用いて構築

kindの環境構築がまだの方は、この記事を読む前に以下の記事を読んでいただけると良いです。

【初心者向け】Kubernetesの基礎

マニフェストについて

まず、Kubernetes クラスタ上でリソースの設定を行うのに必要な マニフェスト とよばれるファイルの書き方について概説します。

ここでは掻い摘んで解説をするので、詳細については以下の公式ドキュメントを御覧ください。

Kubernetesドキュメント

マニフェストとは

Kubernetesで使用するオブジェクトの設定内容を書いたファイルのことです。マニフェストには、オブジェクトの仕様(sepc)や状態(status)を書いておきます。Kubernetesは、マニフェストに書かれたオブジェクトが常に存在し続けるように動きます。

例えば、マニフェストには2つのPodを実行するよう書かれてあったとき、2つの内1つのPodが停止した際は新しいPodを立ち上げて、常に2つのPodがあるように動いてくれます。これにより、耐障害性の高いアプリケーションの作成の基盤として優れています。

マニフェストの書き方

それでは次に、マニフェストの書き方を見ていきましょう。マニフェストはYAML形式またはJSON形式で記述されます。この節では、YAML形式における書き方および、マニフェストに記載する必須のフィールドについて解説します。

必須のフィールド

マニフェストには以下のことを必ず書く必要があります。

  • apiVersion
    • 使用するKubernetesAPIのバージョン
  • kind
    • 作成するオブジェクトの種類
  • metadata
    • オブジェクトを一意に特定するための情報
  • spec
    • 作成するオブジェクトのあるべき状態

上記のことを記載した最小構成のマニフェストは以下のようになります。

YAML
apiVersion: v1
kind: Pod
metadata:
  name: httpd
spec:
  containers:
  - name: httpd
    image: httpd:alpine3.22

これは、httpd:alpine3.22 によりデプロイしたコンテナを1つ持つPodを作成するマニフェストです。

specの部分については、作成するリソース毎に書き方が変わります。詳細を知りたい方は、以下のページを参照ください。

NO IMAGE

上記のマニフェストでは、httpd:alpine3.22 によりデプロイしたコンテナを持つPodを作成しましたが、現場においてはPod単独での作成はコンテナの冗長化ができないため推奨されません。それでは、実際の本番環境ではどうしているかというと、Deployment および その下に位置するReplicaSetというリソースを作り、アプリケーションが稼働するPodはReplicaSetが作成しています。そのため、次にこれら Deployment および ReplicaSet について解説します。

ReplicaSet と Deployment

この節では、実際の現場で使われることが多い ReplicaSet と Deployment について解説します。結論としては、Pod、ReplicaSet、Deployment は以下の図のような関係になっているので、これらを念頭に解説を読んでいただければ理解が深まると思います。

ReplicaSet

ReplicaSetは、指定した数だけPodを複製するリソースになります。具体的には、 replicasで複製する数を指定します。実際のマニフェストの例としては以下になります。

YAML
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: httpd-server
  labels:
    name: httpd-server
spec:
  # replicasに指定した数だけ Pod を複製。ここでは3つ作成する。
  replicas: 3
  selector:
    matchLabels:
      # templateで指定するlabelsと一致する必要あり
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:alpine3.22

コメントでも書いてる通りselector内のmatchLabelsで指定する文字列は、その下のtemplate内のlabelsと一致する必要があります。

上記マニフェストを実行してPodの状態を確認すると、以下のように3つのPodが作成されることが分かります。

Bash
$ kubectl get pod --namespace default
NAME                                READY   STATUS    RESTARTS   AGE
httpd-server-28b94                  1/1     Running   0          2m27s
httpd-server-6lpkz                  1/1     Running   0          2m27s
httpd-server-k6mbj                  1/1     Running   0          2m27s

Deployment

Deployment は ReplicaSet の上位概念にあたり、実際の現場では ReplicaSetと併せて使用されています。Podの冗長化という点だけ見ると、ReplicaSetまでで十分と感じるかもしれません。しかし、本番サービスのコンテナを更新したい場合、ReplicaSet だけだとすべてのPodを作り直す必要がり、サービスを提供できない時間が発生してしまいます。

Deployment を用いると、変更前の ReplicaSet を継続させながら、更新を加えた新しいReplicaSet を動かすことができるため、ダウンタイム無しでメンテナンスをすることが可能になります。

実際のマニフェストの例としては以下になります。

YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
  labels:
    app: httpd
spec:
  replicas: 3
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      name: httpd-pod
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:alpine3.22

このマニフェストを適用すると、以下のようにDeployment、ReplicaSet、Pod(replicasで設定している通り3つ)が作成されている事がわかります。

Bash
$ kubectl get deployment --namespace default
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
httpd-deployment   3/3     3            3           45s

$ kubectl get replicaset --namespace default
NAME                          DESIRED   CURRENT   READY   AGE
httpd-deployment-5c9c5dbdf9   3         3         3       58s

$ kubectl get pod --namespace default
NAME                                READY   STATUS    RESTARTS   AGE
httpd-deployment-5c9c5dbdf9-2rg78   1/1     Running   0          6m18s
httpd-deployment-5c9c5dbdf9-lkzpg   1/1     Running   0          6m18s
httpd-deployment-5c9c5dbdf9-lp8p9   1/1     Running   0          6m18s

Kubernetes で 自動復旧する Web サーバを立てる

早速 Kubernetes で Web サーバを構築し、自動復旧する様子を見てみましょう。

適当なディレクトリを作って、以下のマニフェストを配置します。

YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-deployment
  labels:
    app: httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      name: httpd-pod
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:alpine3.22
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: httpd-service
spec:
  selector:
    app: httpd
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080

新しいリソースのServiceについて軽く説明します。
ServiceはPodで実行されているアプリケーションを公開するために使われるリソースです。このリソースは仮想IPを持った状態でPodの前面に配置され、クライアントがSeviceに設定されたIPアドレスにアクセスすると、各Podにアクセスを転送します。
そのため、イメージとしてはロードバランサーに近い概念です(もちろん厳密には違います)。

それでは、上記マニフェストを適用してみましょう。以下のコマンドを実行してください。

Bash
kubectl apply --filename http_server.yaml --namespace default

次の表示がされれば、無事 Deployment と Service が起動しました。

Bash
$ kubectl apply --filename http_server.yaml --namespace default
deployment.apps/httpd-deployment created
service/httpd-service created

念の為、Pod、Replicaset、Deployment、Serviceを確認してみましょう。

Bash
$ kubectl get pod --namespace default
NAME                                READY   STATUS    RESTARTS   AGE
httpd-deployment-6bd99c794d-fh9z5   1/1     Running   0          2m11s

$ kubectl get replicaset --namespace default
NAME                          DESIRED   CURRENT   READY   AGE
httpd-deployment-6bd99c794d   1         1         1       2m20s

$ kubectl get deployment --namespace default
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
httpd-deployment   1/1     1            1           2m27s

$ kubectl get service --namespace default
NAME            TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
httpd-service   NodePort    10.96.59.52   <none>        80:30080/TCP   2m34s
kubernetes      ClusterIP   10.96.0.1     <none>        443/TCP        47h

ちゃんと起動していることがわかりますね。それではブラウザでもapacheのデフォルトのコンテンツが見れるか確認してみましょう。まず、以下のコマンドで、localhostの8080番ポートをPodの80番ポートに転送する設定を行います。

Bash
kubectl port-forward svc/httpd-service 8080:80

実行すると、以下のようにターミナルが止まるので、この間にブラウザで http://localhost:8080にアクセスしてみます。以下のデフォルトのコンテンツが返ってくればアクセス確認完了です。

復旧動作確認

最後に、作成したPodを削除してもすぐに起動する様子を観察しましょう。

まず、今まで開いていたターミナルとは別のターミナルを開き、以下のコマンドを実行してください。

Bash
kubectl get pod --watch --namespace default

このコマンドは指定したリソースの更新を監視するコマンドであり、実行結果は以下です。

Bash
$ kubectl get pod --watch --namespace default
NAME                                READY   STATUS    RESTARTS   AGE
httpd-deployment-6bd99c794d-2dnhn   1/1     Running   0          33s

これを実行し続けた状態で、もとのターミナルに戻りPodを削除してみましょう。まず作成されたPod名を取得します。

Bash
$ kubectl get pod --namespace default
NAME                                READY   STATUS    RESTARTS   AGE
httpd-deployment-6bd99c794d-2dnhn   1/1     Running   0          16m

渡しの環境の場合、httpd-deployment-6bd99c794d-2dnhnがPod名なので、次のコマンドで削除してみます。

Bash
kubectl delete pod httpd-deployment-6bd99c794d-2dnhn --namespace default

そうすると、監視をしていたターミナルの出力が変化し、次のような表示がされます。

Bash
$ k get pod --watch --namespace default
NAME                                READY   STATUS    RESTARTS   AGE
httpd-deployment-6bd99c794d-2dnhn   1/1     Running   0          33s
httpd-deployment-6bd99c794d-2dnhn   1/1     Terminating   0          17m
httpd-deployment-6bd99c794d-z5pzz   0/1     Pending       0          0s
httpd-deployment-6bd99c794d-z5pzz   0/1     Pending       0          0s
httpd-deployment-6bd99c794d-z5pzz   0/1     ContainerCreating   0          0s
httpd-deployment-6bd99c794d-2dnhn   0/1     Completed           0          17m
httpd-deployment-6bd99c794d-z5pzz   1/1     Running             0          1s
httpd-deployment-6bd99c794d-2dnhn   0/1     Completed           0          17m
httpd-deployment-6bd99c794d-2dnhn   0/1     Completed           0          17m

上記出力を見ると、httpd-deployment-6bd99c794d-2dnhnの削除が開始されたタイミングで別のPodであるhttpd-deployment-6bd99c794d-z5pzzが作成されたのが分かりますね。ここでは確認しませんが、再度ポートフォワーディング設定をしてあげると、ブラウザでコンテンツが表示されることを確認できます。

まとめ

今回は、前回解説したKubernetesから少し踏み込んで、実際にWebサーバを構築し自動復旧する動作の確認を行いました。今回の記事を通して、少しでもKubernetesの使い方に慣れていただければ幸いです。

また、もっと学んでみたい方は以下の本がおすすめです。この本は実際に手を動かしながらKubernetesを学んでいける内容となっているので、より詳しい内容を知りたいという方がいれば是非購入をご検討ください。

今回の内容
  • マニフェストの書き方
  • ReplicaSet、Deploymentの概要
    • ReplicaSetはPodをまとめたもの
    • DeploymentはReplicaSetをまとめたもの
  • 自動復旧するWebサーバを構成し、実際にアクセスして動作を確認。
  • わざとサーバを壊して、自動復旧するか確認