こんにちは。Red Hatの森 (@mosuke5) です。
今日はRuby on RailsのOpenShift上での開発について少しみていきたいと思います。
自分がまさにそうなのですが、生のKubernetesを先に学んだあとにOpenShiftにはいると、結構違う部分もあってはじめ理解が難しい部分もあります。
皆さん馴染みあるRubyで(?) 簡単な開発環境を作ってみて確認していきます。
4月に入社した身として、触っていく中でよくわからなかった部分、気になったを中心に書いているので、 OpenShift初心者の方に少しでも参考になればと思います。
minishiftを起動しよう
OpenShiftの環境は、一番用意しやすいであろうminishift(v1.33.0+ba29431)を使いました。 minikubeのopenshift版と思っていただければ大丈夫です。ローカル端末の仮想マシンの上にOpenShiftが動く環境を立ち上げられるものです。
本記事ではminishiftのインストールについては割愛しますが、下記を参考にインストールしてみてください。
Overview - Getting Started | Minishift | OKD Latest
ocクライアントのバージョンは下記のとおりです。
$ oc version oc v3.11.107 kubernetes v1.11.0+d4cacc0 features: Basic-Auth Server https://192.168.64.7:8443 kubernetes v1.11.0+d4cacc
RailsをOpenShift上で動作させる
ドキュメントを探していると、OpenShift上で動かせるRailsのアプリのサンプルを見つけました。
しかし、この手順の通り試しても抽象度が高すぎて「動いたけどよくわからん!」みたいな状態になったので、
ポイントを解説しながらみていきたいと思います。それがこの記事を書こうと思ったモチベ。
oc new-appというコマンド
OpenShiftにはoc new-app
というただのPodやDeploymentだけでなくアプリケーションを作るためのコマンドが用意されています。
あんまり実運用では利用されることは少ないかもしれませんが、今回のような開発環境のセットアップなどでは便利です。
しかし、このコマンドは良くも悪くもいろんな動きをするので理解が難しい一面もあります。
1つとして、OpenShiftなどで用意しているテンプレートからアプリケーションを実行することができます。
どんなテンプレートがあるかは下記のように確認できます。Rails+PostgreSQLのセットになったもの、Jenkinsなどいくつかあります。
$ oc get template -n openshift NAME DESCRIPTION PARAMETERS OBJECTS cakephp-mysql-persistent An example CakePHP application with a MySQL database. For more information ab... 20 (4 blank) 9 dancer-mysql-persistent An example Dancer application with a MySQL database. For more information abo... 17 (5 blank) 9 django-psql-persistent An example Django application with a PostgreSQL database. For more informatio... 20 (5 blank) 9 ...
テンプレートの実体は、kubernetesのマニフェストファイルでリソースの集合体です。OpenShiftにはTemplateというリソースがあります。 マニフェストファイルの中のパラメータなどを変数化することが可能で、データベースなどのシークレット情報をパラメータ化して配布することなど可能です。
実際にテンプレートの中身は下記のようにして見ることができます。
試しにpostgresqlのテンプレートをみてみますが、SecretやDeploymentConfig、Serviceのリソースが記載されているのがわかるかと思います。
また、テンプレートを使って起動する際にどんなパラメータを指定できるかなど確認できるので便利です。
$ oc get template -n openshift postgresql-persistent -o yaml | less
Railsアプリの起動
今回は上で紹介したRailsのサンプルのアプリケーションのレポジトリがあったので、こちらでRailsアプリの方の起動を行っていきます。 レポジトリをfork & cloneして、テンプレートで起動してみます。一旦この場は思考停止で実行してみます。
$ git clone git@github.com:mosuke5/rails-ex.git $ oc new-project rails-dev $ oc new-app openshift/templates/rails-postgresql.json -p SOURCE_REPOSITORY_URL=https://github.com/mosuke5/rails-ex --> Deploying template "rails-dev/rails-postgresql-example" for "openshift/templates/rails-postgresql.json" to project rails-dev Rails + PostgreSQL (Ephemeral) --------- (略) --> Creating resources ... secret "rails-postgresql-example" created service "rails-postgresql-example" created route.route.openshift.io "rails-postgresql-example" created imagestream.image.openshift.io "rails-postgresql-example" created buildconfig.build.openshift.io "rails-postgresql-example" created deploymentconfig.apps.openshift.io "rails-postgresql-example" created service "postgresql" created deploymentconfig.apps.openshift.io "postgresql" created --> Success Access your application via route 'rails-postgresql-example-rails-dev.192.168.64.7.nip.io' Build scheduled, use 'oc logs -f bc/rails-postgresql-example' to track its progress. Run 'oc status' to view your app.
実行後にpodの状態をみてみると、rails-postgresql-example-1-buildというbuildのpodが動いているのがわかります。 こちらは後ほど説明しますが、いまRailsのアプリケーションのイメージを作っている最中です。
$ oc get pod NAME READY STATUS RESTARTS AGE postgresql-1-mwm94 1/1 Running 0 55s rails-postgresql-example-1-build 1/1 Running 0 58s // 終わるとアプリケーション本体のPodが起動する $ oc get pod NAME READY STATUS RESTARTS AGE postgresql-1-mwm94 1/1 Running 0 2m rails-postgresql-example-1-build 0/1 Completed 0 3m rails-postgresql-example-1-md8bx 1/1 Running 0 17s
起動が終わったあとに、routeを確認してアクセスしてみると。アプリケーションが動作しているのがわかります。
route
はingress
に相当するような概念で、外部からPodへのアクセスの経路を確保します。
rails-postgresql-example-rails-dev.192.168.64.7.nip.io
は仮のドメイン名なので、/etc/hostsに192.168.64.7 rails-postgresql-example-rails-dev.192.168.64.7.nip.io
と書いてあげればアクセスできますね。
もちろん、oc port-forward service/rails-postgresql-example 8080:8080
のようにport-forwardを使ってアクセスしても問題ありません。
$ oc get route NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD rails-postgresql-example rails-postgresql-example-rails-dev.192.168.64.7.nip.io rails-postgresql-example <all> None
/articles
などアクセスすれば、記事の登録もできDB連携もできていることが確認できます。
さて、まあここまでやれば「なんか動いた!」という感じなわけですが、何が起きたのか少し追っていきたいと思います。
まずは作成されたリソースをみてみます。
NAME READY STATUS RESTARTS AGE pod/postgresql-1-mwm94 1/1 Running 0 9m pod/rails-postgresql-example-1-build 0/1 Completed 0 9m pod/rails-postgresql-example-1-md8bx 1/1 Running 0 7m NAME DESIRED CURRENT READY AGE replicationcontroller/postgresql-1 1 1 1 9m replicationcontroller/rails-postgresql-example-1 1 1 1 7m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/postgresql ClusterIP 172.30.66.38 <none> 5432/TCP 9m service/rails-postgresql-example ClusterIP 172.30.72.44 <none> 8080/TCP 9m NAME REVISION DESIRED CURRENT TRIGGERED BY deploymentconfig.apps.openshift.io/postgresql 1 1 1 config,image(postgresql:10) deploymentconfig.apps.openshift.io/rails-postgresql-example 1 1 1 config,image(rails-postgresql-example:latest) NAME TYPE FROM LATEST buildconfig.build.openshift.io/rails-postgresql-example Source Git 1 NAME TYPE FROM STATUS STARTED DURATION build.build.openshift.io/rails-postgresql-example-1 Source Git@67d882b Complete 9 minutes ago 2m39s NAME DOCKER REPO TAGS UPDATED imagestream.image.openshift.io/rails-postgresql-example 172.30.1.1:5000/rails-dev/rails-postgresql-example latest 7 minutes ago NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD route.route.openshift.io/rails-postgresql-example rails-postgresql-example-rails-dev.192.168.64.7.nip.io rails-postgresql-example <all> None
なにやらいろいろと作成されていますが、取り上げてみていくのが、deploymentconfigとbuildconfigとimagestreamです。
DeploymentConfig
まずはDeploymentConfigをみていきます。DeploymentConfig(dcと略せる)はOpenShift特有のリソースで、 KubernetesのDeploymentに該当するような機能で、trigger機能などがいくつかの機能が拡充されています。
気になるポイントの1つ目としては、strategy
のなかのpre
です。
Railsアプリケーションではデータベースを使う場合に、bundle exec rails db:migrate
でテーブルのスキーマを作っていきますが、今回それを実行することなくアプリケーションの動作までいけていました。どこで実行されていたのか?というとここです。migrate-database.sh
が用意されていて、Podが新しくできるときにシェルを実行していました。
次に気になるポイントは、triggers
の部分です。この設定では、イメージの変更をトリガーに更新できる設定が入っています。
後述するBuildConfigが新しいイメージを作成すると、それをトリガーにDeploymentConfigがPodを入れ替えてくれるわけです。
$ oc get dc NAME REVISION DESIRED CURRENT TRIGGERED BY postgresql 1 1 1 config,image(postgresql:10) rails-app 1 1 1 config,image(rails-app:latest) $ oc get dc rrails-postgresql-example -o yaml | less ... strategy: activeDeadlineSeconds: 21600 recreateParams: pre: execNewPod: command: - ./migrate-database.sh containerName: rails-postgresql-example failurePolicy: Abort timeoutSeconds: 600 resources: {} type: Recreate (略) triggers: - type: ConfigChange - imageChangeParams: automatic: true containerNames: - rails-app from: kind: ImageStreamTag name: rails-app:latest namespace: rails-dev lastTriggeredImage: 172.30.1.1:5000/rails-dev/rails-app@sha256:487b72449aa58cd0ef56252a72d26e2aac98e2c63699042becd5eb2ae3d88053 type: ImageChange
BuildConfig
続いてBuildConfig(bcと略せる)をみてみます。
今回稼働させたRailsアプリケーションのコンテナイメージを作成するためのビルドの設定がこちらです。
生のKubernetesではKubernetesとは別のプロセスとしてコンテナイメージを作成することが一般的かと思います。
OpenShiftでは、S2I (Source to Image)という機能があり、開発者がコンテナイメージを作らなくても、書いたコードをOpenShiftで動かすためのワークロードが用意されています。
そのビルドの設定がBuildConfigです。oc new-app
の時に指定した SOURCE_REPOSITORY_URL
の中身をみてアプリケーションの種別(RubyなのかJavaなのか)を判別して、イメージをビルドしてくれました。
似ているものとしては、buildpack, herokuなどが近いでしょうか。
$ oc get bc NAME TYPE FROM LATEST rails-app Source Git@blog-sample 1 $ oc get bc rails-app -o yaml | less source: git: ref: blog-sample uri: https://github.com/mosuke5/rails-ex.git type: Git strategy: sourceStrategy: from: kind: ImageStreamTag name: ruby:2.5 namespace: openshift type: Source
S2Iについてもっと知りたい方向けリンク
- GitHub - openshift/source-to-image: A tool for building/building artifacts from source and injecting into docker images
- GitHub - sclorg/s2i-ruby-container: Ruby container images based on Red Hat Software Collections and intended for OpenShift and general usage, that provide a platform for building and running Ruby applications. Users can choose between Red Hat Enterprise Linux, Fedora, and CentOS based images.
ImageStream
続いてImageStream (isと略せる)ですが、こちらも生のKubernetesにはないリソースです。ImageStreamは概ねDocker Registryと考えていただいて大丈夫ですが、
コンテナイメージを抽象化した概念になります。
特徴としては、コンテナイメージをプッシュした履歴が残る点です。今回、2回ビルドを行い、どちらもlatest
タグでプッシュしましたが、イメージのハッシュ値で別々に管理されているのがわかります。そのため、今回のようにイメージのタグを特に考えずlatestのままでしたが、適切にPodの入れ替えが行われたということです。
$ oc get is rails-postgresql-example -o yaml | less ... status: dockerImageRepository: 172.30.1.1:5000/rails-dev/rails-postgresql-example tags: - items: - created: 2019-07-09T08:40:53Z dockerImageReference: 172.30.1.1:5000/rails-dev/rails-postgresql-example@sha256:1d42310b4182199b471839108b278c646b99e3e731af783a1d0f5e4fca22e1d7 generation: 1 image: sha256:1d42310b4182199b471839108b278c646b99e3e731af783a1d0f5e4fca22e1d7 - created: 2019-07-09T07:42:45Z dockerImageReference: 172.30.1.1:5000/rails-dev/rails-postgresql-example@sha256:ca0141e090f7fd82ff665b6f87a7e099b920fa6a04d8dcf5263ddfccfad9f9ec generation: 1 image: sha256:ca0141e090f7fd82ff665b6f87a7e099b920fa6a04d8dcf5263ddfccfad9f9ec tag: latest
アプリケーションの変更
適当にアプリケーションを変更してgithubへpushします。
せっかくなので、データベースのスキーマも変更するような修正を入れたいと思います。
scaffoldを使ってユーザモデルとコントローラを作ります。
$ bundle exec rails generate scaffold user name:string age:integer $ bundle exec rake routes bundle exec rake routes Prefix Verb URI Pattern Controller#Action users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy $ git add . $ git commit -m 'update' $ git push origin master
それではこの変更をOpenShift上のRailsアプリケーションに反映させたいと思います。
oc new-app
で作られたBuildConfigなどはもちろんあとから実行が可能です。oc start-build
で実行してみます。
rails-postgresql-example-2-build
という2回目の実行のbuildコンテナが動いているのがわかります。
$ oc start-build rails-postgresql-example $ oc get pod -w NAME READY STATUS RESTARTS AGE postgresql-1-mwm94 1/1 Running 0 49m rails-postgresql-example-1-build 0/1 Completed 0 49m rails-postgresql-example-1-md8bx 1/1 Running 0 46m rails-postgresql-example-2-build 1/1 Running 0 <invalid>
buildが終わるとImageStreamが更新され、それをトリガーにDeploymentConfigが新しいイメージでPodを入れ替えてくれました。
実際に、 http://rails-postgresql-example-rails-dev.192.168.64.7.nip.io/users
にアクセスするとユーザ情報を操作できる画面がでてきました。
データベースのスキーマも無事に変更してくれています。
まとめ
OpenShiftを触り始めて、チュートリアル的に行うnew-appコマンドを使ったアプリケーションの構築でしたが、 裏では様々な動きが行われていて、奥深さを感じるものでした。 また、OpenShiftを使った開発のフローや戦略がみえるものでもあるなあと感じます。
今日見てきたところはほんのごく一部の基礎的なところではありますが、これからもOpenShiftを使ったアプリケーション開発について情報発信できたらと思います。