Ponz Dev Log

ゆるくてマイペースな開発日記

IBM CloudのコンソールログインにMFAを適用する

IBM Cloudのコンソールへのログインに多要素認証(MFA)を適用する話。

AWSやAzureだとIAMでMFAの適用設定できるし、GCPだとGoogleアカウント自体のセキュリティ設定で同様のことができます。 ではIBM Cloudは?と一緒に仕事をしているエンジニアに聞かれたので調べたことを覚え書きします。

結論を先に言えば、コンソールのIAMの設定で有効化できます。

※ 厳密に言えばAzureはGitHubアカウントでサインインできるので、GitHubの設定でもMFAを有効化できたりします。

MFA適用ガイド

IBM Cloudのドキュメントにやり方は書いてあります。こちらに則って説明します。

cloud.ibm.com

やり方

(1) 上のバーの「管理」→「アクセス(IAM)」を選択

(2) アクセス(IAM)画面の左メニューから「設定」を選択

(3) 「アカウント・ログイン」のパネルの「編集」ボタンを選択

f:id:accelerk:20200416005317p:plain
アクセス(IAM)画面

(4) 「全てのユーザー」を選択し、「更新」ボタンを押して有効化

f:id:accelerk:20200416005401p:plain
MFA設定ダイアログ

(5) 一回ログアウト (MFAが有効になるまで5分程度かかる)

(6) IBMidで再度ログイン

(7) MFAの入力画面が出るので、「オーセンティケーター・アプリケーションをセットアップします」のリンクを押して設定用のQRコードを表示する

f:id:accelerk:20200414075337p:plain
検証コード入力・設定画面

(8) Google AuthenticatorやIBM Verifyなどのオーセンティケーター・アプリを用意して、上記(7)のQRコードを読みこむ。1つ罠があるので後ほど説明。

(9) もう一度検証コード入力画面に戻り、(8)で設定した検証コードを入力する。これでログイン時にMFAできました🎉🎉

MFA設定リンクの罠

上記手順で注意すべきは、 手順(7)(8)はアカウントにつき1回しか設定できない点です。

「オーセンティケーター・アプリケーションをセットアップします」のリンクを2回目以上踏むと、 以下のスクショのように設定済みのエラーメッセージが出てきます。 1回リンクを踏んで「後で設定しよう」ができないので、これをやってしまうとコンソールにログイン不可能になります。

また、オーセンティケーター・アプリから設定情報を削除しても同様です。何と再設定できません。

もしやらかしたらHelp Deskに問い合わせてMFAを無効化してもらいましょう。 (私は過去2回やらかして問い合わせました)

f:id:accelerk:20200416010130p:plain
検証コードアプリの設定は1回しかできない


MFAはコンソールへのログイン確認と同時に設定したい(してもらいたい)ので、手順が簡単なのはありがたいですね。 以上。

技術書典 応援祭で出したKafka本の振り返り

技術書典 応援祭でKafkaに関する技術同人誌を執筆しました。 3月の頭から頒布が始まってからだいぶ時間が経ってしまったので、執筆前から執筆後までの振り返りを書き連ねます。

執筆した本はこちらです↓

[技術書典マーケット] techbookfest.org

[BOOTH] ponzdou.booth.pm

執筆まで〜執筆中

技術書典7で1冊書いたことがあったので、スケジュール感と本の執筆環境準備は手間取らなかったです。 参考までに執筆環境はこんな構成です↓

手元でプレビューを確認しつつ、git commit → git pushしたら自動的にCloud BuildでPDF生成が走るように自動化していました。 CI環境が整うと効率上がるのですごく良いです。CIはいいぞ😊

書いた中身の振り返り

最初に目次レベルのレビューを受けた時点では、頒布前告知の以下の記事で宣言した通りかなり広く浅く触れる本にする予定でした。 ponzmild.hatenablog.com

ただ、紙に印刷して読み直してみると、手を広げ過ぎて全てが浅い本になっていたので結果的に構成変更しています。 「Kafka初学者」をターゲットにするべく、詳細の深掘りやKafkaの周辺プロダクトの話はスコープ外して、基本的な部分の説明を充実させています。

  • メッセージのデータ構造を定義するSchema Registryの章は全て削除した。
  • 概要説明とコマンドライン操作の章を挿絵入れて丁寧にした。 (ページ数が倍に...)
  • Kafka ConnectのConnector自前実装の節を削除。替わりにOSSのConnector(PostgreSQLのもの)の利用チュートリアルを厚めにした。
  • Kafka Streamsはメッセージ加工するステートレスなアプリのみ扱い、メッセージの集約・表示するステートフルなアプリはGitリポジトリ参照に留めた。
  • コラムは長く書かずに1行リンクに寄せた。

書きたかった(≒深掘りして学びたかった)トピックはたくさんあったのですが、 次に書く内容が増えたと捉えて内容を取捨選択したことで読みやすくなったと思っています。

書いた後の振り返り

本を書いたきっかけは単なる技術的関心の追究だったわけですが、 お仕事でも使う機会がありそうだったので啓蒙資料としてKafkaの魅力プレゼン資料として使うことも目的としていました。

実際にチームメンバーに説明する機会にも恵まれ、メンバーに簡潔に説明してクライアントアプリを書き始められるところまで持っていけたのは1つの成果です。 説明していて気付いたのですが、自分の本をさらにサマリした数枚のスライドにまとめて見せると良いですね。 理解してほしいポイントが著者・聴衆共に分かりやすく、同時に本の内容にも興味を持ってもらえます。

ただ、Kafkaそのものの理解が深まっても業務アプリに活かすためには考慮すべき点が多く、学ぶべき点につきません。。。 例えば、以下のような考慮点は処理する業務特性を理解した上で設計する必要があります。

  • メッセージの二重送信防止
  • 受け取ったメッセージの二重実行の防止
  • Consumerのエラーハンドリング (失敗したら、メッセージは捨てるのか、リトライか、Dead Letter Queueに入れるのか...)
  • Web APIの処理をProducer/Consumerに分割する方針
  • Kafkaを使えるユースケースの選定
  • etc...

これらの話題はまだ自分の中で答えは見つかっていません。 Red Hatさんの以下のブログ記事が参考になりそうですが、自分の中で整理できたらブログ記事にするか本にしたいですね。

rheb.hatenablog.com

本当に、本を書き終わってからが本番ですね。


また別の機会にKafka関連の本書きたいですね。Kafkaアプリ設計・実装つまづき集とか。

本じゃなくてもLTとかに登壇というスタイルもいいかも。どこかいい場所あるかしら?

以上。

MavenでAvroのスキーマファイルからJavaコードを生成するのにつまづいた

Kafka Tutorialsを進めていたときに、タイトル通りMavenでAvroのスキーマからJavaのコード生成するのにつまづいた話です。 このサイトに掲載されているサンプルコードは全てGradleでビルドされているのですが、自分が良く使っているMavenだったらどうかなと実践してみた次第です。

kafka-tutorials.confluent.io

Avroの公式サイトにはMavenプラグインでAvroのスキーマからJavaのコードを生成する方法が乗っていますが、 Mavenを使うならば mvn compile を実行するようにガイドされています。

avro.apache.org

しかし、 mvn compile をいざ実行してみると、 コンパイルするものがないとメッセージが返ってきます。無慈悲です。

[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ kstreams-serialization ---
[INFO] Nothing to compile - all classes are up to date

最終的にはStackoverflowの以下のissueに辿り着き、 mvn avro:schema を実行すればできると。 よくよく考えたらgoalsに書いてるんだから当然でした笑

stackoverflow.com

movie.avsc というスキーマファイルを作成して実際に上記のコマンドを実行してみると、無事 generated-sources 配下にJavaのコード Movie.java が出力されました。

f:id:accelerk:20200204010005p:plain

めでたし、めでたし。


しかし、なぜ mvn compileで通らないのかは理解しなければ。。。

技術書典8でKafka本を出します

前回の技術書典7に引き続き、技術書典8でも技術同人誌を出すことになりました! 2月になると宣伝する余裕がなさそうなので、今のうちに宣伝しておきます。

techbookfest.org

今回出す本

今回は分散ストリーミングプラットフォームのKafkaの解説同人誌を出す予定です。

前回のメッセージングプラットフォームのNATSを調べたときに類似するOSSとして意識していたのですが、 触ってみるとめちゃくちゃ面白かったので推しポイントをプレゼンするつもりで書いてみる次第です。

目次

目次は以下のようなものを考えてます。 元々は自分の今いるチームメンバーに知識共有できたらいいなぁと考えていたので、意識的に章を細かく設定しています。 なので初学者が分かりやすい本になっているはず!

  • Kafkaとは ... 概要説明とKafk API概観
  • Kafkaの基本操作 ... クラスタ構築とコンソール操作
  • Kafka Producerアプリケーションを作る
  • Kafka Consumerアプリケーションを作る
  • Kafka ConnectでデータベースとKafkaを繋ぐチュートリアル
  • Kafka Streamsアプリケーションでストリーム処理してみる
  • Schema Registryでメッセージのスキーマを定義する

また、ストリーム処理だけでなく、Kafka ConnectやSchema Registryも取り上げているのが個人的にはポイントです。他の本ではあまり書いてないのでは?

ちなみに目次はオライリーのKafka本のものをベースにしています。もちろんこの本を読んでいなくても理解できるようになっています!

www.oreilly.co.jp

対象読者と書かないこと

対象読者はアプリ開発者でKafka初学者の想定です。以下の項目は今回の本のスコープから外す予定です。

  • Kafkaの内部実装の深堀り ... どのようにディスクに書き込んでいるのか等
  • Kafkaのセキュリティ周り ... SSLでメッセージの暗号化と認証認可まわり

それではどうぞお楽しみに! 当日は既刊のNATS本も持っていく予定です :)

以上。

2019年の振り返り

今年の年末エントリーです。 今年は去年以上に色んなことに手を出したので、その振り返りと来年の目標を書きます。

去年の年末エントリーはこちら↓ https://ponzmild.hatenablog.com/entry/2018/12/31/160746

やってきたこと

お仕事

クラウドビジネスを推進する部門にいるので、クラウド漬けでした。 言語も使うサービスもクラウドも違っていたので覚えることが多かったのですが、去年よりも使うべきサービスの選択に迷わなくなった印象です。裏で取得してたクラウド資格や書籍の読み漁りが業務でもプラスになっていたと信じたい。

  • AWS上でAPI開発 w/ Java
  • SAP Cloud上でAPI開発 w/ Java(Spring)
  • IBM Cloud上でAPI開発 w/ Node.js & Blockchain, UI開発 w/ Angular
  • 分析基盤構築 & 予測モデル構築 w/ Python,Watson

個人ワーク

めぼしいところだと技術書典7のサークル参加が大きな成果でした。 今までブログやGitHubでこっそりやっていた技術のアウトプットを、もっと広くの人に見てもらえる場所で、しかも本という形に残るもので表現できたのです。いかに自分の知識が曖昧か学んだいい機会でした。

以下自分のワークです。

NATSによるPub/Subメッセージング入門 | ぽんず堂 https://booth.pm/ja/items/1562371 #booth_pm

私生活

残念ながら去年の体重増量目標の60.0kgは達成できず。。。結果は+1.2kgの56.2kgでした。

2020年にやりたいこと

  • 技術書典8

    • 実はまた出ます!サークル参加は1日目の2/29です。予定としては前回出したNATS本の改訂版と、NATS/Kafka/RedisのPub/Subメッセージングソフトウェアの比較本を出す予定です。
  • 英語力UP

    • TOEIC780点を目指します。昇進にもTOEICのスコアが必要だったのですが、最後に受験したのが2年前だったので、今までの最高点を目標にします。
  • 資格

    • 上半期にCKAD(Certified Kubernetes Application Developer)取ります!
  • ブログ

    • 今年のペースを落とさず年24本書きます。
  • 体重増やす

    • 来年末までに60kgまで増やします。もちろん健康的に自炊しながらね :)

それでは2020年もよろしくお願いします!良いお年を😊

以上。

IBM CloudでEventStreamにメッセージを送受信する

今更ながらApache Kafkaに入門しました。 なんで今更触り始めたかというと、IBM CloudのEvent Stream(Kafkaのマネージドサービス)にLiteプランが出てきたからです。 限られた機能しか操作できないとはいえ、無料で使うのには十分です。

今回はJavaでメッセージの送受信を試みます。

公式のドキュメントにはJavaのサンプルコードが掲載されていますが、 1年近く前のコードなので非推奨のメソッドやクラスがあります。これらを避けながら改めて自分でも書いてみます。

github.com

以下、コードになります。 1クラスに押し込めているので少しコードが長くなっていますが、200行くらいで書けるもんですね。

Event Stream (manged Kafka service) Pub/Sub

ポイントとしては、Kafkaのクライアントに Properties クラスに設定値を詰める時です。 IBM CloudのEvent Streamに繋ぐ場合は SSL / SASLの設定が必要です。


書いてみると意外と簡単ですね。Spring FrameworkでもKafkaを扱えるようなので、次はこちらにチャレンジですね。

www.baeldung.com

以上。

ずっと避けて通っていたNode.jsのStreamに助けられた話

自分は業務・個人開発ともに普段はNode.jsでアプリ開発することが多いのですが、 今回は1プロセスのバッチでは到底処理しきれない量のデータを Node.js Stream で何とか乗り切った話です。 乗り切るまでの過程も忘れないように覚え書きします。

数十MB ~ GB単位のデータセットを加工→ファイルダンプするシチュエーションをイメージしてください。

fs.writeFile で一気に書き出すとデータセット加工時に死ぬ

まずStreamを使用する前に、Node.js標準ライブラリの fs を使って以下のような考え方で機能実現を試みました。

  • (前提) 1プロセスのNode.jsバッチアプリケーションで処理する。
  • 処理順序は、作成済みの配列を _createLine で加工 → 末尾で改行して文字列連結 → ファイルに非同期で書き出し の順に行う。
  • ファイルの書き出しには、Node.js標準モジュールの fs.writeFile を用いて一括でファイルに書き出す。

以下サンプルコードです。 (もちろん以下のソースコードは当時とは別物のサンプルです)

class DumpServiceFsImpl {
    async dump(fileName: string) {
        const rows = this.dataset.map(this._createLine).join("\n");
        await this._writeFileAsync(`./data/${fileName}`, rows);
    }
}

上記の方法で実行してみると、データセットが500万件を超えたあたりで以下のようなエラーが出るようなりました。

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

そうです、Heap Out of memoryです。これはNode.jsのヒープサイズ上限を超えたオブジェクトを保持すると発生するエラーです。 元のデータセットと一緒に加工後のデータも全て保持して倍以上のヒープを消費したことが原因でした。

Out of MemoryはJavaのアプリだと(うっかり実装で)良く出会う印象ですが、Node.jsもOut of Memoryで落ちることあるのですよ。 (軽量なアプリケーションの作成にばかりNode.jsを使っているとなかなか出会さないだけ)

ヒープサイズを増やせば解決するか?

Out of Memoryで死んだのは、ヒープサイズ上限を超えたオブジェクトを保持したからでした。 上記のポストにも記載されていますが、nodeコマンドに特定のフラグ (--max-old-space-size) をつけて実行することでヒープサイズの上限を引き上げて解決できます。 Node.jsのコアであるV8エンジンのヒープサイズ上限は元々1.7GBとそこそこ大きいサイズを持っていますが、それでも足りない場合の対処方法です。

stackoverflow.com

ですが、自分の場合はプラットフォームの制約から安易にヒープサイズを上げることができなかったのでこの方法は断念。。。(実行環境のインスタンスのメモリが2GBしかなかった)

スケールアップ(ヒープサイズ上限の引き上げ) & スケールアウト(複数のプロセスで実行)の道が閉ざされてしまったので、アプリの処理をヒープサイズを消費しない省エネな作りに変えて対応することにしました。

StreamでちょっとずつETL

そこで白羽の矢が立ったのが Node.js Streamです。StreamはNode.jsの標準ライブラリに含まれています。 入力のオブジェクトをStreamに読み込むと、Stream内部で持っているバッファに読み込んだ要素を順次溜めていき、閾値まで溜めたらアウトプットにまとめて書き出すことができます。

Streamは読み込み / 加工 / 書き込み用のStreamが用意されているため、少しずつ読み込み→加工→書き出しとStreamをつなげることでヒープサイズが枯渇しないようなETLの実装が可能です。 今回のユースケースにはピッタリです。

最終的には大量データの書き出しは以下のコードで解決できました。(これもサンプルです)

import {Order} from "./models/model";

// 変換ストリームを自分で実装
class LineTransform extends Transform {
    private rowCounter: number;

    constructor(options = {}) {
        // objectMode: true で chunkをObjectで受け取る。
        // この設定しないとchunkはstring or Buffer型を期待するため、TYPE_ERRORで例外を投げる。
        super({...options, objectMode: true});
        this.rowCounter = 1;
    }

    _transform(chunk: Order, encoding: string, callback: (error?: (Error | null)) => void): void {
        const line = this._createLine(chunk, this.rowCounter);
        this.push(line); // 書き出し先のStreamに要素を追加
        this.rowCounter++; // increment counter
        callback();
    }
}

import intoStream from "into-stream";  // Object -> ReadableStreamへの変換ライブラリ
import {createWriteStream} from "fs";

export default class implements DumpService {
    private readonly dataset: Order[];

    constructor(dataset: Order[]) {
        this.dataset = dataset;
    }

    async dump(fileName: string) {
        const path = `./data/${fileName}`;
        logger.debug(`Dump dataset into ${path}`);

        const ordersStream = intoStream.object(this.dataset);  // データセットをReadableStreamに変換して読み出す
        const transformer = new LineTransform();  // 変換ストリームでデータセットの各要素を加工
        const outputStream = createWriteStream(path);  // Writable Streamでファイルに出力
        await ordersStream.pipe(transformer).pipe(outputStream);  // pipeでStreamを連結
    }
}

解説すると、Readable Stream, Writable Stream, Transformerを3種類を活用しています。 Stream同士は pipe メソッドで連結可能なので、読み込み / 加工 / 書き込みでそれぞれ責務を分割しながらヒープサイズの消費を押さえて大量データをETLできます。

またそれぞれのStreamはInterfaceとして提供されているので、カスタムのStreamを実装も可能です。 上記のコードだとデータセットを加工するTransformクラスを継承したStreamクラスを作成しています。

Streamは普段使わないと正体が掴めない代物です。ずっと避けて通ってみたものの、触ってみると案外使いやすい便利な標準ライブラリでした。

Streamを使えばOOMを必ず回避できるのか?

Node.js Streamがヒープサイズの消費を押さえた省エネな作りをしていたとしても、OOMを回避できない場面が出てきます。 一度に大量のオブジェクトを作ってしまったらStreamに流し込む前にヒープサイズが枯渇してしまいます。

実際に数百MB以上のデータセットを扱ったときは、100万件程度でデータセットを分割して加工→ダンプ(追記)する方法とStreamで少しずつ流し込む方法の両方を採用していました。

次に同じような実装をするときは、1つのバッチにまとめずに複数のアプリに分散 or 責務分割するか、Node.jsをやめて大量処理に向いたSpark (Python, Java, Scala) やETLサービスを使って実現したいですね。


以上。