ドローンクラウドサービス開発記録 〜Part 3 動くスケルトン〜
こんにちは、Tomofilesです。
だいぶご無沙汰になっています。
このブログでやりたいと思っていたことが、ひと通りできてしまったので、少し更新が控えめになっていました。
最近、仕事でも、さらにドローンとのつながりが少なくなって、少し悲しいのですが、その分、個人開発で色々始めて、ドローンと向き合っています。
今回は、Part1、2でプロトタイプを作ったSkysignプロジェクトの、バージョン2について、記事を書いていこうと思います。
少し、ITの技術面が強めですが(そもそも私はITエンジニアなので)、もし興味があれば、読んでいってください。
はじめに
始める前に、少しだけ宣伝ですが、このブログとは別に、IT技術系の記事をメインに書いている、以下のブログを新しく作りました。
5月くらいからReactの勉強をしていて、ついでにJamstackにも触れておきたいと思って、Gatsbyという静的サイトジェネレータを使って、ブログを作ってみました。IT技術面に興味があれば、ぜひこちらも読んでいってください。
さて、今回の記事は、以前に一旦かたちにできたSkysignというクラウドサービスを、基盤を新たに再構築して作り直し始めたので、その経緯などをまとめるものです。
tomofiles.hatenablog.com
tomofiles.hatenablog.com
この再構築版を、バージョン2と名付け、前回までに作成したものと資産を分けました。あれはあれで、残しておきたかったので。(また、以前のバージョンは、ベータ版と呼びます)
このバージョン2では、多くのソフトウェア開発の技術を盛り込んでいます。私が知っている知識やテクニック、触ってみたい技術、やってみたい環境などを、多く採用しています。
今回の記事は、そのあたりのIT技術面の話をメインにできればなと、考えています。ドローン関連の話題は、前回までの開発でほとんどドメインは見えているので、新しい話はありません。
バージョンを新たにした動機は何か
そもそも資産を新しく作る必要性とはなんだったのか、そこを明らかにしたいと思います。
以前のバージョンは、Dockerイメージとしてビルド・デプロイするように開発していなかったので、まずはコンテナ化したいと考えていました。
しかしながら、問題がいくつかあり、その中で一番大きかったのは、「Websocket・SSEによるコネクション保持」が前提であり、クラウドのマネージドサービスと相性が悪く、かつ、スケールアウトが難しい、という点でした。
↓ベータ版のGitHubリポジトリー
github.com
ベータ版でWebsocketを使用しているのは、ドローンとクラウド間の接続であり、一番の採用理由が、クラウドからドローンへの飛行制御のコマンドを転送しないといけないという点でした。
クラウドからドローンをインターネット上から識別して配信するには、ドローンをサーバーと見立てないといけないため、実現したい内容に対して、仕組みが大掛かりになりすぎます。
そのため、ドローンからクラウドに接続しっぱなしにしておけば、クラウドからドローンにコマンドを配信できると考え、このようなアーキテクチャを採用していました。
(ベータ版のアーキテクチャの詳細は、Part1、2の記事をご覧ください)
そもそも、ドローンからクラウドへのテレメトリー配信を、負荷を気にして、HTTPで毎秒リクエストしてはいけないという先入観もありました。これに関しては、具体的な指標を取得したわけでもないので、どのように負荷がかかるか検証もせずに対策を講じても意味がありませんでした。
現在の業界におけるアーキテクチャの主流は、負荷が高い業務サーバはスケールアウトして(今どき、マネージドサービスで簡単にできるし)、大量のリクエストをさばけるように調整して対策を講じるのが通常であり、その基本に乗っかってアーキテクチャを構築するほうが妥当だと判断しました。
よって、Websocket等のステートフルな仕組みを極力排除し、サーバをHTTP(および、用途に応じてgRPC)で単メッセージごとにリクエストをさばき、適切な処理単位で機能をまとめてデプロイできるようにし、データベースでステートを管理できるように、アーキテクチャを組み直すことにしました。
これが、バージョン2のアーキテクチャ刷新の、もっとも大きな動機です。ベータ版のコンセプトは、一切残らないので、新しく新規で構築し直したということです。
なお、クラウドからドローンへのコマンドの転送は、依然として課題であり、これを解決するクラウド・ドローン間のコラボレーション・コンセプトも、新たに構築しました。GitHubリポジトリー上のテキストで、詳細を紹介しています。
skysign_cloud_v2/concepts.md at master · Tomofiles/skysign_cloud_v2 · GitHub
ビルド・デプロイ・環境構築から手作業を排除したい
今どき、手で本番環境にリリースするなんて、と思う人も多いと思いますが、SI系では日常風景です。
まぁ私はイケてないと思うので、ワンクリックで本番環境にリリースできることを目指して、環境構築を試みてみました。
現在のxOpsのベストプラクティスは、GitOpsだと言われています。
GitOpsについては、以下のQiitaの記事がまとまっていて、私もよく参照していました。
CI/CD(継続的インテグレーション・デプロイ)は、2年ほど前、初めてCircleCIとGKEを使って、プライベートで構築してみたのが、初めての体験でした。
当時は、GitHubへのPushをトリガーにテスト・ビルド・GKEへのリリースまで行う、CI/CDが結合したパイプラインを構築していました。参考にした海外のテック記事がそうなっていたので、まるごと真似してみたんですね。
従来のCI/CDのCD(自動デプロイ)は、外部からのPushで行われるものでした。つまり、CircleCI等のCIツールから、GKEにアクセスして、マニフェストファイルをapplyする方式が主流でした。
当時の私は、これだけで、すげーと思っていましたが、GitHubへのPush・イコール・本番環境へのリリース、となってしまうため、ちょっと扱いにくいなと思ったものでした。
GitOpsのプラクティスでは、CIとCDを厳密に分けることが挙げられています。
これを実現するために、CIパイプラインをキックするGitリポジトリーと、CDパイプラインをキックするGitリポジトリーを分けて用意することになります。
つまり、アプリケーションのソースコードと、動作環境の構築に関するコードを、リポジトリーを分けて管理するということです。
以下の図は、GitOpsを提唱したweaveworksという企業のウェブサイトからの転載です。
CodeリポジトリーとConfigリポジトリーが、その2つのリポジトリーですね。
GitOpsは、Gitにすべての信頼をおいたプラクティスです。
Git上に管理された資産・イコール・動作環境の状態、という関係が実現できるように、様々なエコシステムが構築されているようです。
よって、ConfigリポジトリーへのコミットのPushが動作環境へのリリースになるし、環境を戻したかったらgit revertでコミットを取り消すことになります。
プロダクト・コードの管理に慎重なソフトウェア開発者たちの、精神的支柱であるGitが、そのまま本番環境へのアプローチ方法になるのであれば、これほど安心できることはないですね。
GitOpsって、CDのたびにデリバリー先のk8sクラスタに新しいマニフェストをpushして反映するんじゃなくて、逆にクラスタ内から構成リポを監視して、差分を検出したら、自ら構成を変更するんだね。
— Tomofiles (@tomofiles) 2020年7月13日
GitOpsでもう一つ面白いのが、CDの方法です。
前述したとおり、従来のCDは外部からのマニフェストファイルのPushにより、実現されてきました。
これにはいくつか問題があるのですが、一番大きな問題がCDを実現するCIツール等が、本番環境へのアクセスと構成変更を行うことができるという、とても大きな権限を持つことでした。
CIツールは開発者もアクセスすることが多いでしょうし(ビルドエラーとかの解析に閲覧するんですかね?)、アクセス管理等を徹底しないと、セキュリティ的にも、運用的にも、結構大きなリスクがあります。
GitOpsで推奨されているCDは、Configリポジトリーと本番環境(Kubernetesクラスタ)との間に、同期コントローラーを配備します。
同期コントローラーは、Kubernetesクラスタ内にインストールされ、クラスタ内からConfigリポジトリーをリッスンし、Kubernetesマニフェストファイルの変更を検出したら、本番環境に検出した差分を適用します。
クラスタ内から外部のGitリポジトリーを参照するので、外部ツールから本番環境へのアクセス制御が不要になり、セキュリティのリスクが1つ減ります。
上図の、Deploy Operatorが、その同期コントローラーになりますね。
SkysignのCI/CD戦略としては、まずは上図のこのGitOpsのパイプラインを、そのまま実現することとしました。なお、同期コントローラーとして、ArgoCDを使用します。
つまり、流れはこうです。
- Codeリポジトリーにソースコードをコミット(このリポジトリーは、上記で紹介したskysign_cloud_v2です)
- CircleCIが、定義されたテスト・コンパイル、および、Dockerイメージをビルドし、GCPのコンテナレジストリーにPushする
- Codeリポジトリーのブランチはmasterとdevelopの2つがあり、developは2.まで、developをmasterにマージしたら、4.以降を行う
- Codeリポジトリーに格納してあるKubernetesマニフェストのプレースホルダーに、今回のビルドタグを埋め込んで、今回のビルド用のマニフェストファイルを作成し、Configリポジトリーにブランチを切ってコミットし、プルリクエストを作成する(ConfigリポジトリーはPrivateです)
- Configリポジトリーにて、プルリクをマージすると、GKEクラスタ内のArgoCDが変更を検出して、新しいDockerイメージをレジストリーからPullして、ポッドの再構築が走る(現在ここは、ArgoCDダッシュボードから、ボタンクリックでキックするようにしています)
ArgoCDの環境構築は、公式サイトを見れば簡単に行えます。
あとは、上記のQiitaの記事もオススメです。(内容は、公式のチュートリアルと同じですが)