Kubernetesでとりあえずアプリを動かしてみる

こんにちは、ルートです。

この記事では、実際の現場を想定してKubernetesでとりあえずアプリを動かしてみようと思います。Kubernetesでは色々なリソースがありますが、その中でも特に使われるのが以下のリソースたちです。

  • ConfigMap
  • Secret
  • Deployment
  • Service

今回は、現場で使われるような構成のアプリをとりあえず動かしてみることを目標にするので、各リソースはそこまで凝った作りにはしませんでした。ただ、この記事を読むことで、上に書いたような各種リソースの作り方やそれらを関連付ける方法が学べるので、Kubernetesに興味がある方は、是非読んでいただけると幸いです!

この記事で学べる内容
  • Secretの概要
  • ConfigMapの概要
  • Kubernetesを用いた簡単なアプリの作成方法

構成図と概要

早速ですが、この記事で作るWebアプリの構成図を以下に記します:

詳しいことはこの後説明しますが、アプリに必要な設定はConfigMap、パスワード等の情報はSecretに書き、それぞれの情報をReplicaset配下のPodに読み込ませるという構成です。また、このアプリについては、Serviceを通してユーザに公開されます。

Kubernetesの基本については以下の記事で解説しているので、もしKubernetesの基礎から学びたいという方はぜひご覧ください。

【初心者向け】Kubernetesの基礎
Kubernetesで自動復旧するWebサーバの構築

ConfigMapとSecret

ConfigMap

コンテナの外部から値を設定するときに使うリソースです。時期やサービスの稼働状況によってある変数の値を変えなくてはいけないとき、プログラム中にその変数がハードコードされていると、変更のたびにシステムをデプロイしなくてはいけません。このような状況を無くすために、各Podで使う値を一度にまとめておくのが、ConfigMapというリソースです。

以下、ConfigMapの具体例です。

YAML
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  KEY1: "value1"
  KEY2: "value2"

実際に値を設定しているのは、dataの部分です。上記の例ではKEY1KEY2にそれぞれ value1value2を設定しています。この ConfigMap をPodを作成するときに読み込ませると、Pod内のアプリケーションが KEY1 および KEY2value1value2 として取り扱うことができます。

Secret

アプリケーションが使う各種のパスワード情報をConfigMapに書いてしまうと、ConfigMapを閲覧できる人全員がパスワード情報も取得できてしまい、セキュリティ的によろしくない状況になってしまいます。

そこで登場するのが、Secret というリソースです。Secretにパスワード等の情報を記載しておくことで、比較的安全に各Podに情報を渡すことが可能になります。

ここで説明したSecretの安全性は、クラスタ内の情報のやり取りにおける安全性という意味であり、Secretを定義したYAMLファイル自体が安全というわけではなりません。YAMLファイルを読める場合、Secretに記載した情報はすぐに盗めてしまいます。

それでは、Secreリソースの定義方法をみていきましょう。ConfigMapと同じように書くことができます。

YAML
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
data:
  APP_PASSWORD: cGFzc3dvcmQ= # base64でエンコードする必要がある

ここで重要なのが、記載する情報は事前にBase64でエンコードする必要があります。Base64自体は暗号化でもなんでもなく、デコードはすぐにできてしまうため、上記で注意したようにこのYAMLファイルを読めてしまうとすぐにパスワード等の情報を盗まれてしまいます。

そのため、運用をするにあたりどういうセキュリティ対策を取るべきかは議論する必要がありますが、少なくともクラスタ内での機密情報の遣り取りをすることがある場合は、Secretに情報を書いておくとよいでしょう。

とりあえず動くアプリ作成

それでは、実際にこの記事で使うアプリを作成していきましょう。繰り返しにはなりますが、今回作るアプリの概念図は以下になります。

Secretの作成

今回、Secretは以下を用います。アプリに渡すパスワードという設定ですが、今回はcurlコマンドでアクセスした際に表示される文字列をAPP_PASSWORDとしてアプリの環境変数に渡します。

YAML
apiVersion: v1
kind: Secret
metadata:
  name: demo-secret
type: Opaque
data:
  APP_PASSWORD: cGFzc3dvcmQ=

ConfigMapの作成

とりあえず動くということを目標にしてるので、ConfigMap は Secret とほぼ同じような構成にします。

YAML
apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-configmap
data:
  APP_MESSAGE: "Hello from ConfigMap"

Secretと同じようにして、アプリ内で使う変数 APP_MESSAGE に 「Hello from ConfigMap」という文字列を代入するだけのConfigMapになります。

Serviceの作成

外からのアクセスをアプリに誘導するためのServiceリソースを作成します。今回は詳しいことは省きますが、以下のYAMLファイルを持ちいます。

YAML
apiVersion: v1
kind: Service
metadata:
  name: secret-demo-svc
spec:
  selector:
    app: secret-demo
  ports:
    - port: 8080
      targetPort: 8080

動かすアプリの作成

今回動かすアプリは、Pyhonで作成します。といっても難しいアプリではなく、Pythonで簡易的なHTTPサーバをたてて、特定の文字列を記載したHTMLを表示させるだけにします。以下がソースコードですが、Secret とConfigMap で指定した文字列を表示させるようにしています。

Python
from http.server import BaseHTTPRequestHandler, HTTPServer
import os

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/secret":
            password = os.environ.get("APP_PASSWORD", "NOT_SET")
            message = os.environ.get("APP_MESSAGE", "NOT_SET")
            body = f"APP_PASSWORD={password}, APP_MESSAGE={message}"
            self.send_response(200)
            self.end_headers()
            self.wfile.write(body.encode())
        else:
            self.send_response(404)
            self.end_headers()

HTTPServer(("", 8080), Handler).serve_forever()

このアプリは、/secretにアクセスがあったときに、Secret と ConfigMap に記載したAPP_PASSWORD と APP_MESSAGE を表示し、それ以外だとステータスコード 404 を返すようにしています。

ただ、このアプリを動かすためには実行するコンテナを作る必要があります。そのため、以下のようなDockerfileを作ってイメージをデプロイしておきます。本当はプライベートレジストリとかに登録したほうが良いかもしれませんが、設定が面倒なので・・・・

Dockerfile
FROM python:3.11-slim
COPY app.py /app.py
CMD ["python", "/app.py"]

Dockerfileの書き方については、以下の記事で解説しているのでもし気になる方は確認してみてください。

Dockerfileを用いたSSH接続可能なコンテナの構築

Dockerfileができたら、以下のコマンドでイメージをデプロイします。

Bash
docker build ./ -t secret-demo

Dockerでこのイメージを使う場合はこのままdocker runをすればよいですが、今回使っているクラスタである kind のPodで使うコンテナのイメージとして使うためには、ロードして上げる必要があります。上記のコマンドでデプロイできたら以下のコマンドでkindに読み込ませます。

Bash
kind load docker-image secret-demo:latest

Deploymentの作成

今回のアプリの要でもあるDeploymentを作成します。少しコードが長くなりますが、以下のDeploymentを今回は使います。

YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secret-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secret-demo
  template:
    metadata:
      labels:
        app: secret-demo
    spec:
      containers:
        - name: app
          image: secret-demo:latest
          # ローカルイメージを使うためにこの下の設定が必要
          imagePullPolicy: Never
          env:
            - name: APP_MESSAGE
              valueFrom:
                configMapKeyRef:
                  name: demo-configmap
                  key: APP_MESSAGE
            - name: APP_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: demo-secret
                  key: APP_PASSWORD

          ports:
            - containerPort: 8080

今回大事になるのが、「imagePullPolicy:」の部分です。これを書かないと、リモートからイメージファイルを取得しようとしてしまうため、ここの値は「Never」にしておきます。

実際に動かしてみる

ここまでくれば、あとは作ったYAMLファイルを適用していくだけです。以下のコマンドを続けて実行してください。

Bash
kubectl apply -f secret.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

以下のようにリソースができていればOKです。

Bash
$ kubectl get secret,configmap,deployment,service
NAME                 TYPE     DATA   AGE
secret/demo-secret   Opaque   1      20m

NAME                         DATA   AGE
configmap/demo-configmap     1      20m

NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/secret-demo   3/3     3            3           20m

NAME                      TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/secret-demo-svc   NodePort    10.96.211.31   <none>        8080:30483/TCP   20m

それでは、実際にアクセスしてみましょう。ポートフォワードするために次のコマンドを実行します。

Bash
kubectl port-forward deployment/secret-demo 8080:8080

別のターミナルアプリを開き、以下のcurlコマンドを実行してみましょう。アプリから応答が来るはずです。

Bash
curl http://localhost:8080/secret

以下、実行例です。

Bash
$ curl http://localhost:8080/secret
APP_PASSWORD=password, APP_MESSAGE=Hello from ConfigMap

一応ブラウでも確認すると、次の表示が得られるはずです。

まとめ

今回は実務でも使われるような構成でKubernetesを用いたとりあえず動くアプリを作ってみました。自分自身、Kubernetesを勉強していてもいまいちどうやって使ったらいいのかわからないリソースや機能が多いため、この記事を書きながら改めて各リソースの作り方やYAMLファイルの書き方の振り返りができました。

また、今回の記事については以下の本を参考にしています。実際にいろいろ試しながらKubernetesを学ぶことができるので、もし興味がある方はお手にとって見てください。

ここまで読んでいただきありがとうございました!また次の記事でお会いしましょう。