1
ソフトウェア/ファームウェアの自動更新戦略
「クライアントデモ用のカフェインを使用した粗雑なプロトタイプ」フェーズの終わりに近づいており、「将来を考える」フェーズに移行する中規模プロジェクトができました。プロジェクトは、ソフトウェアとファームウェアを備えたLinuxベースのデバイス、および中央管理Webサーバーで構成されています。現在10個のプロトタイプが存在し、生産は1000年代のオーダーになると予想されます。 自動更新の技術に精通しておらず、時間が足りないので、私は自分のソフトウェアの展開/自動更新戦略をすぐに展開しました。現在、次の要素で構成されています。 ホストされたgitリポジトリ(GitLab)と本番リリースブランチ(Webサーバーのソースもこの同じリポジトリにあることに注意してください)。 次のようなWebインターフェースの「更新の展開」ボタン 最新バージョンを本番リリースブランチからローカルリポジトリエリアにプルし、それを一時パッケージ準備ステージングエリアにもコピーします。 ステージング領域でサニタイズスクリプト(リポジトリに格納されている)を実行して、無関係なソースファイル(サーバーソース、ファームウェアソースなど)と.gitファイルを削除します。 現在のgitハッシュを更新パッケージのファイルに書き込みます(目的は以下で明らかになります)。 すべてうまくいった場合は、gzipで圧縮し、以前のgzipで圧縮されたパッケージを同じ名前のファイルで上書きしてサービスの準備を整え、ステージング領域を削除します。 サーバー上に現在のデバイスソフトウェアの2つのコピーが存在することに注意してください。これらは同期がとられていることが期待されています。同じバージョン。 デバイス上のソフトウェアは、という名前のディレクトリに自己完結し/opt/example/currentています。これは、ソフトウェアの現在のバージョンへのシンボリックリンクです。 起動時に次のことを行うデバイスの自動更新機能: do_not_updateファイルの存在を確認し、ファイルが存在する場合はそれ以上のアクションを実行しません(開発デバイスについては、以下を参照してください)。 上記のテキストファイルから現在のコミットハッシュを読み取ります。 そのハッシュをクエリパラメータとして使用して、サーバーにHTTPリクエストを送信します。サーバーは304(ハッシュは現在のバージョン)で応答するか、gzip圧縮された更新パッケージを提供します。 更新パッケージを受け取った場合は、次の方法でインストールします/opt/example。 という名前のフォルダーに更新されたソフトウェア情報を抽出していますstage。 更新に必要なローカル変更などを行う更新パッケージからのインストール後スクリプトの実行 現在のソフトウェアルートフォルダーをにコピーしますprevious(存在するprevious場合は、最初に既存のものを削除します)。 stageフォルダをコピーしますlatest(存在するlatest場合は、最初に既存のものを削除します)。 確保currentへのポイントへのシンボリックリンクをlatest。 デバイスを再起動します(ファームウェアの更新がある場合は、再起動時に適用されます)。 新しく構築されたデバイスでの初期展開の問題もあります。デバイスは現在 SDカードベースである(ここでは範囲外の独自の問題があります)ため、このプロセスは次のように構成されます。 以前のバージョンのソフトウェアが含まれているSDイメージが存在します。 この画像からSDカードが作成されます。 初回起動時に、さまざまな初回のデバイス固有(シリアル番号ベース)の初期化が行われ、その後、オートアップデーターがソフトウェアの最新の製品バージョンを取得して通常どおりインストールします。 さらに、開発デバイスのサポートも必要でした。開発デバイスの場合: 完全なローカルgitリポジトリがデバイスで維持されます。 currentシンボリックリンクは、開発ディレクトリを指します。 do_not_update自動アップデーターがプロダクションアップデートで開発コードを吹き飛ばすのを防ぐローカルファイルが存在します。 現在、展開プロセスは理論的には次のように意図されていました。 コードをデプロイする準備ができたら、リリースブランチにプッシュします。 サーバーの[更新の展開]ボタンを押します。 これでアップデートが公開され、デバイスは次回チェック時に自動更新されます。 しかし、そこにあるトン実際に問題のは: Webサーバーのコードはデバイスのコードと同じリポジトリにあり、サーバーにはローカルのgitリポジトリがあり、それを実行しています。最新のWebサーバーコードが最新のデバイスコードと同じブランチにありません。ディレクトリ構造に問題があります。[更新のデプロイ]ボタンをクリックすると、最新バージョンが本番ブランチからプルされ、サーバーコードのサブディレクトリにプルされます。つまり、サーバーに最初からデプロイする場合、デバイスのプロダクションブランチをそこに取り込むことにより、このサブディレクトリを手動で「シード」する必要があります。これは、おそらくgit user errorから、デプロイを試みないと、親ディレクトリのWebサーバーブランチからデバイスコードをプルします。これは、ステージング領域をサーバーのローカルgitリポジトリのサブディレクトリにしないことで解決できると思います。 Webサーバーは現在、デバイスソフトウェアのgitハッシュを永続的に維持していません。サーバーの起動時に、git rev-parse HEADローカルデバイスソフトウェアリポジトリで現在のハッシュを取得します。理由がわからないため、ここでは説明しませんが、大量の論理エラーが発生しています。サーバーを再起動すると、特にサーバーが新品で生産がない場合は、問題が発生する可能性があります。ブランチリポジトリはまだ引っ張られています。リクエストがあれば喜んでそのロジックのソースを共有しますが、この投稿は長くなりつつあります。 何らかの理由でサニタイズスクリプト(サーバー側)が失敗した場合、サーバーには最新のレポが残されますが、同期git rev-parse HEADが取れておらず、更新パッケージがないため、実際のハッシュと一致しないハッシュが返されますここで問題はサーバーのコマンドラインで手動で修正する必要があります。つまり、サーバーは更新パッケージが正しくないことを認識しておらず、純粋な信念で常にそうであることを前提としています。これを前のポイントと組み合わせると、サーバーは実際には非常に壊れやすくなります。 最大の問題の一つは:デバイス上で動作しているデーモン全く別のアップデータは現在ありません。wifiインターネットアクセスが来るのを待っている複雑さといくつかの土壇場のハッカーのため、デバイスをチェックして更新するのはメインのデバイス制御ソフトウェア自体です。これは、テストが不十分なバージョンが何らかの形で本番環境に移行し、制御ソフトウェアが起動できない場合、存在するすべてのデバイスは、それ自体を更新できなくなるため、本質的に機能しなくなることを意味します。これは、本番環境では絶対的な悪夢です。不運なときに電源が切れた場合の単一のデバイスの同じ取引。 その他の主な問題は次のとおりです。増分更新はサポートされていません。たとえば、しばらくの間デバイスの電源が入っていない場合、次にデバイスが更新されたときに、一連のリリースバージョンがスキップされ、直接バージョンスキップの更新を実行できる必要があります。この結果、更新された展開は、特定の更新を特定の過去のバージョンの上に適用できることを確認するという悪夢になります。さらに、gitハッシュはバージョン番号ではなくバージョンを識別するために使用されるため、増分更新を容易にするためのバージョンの辞書式比較は現在不可能です。 現在サポートしていない新しい要件は、管理サーバー側で構成する必要があるデバイスごとの構成オプション(キー/値のペア)がいくつか存在することです。ソフトウェアの更新と同じHTTP要求でデバイスにこれらのデバイスごとのオプションを提供することはどうしても構いません(多分、それをHTTPヘッダー/ Cookieにカプセル化できます)。常に別のHTTPリクエストにします。 ハードウェアの2つのバージョン(および将来的にはさらに多く)のハードウェアが存在するため、多少複雑になります。ハードウェアの現在のバージョンは、実際には初期SDイメージの環境変数として格納され(それらは自己識別できません)、すべてのソフトウェアはデバイスのすべてのバージョンと互換性があるように設計されています。ファームウェアの更新はこの環境変数に基づいて選択され、更新パッケージにはハードウェアのすべてのバージョンのファームウェアが含まれています。ちょっと不格好ですがこれで我慢できます。 現在、手動で更新をデバイスにアップロードする方法はありません(要するに、これらのデバイスには2つのwifiアダプターがあり、1つはインターネットへの接続用で、もう1つはユーザーがデバイスの構成に使用するAPモードです。将来的にはデバイスのローカルウェブインターフェースに「アップデートソフトウェア」機能を追加するつもりです)。これは大きな問題ではありませんが、更新のインストール方法にある程度の影響を与えます。 …