nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

MattermostをOpenShift Enterprise 3.1.1上でインスタントアプリケーション化する

MattermostはSlackのようなWebベースのチームチャットツールです。GolangとReactで書かれています。

以下の一行でセットアップができるようになっています。Mattermost 1.4.0, MySQL 5.6でMySQLがPersistentVolume使います。コードはGitHubにあります。

oc new-app -f https://raw.githubusercontent.com/nekop/openshift-sandbox/master/apps/mattermost/mattermost.yaml

アクセスすると以下のように初期画面がでます。

f:id:nekop:20160205145040p:plain:w300

このアプリケーションの定義ファイル(マニフェスト、とも呼ばれます)は200行程度です。route定義は含めていないのですが、oc expose svc mattermostすればrouteができて環境に応じたURLが割り当たるはずなので、マニフェストには含めなくてもいいかなぁ、と思っています。

Mattermostはjson設定ファイルにデータベースの接続情報を持つので、DockerのENTRYPOINTに指定する起動シェルスクリプトで、環境変数からデータベースの接続情報をこのファイルに反映させる、という処理をする必要があります。

Mattermostはデータベースの接続情報の他にも設定項目はいっぱいあるのですが、デプロイ時に動的に変わるものはとりあえずデータベースの接続情報だけのような感じだったので、あとの設定はイメージに焼いてしまっていいかなーと思って環境変数化していません。というか50個くらいある設定項目を全部環境変数にしてしまうと環境変数ヘルと呼ばれるような状態になりますし、json書き換えるのも収集つかなくなると思います。他の設定項目を書き換えたかったらおとなしくイメージを再ビルドすることにします。ビルドはOpenShiftに投げるだけですしね。

これからのDocker対応アプリケーションでどのように設定を受け取るか、というのは結構重要なポイントかもしれません。とりあえずアプリケーションは設定を環境変数を受けとれるようにする、という機能は入れておいて欲しいですね。yamlやらjsonやらxmlの設定ファイルに環境変数を反映させるようシェルスクリプトで書き換えるのはかなりきびしい印象があります。また、外部から設定情報をフェッチする、という方法も考えたんですがDockerイメージの自己完結性が失われるのでかなりダメな感じがします。外部に置いてある設定ファイルを編集したら動いていたDockerイメージが動かなくなった、というのはちょっと勘弁してもらいたいシナリオです。DockerイメージはDocker環境で完結する情報だけで動作すべきでしょう。

今回Mattermostのインスタントアプリケーションを作る手順は以下のような感じでした。

  1. Dockerfileを書いてgit push
  2. ベース作成のために oc new-app
    • oc new-app <git clone url>
  3. oc exportしてベースにするyaml生成
    • oc export dc,is,bc,svc -o yaml --as-template=mattermost > mattermost.yaml
  4. import時に省略できる設定も明示的にexportされてしまうので(ポータビリティ考えるとまぁそうだよね感)、そのへんは消す。dancer-exampleが結構最小に近いようなので参考にした。
    • oc get template dancer-example -n openshift -o yaml
  5. アプリケーション名をAPPLICATION_NAMEパラメータにして作成時にカスタマイズできるようにする
    • mattermost${APPLICATION_NAME}に置換して最終部パラメータにAPPLICATION_NAMEを定義
  6. MySQLを追加する。mysql-persistentのテンプレートからobjectsとparametersを抜き出してそのままmattermost.yamlにつなげる
    • oc get template mysql-persistent -n openshift -o yaml
  7. MySQLのパラメータをmattermostのDeploymentConfigにも渡すようにする
  8. mysqlのDeploymentConfigのenvをそのままコピー
  9. 設定ファイルにデータベース接続情報を反映させて起動するmattermost-launch.shを作成してDockerfileENTRYPOINTにする

いくつか参考になるテンプレート、シェルスクリプトやDockerfileを把握しておけば1時間程度でできるかな、という雰囲気です。

WildFly 10.0.0.Final リリースしました

Java EE 7アプリケーションサーバであるWildFly 10.0.0.Finalがリリースされました。WildFlyのサイトからダウンロードできます。

f:id:nekop:20160201100503p:plain

リリースノートはこちら。起動時間は僕の手元で1.6秒でした。

09:53:17,610 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 10.0.0.Final (WildFly Core 2.0.10.Final) started in 1606ms - Started 267 of 553 services (371 services are lazy, passive or on-demand)

ハイライトとしては以下のような感じです。

ActiveMQ ArtemisはHornetQがActiveMQに合流してできた新しいメッセージングミドルウェアです。このような合流はJavaミドルウェアだと以前にXML WebServicesでCeltixとXFireが合流してApache CXFができた、というのがありますね。

Java EE 7の商用サポート版であるJBoss EAP 7はこのWildFly 10をベースにエンタープライズ版になるためのエンジニアリングがされてリリースされる予定です。JBoss EAP 7のリリース予定日はまだ公式には未発表ですが、今までのリリースの傾向からするとたぶん今年の前半には出るんじゃないかなーという感じです(非公式見解です、内部リリース予定情報は見ないで書いています)。Red Hat Summitも6月にありますし。

OpenShiftのsource-to-image (s2i)をすごく簡単に説明するよ

この投稿はOpen PaaS Advent Calendar 2015の16日目の記事です。

OpenShiftにはソースコードからDockerイメージをビルドするs2iという仕組みがあります。ドキュメントを見ると煩雑な印象を受けるかもしれませんが、実はとっても簡単です。

単体のs2i

s2iはgithubなどのソースコードのURL、s2i builder imageを受け取って、Dockerイメージを作成するソフトウェアです。以下実行例ですがそのまんますぎて説明のしようがないくらいです。openshift/ruby-20-centos7というs2i builder imageを使ってtest-ruby-appというDockerイメージをビルドしています。

s2i build git://github.com/pmorie/simple-ruby openshift/ruby-20-centos7 test-ruby-app
docker run --rm -i -p :8080 -t test-ruby-app

s2i builder image

s2i builder imageはs2iスクリプトが入っていて、ラベルが付いているだけのDockerイメージです。ぶっちゃけ以下のラベル付けるだけでs2i builder imageの要件は満たせます。

LABEL io.openshift.s2i.scripts-url=image:///usr/libexec/s2i

s2iはこのs2i builder imageのラベルからs2iスクリプトの場所を読み込み、実行してその結果のイメージを出力のDockerイメージとするだけです。s2iをマニュアルでやるとするとdocker runしてs2iスクリプトをキックしてdocker commit/tagする、というイメージです。

OpenShiftでのsti-builderイメージ

OpenShiftではopenshift-sti-builderとかorigin-sti-builderなどのイメージが提供されていますが、これらはs2i builder imageではありません。ややこしいですね、すみません。

これは実際には最初に説明した「単体のs2i」をOpenShift環境上で行うDockerイメージ、つまりOpenShift版s2iです。openshift-sti-buildコマンドというs2iを実行するコマンドが入っていて、OpenShift上ではs2iはこのイメージで実行しています。

stiだったりs2iだったり

同じです。最初stiだったんですけど途中でs2iにしようぜってことになったんですが全部修正されているわけではないので表記が混ざっています。熟練したOpenShift使いには同じに見えるはずです。

まとめ

  • s2iはすごくシンプル
  • s2i builder imageもスクリプト入ってるだけの単なるDocker image
  • OpenShiftのsti-builderは単なるs2i

OpenShift専用ホスティングサービス正式開始しました

OpenShift Dedicatedというお客様専用のOpenShiftをホスティング提供するサービスが正式開始しました。

f:id:nekop:20151216104645j:plain

OpenShift DedicatedはRed Hatがセットアップしてメンテナンスも提供するので、お客様は利用するだけ、というモデルになっています。AWS上全リージョンで提供されるので東京リージョンでも利用できます。

BASEパッケージではアプリケーションノードとして4ノード、24時間サポートつき、という内容です。アプリケーションノードはm4.xlargeで、4vCPU 16GB memoryです。master, etcd, router, docker registryは5ノードHAで構成しています。

興味がある方はお問い合わせフォームか、電話であれば0120-266-086へどうぞ。僕に直接連絡してくれてもたぶん大丈夫です。

OpenShift Origin v1.1 開発Vagrant環境を構築する

この投稿はOpen PaaS Advent Calendar 2015の14日目の記事です。

以前OpenShift Origin v3の最新版を試す - Vagrantで構築編OpenShift Origin v3の最新版を試す - 利用編という2つの記事を書きましたが、7月時点のものでありバージョンはv1.0系でした。現時点ではv1.1系なので、内容をアップデートしておきます。

このエントリはOpenShiftをソースコードからビルドしてStandaloneモードで動作させる方法です。開発やデバッグなどの目的で動作させる方法ですので、通常の運用する形態のものを動作させたい場合はインストーラーでインストールするopenshift-ansibleとVagrantでOpenShift Origin v1.1のAll-in-one環境を構築するのほうを参照してください。

Fedora 23でのVagrant libvirtのセットアップまでのステップはVagrantで構築編から変更はありませんが、古いfedora_instイメージを持っている場合は一度削除して更新したほうが良いです。現時点でのイメージの最終更新日時は12/10になっています。

vagrant box remove fedora_inst

v1.1のコンパイルには前回記述した2GBのメモリでは足りなくなっています。4GBを割り当てましょう。vagrant sshすると、以前はホームディレクトリが/data/src/github.com/openshift/originになっていましたが、現在は/home/vagrantになっています。今回は前回は省略したルーターのセットアップ手順も入れておきました。

git clone https://github.com/openshift/origin/
cd origin
cat <<EOF >.vagrant-openshift.json 
{
    "cpus": "4",
    "memory": "4096"
}
EOF
vagrant up
vagrant ssh
cd /data/src/github.com/openshift/origin/
make clean build # hack/build-go.sh took 258 seconds on my laptop
sudo systemctl start openshift
cat ./examples/image-streams/image-streams-centos7.json | oc create -n openshift -f -
echo '{"kind":"ServiceAccount","apiVersion":"v1","metadata":{"name":"registry"}}' | oc create -f -
echo '{"kind":"ServiceAccount","apiVersion":"v1","metadata":{"name":"router"}}' | oc create -f -
(oc get scc privileged -o yaml -n default; echo "- system:serviceaccount:default:registry"; echo "- system:serviceaccount:default:router") | oc replace -f -
sudo `which oadm` registry --config=/openshift.local.config/master/admin.kubeconfig --credentials=/openshift.local.config/master/openshift-registry.kubeconfig --service-account=registry
sudo `which oadm` router   --config=/openshift.local.config/master/admin.kubeconfig --credentials=/openshift.local.config/master/openshift-router.kubeconfig   --service-account=router

これで一通り動作するOpenShiftができあがります。

OpenShiftでアプリケーションプロセスを殺してみる

この投稿はOpen PaaS Advent Calendar 2015の13日目の記事です。

OpenShift / Kubernetesではプロセスやコンテナのモニタリングが行われているので、煮たり焼いたり殴ったり殺したりしてもすぐ復旧するらしいのでやってみましょう。

材料はhello-sinatraさんです。

$ oc new-app https://github.com/nekop/hello-sinatra

とりあえずpsすると、アプリケーションであるrubyのプロセスが見えます。DockerのENTRYPOINTやで起動されるものはpid 1で起動します。これはコンテナプライマリプロセスと呼ばれます。

$ oc exec hello-sinatra-1-wznjc -- ps -eaf
UID         PID   PPID  C STIME TTY          TIME CMD
1000030+      1      0  0 05:51 ?        00:00:00 ruby /opt/app-root/src/bundle/ruby/bin/rackup -E production -P /tmp/rack.pid --host 0.0.0.0 --port 8080
1000030+     75      0  0 06:17 ?        00:00:00 ps -eaf

まずはシンプルにkillしてみましょう。SIGTERMです。

$ oc exec hello-sinatra-1-wznjc -- kill 1
$ oc exec hello-sinatra-1-wznjc -- ps -eaf
error: error executing remote command: Error executing command in container: container not found ("hello-sinatra")
$ oc exec hello-sinatra-1-wznjc -- ps -eaf
UID         PID   PPID  C STIME TTY          TIME CMD
1000030+      1      0 38 06:18 ?        00:00:00 ruby /opt/app-root/src/bundle/ruby/bin/rackup -E production -P /tmp/rack.pid --host 0.0.0.0 --port 8080
1000030+     25      0  0 06:18 ?        00:00:00 ps -eaf
$ curl http://hello-sinatra-test.apps.192.168.100.100.xip.io/
hello

一瞬Pod内のコンテナが居なくなっていますが、すぐ再起動されているようです。Startup timeが変化しました。Podは変化していません。SIGKILLでもまぁ同じ感じになります。

サスペンドしてみましょう。Ctrl-zとかで送られるSIGTSTPです。

$ oc exec hello-sinatra-1-wznjc -- kill -TSTP 1
$ oc exec hello-sinatra-1-wznjc -- ps -eaf
UID         PID   PPID  C STIME TTY          TIME CMD
1000030+      1      0 38 06:18 ?        00:00:00 ruby /opt/app-root/src/bundle/ruby/bin/rackup -E production -P /tmp/rack.pid --host 0.0.0.0 --port 8080
1000030+     25      0  0 06:18 ?        00:00:00 ps -eaf
$ curl http://hello-sinatra-test.apps.192.168.100.100.xip.io/
hello

まったく効きませんね。

oc delete podでpodを潰すとPodのIDが新しくなりますが、killと特に違いはありません。

$ oc delete pod $(oc get pod | grep Running | head -n1 | awk '{print $1}')
pod "hello-sinatra-1-d0b4c" deleted
$ oc exec $(oc get pod | grep Running | head -n1 | awk '{print $1}') -- ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000030+      1 19.5  1.2 117492 23764 ?        Ssl  06:38   0:00 ruby /opt/app-root/src/bundle/ruby/bin/rackup -E production -P /tmp/rack.pid --host 0.0.0.0 --port 8080
1000030+     21  0.0  0.0  19764  1168 ?        Rs   06:38   0:00 ps aux

アプリケーションがハングしたような状態はどうなるでしょうか。

実は、ハングのチェックはデフォルトで有効になっていないため、特にそのままでは自動的に検知して再起動したりはしません。Deployment Configのspec.containersの下のコンテナ定義にlivenessProbeを加えることでモニタリングを有効化できます。

    spec:
      containers:
(省略)
        livenessProbe:
          httpGet:
            path: /
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 15
          timeoutSeconds: 12

アプリケーションを変更して100秒スリープするというよく寝る子にして再度ビルドとデプロイを行います。

$ oc delete pod $(oc get pod | grep Running | head -n1 | awk '{print $1}')
$ vi app.rb
$ git commit -am 'sleep 100' && git push
$ oc start-build hello-sinatra

少し放置プレイしておいてoc get podを見ると9分で6回再起動されているので、上の設定と合わせて考えるとlivenessProbeの実行は60秒くらいのインターバルがありそうです。このあたりは後でもう少しつっこんで調べようと思います。

$ oc get pod
NAME                    READY     STATUS      RESTARTS   AGE
hello-sinatra-5-yugu3   1/1       Running     6          9m

OpenShiftのシステムログを見てみると、以下のようなログが出ていました。

[vagrant@ose3-master ~]$ sudo journalctl -n10000 | grep Live | grep -v refuse
Dec 15 01:58:48 ose3-master.example.com origin-node[21405]: I1215 01:58:48.913911   21405 prober.go:105] Liveness probe for "hello-sinatra-5-yugu3_test:hello-sinatra" failed (failure): Get http://10.1.0.26:8080/: read tcp 10.1.0.26:8080: use of closed network connection
Dec 15 02:00:18 ose3-master.example.com origin-node[21405]: I1215 02:00:18.913822   21405 prober.go:105] Liveness probe for "hello-sinatra-5-yugu3_test:hello-sinatra" failed (failure): Get http://10.1.0.26:8080/: read tcp 10.1.0.26:8080: use of closed network connection
Dec 15 02:01:48 ose3-master.example.com origin-node[21405]: I1215 02:01:48.913582   21405 prober.go:105] Liveness probe for "hello-sinatra-5-yugu3_test:hello-sinatra" failed (failure): Get http://10.1.0.26:8080/: read tcp 10.1.0.26:8080: use of closed network connection
Dec 15 02:03:18 ose3-master.example.com origin-node[21405]: I1215 02:03:18.914962   21405 prober.go:105] Liveness probe for "hello-sinatra-5-yugu3_test:hello-sinatra" failed (failure): Get http://10.1.0.26:8080/: read tcp 10.1.0.26:8080: use of closed network connection
Dec 15 02:04:48 ose3-master.example.com origin-node[21405]: I1215 02:04:48.913876   21405 prober.go:105] Liveness probe for "hello-sinatra-5-yugu3_test:hello-sinatra" failed (failure): Get http://10.1.0.26:8080/: read tcp 10.1.0.26:8080: use of closed network connection

今回の実験はここまで。

OpenShiftでCrashLoopしているPodのログを見る

この投稿はOpen PaaS Advent Calendar 2015の10日目の記事です。

OpenShiftでおもむろにnginxをデプロイしてみます。

$ oc new-app nginx # official "nginx" docker image on docker hub

oc get allしてみますが、CrashLoopBackOffという状態になっていて稼働していないようです。RESTARTSが3になっていて増加していってるようです。

$ oc get all
NAME            DOCKER REPO                 TAGS                           UPDATED
nginx           library/nginx               1.7.10,latest,1 + 19 more...   About a minute ago
NAME            TRIGGERS                    LATEST
nginx           ConfigChange, ImageChange   1
CONTROLLER      CONTAINER(S)                IMAGE(S)                                                                                SELECTOR                                              REPLICAS                           AGE
nginx-1         nginx                       library/nginx@sha256:0a8bad8dfc80e38ccdd09c41d4efd2547fa9d6d58d8706431952a2ef312e2034   app=nginx,deployment=nginx-1,deploymentconfig=nginx   1                                  1m
NAME            CLUSTER_IP                  EXTERNAL_IP                                                                             PORT(S)                                               SELECTOR                           AGE
nginx           172.30.126.189              <none>                                                                                  80/TCP,443/TCP                                        app=nginx,deploymentconfig=nginx   2m
NAME            READY                       STATUS                                                                                  RESTARTS                                              AGE
nginx-1-0khqs   0/1                         CrashLoopBackOff                                                                        3                                                     1m

back offというのは意図的に待っている状態で、この場合は再起動のインターバルを待っている状態です。docker pull待ちのDockerPullBackOffというのもあります。

oc logsでログを見てみましょう。

$ oc logs nginx-1-0khqs
Error from server: Internal error occurred: Pod "nginx-1-0khqs" in namespace "test": container "nginx" is in waiting state.

コンテナが現在稼働していないのでログが取れないと言われてしまいました。困りましたね。

このような場合、--previousというオプションを指定することで、前回のログを見ることができます。

$ oc logs --previous nginx-1-0khqs
nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied)
2015/12/15 05:08:11 [warn] 1#1: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2
2015/12/15 05:08:11 [emerg] 1#1: open() "/var/log/nginx/error.log" failed (13: Permission denied)

Permission deniedですね。OpenShiftでPermission deniedが出ている場合は大抵USERが指定されているか、もしくはUSERを指定しておらずrootでの動作を前提としてしまっているイメージです。

OpenShiftではuser IDで権限を制御する部分があるので、USERによるユーザ名指定はデフォルトで禁止されており、動的にユーザIDが割り当たるようになっているため、上記のようなイメージはこのようなパーミッションエラーを引き起こします。詳しい説明はイメージガイドラインのSupport Arbitrary User IDsのところに記述されています。

ちなみにUSER、USER未指定rootの利用を許可する設定もできます。

oc logs --previousKubernetes由来のもので、kubectl logs --previousというコマンドなのですが、どちらもぐぐってもほとんど情報が出てこないという謎の隠しコマンド状態です。なんででしょうね。レアポケモンの紹介でした。

追記: 初出時に「USERの指定がされているためエラーになった」旨で記載していましたが、実際にはnginxのイメージは「USERの指定がされていない、かつ実行時root前提のためエラーになった」というものだったので修正しました。