2018localYouser技術のお話し前提事項ここで記載する内容なんですが、以下が前提になっています。Calico)、DBコンテナ環境やらRedisコンテナ環境やらはすでに出来上がっているちょっと期待はずれな内容かもしれませんので、先に記載しておきます。今回やった事ずばり、Mastodonエンジンのコンテナ化です。Mastodonはコンテナ構成のものも払い出されては居ますが、あくまでdocker-composeを前提としたものであり、Kubernetesのようなクラスタ構成を想定したものとはなっていません。私自身、以前Dockerコンテナ型のインスタンスを立てて挫折した経験があるんですが、当時は全くコンテナ技術に対する知見がなく、理解しようとしても中々理解至るのに苦慮しましたが、今回自宅環境でKubernetesに手を出した、ということもあり、思い切ってやってみることにしました。目的は「リソースの節約」です。自宅環境、一応こう見えても物理サーバの台数は2台、CPU総合計16Core、メモリ148GB、ストレージ実効4.2TiBほどあるんですが、そこで30台ほど仮想サーバ動かしてたら流石にパツパツになってきました。仮想アプライアンスのメモリ使用量が比較的デカイ状況があります。この度ちょっとコンテナに手を出して、手始めにKubernetesクラスタを組み、WWWインフラをコンテナ化してみたところ、これがすごくサクサク動く。よし、次にメモリ食ってるのはどこだ?って調べてみたところ、Mastodonが該当してました。よし、これをサクサク軽く動くようにするぞと。現状の構成こんな感じになっています。実は前回の記事投稿後、コンテナ基盤の拡張を少々行っています。DB/Redisについてはすでにコンテナ化を実施してたわけですが、Mastodonエンジンのコンテナ化はまだ行ってませんでしたし、nginxのコンテナ化は逆にロードバランサがうまく機能せずに失敗してました。ところで、nginxってラウンドロビン方式であれば、それ自身をロードバランサに出来るんですね。そこで、元々あるAPサーバのnginxを残し、すべてコンテナ化してしまい、かつ、元々APサーバにあるnginxをロードバランサとして稼働するインスタンスに生まれ変わらせてみることにしました。いうわけで、今回紹介する対象範囲は以下のとおりです。DB/Redisに関しては、色々情報があると思うので、他をあたってもらえればと思います。Mastodonの情報を読み解くそもそも、MastodonのDocker版構成情報ってどうなってるんでしょう?って思ってgithubの内容を参照しました。latestであれば、以下ページにdocker-compose.ymlの情報があります。https://github.com/tootsuite/mastodon/blob/master/docker-compose.ymlこの中で、DB/Redis/Elasticsearchを除く3つのコンポーネントについて読み解きをしてみようと思います。railsで処理されたコンテンツを応答するためのWebサーバで、内部ではpumaと言うソフトウェアが動いています。pumaが全部のWeb処理をしてるわけではなく、静的コンテンツの処理は前段のnginxが行う形になっています。system3つのディレクトリが永続領域として設定されています。Mastodon-streamingの構造Mastodon-streamingはタイムラインのストリーミング処理を行っているサーバで、これがうまく機能しないとタイムラインの動的更新ができません。コンテナは4000/tcpで待ち受けしており、webサーバとは異なり、永続領域の設定はありません。pushの4種類が存在します。キュー構成は柔軟に行うことができ、1本のキューに複数の機能をもたせることができれば、また複数のキューに分割させることもできます。public領域の内、packs/systemの2領域に対する永続領域設定があります。全体を読んでみるとこんな構成になっていたdocker-composeのYAMLファイルを眺める限り、こんな構成になっているのが読み取れました。外のnginxとやり取りするコンテナがExternal-networkへ接続しています。全サーバ共通でInternal-Networkに接続しており、どうやら複数セグメントで構成される感じになってました。永続ボリュームに関してもそれぞれ必要なところだけ設定がなされており、実はバージョン1.xの頃と比べると構成がだいぶしっかりしたものになっています。全体の構成としては以下の通りになります。Calico」という製品を導入しており、それをカスタマイズして10.240.0.0/16空間をコンテナ用ネットワークとして展開しています。k8s環境では、大まかに3つのネットワーク階層が構成されることが多いです。物理ネットワーク所謂ホストが接続するネットワークです。一部のシステムコンテナがこれにブリッジ接続をしていたりします。外部ネットワークをやり取りする際はここが中継地点になります。基本的にはポート払い出しによって外部との接続を担保します。クラスタネットワーク主にk8sリソースの「Service」リソースが使用します。IPと外部の接続はできません。k8sコンテナ側で呼び出すIPと考えたほうが良いです。IPとのICMP通信は許可されておらず、本当のサービスポートとしかアクセスができません。Pingが通らないのは仕様ということでびっくりなさらぬようにー。コンテナネットワークネットワークコンポーネントが払い出すネットワークです。デフォルト設定は製品により異なります。weaveの場合は一気通貫の12bitマスクセグメントが1つ用意されます。Calicoの場合は全体として16bitマスク、ノード毎に24bitマスクのセグメントが用意されます。ユーザコンテナやdashboard、kube-dnsがこのネットワークに接続します。ホストも仮想ブリッジを形成してこのネットワークに接続しています。フロントアクセス、バックエンドアクセスに関しては後述していますので、続きを読んでいただければと思います。フロントアクセス部分の通信設計フロント部分のネットワーク通信は以下のようになります。外部サーバからのアクセスは今回、NodePortモードでサービスを構成することにより、クラスタを構成するノード全てに待ち受けポートを配備する構成とします。上流のMastodonフロントエンドである旧APサーバをロードバランサ兼一部コンテンツWebサーバとして動作させ、全体として可用性を向上させた構成とします。バックエンドの通信設計バックエンドの通信部分は以下の通りとしています。MastodonコアエンジンとDB/Redis間は直接コンテナへ通信するのではなく、Serviceに割り当てられたFQDNを通じてアクセスする方針とします。これは、設定変更のオーバーヘッドを減らすためです。k8s環境には、kube-dnsというDNSサーバコンテナが存在しており、これがコンテナネットワークに接続されています。」と言う形で割り当てられ、kube-dns内に登録されています。コンテナは再作成されるたびに名称が変わったりIPアドレスが変わったりします。Serviceはそれを緩衝してくれる存在と考えればよいかと思います。ストレージ設計ストレージ設計ですが、前段に記載したdocker-compose.ymlで読み取った内容に従って構成を行います。NAS上にmastodonユーザのホームディレクトリを作成してマウントしており、それに合わせる形でディスクマウントの構成を作っています。コンテナからは永続ボリュームを使用してマウントします。hostPathを使うのも手ですが、それをやると今後ノードが増えるたびにノードに対してNFS設定をしなければならなくなるので、永続ボリュームの使用があるべき姿なのかなと思います。複数台からアクセスできるよう、マウント方式はReadWriteManyを指定します。イメージ設計コンテナのベースとなるイメージですが、下図の流れで作成・展開します。本当は、mastodonのgithubサイトから手に入るDockerfileを用いて構築したかったのですが、残念ながらうまくいかず、どうしてもPostgreSQLのライブラリが動いてくれませんでした。いうわけで、今回コンテナの構築に際しては私がコレまでに構築して動かしてきたCentOSベースの流れをそのままコンテナに転用したやり方で作っています。これは最適解ではないと思っていて、やっぱりいちばん効率の良いやり方としては、githubサイトからDockerfileを落としてきて、それをカスタマイズして作るのが良いのでしょう。ちなみに作るイメージは1つだけでいいです。そのイメージでpumaもsidekiqもstreamingもどれも動きます。コンテナ設計コンテナの設計内容としては以下の通りとなります。コンテナの構築にはReplicationControllerを使うことにしました。理由としては、記述が最もシンプルかつ、ノード台数の変更が容易に効く点が挙げられます。Scale設定をすることにより、台数を増やすことも減らすことも止めることも出来るのは非常に便利です。他のリソースのように複雑なYAML書式が必要ないという点も嬉しいポイントでした。コンテナを作成する上での留意ポイントコンテナ・・・・・k8sではPodと呼ぶのですが、これの定義を行う上で必要なパラメータは原則docker-compose.yamlで定義された内容に従えばいいと思います。私の場合、1年ぐらい前からインスタンスを動かしてきた関係上、書式が一部古くなっているところがあります。そこは、本来バージョンに合わせて変えるべきですが、私の場合は実績を重視して、コンテナの定義をsystemdの記載に合わせています。以下、参考にした箇所についてまとめていきます。その他、実行コマンドやWorkingDirectoryはここに書かれている内容を使用することになります。基本的な路線はWebと同じですが、設定として拾う箇所は圧倒的に少ないです。現行バージョンではどうやらnpmではなく、yarnを使用することが推奨されているようです。デベロッパー界隈には居ない人間なので、いまいちこの辺りがピンときてませんwMastodon-Sidekiqの構成Mastodonインスタンスを構築されている方はおわかりかと思いますが、Sidekiqのキュー配置を設定するのは「ExecStart」の箇所になります。ここで、-qを使って何のキューを適用するかを記載することになります。DB_POOLと-cオプションで指定する数値は一致させる必要があります。こちらも、複数分散構成にするため、数値を引き下げる必要があります。このようにして、設定内容を参考にしながら各種リソースのYAMLファイルを書いていくことになります。任意のディレクトリを作成して、まずはOSベースイメージの作成を行います。Dockerfileを以下のように作成します。途中、mastodonユーザのUID/GIDを設定していますが、これは既存環境のサーバに割り当てられているUID/GIDに合わせるためです。最後、3000番ポートと4000番ポートをEXPOSEして、どちらの用途にもイメージが使用できるようにしています。4000続いて、このDockerfileを用いてコンテナを1台作成します。.コンテナへログインします。mastodon」でmastodonユーザにもなれます。/home/mastodonも作成済みの状態です。そこへ、既存サーバのホームディレクトリをコピーして持ってきます。excludelist.lstで、コピーしておいてなんですが、rbenvとrubyを入れ直します。入れっぱなしで動かそうとすると、エラーが発生したため、この措置をとることにしました。rehashruby実行環境が整ったので、改めてアプリケーションインストール作業を実施します。.env.production-----REDIS_HOST=ext-redis-server.bluecore-net.svc.cluster.localREDIS_PORT=6379DB_HOST=pgset-primary.bluecore-net.svc.cluster.localDB_PORT=5432----ここでミソなのが、REDIS_HOSTとDB_HOST指定のところです。Serviceで定義するホスト名を指定しています。Serviceリソースは仮にNodePort方式で定義しても、クラスタIPが提供されるようになっています。クラスタ内であれば、そこから普通にポートアクセスが行えるようになっています。先にも言ったように、コンテナ環境はコロコロIPアドレスやホスト名が変わるので、そういう環境においては、このサービスリソースが提供するFQDNに従うことが望ましいです。assets:precompileの実行をします。assets:precompileこれで下地ができたので、「Ctrl+P」+「Ctrl+Q」を押してコンソールを抜けます。次に構成確定を一度行います。⇒少し時間がかかる上記、commit操作には少し時間がかかると書いていますが、実際には結構なファイルの操作をしているため、十数分ぐらいかかると思います。結果として、私の環境ではでっかいコンテナイメージができてしまいました・・・トホホ。GB一旦バックアップの意味もこめて、プライベートリポジトリに突っ込みます。192.168.100.171:5000/mstdn/engine:201804xxbこのイメージをベースに、今度はデフォルトユーザをmastodonにするためのDockerfileを適用してイメージをBuildします。192.168.100.171:5000/mstdn/engine:201804xxcこれで、イメージが出来上がりました。次はk8sリソースの作成です。作成するリソース作成するリソースはこれだけあります。pv-nfs-mstdn-front-assetsリソースの作成リソースの作成は「PersistentVolume」⇒「PersistentVolumeClaim」⇒「ReplicationController」⇒「Service」の順で作る感じになります。(私の環境の場合)」で確認するのも良いですし、dashboardを見るというのも手です。CrashBackOffが発生していないか、リブートが発生していないかどうかを見ましょう。6h取り敢えずみんなRunningになってれば大丈夫かな。コマンドを叩いてみるとかすると良いかもしれません。dashboardなんかは結構便利で、サービス画面を見ることで、正しくサービスポートにPodsが紐ついているかどうかが確認できます。上記例はpumaサービスリソースの画面ですが、もしEndpointsに1台もPodsが表示されていなければ、その場合はラベルセレクターの設定を間違っているか、ReplicationControllerのTemplate部分でラベル設定を間違えているかの何れかを起こしているので要チェックです。既存APサーバで以下の通り設定変更を行います。/500.html;}nginxを再起動します。nginxインスタンスのURLをブラウザで叩き、無事にアクセスできたら成功です。実際、どう?軽いです。動作がかなり軽いです。VM単騎でやってた時は、どうしてもCPUがうまく使用しきれてなかったようで、それによるパフォーマンスの目詰まりが若干あったのですが、コンテナ化したことで、複数用意したコンテナがプロセスとして動くため、CPUが効率良く使えるようになったのかなーという気がします。́Д`)ハァ...でも、コレをきっかけにかなりのことが楽しめたのと、理解を深めることができたのと、いろいろな意味で美味しい日々が過ごせました。ありがとう!Mastodon!ありがとう!Kubernetes!ありがとう!Docker!ってところですかね?おまけ:ログの扱いについてk8sでは、アプリケーションログはコンテナの標準出力経由からsyslogに吐き出されています。こんな形で吐き出されていることを確認しました。Calicoのログとか混ざってたりするのですが、「journal」から始まるメッセージは基本的にk8s絡みのものであり、bundle系列のログはだいたい吐き出されています。コンテナの性質上、コアとなるプロセスはフォアグラウンド動作させてなんぼなので、大抵は標準出力に表示させてるだろうと思われるのですが、例えばサーバを単純にコンテナ化させた場合なんかは、ログ記録の設定等残っている可能性があり、注意が必要です。(永続ボリューム組まない限り落としたら吹っ飛ぶし、そもそもそのために永続ボリューム構成するのも馬鹿らしいし)とまぁ、ログが取れる素地はそれなりにあるようなので、そこでログ監視すると、それなりに良いのかもしれません。高度なログ管理をしたければ、その場合はfulentdなどの力を借りる必要があると思われます。ついでにKubernetesは非常にリソース使用が少なく、検証リソースが少ない場合、もしそれがミドルウェア寄りの内容であれば、Kubernetesを使ったりDockerを使ったりするのも一つの手なのかなと思いました。私のように自宅サーバで運用してる中で、ある程度構成が固まって触らないものとか。サーバとしての運用はそれはそれで必要なんですが、「このサービスにあまりリソース切り出したくないなー」「本番化してだいぶ立つけどもう少し可用性上がらないかなー」とか思ったときに役に立つなぁと。自宅サーバ内の話としてではありますけれども。いう意味でDocker/docker-compose/Kubernetesを学ぶのって良いのかもしれません。コンテナ環境として、ミニマムからエンタープライズちょい手前ぐらいのレンジで多くの学びが得られるのかなと思います。エンタープライズ向けとしては、コレを進化させたOpenShiftと言うプラットフォームがRedHatから出ていますが、なんだか要求リソース量が半端なくて、ちょっと組むには組めなかったです。(業務の中で評価しようとしたんだけど、プロキシ越しだとどーしてもうまく組めなかった)その他、課題など。実はまだ完全に移行できたわけではなく、いくつか不具合があったりします。その辺りは追々解決を進めていきたいなぁとは思ったりしています。IPv6に対応できてない。コンテナをIPv6に対応させるのって意外と難しい。Calicoに変更した理由がこれ。Replicationの関係性、Master/Slaveの読み書きの区別の付け方がわかっておらず、ほとんど機能が無駄になってる。́Д`)ハァ...あ、ところでこんなことやってんの俺だけだろうなーふふ~んとか思ってたら、すでにやっておられる方がいらっしゃいました。!しかも一年前ときたもんだ。https://blog.haramishio.xyz/post/000022/やっぱり自分自身色々遅れてるんかなーとか、まだまだ上には上がいるんだなーとか、そういうのを痛感しながら今日も過ごしてまぁす。