nekop's blog

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

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前提のためエラーになった」というものだったので修正しました。

openshift-ansibleとVagrantでOpenShift Origin v1.1のAll-in-one環境を構築する

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

openshift-ansibleとVagrantでOpenShift Origin v1.1を構築するでは3台構成のOpenShiftを構築しましたが、ちょっと試すだけなら3台も要らないよね?ってことで1台の構成を作ってみましょう。1台であればメモリももうちょっと割り当てられますし。

手順は前回とほぼ同じなのですが、vagrant upの前にこの環境変数をexportしておきます。

export OPENSHIFT_NUM_NODES=0

0という指定は若干奇妙ですが、masterノードのみセットアップします。masterは実際にはnodeサービスも同居して動作しているので、All-in-one構成として利用可能です。masterのnodeサービスはschedulable=falseになっているので、trueに変更したまま運用します。

vagrant up --provider=libvirt --no-provision
vagrant provision
vagrant ssh master
sudo oadm manage-node ose3-master.example.com --schedulable=true
sudo oadm registry --selector=kubernetes.io/hostname=ose3-master.example.com --credentials=/etc/origin/master/openshift-registry.kubeconfig --service-account=registry
sudo oadm router   --selector=kubernetes.io/hostname=ose3-master.example.com --credentials=/etc/origin/master/openshift-router.kubeconfig   --service-account=router
sudo vi /etc/origin/master/master-config.yaml
# Change the subdomain value as follows:
#   subdomain:  "apps.192.168.100.100.xip.io"
sudo systemctl restart origin-master

これで1台構成のOpenShiftができあがりますので、そのまま遊べます。

f:id:nekop:20151215131341p:plain

openshift-ansibleとVagrantでOpenShift Origin v1.1を構築する

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

OpenShiftにはopenshift-ansibleというインストーラーのプロジェクトがあり、Vagrantと組み合わせて簡単にOpenShiftの実行環境が構築できるようになっています。VagrantでのセットアップについてはREADME_vagrant.mdに記述されています。

デフォルトの設定では以下の3台構成のOpenShiftができあがります。

192.168.100.100 ose3-master.example.com
192.168.100.200 ose3-node1.example.com
192.168.100.201 ose3-node2.example.com

これを自分の4 CPU cores, 12GBメモリのラップトップで構築してみましょう。Fedora 23でのVagrant libvirtのセットアップまでのステップはVagrantで構築編を参照してください。

openshift-ansibleではansibleとvagrantvagrant-hostmanagerプラグインが必要ですので、それらもインストールします。

sudo dnf install ansible -y
vagrant plugin install vagrant-hostmanager

CPUやメモリの設定はVagrantfileにハードコードされていますが、多少控え目に定義してあって実際に利用するとつらい感じになりやすいので、cloneした後に若干増やしておきます。

-    libvirt.cpus = 2
-    libvirt.memory = 1024
+    libvirt.cpus = 4
+    libvirt.memory = 2048

vagrant upのときに--no-provisionを忘れずにつけましょう。忘れると1ノード時点でprovisionがはじまってしまうのでやり直すはめになります。

vagrant provisionを実行するとAnsibleによるインストールがはじまるので、結構時間がかかります。

git clone https://github.com/openshift/openshift-ansible/
cd openshift-ansible
vi Vagrantfile # modify cpus and memory if needed
vagrant up --provider=libvirt --no-provision
vagrant provision

マルチノード構成なので、masterノードで作業します。

vagrant ssh master

oc get nodeを実行すると3台構成となっているのがわかります。

oc get node
NAME                      LABELS                                           STATUS                     AGE
ose3-master.example.com   kubernetes.io/hostname=ose3-master.example.com   Ready,SchedulingDisabled   26m
ose3-node1.example.com    kubernetes.io/hostname=ose3-node1.example.com    Ready                      26m
ose3-node2.example.com    kubernetes.io/hostname=ose3-node2.example.com    Ready                      26m

docker registryとrouterを作成しましょう。3台くらいの構成ではmasterにregistryとrouterを稼働させるのが一般的です。デフォルトでschedulable=falseとなっているので、このときだけtrueに変更します。

sudo oadm manage-node ose3-master.example.com --schedulable=true
sudo oadm registry --selector=kubernetes.io/hostname=ose3-master.example.com --credentials=/etc/origin/master/openshift-registry.kubeconfig --service-account=registry
sudo oadm router   --selector=kubernetes.io/hostname=ose3-master.example.com --credentials=/etc/origin/master/openshift-router.kubeconfig   --service-account=router
sleep 120 # docker pullがどのくらいの時間で終わるのかは環境依存なので、目視確認したりsleep調整したりしてください
sudo oadm manage-node ose3-master.example.com --schedulable=false

master-config.xmlのsubdomainにxip.ioを記述しておくとアプリケーションもそのままアクセスできるようになるので設定しておきましょう。

sudo vi /etc/origin/master/master-config.yaml
# Change the subdomain value as follows:
#   subdomain:  "apps.192.168.100.100.xip.io"
sudo systemctl restart origin-master

これでmaster上での作業は終わりです。あとはVagrantホストからリモートで利用できます。

vagrant-hostmanagerプラグインによって、vagrantホストの/etc/hostsエントリも追加されるので、ブラウザでWebコンソールにアクセス可能になっています。オレオレTLSなので警告が出ます。

https://ose3-master.example.com:8443/console/

認証はAllowAllが設定されているので、適当なユーザ名と空ではないパスワードで認証が通るようになっています。

ブラウザアクセスだけではなく、ocコマンドももちろん利用できます。masterからocコマンドを取ってきて実行すれば良いです。

vagrant ssh master -- cat /usr/bin/oc > oc
chmod +x ./oc
./oc version
./oc login ose3-master.example.com

さて、プロジェクトを作成してアプリケーションを放り込んでみましょう。

./oc login ose3-master.example.com --username=nekop
./oc new-project test
./oc new-app https://github.com/nekop/hello-sinatra
./oc expose service hello-sinatra

oc get allで状態を確認すると以下のように見えます。

./oc get all
NAME                    TYPE                                             FROM      LATEST
hello-sinatra           Source                                           Git       1
NAME                    TYPE                                             FROM      STATUS    STARTED         DURATION
hello-sinatra-1         Source                                           Git       Running   2 minutes ago   2m48s
NAME                    DOCKER REPO                                      TAGS      UPDATED
hello-sinatra           172.30.90.98:5000/test/hello-sinatra                       
NAME                    TRIGGERS                                         LATEST
hello-sinatra           ConfigChange, ImageChange                        0
NAME                    HOST/PORT                                        PATH          SERVICE         LABELS                                             INSECURE POLICY   TLS TERMINATION
hello-sinatra           hello-sinatra-test.apps.192.168.100.100.xip.io                 hello-sinatra   app=hello-sinatra                                                    
NAME                    CLUSTER_IP                                       EXTERNAL_IP   PORT(S)         SELECTOR                                           AGE
hello-sinatra           172.30.44.105                                    <none>        8080/TCP        app=hello-sinatra,deploymentconfig=hello-sinatra   2m
NAME                    READY                                            STATUS        RESTARTS        AGE
hello-sinatra-1-build   1/1                                              Running       0               2m

ビルドが終われば、http://hello-sinatra-test.apps.192.168.100.100.xip.ioにアクセスすることでアプリケーションが稼働していることを確認できます。

年末調整メモ

年末調整ってなんであんなに書きにくいフォーマットなんだろうね?IT化自動化してほしい。

自分は都民共済(生命保険と介護保険の2つに分かれる)と学資保険が子供2人分なので、生命保険のエントリが4つ、介護保険のエントリが2つ。支払の主体としては自分と奥さんのお財布の区別はないので、自分と奥さんの年末調整、どちらにでも書いていいはず。学資保険の額が大きいので、学資保険を自分と奥さんに振り分けて書く。あとはどっちに書いても控除額は増えないので、自分にまとめて書く。

住宅ローンがある場合は10年分の住宅借入金等特別控除申告書が手元にあるはずなので、残高証明書を元に記入する。

年末調整に向けて届く書類は、保険の支払証明が2通(2社分)、残高証明書、年末調整書類。あとは保管してある住宅借入金等特別控除申告書で、必要書類が揃う。こういうの毎年何いるんだっけ?ってなる。

全部書いたら一応全部写真を取って返送する。

OpenShiftにGogsをセットアップする

インターナルでWebhookが使えるGit hostでhttpしゃべるものが欲しかったのだけど、社内のGitがhttpsなのでフロントエンドでプロキシするか別のGit hostを、ということで気になっていたGogs試してみた。この人ボストンで学生やってるっぽいんだけどめちゃめちゃコード書いててすごい。

private docker registryの時と同じくRunAsAny設定が必要。

oc new-project gogs
oc new-app gogs/gogs
oc expose dc gogs --port=3000 --name=gogs-http
oc expose se gogs-http

http://gogs-http.gogs.tkimura.shiftにアクセスするとセットアップ画面が出るので、httpのURLがデフォルトでlocalhostになっているのをこのサーバに修正しておしまい。そのあとログイン画面に遷移するけど、最初に作ったユーザがadminになるので普通に登録してログインすればいい。tkimura.shiftは自分のDNSに設定しているオレオレOpenShiftドメイン名。

あとはぽちぽちリポジトリ作ってgit clone http://gogs-http.gogs.tkimura.shift/nekop/first.gitして動いたのでOK。

Gogsは3000と22の二つのポートがexposeされているので、3000のほうを別のServiceにしてRouteを作る。oc expose se gogsとやってしまうとHTTPとSSHにロードバランスされて面白いことになる。

Dockerの不要なコンテナとイメージを削除する

変なgrepしてたりコマンド置換$(...)使ってdocker rmから引数ないぞ警告でちゃうような微妙なサンプルが多かったので、自分のバージョンをメモ。

/etc/cron.daily/docker-cleanupに置いて日次実行にしてます。たまにマニュアルで叩いてたりもします。

#/bin/bash

docker ps -q -f status=exited | xargs --no-run-if-empty docker rm
docker images -q -f dangling=true | xargs --no-run-if-empty docker rmi