背景の紹介
Web プロジェクトがあり、デプロイ方法は単一のアプリケーション (コアのない dotnet) + ロード バランサー (nginx) によって実現される分散デプロイです。近年のユーザー急増により、負荷マシン数を2台から8台に増加しました。これがまた問題で、マシンを追加したり、ソフトウェアを更新したり、バージョンを戻すたびに、非常に面倒で苦痛になり、まともに釣りができなくなり、繰り返しの作業で人生が無駄になってしまいます。技術的な内容はありません。
退役軍人の中には、「なぜマイクロサービス アーキテクチャ システムを作成すべきではないのか? kubernetes を使用して自分のスキルを誇示し、数百、数千のマシン クラスターを簡単に管理できるようにしましょう。話したり笑ったりしているうちに、すべてが消去されます。」と言う人もいるでしょう。綺麗でしょう?
私が言えるのは、ティエジ、私も片手ですべてを制御する喜びを楽しみたいということだけですが、実際の状況はまだそれを許可していませんが、私はすでにその方向に積極的に開発しているということです。
遠くにある水では近くの渇きを潤すことはできず、現在の状況では、この作業の重複の問題を解決するための半一時的な解決策が急務となっています。
既製のソリューションはないので、自分で作成してください。
一生
手動プロセス
負荷が軽いときに考えてください。通常、サイト更新プロセス全体には次のリンクが含まれます。
- 更新エラーを避けるために、更新する前に、元の安定して実行されているサイトのバージョンをバックアップしてください。もちろん、すべてのファイルをバックアップする必要があるわけではなく、ストレージを圧迫するため、実際の状況に応じて重要なファイルのみがバックアップされますが、どの重要なファイルがバックアップされるかについては、その都度状況に応じて判断する必要があります。
- 新しく公開されたファイルをパッケージ化する
- サーバーにアップロードして元のファイルを置き換えます
- 各更新にサーバー側のファイルが含まれている場合は、iis サイトを再起動する必要があります (現在、Web 側のビジネスは iis で実行されていますが、実際には Apache、tomcat、nginx、およびその他のサイト サーバーに拡張する必要があります)。
自動運転プロセス
更新プログラムの展開プロセス全体を整理した後、更新プロセス全体を次の段階に整理しました。
- 監視: この段階では、プログラムは特定のファイルの変更を定期的に監視します。ここで設定したファイルは、新しいファイルのバージョン番号やダウンロード アドレスなどの情報を記録する json 形式のファイルです。バージョン番号が検出された場合、後続のプロセスの自動化を開始します。
- ファイルのダウンロード: この部分は、リスニング段階で取得したダウンロード アドレスに基づいて、指定された場所にファイルをダウンロードします。
- サイトを一時停止する (オプション): すべての更新でサイトの再起動が必要なわけではないため、この手順はオプションです。
- バックアップ: 受信したバックアップ パラメータに基づいて、ファイルのバックアップを実行し、バックアップ ログを生成します。
- 解凍: バックアップが完了したら、2 番目にダウンロードしたファイルを解凍し、古いファイルと新しいファイルの置き換えを完了できます。
- サイトを再起動する (オプション): サイトが一時停止されている場合は、解凍と移行が完了した後にサイトを再度再起動する必要があります。
- 通知: 更新プロセス全体が完了したら、関連する開発、運用、保守担当者に電子メール通知を送信して、この自動更新の重要な情報を通知する必要があります。
上記の主要なプロセスを明確にしたら、コーディングを開始できます。
ここで使用するのはコンソール構造であり、Coravel、Serilog、SharpCompress、Hosting などのいくつかの重要なツール パッケージを使用します。
完全なコードのアドレスは記事の最後に記載されています。
モニター
コンソール プログラムであるため、Web プログラムのように URL にアクセスする動作を監視するには、Microsoft.Extensions.Hosting パッケージを導入する必要があります。
導入後はCoravelと組み合わせてスケジュール監視が可能です。
public static void UseCoravelService(string[] args)
{
// Renders each item with own style
bool intervelFlag = true;
var process = new Rule().RuleStyle("#FDE047");
var host = new HostBuilder()
.ConfigureAppConfiguration((hostContext, configApp) =>
{
configApp.AddEnvironmentVariables(prefix: "PREFIX_");
configApp.AddCommandLine(args);
})
.ConfigureServices((hostContext, services) =>
{
// Add Coravel's Scheduling...
services.AddScheduler();
})
.Build();
host.Services.UseScheduler(scheduler =>
scheduler
.Schedule(async () =>
{
//核心业务
//...
})
.EveryFifteenSeconds()
);
host.Run();
}
ネットワークプロキシとCoravelを導入した後の動作は業務コードです。
/// <summary>
/// 监听
/// </summary>
internal class StepMonitor
{
public static async Task<bool> getJsonFile(string address)
{
try
{
if (string.IsNullOrEmpty(address))
address = "<我这里给了一个默认地址,也可以不要>";
HttpClient hc = new HttpClient();
var content = await hc.GetStringAsync(address);
var json = JsonHelper.JsonDeserialize<VersionModel>(content);
string versionFile = "currVersion.txt";
string currVersion = "0";
currVersion = await MainScheduling.ReadFile(versionFile);
if(currVersion.Trim()==json.Version)
{
return false;//线上版本和本地版本相同,继续等待;
}
await MainScheduling.WriteFile("currVersion.txt", json.Version);
await MainScheduling.WriteFile("DownloadList.json", JsonHelper.JsonSerialize(json.Items));
return true;
}
catch (Exception ex)
{
AnsiConsole.WriteLine($"[red]下载更新索引文件失败,{ex.Message}[/]");
throw;
}
}
}
関連する監視メソッドを定義した後、「スケジュール」に戻ってそれらを呼び出すことができます。
host.Services.UseScheduler(scheduler =>
scheduler
.Schedule(async () =>
{
string cache = CacheManager.Default.Get<string>("step");
AnsiConsole.MarkupLine($"[#1E9FFF]当前状态:【{cache}】,{DateTime.Now.ToString()}[/]");
if (cache == "waiting")
{
intervelFlag = false;
OutputStep(0, "监听中(waiting),正在执行...");
string file = GetParamValue(args, "file");
if (await StepMonitor.getJsonFile(file))
{
//CacheManager.Default.Set_SlidingExpire<string>("step", "download", TimeSpan.FromMinutes(10));
await SetStep("download");
}
intervelFlag = true;
}
})
最終的なモニタリング効果は次のとおりです。
ダウンロード
この部分については特に言うことはありません。好きな方法でファイルをダウンロードしてください。wget などの整形ツールを呼び出すことさえ不可能ではありません。ただ、情報を適切に操作する必要があります。プログラム。
ダウンロード コードはここには掲載しませんが、ソース コードで確認できます。スクリーンショットは次のとおりです。
サイトを閉じる (オプション)
ここでサイトを閉じるかどうかは、バージョンファイルにstaticキーワードが含まれているかどうかで判断します。
string currVersion = await ReadFile("currVersion.txt");
if (currVersion.Contains("static"))
{
OutputStep(2, "仅更新静态文件,无需重启站点");
}
else
{
OutputStep(2, "站点关闭中(stopweb),正在执行...");
string appSite = GetParamValue(args, "appSite");
string appPool = GetParamValue(args, "appPool");
StepIISManager.Stop(appSite, appPool);
}
2 つの状況のスクリーンショットは次のとおりです
バックアップファイル
バックアップ ファイルの場合、ここでは特別な入力パラメータ "-subpath" を設定しました。このパラメータが渡されない場合、サイトは完全にバックアップされます。このパラメータが渡された場合、バックアップは渡されたファイルまたはファイルに基づいて行われます。ディレクトリ。
//这里的参数是解压地址
string inputPath = GetParamValue(args, "output");
string outputPath = GetParamValue(args, "backuppath");
string currVersion = await ReadFile("currVersion.txt");
string subPath = GetParamValue(args, "subpath");
OutputStep(3, "站点备份中(backup),正在执行...");
if (!string.IsNullOrEmpty(subPath))
{
AnsiConsole.MarkupLine("[#FDE047]检测到子目录参数,不再进行全量备份,正在顺序备份[/]");
if (!string.IsNullOrEmpty(subPath))
{
string[] parts = subPath.Split(',');
var paths = parts.Where(u => !u.Contains(".")).ToList();
var files = parts.Where(u => u.Contains(".")).ToList();
if (files.Any()&&paths.Any(u=>!u.Equals("_temp")))
{
paths.Add("_temp");
}
foreach(var file in files)
{
AnsiConsole.MarkupLine($"[#FDE047]文件{file}备份中...[/]");
string subInputPath = Path.Combine(inputPath, file);
if (File.Exists(subInputPath))
{
string tempPath = Path.Combine(inputPath, "_temp");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
File.Copy(subInputPath, Path.Combine(tempPath, file), true);
}
}
foreach (var item in paths)
{
AnsiConsole.MarkupLine($"[#FDE047]{item}备份中...[/]");
string subInputPath = Path.Combine(inputPath, item);
await StepDeCompress.ZipCompress(subInputPath, outputPath, item);
}
}
}
else
{
await StepDeCompress.ZipCompress(inputPath, outputPath);
}
解凍する
解凍では、sharpcompress コンポーネントの機能を引き継ぎ、2 番目の手順でダウンロードしたファイルを指定されたディレクトリに解凍します。
/// <summary>
/// 解压文件
/// </summary>
/// <param name="inputFile">解压文件路径</param>
/// <param name="outputFile">解压文件后路径</param>
public static void Decompression(string inputFile, string outputFile)
{
try
{
if (!inputFile.Contains(":"))
{
inputFile = Path.Combine(Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName), inputFile);
}
SharpCompress.Readers.ReaderOptions options = new SharpCompress.Readers.ReaderOptions();
options.ArchiveEncoding.Default = Encoding.GetEncoding("utf-8");
var archive = ArchiveFactory.Open(inputFile, options);
AnsiConsole.Status()
.Start("解压文件...", ctx =>
{
foreach (var entry in archive.Entries)
{
if (!entry.IsDirectory)
{
entry.WriteToDirectory(outputFile, new ExtractionOptions { ExtractFullPath = true, Overwrite = true });
}
}
AnsiConsole.MarkupLine("[#1E9FFF]解压完成[/]");
});
}
catch (Exception ex)
{
AnsiConsole.WriteLine($"[red]{ex.Message}[/]");
throw;
}
}
レンダリング
再起動サイト (オプション)
受信コマンドが start になることを除いて、サイトを一時停止する場合と同じ手順です。
if (currVersion.Contains("static"))
{
OutputStep(5, "仅更新静态文件,无需重启站点");
}
else
{
OutputStep(5, "站点启动中(startweb),正在执行...");
string appSite = GetParamValue(args, "appSite");
string appPool = GetParamValue(args, "appPool");
StepIISManager.Start(appSite, appPool);
Console.WriteLine(DateTime.Now.ToString());
}
効果
通知を送信する
この部分は実は言うまでもありませんが、指定管理者に更新内容をメールで送信するというものです。
コードはここには載せませんので、レンダリングを見てみましょう。
要約する
クラウドネイティブの時代を迎え、マイクロサービス化したさまざまなフレームワークが次々と登場していますが、依然としてモノリシックアプリケーションが依然としてかなりの割合を占めていることも無視できない事実です。特に中小企業や零細企業、さらにはかなりの中堅・大企業のプロジェクトにおいては、依然としてモノリシックアプリケーションが主流を占めるはずであり、自社のビジネスの限界により、一部のビジネスは、企業のランクに移管することが適さない可能性があります。誕生から消滅までのマイクロサービス。これが現実であり、残念でもあります。
私個人のマイクロサービスに対する考え方は基本的にマイクロサービス信者であり、積極的に変革に取り組んでおり、チームリーダーも現状を踏まえて段階的に既存事業をマイクロサービスアーキテクチャに転換することをサポートしてくれています。は運用が開始されたプロジェクトであり、まだ運用を開始する必要があります。安定性が最優先事項であるため、かなりゆっくりと進めています。多くのビジネスは依然として単一のアプリケーションの構造に基づいて運営されており、主要な開発および保守タスクは依然として実際のビジネスに基づいています。これは客観的で前向きで正しい姿勢だと思いますが、イノベーション自体は魅力的ですが、将来は不確実です。しかし、あまりにも入り込みすぎたり、極端な操作をしたりすると、予測できないリスクが生じます。現実から出発し、ビジネス条件を組み合わせて、段階的に進めるのが正しい方法です。また、私は 2023 年に、いよいよ変革への道において最も重要な一歩を踏み出すことになると信じています。
添付
コードアドレス: For Yourself_Bring Salt/AutoDeploy · GitCode