WindowsのブラウザーからWSL Ubuntuに作成したKubernetes（minikube）クラスター内のPod（Webサーバー）にアクセスする（その２）

2025/01/28：WSL 2.3.26.0、および Ubuntu 24.04.1 LTS で本稿手順が動作する事を確認

## その２は自作コンテナの第一歩！
ベースの環境は「[その１](https://qiita.com/SHIRANO/items/04ceefbe3b296b5206e1)」の記事内容が前提になっています。
その１で構成したコンテナオーケストレーション（minikube）環境を使って、今度は**自分で作成した Pythonアプリケーションをコンテナに組み込んで動かしてみます**。その１と同じく最小の手順になっており YAML も使用しません。
（本来 Kubernetes の操作で YAML の知識は必須ですが、できるだけ各コンポーネントの関係にフォーカスするため本稿では YAMLを使用しない手順としています）

## Python 関連のアップデートなど
「[その１](https://qiita.com/SHIRANO/items/04ceefbe3b296b5206e1)」で設定した WSL Ubuntu は何もしなくても Python（Python3）が使えますが念のためアップデートを行います。
~~~
$ sudo apt update && sudo apt upgrade
$ sudo apt upgrade python3
~~~

Python の標準ライブラリに含まれていない追加のパッケージをインストールして管理するために pip をインストールします。
~~~
$ sudo apt install python3-pip 
~~~
PythonでWebアプリを実行させるために Flaskライブラリをインストールします。
~~~
$ pip3 install flask

# 環境が外部で管理されている旨のエラーになる場合は、以下コマンドで Flask をインストール
# sudo apt install python3-flask
~~~

## Python ソースコードの作成、編集
Ubuntu でファイル（ helloweb.py ）を作成します。
~~~
$ echo aaa > helloweb.py
~~~
この後 Windowsのファイルエクスプローラーから helloweb.py をメモ帳などで編集、保存してください。
もちろん "aaa" は消してください。このような事をする理由は「[その１](https://qiita.com/SHIRANO/items/04ceefbe3b296b5206e1)」の余談に！

よく見かける固定の文字列を返すだけのシンプルな Python の Web（Flask）アプリです。
~~~
# helloweb.py

from flask import Flask

#Flaskオブジェクトの生成
app = Flask(__name__)

#ブラウザで仮想ディレクトリルートが指定された場合の動作
@app.route('/')
def hello():
    return 'hello world\n'

#Webサーバを起動
if __name__ == '__main__':
    app.run(debug='true',host='0.0.0.0',port='8888')
~~~

Webアプリを Ubuntu上で直接実行します。
~~~
$ python3 helloweb.py
~~~

Windows上のブラウザーからアクセス
http://localhost:8888/

ブラウザーに hello world の文字列が表示されたら正常に動作しています。Pythonアプリは Ctrl + c で終了できます。

## コンテナのビルドと実行
動作確認済みの Pythonアプリを組み込んだコンテナをビルドします。
以下内容の Dockerファイル（ helloWebDockerfile.txt ）を用意します。
※もし実施した際に FROM のベースコンテナが見つからないような場合は、その時々で使える Pythonの公開イメージ名に置き換えてください。
~~~
# helloWebDockerfile.txt

# 元になる Python コンテナ
FROM python:3.11-slim-bookworm

# ワークディレクトリを作成
workdir /app

# コンテナホスト側の helloweb.py をコンテナ側にコピー
copy ./helloweb.py .

run pip install flask

CMD python helloweb.py
~~~
コンテナをビルドします。
~~~
$ docker build -t helloweb:001 -f helloWebDockerfile.txt . 
~~~
まずはDocker環境で動作しているか確認します。
~~~
$ docker run --rm -d -p 8888:8888 --name helloweb helloweb:001

$ docker ps
~~~
もしコンテナ状態表示で起動が確認できない場合は以下の -it で問題を確認。
~~~
$ docker run --rm -it -p 8888:8888 --name helloweb helloweb:001
~~~
curlコマンドで hello world の文字列が表示されれば正常に動作しています。
~~~
$ curl localhost:8888
hello world
~~~
Dockerコンテナを停止します。
~~~
$ docker stop helloweb
~~~

## minikube クラスターでのアプリ実行
では minikube クラスター内で実行してみましょう。
まずクラスターをスタートしておきます。
~~~
$ minikube start
~~~

環境変数を変更して dockerコマンドの出力先を minikubeクラスターに設定しておきます。
~~~
$ eval $(minikube -p minikube docker-env)
~~~
※本稿執筆時には minikube image load の問題があるためコンテナイメージの作成先を環境変数で変更する手順とします。

元のDockerホスト用の環境に戻すときは次のコマンドで。
eval $(minikube -p minikube docker-env -u)
環境の確認は docker context list で。

コンテナをビルドします。先ほどのビルドと同じコマンド内容ですが、環境変数を変更しているのでこのコンテナイメージは minikube クラスター内に作成されます。
~~~
$ docker build -t helloweb:001 -f helloWebDockerfile.txt .
~~~

クラスター内にアプリの Pod をデプロイします。
~~~
$ kubectl create deployment --image helloweb:001 helloweb
~~~
Pod の状態が Running になっているのを確認します。
~~~
$ kubectl get pods
NAME                        READY   STATUS    RESTARTS        AGE
helloweb-5fdb645d4d-r24x8   1/1     Running   0               7s
~~~
Pod に繋げるためのサービスを定義、確認します。
~~~
$ kubectl expose deployment helloweb --port 8888 --type=NodePort
$ kubectl get service
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
helloweb     NodePort    10.109.48.35     <none>        8888:31062/TCP   39s
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP          1h
~~~

このサービスを更に外部からアクセス可能にします。
~~~
$ minikube service helloweb --url
http://127.0.0.1:37843
❗  Because you are using a Docker driver on linux, the terminal needs to be open to run it.
~~~
表示された URL に Windows上のブラウザーからアクセスします。
![hello.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475210/57795d3a-8c7c-7e09-9004-71ba153fdab5.jpeg)

アプリケーションを組み込んだコンテナがクラスターで正常に動作している事が確認できました。

## 最後に
各コンポーネントの関係を理解した上で本稿の内容（その１と併せて）の実施や、更に kubectl で色々なコマンドを試す事によってコンテナオーケストレーションの基本的な知識が得られると思います。
本稿作成前は、WSL と VSCode の連携や Python の 仮想プロジェクト管理（venv） など、開発環境や手順にも触れようとしていたのですが煩雑になるのでカットしました。**VSCode などの開発環境は効率や品質を高めるために必ず利用すべきです。本稿中で触れたような改行コードのケアも不要になります**。VSCode も出たばかりの頃は便利なソースコードエディターくらいの感覚でしたが、現在は拡張機能の充実により統合開発環境として広く利用されています。
WSL と VSCode の連携については Microsoft が公開しているこのようなガイド（ https://learn.microsoft.com/ja-jp/windows/python/web-frameworks ）で簡単に入門できると思います。
もともとは Kubernetes の経験が無いメンバーのために、Windowsローカルでコンテナアプリケーションの開発、単体テストをしてもらうためのハンズオン・オリエンテーション資料を探していたのですが、検索しても適当な内容が見つからなかったので本稿内容を作成しました。
コンテナ、Kubernetes に触れるタイミングがなかった方々の第一歩として、コンポーネントの関係や大まかな流れを理解するために本稿がその一助となれば幸いです。

## おまけ：コンテナイメージのサイズを小さくしている有識者の記事を参考にしてマルチステージビルドで Distrolessコンテナ、および gunicornも使用するようDockerfileを変更

大変スマートな方法を考えている方の Dockerfile を勝手に参考にしました。m(_ _)m
このベースイメージであれば運用レベルでも大丈夫かも。。

~~~
# helloWebDockerfile2.txt

FROM python:3.11-slim-bookworm as builder
WORKDIR /app
copy ./helloweb.py .
ENV PYTHONUSERBASE=/app/__pypackages__
RUN pip install --user Flask gunicorn

FROM gcr.io/distroless/python3-debian12
WORKDIR /app
COPY --from=builder /app .
ENV PYTHONUSERBASE=/app/__pypackages__

ENTRYPOINT ["python", "-uB", "-m", "gunicorn", "helloweb:app", "-b", ":8888"]
~~~
結果：**153M → 58.5M** Pythonコンテナの軽量化を確認
~~~
$ docker images
REPOSITORY                    TAG       IMAGE ID       CREATED          SIZE
helloweb                      001       xxxxxxxxxxxx   58 minutes ago   153MB
helloweb                      002       xxxxxxxxxxxx   24 minutes ago   58.5MB
~~~