Red Hat - Open Innovation Labsのブログ - TECH PLAY

TECH PLAY

Red Hat  - Open Innovation Labs

Red Hat - Open Innovation Labs の技術ブログ

全900件

レッドハットの森若%仕事納めモード です。 猫は意外と胴が長いのですがこの記事とはあまり関係ありません。 ねこはのびます Photo by Timo Volz on Unsplash RHEL7とRHEL8で動作が違うcat 突然ですが以下の2行をRHEL 7とRHEL 8で実行すると動作に違いがあります。どうなるでしょうか? $ echo test > hoge $ cat < hoge >> hoge こたえはそれぞれ以下のようになります。 RHEL 7: 無限ループになって Ctrl-Cなどで停止させるまで止まらない。そしてファイルhogeにはどんどん'test\n' が追記されていく。 RHEL 8: cat: -: input file is output file とエラーが出力されてcatが停止、ファイルhogeは変化しない。 何が起きているのか? (shell編) ここからはRHEL 8 の環境で cat < hoge >> hoge を実行したときに何が起こっているのかを詳しくみていきましょう。 観察するため、シェルのPIDを確認し、別のシェルでstraceを動かします。ファイルを準備している様子をみたいので、 ファイル関連のシステムコールだけを表示するように-e file オプションをつけてフィルタします。 [観察対象のsh] $ echo $$ 14340 [作業用のsh] $ strace -p 14340 -e file -f 準備ができたので、観察対象のシェルでふたたび cat < hoge >> hoge を実行します。straceを仕込んだ方には以下の出力がでてきました。 strace: Process 14340 attached strace: Process 14911 attached [pid 14911] openat(AT_FDCWD, "hoge", O_RDONLY) = 4 [pid 14911] openat(AT_FDCWD, "hoge", O_WRONLY|O_CREAT|O_APPEND, 0666) = 4 [pid 14911] execve("/usr/bin/cat", ["cat"], 0x55c4fc2d96c0 /* 41 vars */) = 0 (以下略) shがコマンドcatを実行するにあたり、まずforkでプロセス14911を作成し、ここでファイル "hoge" を2回open します。1回は標準入力(stdin)のために、読み込み専用(O_RDONLY)で、もう1つは標準出力(stdout)として、書き込み専用(O_WRONLY)かつ追記(O_APPEND)で開いています。 シェルがファイルを用意して環境がととのったのでexecveでcatが実行します。 ここまでの動作はRHEL 7でも同じです。 何が起きているのか? (cat編) いよいよcatの実行がはじまります。今回は明確なエラーメッセージが出力されているのでソースの中でメッセージを探します。 RHEL8の coreutils 8.30内 cat.c より 690 691 /* Don't copy a nonempty regular file to itself, as that would 692 merely exhaust the output device. It's better to catch this 693 error earlier rather than later. */ 694 695 if (out_isreg 696 && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino 697 && lseek (input_desc, 0, SEEK_CUR) < stat_buf.st_size) 698 { 699 error (0, 0, _("%s: input file is output file"), quotef (infile)); 700 ok = false; 701 goto contin; 702 } 703 stat_bufは入力ファイルに対してstatを行った結果です。 出力先のファイルがpipeやsocketではない通常のファイルで 出力ファイルと比較してデバイスとinode番号が同じ(つまり同じファイル実体を指す)でかつ 入力側の現在位置よりあとにデータが存在する の3つが成立する場合、RHEL 7の時の動作として説明したように無限にファイルが伸びつづけてしまう(その結果リソースが枯渇する)のでこれをエラーとして早めに停止させるというコードが書かれています。 なるほど事故防止の意図はわかりました。RHEL 7のcatにはこのチェックは存在しなかったのでしょうか……? 該当する箇所をみてみましょう。 RHEL 7の coreutils 8.22内 cat.c より 707 /* Compare the device and i-node numbers of this input file with 708 the corresponding values of the (output file associated with) 709 stdout, and skip this input file if they coincide. Input 710 files cannot be redirected to themselves. */ 711 712 if (check_redirection 713 && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino 714 && (input_desc != STDIN_FILENO)) 715 { 716 error (0, 0, _("%s: input file is output file"), infile); 717 ok = false; 718 goto contin; 719 } おおむね同じような意図のコードがあるのですが、少しエラー検出の条件が甘く、入力ファイルが標準入力であれば見逃されてしまうようです。 確認 理解が正しいか確認するため、RHEL 7で以下のコマンドで正しくエラー検出がされることを確認します。 $ cat hoge >> hoge cat: hoge: input file is output file 無限に長くなる猫は好まれないという話でした。
こんばんわ。 2019年ももぅあと僅かですね。今年は何やってたんやろ…と思ってたら終わりました。 そしてAdvent Calendarも、いよいよ終わりになりましたね。ブログはもともと苦手なんですが、師走はバタバタしており、時間がないともっと苦手になりますねー。 それでも自分の番がやってくる。恐るべしAdvent Calendar (/・ω・)/ 今回は『 OpenShift Advent Calendar 2019 - Qiita 』の最終日投稿です。 検証したい技術ネタもそこそこ溜まってますが、 OpenShift.Run を12/20に行ったので、記憶が新しいうちにまず結果をお送りしますっ。 なんで「OpenShift.Run」? Red Hat社内のSA(Solution Architect)が顧客向けにOpenShiftのデモ環境を作ってるんですね…。 そしてOpenShiftをインストールしたことある方は分かると思うのですが、コンテナアプリケーションの外部接続のためにドメインが必要です。そこで使っていたのが(例)「*.app.cluster.openshift.run」みたいなやつ。 そのTopドメインをもらって、OpenShift.Runになりました。なお、個人取得のドメインですw ただただ「.Run(ドット・ラン)」ってかっこよくね?みたいなノリです。 この規模のイベントをノリで許されるのが赤帽のいいところですよねー。 OpenShift.Runのイベントサイトについて 多くの場合、イベントサイトはイベント会社に作ってもらうのですが、今回はイベントサイトをAzure上のWordpressで動かしていました。 (*さすがにずっと動かしてるとお金がかかるので、もう停止しましたがw) コミュニティなので、自分で作ったほうがビジネスっぽくなくていいなぁーとか思ってやってました。(ただの自己満) それこそOpenShift上で動いていましたっ!というとカッコいいのですが、ガッツリVM(app-gateway x1, db x1, app x1)で運用。 よく「うちのサービス、コンテナ化する理由がないんです。」みたいなお客さんもいるのですが、まさにそれですね。とくに頻繁なメンテナンスやCIを回さない以上、コンテナ化する労力のほうがかかってしまうという。ただ、逆に言うと、頻繁に運用したり、リリーススピードを上げたいサービスだったらコンテナのほうがいいってことですよね。 OpenShift Communityで意識していること そんなにOpenShift感出して(特にビジネス中心)、コミュニティやりたいとは正直思ってないんですよね。 エンタープライズのシステムにもKubernetesを導入してほしいなー。くらいの思いで。 ただ、OpenShiftと言ってしまうと、どうしてもビジネス色がついてしまう点がむずかしいところ。冒頭の写真を見ても分かるように、半数がスーツという会になっているので、普段会わない人同士(スーツ x Tシャツ)のタッチポイントとなればいいなぁとは意識しつつ…。 OpenShift Communityで今後取り扱ってほしい特集 会場でKubernetesリソースマグネットと引き換えに、アンケートをとってたので一部紹介します。まず1つ目は「OpenShift Communityで今後取り扱ってほしい特集」。 OpenShift.Runのアンケート OpenShift Communityで扱ってほしい特集の一位は 『基本的なOpenShiftの機能紹介』 まぁ…そうですよね。色々ググれば情報は出てくるのですが、改めてOpenShiftの機能を知りたいという要望も多いと思うので、年明けにでも検討したいなと思います。また、Operatorも人気がありますね。どうもOperator SDKなどの作る方にも注目いただけそうだったので、こちらも合わせて検討したいトピック。 参加者のKubernetes、またはOpenShiftの利用/学習状況はどのあたりですか 面白かったのがこのアンケート結果。 まだまだKubernetes/OpenShiftの利用経験がない方が多い反面、すでに開発環境で使い始めている方も同じ割合くらいいらっしゃったということ。また、チュートリアルくらいの利用に留まることも多く、実践に向けた過渡期の方々に多く参加頂けているのだと感じました。 Kubernetes/OpenShiftを社内システムに導入する際(または仮定した場合)に、一番障壁となりがちなポイント Kubernetes導入の障壁は 「チームメンバーの技術力のばらつき」 やはりKubernetesを知っている人が1人いても、チーム全体としてサービスを運用できないため、結果的に導入が難しくなると言ったところでしょうか。技術レベルを完全に統一することは出来ませんが、最低のボーダーラインは抑えるという点で、Kubernetes運用の課題が見えてきそうですね。 OperatorやManagedサービスが行うように、Kubernetesをいかに意識せずに運用できるかが、今後のシステム運用には求められる気がします。 年末年始や年明けにでも試したいものはありますか 本イベントでOpenShiftに興味を持っていただいた方も多く、積極的に触れてみたいという意見が多かったです。 Kubernetesやコンテナに関する本/ブログを読んでみたい。 「 https://learn.openshift.com 」でOpenShiftを試してみたい。 無料のOpenShiftハンズオンワークショプに参加してみたい。 会場でも紹介しましたが、OpenShiftを試すには、KataCodaの「Learn OpenShift」が一番手っ取り早いです。 learn.openshift.com また、今後のハンズオンワークショップの情報などは「Now On Red Hat」などに掲載していく予定です。 www.redhat.com 是非、年末年始や年明けに有言実行していただけると、とても嬉しいところです。 おわりに 色々とアンケート結果をご紹介してきましたが、参加いただいた方々に有意義な時間を過ごしていただければ幸いです。 また、OpenShift.Runも継続的に行えればと思っています。 Twitter や Connpass などでも情報発信していきますので、是非フォローください。 それでは、良いお年をっ!!
この記事は「 Ansible Advent Calendar 2019 」24日目の記事です。 レッドハットでAnsibleのテクニカルサポートエンジニアをしている八木澤(ひよこ大佐)です。 我々テクニカルサポートエンジニアは、お客様からのお問い合わせに基づいて、提供いただいたAnsibleのログなどからおおよそのエラーの原因を推定し、調査を進めます。そういったトラブルシューティングの知識は、現場レベルで伝えられることはあっても、文章として伝えることは困難です。 しかし「Ansibleを使い始めてみたけれど、エラーがわかりにくくて何が問題なのかわからない!」という方にとっては、まさにトラブルシューティングのための情報こそが有用なのではないでしょうか。この記事では、そういった方々を対象に、日々Ansibleのテクニカルサポートをしている経験をもとに、Ansibleで確認するべきトラブルシューティングのファーストステップと、トラブルシューティングのエッセンスをご説明します。 また、これらの基本はAnsible Automationサブスクリプションをご利用のお客様で、「Red Hatサポートに問い合わせをしたいけれども、何の情報を収集すればよいのか」という指針にもなると思いますので、ぜひ参考にしていただければ幸いです。 基本的なトラブルシューティング 初回は、Ansibleのトラブルシューティングの基本からおさらいしましょう。Ansibleのトラブルシューティングは、通常のITインフラにおけるプロセスと大きな違いはありません。重要なのは、Ansibleがどのような方法でPlaybookを実行しており、どのプロセスでエラーが発生しているのか理解することです。 Ansibleのアーキテクチャを理解する Ansibleはご存知の通り、コネクションプラグインを通じてターゲットノードに接続し、変更を実施します。Linuxノードであれば、接続後にターゲットノード上でPythonスクリプトを実行し、実行結果をコントロールノードへ返却します。 これらの動作は、対象となるターゲットノードの環境やコネクションプラグインによって異なることに注意してください。ネットワーク機器やWindowsではまた異なる手法(httpapiやwinrmなど)で接続するため、トラブルシューティングで確認するべきポイントも異なります。 Ansibleがどのようにターゲットノードに接続していて、どこで問題が発生したのか、頭の中でイメージを描いたり実際に図を描いて考えることが重要です。 トラブルシューティングのフロー トラブルシューティングの基本的なフローは、以下の通りです。 エラーメッセージの確認 事象の切り分け 情報収集 対処 エラーメッセージの確認 Ansibleで実行に失敗すると、何らかのエラーが出力されます。まず、出力されたエラーを確認します。多くの場合、エラーメッセージの中にトラブルシューティングに必要な情報が存在します。よくあるエラーメッセージとその対処法については、次回ご説明します。 また、Ansibleでは ansible-playbooks コマンド実行時に「-vvv」オプションを指定することで、より詳細な出力を確認することもできます。これらの情報をもとに、どのようなエラーが発生しているのかを把握します。 エラーメッセージを確認する際に重要なのは、「AnsibleがPlaybookを実行するプロセス」のどこで問題が発生しているのか確認することです。例えば、「UNREACHABLE」のエラーであれば、そもそもAnsibleがターゲットノードに接続することができていませんので、コントロールノードとターゲットノード間でSSH接続が可能か確認する必要があります。 Ansibleでは、さまざまな問題が発生する可能性があります。たとえばPythonのパスが通っていなかったり、パーミッションの問題、モジュールのバグ、Playbookの記述ミスなど、考え始めるとキリがありません。ですので、問題があると思われる要素を絞りこむ必要があります。 事象の切り分け エラーメッセージを確認したら、具体的にどのような事象なのか切り分けを行います。切り分けを実施する際に確認するポイントはいくつかありますが、特に重要なのは以下のポイントです。 再現可能か、再現不可能か 環境の確認(Ansibleのバージョン、OSのバージョン、プロキシの有無等) 発生頻度(1度だけ発生する/たまに発生する/毎回発生) 発生する対象(1台だけ/すべて) これはおそらくどのようなトラブルシューティングでも同様だと思われますが、これらの情報は問題のある箇所の特定にも役立ちます。例えばサポートに問い合わせをする際に、上記のような情報を添付すると、スムーズにトラブルシューティングを開始することが可能です。 情報収集 エラーメッセージや出力されたTracebackから、類似の事象や問題がないか確認します。単純にGoogleで検索してもいいですが、 AnsibleのGitHubリポジトリ のIssueを確認するのもよいでしょう。Ansibleユーザー会のコミュニティでは「qa」というSlackチャンネルがありますので、類似する問題が見つからなければそこで聞くのも手です。 Ansibleを使用しているユーザーは世界中にいますので、調べてみると同じような問題に遭遇しているユーザーがいたりします。それらの情報の内容と信憑性を確認しながら、検証を進めていきます。 検証を進めた結果、「これかもしれない」という情報が見つかる場合があります。その場合は、その裏付けとなる証拠を集める必要があります。例えば別の観点でログを確認したり、その情報とバージョンが同一であるか確認したり、場合によってはモジュールなどのコードを直接確認し、挙動を推察して仮説を立てます。そのロジックに抜け漏れがないか、仮説を補強する証拠がないか確認していきます。また、検証環境があれば実際に再現し、事象が改善するか確認します。 対処 もし原因と思わしき設定箇所や問題が判明し、「おそらくこれに間違いない」という確証を得た場合は、実際に対処します。ここで解決しなかった場合は、Ansibleの未知のバグである可能性も検討しつつ、また別の観点で仮説を立てたうえで、再度情報を収集し分析する必要があります。 これらのステップは、ドラマ・小説で刑事や探偵が事件を捜査する流れとよく似ています。証拠となるログやエラーメッセージをもとに仮説を立て、推理し、さまざまな可能性を検討しながら事件の真相解明と事件解決へと向かっていきます。しかし、仮説を立てるためにはAnsibleがどのように動作しているのか理解していなければ、そもそもの原因を推測することもできません。ですので、基本に立ち返ってAnsibleのしくみを学ぶことが何よりも大切です。 次回以降では、より具体的なAnsibleのトラブルシューティングや、トラブルシューティングに役立つ情報についてご紹介します。
赤帽エンジニアブログをご覧頂いているみなさん、めりーくりすます。Red Hatのさいとうです。赤帽エンジニアブログの Advent Calendar 2019 最終日の記事はAnsible AWXです。だってみんな使ってるもんね! Hello! New UI! 2020年のいつ頃かは未定ですが、AWXのUIが大きく変更される予定で、水面下?で開発が進んでいます。もちろんAWXはOSSプロジェクトですから、みなさんもいち早くこの新しいUIの世界を体験することができます。 只今開発中ということもあって、突然ルック・アンド・フィールが劇的に変更される可能性もありますが、現時点でのUIの体験方法をご紹介します。 まずはdevel版のコンテナをビルドする アップストリームのAWXを git clone して、develブランチのAWXコンテナ群をビルドします。ビルドの方法については、こちらの記事( 2019/12/04 と 2019/12/25 でもう少しだけ細かく説明しているので興味あるかたはご覧ください。 $ python3 -m venv awxdevel $ source awxdevel/bin/activate (awxdevel)$ pip install docker-compose (awxdevel)$ git clone https://github.com/ansible/awx.git (awxdevel)$ cd awx (awxdevel)$ cp awx/settings/local_settings.py.docker_compose awx/settings/local_settings.py (awxdevel)$ make docker-compose-build 続いて、現行バージョンのUIをビルドします。 (awxdevel)$ make clean (awxdevel)$ make languages (awxdevel)$ make ui-devel コンテナ群を起動します。 (awxdevel)$ make docker-compose 起動すると make docker-compose を実行したターミナルがコンテナにもっていかれてしまうので、別途ターミナルを起動して、tools_awx_1コンテナに接続してAWXの管理者とデモ用のデータを作成します。ここまでは、 2019/12/04の記事 と同様です。 $ cd <cloneしたAWXのソースコードリポジトリ> $ docker exec -it tools_awx_1 bash アタッチしたコンテナ上で、以下のコマンドを実行して管理者とデモ用データを作成します。 $ awx-manage createsuperuser Username (leave blank to use 'awx'): awx Email address: Password: ******** Password (again): ******** Superuser created successfully. $ awx-manage create_preload_data Demo Credential, Inventory, and Job Template added. (changed: True) $ exit これで、ブラウザを利用して https://127.0.0.1:8043/ からダッシュボード(現行版)にログインすることができます。 ログイン画面はこちら: AWX Login ログイン後のダッシュボード: AWX Dashboard 次世代UIの起動 それでは、devel版のコンテナを起動した状態で、コンテナ群を起動した開発環境のホストから次世代UI用をビルドして起動してみましょう。 $ cd awx/awx/ui_next $ npm install $ npm start 次世代UIが無事に起動すると、ブラウザから https://127.0.0.1:3001/ にアクセスすることで...おそらく2020年はこうなるであろうログイン画面が!: AWX Login NEXT そして、絶賛実装中のダッシュボード: AWX Dashboard NEXT いろいろ開発中でまともには利用できないんですけどね :) 次世代UIの開発状況 冒頭でご説明した通り、AWXの次世代UI(ui_next)はAWXコミュニティで絶賛開発中です。みなさんも絶対に開発に協力したはず!そんなあなたにクリスマスプレゼントとして、以下のIssueとPull Request一覧のリンクをお知らせしちゃいます。いいバグみつけてくださいね :) Open中のIssue一覧 Open中のPull Request一覧 以上、赤帽エンジニアブログAdvent Calendarの最終日の記事は、AWXの少し近い未来のお話をしてみました。来年もAnsibleとAWXとTowerをかわいがってあげてください :) Happy Hacking!そしてめりーくりすます!
めりーくりすます。Red Hatのさいとうです。この記事は Ansible 2 Advent Calendar の12月25日のエントリです。 前回の 記事 に引き続き、2回めの今回は、実際にAWXのコードを修正して規定のテストを実施し、アップストリームのAWXプロジェクトにPull Requestを作成するまでの流れを紹介します。 ※前回同様、開発環境上での作業はプロンプトに [host] を、開発環境上に起動させた開発用のコンテナ上で作業する作業は [container] と明記しますので、実際に作業する環境に注意して読んでください。 AWXの開発用コンテナ群が起動していることを確認する How to contribute - AWX(1) にしたがって、すでに開発用のコンテナ群が起動しており、管理ユーザ(awx)が作成されていることを確認します。 tools_awx_1 , tools_rabbitmq_1 , tools_postgres_1 そして tools_memcached_1 という4つのコンテナが起動しているはずです: [host]$ docker ps --format "table {{.ID}}\t{{.Names}}\t{{ .Ports }}" CONTAINER ID NAMES PORTS 210c423c473c tools_awx_1 0.0.0.0:6899->6899/tcp, 0.0.0.0:7899-7999->7899-7999/tcp, 0.0.0.0:8013->8013/tcp, 0.0.0.0:8043->8043/tcp, 0.0.0.0:8080->8080/tcp, 22/tcp, 0.0.0.0:8888->8888/tcp 5226b93ab835 tools_rabbitmq_1 4369/tcp, 5671-5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp 82c29aab2d47 tools_postgres_1 0.0.0.0:5432->5432/tcp 7cb708df7506 tools_memcached_1 0.0.0.0:11211->11211/tcp AWXのダッシュボードやジョブ実行の機能を提供しているのが tools_awx_1 です。このコンテナは、開発環境にチェックアウトしたawxリポジトリをマウントしています。 [host]$ docker inspect tools_awx_1 -f "{{ .Mounts }}" [{bind /home/ansible/work/src/awx /awx_devel rw true rprivate} {bind /home/ansible/work/src/awx/awx/projects /var/lib/awx/projects rw true rprivate}] つまり、コードの修正は開発環境上で実施すればOKです。ただし、フロントエンドのJavaScriptのコードを修正する場合は、修正を反映するために: tools_awx_1コンテナの停止( make docker-compose で起動しているはずなので CTRL+Cで停止 ) make clean && make languages && make ui-devel の実施 make docker-compose で再度起動 が必要になるので注意してください。 コードの修正 それでは、実際にコードを修正してから、規定のテストを実施してみましょう。サンプルとしてダッシュボードへのログイン時に出力されるログを修正してみます。 [修正前] awx_1 | 2019-12-21 02:51:51,354 INFO awx.api.generics User awx logged in from N.N.N.N [修正後] awx_1 | 2019-12-21 02:51:51,354 INFO awx.api.generics Welcome to Ansible AWX! User awx logged in from N.N.N.N 以下のようにログ出力メッセージを修正してみました。 [host]$  git diff awx/api/generics.py diff --git a/awx/api/generics.py b/awx/api/generics.py index 37ca8bef4..eb8de518c 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -92,7 +92,7 @@ class LoggedLoginView(auth_views.LoginView): ret = super(LoggedLoginView, self).post(request, *args, **kwargs) current_user = getattr(request, 'user', None) if request.user.is_authenticated: - logger.info(smart_text(u"User {} logged in from {}".format(self.request.user.username,request.META.get('REMOTE_ADDR', None)))) + logger.info(smart_text(u"Welcome to Ansible AWX! User {} logged in from {}".format(self.request.user.username,request.META.get('REMOTE_ADDR', None)))) ret.set_cookie('userLoggedIn', 'true') current_user = UserSerializer(self.request.user) current_user = smart_text(JSONRenderer().render(current_user.data)) DjangoアプリケーションのPythonコードに対する修正ですから、コンテナをリスタートすることなく make docker-compose を実行しているコンソールのログには、ダッシュボードからのログイン時に修正されたログが出力されるはずです。 規定のテストを実施する 実際に動作確認して、動作確認できたらcommit&pushしてPRを作成し、あとはアップストリーム側のCIテストにおまかせしてしまう...という手もありますが、 本家のお作法 にしたがって、手元でテストを実施してからPull Requestを作成します。 まずはflake8( make flake8 )でPythonコードの書式のチェックです: [container]$ make flake8 ./awx/api/generics.py:95:161: E501 line too long (162 > 160 characters) make: *** [Makefile:342: flake8] Error 1 長い...しまった...修正した行が長すぎた...では怒られないような書式に修正します: [host]$ git diff awx/api/generics.py diff --git a/awx/api/generics.py b/awx/api/generics.py index 37ca8bef4..642c761b3 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -92,7 +92,8 @@ class LoggedLoginView(auth_views.LoginView): ret = super(LoggedLoginView, self).post(request, *args, **kwargs) current_user = getattr(request, 'user', None) if request.user.is_authenticated: - logger.info(smart_text(u"User {} logged in from {}".format(self.request.user.username,request.META.get('REMOTE_ADDR', None)))) + logger.info( smart_text( + u"Welcome to Ansible AWX! User {} logged in from {}".format(self.request.user.username,request.META.get('REMOTE_ADDR', None)))) ret.set_cookie('userLoggedIn', 'true') current_user = UserSerializer(self.request.user) current_user = smart_text(JSONRenderer().render(current_user.data)) これでよかろう: [container]$ make flake8 [container]$ 問題が報告されることなく無事に通過しました。続いてJavaScriptコードは修正していませんが、念の為 JSHint( make jshint )も実施しておきます: [container]$ make jshint ... Running "jshint:source" (jshint) task ✔ No problems ... コードの書式的には、これで問題ないようです。次に、テストフレームワーク(pytestとJasmine)を利用したテストを実施します。まずはpytest( make test )から: [container]$ make test ====================================== test session starts ======================================= platform linux -- Python 3.6.8, pytest-3.6.0, py-1.8.0, pluggy-0.6.0 Django settings: awx.settings.development (from ini file) rootdir: /awx_devel, inifile: pytest.ini plugins: xdist-1.27.0, timeout-1.3.3, pythonpath-0.7.3, mock-1.11.1, forked-1.1.3, django-3.7.0, cov-2.8.1, celery-4.3.0 gw0 [2572] / gw1 [2572] / gw2 [2572] / gw3 [2572] .....................................................................................................................................................s [ 5%] ... .................... [100%] ========================================================= 2562 passed, 10 skipped in 388.78 seconds ========================================================== cd awxkit && /venv/awx/bin/tox -re py2,py3 GLOB sdist-make: /awx_devel/awxkit/setup.py py2 create: /awx_devel/awxkit/.tox/py2 ... py2: commands succeeded py3: commands succeeded congratulations :) awx-manage check_migrations --dry-run --check -n 'vNNN_missing_migration_file' No changes detected エラーで失敗するようであれば、コードに問題がある可能性が高い(テスト自体の問題である場合ありますが、経験上その可能性は非常に低いです)ので、修正したコードを見直してから make test を再実行しましょう。 続いてUIのテストです。 make ui-test-ci は、Chromeをヘッドレスモードで起動してUIテストを実施してくれます。 [container]$ make ui-test-ci npm --prefix awx/ui run test:ci ... HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 4 of 41 (skipped 1) SUCCESS (0 secs / 0.254 secs) HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 6 of 41 (skipped 1) SUCCESS (0 secs / 0.45 secs) HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 8 of 41 (skipped 1) SUCCESS (0 secs / 0.656 secs) HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 11 of 41 (skipped 1) SUCCESS (0 secs / 0.885 secs) HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 13 of 41 (skipped 1) SUCCESS (0 secs / 1.008 secs) HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 14 of 41 (skipped 1) SUCCESS (0 secs / 1.185 secs) HeadlessChrome 71.0.3542 (Linux 0.0.0): Executed 38 of 41 (skipped 3) SUCCESS (1.967 secs / 1.753 secs) 無事にパスすればテスト完了です。 commit & push そしてPull Request git commit --signoff でサインして適切なコメントをつけて修正コードをcommit & pushしましょう。 [host]$ git add awx/api/generics.py [host]$ git commit --signoff [host]$ git push --set-upstream origin PR/adc20191204 最後に、GithubサイトでPull Requestを作成してください。 以上、AWXのアップストリームプロジェクトにコードをコントリビュートするためのオーソドックスな方法について紹介しました。AWXプロジェクトにコードをコントリビュートしたいと考えている「みなさん」の参考になりますように :)
2019年ももうおしまいですね。進捗いかがですか? この記事は Ansible Advent Calendar 2019 の25番目の記事です。僭越ながら、Ansible Advent Calendar 2019の最終日に投稿させていただきます。 2019年も参加者の皆さんのおかげで、あらゆる形でコミュニティ活動をしてきました。2019年ももう終わりということで、2019年のAnsibleユーザー会について振り返ってみたいと思います。 数字でわかる2019年のAnsibleユーザー会 Connpassメンバー数(4,024) 以下の横地さんのツイートからもわかるように、2019年1月の末時点では3,000人だったメンバー数が、この記事を仕込んでいる時点で4,024人になっていました。1年弱で、1,000人くらい増えたということになります。 Ansibleユーザー会、3000人! https://t.co/EwZZakLWqs pic.twitter.com/xPXObUlfjh — よこち(yokochi) (@akira6592) 2019年1月30日 ネットワーク自動化やWindows自動化など、まだまだAnsibleで自動化できる領域があると改めて気づいた方や、はじめてAnsibleを使ってみようと思ってもくもく会に参加する方など、ガチ勢から初心者など参加者の多様性も広がったのではないかなと思います。 Slackユーザー数(843) オンラインのコミュニケーションプラットフォームとして、2018年7月に作成した、Slackのコミュニティですが、こちらも日々参加メンバーが増えています。2019年1月に記録をとった際には449名だったメンバー数が、この記事を仕込んでいる時点で843名となっており、400名近く参加が増えていました。 Slackのオンラインコミュニティでは、疑問に思うことをコミュニティメンバーに聞いたり、有志の方々で回答してもらったりしています。また、Ansible Nightや部での企画持ち込みができるチャンネルを準備していたりします。 実施されたもくもく会の回数(14) 地方開催なども含めていろいろな場所で開催させていただいたもくもく会。Connpassで記録されている限りは14回実施していました。東京開催はなるべく1ヶ月に1回開催するようにしておりますが、地方開催についてもご要望に応じて対応させていただいていますので、ぜひ我こそはという方はお声がけくださいね。 主なAnsibleユーザー会での活動 今年は多岐に渡り活動をさせていただきました。いろいろなところから声がかかって楽しかったです。Ansibleユーザー会のイベントは Connpassに履歴が残っている ので、そちらもご確認ください。 2月: 初の名古屋でAnsible Night開催 JAWSUGに参加、Software Designを配布 3月: Githubで Ansibleユーザー会のまとめページ をオープン 4月: 甲府でAnsibleもくもく会を開催、新聞取材→ 地元紙に掲載 7月: Software Design「Ansible問題解決マップ」連載開始 (※ユーザー会メンバーが執筆開始) 8月: 極度自動化Tシャツ リリース 9月: 松江でAnsibleもくもく会開催 11月: 京都でAnsibleもくもく会開催 Ansiblejpネットワーク部を初開催 12月: 札幌でAnsibleもくもく会開催 Advent Calendarが3つも埋まった Angry Potatoアカウント が誕生 JAWSUGにてSoftware Designを配布 もくもく会 in 甲府の取材風景 「部」を始動 会員数も大幅に増えており、アンケート調査でもあらゆる要望があると分かった( 参考 )ので、2019年春くらいから分科会のようなものをしたいなと考えていました。色々良いきっかけもあり、11月からネットワーク部を始動いたしました。こちらはまだ始めたばかりなので、2回目、3回目と企画できればなと考えています。 また、2月頃にはもう1つ部が始動する予定ですので、そちらもご期待ください。企画を作って、運営支援してくれる人も随時募集しております(立候補の仕方、Contributeの方法については別途まとめる予定です)。 運営メンバー/会場提供 Ansible Nightでは受付やら運営をご支援いただける方を募集していました。今年は4名の方にいくつかのAnsible Nightをご支援いただきました。実施後の振り返りなどを通して会の最適化にご貢献頂いています。 運営を支援していただいた4名の方々🎉 - @sky_jokerxx - @maaaato - @paihu - @akira6592 また、会場をご提供やご手配頂いた以下の企業様にも改めて御礼申し上げます。🙇‍♂️ 富士通様 Misoca様 オイシックス・ら・大地様 エーピーコミュニケーションズ様 アイティドゥ様 GMOクラウド様 Yahoo Lodge様 ソフトバンク様 マイクロアド 様 日本電気様 楽天 札幌支社様 ブロードリーフ様 ※順不同 さて、2020年はどんなAnsibleイヤーになるでしょうか???みなさんとAnsibleをきっかけとした学びや人と人とのつながりを実現できるように引き続き支援していきたいと思います。 ハッピー・オートメーション!
この記事は、 赤帽エンジニア Advent Calendar 2019 23日目の記事です。 皆様こんにちは、Red Hatの八木澤(ひよこ大佐)です。前回、PrometheusでAnsible Towerを監視する記事を投稿しましたが、Ansible Towerには「Automation Analytics」と呼ばれる、お手軽にAnsible Towerのジョブ実行数などを確認することができるサービスがあります。 Automation Analyticsとは? 「Automation Analytics」は、Ansible Towerの各クラスタノードの状況やステータスなどを確認することができるサービスです。SaaSとして提供されているため、別途監視用のソフトウェアなどをセットアップすることなく使い始める事ができます。Ansible Tower 3.5.3以上であればこのサービスを利用することができます。 詳細は こちらのブログ記事 でも解説されています。 www.ansible.com Ansible Tower側でデータ収集を有効化する Automation Analyticsを利用するためには、Ansible Tower側で情報を送信するための設定を行う必要があります。Ansible Towerにログインし、「設定」→「システム」を開き、「GATHER DATA FOR AUTOMATION ANALYTICS」にチェックが入っていることを確認します。 手動でデータを収集する 上記自動収集を有効化していない場合でも、Automation Analyticsに情報を手動で送信することができます。Ansible TowerノードにSSHでログインし、以下のコマンドを実行します。 # awx-manage gather_analytics --ship Last analytics run was: 2019-12-24 07:19:21.028354+00:00 /tmp/aeae8652-0202-4130-b401-31d627c6dcac_2019-12-24-072006+0000.tar.gz shipping analytics file: /tmp/aeae8652-0202-4130-b401-31d627c6dcac_2019-12-24-072006+0000.tar.gz Automation Analyticsにログインする Automation Analyticsには、以下のサイトからログインできます。Red Hat Customer Portalで使用しているユーザー名とパスワードを入力してください。 https://cloud.redhat.com/ ログインしたら、右側の「Red Hat Ansible Automation Platform」のパネルから「Automation Analytics」をクリックします。 収集された情報を確認する Automation Analyticsにアクセスすると、以下のような画面が表示されます。 中央のパネルには成功したジョブと失敗したジョブのグラフを、指定した期間やTowerクラスタごとに表示させることができます。また、実行されたジョブテンプレートやモジュールのランキングも確認できます。 また、「Organization Statistics」では各組織ごとのジョブ実行などの詳細を確認することもできます。Ansible Towerを利用されている方はぜひ一度試してみてはいかがでしょうか。
Red Hat でソリューションアーキテクトを担当している荒木です。赤帽エンジニアブログ アドベントカレンダー 21日目をおおくりいたします。 2019年12月20日 に開催されたOpenShift.run で OpenShift 4.2 の、Machine Config Operator について紹介させていただきました。今回はその番外編として、Machine API の Machine Health Check を有効にする方法の紹介をしたいと思います。 尚、今回は、AWS上にIPI で構築された、OpenShift 4.2 を前提として紹介しております。環境や前提が異る場合、動作が異る可能性があるのでご注意ください。 openshift-installer によるAWS上のOpenShiftクラスタ構築については、こちが参考となります。 github.com Machine API について Machine API の概要を紹介します。Machine API は、アップストリームのCluster API プロジェクトで開発されている機能を、OpenShiftに拡張/調整して実装されています。 ここで、簡単に、Cluser APIについて紹介します。Cluster API は、Kubernetes クラスタのライフサイクル全般(作成〜削除)を範囲とし、クラスタに対する運用管理操作に一貫したインタフェースと振舞を提供する事を目標として、現在、盛んに機能が開発されているプロジェクトとなります。 Cluster API 機能概要 Kubeクラスタのライフサイクル管理を提供 各種クラスタ上のオブジェクトを抽象化 様々な実オブジェクト(非Kubeな実態)を抽象化し Kube-Native(宣言型かつ調整的な機構)を実現する Machine API は、Cluster APIの思想に従うもので、OpenShiftクラスターが稼働するプラットフォーム(各種IaaSや、Baremetal、仮想統合環境)によらず、クラスタ環境にシンプルで一貫性のある運用管理性の実現を目指しています。 尚、Cluster API には、複数のクラスタを管理する Management Clusterのような機能がありますが、これまでのところOpenShift には実装されていません。しかし、今後、マルチクラスター機能を高度化していく過程で複数のクラスタ環境を横断的に管理する機能が提供されるようになると想像します。 Machine API の機能 ノードの即時的なスケールイン/アウトをコマンドラインから可能に ノードのスケール調整そのものを自動化(autoscale) ノードのローリングアップデート ノードの自動リカバリ(OCP 4.2 では TP) 詳細は、 こちら のドキュメントを参照ください。 Machine Health Check について Machine Helath Check (MHC)は、 OpenShiftクラスタを構成する、node(masterを除く)の稼働状況を監視し、異常を検出した場合、該当するnodeを削除し、新しいnodeを再作成するという機能を提供します。但し、Machine API を前提した機能となるため、Machine API の機能群が動作することが前提となります。(例えば、UPIで構築した環境では、Machine API の機能が制限されているため、Machine Health Check についても同様に制限があるものと考えられます。) 尚、バージョン4.2 時点、MHCは、Technical Preview です。商用環境での使用は推奨していない機能となります。 注意事項/制限事項 をご理解いただいたうえで利用をご検討ください。 Machine Health Check の有効化 現在、クラスタ構築後の初期状態では、MHCは、無効化されています。手動で有効化する方法を紹介します。 FeatureGate の有効化 注意 :この操作がOpenShift 環境に大きな影響となる場合があります。 FeatureGateが有効な場合、OpenShift環境のアップグレードを行えなくなります。また、FeatureGate を有効にした後、再度無効化は行なえません。このため、この操作を行う環境は、バージョンアップが必要となった場合や、致命的な不具合が発生した場合に、破棄または、再構築することが許容される環境でのみ実施してください。 FeatureGateの有効化は、Webconsole から行えます。 Step1:Custom Resource Definitions の表示 WebConsole のAdministrationメニュー下にあるCustom Resource Definition 選択し、フィルター等を使用して、"FeatureGate" CRD を特定、リンクをクリックします。 CRD一覧 Step2:Feature Gate のインスタンス一覧 Feature Gate CRD の詳細画面にて、右上の"Actions"ドロップダウンリストより、"View Instances" を選択します。 Feature Gate CRD の詳細 Step3:Feature Gateを作成する。 "Create Feature Gate"ボタンをクリックし、作成パラメータの編集画面を表示します。 FeatureGateの作成 テキストフィールドに以下の内容を入力します。 apiVersion: config.openshift.io/v1 kind: FeatureGate metadata: name: cluster-tp spec: featureSet: "TechPreviewNoUpgrade" 入力したら、"Create"ボタンをクリックします。 パラメータの入力と作成 Machine Health Check リソースの作成 リソース作成のため、定義ファイルを用意します。(machine-health-check.yaml) 定義ファイルには、以下のパラメータが必要となります。 クラスタ名:cluster マシンセット名:cluster-worker-ap-southeast-1a apiVersion: healthchecking.openshift.io/v1alpha1 kind: MachineHealthCheck metadata: name: sample-machine-health-check namespace: openshift-machine-api Spec: Selector: matchLabels: machine.openshift.io/cluster-api-cluster: cluster machine.openshift.io/cluster-api-machine-role: worker machine.openshift.io/cluster-api-machine-type: worker machine.openshift.io/cluster-api-machineset: cluster-worker-ap-southeast-1a oc apply コマンドより、リソースを作成します。 $ oc apply -f machine-health-check.yaml machinehealthcheck.healthchecking.openshift.io/sample-machine-health-check created $ oc get MachineHealthCheck NAME AGE sample-machine-health-check 3s Machine Health Check は、デフォルトの状態ではNotReady状態のみを認識し、5分継続した場合に障害と断定し、削除する動作となるようです。 この障害の判断基準を変更する場合は、コンフィグマップ"node-unhealthy-conditions"を作成してカスタマイズを行います。 以下の例(mc-configmap.yaml)では、5分間、通信が途絶えた場合に障害と判断します。 apiVersion: v1 kind: ConfigMap metadata: name: node-unhealthy-conditions namespace: openshift-machine-api data: conditions: | items: - name: NetworkUnavailable timeout: 5m status: True oc apply コマンドより、コンフィグマップを作成します。 $ oc apply -f mc-configmap.yaml configmap/node-unhealthy-conditions created $ oc get configmap NAME DATA AGE cluster-autoscaler-operator-ca 1 32h cluster-autoscaler-operator-leader 0 32h machine-api-operator 0 32h machine-api-operator-images 1 32h node-unhealthy-conditions 1 57s $ おわりに 繰り返しとなりますが、MHCは、Tech Previewなので、残念ながら商用環境を対象として本格的に使用いただくことはできませんが、この機能がGAとなれば、node に何らかサービスを継続できない障害が発生した場合に、自動的にnodeの復旧が行われるようになります。つまり、OpenShift クラスタ可用性の向上が期待される魅力的な機能ではないかと思います。 一方で、動作としては障害が検出されたnode は、自動的に削除されていまうため、障害の根本原因の追求には都合が悪い面も考えられます。また、node が復旧した場合、障害が発生したnodeで稼働していたpodや、影響をうけうるサービスの状態については個別に観察する必要があるようです。 このため、サービスまで含めた運用設計にこれらの振舞を加味する必要がありそうです。
この投稿はAnsible 2 Advent Calendar 2019の21日目の記事です。 qiita.com レッドハットの杉村です。Ansible のテクニカルサポートエンジニアをしています。 今回の記事では、サポートがよく見るドキュメントについて紹介してみます。 Ansible のドキュメントは、基本的には全て https://docs.ansible.com/ からたどれるようになっています。日本語訳もありますが、翻訳の都合上どうしても遅れてしまいますので、英語の原文を参照するようにしましょう。 Ansible Engine Ansibleのインストール Ansible のインストールの仕方は多岐に及びます。通常は RHEL にインストールすることを想定していますので、リポジトリを設定して yum/dnf コマンドでインストールしていただくという流れになります。 https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html Python で書かれていますので pip でもインストールできますが、あまりお勧めはしていません。RHEL 以外の OS にインストールしたい場合の方法もいろいろと記載がありますので、参考になると思います。 モジュールの種別 次に紹介したいのはモジュールのドキュメントについてです。 Ansible のモジュールにはさまざまなものがありますが、サポートの観点からは4つに分類されます。 https://docs.ansible.com/ansible/latest/user_guide/modules_support.html 1) Core 2) Network 3) Certified 4) Community 1) Core モジュールは Engineering Team がメンテナンスをしています。サポートチームで解決できない場合は、Engineering Team と議論して対応することになります。 2) Network モジュールは Networking Team がメンテナンスをしています。Core モジュールと同じようにサポート対象となっています。 3) Certified モジュールはパートナーとなっていただいているサードパーティの会社によってメンテナンスされているものです。弊社から各ベンダへの問い合わせをすることもあります。 4) Community モジュールはコミュニティでメンテナンスされているものです。このモジュールについては、弊社はサポートを提供しません。コミュニティの皆さんに聞いてくださいという誘導をします。 これらのモジュールの種別については、各モジュールのドキュメントの一番下に、例えばこのように記載されるようになっています。 This module is maintained by the Ansible Core Team. [core] Windows Guides Windows に関する問い合わせもよくいただきます。こちらにかなり詳しくまとまっていますので、まずはここを見るようにしています。 https://docs.ansible.com/ansible/latest/user_guide/windows.html Kerberos 認証による接続や、Windows ドメインの扱いなどの問い合わせが多いように思います。 Ansible Tower Towerのバージョンアップ Ansible Tower は Engine と同じように頻繁にバージョンアップされるプロダクトなので、バージョンアップについての問い合わせがかなり多くなっています。 https://docs.ansible.com/ansible-tower/latest/html/installandreference/upgrade_tower.html インストールできる構成もシングル構成からクラスタ構成、Isolated Host の構成など、さまざまなものがありますので、状況をヒアリングして同等の構成を手元で作成して検証の上で回答するよう心がけています。 virtual environment Tower には複数の Ansible Engine の環境を使える仕組みが整っており、Python の virtual environment で実現されています。 https://docs.ansible.com/ansible-tower/latest/html/upgrade-migration-guide/virtualenv.html 例えば 2.7/2.8/2.9 を共存して順次移行しながら使いたいというときにはおすすめの機能です。 特に Python 2 が2019年いっぱいで EOL (End Of Life) となるということもあり、Python 3 への移行についての問い合わせもよくいただきます。RHEL でお使いいただく限りにおいては、特に気にせず来年からも Python 2 でお使いいただけます。 リリースノート Tower のリリースノートはこちらです。トラブルをご相談いただいたときには、バージョンアップして直るものなのかどうかをまず調べます。 https://docs.ansible.com/ansible-tower/latest/html/release-notes/relnotes.html 基本的にはバージョンアップして使えなくなるという機能はありませんので、お気軽にバージョンアップしてほしいと考えています。 その他のドキュメント GitHub の issue 既存のドキュメントや手元での検証では解決できない場合、Engineering Team や Networking Team と議論するために GitHub の issue を上げることがあります。public なものと private なものとそれぞれあります。みなさんにも見ていただけるものは ansible/ansible と ansible/awx です。AWX は Tower の Upstream プロジェクトです。 https://github.com/ansible/ansible https://github.com/ansible/awx KCS (Knowledge Centered Service) 過去の事例から よくあるトラブルと回答を集めたようなナレッジベースが弊社にもあります。サブスクリプションをご購入いただいていれば読めるようになっています。 テクニカルサポートの普段の業務の一つにもなっており、問い合わせへの回答をして再利用できそうなものがあれば、ドキュメントにまとめて今後の役に立てるようにするという活動をしています。特に他のサービスやプロダクトとの連携部分については Ansible 本体のドキュメントにないものや詳しく書かれていないものもありますので、積極的にまとめるようにしています。ドキュメントを自分で書くとその分野にも詳しくなります。 わたしが書いたものをいくつか紹介してみます。もしアカウントがあって見れる方はご笑覧ください。 How Do I Connect to a Windows Server with Ansible Engine Using Kerberos? https://access.redhat.com/solutions/3713661 How To Configure SAML Authentication with Azure AD? https://access.redhat.com/solutions/3889291 How Do I Create a HAR File To Capture Diagnostic Information for Ansible Tower in Chrome or Firefox? https://access.redhat.com/solutions/4045281 おわりに 他の方のテクニカルなネタとは少々毛色の違う投稿をしてみました。 Ansible のサポートではプレイブックはほとんど書かないというのが実際のところで、皆さんの投稿のように直接的に役に立つようなことはうまくまとめられないというのもありました。Ansible に関わるようになって1年半近くになりましたが、幅広くいろいろなことを身に付けたなと思っています。これからもお役に立てるよう頑張ります。 Happy Automation!
追記: こちらの記事は執筆時点(2019/12/20)での Quarkus 最新バージョンであった 1.0.1.Final の動作を元に記載されていました。しかし、その後に以下の修正によって Quarkus 1.1.0.Final 以降からは、Quarkus RESTEasy では Vert.x スレッドプールは使われなくなり、Servlet 依存の有無によらず quarkus.thread-pool.max-threads で設定される main のスレッドプールが利用されるように動作が変更されました。 github.com Red Hat で JBoss ミドルウェアのサポートをしている三浦です。この記事は 赤帽エンジニア Advent Calendar 2019 の20日目のエントリです。 Quarkus の HTTP レイヤーのスレッドプールについて書きたいと思います。 TL; DR Quarkus の HTTP レイヤーは Vert.x ベースになっている Vert.x 関連スレッドプールの設定は quarkus.vertx.event-loops-pool-size / quarkus.vertx.worker-pool-size プロパティで調整可能 依存関係に Servlet があると、Undertow を利用する実装にフォールバックして、 quarkus.thread-pool.max-threads で設定される別スレッドプール が利用される プロパティの設定および確認には quarkus:generate-config が便利 Quarkus の HTTP レイヤーの実装 Quarkus の HTTP レイヤーの実装は、2019/12/20 現時点で最新の 1.0.1 になるまでにバージョンによって以下のような変遷をとげてきています。 0.22 まで: JAX-RS/Servlet アプリケーションなどのHTTPリクエスト処理は Undertow/XNIO を経由して行われていた (Reactive系のアプリケーションで明示的に Vert.x を使った場合を除く) 0.23 以降: すべてのHTTPリクエスト処理は Vert.x を経由するように変更された さらに 0.24 以降で、JAX-RS アプリケーションにおいて Servlet API が不要であれば、Undertow を経由することなく直接 Vert.x 上で処理されるように変更された JAX-RS アプリケーションで Servlet API を利用したい場合には、依存ライブラリとして quarkus-undertow を追加することで可能です。依存関係に quarkus-undertow があることが検知された場合には、Quarkus は自動的に JAX-RS 実装である RESTEasy が Servlet 上で動くようにフォールバックする挙動になっています。 なお、この Quarkus における Servlet 実装は Undertow ベースのままですが、実態は Undertow 3.x となる予定だったものからフォークした Vert.x based Undertow となっています。 Quarkus/JAX-RSアプリケーションで確認する まずは、 https://code.quarkus.io/ で "RESTEasy JAX-RS" のみを選択してサンプルの maven プロジェクトを生成して動かしてみましょう。 code-with-quarkus.zip が生成されるので、それを展開して以下のコマンドでコンパイルして Hot Reloading が有効な Quarkus Dev モードで動かします。 $ ./mvnw compile quarkus:dev サンプルプロジェクトの src/main/java/org/acme/ExampleResource.java の hello() メソッドにスレッド名や呼び出しのスタックトレースを出力するログを追加します。 ... import org.jboss.logging.Logger; @Path( "/hello" ) public class ExampleResource { Logger logger = Logger.getLogger(getClass()); @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { logger.infof( new Throwable(), "ExampleResource#hello() is invoked : thread name [%s], thread class name [%s]" , Thread.currentThread().getName(), Thread.currentThread().getClass().getName()); return "hello" ; } } これで http://localhost:8080/hello にアクセスしてみると、ログから以下のように vert.x-worker-thread-N というブロッキングな処理を行うための worker スレッドプール上で実行されていることがわかります。 2019-12-01 17:39:29,744 INFO [org.acm.ExampleResource] (vert.x-worker-thread-1) ExampleResource#hello() is invoked : thread name [vert.x-worker-thread-1], thread class name [io.vertx.core.impl.VertxThread]: java.lang.Throwable at org.acme.ExampleResource.hello(ExampleResource.java:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:151) at org.jboss.resteasy.core.MethodInjectorImpl.lambda$invoke$3(MethodInjectorImpl.java:122) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616) at java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:628) at java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:1996) at java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:110) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:122) at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:594) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:468) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:421) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:423) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:391) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invoke$1(ResourceMethodInvoker.java:365) at java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:995) at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2137) at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:110) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:365) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:477) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:252) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:153) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363) at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:156) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:238) at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:118) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.lambda$handle$0(VertxRequestHandler.java:74) at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$2(ContextImpl.java:316) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) Quarkus のソースコードを確認すると、 io.quarkus.resteasy.runtime.standalone.VertxRequestHandler#handle() にて Vertx#executeBlocking() が呼び出されて、ブロッキングな処理を行うスレッドである vert.x-worker-thread-N に処理が渡されていることがわかります。 Vert.x関連スレッドプールとハングスレッド検知機能 この時点でスレッドダンプをとってみると、Vert.x 関連スレッドとして以下のようなスレッドが存在することがわかります。 "vert.x-worker-thread-1" #50 prio=5 os_prio=0 tid=0x00007f0e20186000 nid=0x7945 waiting on condition [0x00007f0e66aed000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000068d73f5b0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) "vert.x-internal-blocking-0" #49 prio=5 os_prio=0 tid=0x00007f0e20185800 nid=0x7944 waiting on condition [0x00007f0e676f9000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000068d73f890> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) "vert.x-worker-thread-0" #47 prio=5 os_prio=0 tid=0x00007f0e20162800 nid=0x7942 waiting on condition [0x00007f0e671f4000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000068d73f5b0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) ... "vert.x-acceptor-thread-0" #44 prio=5 os_prio=0 tid=0x00007f0e200e5800 nid=0x783e runnable [0x00007f0e679fc000] java.lang.Thread.State: RUNNABLE at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method) at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269) at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93) at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) - locked <0x000000068d63ad38> (a io.netty.channel.nio.SelectedSelectionKeySet) - locked <0x000000068d63be38> (a java.util.Collections$UnmodifiableSet) - locked <0x000000068d63bd60> (a sun.nio.ch.EPollSelectorImpl) at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62) at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:824) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:457) at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) ... "vert.x-eventloop-thread-0" #36 prio=5 os_prio=0 tid=0x00007f0e908ca800 nid=0x7836 runnable [0x00007f0e674f7000] java.lang.Thread.State: RUNNABLE at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method) at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269) at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93) at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) - locked <0x000000068d78ba98> (a io.netty.channel.nio.SelectedSelectionKeySet) - locked <0x000000068d78cba8> (a java.util.Collections$UnmodifiableSet) - locked <0x000000068d78cac0> (a sun.nio.ch.EPollSelectorImpl) at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62) at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:824) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:457) at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) それぞれ大雑把に書くと、以下のような役割で動くスレッドです。 vert.x-acceptor-thread : TCP接続を受け付けるスレッド vert.x-eventloop-thread : ノンブロッキングIOを処理する EventLoop スレッド vert.x-worker-thread : ブロッキングな処理を行う Worker スレッド。 executeBlocking() を呼び出すことでディスパッチされる。 vert.x-internal-blocking : 通常のアプリで利用されるものではなく、内部で行われるブロッキングな処理(ファイルシステムからの読み込み処理など)を行うためのスレッド vert.x-eventloop-thread 、 vert.x-worker-thread および vert.x-internal-blocking のプールサイズは application.properties で以下のようなプロパティにて設定できます。 quarkus.vertx.event-loops-pool-size : vert.x-eventloop-thread のプールサイズ設定。デフォルトでは CPUコア数 x 2 。 quarkus.vertx.worker-pool-size : vert.x-worker-thread のプールサイズ設定。デフォルトでは 20 。 quarkus.vertx.internal-blocking-pool-size : vert.x-internal-blocking のプールサイズ設定。デフォルトでは 20 。 なお、 application.properties に設定できるパラメータは Quarkus ドキュメントの All Config ページ で確認できます。ただ、現時点のプロジェクトで関連する設定パラメータは quarkus:generate-config を実行することで src/main/resources/application.properties.example を生成でき、その application.properties.example の中にコメントアウトされた形で関連する設定パラメータが入っています。 そのため、これを application.properties にリネームして利用するか、適宜必要なものをコピーして使う方法が簡単だと思います。 $ ./mvnw quarkus:generate-config 生成された application.properties.example には、たとえば以下のように説明付きでリストされます。 # # The number of event loops. 2 x the number of core by default. # #quarkus.vertx.event-loops-pool-size= ... # # The size of the internal thread pool (used for the file system). # #quarkus.vertx.internal-blocking-pool-size=20 ... # # The size of the worker thread pool. # #quarkus.vertx.worker-pool-size=20 application.properties.example を見ていると、 quarkus.vertx.max-worker-execute-time と quarkus.vertx.warning-exception-time といったパラメータがあることに気づきます。これは worker スレッドで処理に指定時間以上かかっているものをWARNログで出力するという機能に関連するパラメータです。なお、 quarkus.vertx.max-worker-execute-time のコメントではデフォルト10s(=10秒)となっていますが、実際には60s(=60秒)です。 # # The maximum amount of time the worker thread can be blocked. # Default is 10s. # #quarkus.vertx.max-worker-execute-time= # # The amount of time before a warning is displayed if the event loop is blocked. # #quarkus.vertx.warning-exception-time=2 この機能を実際に試してみましょう。 application.properties には以下のように 5s (=5秒) と指定してみます。なお、現時点で確認する限りは、 quarkus.vertx.max-worker-execute-time などの変更は Hot Reloading が効かなかったので、一度停止してから起動しなおす必要があります。 quarkus.vertx.max-worker-execute-time=5s src/main/java/org/acme/ExampleResource.java を以下のように10秒スリープするコードをいれます。 ... import java.util.concurrent.TimeUnit; import org.jboss.logging.Logger; @Path( "/hello" ) public class ExampleResource { Logger logger = Logger.getLogger(getClass()); @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { logger.infof( new Throwable(), "ExampleResource#hello() is invoked : thread name [%s], thread class name [%s]" , Thread.currentThread().getName(), Thread.currentThread().getClass().getName()); try { long duration = 10L ; logger.info( "sleeping " + duration + " seconds..." ); TimeUnit.SECONDS.sleep(duration); logger.info( "done!" ); } catch (InterruptedException ignore) {} return "hello" ; } } これで http://localhost:8080/hello にアクセスしてみると、 max-worker-execute-time 設定値の5秒経過後に、ハングしているスレッド名、何秒ハングしているかがスタックトレースとともに WARN ログに出力されることが確認できます。その後も引き続きスレッドのハングが継続していた場合には、 max-worker-execute-time の2秒(デフォルト値の)を経過するごとに出力されます。 2019-12-01 21:12:27,781 INFO [org.acm.ExampleResource] (vert.x-worker-thread-1) sleeping 10 seconds 2019-12-01 21:12:33,617 WARNING [io.ver.cor.imp.BlockedThreadChecker] (vertx-blocked-thread-checker) Thread Thread[vert.x-worker-thread-1,5,main]=Thread[vert.x-worker-thread-1,5,main] has been blocked for 5857 ms, time limit is 5000 ms: io.vertx.core.VertxException: Thread blocked at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at org.acme.ExampleResource.hello(ExampleResource.java:30) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:151) at org.jboss.resteasy.core.MethodInjectorImpl.lambda$invoke$3(MethodInjectorImpl.java:122) at org.jboss.resteasy.core.MethodInjectorImpl$$Lambda$316/1812743798.apply(Unknown Source) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616) at java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:628) at java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:1996) at java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:110) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:122) at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:594) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:468) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:421) at org.jboss.resteasy.core.ResourceMethodInvoker$$Lambda$315/408648673.get(Unknown Source) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:423) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:391) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invoke$1(ResourceMethodInvoker.java:365) at org.jboss.resteasy.core.ResourceMethodInvoker$$Lambda$314/12574409.apply(Unknown Source) at java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:995) at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2137) at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:110) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:365) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:477) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:252) at org.jboss.resteasy.core.SynchronousDispatcher$$Lambda$310/1423111402.run(Unknown Source) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:153) at org.jboss.resteasy.core.SynchronousDispatcher$$Lambda$311/905682736.get(Unknown Source) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363) at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:156) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:238) at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:118) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.lambda$handle$0(VertxRequestHandler.java:74) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$$Lambda$308/650568468.handle(Unknown Source) at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$2(ContextImpl.java:316) at io.vertx.core.impl.ContextImpl$$Lambda$302/216113056.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:748) 2019-12-01 21:12:35,617 WARNING [io.ver.cor.imp.BlockedThreadChecker] (vertx-blocked-thread-checker) Thread Thread[vert.x-worker-thread-1,5,main]=Thread[vert.x-worker-thread-1,5,main] has been blocked for 7858 ms, time limit is 5000 ms: io.vertx.core.VertxException: Thread blocked at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at org.acme.ExampleResource.hello(ExampleResource.java:30) ...(上と同じなので省略)... 2019-12-01 21:12:37,617 WARNING [io.ver.cor.imp.BlockedThreadChecker] (vertx-blocked-thread-checker) Thread Thread[vert.x-worker-thread-1,5,main]=Thread[vert.x-worker-thread-1,5,main] has been blocked for 9858 ms, time limit is 5000 ms: io.vertx.core.VertxException: Thread blocked at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at org.acme.ExampleResource.hello(ExampleResource.java:30) ...(上と同じなので省略)... 2019-12-01 21:12:37,781 INFO [org.acm.ExampleResource] (vert.x-worker-thread-1) done! Quarkus/JAX-RS アプリケーションで Servlet 依存を追加したときにどう変わる? 前述したように、依存関係に quarkus-undertow を追加することで、自動的に Servlet API を利用すると認識され、Vert.x based Undertow 上で処理されるようになります。この場合の動作も確認してみます。 既存プロジェクトへの quarkus-undertow 依存の追加は quarkus:add-extension で以下のように実行すればできます。 $ ./mvnw quarkus:add-extension -Dextensions=quarkus-undertow これで pom.xml に quarkus-undertow への依存が追加されます。 <dependencies> <dependency> <groupId> io.quarkus </groupId> <artifactId> quarkus-resteasy </artifactId> </dependency> <dependency> <groupId> io.quarkus </groupId> <artifactId> quarkus-junit5 </artifactId> <scope> test </scope> </dependency> <dependency> <groupId> io.rest-assured </groupId> <artifactId> rest-assured </artifactId> <scope> test </scope> </dependency> <!-- 以下の依存が追加される --> <dependency> <groupId> io.quarkus </groupId> <artifactId> quarkus-undertow </artifactId> </dependency> </dependencies> これで起動してアクセスしてみます。依存を追加した以外、コードや設定は上で書いたもののままです。 2019-12-01 22:51:07,912 INFO [org.acm.ExampleResource] (executor-thread-1) ExampleResource#hello() is invoked : thread name [executor-thread-1], thread class name [org.jboss.threads.JBossThread]: java.lang.Throwable at org.acme.ExampleResource.hello(ExampleResource.java:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:151) at org.jboss.resteasy.core.MethodInjectorImpl.lambda$invoke$3(MethodInjectorImpl.java:122) at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616) at java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:628) at java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:1996) at java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:110) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:122) at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:594) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:468) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:421) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:423) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:391) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invoke$1(ResourceMethodInvoker.java:365) at java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:995) at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2137) at java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:110) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:365) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:477) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:252) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:153) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363) at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:156) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:238) at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:249) at io.quarkus.resteasy.runtime.ResteasyFilter$ResteasyResponseWrapper.sendError(ResteasyFilter.java:65) at io.undertow.servlet.handlers.DefaultServlet.doGet(DefaultServlet.java:172) at javax.servlet.http.HttpServlet.service(HttpServlet.java:503) at javax.servlet.http.HttpServlet.service(HttpServlet.java:590) at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129) at io.quarkus.resteasy.runtime.ResteasyFilter.doFilter(ResteasyFilter.java:28) at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:63) at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:133) at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:65) at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50) at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:270) at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:59) at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:116) at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:113) at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$9$1$1.call(UndertowDeploymentRecorder.java:475) at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:250) at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:59) at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:82) at io.undertow.server.Connectors.executeRootHandler(Connectors.java:290) at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:669) at io.quarkus.runtime.CleanableExecutor$CleaningRunnable.run(CleanableExecutor.java:224) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1395) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29) at java.lang.Thread.run(Thread.java:748) at org.jboss.threads.JBossThread.run(JBossThread.java:479) 2019-12-01 22:51:07,913 INFO [org.acm.ExampleResource] (executor-thread-1) sleeping 10 seconds 2019-12-01 22:51:17,913 INFO [org.acm.ExampleResource] (executor-thread-1) done! スタックトレースから Undertow 上で実行され、また、スレッド名も vert.x-worker-thread-N ではなく executor-thread-N という別なスレッドプールが利用されるように変わっていることが確認できます。また、Vert.x の worker スレッド上ではないため、 max-worker-execute-time 設定による WARN ログも出なくなったことがわかります。 このスレッドプールは、Vert.x 関連とは異なる以下のプロパティで設定できるものが利用されています。依存関係の違いでアプリケーション処理で利用されるスレッドプールが変わるというのはチューニングする際に落とし穴になりそうですね。 quarkus.thread-pool.max-threads : スレッドプールの最大サイズ。デフォルトでは CPUコア数 x 8 。 quarkus.thread-pool.core-threads : スレッドプールの最小サイズ。デフォルトでは 1 。 quarkus.thread-pool.keep-alive-time : 使用されていないスレッドを保持しておく時間(単位は秒)。この時間を経過したスレッドは最小サイズまで破棄される。デフォルトは 30 (秒)。 参考リンク Quarkus - Guides All Config https://quarkus.io/guides/all-config Start coding with code.quarkus.io Vert.x Core Manual
Red Hatでコンサルタントをしている織です。この記事は OpenShift Advent Calendar 2019 19日目のエントリです。 OpenShiftはRed Hatが製品として出しているKubernetesのディストリビューションです。Kubernetesでは、コンテナのネットワークインターフェースを設定する仕組みをCNIという仕様/ライブラリとして定義しており、様々なCNIプラグインを差し替えて使うことができるようになっています。OpenShiftは、「OpenShift-SDN」という独自実装のCNIプラグインを提供しており、デフォルト設定ではこれが使われるのですが、OpenShift v4.2から、OpenShift-SDNに加えて「OVN(Open Virtual Networking)」という仕組みを使った「ovn-kubernetes」というCNIプラグインもTechnical Previewとして提供しています。本エントリでは、OVNをOpenShiftで使うとどういったいいことがあるかについてご紹介します。 OVNとは OVNは、複数の物理ノード(もしくはハイパーバイザ)にまたがった仮想ネットワークを作るための仕組みで、Open vSwitch (以下OVS) [ 1 ]というオープンソースの仮想スイッチのサブプロジェクトとして2015に開発が始まりました。最初はOVSのリポジトリ内で開発が進められていましたが、2019年2月にリリースされたOVS v2.11からリポジトリがわかれ、今は https://github.com/ovn-org で開発が続けられています。 OVNの特徴としては、 OVSDBというデータベースの操作によるコンフィギュレーション 複数のハイパーバイザにまたがった仮想ネットワーク「Logical Network」を「Logical Flow」で定義 Logical FlowはOpen Flowに似た構造になっており、フローの集合であるフローテーブルをつなげてパイプライン化し、入ってきたパケットがmatchするフローがあれば対応するactionを実施する、という形でパケットが処理されます ノード間の通信はGeneveを使ってカプセル化 といった辺りが挙げられます。 OVNをKubernetesで使うためのCNIプラグインとしてovn-kubernetes [ 2 ] があります。このovn-kubernetesがOpenShift v4.2から使えるようになった、というわけです。 OVNのコンポーネントは下図のようになります。 OVNのコンポーネント OpenShiftとOVNはNorthbound DBというデータベースを経由して情報をやり取りし、ここにLogical Networkの情報が格納されます。 ovn-northdというデーモンがNorthbound DBにある論理情報を各ノードの物理情報と対応付け、Southbound DBに格納します。 各ノードで動いているovn-controllerというデーモンがSouthbound DBの情報を元に、自ノードのOVSに注入するOpenFlowのフローを生成します。 OpenShift + OVNのセットアップ OpenShift + OVNの環境を作る手順は下記のとおりです。 openshift-intall create install-config を実行して install-config.yaml を生成する install-config.yamlの networking.networkType の値を OVNKubernetes に変更する openshift-install create cluster を実行してクラスターをデプロイする 詳細はドキュメント[ 3 ]をご参照ください。 OpenShiftでOVNを使う場合、openshift-ovn-kubernetes という namespace に ovn-kubernetes 関連のPodがデプロイされ [ 4 ]、これらがOVNの制御を行います。 $ oc -n openshift-ovn-kubernetes get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ovnkube-master-7bdf6459dd-t6p27 4/4 Running 0 53d 10.0.147.198 ip-10-0-147-198.ap-northeast-1.compute.internal <none> <none> ovnkube-node-5fsgb 3/3 Running 0 53d 10.0.160.241 ip-10-0-160-241.ap-northeast-1.compute.internal <none> <none> ovnkube-node-9bb84 3/3 Running 0 53d 10.0.164.184 ip-10-0-164-184.ap-northeast-1.compute.internal <none> <none> ovnkube-node-jbjjw 3/3 Running 0 53d 10.0.142.114 ip-10-0-142-114.ap-northeast-1.compute.internal <none> <none> ovnkube-node-nf5jc 3/3 Running 0 53d 10.0.131.146 ip-10-0-131-146.ap-northeast-1.compute.internal <none> <none> ovnkube-node-vlvwh 3/3 Running 0 53d 10.0.147.198 ip-10-0-147-198.ap-northeast-1.compute.internal <none> <none> ovnkube-node-xtht2 3/3 Running 0 53d 10.0.146.63 ip-10-0-146-63.ap-northeast-1.compute.internal <none> <none> OpenShift/KubernetesでOVNを使うとうれしい点 OpenShift/KubernetesでOVNを使うとうれしい点がいくつかあります。 大きな特徴としては、iptablesの利用を大幅に減らすことができる点が挙げられます。 KubernetesのServiceオブジェクトによるエンドポイント(Pod)間のロードバランスは、多くのCNIプラグインではiptablesのstatisticモジュールのrandomモードを使って実現しています。 iptablesはルール数が増えた場合のスケーラビリティに課題があることが知られており、Serviceオブジェクトが増えるとその分iptablesのルールが増えることになり、パフォーマンス上の懸念がありました。 一方OVNの場合は、ServiceオブジェクトはLogical Network上の分散ロードバランサとして表現されます。最終的にOVSのFlowとして処理されるため、iptablesのルールが増えることはありません。 また、Network Policyの機能も多くのCNIプラグインではiptablesで実現していますが、OVNを使った場合はOVSのFlowとして処理するため、iptablesのルールが増えることはありません。 このように、OVNではなるべくiptablesを使わないように設計されています [ 5 ]。 その他、OVNを使って、WindowsノードとLinuxノードの混在クラスターのサポートやPod単位のEgress IP、複数クラスターにまたがった通信などを実現しようとしています。 OVNの課題と今後 今のOVNはOVSDBに大きく依存しており、OVSDBが性能上のボトルネックになるケースがいくつかありました。これらに関してはすでに修正が入っており、今のupstreamのコードはスケーラビリティがかなり改善されています。 また、OpenShift v4.2でOVNを使う場合、OVSDBのPod(ovnkube-master)が冗長化されておらず、単一障害点となります。 upstreamではRaftアルゴリズムを使ったOVSDBのActive-Active構成の実装中で[ 6 ]、その成果を取り込んで冗長化する方向で開発を進めています。 OpenShiftのロードマップとしては、次かその次くらいのバージョンでOVNを正式にサポートしようと頑張っているところです。OpenShiftとOVNの今後にご期待ください。 https://www.openvswitch.org/ ↩ https://github.com/ovn-org/ovn-kubernetes ↩ Installing a cluster on AWS with network customizations ↩ OVNではなくOpenShiftデフォルトのOpenShift-SDNというCNIプラグインを使う場合は、openshift-sdnというnamespaceに関連するPodがデプロイされます。 ↩ OVNはiptablesを全く使わないわけではなく、例えばNodePortやExternalIPを使ってクラスター外部からPodに対して通信する場合は、ホスト上のiptablesでDNATしてOVNのLogical Networkにパケットを引き込みます。 ↩ OVSDBの冗長化方式としては、Raftベースのクラスタリングの他に、Master-Slave形式のレプリケーションの機能もあります。Red HatのOpenStackディストリビューションでOVNを使う場合のOVSDBは、Master-Slaveのレプリケーションを使った構成で、Pacemakerで冗長化しています。 ↩
この記事は、『[Ansible 2 Advent Calendar]』( https://qiita.com/advent-calendar/2019/ansible2 )18日目の記事です。 qiita.com こんにちは、Red HatでAnsibleのテクニカルサポートエンジニアをしている八木澤(ひよこ大佐)です。 今回は、Ansible Towerの最新版であるAnsible Tower 3.6.2のノードを、人気の監視ツール「Prometheus」から監視する手順をご紹介します。 ※下記手順は、基本的に公式ドキュメントの以下のページの手順に基づきます。 docs.ansible.com Prometheusとは? OSSとして公開されている監視ソフトウェアです。詳細については、以下の公式サイトを確認してください。 prometheus.io 検証環境 さっそく、検証用に以下のような環境を構築します。今回はどちらの仮想マシンもCentOS 7を利用しています。 図1: 検証環境の概要 Vagrantを利用し2台の仮想マシンを生成します、1台はAnsible Towerをセットアップ、もう1台にPrometheusを構築します。 Ansible Towerノードでの操作 本記事では、Ansible Towerのセットアップ作業については割愛させていただきます。Ansible Towerのインストレーションについては、 公式ドキュメント を参照してください。 docs.ansible.com Tokenの取得 Prometheusが情報を取得するためには、Prometheusが情報取得に利用するユーザーのTokenを取得する必要があります。以下のAPIエンドポイントにアクセスし、「POST」をクリックすることで新しいTokenが発行されます。 https://<TOWERのURL>/api/v2/users/<ユーザーのID>/tokens 図2: Tokenの取得 Prometheusノードでの操作 Prometheusのセットアップは、Dockerなどのコンテナ上に構築する場合と、通常のアプリケーションとして構築する2通りの方法があります。 今回は、通常のアプリケーションとしてセットアップします。 Prometheusのダウンロード 以下のコマンドを実行し、PrometheusのパッケージをGitHubからダウンロードします。 # wget https://github.com/prometheus/prometheus/releases/download/v2.14.0/prometheus-2.14.0.linux-amd64.tar.gz ダウンロードしたら、tarコマンドでアーカイブを展開します。 # tar xvf prometheus-2.14.0.linux-amd64.tar.gz prometheus-2.14.0.linux-amd64/ prometheus-2.14.0.linux-amd64/NOTICE prometheus-2.14.0.linux-amd64/prometheus prometheus-2.14.0.linux-amd64/LICENSE prometheus-2.14.0.linux-amd64/prometheus.yml prometheus-2.14.0.linux-amd64/console_libraries/ prometheus-2.14.0.linux-amd64/console_libraries/prom.lib prometheus-2.14.0.linux-amd64/console_libraries/menu.lib prometheus-2.14.0.linux-amd64/consoles/ prometheus-2.14.0.linux-amd64/consoles/prometheus.html prometheus-2.14.0.linux-amd64/consoles/node-overview.html prometheus-2.14.0.linux-amd64/consoles/node-cpu.html prometheus-2.14.0.linux-amd64/consoles/prometheus-overview.html prometheus-2.14.0.linux-amd64/consoles/node.html prometheus-2.14.0.linux-amd64/consoles/index.html.example prometheus-2.14.0.linux-amd64/consoles/node-disk.html prometheus-2.14.0.linux-amd64/tsdb prometheus-2.14.0.linux-amd64/promtool prometheus.ymlの作成 展開したPrometheusのディレクトリに移動すると、prometheus.ymlという設定ファイルがあります。ここに、PrometheusがどのようにAnsible Towerの情報を取得するかを定義します。 Ansible Towerには「/api/v2/metrics」というAPIエンドポイントがあり、ここに出力される情報をPrometheusによる監視に利用することができます。 Prometheusディレクトリ内のprometheus.ymlを開き、「scrape_configs」のセクションを検索し、以下の記述を追記します。 - job_name: 'tower' tls_config: insecure_skip_verify: True metrics_path: /api/v2/metrics scrape_interval: 5s scheme: https bearer_token: <確認したトークンの文字列> # basic_auth: # username: admin # password: password static_configs: - targets: - 192.168.100.10 Prometheusの起動 上記を追記したら、以下のコマンドを実行して、Prometheusを起動します。「nohup」を利用してバックグラウンドで起動し、出力をすべて「prometheus.log」に出力させるようにしています。 # nohup ./prometheus --config.file=prometheus.yml > prometheus.log 2>&1 & 起動したら、早速アクセスできるか確認してみましょう。この検証環境では、以下のURLにアクセスすることでPrometheusのダッシュボードを開くことができます。 http://192.168.100.20:9090 以下の画面が表示されれば成功です、おめでとうございます! 図3: Prometheusの画面 「- insert metric at cursor」をクリックすると、様々な情報を表示させることができます。Tower関連の情報は、「awx_*」のアイテムを選択し、「Execute」をクリックすることで表示できます。 図4: Towerの情報を表示 (オプション) Grafanaもセットアップしてみる Prometheusのダッシュボードはかなりシンプルな作りなので、本格的な監視を検討するうえでは、もっと高機能なダッシュボードが欲しくなると思います。 Prometheusとあわせてよく使われるのが「Grafana」というツールです。こちらのツールと組み合わせることで、よりかっこよく高機能なダッシュボードを使って、取得した情報を整理することができます。 Grafanaのインストール では、早速インストールしてみましょう。 Prometheusを実行しているツールで以下のコマンドを実行し、rpmファイルをインストールします。 wget https://dl.grafana.com/oss/release/grafana-6.5.2-1.x86_64.rpm sudo yum localinstall grafana-6.5.2-1.x86_64.rpm リポジトリを登録してインストールすることもできます。詳細な手順についてはGrafanaの 公式ドキュメント を参照してください。 grafana.com Grafanaの起動 インストールが完了したら、systemctlコマンドでGrafanaのサービスを起動します。 # systemctl start grafana-server ではGrafanaのWeb UIにアクセスしてみましょう。以下のURLから接続することができます。 http://192.168.100.20:3000 アクセスすると、以下のようなログイン画面が表示されるので、ID/パスワードともに「admin」と入力し、ログインします。 図5: Grafanaのログイン画面 初回ログイン時は、パスワードの変更を求められます。新しいパスワードを入力し、「Save」をクリックします。 図6: パスワードの変更 Grafanaの設定 まず、GrafanaとPrometheusを連携させる必要があります。「Add data source」から、Prometheusをデータソースとして選択し、「Select」をクリックします。 図7: Data Sourcesの設定 次に、詳細な設定を入力します。「HTTP」セクションのURLにPrometheusのURLを記述し、「Save & Test」をクリックします。これで、Prometheusから情報を取得することができるようになります。 図8: Prometheusの詳細設定 最後に、実際に取得した情報を確認するDashboardを設定します。「New dashboard」をクリックし、新しくDashboardを作成します。 図9: Dashboardの作成 「New Panel」の設定で、「Add Query」をクリックします。ここでさきほどPrometheusで確認していたようなmetricsの情報をDashboardに表示させることができます。 図10: Panelの作成 では、サンプルとしてAnsible Towerで実行しているジョブと、Pendingになっているジョブの数を監視するパネルを作成してみましょう。 「Metrics」を選択し、Prometheusの時と同様に確認したい情報を選択します。ここでは、「awx_running_jobs_total」と「awx_pending_jobs_total」をグラフ形式で表示させます。 図11: パネルに表示させるMetricsの設定 この設定を保存すると、以下のように新たにDashboardが確認され、作成したパネルの内容を確認することができます。これ以外にも監視できる情報は数多くあり、Prometheus側のexporterを追加することでPostgreSQLの状態などを監視することもできますので、いろいろと試してみてください。 図12: 作成したDashboard Automation Analytics ここではPrometheus + Grafanaによる監視の一例をご紹介しましたが、Red Hatでは「 Automation Analytics 」という機能を提供しています。こちらを利用することでより詳細なAnsible Towerの監視が可能になります。 www.ansible.com Automation Analyticsについては別の機会にまた改めて記事として取り上げたいと思いますので、ぜひ利用をご検討いただければと思います。 Happy Automation & Monitoring!
本記事は 赤帽エンジニア Advent Calendar 2019 の17日目のエントリです。 はじめまして。 Red Hat でコンサルタントをしている白川です。今年6月に Red Hat に入社してから初めての投稿となります。 今回 Red Hat CodeReady Workspaces の最新バージョンである 2.0 を触ってみたので、 1.2 からの機能強化の 1 つである devfile を利用して CodeReady Workspaces で開発環境をコードとして管理する方法を紹介したいと思います。 はじめに サービスやプロダクトを開発する際に、プロジェクト、あるいはチーム間で開発環境をどう共通化し、共有するのか、という問題があると思います。 例えばチームに新しいメンバが入ってきた場合、まず環境構築手順の共有と構築作業が必要となります。 よくある方法として、 Readme などに環境構築手順を記載し、それをもとに個々の開発環境を構築することが考えられます。 この方法でも手順がプロジェクトのソースコードリポジトリに格納されていればメンバ間で共有することが可能ですし、 Readme がバージョン管理されていることで過去のリビジョンでも必要な手順を確認することが可能です。 しかし、以下のような問題は解決できていません。 手作業での環境構築に時間をとられる OSなど個々人の開発マシンの差異によって環境構築手順が異なったりしてハマる 異なるブランチでの開発や過去のリリースバージョンの修正が必要といった場合に、一度構築した環境(利用ツールのバージョンなど)を変更する必要が出てくる 上記のような問題は、 Ansible などの構成管理ツールを導入することで解決できるかもしれません。また、Dockerfile などのコンテナイメージのビルド定義ファイルを共有し、開発環境をコンテナイメージとしてパッケージングする場合は開発マシン間の差異についても解決できそうです。 これら (Ansible, コンテナイメージ化) の方法はいずれもこれまで手作業で実施していた手順をコード(Playbook, Dockerfile)化することで従来あった問題を解決しています。 OpenShift でアプリケーションの開発を行う場合、上記のような開発環境構築の問題を Red Hat CodeReady Workspaces を利用することで解決できます。 今回は OpenShift に実際に Red Hat CodeReady Workspaces をインストールし、 CodeReady Workspaces 2.0 で開発環境のコード化を実現する devfile を使用した開発環境の共有を試してみたいと思います。 Red Hat CodeReady Workspaces とは Red Hat CodeReady Workspacesは、Webベースの統合開発環境 (IDE) です。 オープンソースプロジェクトの Eclipse Che をベースとしています。 各開発者の環境 (Workspace) は OpenShift 上で Pod として実行され、ランタイム、エディタやプラグイン、ツールなどはこの Pod 内でコンテナとして実行された状態で提供されます。そのため、開発者は冒頭に書いたような環境構築の問題に煩わされることなく、 CodeReady Workspaces にアクセス可能なブラウザさえあれば OpenShift 上でのアプリケーション開発を開始できます。 また、最新バージョンである 2.0 では以下の機能強化が行われており、今回着目している devfile を使用したワークスペースのコード化も 2.0 で追加された機能です。 devfileを使用したワークスペースのコード化 OpenShift Developer Perspectiveとの統合 デフォルトのエディタとして Che-Theia IDE を使用するようになったことで VSCode の拡張機能が利用可能に ネットワーク的に制限された環境下でのインストール その他にも、以前 OpenShift Do (odo) を利用して OpenShift 上でアプリケーションを開発する で紹介した odo や oc を内部で使用して IDE 上から OpenShift の開発を容易にするプラグインが提供されていたりします。 Red Hat CodeReady Workspaces 2.0 を OpenShift にインストールする まずはRed Hat CodeReady Workspaces 2.0 を OpenShift にインストールしていきます。 今回は以下の環境で検証を行います。 OpenShift Container Platform v4.2.8 Red Hat CodeReady Workspaces は OperatorHub からインストールできる Operator を利用して簡単に OpenShift にデプロイできるようになっています。以下の手順で CodeReady Workspaces をインストールできます。 CodeReady Workspaces を管理する Operator と CodeReady Workspaces をインストールする Project を任意の名前で作成します。今回は workspaces という Project を作成します。 OpenShift Web コンソール上の OperatorHub から Red Hat CodeReady Workspaces で検索し、 CodeReady Workspaces Operator を選択してインストールします。 このとき、A specific namespace on the cluster として workspaces を指定します。 CodeReady Workspaces Operator の Status が InstallSucceeded になっていることを確認します。 CodeReady Workspaces Operator の Operator Details の Overview タブから Create CodeReady Workspaces Cluster の Create Instance のリンクをクリックします。 CheCluster カスタムリソースの YAML ファイル編集画面が表示されますが、今回は特にデフォルトから変更せずインストールを行います。 実行後Message フィールドに特にエラーが出力されていなければ、 oc コマンドで workspaces Project の状態を確認してみます。 Operator Pod 以外に CodeReady Workspaces を構成する Pod が起動していることが確認できます。また、 CodeReady Workspaces Operator の管理対象となる CheCluster カスタムリソースも確認できます。 $ oc project workspaces Now using project "workspaces" on server "https://api.crc.testing:6443". $ oc get pods NAME READY STATUS RESTARTS AGE codeready-79b999c8cd-526g9 1/1 Running 0 11m codeready-operator-56c4876b78-66855 1/1 Running 0 35m devfile-registry-74bcdd9fd4-npntc 1/1 Running 0 12m keycloak-6f96588dc5-xlfkj 1/1 Running 0 17m plugin-registry-6cd9f45fdb-kbs9n 1/1 Running 0 11m postgres-5447b5d8db-9q7rm 1/1 Running 0 18m $ oc get CheCluster NAME AGE codeready-workspaces 37m Webコンソールに表示されているCodeReady Workspaces の URLにアクセスします。 OpenShift OAuthを使用してOpenShiftでCodeReadyワークスペースにログインします。 最初に Authorize Access の画面が表示されるので、 Allow selected permissons を選択します。 メールアドレスや氏名などの情報を入力後 Submit を選択することで初回ログインが完了し、以下の画面が表示されます。 devfile で 開発環境を Code 化する Red Hat CodeReady Workspaces 2.0 のインストールと初回ログインが完了したので実際に devfile を使ってワークスペースを作成します。 devfile は開発環境を YAML 形式で記述した devfile.yaml というファイルで、以下の内容を含めることができます。 開発するソースコードの参照先 開発コンポーネント (エディタ、プラグインやビルドツール、アプリケーションランタイムなど) ユーザ定義のコマンド 今回は以下のサンプルの devfile を使って、 Hello World Che! と出力するだけの単純な Java アプリケーションのワークスペースを作成します。 https://github.com/redhat-developer/devfile/blob/master/getting-started/java-maven/devfile.yaml Red Hat CodeReady Workspaces 2.0 で devfile を使用してワークスペースを作成する方法はとても簡単で、CodeReady Workspaces の URLに繋げて /f?url=<devfile 参照先 URL> を指定することで CodeReady Workspaces が自動的に参照先の devfile.yaml を検知し、定義にもとづいたワークスペースの構築(Workspace Pod の起動)を行ってくれます。 devfile 参照先 URL は以下のいずれかを指定可能です。 Git リポジトリの default branch Git リポジトリの指定した branch HTTP でアクセス可能な devfile 今回はサンプルの devfile のリポジトリを使用している都合上 HTTP でアクセス可能な devfile のURL を指定してワークスペースの構築を行いますが、本記事の冒頭で触れたようにプロジェクトのソースコードリポジトリに devfile を一緒に格納することで、ソースコードとそれを実行できるコード化された開発環境を同時に管理し共有することも可能となります。 また、Git の branch ごとに異なる開発環境を構築することも可能となります。 今回は以下の URL にアクセスすることでワークスペースを構築しています。 https://<CheHost>/f?url=https://raw.githubusercontent.com/redhat-developer/devfile/master/getting-started/java-maven/devfile.yaml 上記にアクセスすると以下の画面が表示され、現在のワークスペースの構築状況が確認できます。 しばらくすると以下の画面が表示されるので、実際にJava アプリケーションを開発できる環境が構築されているか確認してみます。 左側のファイルのアイコンを選択すると、 console-java-simple というディレクトリがありその中にソースコードが存在していることが確認できます。 次にこのソースコードをビルド、テスト、実行できるか確認を行います。後述しますが、 devfile にはワークスペースで利用できる独自コマンドを定義することが可能です。このワークスペースでは maven build と maven build and run という独自コマンドが定義されているので、 maven build and run を実行してビルド〜実行まで行います。 独自コマンド実行はワークスペース画面上の Terminal > Run Task から行います。 Maven による依存関係解決とビルド、テストが実行され、無事 Hello World Che! が出力されることを確認できました。 devfile の定義内容 devfile からワークスペースを作成できることが確認できたので、 devfile の実際の記述がどのようになっているのか確認してみます。 devfile の api バージョンとワークスペース名です。これらの項目は必ず指定する必要があります。 apiVersion : 1.0.0 metadata : name : java-maven attributes はワークスペースの様々な設定変更を行う箇所です。今回使用した devfile では persistVolumes: 'false' を指定しています。これは通常 PV がアタッチされて作業内容の永続化が行われるワークスペースをエフェメラルなものに変更する設定です。 attributes : persistVolumes : 'false' projects はソースコードの参照先を定義します。ここで指定した参照先のソースコードがワークスペースに格納されます。 projects : - name : console-java-simple source : type : git location : "https://github.com/che-samples/console-java-simple.git" components はワークスペースを構成するコンポーネントを定義します。 chePlugin はワークスペースで利用するプラグインを定義しています。 dockerimage に指定したコンテナはワークスペースの Terminal や独自コマンド実行に利用可能です。 components : - type : chePlugin id : redhat/java/latest - type : dockerimage alias : maven image : quay.io/eclipse/che-java11-maven:nightly env : <~省略~> endpoints : - name : '8080/tcp' port : 8080 mountSources : true volumes : - name : m2 containerPath : /home/user/.m2 commands は先程アプリケーションのビルド〜実行にも使用したワークスペースの独自コマンドを定義できる箇所です。先程の maven build and run コマンドは実際には mvn clean install && java -jar ./target/*.jar を実行していたことがわかります。 commands : - name : maven build actions : - type : exec component : maven command : "mvn clean install" workdir : ${CHE_PROJECTS_ROOT}/console-java-simple - name : maven build and run actions : - type : exec component : maven command : "mvn clean install && java -jar ./target/*.jar" workdir : ${CHE_PROJECTS_ROOT}/console-java-simple このように、先程作成したワークスペースのソースコードやプラグイン、ビルド環境がコードとして表現されていることが確認できます。 devfile の定義内容の詳細は以下をご参照ください。 https://access.redhat.com/documentation/en-us/red_hat_codeready_workspaces/2.0/html-single/end-user_guide/index#making-a-workspace-portable-using-a-devfile_crw さいごに Red Hat CodeReady Workspaces 2.0 の devfile を利用して開発環境をコードとして管理する方法をご紹介しました。 最初に devfile を書く必要はありますが、一度書いてしまえばチーム全員が同じ環境で開発していることを担保できるのはかなりのメリットではないでしょうか。変更があったとしても devfile の変更をリポジトリに Push することですぐに他のメンバも同じ環境を利用できるようになります。 Red Hat CodeReady Workspaces 2.0 は devfile だけでなく VSCode の拡張機能が使えたり、 IDE 上から OpenShift アプリケーションを容易に開発できるプラグインが用意されていたりと便利な機能強化が行われていますので、ぜひ利用してみてください。 参考 Release Notes and Known Issues Red Hat CodeReady Workspaces 2.0 | Red Hat Customer Portal Installation Guide Red Hat CodeReady Workspaces 2.0 | Red Hat Customer Portal End-user Guide Red Hat CodeReady Workspaces 2.0 | Red Hat Customer Portal Red Hat Developer | Red Hat CodeReady Workspaces 2: New tools to speed Kubernetes development - Red Hat Developer CodeReady Workspaces devfile, demystified - Red Hat Developer
この記事は OpenShift Advent Calendar 2019 - Qiita 17日目の記事です。 こんにちは、レッドハットでストレージを中心にクラウドインフラを生業にしている宇都宮です。 前回 の続きでNooBaaの紹介をしていきます。 OpenShift Container Storage(OCS) 4をデプロイする NooBaaの前にOCSをデプロイしてストレージクラスターとして使えるようにします。 OCSのデプロイは簡単です。 まずOpenShift Web ConsoleのOperatorHubからOCS Operatorをインストールします。検索したら出てくるので、普通にインストールすればOKです。 "openshift-storage"というプロジェクトを作って、そのnamespaceを指定してインストールします。 GA前なので裏でマニフェストでOperatorGroupやCatalogSourceの宣言をしています。 そのためキャプチャはCustom Operator表示がされていますが、GAになったら消えるはずです。 インストールしたらOCSクラスター(Cephクラスター)を作ります。これもWeb Consoleから簡単にできます。 OCS Operatorの"Storage Cluster"から [Create OCS Cluster Service] を押して、デプロイするworker nodeを選択すればOKです。 OCS Operatorの"Storage Cluster"インスタンス worker nodeの選択 後は自動的にRook-CephとNooBaaが展開されるので、10分ほど待ちます。 [utubo@tutsunom ocp4ocs4]$ oc get pod -n openshift-storage NAME READY STATUS RESTARTS AGE aws-s3-provisioner-68dbcc4b7f-cnx5j 1/1 Running 0 9m52s csi-cephfsplugin-44c2h 3/3 Running 0 25h csi-cephfsplugin-4xhnp 3/3 Running 0 25h csi-cephfsplugin-8j9ww 3/3 Running 0 25h csi-cephfsplugin-hw9wv 3/3 Running 0 19h csi-cephfsplugin-mhj6t 3/3 Running 0 25h csi-cephfsplugin-n7ng6 3/3 Running 0 19h csi-cephfsplugin-provisioner-597ccdb5d9-dtm9m 4/4 Running 0 25h csi-cephfsplugin-provisioner-597ccdb5d9-fmgjm 4/4 Running 0 25h csi-cephfsplugin-r6lsq 3/3 Running 0 19h csi-cephfsplugin-vv9qj 3/3 Running 0 25h csi-cephfsplugin-xwzjx 3/3 Running 0 25h csi-rbdplugin-4cd8n 3/3 Running 0 25h csi-rbdplugin-6glxr 3/3 Running 0 19h csi-rbdplugin-bpt6r 3/3 Running 0 25h csi-rbdplugin-dmplt 3/3 Running 0 25h csi-rbdplugin-ffs9b 3/3 Running 0 25h csi-rbdplugin-fm5v4 3/3 Running 0 19h csi-rbdplugin-lsknn 3/3 Running 0 25h csi-rbdplugin-pdsck 3/3 Running 0 19h csi-rbdplugin-provisioner-78578878dc-vdq9b 4/4 Running 0 25h csi-rbdplugin-provisioner-78578878dc-z8tdc 4/4 Running 0 25h csi-rbdplugin-xvwht 3/3 Running 0 25h noobaa-core-0 2/2 Running 0 25h noobaa-operator-64fcdf84-jtvjk 1/1 Running 0 25h ocs-operator-85d5fb77b7-mpq8t 1/1 Running 0 25h rook-ceph-drain-canary-2d7f3126cb4367449f7a2e8adce08039-7fqxh52 1/1 Running 0 19h rook-ceph-drain-canary-50366dcfded428ad765492d169f3d3af-6d5rljm 1/1 Running 0 19h rook-ceph-drain-canary-780ea0d523935747378b365c55449deb-84k4ngs 1/1 Running 0 19h rook-ceph-drain-canary-85f74499308eea885b4ddbf312d7efdc-c6k2hsx 1/1 Running 0 25h rook-ceph-drain-canary-bfabaa00d0723831d53851468776ea58-68wwggh 1/1 Running 0 25h rook-ceph-drain-canary-ip-10-0-151-3.us-east-2.compute.intk9nr7 1/1 Running 0 25h rook-ceph-mds-ocs-storagecluster-cephfilesystem-a-5574c6f5w5c92 1/1 Running 0 25h rook-ceph-mds-ocs-storagecluster-cephfilesystem-b-58895bb6h5lb8 1/1 Running 0 25h rook-ceph-mgr-a-749d74dd65-qzbpt 1/1 Running 0 25h rook-ceph-mon-a-5b465dc844-bz82d 1/1 Running 0 25h rook-ceph-mon-b-84d69c8447-pv4kh 1/1 Running 0 25h rook-ceph-mon-c-5cbd9ffc6-2xjzw 1/1 Running 0 25h rook-ceph-operator-765987c885-d9dww 1/1 Running 0 25h rook-ceph-osd-0-659f9595bc-6wxh4 1/1 Running 0 25h rook-ceph-osd-1-6b944d75d9-ct2xp 1/1 Running 0 25h rook-ceph-osd-2-7c49f64fdb-cxrl7 1/1 Running 0 25h rook-ceph-osd-3-7fc7c65689-7vkbb 1/1 Running 0 19h rook-ceph-osd-4-5b747b847f-ht6bd 1/1 Running 0 19h rook-ceph-osd-5-5b4fd57588-fsjzs 1/1 Running 0 19h rook-ceph-osd-prepare-ocs-deviceset-0-0-dgzhj-98cx6 0/1 Completed 0 25h rook-ceph-osd-prepare-ocs-deviceset-0-1-5nd72-f2dqt 0/1 Completed 0 19h rook-ceph-osd-prepare-ocs-deviceset-1-0-274fz-wtxs6 0/1 Completed 0 25h rook-ceph-osd-prepare-ocs-deviceset-1-1-95tbz-n2xl9 0/1 Completed 0 19h rook-ceph-osd-prepare-ocs-deviceset-2-0-xptqm-kgl9f 0/1 Completed 0 25h rook-ceph-osd-prepare-ocs-deviceset-2-1-zf6d4-ksj59 0/1 Completed 0 19h noobaa-core-0 というPodがNooBaaですね。 NooBaa CLIを使う NooBaaを直接触るにはNooBaa CLIを使うのが簡単です。NooBaa CLIはoc CLIと同じような感覚で使えます。皆さんの端末にも入れておくとなにかとべんりでしょう。次のようにインストールできます。 Mac brew install noobaa/noobaa/noobaa Mac without Homebrew curl -s https://api.github.com/repos/noobaa/noobaa-operator/releases/latest | grep "mac" | cut -d : -f 2,3 | tr -d \" | wget -qi - ; mv noobaa-mac-* noobaa ; chmod +x noobaa; sudo mv noobaa /usr/local/bin/ Linux curl -s https://api.github.com/repos/noobaa/noobaa-operator/releases/latest | grep "linux" | cut -d : -f 2,3 | tr -d \" | wget -qi - ; mv noobaa-linux-* noobaa ; chmod +x noobaa; sudo mv noobaa /usr/bin/ インストールしたら noobaa と打ってみます。 [utubo@tutsunom ocp4ocs4]$ noobaa ._ _ ______ | \ | | | ___ \ | \| | ___ ___ | |_/ / __ _ __ _ | . \ |/ _ \ / _ \| ___ \/ _\ |/ _\ | | |\ | (_) | (_) | |_/ / (_| | (_| | \_| \_/\___/ \___/\____/ \__,_|\__,_| (以下略) イヤッハー! 閑話休題。 noobaa status -n openshift-storage などとnamespaceを指定することでインストールされたNooBaa MCGのステータスが見られます。 NooBaaのBackingStoreとBucketClassを設定する NooBaaはbucketを提供しますがあくまでGatewayなので、実際にオブジェクトを保存するにはバックエンドのbucketが必要です。 本当はRook-CephのCephObjectStoreで作ったbucketを指定したいのですが、手動でCephObjectStoreを作ってもうまくいかず、断腸の思いでAWS S3のBucketをBackingに指定します。 S3で"noobaa-backend"というbucketを作って、S3FullAccessなIAMユーザーを作ってAccessKeyやSecretKeyをメモって(雑)、my-s3-bucketという名前でBackingStoreを追加します。ついでにOBC作るときに必要なBucketClassもBackingStoreに紐づけて作ります。 [utubo@tutsunom ocp4ocs4]$ noobaa backingstore create aws-s3 --access-key='ACCESS_KEY' --region='us-east-2' --secret-key='SECRET_KEY' --target-bucket='noobaa-backend' my-s3-bs -n openshift-storage INFO[0002] ✅ Exists: NooBaa "noobaa" INFO[0003] ✅ Created: BackingStore "my-s3-bs" INFO[0003] ✅ Created: Secret "backing-store-aws-s3-my-s3-bs" INFO[0003] INFO[0003] NOTE: INFO[0003] - This command has finished applying changes to the cluster. INFO[0003] - From now on, it only loops and reads the status, to monitor the operator work. INFO[0003] - You may Ctrl-C at any time to stop the loop and watch it manually. INFO[0003] INFO[0003] BackingStore Wait Ready: INFO[0003] ⏳ BackingStore "my-s3-bs" Phase is "Verifying": TemporaryError BackingStore Secret "backing-store-aws-s3-my-s3-bs" not found, but not rejecting the young as it might be in process INFO[0007] ✅ BackingStore "my-s3-bs" Phase is Ready INFO[0007] INFO[0007] INFO[0007] ✅ Exists: BackingStore "my-s3-bs" INFO[0007] ✅ Exists: Secret "backing-store-aws-s3-my-s3-bs" INFO[0007] ✅ BackingStore "my-s3-bs" Phase is Ready # BackingStore spec: awsS3: region: us-east-2 secret: name: backing-store-aws-s3-my-s3-bs namespace: openshift-storage targetBucket: noobaa-backend type: aws-s3 # Secret data: AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXX AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXX [utubo@tutsunom ocp4ocs4]$ noobaa bucketclass create my-s3-bc --backingstores=my-s3-bs --placement="" -n openshift-storage INFO[0002] ✅ Exists: NooBaa "noobaa" INFO[0002] ✅ Exists: BackingStore "my-s3-bs" INFO[0002] ✅ Created: BucketClass "my-s3-bc" INFO[0002] INFO[0002] NOTE: INFO[0002] - This command has finished applying changes to the cluster. INFO[0002] - From now on, it only loops and reads the status, to monitor the operator work. INFO[0002] - You may Ctrl-C at any time to stop the loop and watch it manually. INFO[0002] INFO[0002] BucketClass Wait Ready: INFO[0002] ✅ BucketClass "my-s3-bc" Phase is Ready INFO[0002] INFO[0002] INFO[0003] ✅ Exists: BucketClass "my-s3-bc" INFO[0003] ✅ BucketClass "my-s3-bc" Phase is Ready # BucketClass spec: placementPolicy: tiers: - backingStores: - my-s3-bs bucketを作る はい、そしたらOBCを投げてbucketを作ります。yamlでなくてもNooBaa CLIで作れます。 [utubo@tutsunom ocp4ocs4]$ noobaa obc create test-obc --bucketclass=my-s3-bc -n openshift-storage INFO[0002] ✅ Exists: BucketClass "my-s3-bc" INFO[0003] ✅ Created: ObjectBucketClaim "test-obc" INFO[0003] INFO[0003] NOTE: INFO[0003] - This command has finished applying changes to the cluster. INFO[0003] - From now on, it only loops and reads the status, to monitor the operator work. INFO[0003] - You may Ctrl-C at any time to stop the loop and watch it manually. INFO[0003] INFO[0003] OBC Wait Ready: INFO[0003] ⏳ OBC "test-obc" Phase is "Pending" INFO[0006] ✅ OBC "test-obc" Phase is Bound INFO[0006] INFO[0006] INFO[0007] ✅ Exists: ObjectBucketClaim "test-obc" INFO[0007] ✅ Exists: ObjectBucket "obc-openshift-storage-test-obc" INFO[0007] ✅ Exists: ConfigMap "test-obc" INFO[0007] ✅ Exists: Secret "test-obc" INFO[0008] ✅ Exists: StorageClass "openshift-storage.noobaa.io" INFO[0008] ✅ Exists: BucketClass "my-s3-bc" INFO[0008] ✅ Exists: NooBaa "noobaa" INFO[0008] ✅ Exists: Service "noobaa-mgmt" INFO[0009] ✅ Exists: Secret "noobaa-operator" INFO[0009] ✈️ RPC: bucket.read_bucket() Request: {Name:test-obc-4ec98214-d276-42c6-b9ca-8fd6c0788717} INFO[0010] ✅ RPC: bucket.read_bucket() Response OK: took 9.0ms ObjectBucketClaim info: Phase : Bound ObjectBucketClaim : kubectl get -n openshift-storage objectbucketclaim test-obc ConfigMap : kubectl get -n openshift-storage configmap test-obc Secret : kubectl get -n openshift-storage secret test-obc ObjectBucket : kubectl get objectbucket obc-openshift-storage-test-obc StorageClass : kubectl get storageclass openshift-storage.noobaa.io BucketClass : kubectl get -n openshift-storage bucketclass my-s3-bc Connection info: BUCKET_NAME : test-obc-4ec98214-d276-42c6-b9ca-8fd6c0788717 BUCKET_PORT : 30791 BUCKET_HOST : 10.0.172.190 AWS_SECRET_ACCESS_KEY : Pxwza4eoWlxy3s2Jn41+9/FNQE5yVgvtYm0uwD6J AWS_ACCESS_KEY_ID : 6WfVQh3nbT1TQ06VUJN8 Shell commands: AWS S3 Alias : alias s3='AWS_SECRET_ACCESS_KEY=Pxwza4eoWlxy3s2Jn41+9/FNQE5yVgvtYm0uwD6J AWS_ACCESS_KEY_ID=6WfVQh3nbT1TQ06VUJN8 aws s3 --no-verify-ssl --endpoint-url https://10.0.172.190:30791' Bucket status: Name : test-obc-4ec98214-d276-42c6-b9ca-8fd6c0788717 Type : REGULAR Mode : OPTIMAL ResiliencyStatus : OPTIMAL QuotaStatus : QUOTA_NOT_SET Num Objects : 0 Data Size : 0.000 B Data Size Reduced : 0.000 B Data Space Avail : 1.000 PB というように、いろいろな情報とともにできました。後はこれらをもとにOpenShiftのAppからbucketが使えますね。 ちなみに、 noobaa bucket create というコマンドもあります。 これでもbucketを作ることはできますが、本当に単にbucketを作るだけでAppが使うようなアクセス情報は全く出てきませんし、BucketClassを指定することもできないですし、使えません。 まとめ お手軽にOCSをデプロイし、NooBaaの動作をNooBaa CLIを使って簡単に紹介をしました。 CephをバックエンドにすることができずMulti-Cloud Gatewayっぷりは見せられず、すみません。別の機会でお見せします。 2つのBackingStoreのbucketでMirrorしたいときは、BucketClassを作るときにBackingStoreを2つ指定し、 --placement=Mirror のオプションをつけることでできます。 では今日はここまで。
Red Hat エンジニアリングの佐藤匡剛です。 赤帽エンジニア Advent Calendar 2019 19日目のつもりで書いたのですが、もう書けちゃったのでせっかくなのでフライングで投稿します。 本ブログやRHTN、勉強会などでたびたびお伝えしていた通り、ついに先月11/28に Apache Camel 3.0がGAリリース されました! Camel 2.0の登場から、10年ぶりのメジャーアップグレードだそうです。 そういえば、10年前というとオープンソースのEIP実装/ESBといえばまだまだMule ESB、Apache ServiceMixが中心だった頃に、簡潔なDSLで非常に軽量なルーティングエンジンとして登場したCamelが一部で注目を集めつつあったのを思い出します。 Camel 3の注目ポイント(Top 10) 1. 3(+ 1)プロジェクトに 2. Webサイトリニューアル 3. Java 11サポート 4. モジュール化されたcamel-core 5. 起動高速化とフットプリント軽量化 6. タイプセーフなエンドポイントDSLの導入 7. ルーティングエンジンのリアクティブ化 8. 独立したcamel-main JAR 9. MicroProfile対応 10. 様々な機能改善 11. マイグレーションガイド Camel 3の注目ポイント(Top 10) 以前のブログ でも紹介しましたが、Camel 3の注目ポイントは以下の通りです。 Apache Camel 3 What's New (top 10) - Apache Camel 3(+ 1)プロジェクトに Webサイトリニューアル Java 11サポート モジュール化された camel-core 起動高速化とフットプリント軽量化 タイプセーフなエンドポイントDSLの導入 ルーティングエンジンのリアクティブ化 独立した camel-main JAR MicroProfile対応 様々な機能改善 マイグレーションガイド 今回は、それぞれの注目ポイントを詳しく解説していきます。 なお、本記事はClaus Ibsenによる上記英文記事の翻訳ではなく、私なりに噛み砕いた紹介記事なので、興味のある方は英文記事の方も読んでみることをオススメします。 1. 3(+ 1)プロジェクトに Camel 2まではSpring BootとKarafのランタイムサポートも含めたすべてのライブラリが1プロジェクトでリリースされてきましたが、Camel 3以降は Camel K 、 Camel Quarkus がApache Camelの正式なサブプロジェクトとして加わり、3プロジェクト体制になります。 ( https://camel.apache.org/blog/Camel3-Whatsnew/ より) Camel 3 ― Apache Camelの中心ライブラリで、これまでのCamel 2と同じくあらゆるJavaアプリケーションで使えるインテグレーションフレームワーク。Camel K、Camel QuarkusはCamel 3をベースとする。 Camel K ― CamelをKubernetes / Knative上で使えるようにするためのプロジェクト。クラウドベースの ランタイム とインテグレーション Operator を含む。 Camel Quarkus ― Quarkus のCamelエクステンション。ネイティブコンパイルした超高速Camelアプリケーションが実行可能になる。 Camel KとCamel Quarkusの役割分担が多少分かりにくいですが、Camel QuarkusはCamelランタイム自体の超高速化、Camel KはCamelアプリケーションをクラウドネイティブにするためのOperator、という位置付けです。両者は競合するものではなく相互に連携し合います。ゆくゆくは、たとえばクラウドネイティブ(Camel K)かつネイティブコンパイルされた超高速なランタイム(Camel Quarkus)で動くCamelアプリケーション、といったものが近いうちに実現できるかもしれません。 なお、3.0リリース時点ではまだプロポーザル段階でしたが、 camel-kafka-connector も今後、正式にサブプロジェクトに加わりそうです。したがって、最終的には「3(+ 1)」プロジェクト体制に生まれ変わります。 Today we are launching another subproject of @ApacheCamel : https://t.co/dT1rljQxZQ , camel-kafka-connector is based on @apachekafka connect API and it is leveraging the available camel components. Discover more in the official repository, contributions are welcome! — Apache Camel (@ApacheCamel) December 6, 2019 A little blog post about the new arrival in the Apache Camel's family: camel-kafka-connector. Just an introduction to the project: https://t.co/8rsqqyIv3K , we'll add more content in the future @ApacheCamel @apachekafka @TheASF — Apache Camel (@ApacheCamel) December 14, 2019 2. Webサイトリニューアル 今ではイケてるオープンソースプロジェクトはWebサイトもモダンでカッコいいのが当たり前になっていますが、Camel 2.xまでは2000年代の古くさいApacheなWebサイトのままでした。3.0リリースを機に、ようやくWebサイトもモダンに生まれ変わりました。 camel.apache.org 個人的なオススメは、新しくなったCamelコンポーネントレファレンスです。Camelといえば300+あるコンポーネントによって色々な外部システムと連携できるのが一番の売りですが、そのコンポーネントリストが非常に参照しやすくなりました。 List of Camel components - Apache Camel 特に注目してほしいのが、この左メニューの上の "Quick lookup" 検索窓です。これで300+の中から、探したいコンポーネントを一瞬で見つけられます! あともう1つ忘れてはいけないのが、Webサイトの各ページに "Edit" ボタンが付いたことです。Editボタンをクリックするだけで、GitHubの該当するソースに直接飛んで、そのままGitHub上で修正をプルリクできます。コミュニティへのコントリビューションの敷居がいっそう低くなりましたので、ぜひどんな小さなタイポでも見つけたらコントリビュートしてみてください! 私もこういう超細かい修正をちょくちょくコントリビュートしています。 https://github.com/apache/camel/commit/120c97301c5f2ed80293c948e8ab1c6729bce968 3. Java 11サポート Camel 3.0がJava 11を正式にサポートする最初のバージョンになります。 Java 8も当分の間はCamel 3.xでサポートされますが、Camel 2.xから3.xへマイグレーションするための補助、という位置付けなので、Java 8のEOLに合わせて将来の3.xバージョンではいずれサポートが外されることになります。 4. モジュール化された camel-core Camelクラウドネイティブ化の一環でアプリケーションのフットプリントを軽量化するため、 camel-core のリファクタリング(さらに細かいJARへの分割)が行われました。このモジュール化によって、Camel 3からは本当に必要な依存JARだけをパッケージできるようになります。 細かい話は、古市さんの翻訳記事に書かれています。 rheb.hatenablog.com Camelユーザの観点で大事なのは、 camel-api 、 camel-support 、 camel-core-engine の3つです。 camel-api ― Camelの基本的なAPI( CamelContext 、 Endpoint 、 Exchange 、 Message など)が含まれます。 camel-support ― Camel APIの基本実装クラスが提供されます。 camel-core-engine ― Camelルートエンジンの実装です。 なお、Clausの英文記事には RouteBuilder は camel-support に含まれると書いてあるのですが、見たかぎり RouteBuilder は camel-core-engine に含まれています。 このモジュール化で大事なポイントの1つは、これまで組み込みコンポーネントとして camel-core に同梱されていた Direct や Log などの基本コンポーネントもすべて外だしされたことです。 camel-bean camel-log camel-stub camel-browse camel-mock camel-timer camel-controlbus camel-properties camel-validator camel-dataformat camel-ref camel-vm camel-direct camel-rest camel-xpath camel-directvm camel-saga camel-xslt camel-file camel-scheduler camel-zip-deflater camel-language camel-seda これからは、本当に必要なコンポーネントだけを使って可能な限り軽量にCamelアプリケーションを作っていくのが推奨スタイルになります。 もちろん、救済措置として本当にミニマルな camel-core-engine の他に、上記基本コンポーネントを含んだ「レガシー」な camel-core も提供されます。Camel 3の軽量なスタイルへ書き換えるまでの移行措置として、この従来型 camel-core を使うことができます。 5. 起動高速化とフットプリント軽量化 camel-core モジュール化の結果として、メモリにロードされるクラスの数が削減されました。Clausの英文記事によれば、Camel 2では5200クラスロードされていたのがCamel 3では4300に減ったそうです。 それに加えて、リフレクションで実装されていた箇所を静的コード生成で置き換えるという最適化も行われています。これはCamel Quarkusの取り組みとも連動しているのですが、リフレクションを使わないことでプログラム起動の高速化を図るとともに、Quarkusでネイティブコンパイル可能にもしています。 6. タイプセーフなエンドポイントDSLの導入 個人的にCamel 3単体での一番の売りはここかなと思っています。Java DSLメインでCamelを使っている方は、すぐにCamel 3を試してみたくなるのではないでしょうか。 「タイプセーフ」なエンドポイントDSLというのは、要するに従来のテキストベースの timer:click?period=3000&fixedRate=true といった定義でなく、URIのパラメータも含めてメソッドチェーンでエンドポイントを定義できるDSLのことです。 テキストベースではパラメータ名が間違ってないか、パラメータの値の型( int など)が正しいか、といったことを厳密にはコンパイル時にチェックできませんでした。 from( "timer:click?period=3000&fixedRate=true" ) .to( "seda:foo?blockWhenFull=true" ); メソッドチェーンのDSLになることで、Javaのコンパイル時型チェックでそういったエラーを検査できるようになります。 from(timer( "click" ).period( 3000 ).fixedRate( true )) .to(seda( "foo" ).blockWhenFull( true )); エンドポイントDSLを使うには、 pom.xml のdependencyに camel-endpointdsl を追加し、 RouteBuilder の代わりに EndpointRouteBuilder を使ってルートを定義します。 return new EndpointRouteBuilder() { @Override public void configure() throws Exception { from(timer( "click" ).period( 3000 ).fixedRate( true )) .to(seda( "foo" ).blockWhenFull( true )); } }; サンプルプロジェクトは以下を見てみてください。 https://github.com/apache/camel-examples/tree/master/examples/camel-example-cafe-endpointdsl 7. ルーティングエンジンのリアクティブ化 内部実装の話で今のところユーザには直接影響ないですが、ルーティングエンジンがリアクティブに書き換えられました。実際のところ、数年前にCamel 3の計画が立てられていた時点ではこのリアクティブ化が目玉機能になる予定だったのですが、実際に蓋を開けてみると約束通りこの目標が達成はされたものの他の注目機能に比べて地味な位置付けになっています。 私も実際にどうリアクティブに書き換えられたのかまだ追えていないのですが、おそらくはスレッドを使わなくなることでリソース消費が効率的になる他に、今までのスレッドベースのエンジンではルート内で例外が発生した際にスタックトレースが冗長になっていたものがより短く簡潔になる、などの恩恵がある(はず)です。 あと、ゆくゆくはJava 8のサポートが終了してJava 11が前提になった段階で、Camel APIに Java 9 Flow API をベースにしたリアクティブなクライアントAPIを導入する構想があります。 8. 独立した camel-main JAR 地味な機能追加ですが、Spring BootやKarafなどのランタイムなしにスタンドアローンでCamelアプリケーションを実行しやすいように、 camel-main ライブラリが外だしされました。これもクラウドネイティブ化の一環で、アプリケーションに不必要なランタイムの依存をなるべく取り除いていこうという方向性にあるものです。 実際の使用サンプルについては、以下を見てみてください。 https://github.com/apache/camel-examples/tree/master/examples/camel-example-main 9. MicroProfile対応 Camelのマイクロサービス対応の流れで、 Eclipse MicroProfile との連携も進められています。具体的には、MicroProfileの設定、メトリクス、ヘルスチェック、耐障害性などをサポートしたコンポーネントが新たに作られました。これらのMicroProfileコンポーネントはCamel Quarkusにも対応しています。 10. 様々な機能改善 Camel 3.0にはその他にも細かな修正や機能改善がいっぱい入っています。Clausの英文記事で言われている点を箇条書きするとこんなところです。 JUnit 5サポート Camel Registry が書き込み可能になってテストで使いやすくなった プロデューサーエンドポイントの遅延初期化が可能に Camel Supervising Route Controller によるルート起動時のより細やかな管理 11. マイグレーションガイド Top 10といいつつ11個目があるのはご愛嬌です。もともとはこのCamel 2.x → 3.0マイグレーションガイドの紹介で記事を書こうと思っていたのですが、まずはCamel 3の注目ポイント紹介だけで十分お伝えすべきボリュームがあったので、予定を変更してこの記事を書きました。 Camel 3はなるべく後方互換性を尊重しつつ作られているとはいえ、10年ぶりのメジャーアップグレードなので、技術的負債の返済も含めて変わるべきところは変わっています。お手元のCamel 2.xアプリケーションを3.0にアップグレードするには、このマイグレーションガイドに沿ってプロジェクトを修正していく必要があります。 Apache Camel 2.x to 3.0 Migration Guide - Apache Camel このガイドの紹介は年明けにでも書こうと思っているので、ご期待ください。 それでは、新しくなったCamel 3でラクダ乗りを楽しみましょう! (クラウドへ向かうラクダのイメージ)― Photo by Federico Gutierrez on Unsplash
Red Hatの福岡オフィスでソリューションアーキテクトをしている田中司恩です。 この記事は OpenShift Advent Calendar 2019 - Qiita 15日目のエントリになります。 早いもので12月も残すところ半月。アドベントカレンダーも後半戦に突入ですね。 書く方も大変ですが、みなさんいろんな視点で良い記事を書かれているので読んでいてとても勉強になります。 残りの記事も楽しみです! さて今回はOpenShift Container Platform 4(OCP4)の特徴的な機能の1つ、OTA(Over-The-Air)アップデート、通称OTAを紹介します。 はじめに [図1] OTAアップデート OTAはOCP4のクラスターを複雑な操作をすることなく更新できる機能です。 本番運用中のインフラ環境を定期的にアップデートしていくのは大変な作業が伴います。 セキュリティパッチやOSの更新、Kubernetesのアップデート、などなど… OTAはボタンをポチッと押すだけでそれらのアップデートが実行できるので、インフラ運用の負荷軽減に役立ちます。 また、OTAはデフォルトでオンになっている機能ですので、新しいバージョンのリリースを検知したらすぐに更新が実行できます *1 。 OCP 4.1で実行されるKubernetes(K8s)はv1.13でした。OCP 4.2になりK8sはv1.14へバージョンが上がっています。OTAでアップグレードすると、OCPだけでなくK8sも同時にアップグレードできます。 このお手軽さはOCP4ならではの機能です。OTAはIPI、UPIどちらでクラスターを作成した場合でも可能です。 (注意事項) 自動でクラスターのアップデートが完了するのは、全てのノードがRHCOSで実行されている場合です *2 RHELノードは、RHCOSノードのアップデート実行後に手動で openshift-ansible のPlaybookを実行する必要があります クラスターのバージョンを以前に戻す、ロールバックはサポートされていません OTAに関するドキュメントは下記になります。 クラスターの更新 4.2 アップグレードチャネル アップデート先のバージョンを選択するために、アップグレードチャネルという概念が導入されています。 OCP 4.1とOCP 4.2それぞれのバージョンに対応するアップグレードチャネルは下記の通りです *3 。 OCP 4.1 prerelease-4.1:プレリリース stable-4.1:安定チャネル OCP 4.2 candidate-4.2:リリース候補 fast-4.2:安定チャネルの前にリリースされる stable-4.2:安定チャネル 基本的には stable- を利用し、いち早くテストしたい場合などには fast- を利用します。 OTA実行の際にはこのチャネルを切り替えて、任意のアップデートバージョンを選択できます。 アップグレードチャネルについては下記のナレッジに記載があります(英語) OpenShift 4.2 Upgrades phased roll out OTAの実行 アップデートの実行はWeb GUIまたはCLIで実行できます。この記事ではGUIでの方法について紹介します。 下記はOCP4.2(v4.2.9)のクラスターの例です。今回はv4.2.9→4.2.10のバージョンアップをやってみます。 まず、GUIのメニューから、Administration > Cluster Settings > Overview へ移動します。 OTAを実行できるバージョンがある場合は、 Update Now が表示されます。 [図2] 現在のクラスターバージョン Channelの下の stable- や fast- などのリンクをクリックしてアップグレードチャネルを切り替えます *4 。今回は stable-4.2 を選択します。 [図3] アップグレードチャネルの選択 Update Now を押し、アップデート先のバージョンを選択画面に遷移します。 Update Now を押してもすぐにOTAが開始されるわけではないのでご安心を。今回はSelect New Versionから4.2.10を選択します。 最後に Update を押すとアップデートが開始します。 [図4] OTAの開始 Cluster Settings > Cluster Operators を確認すると、現在進行中のOperatorのアップデートの様子が確認できます *5 。 また、アップデートの実行中はローリングアップデートが実行されます。稼動中のPodの数やノードの数が多いと完了までに時間がかかります。 アップデート中にUpdate Statusが Failing になることがありますが、正常に進行していれば問題ないので作業完了まで気長に待ちましょう。 アップデートが完了するとUpdate Statusは下記のような表示になり、無事に作業完了したことを確認できます。 [図5] アップデートの完了 ページ下のUpdate Historyを確認すると、開始から完了までにかかった時間を確認できます。 [図6] アップデートの履歴 OCP4.1 → 4.2のアップデート 次に、K8sのバージョンアップを伴うOTAをやってみます。基本的な操作は先ほどと同じです。今回は4.1.27から4.2.10へのアップデートしてみます *6 。 まず、現在のOCP4.1のクラスターのバージョンを確認します。 [図7] 現在のクラスターバージョン 次にアップグレードチャネルを現在の stable-4.1 から stable-4.2 に変更します。 [図8] アップグレードチャネルの選択 最後に、 Update now または Updates Available を押してバージョン選択画面へ遷移し、アップデート先のバージョン(今回は4.2.10)を選択し、 Update を押します。 [図9] OTAの実行 アップデートが完了し、クラスターのバージョンが4.2.10になっていることが確認できます。 [図10] アップデートの完了 K8sのバージョン確認 K8sのバージョンを確認するとアップデート前(4.1.27)は v1.13.4+2e68cca でした。 # oc version Client Version: openshift-clients-4.2.2-201910250432 Server Version: 4.1.27 Kubernetes Version: v1.13.4+2e68cca アップデート後(4.2.10)は v1.14.6+17b1cc6 になっています。 # oc version Client Version: openshift-clients-4.2.2-201910250432 Server Version: 4.2.10 Kubernetes Version: v1.14.6+17b1cc6 OCPのアップデートと共にK8sのバージョンが上がっていることが確認できました *7 。 アップグレードパスについて ここからはおまけです。 OCP4のアップグレードパスは複雑です。アップグレード元と先の対応関係は各バージョンのリリースノートに記載されています。 しかし、すべてのバージョンの対応関係を手作業で調べるの大変ですので、簡単に対応図を作成できる下記のナレッジの方法をご紹介します。 OpenShift Container Platform (OCP) 4 upgrade paths OCP4のアップグレード情報を取得するAPIが公開されていて、下記のように利用します。 $ curl -H "Accept: application/json" https://api.openshift.com/api/upgrades_info/v1/graph?channel=<CHANNEL>&arch=<ARCH> | jq '.' <CHANNEL> :アップグレードチャネルで紹介したチャネル <ARCH> :x86_64は amd64 、IBM Zは z390x 出力される情報を利用し、Graphvizを用いて図式化します。サンプルスクリプトが下記に公開されています。 cincinnati/graph.sh at master · openshift/cincinnati graph.sh #!/bin/sh # # Usage: # # cat cincinnati.json | graph.sh >graph.dot # # For example: # # curl -sH 'Accept:application/json' 'https://api.openshift.com/api/upgrades_info/v1/graph?channel=prerelease-4.1' | graph.sh | dot -Tsvg >graph.svg set -e JQ_SCRIPT='"digraph Upgrades {\n labelloc=t;\n rankdir=BT;" as $header | ( [ .nodes | to_entries[] | " " + (.key | tostring) + " [ label=\"" + .value.version + "\"" + ( if .value.metadata.url then " href=\"" + .value.metadata.url + "\"" else "" end ) + " ];" ] | join("\n") ) as $nodes | ( [ .edges[] | " " + (.[0] | tostring) + "->" + (.[1] | tostring) + ";" ] | join("\n") ) as $edges | [$header, $nodes, $edges, "}"] | join("\n") ' exec jq -r "${JQ_SCRIPT}" 上記のスクリプトを利用してSVGファイルを作成します。 Graphvizの実行コマンド dot と jq を使用するため、必要なパッケージをインストールしてください。 RHEL8の場合 # yum install graphviz jq macOSの場合 $ brew install graphviz jq 下記は stable-4.2 (x86-64)のアップグレードパスを出力する例です。 $ curl -sH 'Accept:application/json' 'https://api.openshift.com/api/upgrades_info/v1/graph?channel=stable-4.2&arch=amd64' | ./graph.sh | dot -Tsvg > stable-4.2.svg 下記のような図が出力されます。SVGファイルはWebブラウザなどで閲覧できます。(2019/12/15時点の図) [図11] stable-4.2(x86-64)のアップグレードパス まとめ OTAを利用したOCPクラスターのアップデートについてご紹介しました。実際にとても簡単にOCPクラスターの更新を行うことができます。 その上OCPのバージョンアップやセキュリティアップデートだけでなく、K8sのアップデートも複雑なことをすることなくできてしまいます。 なにかと難しくなりがちなインフラの運用管理も、OCP4とOTAをうまく活用することでエンタープライズな環境へのK8sの導入を進めやすくなるのではないでしょうか。 気になる方は是非、OCP4の検証環境を構築してOTAにトライしてみてください! Let's get Big Ideas ! *1 : クラスター内のCluster Version Operatorが更新サービスを定期的にチェック *2 : RHCOS: Red Hat Enterprise Linux CoreOS *3 : nightly-4.1もありますが、ドキュメントに記載が無いので省略します。 *4 : ドキュメントにはアップグレードチャネルと書いてますが、GUI上はUpdate Channel。 *5 : Update Statusの下のView detailsを選択すると、Cluster Operatorsへ飛びます *6 : 2019/12/15時点の最新版へのアップグレードパス *7 : GUIではクラスターのAbout画面でK8sのバージョンが確認できます
こんにちは!レッドハットでマーケティングに所属する中村( @fideleruuth )です。この記事は、 Ansible 3 Advent Calendar 2019 の15日目の記事です。そしてこの記事は完全に箸休め記事です。 AnsibleFestとは AnsibleFestは、年に1度の”Ansible”のみに限定したカンファレンスで、 2016年から開催されている(Wikipedia情報)ようです。実際にはもう少し前からコミュニティ主導で開催しているようで、最初の会はニューヨークで、数十人規模からはじまったものらしいです。 AnsibleFest2019概要 内容としては、初心者から上級者、利用者だけでなく、ソフトウェアへの貢献者まで参加者層が幅広いです。ユーザー企業による事例セッションや、テクニカルディープダイブなど3日ほどで非常に多くの情報に触れる事のできるカンファレンスです。 2019年のAnsibleFestは9月24日〜26日の3日間、1,500名の登録、日本からの参加者19名という形で実施されました。日本から行く方も徐々に増えてきています。 AnsibleFestに関する報告は11月25日のAnsible Night Tokyoでカバーさせていただきました。資料が掲載されているので、以下をご参考いただくと日本からの参加者の皆様の気づきなどを知ることができます。また、セッション動画(もちろん英語)が公開されていますので、こちらもご興味あればご確認ください。 Ansible Night in Tokyo 2019.11 - 資料一覧 - connpass AnsibleFest2019セッションの動画 AnsibleFestに参加する理由 さて、AnsibleFestは上述したとおり、Ansibleに特化した海外カンファレンスなわけなのですが、皆さんがAnsibleFestに参加する理由をまずご紹介いたします。 1.Ansibleに関する最新情報と事例を聞くことができる Ansibleに関する情報は日本国内でも豊富に入手することができると思いますが、AnsibleFestは海外の動向、最新の技術についてキャッチアップするにはもってこいの場です。 技術情報に関しては特に、Moleculeのようなテストツールに関しては2018年のAnsibleFestで情報が公開され、2019年も道半ばではあるが情報展開がされたようです(僕はセッションにでていない)。 また、事例情報に関しても豊富にあります。基調講演だけでもある程度キャッチアップできると思いますが、毎年海外の先進的企業(超大企業でレガシーと思われがちな企業も含め)がどのように自動化に取り組んでいるのかを知ることができます。これがAnsibleをチームに広げようとしている方々の刺激となり、模範となるケースもちらほら見受けられます。 2.海外ユーザーとの交流やプロフェッショナルへの質問ができる これはなかなか難しいところがありますが、今から準備すればきっと大丈夫です(笑)!会期中、毎晩カジュアルなパーティが開催されます。ボッチ対策がほとんどないのですが、この場で当日聞いたセッションの講演者に声をかけお話することが可能です。結構勇気がいりますが、今回日本から参加された方でチャレンジしている方もいらっしゃいました。 また、展示会場では、Ask the Expertコーナーが設けられていたりしますので、海外のプロフェッショナル(例えば、 geerlingguy (Jeff Geerling) · GitHub など)へ質問することが可能です。これも勇気がいりますが、僕も誰が誰だかわからず今回の参加者の方と一緒に、「Moleculeについて質問したいんだけどー」と突撃してみたところ、「あいつがいいよー」と紹介してもらい、上記のJeff Geerlingさんに質問することができました。 このように、カンファレンスならではの(勇気は要りつつも)情報入手するチャンスが沢山転がっています。また、セッションの混雑具合をみて「これはグローバルでもホットトピックなんだな」といったように体でAnsibleの熱なんかを感じることができます。 だんだん行きたくなってきましたか・・・?(笑) 今から準備できること さてさて、だんだん行きたくなってきたところで、、、。2020年のAnsibleFestは10月13日〜15日です。 1.予定を入れる そろそろ来年の計画を立て始めたころではないでしょうか?もしくは立てようかな〜と思っているくらいという方もいらっしゃると思います。まずは、皆さんがお使いの手帳やカレンダーに予定を入れましょう! ▶ Google カレンダーへ登録 2.最新情報を入手するために登録 毎年Super Early Birdや、Early Birdのように早割が提供されます。この早割で2−3万円くらい安くなりますので、活用しない手はありません。登録がオープンした通知を受けるには以下のサイトから、メールアドレスを登録してもらうと、最新情報が送られてきます。 ▶ AnsibleFest公式サイト 3.上司にささやく&予算感 来年度の計画が既に始まっていることと思いますが、皆さんの技術習得プランとあわせて海外の最新動向を聞いてくるというミッションを自分自身に課してみてください。 http://redhat.lookbookhq.com/fest2019/ansiblefest-atlanta また、忘年会シーズンということもありチーム忘年会などもあることでしょう。飲みの席だとOKが出やすいというケースもあると思います(実際に僕は飲みの席で軽く「行きたいんだよね〜」と言ったら「別にいいんじゃない *1 」と言われた)。以下のような文章をご参考に囁いてみてください。 来年度自動化推進するにあたって折返し地点で自分たちの自動化レベルを確認する上でも9月にAnsibleFestに参加したいです 海外のエンジニアとのディスカッションを通して、日本では得られない情報や、自分自身のレベルアップに繋げたいと思っています また、この会話の際に「で、いくらぐらいかかるの?」という質問があると思います。この場合は以下の情報を参考にしていただければと思います。合計でだいたい40〜50万円くらいかかるとお伝えすると良いでしょう。 参加費:8万円(Super Early Bird) 渡航費:20万円くらい 宿泊費:8万円くらい(2万円×4泊) ※主催者が準備した公式?のホテルは2−3万円/泊します。自分で手配しても、アメリカのホテルは高いので、2万円/泊を想定したほうがいいです。 4.先人がどんな準備をしていたかをサーチしてみる KubeConやOpen Infrastructure Summit(旧Open Stack Summit)などのテックカンファレンスに参加している方は多くいらっしゃいます。「KubeCon 参加 レポート」などで検索するとブログ記事が出てくるのでその内容において海外カンファレンスに参加してみてどうだったのかを調べてみるのも良いでしょう。 また、AnsibleFestに関しては11月25日にAnsibleFest報告会を実施していたので、 そちらの情報 を参考にしてみてください。 2019年8月にOisixさんが開催していた勉強会では、海外カンファレンスってどんな感じ?というテーマで勉強会も実施されていたので、こちらの資料なども参考にしてみると良いかもしれまんせん。 https://oi-study.connpass.com/event/141316/presentation/ と、様々な情報入手先があるみたいですが、さらに、AnsibleのSlackコミュニティではAnsibleFestというチャンネルがあるので、そちらで発言してみると参加された方からコメントが貰えるかもしれません。 5.技術トピック&英語 まだAnsibleFest 2020までまだ9ヶ月以上あります。今年のAnsibleFestのセッション内容をみつつ、自分自身の興味領域について明確にするとともに、「こんな話をしてきたいな」「XXXに関する不明瞭なところを明確にするにはどうすればいいのだろう」などのように海外カンファレンス参加することでの目標設定を領域だけでも決めておくと良いかと思います。(上司への説得の際にも重要) そして最後の難関英語ですが、今から少しずつ勉強していきましょう。Youtubeで技術系動画を字幕付きで見て真似してみるとかはいいかもしれませんし、英会話学校に通ってみるのも良いかもしれません。また、難関と言いましたが、技術系英語は拙くても伝わるケースも多数ありますので、ヒアリング能力を中心にスキルアップしておき、質問は事前準備、ヒアリング頑張る!の戦術で挑むと良いと思います。 さいごに AnsibleFestは比較的小さめのイベントかつワントピックなので、Ansibleを使う多くの人が参加するには規模感のちょうどいいカンファレンスです。海外カンファレンス初心者の方でも居心地良く楽しめると思います。 また、2020年のAnsibleFestは30名くらいの日本人が参加すれば良いな!と思っていますので、ぜひ来年度の目標に「AnsibleFestに参加する!」を入れていただければ幸いです。 一緒にAnsible飯 in San Diegoをやりましょう! Ansible飯 in Atlanta!! #ansiblejp #AnsibleFest pic.twitter.com/irPQNGZtmj — なかむらまこと @ 極度自動化(しなさい) (@fideleruuth) 2019年9月25日 *1 : 「別にいいんじゃない」はちゃんと理由つけて承認申請後でしてね、という意味ですが。
この記事は 赤帽エンジニア Advent Calendar 2019 - Qiita 15日目の記事です。 こんにちは、レッドハットでストレージを中心にクラウドインフラを生業にしている宇都宮です。 そして引き続きRookとOpenShift Japan CommunityのTwitterアカウント、 @japan_rook と @openshiftjp のフォローお願いしまっす。 今日はストレージオーケストレーターRookのスピンオフで、OpenShift Container Storageのお話しをします。 OpenShift Container Storage OpenShiftは永続的なストレージを必要とします。一般的にストレージが利用されるのは次のような用途です。 ステートフル・アプリケーションのPersistent Volume OpenShiftのインフラサービス(Registry, Metrics, Loggingなど) OpenShiftでは様々なストレージがサポートされますが、ストレージがあるのは基本的にはOpenShiftの外側です。 OpenShift Container Storage(OCS)は、OpenShift上のアプリケーションとしてストレージを構成し、他のアプリケーションに対してストレージサービスを提供できるようにするソフトウェアです。いわゆるCloud(Container) Native Storageです。 OCSは上記の用途の両方にストレージを提供できるので、他のストレージと組み合わせて使う必要はありません。OCSがあれば充分です。 またOCSはオンプレ・クラウドを問わずOpenShiftがあればどんなプラットフォームでも稼働できるし、運用管理もOpenShift上で統合できるのでとっても便利です。 OCSの要素 実はOCSはこれまでにもありました。これまでのOCSはRed Hat Gluster StorageをベースにしたOpenShift 3でのみ使えるものでした。OCS 3と呼ばれます。 OpenShift 4で使えるOCSは OCS 4 と呼ばれ、これまでとはガラリと変わって次の技術をベースとしています。 Ceph Rook NooBaa これらの技術を使って、ブロック・ファイル・オブジェクト3種類の形態のストレージを提供します。 CephとRookについてはストレージオーケストレーターRookの記事を読んで貰えればなんとなくわかると思うので、ここではNooBaaについて軽く紹介します。 NooBaa : Multi-Cloud Gateway NooBaaは2018年にRed Hatが買収したデータ管理の企業およびテクノロジーです。 NooBaaは複数のクラウドに存在するオブジェクトストレージを仮想的に集約し、アプリケーションからはクラウドを意識することなく単一のエンドポイントから利用できるようになります。NooBaa Multi-Cloud Gateway(MCG)と呼ばれます。 MCGはオブジェクトストレージをBucket単位で管理します。アプリケーションにBucketを提供しながら、管理下のBucket間でオブジェクトをミラーリングしたりマイグレーションしたりすることもできます。 OCSにはこのNooBaa MCGの機能が含まれています。 Rook-Cephで作るオブジェクトストレージはもちろん、S3やBlob Storageなど一般的なS3互換のオブジェクトストレージサービスとも背後で繋げることで、アプリケーションに対してまるでローカルに大きなオブジェクトストレージがあるような形でBucketを提供することができます。 まとめ OCS 4とNooBaaの簡単な紹介をしました。実はまだOCS 4はGAされていません。あともう少しでGAとなる予定です。 NooBaaが具体的にどんな感じで動くのかイメージは、OpenShift Advent Calendar 17日目のエントリーで紹介する予定です。 qiita.com というわけで今日はここまで。
こんにちは。Red HatにてMiddlewareを中心にソリューションアーキテクトをやっている梅野です。 赤帽エンジニア Advent Calendar 2019 - Qiita の14日目の投稿です。 昨日の ルール駆動開発 rheb.hatenablog.com に引き続き、ルールエンジンの話題を。 量子コンピュータって? 皆さんは量子コンピュータという言葉は一度はお聞きになっているかと思います。今までのコンピュータと何が違うのかというと、今までのは「チューリングマシン」と呼ばれる原理に基づき「フォイ・ノイマン型アーキテクチャ」で作られており、データを0か1かで表現していました。 それが量子コンピュータでは「非ノイマン型」として、量子力学的な考え方で「重ね合わせ」の原理で動作します。データも0か1だけではなく、複数の状態を表すことができ、それは並列性を実現できるコンピュータになります。「ノイマン型」とは、実行すべき命令が記憶媒体の中で順番に並んでおりBusを通じて一つ一つ取り出されて実行される、いわゆるシーケンシャルな動きを指しますが、「非ノイマン型」とは、必要なデータが揃っていれば実行できる命令を決定するという動作のことを指します。 量子コンピュータって、宇宙人みたいな形してません?(画伯うめぞー作) という難しいことはさておき、量子コンピュータは一般的には、今までの計算量と桁違いのスピードが出るから早いんだ と思われている節がありますが、それは結果的な話であり、解へのアプローチが異なることは理解しておく必要があります。 この量子コンピュータも、2つのタイプがあります。一つは量子回路型(ゲート型)、もう一つは量子アニーリング型というものです。 量子回路型では、データの探索、パターンマッチング、シミュレーション等に利用が見込まれます。量子アニーリング型は組合せ最適化処理に利用が見込まれます。 そう。実はレッドハットのルールエンジン、Red Hat Decision Managerは、この2つができるのです!(むしろ、この2つのみできる...) ルールエンジンって? ルールエンジンの本質はパターンマッチング技術を使っていることです。ルールエンジンと聞くと、エクセルのようなスプレットシートのテーブル形式でルールが書けるという事ばかりが先行していますが、それは仮の姿で、 パターンマッチング が本来の使い方になります。RETEアルゴリズムをベースとしているので、「データがルールを検索して実行し、しかも再帰的に評価と実行を行う」という、「非ノイマン型」な動作を行います。(そう聞くとちょっと難しそうですが、慣れたら簡単!もう元に戻れません!) また、Red Hat Decision Managerが持つ一つの機能、Business Optimizer は、パターンマッチング技術のルールエンジンと、シミュレーテッド・アニーリング(Simulated Annealing) 等のOperations Reserchから生まれた探索アルゴリズムが複数搭載されており、 組合せ最適化問題 に使われています。国内でも、ルートの最適化(Vihecle Routing Problem)やシフト計画等の事例がどんどん出始めています。 今のコンピュータは量子力学に基づいていないので、「シミュレーション」したアニーリングというわけですが、これが「リアル」なアニーリングが実現できる量子コンピュータになると、爆速で最適解が見つかると思われます。 パターンマッチング技術は、データキャッシュや判断ツリーの最適化が行われているので、従来のIf-Then のロジックに比べるとより大量のデータに対して高速に動作することが可能です。国内の事例では、2100万件のデータとのマッチングで2ms以下で答えを返すなんてこともやっています。(すげぇな...) 組合せ最適化では、「その組み合わせが制約に合致しているのかどうか」を判断し、組み合わせをちょっと変えて試して... の繰り返しを超高速で行います。つまり、判断のところが遅いと使い物にならないのです。その意味でもルールエンジンによる高速判断は重要になります。 触ってみよう! なんと、レッドハットでは、従来有償サービスにてのみ提供していたRed Hat Decision Manager の日本語ハンズオン教材を、 無料で公開 いたしました! http://redhat.lookbookhq.com/rhdm_handson2019 redhat.lookbookhq.com 概要説明編  だけではなく、 応用編・実践編  も合わせて公開しています!(なんという太っ腹...) 量子コンピュータは凄いですが、もうその技術に触れることが出来るならば素敵じゃないですか?!技術者魂をくすぐられませんか?! 残念ながらこの資料の中身についてのお問合せは有償になってしまいますが、まずはルールエンジンでパターンマッチング技術を試してみてはいかがでしょうか。 さらに、組合せ最適化については、コミュニティー版ですが https://www.optaplanner.org/ www.optaplanner.org よりダウンロードして サンプルを動作 させることが可能です。 JDKがすでにインストールされていれば、ダウンロードしてzipを解凍してrun.sh(run.bat) を動かすだけです。 ぜひお試しください。 うめの
レッドハットのソリューションアーキテクト、松田です。 赤帽エンジニア Advent Calendar 2019 - Qiita の13日目の投稿です。 11/15に開催された、Red Hat Forum Tokyo 2019では、 『ブラックボックス化した業務システムを”ルールエンジンで”華麗にホワイト化する方法』というタイトルのセッションを担当しました。 セッション資料はこちら↓ redhat.lookbookhq.com YouTubeもあるよ。顔が丸い人が喋ってます。 Red Hat Forum Tokyo 2019:ブラックボックス化した業務システムを”ルールエンジンで”華麗にホワイト化する方法 今回のセッションテーマは、これでした。 そして、この開発手法を、このたび、 「 ルール駆動開発 」 と名付け、提唱しました。 ルール駆動開発という名前 セッションタイトルを考えた時は、まだこのワードを出すかどうか決めていなかったのですが、ネーミングとかキャッチフレーズって大事なんですよね。 「ONE TEAM」とか「東洋の魔女」(古!)とか。 もっともらしい御託をつらつらならべるより、一言で伝えられたほうが、話が早いし、心に残りやすいはず。 そこで、今回は、ブラックボックス化したシステムでも迅速にリプレース可能になるこの手法を「ルール駆動開発」と名付けてみました。 といっても、この手法、昨日今日考えついたものではありません。 Red Hatでルールエンジンを用いた開発を重ねてきた結果から導かれた、ベストプラクティスです。 ルール駆動開発のつかいどころ まとめるとこういう話なんですが、その詳細は、セッション資料をぜひ見ていただきたい。 そして、実はこの手法は、レガシーシステムのリプレース時だけでなく、新システムの構築時にも力を発揮します。 業務ルールを業務目線で整理して、アプリケーションの外に出し、繰返し開発で進めていくやりかたは、まだ業務ルールが固まりきっておらず、Try & Errorを迅速に繰返しながらフィードバックを即システムに反映したい、というような新サービスの構築にも非常に向いています。 最近では特に、顧客へOne to Oneのきめ細やかなサービスを提供したいというようなケースにおいて、ルールエンジンを使った「ルール駆動開発」を適用して、Try & Errorを高速に繰り返しながら、サービス品質を向上させるという事例も増えています。 最後に 様々な開発手法が世の中にあります。 私も長年の開発経験の中で、いろいろな手法を取り入れてやってきました。 どれもそれなりには意味があるものの、これが完璧というものはなく、対象システムの性格に合わせて、うまく組み合わせて使っていくべきものだと思っています。 この「ルール駆動開発」もこれさえやれば、いつでもどこでも完璧にうまくいくというものではありません。 ですが、従来の開発手法に足りていなかった、”業務ルールによる判断を一つのサービスとして切り出す”という重要な観点をプラスすることができる手法です。 業務アプリの開発に携わるすべての方に、ぜひ知っていただきたい、そして試していただきたい手法です。 Red Hatには、この「ルール駆動開発」のスペシャリストがいます。 そして、「ルール駆動開発」に欠かせないのが、優れたルールエンジン製品です。 Red Hat Decision Managerについて、また、「ルール駆動開発」について、ぜひご相談ください。 【2021/10 追記】 ルール駆動開発のマンガを作成しました! 以下のリンクから無料で読めます。ぜひ、ダウンロードしてみてください。(お名前の登録のみお願いします) https://redhat.lookbookhq.com/rhdm_manga