これは TECHSCORE Advent Calendar 2019の11日目の記事です。
KubernetesにとりあえずWEBサーバを立てたい時、ありませんか?
単にHTTPを受け付けるサーバが欲しいのであれば、適当にApacheやnginxのイメージを起動すれば良いのですが、筆者の場合、エッジケースの調査のために、処理遅延させたり負荷をかけたり、もう一捻り小細工を入れたいケースがよくあります。
自作のプログラムをKubernetesで動かす場合、アプリケーションをDockerイメージで用意するのが普通です。たった一行のプログラムを動かすために、Dockerファイル書いて、イメージをビルドして、リポジトリ用意して、プッシュして…… ものぐさな筆者には耐え難い苦痛です。
そんなわけで、WEBアプリケーションをDockerイメージを構築せずにサクッと起動させる方法をご紹介します。
httpbin編
自分でプログラムを書く前に、代替手段がないか検討してみると良いでしょう。
例えば「指定時間sleepを入れたい」なんて場合はhttpbinが解決してくれます。
httpbinは「A simple HTTP Request & Response Service.」の説明で https://httpbin.org/ でホストされているサービス。
リクエストのインスペクション、Cookie発行、リダイレクト、ランダムデータ生成など、HTTP開発にまつわる様々なツールを提供してくれます。
Pythonで書かれたOSSなので、自前で起動しておくと負荷を掛けたい時にも気兼ねなく呼びだすことができます。
| 1 | $ kubectl create deployment --image kennethreitz/httpbin httpbin | 
ローカルからHTTPでアクセスするだけならkubectl port-fowardが楽で良いです。
以下のコマンドを実行している間、kubectlを実行したマシンの8080ポートにアクセスするとhttpbinが使えます。
| 1 | $ kubectl port-forward $(kubectl get po | grep httpbin | cut -d" " -f1) 8080:80 | 
http://localhost:8080/ にアクセスすると、SwaggerUIの画面が表示されるのでどんなAPIがあるのか見ておくと良いでしょう。
例えば以下のURLでレスポンスを10秒遅延させることができます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ curl http://localhost:8080/delay/10 {   "args": {},   "data": "",   "files": {},   "form": {},   "headers": {     "Accept": "*/*",     "Host": "localhost:8080",     "User-Agent": "curl/7.29.0"   },   "origin": "127.0.0.1",   "url": "http://localhost:8080/delay/10" } | 
記事のタイトルにあるように「使い捨てWEBアプリ」なので不要になったら削除するのを忘れずに。
| 1 | kubectl delete deployment httpbin | 
PHP編
httpbinは便利ですが、それだけですべての要望に対応することはできません。
サクッと書いたコードを動かすのが手っ取り早いことも多いです。
そんな時のため、KubernetesのマニフェストだけでPHPアプリケーションを動作させてみます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | $ cat << EOS | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata:   name: webapp-php spec:   selector:     matchLabels:       app: webapp-php   template:     metadata:       labels:         app: webapp-php       annotations:         updated: "$(date +%s)"     spec:       containers:         - name: webapp-php           image: php:7.3-apache           volumeMounts:             - name: config-volume               mountPath: /var/www/html       volumes:         - name: config-volume           configMap:             name: webapp-php --- kind: ConfigMap apiVersion: v1 metadata:   name: webapp-php data:   index.php: |     <?php phpinfo(); ?> EOS | 
httpbinに比べてかなり複雑になりましたが、1コマンドで実行できるので我慢しています。
annotationに日付を埋め込んでいるのは、2回以降にapplyした際に新しいコードが反映されるようにするためです。
port-forwardを設定しhttp://localhost:8080 にアクセスすると、おなじみのphpinfoが表示されます。
| 1 | kubectl port-forward $(kubectl get po | grep webapp-php | cut -d" " -f1) 8080:80 | 
使い終わって削除する際は kubectl apply -f - を kubectl delete -f - に変えて実行してください。
Spring Boot編
今度はSpringBootを実行してみましょう。
Spring Boot CLI を利用します。
単一のgroovyファイルを書くだけで動作させることが出来るため、PHPと同じように、Kubernetesのマニフェストだけで起動することができるのです。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | $ cat << EOS | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata:   name: webapp spec:   replicas: 1   selector:     matchLabels:       app: webapp-spring   template:     metadata:       labels:         app: webapp-spring       annotations:         updated: "$(date +%s)"     spec:       containers:         - name: webapp-spring           image: rocko/spring-boot-cli-docker           command: ["/root/.sdkman/candidates/springboot/current/bin/spring"]           args: ["run", "app.groovy"]           workingDir: /app           volumeMounts:             - name: config-volume               mountPath: /app           readinessProbe:             httpGet:               path: /actuator/health               port: 8080       volumes:         - name: config-volume           configMap:             name: webapp-spring --- kind: ConfigMap apiVersion: v1 metadata:   name: webapp-spring data:   app.groovy: |     @Grab("spring-boot-starter-actuator")     @RestController     class WebApplication {         @RequestMapping("/")         String home() {             "Hello World!"         }     } EOS | 
起動ごとに依存ライブラリをダウンロードしてくるので起動がやたら遅いですが、Grabアノテーションでコード中に依存関係を記述できるため、1ファイルで大概の処理をこなせてしまいます。
筆者は普段JavaやKotlinでSpringBootアプリケーションを書いているためこちらをよく利用します。
まとめ
要は、スクリプト言語であれば、必要なファイルをConfigMapに突っ込んでマウントすればカスタムイメージ作らなくてもアプリ起動できるよね、という話でした。
NodeJS+ExpressやRuby+Sinatraあたりが使えると快適な気がしますが、依存ライブラリのインストールの手間をうまく解消する手段が思いつかず、
Apacheだけでそれなりに動作してくれるPHPと、ソースコード中に依存ライブラリを記述してしまえるSpringBootCLIを紹介しました。
P.S. Kubernetesのとある挙動を調査して記事にしようとしていたものの、挫折した末に副産物を記事にしたのは内緒です。

 
						