まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:ibm

前回dokku を使ったプライベート PaaS 環境の構築、およびシンプルなアプリケーションのデプロイ手順を紹介しました。 今回はより実践的なアプリケーションとして PostgreSQL データベースを併用するアプリケーションのデプロイ手順を紹介します(といっても、実は heroku を CLI で操作する時の手順とあまり変わりません・・)。

なお今回紹介する内容は、前回のセットアップ時に "withcorona.world" という独自ドメインを設定している想定で紹介しています。異なるドメインで設定されている場合は自分で設定したドメインに適宜読み替えてください。


【dokku 内で PostgreSQL データベースを動かす】
まず dokku 環境内にデータベースサーバーを用意します。今回は PostgreSQL を使うケースを想定して以下で紹介します。まずは dokku サーバーにログインしておきます。

dokku ではいくつかのサービスが「プラグイン」という形で連携できるよう用意されています。PostgreSQL もその1つです。というわけで、まずは dokku にログインして PostgreSQL プラグインをインストールします:
# dokku plugin:install https://github.com/dokku/dokku-postgres.git

インストールが完了すると PostgreSQL データベースをインスタンス化することができるようになります。例えば "mydb" という名前を付けて1インスタンス作るには以下のように入力します:
# dokku postgres:create mydb

作成後に作ったデータベースインスタンスの情報を確認する場合は以下のように入力します:
# dokku postgres:info mydb

=====> mydb postgres service information
       Config dir:          /var/lib/dokku/services/postgres/mydb/data
       Config options:
       Data dir:            /var/lib/dokku/services/postgres/mydb/data
       Dsn:                 postgres://postgres:XXXXXXXX@dokku-postgres-mydb:5432/mydb
       Exposed ports:       -
       Id:                  a017a6896694987cb0e729b4ec1042f831eecd0d8f726d52eeea435ecd9fcf4e
       Internal ip:         172.17.0.3
       Links:               -
       Service root:        /var/lib/dokku/services/postgres/mydb
       Status:              running
       Version:             postgres:14.2

この確認結果の Dsn 値として紹介されている "postgres://" で始まる文字列がいわゆる接続文字列になっていて、(後述する)環境変数の値になります。 このインスタンスのシェルに入って PostgreSQL の CLI を使ってテーブルを1つ定義しておきたいので、以下のように実行してください(データベースに接続する時は接続文字列の "dokku-postgres-mydb" 部分を "localhost" に変えて実行してください):
# dokku postgres:enter mydb (シェルにログイン)

/# psql "postgres://postgres:XXXXXXXX@localhost:5432/mydb" (psql でデータベースに接続)

mydb=# create table if not exists items ( id varchar(50) not null primary key, name varchar(50) default '', price int default 0, created bigint default 0, updated bigint default 0 ); (SQL で items テーブル作成)

mydb=# \q (データベースから切断)

/# exit (シェルからログアウト)

これで dokku 環境内に mydb という名前の PostgreSQL データベースを1つ作り、items という名前のテーブルを1つ定義する所まで用意できました。続いて、この mydb データベースと items テーブルを使ったウェブアプリケーションを dokku 内で動かします。


【dokku 内で PostgreSQL データベースに接続するアプリケーションを動かす】
dokku 内で PostgreSQL データベースを使うアプリケーションを動かします。今回は以下のサンプルを使います(上記で作成した items テーブルを使うアプリケーションです):
https://github.com/dotnsf/cnapp_postgresql


このアプリケーションの PostgreSQL と接続する部分は以下のように記述されています:
  :
  :
var database_url = 'DATABASE_URL' in process.env ? process.env.DATABASE_URL : settings.database_url; 
var pg = null;
if( database_url ){
  console.log( 'database_url = ' + database_url );
  pg = new PG.Pool({
    connectionString: database_url,
    idleTimeoutMillis: ( 3 * 86400 * 1000 )
  });
  :
  :


具体的な挙動としては、アプリケーション実行時の "DATABASE_URL" という環境変数値を参照し、値が設定されていたらその内容を接続文字列とみなして PostgreSQL サーバーに接続する、という実装内容になっています(説明は省略しますが、接続後に items テーブルを読み書きする内容になっています)。

なので、このアプリケーションが dokku 内で実行される時に、先ほど作成した mydb データベースへの接続文字列が環境変数 DATABASE_URL として定義されていればこのアプリケーションは正しくデータベースに接続して動く、ということになります。

この辺りは heroku ユーザーであればなんとなく「同じだ・・」とわかると思います。で、その環境変数を設定するためには dokku 内のアプリケーションとデータベースがリンクさえされていれば実現できるようになっています(この辺りはクラウドネイティブアプリケーションを開発する際の 12 factors と呼ばれるベストプラクティスに沿った仕様となっています)。

というわけで、まずは dokku にアプリケーションを追加し、データベースとのリンクを設定します。今回は "cnapp" という名前のアプリケーションを作ることにして、この "cnapp" アプリケーションと "mydb" データベースをリンクしておきます(これで cnapp アプリの実行時にデータベース mydb に接続するための接続文字列が環境変数 DATABASE_URL にセットされて起動します):
# dokku apps:create cnapp

# dokku postgres:link mydb cnapp

そして前回同様にこのサンプルアプリを Git clone して、リモート接続先に dokku を追加して、main ブランチを push します:
# git clone https://github.com/dotnsf/cnapp_postgresql

# cd cnapp_postgresql

# git remote add dokku dokku@withcorona.world:cnapp

# git push dokku main

ここまでのコマンドが正しく実行されていると http://cnapp.withcorona.world/ でアクセスできるようになります※:

2022060101


※稀にこの URL では想定していないページ(Nginx のデフォルトページなど)が表示されることがあります。その場合は http://cnapp.withcorona:8080/ のようにポート番号をつけてアクセスするとうまくいきます。その後に以下のコマンドを実行するとポート番号指定なしでも正しく表示できるようになります:
# dokku proxy:ports-add cnapp http:80:8080


これだけでも一応動きますが、ついでに(?) https 接続できるよう、Let's Encrypt プラグインの設定も行っておきます:
# dokku letsencrypt:enable cnapp

最後にウェブブラウザで https://cnapp.withcorona.world/ にアクセスして動作確認します:
2022060102


最初は何も登録されていませんが、名前(name)と価格(price)を入力して追加(Create)すると、そのデータが( PostgreSQL の)mydb データベースの items テーブルに登録され、一覧として表示されるようになります:
2022060103


以上、dokku でデータベース連携アプリケーションを作って動かすための設定でした。PostgreSQL 以外にも dokku には公式機能として MySQL や Redis 、ElasticSearch といったプラグインが用意されているので、クラウドで認証した結果をセッション共有するようなアプリケーションでも動かすことができると思います。




ある程度 heroku を使ったことがある人であれば、dokku は文字通りに heroku ライクなプライベート環境に感じることができると思います(ただ dokku だとほとんどの作業が CLI からのコマンドになる点が、ウェブ GUI で色々用意されている heroku とは異なる点です)。また Cloud Foundry と比較しても、Cloud Foundry では
$ cf push (app)

のようにしてアプリをデプロイしていましたが、dokku はほぼ同様にして、
$ git remote add dokku (app) (を一度実行してから)
$ git push dokku main

といった形でアプリのデプロイができるので、Cloud Foundry の代わりとしても使いやすい環境のように感じています。今回紹介した withcorona.world というドメインは実験用の捨てドメインなのでおそらくこの紹介記事でしか使うことはないと思っていますが、他の取得ドメイン(とちょっと規模の大きめな IaaS 環境)を使って自分のプライベート Cloud Foundry 環境を作って運用してみるつもりでいます。



唐突ですが、自分も使っている IBM Cloud の PaaS 機能の1つである Cloud Foundry ランタイムのサービス終了がアナウンスされました:
Cloud Foundry on IBM Cloud サービス提供終了のお知らせ


個人的にもサービス開始とほぼ同時期から使っていたユーザーとして、またソースコードから一発でクラウド環境で動かすことができる環境として使っていただけに、とても残念なニュースでした。実は少し前から知ってはいたのですが、公開できるのが今日からということで公にできずに悶々としていました(代わりにこのブログの公開準備を進めていました)。

で、IBM Cloud として推奨している移行先は Kubernetes 系サービスや Code Engine とされています:
IBM Cloud FoundryからCode Engineへのマイグレーションに関するベスト・プラクティスの紹介:サービス・バインディングとコード


Code Engine は Kubernetes の K-native をベースとしたコンテナ・ランタイム環境です。無料で使用可能なリソース枠も用意され、これまで Cloud Foundry で動いていたアプリケーションを比較的容易に移行できる環境となっております。


ただ、私自身はこの Code Engine に加えて、以下で紹介する dokku 環境も Cloud Foundry からの移行先としてアリだと思っています。Cloud Foundry よりも heroku と比較されることが多く、また残念ながら無料で運用できるわけではない(プライベート環境なので、そのサーバーリソースが必要)のですが、一方で独自ドメインが使える上にコンテナイメージを意識することなくソースコードから稼働環境を簡単に作れる、という点で相性は悪くないと思っています。 そんな意味も含めて、このタイミングで紹介させていただくことにしました。

予定としては2回に分けて、前半である今回はセットアップ部分を中心に、次回の後半でデータベースなど外部リソースとの連携について触れるつもりでいます。





昨年あたりから heroku を使うことが多くなってきました。無料でもある程度利用できるクラウドリソースが PaaS で提供され、Git と連動してアプリケーションのデプロイができるのが非常に便利です。

とても便利なので利用頻度が高くなっていき、あっという間に無料枠の限界が近づき・・・そして同時に無料枠の制約(インスタンスが使われていないと自動的に止まっちゃうとか、複数インスタンスで運用できないとか)を超えた使い方にも興味が出てきます。そうなってくるとパブリックな heroku 利用もいいけど、プライベートな heroku 環境を自由に使えたらいいなあ、というエンジニア欲も出てきます。

そんな背景もあって、「プライベート版 heroku 」という側面もある dokku を使ってみることにしました。dokku は内部的に docker を使って1台のサーバー内に仮想的な PaaS 環境を構築するものです。heroku (や Cloud Foundry )同様にビルドパックを使った git push デプロイが可能なので、手元のソースコードを簡単に(Dockerfile とか docker イメージ化などを意識せずに)ウェブ上に公開することもできます。1台のサーバーで運用することを想定しているため可用性という面では足りないと感じる面があるかもしれませんが、自分のように作ったアプリを試験的に公開する、という機会が多い場合に非常に重宝します。またプライベート PaaS はインフラ部分の管理を自分で行うことを意味していますが、その点では1台のサーバーの面倒だけみればよい、というのはある意味でアドバンテージにもなり得るものと考えています。

今回は以下の条件で環境を構築してみました:
 ・(2022/06/01 時点で最新の)dokku v0.27.5 を使用する
 ・IBM Cloud 上に Ubuntu サーバーを1台用意して、この中に dokku 環境を導入する
 ・GoDaddy.com で取得した独自ドメイン(withcorona.world)を使った PaaS 環境を作る

IBM Cloud 上に IaaS サーバー(Ubuntu 18.04)を用意するので、このサーバー料金が必要です。IBM Cloud である必要はありませんが、後述のように DNS の設定ができる IaaS 環境が必要です(Vultr.com では同様の設定ができることを確認しています)。なお、dokku は独自ドメインを使わなくても、sslip.io サービスを併用した疑似サブドメインを使って環境構築することもできますが、今回の紹介内容では軽く触れる程度とし、動作確認などは除外させていただきます。


【dokku 環境構築】
では早速 dokku 環境を用意して、アプリケーションを dokku 上で動かしてみます。まずは1度行う必要のある環境構築の手順を紹介します。

最初にクラウド上に Ubuntu サーバーを用意します。今回利用する dokku v0.27.5 では Ubuntu 18.04, Ubuntu 20.04, Ubuntu 22.04 までがサポート対象となっていましたが、古い記事で Ubuntu 18.04 だけがサポートされていたドキュメントを参照していたこともあり、自分も Ubuntu 18.04 を使うことにしました。なお Debian 系はサポート対象ですが、RHEL/CentOS 系は 2022/06/01 時点では Experimental 機能としての提供です。

(自分の場合は)IBM Cloud 上に Ubuntu 18.04 サーバーを1台用意します。スペックは 1 vCPU で 2GB RAM のものとしましたが、ここで選ぶスペックが自分の dokku 環境となるので、比較的多くのアプリケーションを動かす場合や、(多くの DB など)多くのリソースを使うことが想定される場合は少し大きめのサーバーを用意してください:
2022060101


サーバーが用意できたら次はネームサーバーをはじめとする DNS の設定が必要です(独自ドメインを使わない場合はここを無視して dokku のインストール作業まで進んでください)。まずは自分が使うサーバー(今回だと IBM Cloud のサーバー)で DNS を設定するために必要なネームサーバー設定を確認します。IBM Cloud の場合は以下のように
 ・プライマリサーバー: ns1.softlayer.com
 ・セカンダリサーバー: ns2.softlayer.com
を設定するように指定されていることが分かります。ここは皆さんが用意したクラウドサーバーのプロバイダーによって異なるので、自分の環境のネームサーバー設定を調べる必要があります:
2022060102


このネームサーバー設定を自分が取得した独自ドメインに適用します。今回の例では GoDaddy.com で取得した独自ドメインを使うので、GoDaddy.com の DNS 設定を変更することになります。 なお今回は "withcorona.world" という独自ドメインを使って、app1.withcorona.world, app2.withcorona.world, ... といった名称のアプリケーションを運用する前提とします。まずは自分がドメインを取得したベンダーのネームサーバー設定画面(DNS 設定画面)に移動します:
2022060103


ここでネームサーバーを変更します。GoDaddy.com の場合は DNS 管理画面の少し下に設定済みのネームサーバーが表示されている画面があるので、ここの「変更」ボタンをクリックします:
2022060104


そして先ほど確認したプライマリ/セカンダリサーバーを指定します。IBM Cloud の場合はプライマリが ns1.softlayer.com 、セカンダリが ns2.softlayer.com だったので、以下のように入力し、最後に「保存」ボタンをクリックします:
2022060105


この作業はこれまで使っていた独自ドメインの DNS 設定を大きく変えることになるので警告メッセージが表示されることがあります。内容を確認して「続行」します:
2022060106


無事に設定が変更されていることを確認します(この変更内容が実際に有効になるまで1時間程度かかることがあります):
2022060107


ネームサーバーの設定変更が出来たら、次は DNS を dokku 向けのものに変更します。新しい DNS 設定画面(今回の例では IBM Cloud の DNS 設定画面)に移動し、まずメインサーバーとなる独自ドメイン名(今回の例では "withcorona.world")と、 Ubuntu サーバーの IP アドレスを入力してドメインを登録します:
2022060108



そして残りの設定を行います。dokku を独自ドメインで利用するには、以下の内容を設定する必要があります:
レコードターゲット
A@(サーバーの IP アドレス)
CNAME*(独自ドメイン名)


なお IBM Cloud の場合は上述の設定ではなく、以下の設定が必要でした(* は CNAME レコードではなく A レコードとして、ターゲットは独自ドメイン名ではなく IP アドレスで指定する必要がありました):
レコードターゲット
A@(サーバーの IP アドレス)
A*(サーバーの IP アドレス)


最終的にこのような DNS 設定となりました:
2022060109


ここまでの作業で dokku 導入前の事前準備が完了です。ここからは dokku のインストール作業を紹介します。


まず SSH 等で Ubuntu サーバーのシェルにログインします。root 以外でログインした場合は "sudo -i" を実行するなどして root に切り替え、"apt update" と "apt upgrade" を済ませておきます:
$ sudo -i

# apt update -y

# apt upgrade -y

以下のコマンドを実行して dokku を(dokku v0.27.5 を)インストールします:
# wget https://raw.githubusercontent.com/dokku/dokku/v0.27.5/bootstrap.sh;

# DOKKU_TAG=v0.27.5 bash bootstrap.sh

2つ目のコマンドが完了(5~10分くらい)すると(docker ごと)dokku が導入されています。

独自ドメインを利用する場合はそのドメインを登録するため、以下のコマンドを実行します(最後の withcorona.world 部分に独自ドメイン名を指定します):
# dokku domains:set-global withcorona.world

このコマンド実行後に、/home/dokku/VHOST ファイルの中身が指定した独自ドメインになっていることを確認してください:
# cat /home/dokku/VHOST

withcorona.world (と表示されることを確認)

また独自ドメインを所有していない(使わない)場合は以下のコマンドを実行して、sslip.io サービスを使った疑似サブドメインを登録します(最後の 11.22.33.44 部分に Ubuntu サーバーの IP アドレスを指定します):
# dokku domains:set-global 11.22.33.44

# dokku domains:set-global 11.22.33.44.sslip.io


次に、実際に dokku を使う前に(dokku の git 利用時に必要な)秘密鍵と公開鍵のペアを登録する必要があります。普段使っている秘密鍵&公開鍵があればそれを使っても構いませんし、今回の作業のために新たに1ペア作って使っても構いません。鍵ファイルの作り方はこちらなどを参照してください。ここでは秘密鍵: id_rsa 、公開鍵: id_rsa.pub という2つの鍵ファイルが手元にあるものとします。

これらを dokku のサーバー環境に登録します。まずは sftp などでこれら2つのファイルを Ubuntu サーバーの /root/.ssh/id_rsa および /root/.ssh/id_rsa.pub となるよう転送しておきます。ここまでの作業ができているものとして以下の説明を続けます。

まず鍵ファイルはファイルパーミッションが正しくないと正しい挙動になりません。これら2つのファイルのパーミッションを 400 にしておきます:
# chmod 400 /root/.ssh/id_rsa*

これら2つのファイルを dokku 環境に登録します。まずは以下のコマンドを実行して秘密鍵を登録します:
# eval `ssh-agent`

# ssh-add -k ~/.ssh/id_rsa (秘密鍵のパスフレーズ入力を求められるので正しく入力します)

続けて公開鍵も登録します:
# cat ~/.ssh/id_rsa.pub | dokku ssh-keys:add admin

dokku 作業としてはここまででほぼ完了しているのですが、ついでに SSL(https) 接続を想定した準備もしておきましょう。以下の2つのコマンドで Let's Encrypt プラグインを導入・設定しておきます:
# dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

# dokku config:set --global DOKKU_LETSENCRYPT_EMAIL=(自分のメールアドレス)

実際にアプリケーションをデプロイする前の、dokku 環境構築に必要な(1回だけの)作業は以上で終わりです。


【dokku でウェブアプリを動かす】
では構築した dokku 環境を使って実際にウェブアプリケーションを稼働させてみます。ここからの内容は dokku に新しいアプリケーションをデプロイするたびに必要な作業です(ここよりも上で紹介した作業は環境構築時に1回行うだけ)。

まずはウェブアプリケーション(のソースコード)が必要ですが、今回は自分が作ったシンプルな「ハローワールド」アプリであるこれを使って紹介することにします(自分で作ったウェブアプリがある場合はそちらを使っていただいても構いません):
https://github.com/dotnsf/simpleweb


このサンプルはアプリというほどの内容ではないのですが、起動してアクセスすると以下のような画面が表示される、というものです。GitHub ページでも公開しているので、実際のサンプルを見たい場合はこちらにアクセスしてください:
https://dotnsf.github.io/simpleweb/

2022060112
(機能はこれだけ)


過去に heroku や Cloud Foundry、Docker、Kubernetes などのコンテナ環境でクラウドネイティブなアプリを作ったことがある人であれば問題ないと思いますが、ウェブアプリケーション起動時のポート番号を環境変数 PORT から取得するようにしている点にご注意ください(以下はこの simpleweb アプリの app.js のソースコード):
//. app.js
var express = require( 'express' ),
    app = express();

app.use( express.static( __dirname + '/web' ) );

var port = process.env.PORT || 8080;
app.listen( port );
console.log( "server starting on " + port + " ..." );

では改めてこのコードを例にして dokku 環境で動かすまでの手順を紹介します。まずは "dokku apps:create" コマンドで新しいアプリケーション(今回はアプリケーション名を simpleweb としています)を作成します:
# dokku apps:create simpleweb

次に git clone でソースコードを入手して、ディレクトリ内に移動します:
# git clone https://github.com/dotnsf/simpleweb

# cd simpleweb

(heroku と同様ですが)このソースコードに新しいリモート Git オリジン(dokku)として、dokku 内の Git リポジトリを追加します:
# git remote add dokku dokku@withcorona.world:simpleweb

そして今追加したオリジン dokku に main ブランチを git push します:
# git push dokku main

(秘密鍵のパスフレーズを聞かれるので入力する)

秘密鍵のパスフレーズを聞かれるので入力すると、GitHub ではなく dokku の内部ソースコードリポジトリにコードが Push され、該当ソースコード向けのビルドパック(今回の例であれば Node.js ビルドパック)を使ってソースコードが dokku 内のコンテナとしてデプロイされて起動します(1分くらいかかります)。この辺りの一連の流れは Cloud Foundry のものに近いです。


無事にデプロイが完了すると、http://(アプリ名).(ドメイン名)/ という URL でパブリックアクセスできるようになります。今回の例であれば http://simpleweb.withcorona.world/ です(この時点ではまだ https ではアクセスできません)※。 なおドメイン名を使わない場合は http://(アプリ名).xx.xx.xx.xx.sslip.io/ でアクセスできます(xx.xx.xx.xx は Ubuntu サーバーの IP アドレス):
2022060110


※稀にこの URL では想定していないページ(Nginx のデフォルトページなど)が表示されることがあります(そうなったりならなかったりします・・・)。Nginx のデフォルトページが表示されてしまう場合は http://(アプリ名).(ドメイン名):8080/ のようにポート番号をつけてアクセスするとうまくいきます。その後に以下のコマンドを実行するとポート番号指定なしでも正しく表示できるようになります:
# dokku proxy:ports-add simpleweb http:80:8080



https でアクセスできるようにするにはもう少しコマンドが必要です。単に https でアクセスできるようにするには(Let's Encrypt で証明書を取得して適用するだけであれば)以下の dokku コマンドを入力するだけです:
# dokku letsencrypt:enable simpleweb

更に証明書の自動更新までを有効にする場合は、続けて以下のコマンドを実行します:
# dokku letsencrypt:auto-renew simpleweb

ここまで正しく完了すると SSL 証明書が発行&適用されて https://simpleweb.withcorona.world/ でもアクセスできるようになります(ドメインを使わない場合はこの作業を省略しても https アクセスできます):
2022060111


なお、この方法で dokku にデプロイされたアプリケーションは heroku の無料利用時のように(アクセスが一定時間以上なかった場合に)自動停止することはなく、また以下のコマンドでスケールアウトすることもできます(この例ではインスタンス数=3):
# dokku ps:scale simpleweb web=3

利用想定規模と環境構築先の Ubuntu サーバーの規模を合わせて構築することで、非常に有用なプライベート PaaS 環境になりうると思いました。


【まとめ】
今回紹介した dokku を使ったプライベート環境は(無料ではありませんが) heroku ユーザーにとってもメリットを感じられるものだと思います。

今回は dokku 環境の構築と、ランタイム部分(ウェブアプリケーション部分)を dokku 環境でデプロイするまでの手順を紹介しました。次回は(これも heroku での作業とほぼ一緒だったりしますが)PostgreSQL などのデータベースサービスを dokku 内に作ったり、データベースと組み合わせてウェブアプリケーションを動かす方法を紹介する予定です。


【参照】
今回は最小限のインストール手順やコマンドだけを紹介しましたが、詳しくは以下も参照ください:

dokku 公式インストール手順
dokku デプロイコマンド


(2022/06/04 追記)
後編はこちら
http://dotnsf.blog.jp/archives/1080505175.html
 

IBM Cloud から提供されている AI サービス IBM Watson の中で「音声→テキスト変換」を行う Speech to Text APIにおいて、2022/05/05 時点ではまだベータ版機能として提供されている "Speaker Labels" 機能を使ってみました。その様子をサンプルソースコードと併せて紹介します。

なお以下で紹介している様子および内容は 2022/05/05 時点のベータ版のものです。今後 API の実行方法や出力フォーマット、価格、提供しているソースコード等も含めて変更になる可能性もあることをご了承ください。


【Speech to Text サービスにおける Speaker Labels 機能とは】
一般的な Speech to Text サービスから提供されている機能の多くは「一人が話している前提」がありました。要は一人の人が話しているという前提で、その音声データをテキスト化する、というものでした。

IBM Watson Speech to Text サービスにおける Speaker Labels 機能はこの点を改良して、「複数人が話している可能性を考慮」した上で音声データをテキスト化するものです。なお、この機能は 2022/05/05 時点においてはベータ版として提供されており、英語に加えてスペイン語、ドイツ語、チェコ語、韓国語、そして日本語に対応しています。詳しくはこちらを参照ください:
https://cloud.ibm.com/docs/speech-to-text?topic=speech-to-text-speaker-labels


【サンプルとその使い方を紹介】
この Speaker Labels 機能を使った Node.js のサンプルアプリケーションを作って公開してみました。興味のある方はこちらから git clone するかダウンロードして使ってください:
https://github.com/dotnsf/s2t_betas


ソースコードを展開後の、アプリケーションの使い方を紹介します。まずアプリケーションを動かすためには Node.js v14 以上及び npm が必要なので、未導入の場合は自分のシステムにあったモジュールをインストールしておいてください:
https://nodejs.org/

また IBM Watson の Speech to Text サービスインスタンスの API Key およびサービス URL も必要です。無料のライトプラン※でも構わないので IBM Cloud 内に作成し、接続情報から API Key およびサービス URL (apikey の値と url の値)を取得しておいてください(すぐ後で使います):
2022050601

※無料のライトプランの場合、変換できるのは1か月間で 500 分ぶんのデータまで、という制約があります。


また実際に Speech to Text で変換する音声データファイルが必要です。特に今回は Speaker Labels 機能を使うため、二人以上で会話している際の音声データが必要です。自分で録音したものを使っても構いませんし、どこかでサンプルデータをダウンロードして用意していただいても構いません。以下の例では、こちらから提供されている日本語会話サンプルデータを使わせていただきました:
https://www.3anet.co.jp/np/resrcs/333020/

上述のページから提供されているサンプルデータをダウンロードし、使えそうな mp3 ファイルをソースコードの public/ フォルダ内にコピーしておいてください。とりあえず 007.mp3 というサンプルはいい感じに2名の男女が会話している様子のデータになっているので、以下はこのファイルをソースコード内の public/ フォルダにコピーできているものとして説明を進めることにします:
2022050602


会話の音声サンプルデータが public/ フォルダ以下に用意できたらアプリケーションを起動するための準備を(1回だけ)行います。まずソースコードフォルダ直下にある settings.js ファイルをテキストエディタで開き、取得した Speech to Text サービスの API Key とサービス URL をそれぞれ exports.s2t_apikey と exports.s2t_url の値として入力した上で保存します:
2022050603


そして依存ライブラリをインストールします。ソースコードフォルダ直下において、以下のコマンドを実行します:
$ npm install

これで起動の準備が整いました。最後にアプリケーションを起動します:
$ node app

成功すると 8080 番ポートでアプリケーションが起動します。実際に利用するにはウェブブラウザで http://localhost:8080/ にアクセスします。すると以下のような画面になります:
2022050601


左上にはソースコードの public/ フォルダにコピーした音声会話データのファイル名が一覧で表示されています。ここから 007.mp3 というファイルを選択してください(これが比較的わかりやすくていい感じの結果でした)。そして POST ボタンをクリックして Speech to Text を実行します:
2022050602


実行と同時に指定した音声ファイルの再生も開始します(つまり音が出ます)。並行して音声の解析が非同期に行われ、解析結果が少しずつ表示されていく様子を確認できます(ここまではベータ版の機能を使っていません):
2022050603


あるタイミングから確定した文節のテキスト内容が複数の色に分類されて表示されます。この色の分類が話している人の分類でもあります(下の結果では茶色の文字との文字になっているので、二人で会話している様子だと判断されていることになります):
2022050604


007.mp3 を最後まで解析し終えると以下のようになりました。(識別精度はともかく(苦笑))2つの文節の中で2人の人が会話している様子だった、と識別された様子がわかります:
2022050605


【サンプルソースコード内を紹介】
最後にこのアプリケーションのサンプルソースコードの内容を紹介しながら、どのように API を実行して、どのような結果を取得しているのか、という内容を紹介します。先に言っておくと、この Speaker Labels 機能を使う上で API の実行方法自体は(オプションを ON にする以外は)以前と全く同じです。実行結果に新しい情報が含まれるようになるので、その部分の対応が必要になります。 また該当部分はすべて app.js ファイル内にあるので、このファイルの内容と合わせて紹介します。

まず 27 行目で定義しているオブジェクトが Speech to Text 実行時のパラメータに相当するものです。この中で日本語変換モデル等を指定していますが、32 行目の speakerLabels: true によって、ベータ版機能である speakerLabels を有効に設定しています:
27: var s2t_params = {
28:   objectMode: true,
29:   contentType: 'audio/mp3',
30:   model: settings.s2t_model,
31:   smartFormatting: true,
32:   speakerLabels: true,
33:   inactivityTimeout: -1,
34:   interimResults: true,
35:   timestamps: true,
36:   maxAlternatives: 3
37: };

実際の音声→テキスト変換は 88 行目の processAudioFile() 関数で行っています。特にこの例では音声データファイルを一括変換する方法ではなく、WebSocket を使った非同期変換(少しずつ変換結果を受け取る方法)である recognizeUsingWebSocket() (90 行目)を使っています。そして SpeakerLabels を有効にしている場合、この実行結果(92行目)は2通り想定する必要があります。1つは「音声→テキスト変換結果」、もう1つは「どの部分を誰が話していたか、の判定結果」です(一括の同期変換を使った場合はこれらをまとめて取得できますが、今回は非同期変換を使っているためこれらの結果がバラバラに返ってくる可能性を考慮する必要があります):
90: var s2t_stream = my_s2t.s2t.recognizeUsingWebSocket( s2t_params );
91: fs.createReadStream( filepath ).pipe( s2t_stream );
92: s2t_stream.on( 'data', function( evt ){
        :



まず「音声→テキスト変換結果」が返ってきた場合です。この場合、92 行目の evt オブジェクト(=テキスト変換結果)は以下のような形で返されます:
        {
          result_index: 0,
          results: [
            { 
              final: true,
              alternatives: [
                {  //. 候補1
                  transcript: "音声メッセージが既存のウェブサイトを超えたコミュニケーションを実現",
                  confidence: 0.95,
                  timestamps: [
                    [ "音声", 0.36, 0.84 ],
                    [ "メッセージ", 0.84, 1.35 ],
                    [ "が", 1.35, 1.59 ],
                       :
                    [ "実現", 4.13, 4.7 ]
                  ]
                },
                {  //. 候補2
                  :
                }
              ]
            }
          ]
        }

まず変換結果をある程度の区切りでひとまとめにしています(ある程度の空白期間が生じるまでを1つの節とみなしています)。その区切りの番号が result_index 値です(上の例では 0 になっています)。そしてテキスト変換した結果が results 内に配列形式で格納されています。各配列要素の中に final というキーがあり、これが true の場合は節として変換結果が確定したことを意味します(false の場合は節が確定する前の、変換途中での結果が返されていることを意味します)。そして altervatives 内にその変換結果が可能性の高い順にやはり配列で格納されています。特にこの部分に注目してください:
                {  //. 候補1
                  transcript: "音声メッセージが既存のウェブサイトを超えたコミュニケーションを実現",
                  confidence: 0.95,
                  timestamps: [
                    [ "音声", 0.36, 0.84 ],
                    [ "メッセージ", 0.84, 1.35 ],
                    [ "が", 1.35, 1.59 ],
                       :
                    [ "実現", 4.13, 4.7 ]
                  ]
                },

文章としては「音声メッセージが既存のウェブサイトを超えたコミュニケーションを実現」というテキストに変換されていることに加え、その自信度が 0.95 であること、そして各単語が現れる音声開始からの通算秒数が timestamps という配列変数内に格納されています。この例だと音声スタートから 0.36 秒後から 0.84 秒後までの間に「音声」と話されていて、次に 0.84 秒後から 1.35 秒後までの間に「メッセージ」と話されていて、・・・といったように変換結果が分類されています(ここ、後で使います)。

次に変換結果として返される可能性のもう1つ、「誰がどの部分を話しているか」の結果が返される場合、evt 変数の内容は以下のようになります:
        {
          speaker_labels: [
            { 
              from: 0.36,
              to: 0.84,
              speaker: 0,
              confidence 0.67,
              final: false
            },
            {
              from: 0.84,
              to: 1.35,
              speaker: 0,
              confidence: 0.67,
              final: false
            },
              :
            {
              from: 4.13,
              to: 4.7,
              speaker: 1,
              confidence: 0.67,
              final: false
            }
          ]
        }

speaker_labels というキーが含まれている場合はこちらのケースと判断できます。そしてその中身は上の例であれば以下のような意味です:

・0.36 - 0.84 秒の間は 0 番目の人(自信度 0.67)
・0.84 - 1.35 秒の間は 0 番目の人(自信度 0.67) (この2つは同じ人)
   :
・4.13 - 4.7 秒の間は 1 番目の人(自信度 0.67)  (上とは別の人)

先程のテキスト変換結果の timestamps 値と合わせて、どの(何秒時点の)テキスト部分を何番目の人が話しているか、がわかるように speaker というラベルが付けられています。後はこれらをうまく組み合わせて、例えばテキストの色を分けて表示するようにしたものが提供しているサンプルアプリケーションです:
2022050600


なお、現時点での仕様としては以下のような制約があるようです:
・「2名で」話している前提で判断するよう最適化されている(実際には3名以上と判断される場合もあるが、あくまで2名の会話であることを想定した上で最適化されてラベルが付けられる)。
・speaker_labels の結果にも最終結果であることを示す final キーは存在しているが、final = true とならずに終わるケースが多い(なので、現状ここは無視してもよさそう)。


この辺りはあくまでベータ版での仕様なので、精度含めて今後の変更の可能性もあると思っています。ただ少なくともベータ版の現時点ではこの speaker_labels は無料で(無料のライトプランでも)使える機能のようで、今のうちから色々試してみたいと思いました。複数人の会話音声データから複数人の会話テキストを取り出せるようになると会議の議事録とかにも使えそうで、使い道の幅が大きく広がると期待しています。


Docker Desktop ショックがあって以来、なるべく少ない制約の下で docker を動かせる環境を色々調べています。そんな中で見つけた1つの方法が Docker in Docker(以下、DinD)です。

DinD はその名前の通りで、コンテナクラスタ(親)の中で動くコンテナ(子)として Docker サーバー&クライアントを動かす、というものです。多くの場合、「親コンテナ=Kubernetes」となることが多いので、正確には "Docker in Kubernetes" と表現すべきかもしれませんが、広い意味(?)での "Docker in Docker" ということだと思います。

単なる運用環境の観点だと「Kubernetes が使えるなら Docker は要らないのでは?」と思うかもしれませんが、特に開発段階だとコマンドとしての docker CLI を使いたいことがあったり、プロダクション環境とは別の小さな開発環境を Docker で作っておけると便利なことも多くあります。そういった意味で Kubernetes があってもそれとは別に Docker 環境が欲しくなることがあるのでした。


IBM Cloud からも IKS(IBM Kubernetes Services)ROKS(Redhat Openshift Kubernetes Services) が提供されていて、これらの環境でも DinD を使うことができます。特に今回は IKS の 30 日無料版を使って DinD 環境を作って使う手順を紹介します。なお、IKS 30 日無料版の制約事項や環境準備手順についてはこちらの過去記事を参照ください:
http://dotnsf.blog.jp/archives/1079287640.html


↑この記事最後の "$ kubectl get all" コマンドが成功するまでになれば準備完了です。なお Kubernetes クラスタに対して "$ kubectl get all" コマンドが実行できるようになっていれば IKS 以外の他の Kubernetes クラスタ環境でも以下同様にして DinD 環境を作ることができると思います。


【(IKS に) DinD の Pod を作る】
では早速 DinD 環境を作ります。DinD 環境といっても大それたものではなく、コンテナ的に言えば「DinD の Pod を1つ作る」ことになります。そしてその1つの Pod の中に「Docker デーモンのコンテナ」と「Docker クライアントのコンテナ」を1つずつ作ります(つまり1つの Pod の中で2つのコンテナを動かします)。

実際の作成に関しても、以下の内容のマニフェストファイルを用意するだけです(この内容を dind.yml という名前で保存してください):
apiVersion: v1
kind: Pod
metadata:
  name: dind
spec:
  containers:
    - name: docker
      image: docker:19.03
      command: ["docker", "run", "nginx:latest"]
      env:
        - name: DOCKER_HOST
          value: tcp://localhost:2375
    - name: dind-daemon
      image: docker:19.03-dind
      env:
        - name: DOCKER_TLS_CERTDIR
          value: ""
      resources:
        requests:
          cpu: 20m
          memory: 512Mi
      securityContext:
        privileged: true

そして、kubectl コマンドで以下を実行してマニフェストを適用します:
$ kubectl apply -f dind.yml

※または dind.yml を用意しなくても、このコマンドでも同じ結果になります:
$ kubectl apply -f https://raw.githubusercontent.com/dotnsf/dind_iks/main/dind.yml


初回のみイメージのダウンロードで少し時間がかかりますが、しばらく待つと dind という名前の Pod が1つ(コンテナは docker dind-daemon の2つ)起動します:
$ kubectl get pods

NAME READY STATUS RESTARTS AGE dind 2/2 Running 2 101m $ kubectl describe pod dind Name: dind Namespace: default Priority: 0 Node: 10.144.222.147/10.144.222.147 Start Time: Sun, 20 Feb 2022 21:52:46 +0900 Labels: Annotations: cni.projectcalico.org/containerID: 0c110243107d25c9e27b82202871504047d9ac691ad294d8f89bb1a9b114ca5e cni.projectcalico.org/podIP: 172.30.216.78/32 cni.projectcalico.org/podIPs: 172.30.216.78/32 kubernetes.io/psp: ibm-privileged-psp Status: Running IP: 172.30.216.78 IPs: IP: 172.30.216.78 Containers: docker: Container ID: containerd://0dbd05bc4171f96caaa7601a10e1b2f853511b4ed7087ab3958c016024c65f1c Image: docker:19.03 Image ID: docker.io/library/docker@sha256:ea1f0761c92b600417ad14bc9b2b3a30abf8e96e94895fee6cbb5353316f30b0 Port: Host Port: Command: docker run nginx:latest State: Running Started: Sun, 20 Feb 2022 22:53:02 +0900 Last State: Terminated Reason: Completed Exit Code: 0 Started: Sun, 20 Feb 2022 21:53:08 +0900 Finished: Sun, 20 Feb 2022 22:53:01 +0900 Ready: True Restart Count: 2 Environment: DOCKER_HOST: tcp://localhost:2375 Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-gz5fb (ro) dind-daemon: Container ID: containerd://5a6a15eefa9ebdf8a0dcc825f1c87fa4a5a37daab005a1d83e5df8f9a33ff7bb Image: docker:19.03-dind Image ID: docker.io/library/docker@sha256:c85365ad08c7f6e02ac962a8759c4a5b8512ea5c294d3bb9ed25fca52e9e22e5 Port: Host Port: State: Running Started: Sun, 20 Feb 2022 21:53:07 +0900 Ready: True Restart Count: 0 Requests: cpu: 20m memory: 512Mi Environment: DOCKER_TLS_CERTDIR: Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-gz5fb (ro) Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: kube-api-access-gz5fb: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: DownwardAPI: true QoS Class: Burstable Node-Selectors: Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 600s node.kubernetes.io/unreachable:NoExecute op=Exists for 600s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Created 43m (x3 over 103m) kubelet Created container docker Normal Started 43m (x3 over 103m) kubelet Started container docker Normal Pulled 43m (x2 over 103m) kubelet Container image "docker:19.03" already present on machine d


DinD 環境はあっけなく完成しました。


【(IKS の) DinD 内の docker を操作する】
次に完成した DinD 環境を実際に CLI で使ってみます。そのためにまずは Docker クライアントが使える環境のシェルにアタッチする必要があります。Docker クライアントは docker という名前のコンテナで動いていることが分かっているので、以下のコマンドを実行します:
$ kubectl exec -it dind -c docker -- /bin/sh

/ #

プロンプトが "/ #" という記号に変わればアタッチ成功です。ここからは docker CLI コマンドが実行できます。試しに "docker version" コマンドを実行するとクライアント&サーバー双方の docker バージョン情報(下の例ではどちらも "19.03.15")を確認できます:
/ # docker version

Client: Docker Engine - Community
 Version:           19.03.15
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        99e3ed8
 Built:             Sat Jan 30 03:11:43 2021
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.15
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       99e3ed8
  Built:            Sat Jan 30 03:18:13 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.3.9
  GitCommit:        ea765aba0d05254012b0b9e595e995c09186427f
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683


実際にサーバーイメージをデプロイして動作確認してみましょう。というわけで、まずは nginx イメージを 8000 番ポートでデプロイしてみます:
/ # docker run -d --name ningx -p 8000:80 nginx

成功したら早速アクセスして動作確認を・・・と思ったのですが、この docker コンテナには HTTP クライアントが curl 含めてインストールされていないようでした。というわけで動作確認用のコマンドもインストールしておきます。とりあえず curl と w3m あたりでいいですかね。。:
/ # apk add --update curl

/ # apk add --update w3m

インストールが成功したら、まずは curl で http://localhost:8000/ にアクセスしてみます:
/ # curl http://localhost:8000/

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

うぉっ、と。少なくとも HTTP サーバーとして動いているらしいことは確認できたのですが、これだとちょっと見にくいですね。というわけで、先程一緒にインストールした w3m で確認してみます:
/ # w3m http://localhost:8000/

Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

2022022101


かろうじて Nginx のトップ画面らしきものが確認できました。というわけで一応動いていると思います。


【まとめ】
というわけで DinD が IBM Cloud の 30 日無料版 Kubernetes クラスタ環境でも動かせることが確認できました。

とはいえ、もともとは Docker Desktop の代替になるような Docker 環境を探していたことに立ち返ると、この(クライアント側のような) CLI だけの Docker 環境はどうしても使い道が限られてしまうように感じます。ある程度、Docker を理解している人向けに、CLI だけで完結する使いみちであればなんとか、といったところでしょうか。


本当はここで作った Pod を外部に EXPOSE できるといいんですが、自分で試行錯誤している限りではまだうまく行ってません。もし方法をご存じの方がいらっしゃったら是非教えてください。


(2022/02/21 追記ここから)
試行錯誤の中で外部公開する方法がわかりました。

上述の "$ kubectl apply -f ..." コマンドを実行する箇所の内容を以下のように変更してください:
$ kubectl apply -f https://raw.githubusercontent.com/dotnsf/dind_iks/main/dind_expose.yml

なお、ここで指定している dind_expose.yml の内容は以下のようなものです。Pod を Deployment に書き換えた上で Service オブジェクトを追加して、8000 番ポートでの待受けを 30800 番ポートから転送するように(この後の作業で 8000 番ポートで待ち受けるアプリケーションをデプロイする想定で)あらかじめ公開しています:
apiVersion: v1
kind: Service
metadata:
  name: dind
spec:
  selector:
    app: dind
  ports:
  - port: 8000
    name: port8000
    protocol: TCP
    targetPort: 8000
    nodePort: 30800
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dind
spec:
  selector:
    matchLabels:
      app: dind
  replicas: 1
  template:
    metadata:
      labels:
        app: dind
    spec:
      containers:
        - name: docker
          image: docker:19.03
          command: ["docker", "run", "nginx:latest"]
          env:
            - name: DOCKER_HOST
              value: tcp://localhost:2375
        - name: dind-daemon
          image: docker:19.03-dind
          env:
            - name: DOCKER_TLS_CERTDIR
              value: ""
          resources:
            requests:
              cpu: 20m
              memory: 512Mi
          securityContext:
            privileged: true

このコマンドの後、"$ kubectl get all" を実行すると以下のような結果になります:
$ kubectl get all
NAME READY STATUS RESTARTS AGE pod/dind-7d8546bc8-fxbhw 2/2 Running 1 19s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/dind NodePort 172.21.8.233 8000:30800/TCP 21s service/kubernetes ClusterIP 172.21.0.1 443/TCP 3d10h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/dind 1/1 1 1 20s NAME DESIRED CURRENT READY AGE replicaset.apps/dind-7d8546bc8 1 1 1 21s

以前の方法では Pod 名は "dind" 固定だったのですが、ここでは "dind-" に続けてランダムな文字列が付与されています(上例では "dind-7d8546bc8-fxbhw" となっています)。この Pod 名を指定して以下を実行して docker コンテナのシェルに接続します:
$ kubectl exec -it dind-7d8546bc8-fxbhw -c docker -- /bin/sh

/ #

この後は以前の方法と同様にして docker コンテナ内で NGINX を 8000 番ポートで起動します:
/ # docker run -d --name ningx -p 8000:80 nginx

これで以前と同様に k8s のワーカーノード内で(NodePort サービスを使って) NGINX が 8000 番ポートで起動します。ただ今回は Service オブジェクトで 8000 番リクエストを 30800 番ポートで外部公開しているので、 http://(ワーカーノードのパブリックIPアドレス):30800/ にアクセスすればクラスタ外部からでもこの NGINX に接続できるようになっています。


ワーカーノードのパブリック IP アドレスは IBM Cloud の IKS 環境であれば、IBM Cloud ダッシュボード画面から確認することができます(この例では "169.51.206.71" となっています):
2022022102


というわけで、改めてウェブブラウザで http://169.51.206.71:30800/ にアクセスしてみると、、、期待通りの画面が表示されました! IBM Cloud の30日無料版 Kubernetes クラスタ環境で構築した DinD のコンテナを外部に公開することができることが確認できました:
2022022100


というわけで、30 日間無料の IBM Cloud Kubernetes 環境を使って、Docker および Kubernetes クラスタの実行環境を構築することができました。

(2022/02/21 追記ここまで)
 

昨年、ブログを使ってこのような夏休み企画(休んでないけど)を実施しました:
「30 日後に死ぬ k8s」


IBM Cloud から提供されている k8s 環境は(ワーカーノード1つなどの条件の下で)30 日間無料で使えます。この環境を使って 30 日間で毎日1つずつ、計 30 種類のコンテナをデプロイして動かす、というものでした。後に追加で動作確認できたり、動作確認はできなかったがデプロイできてるっぽいものもあったのですが、その際も IBM Db2 は公式イメージを docker で動かすことはできたのですが、この k8s 環境ではうまくデプロイできずにいました。

が、最近になって改めて挑戦し、今度は同環境で IBM Db2 コンテナを動かすことができました。Db2 クライアントからの接続も確認できました。以前できなくて今回できた理由ははっきりとはわかっていませんが、自分の腕が上がったのか、イメージ側の更新と関係があるのか、あるいはその両方か・・・ ともあれ、遅ればせながら IBM Db2 コンテナイメージを IBM Cloud の 30 日間無料 k8s クラスタにデプロイして動かす方法を紹介します。


【30 日間無料の k8s クラスタを用意】
こちらの記事を参照して、IBM Cloud の 30 日間無料 k8s クラスタをセットアップしてください。また具体的な操作をする際に必要な ibmcloud CLI や kubectl CLI といったコマンドツール類のインストール手順も含まれています(リンク先最後の "$ kubectl get all" コマンドが正しく実行できる状態にしてください):
http://dotnsf.blog.jp/archives/1079287640.html


なおリンク先でも触れられていますが、この k8s クラスタ環境はシングルワーカーノードです。イメージをデプロイした後に "type = NodePort" を指定することでサービスを公開できます(Ingress などが提供されていないため、他の方法でサービス公開はできません)。


【IBM Db2 コンテナイメージ】
公式な IBM Db2 コミュニティ版のコンテナイメージはこちらから無料で提供されています:
https://hub.docker.com/r/ibmcom/db2


同ページ内の説明文によると、この無料コミュニティ版コンテナイメージを動かす前提条件として、「CPU: 4コア以下、メモリ: 16GB 以下」が挙げられています:
2022011801


一方で IBM Cloud の 30 日間無料 k8s クラスタのワーカーノードは「CPU: 2 コア、メモリ: 4GB」であることがわかっています:
2022011802


一応、条件を満たした環境で使う、ということになります。なおコンテナイメージのリンク先情報によると、2022/01/18 時点で特にバージョンを指定せずにこのコンテナを利用しようとすると、最新版である v11.5.7.0 が使われるようです(以下の内容はこのバージョンで動作確認しています):
2022011803



【IBM Db2 を k8s にデプロイするための yaml ファイル】
色々試行錯誤してできあがった IBM Cloud の 30 日間無料 k8s クラスタにデプロイできるよう調整した yaml ファイルはこちらです:
https://github.com/dotnsf/yamls_for_iks/blob/main/db2_deployment_nodeport.yaml

このファイルを指定して、
$ kubectl apply -f https://raw.githubusercontent.com/dotnsf/yamls_for_iks/main/db2_deployment_nodeport.yaml

とすることでデプロイできます(その場合は db2inst1 ユーザーのパスワードは db2inst1 、サンプル(SAMPLE)データベースは作成、それとは別に db2inst1 ユーザーから使える mydb という空のデータベースも作成する、というオプションが初期設定されています)。

あるいは以下のように一度 wget コマンドでローカルにダウンロードしてから内容をカスタマイズして、その後に改めて kubectl コマンドでデプロイすることも可能です:
$ wget https://raw.githubusercontent.com/dotnsf/yamls_for_iks/main/db2_deployment_nodeport.yaml

(ダウンロードした db2_deployment_nodeport.yaml ファイルをカスタマイズ)

$ kubectl apply db2_deployment_nodeport.yaml


例えば db2inst1 ユーザーのパスワードを変えたい場合は 38 行目を変更、サンプルデータベースを作る必要がなければ 39, 40 行目を削除、mydb データベースもこの時点で作る必要がなければ 41, 42 行目を削除、といった具合です。

なお後者のカスタマイズをする場合に指定可能なオプション内容については公式ページ内"Advanced Configuration Options" 欄を参考に編集してください。

"kubectl get all" コマンドでデプロイ後のクラスタの様子を確認してみます:
$ kubectl get all

NAME                            READY   STATUS    RESTARTS   AGE
pod/db2-54ff649944-w7d24        1/1     Running   0          2d14h
pod/hostname-7b4f76fd59-c8b2l   1/1     Running   0          8d

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)           AGE
service/db2          NodePort    172.21.54.211           50000:30500/TCP   2d14h
service/hostname     NodePort    172.21.105.49           8080:30080/TCP    8d
service/kubernetes   ClusterIP   172.21.0.1              443/TCP           8d

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/db2        1/1     1            1           2d14h
deployment.apps/hostname   1/1     1            1           8d

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/db2-54ff649944        1         1         1       2d14h
replicaset.apps/hostname-7b4f76fd59   1         1         1       8d

↑のように Db2 関連の pod, service, deployment, replicaset が1つずつ追加されていればコマンドは成功しています。 また service を見ると 50000 番ポート(Db2 の標準動作ポート)が 30500 番で公開されていることも確認できます。つまり Db2 は
 (ワーカーノードのIPアドレス※):30500
で公開されて動いていることがわかります(※ワーカーノードの IP アドレス確認方法はこちらを参照してください)。


【動作確認】
では実際に Db2 サーバーが正しく動作していることを動作確認してます。。。 と、実はここが大きな問題でした。Db2 サーバーは正しくデプロイされて動作しているのですが、Db2 サーバーに接続試験を行おうとすると、その環境準備がちょっと面倒だったりするのです。

Db2 サーバーに CLI でアクセスしようとすると Db2 クライアントライブラリのダウンロード&インストールが必要です。Node.js などのプログラムから接続するにもクライアントライブラリが必要になります。この Db2 クライアントライブラリをインストールするには、無料版の Db2 Community Edition をダウンロードしてインストールする必要があり、ここで急にハードルが上がってしまいます。

そこでこれらの手間を省くために別の動作確認方法を考えました。それは「Db2 のコンテナインスタンスをもう1つ作って、そこからリモートサーバーにアクセスする」という方法です。マネージドサービスと異なり、Db2 サーバーのコンテナイメージからはライブラリセットアップ済みの CLI も使えるのです。そのための Db2 コンテナをもう1つ追加で作ってクライアントとして使うことで(手間という意味では)比較的容易に動作確認用クライアント環境が用意できると思いました。

というわけで、先程の yaml ファイルを少し変更(サンプルDB 作らず、mydb DBも作らず、ポート番号は 30501 に変更、コンテナの名称も db2 から db2cli に変更)した yaml ファイルを使って、(機能的にはサーバー機能もインストール済みですが)新しい Db2 クライアントコンテナを作成します:
$ kubectl apply -f https://raw.githubusercontent.com/dotnsf/yamls_for_iks/main/db2cli_deployment_nodeport.yaml

作成後にクラスタ内の様子を kubectl コマンドで確認すると、新しい db2cli インスタンスが起動しているはずです:
$ kubectl get all
NAME                            READY   STATUS    RESTARTS   AGE
pod/db2-54ff649944-w7d24        1/1     Running   0          2d14h
pod/db2cli-86d476659d-m7c6d     1/1     Running   0          9s
pod/hostname-7b4f76fd59-c8b2l   1/1     Running   0          8d

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)           AGE
service/db2          NodePort    172.21.54.211           50000:30500/TCP   2d14h
service/db2cli       NodePort    172.21.112.13           50000:30501/TCP   11s
service/hostname     NodePort    172.21.105.49           8080:30080/TCP    8d
service/kubernetes   ClusterIP   172.21.0.1              443/TCP           8d

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/db2        1/1     1            1           2d14h
deployment.apps/db2cli     1/1     1            1           11s
deployment.apps/hostname   1/1     1            1           8d

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/db2-54ff649944        1         1         1       2d14h
replicaset.apps/db2cli-86d476659d     1         1         1       11s
replicaset.apps/hostname-7b4f76fd59   1         1         1       8d

ここからは db2cli コンテナからの作業となります。まずは db2cli コンテナ上のシェルにログインする必要があります。まずは IBM Cloud の k8s サービスからダッシュボードにアクセスします:
2022011801


ダッシュボード画面左メニューから「ポッド」を選び、ポッド一覧から "db2cli" で始まる名前のポッドを探し、画面右のメニューから「実行」を選択します:
2022011802


これで該当 Pod のシェルをブラウザからリモート操作できるようになります:
2022011803


以下の手順で(最初に作った方の)Db2 サーバーに接続して SQL を実行する、という動作確認をします。

まずは Db2 を利用可能なユーザー(db2inst1)にスイッチします(プロンプトが # から $ へ切り替わります):
# su - db2inst1
$

次に Db2 コマンドをインタラクティブに実行できるシェルに切り替えます(プロンプトが $ から db2 => に切り替わります):
$ db2
db2 =>

この状態から Db2 サーバーに接続したり、ログインしたり、SQL を実行したりできます。まずは Db2 サーバーへの接続が必要です。Db2 ではリモートサーバーやデータベースを「カタログ」という単位で管理するので、まず最初にリモート Db21 サーバーを db2onk8s という名前でカタログに登録します。以下のコマンドを実行します(1.2.3.4 部分はワーカーノードの IP アドレスに置き換えてください):
db2 => catalog tcpip node db2onk8s remote 1.2.3.4 server 30500

次にこのサーバー上の SAMPLE データベースをカタログに登録します:
db2 => catalog database sample at node db2onk8s

これで Db2 クライアントから Db2 サーバー上の SAMPLE データベースに接続するためのカタログができました。

ではこのデータベースカタログを使って Db2 サーバー上の SAMPLE データベースに接続します。このコマンドを入力した後に db2inst1 ユーザーのパスワードを聞かれるので、Db2 サーバーのコンテナを作成した時に指定した db2inst1 ユーザーのパスワード(変更していない場合は db2inst1)を入力してください:
db2 => connect to sample user db2inst1
Enter current password for db2inst1:


正しいパスワードを入力すると目的のデータベースに接続します。この状態で Db2 クライアントから Db2 サーバーに接続しています:
db2 => connect to sample user db2inst1
Enter current password for db2inst1:

   Database Connection Information

 Database server        = DB2/LINUXX8664 11.5.7.0
 SQL authorization ID   = DB2INST1
 Local database alias   = SAMPLE

db2 => 

SQL を使って Db2 サーバーの SAMPLE データベースのテーブルからレコードを取り出したり、追加・変更・削除といった操作が可能です:
db2 => select * from employee;

EMPNO  FIRSTNME     MIDINIT LASTNAME        WORKDEPT PHONENO HIREDATE   JOB      EDLEVEL SEX BIRTHDATE  SALARY      BONUS       COMM       
------ ------------ ------- --------------- -------- ------- ---------- -------- ------- --- ---------- ----------- ----------- -----------
000010 CHRISTINE    I       HAAS            A00      3978    01/01/1995 PRES          18 F   08/24/1963   152750.00     1000.00     4220.00
000020 MICHAEL      L       THOMPSON        B01      3476    10/10/2003 MANAGER       18 M   02/02/1978    94250.00      800.00     3300.00
000030 SALLY        A       KWAN            C01      4738    04/05/2005 MANAGER       20 F   05/11/1971    98250.00      800.00     3060.00
000050 JOHN         B       GEYER           E01      6789    08/17/1979 MANAGER       16 M   09/15/1955    80175.00      800.00     3214.00
000060 IRVING       F       STERN           D11      6423    09/14/2003 MANAGER       16 M   07/07/1975    72250.00      500.00     2580.00
000070 EVA          D       PULASKI         D21      7831    09/30/2005 MANAGER       16 F   05/26/2003    96170.00      700.00     2893.00
000090 EILEEN       W       HENDERSON       E11      5498    08/15/2000 MANAGER       16 F   05/15/1971    89750.00      600.00     2380.00
000100 THEODORE     Q       SPENSER         E21      0972    06/19/2000 MANAGER       14 M   12/18/1980    86150.00      500.00     2092.00
000110 VINCENZO     G       LUCCHESSI       A00      3490    05/16/1988 SALESREP      19 M   11/05/1959    66500.00      900.00     3720.00
000120 SEAN                 O'CONNELL       A00      2167    12/05/1993 CLERK         14 M   10/18/1972    49250.00      600.00     2340.00
000130 DELORES      M       QUINTANA        C01      4578    07/28/2001 ANALYST       16 F   09/15/1955    73800.00      500.00     1904.00
000140 HEATHER      A       NICHOLLS        C01      1793    12/15/2006 ANALYST       18 F   01/19/1976    68420.00      600.00     2274.00
000150 BRUCE                ADAMSON         D11      4510    02/12/2002 DESIGNER      16 M   05/17/1977    55280.00      500.00     2022.00
000160 ELIZABETH    R       PIANKA          D11      3782    10/11/2006 DESIGNER      17 F   04/12/1980    62250.00      400.00     1780.00
000170 MASATOSHI    J       YOSHIMURA       D11      2890    09/15/1999 DESIGNER      16 M   01/05/1981    44680.00      500.00     1974.00
000180 MARILYN      S       SCOUTTEN        D11      1682    07/07/2003 DESIGNER      17 F   02/21/1979    51340.00      500.00     1707.00
000190 JAMES        H       WALKER          D11      2986    07/26/2004 DESIGNER      16 M   06/25/1982    50450.00      400.00     1636.00
000200 DAVID                BROWN           D11      4501    03/03/2002 DESIGNER      16 M   05/29/1971    57740.00      600.00     2217.00
000210 WILLIAM      T       JONES           D11      0942    04/11/1998 DESIGNER      17 M   02/23/2003    68270.00      400.00     1462.00
000220 JENNIFER     K       LUTZ            D11      0672    08/29/1998 DESIGNER      18 F   03/19/1978    49840.00      600.00     2387.00
000230 JAMES        J       JEFFERSON       D21      2094    11/21/1996 CLERK         14 M   05/30/1980    42180.00      400.00     1774.00
000240 SALVATORE    M       MARINO          D21      3780    12/05/2004 CLERK         17 M   03/31/2002    48760.00      600.00     2301.00
000250 DANIEL       S       SMITH           D21      0961    10/30/1999 CLERK         15 M   11/12/1969    49180.00      400.00     1534.00
000260 SYBIL        P       JOHNSON         D21      8953    09/11/2005 CLERK         16 F   10/05/1976    47250.00      300.00     1380.00
000270 MARIA        L       PEREZ           D21      9001    09/30/2006 CLERK         15 F   05/26/2003    37380.00      500.00     2190.00
000280 ETHEL        R       SCHNEIDER       E11      8997    03/24/1997 OPERATOR      17 F   03/28/1976    36250.00      500.00     2100.00
000290 JOHN         R       PARKER          E11      4502    05/30/2006 OPERATOR      12 M   07/09/1985    35340.00      300.00     1227.00
000300 PHILIP       X       SMITH           E11      2095    06/19/2002 OPERATOR      14 M   10/27/1976    37750.00      400.00     1420.00
000310 MAUDE        F       SETRIGHT        E11      3332    09/12/1994 OPERATOR      12 F   04/21/1961    35900.00      300.00     1272.00
000320 RAMLAL       V       MEHTA           E21      9990    07/07/1995 FIELDREP      16 M   08/11/1962    39950.00      400.00     1596.00
000330 WING                 LEE             E21      2103    02/23/2006 FIELDREP      14 M   07/18/1971    45370.00      500.00     2030.00
000340 JASON        R       GOUNOT          E21      5698    05/05/1977 FIELDREP      16 M   05/17/1956    43840.00      500.00     1907.00
200010 DIAN         J       HEMMINGER       A00      3978    01/01/1995 SALESREP      18 F   08/14/1973    46500.00     1000.00     4220.00
200120 GREG                 ORLANDO         A00      2167    05/05/2002 CLERK         14 M   10/18/1972    39250.00      600.00     2340.00
200140 KIM          N       NATZ            C01      1793    12/15/2006 ANALYST       18 F   01/19/1976    68420.00      600.00     2274.00
200170 KIYOSHI              YAMAMOTO        D11      2890    09/15/2005 DESIGNER      16 M   01/05/1981    64680.00      500.00     1974.00
200220 REBA         K       JOHN            D11      0672    08/29/2005 DESIGNER      18 F   03/19/1978    69840.00      600.00     2387.00
200240 ROBERT       M       MONTEVERDE      D21      3780    12/05/2004 CLERK         17 M   03/31/1984    37760.00      600.00     2301.00
200280 EILEEN       R       SCHWARTZ        E11      8997    03/24/1997 OPERATOR      17 F   03/28/1966    46250.00      500.00     2100.00
200310 MICHELLE     F       SPRINGER        E11      3332    09/12/1994 OPERATOR      12 F   04/21/1961    35900.00      300.00     1272.00
200330 HELENA               WONG            E21      2103    02/23/2006 FIELDREP      14 F   07/18/1971    35370.00      500.00     2030.00
200340 ROY          R       ALONZO          E21      5698    07/05/1997 FIELDREP      16 M   05/17/1956    31840.00      500.00     1907.00

  42 record(s) selected.


この時点で k8s ダッシュボード画面はこのようになっています。必要に応じて続けてコマンドを実行することもできます:
2022011800


操作を終了して接続を切るには quit コマンドを実行します:
db2 => quit

$ exit

# 


改めて k8s 上にデプロイした Db2 サーバーコンテナ(とクライアントコンテナ)が期待通りに動いていることが確認できました。


このページのトップヘ