とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
読者登録と各種SNSのフォローもよろしくお願いいたします。

テスト用のアプリのコンテナ化を試した話

最近、KubernetesとかDockerやPodmanを中心としたコンテナーをよく利用しています。 これらでコンテナー化していないアプリケーションをコンテナ化するために必要なことを調べるため、またはこれらのプラットフォームでアプリケーションの実行をデモするために、適当なアプリを作ることにしました。

まずは比較的触ったことがあるPHPベースで試してみようと思います。

試そうと思ったこと

つぎの3つの動作確認が目的です。 アプリ主体じゃないので、アプリ自身は単機能なものを用意する感じにしたいと思います。

  • Kubernetesでアプリケーションが動作するか

    • シングルノードでの動作
  • 将来的に確認したいこと

    • マルチノードでの動作
  • Knative Servingで同じアプリケーションを実行して動作する

    • 必要なときだけフロントエンドにアクセスできる

今回は試したいことのうち、一つ目を試してみたいと思います。

初期の開発

初期の開発は普段使っているmacOS環境で行いました。PHPとMySQLはインストーラーを使ってインストールしたり、Homebrewからインストールしたりでも良かったのですが、今回はMAMPを使いました。

www.mamp.info

MAMPのインストールは結局Homebrewを使って行いました。 brew install mampだけで終了です。

インストール後サービスを起動するだけで、WebサーバーとPHP、MySQLサーバーの準備は完了です。

あとはコードを打ち込めば動作します。WebサーバーはApache httpdとNGINXから選べます。PHPは現在のバージョンは7.4と8.0から選べます。ポートはWebサーバーは8888、MySQLは8889が設定されていますが、好みの番号に変更可能です。

MySQL Serverは5.7.34で固定みたいです(バージョンは変更不可)。

Dockerの利用については、macOS版のRancher Desktopを利用しました。まずDockerが利用できる点、次にk3sベースで軽量なKubernetesを簡単にデプロイできる点、デプロイできるK8sバージョンに幅がある点などからRancher Desktopを選択しています。

rancherdesktop.io

データベースはコンテナ化するか

コンテナ化されていないアプリケーションをコンテナ化するのにあたり、データベースサーバーはコンテナ化するかするか否かという話はよく出てきます。意見が分かれるところだと思います。

今回Ranche Desktopを利用したためLocal-pathベースの永続ストレージの利用はできるのですが、データベースコンテナーに永続ストレージの割当はちょっと大変なので、今回はコンテナ化しないことにします。

というわけで、初期開発ではつぎような環境を目指します。

初期開発の結果

PHPのコードはつぎのサイトのサンプルを使わせてもらいました。

物理環境での実行

とりあえず、物理環境では問題なく動作しました。スクリーンショットはないのですが、入力ボックスに文字列を入力して「登録する」ボタンを押すと、データベーステーブルの中の値が新しいものに更新されるように動きます。

Dockerコンテナでの実行

物理でアプリケーションの動作を確認したので、つぎにDockerfileを使ってコンテナイメージを作成して、そのイメージでアプリケーションを実行してみます。

ベースイメージはphp:7-apacheを利用しました。Debianベースのイメージで、PHP7とApache Webサーバーがインストール済みのイメージです。一番最後のコマンドで必要なモジュールの導入、有効化が必要でした(その詳細は後述)。 ADDでコード類をドキュメントルートにコピーしています。

FROM docker.io/php:7-apache

ADD index.html /var/www/html/
ADD connect.php /var/www/html/
ADD database2.php /var/www/html/
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli

つぎのように実行してイメージの作成、作成したイメージでコンテナの作成を行います。

% docker image build --compress -t myphp:latest -f Dockerfile .
% docker container run -d -p 8080:80 --name=cont1 -it myphp:latest 

ブラウザーでアクセスすると... こんな感じです。物理のときと同じようにアプリケーションが動きます。

イメージ作成周りでハマったこと

今回はPHPベースのアプリケーションを想定していたので、オフィシャルのphpイメージを利用しました。 試しにDockerで動かしてみたら、アプリが「mysqliモジュールがない」と怒られてアプリが動きませんでした。ページを下まで見れば書かれていますが、必要なモジュールのインストールと有効化が必要だったようです。

そのため、つぎのようなDcokerfileでは正常に動かうことができず、

FROM docker.io/php:7-apache

ADD index.html /var/www/html/
ADD connect.php /var/www/html/
ADD database2.php /var/www/html/

つぎのように修正が必要でした。

FROM docker.io/php:7-apache

ADD index.html /var/www/html/
ADD connect.php /var/www/html/
ADD database2.php /var/www/html/
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli

Kubernetesでアプリケーションを動かす

今度はつぎのような環境のセットアップとアプリケーションが正常に動作することを目指します。

Dockerで動いたので、つぎはKubernetesで確認します。Rancher Desktopの「Kubernetes Settings」を選んで、「Enable Kubernetes」がオンになっていることを確認します。

Kubernetesでアプリケーションを実行する場合は、前の手順で作ったイメージをレジストリーに登録して、そこから参照できるようにする必要があります。今回は未だ開発中のイメージでDocker Hubといったような公開レジストリーに登録はしたくなかったため、ローカル環境上のDockerにRegistryを動かしてイメージ管理することにします。

つぎのように実行してまずRegistryを動かします。

% docker run -d -p 5000:5000 --restart always --name registry registry:2

つぎに、イメージをRegistryに登録します。

% docker tag myphp:latest localhost:5000/myphp:latest 
(イメージにタグをつける)

% docker push localhost:5000/myphp:latest 
(イメージをregistryに登録)

Rancher Desktopでこのレジストリーを使えるようにするため、Insecure Registryを設定します。 設定変更方法はdiscussions/1477 に書かれていますが、まずは以下のように実行してLimaのシェルに入り....

% LIMA_HOME="$HOME/Library/Application Support/rancher-desktop/lima" "/Applications/Rancher Desktop.app/Contents/Resources/resources/darwin/lima/bin/limactl" shell 0
lima-rancher-desktop:~# tail -1 /etc/conf.d/docker
DOCKER_OPTS="--insecure-registry=insecure.home:80"

Insecureオプションを設定します。設定後はexitでシェルから抜けていったんRancher Desktopの終了、再開を実行します。これでLima VMが再起動されて、設定が反映されます。

lima-rancher-desktop:~$ sudo vi /etc/conf.d/docker
...
DOCKER_OPTS="--insecure-registry=127.0.0.1:5000"

再起動後にdocker infoを実行してみます。Insecure Registries行に次のように追記されていれば成功です。

 % docker info  
 ...
 Experimental: false
 Insecure Registries:
  127.0.0.1:5000    #追記された行
  127.0.0.0/8
...

つぎのようなマニフェストファイルを作成します。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app1
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:
      - name: app1
        image: 127.0.0.1:5000/myphp:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: app1-lb
  labels:
    app: app1
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: app1

マニフェストファイルを使って、Podを作成します。

% kubectl apply -f deploy.yaml
% kubectl get -f deploy.yaml
NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/app1   1/1     1            1           5s

NAME              TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
service/app1-lb   LoadBalancer   10.43.6.45   <pending>     80:30198/TCP   5s

はじめての方向けに少しだけ補足しておくと、Kubernetesではクラスターの外部からコンテナにアクセスするために、Kubernetes Serviceを利用します。サービスタイプにはいくつかあるのですが、今回はLoadBalancerタイプを使っています。

パブリッククラウド上のKubernetesであれば、LoadBalancerタイプのサービスを作成するとグローバルのIPアドレスが発行されて、そのIPアドレスを使ってコンテナにアクセスできます。

LBサービスとして「traefik」はうごいているものの、ローカルクラスターではEXTERNAL-IPはpendingのまま発行されませんので、とりあえずNodePortでアクセスしてください。

アドレスはkubectl get node -o wideの実行で出力されたINTERNAL-IPとkubectl get svc app1-lbの実行で出力されたポートを指定します。下記の例では「http://192.168.0.140:30198」です。

% kubectl get svc app1-lb
NAME      TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
app1-lb   LoadBalancer   10.43.6.45   <pending>     80:30198/TCP   5m45s

% kubectl get node -o wide
NAME                   STATUS   ROLES                  AGE    VERSION        INTERNAL-IP     EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION   CONTAINER-RUNTIME
lima-rancher-desktop   Ready    control-plane,master   3d5h   v1.24.3+k3s1   192.168.0.140   <none>        Alpine Linux v3.15   5.15.40-0-virt   docker://20.10.16

Dockerで動かした時と同じ動きをするアプリケーションにアクセスできるはずです。

データ表示のリンクをクリックすると...

入力ボックスに文字列を入れて「登録する」ボタンを押すと...

というわけで、なんとかWebフロントエンド側のコンテナ化は無事できることが確認できました。 このあとはKnative Servingを試したいと思います。以前試したことはあるのでハマることはなさそうですが、はて?...という感じです。

tech.virtualtech.jp