この記事では、InterSystemsプラットフォームでのアプリケーションのアセンブリ、テスト、および配信を自動化する継続的統合 /継続的配信プロセスの構成についてお話したいと思います。
次のようなトピックを検討してください。
Git 101
メイントピックはCD、 Gitであるという事実にもかかわらず、それを支える多くの基本原則がソフトウェア開発プロセスに大きな影響を与えているので、Gitの基本用語の簡単な説明から始めましょう。
Gitは、次のようなアイデアに基づいたバージョン管理および管理システムです。
- 非線形開発-アプリケーションのバージョンが順次リリースされるという事実にもかかわらず、各バージョンの開発は、アプリケーションの同じ部分を同時に編集できる複数の開発者によって並行して実行されます。
- 分散開発-開発者は中央サーバーから独立しており、ローカル環境で問題なく開発できます。
- マージ-前の2つのポイントを実装すると、プロジェクトには複数の正しいバージョンが同時に存在し、それらを1つのバージョンの真実に結合する必要があることが多くなります。
Gitはこれらのアイデアが実装された最初のバージョン管理システムではありませんでしたが、Gitはこれらのアイデアを普及させ、実用化を大幅に簡素化しました。
リポジトリ
データが保存および維持される場所。
- 「物理的に」-OSのフォルダー
- ファイルとフォルダーを保存します
- 変更の履歴を保持します
ローカルリポジトリ-ローカルコンピューターにあるリポジトリ
リモートリポジトリ-リモートサーバーにあるリポジトリ
コミットする
リポジトリのコミットされた状態。
親と呼ばれる他のコミットとの差(Diff)を保存します。
親は:
- 0-最初のコミット時。
- 1-これは通常のケースです。
- 2-変更をマージします。
- 3+-変更をマージします(ただし、必要はありません)。
支店
コミットへのポインター。
最初のコミットの前にいつでも彼の話を見ることができます。 たとえば、 master
ブランチ:
コミットツリー
ツリーのコミット-リポジトリのグラフィカルな表現。 上の画像から2つの既存のコミットにさらに3つのコミットが追加された場合、最も単純な線形オプションを検討してください。
現在、ツリーの例はより複雑です。2人の開発者が同時に作業し、互いに干渉しないように、それぞれが自分のブランチで動作します。 次のようになります。
しばらくして、変更をマージする必要があります。これにはマージ要求があります。これは、2つのリポジトリ状態を1つの新しい状態に結合する要求です。 私たちの場合、 develop
ブランチをmaster
ブランチにマージするリクエスト。 リクエストがレビューおよび承認され、マージが行われた後、リポジトリは次のようになります。
その後、開発が続行されます。
結論-Git 101
- Gitはバージョン管理システムです。
- リポジトリ-データが保存および維持される場所。
- コミット-リポジトリのコミット状態。
- ブランチ-コミットへのポインタ。
- マージ要求(プル要求またはマージ要求)-2つのリポジトリ状態をマージする要求。
開発方法(Gitフロー)
Gitベースの開発方法論は、Gitを開発の基盤として使用する一連のソフトウェア開発アプローチです。 多くの開発方法論がありますが、そのうちの2つを検討してください。
- Githubフロー
- Gitlabフロー
Githubフロー
GitHubフローは、おそらくGitに基づいた最も単純な開発方法の1つです。 ここにあります:
- 新しい機能ごとに、機能ブランチと呼ばれる新しいブランチを作成します。
- 変更は新しいブランチにコミットされます
- 変更がコミットされた後、マージ要求が送信されます
- 合併の要求は議論され、最終決定されています。
- マージリクエストが承認されました
さらに、いくつかのルールがあります。
-
master
ブランチは常に動作可能です -
master
ブランチには開発がありません - 開発は別々のブランチで行われます
- ブランチ
master
==産業環境 - 産業環境は、
master
ブランチの変更ごとに更新されます。
環境は、アプリケーションコードが実行される構成済みのリソースです。 サーバー、仮想マシン、またはコンテナです。
これは次のようなものです。
HabréでのGitHubフローの詳細については、すでに複数 回 書いて います。
Gitlabフロー
産業環境でコードを自動的に更新する準備ができていない場合、GitLabフローはGitHubフローと複数の環境の統一を提供します。 仕組みは次のとおりです。開発はGitHubフローと同様に実行されます。別々のブランチでmaster
にマージされますが、 master
ブランチのコンテンツはテストサーバーにデプロイされます。 さらに、環境のコンテンツに対応するコンテンツを持つ環境ブランチがあります。 通常、3つの環境がありますが、要件に応じてこれよりも多い場合も少ない場合もあります。
- テスト環境==
master
ブランチ - 経験のある環境==
preprod
ブランチ - 産業環境==製品ブランチ
開発プロセスは次のようになります。
HabréでのGitLabフローについての詳細も書いています。
結論-開発方法(Gitフロー)
単純なものから複雑なものまで、多数のGitベースの開発方法論があります。 一方では複雑ではない方法論を選択し、他方ではプロジェクトのステータスを適切に制御できます。
Gitlabのワークフロー
GitLab Workflowは、開発の段階だけでなく、アイデアからユーザーフィードバックまでの製品ライフサイクル全体に影響するソフトウェア開発方法論です。 これは次のようなものです。
- アイデア:新しい機能はアイデアから始まります。
- 問題:アイデアを議論する最も効果的な方法は、そのためのタスクを作成することです。 他の開発者がアイデアを改善し、それを実装する方法を提案します。
- 計画:タスクの議論が完了したらすぐに、コードを作成します。 しかし、最初に、ワークフローに優先順位を付けて整理する必要があります。 これを行うには、ステージ、かんばん、実行日、責任者があります。
- コード:これで、コードを作成する準備がすべて整いました。
- コミット:コードに満足したら、すぐにそれをコミットに転送できます。 GitLabフローの詳細は上記で説明されています。
- テスト:GitLab CIを使用してスクリプトを実行し、アプリケーションをコンパイルおよびテストします。
- レビュー:コードが機能し、テストとアセンブリが成功するとすぐに、コードレビューを行うことができます。
- パイロット操作:ここで、パイロット環境にアプリケーションの新しいバージョンをデプロイして、すべてが機能するかどうか、またはさらに編集が必要かどうかを確認します。
- 産業運用:産業環境に展開する時です
- フィードバック:他に改善が必要なもの。
各ステージの詳細については、GitLabのWebサイトで説明します。いくつかのステージの説明に限定します。
タスクと計画
GitLabワークフローの初期段階は、タスク、つまり新機能、バグ、またはその他の個別のワークロードに焦点を合わせています。 このタスクには、次のようないくつかの目標があります。
- 管理:タスクには、問題の解決策を追跡するための作成と実行の日付、責任、時間コスト、優先度などがあります。
- 管理:問題は開発フェーズの一部であり、かんばんボードでもあります。これにより、作業計画を作成し、その実装を監視できます。
- 開発:問題では、それを解決する方法について話し合うことができます。コミット、ブランチ、マージ要求、その他のタスクもタスクに関連付けることができます。
計画段階では、優先度、段階、ステータスに従ってタスクをグループ化できます。
上記の開発について議論し、git開発方法論に従います。 新しい機能を開発し、それをmaster
ブランチにマージした後、次は何をしますか?
継続的デリバリー
継続的デリバリは、チームが短いスプリントでソフトウェアを開発するソフトウェア開発アプローチであり、アプリケーションの新しいバージョンがいつでもリリースできるようにします。 このアプローチは、ソフトウェアの組み立て、テスト、および配信を自動化します。 このアプローチは、変更を行うコストとリスクを軽減するのに役立ち、産業用アプリケーションの高速増分更新を取得できます。 継続的な配信では、シンプルで繰り返し可能な配信プロセスを設定することが重要です。
GitLabでの継続的デリバリー
GitLabでは、連続配信設定は各リポジトリに対して個別に定義され、リポジトリのルートにあるYAML設定ファイルに保存されます。
- 連続配信構成は、一連の連続した手順です。
- 各ステージには、並行して実行される1つ以上のスクリプトがあります。
スクリプト
1つのアクションと、それを実行するために満たす必要がある条件を定義します。
- 対処方法(OSコマンドを実行し、コンテナを起動します)?
- スクリプトを実行するタイミング:
- トリガー(
master
ブランチへのコミットなど)? - 前の手順が失敗した場合(デフォルトではno)、スクリプトを実行する必要がありますか?
- 手動または自動で開始しますか?
- トリガー(
- スクリプトを実行する環境は?
- 実行後に保存されるアーティファクト(それらは環境からGitLabに読み込まれ、すばやくアクセスできます)
環境は、スクリプトを実行できる構成済みのサーバーまたはコンテナです。
ランナー -特定の環境でスクリプトを実行するサービス。 GitLabに接続し、必要に応じてスクリプトを実行します。
ランナーは、サーバー、コンテナ、またはローカルの開発者のコンピューターに展開できます。
連続配信はどのように行われていますか?
- 新しいコミットがリポジトリにアップロードされます。
- GitLabは継続的デリバリー構成を検証します。
- 継続的配信構成には、すべての場合に可能なすべてのスクリプトが含まれているため、この新しいコミットに対して実行する必要があるスクリプトのセットにフィルターされます(たとえば、
master
ブランチのスクリプトは、コミットがmaster
ブランチにある場合にのみ機能します)。 このセットはパイプラインと呼ばれます。 - 起動は適切な環境で実行され、結果は保存され、GitLabで利用できます。
実行例を次に示します。
順次実行される4つのステップで構成されます
- サーバーへのアップロードアップロードコード
- テストは単体テストを実行します
- パッケージは、並行して実行される2つのスクリプトで構成されます。
- 顧客ビルド
- サーバーコードを1つの「xml」にエクスポートします(主に情報提供の目的で)
- Deployは、アセンブルされたクライアントをWebサーバーディレクトリに移動します。
ご覧のとおり、各スクリプトは正常に実行され、スクリプトの1つが失敗すると、次のスクリプトは実行されません。
スクリプトを開くと、エラーで終了した理由を確認できます。
Running with gitlab-runner 10.4.0 (857480b6) on test runner (ab34a8c5) Using Shell executor... Running on gitlab-test... Fetching changes... Removing diff.xml Removing full.xml Removing index.html Removing tests.html HEAD is now at a5bf3e8 Merge branch '4-versiya-1-0' into 'master' From http://gitlab.eduard.win/test/testProject * [new branch] 5-versiya-1-1 -> origin/5-versiya-1-1 a5bf3e8..442a4db master -> origin/master d28295a..42a10aa preprod -> origin/preprod 3ac4b21..7edf7f4 prod -> origin/prod Checking out 442a4db1 as master... Skipping Git submodules setup $ csession ensemble "##class(isc.git.GitLab).loadDiff()" [2018-03-06 13:58:19.188] Importing dir /home/gitlab-runner/builds/ab34a8c5/0/test/testProject/ [2018-03-06 13:58:19.188] Loading diff between a5bf3e8596d842c5cc3da7819409ed81e62c31e3 and 442a4db170aa58f2129e5889a4bb79261aa0cad0 [2018-03-06 13:58:19.192] Variable modified var=$lb("MyApp/Info.cls") Load started on 03/06/2018 13:58:19 Loading file /home/gitlab-runner/builds/ab34a8c5/0/test/testProject/MyApp/Info.cls as udl Load finished successfully. [2018-03-06 13:58:19.241] Variable items var="MyApp.Info.cls" var("MyApp.Info.cls")="" Compilation started on 03/06/2018 13:58:19 with qualifiers 'cuk /checkuptodate=expandedonly' Compiling class MyApp.Info Compiling routine MyApp.Info.1 ERROR: MyApp.Info.cls(version+2) #1003: Expected space : '}' : Offset:14 [zversion+1^MyApp.Info.1] TEXT: quit, "1.0" } Detected 1 errors during compilation in 0.010s. [2018-03-06 13:58:19.252] ERROR #5475: Error compiling routine: MyApp.Info.1. Errors: ERROR: MyApp.Info.cls(version+2) #1003: Expected space : '}' : Offset:14 [zversion+1^MyApp.Info.1] > ERROR #5030: An error occurred while compiling class 'MyApp.Info' ERROR: Job failed: exit status 1
コンパイルエラーが原因でスクリプトが失敗しました。
理論から実践に移りましょう。
GitLabをインストールする
GitLabを独自のサーバーにインストールします。 ただし、GitLab.comを使用できます。 GitLabをインストールするには、ソースから、パッケージから、コンテナにさまざまな方法があります。 お好みの方法を選択し、インストール手順に従ってください 。
要件:
- 別のサーバー-GitLabはかなりリソースを集中的に使用するWebアプリケーションなので、別のサーバー(4 Gb RAM、2 CPU)に保持することをお勧めします。
- Linux OS
- (オプションですが推奨)ドメイン-セキュリティおよび起動ページに必要です。
構成
次に、Pagesをインストールすることをお勧めします。 前述のように、スクリプトアーティファクトはGitLabにアップロードできます。 ユーザーはそれらをダウンロードできますが、ブラウザで直接開くと便利な場合が多く、そのためにはページをインストールする必要があります。
ページが必要な理由:
- プロジェクトに関連するウィキまたは静的ページのセット。
- HTMLアーティファクトを表示します。
- そして、他の多くの理由 。
ページをロードする際の自動リダイレクトをHTMLに追加できるため、単体テストの結果を含むページにユーザーを誘導できます。
ClassMethod writeTestHTML() { set text = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(), "html").Data.Read() set text = $replace(text, "!!!", ..getURL()) set file = ##class(%Stream.FileCharacter).%New() set name = "tests.html" do file.LinkToFile(name) do file.Write(text) quit file.%Save() } ClassMethod getURL() { set url = "http://host:57772" set url = url _ $system.CSP.GetDefaultApp("%SYS") set url = url_"/%25UnitTest.Portal.Indices.cls?Index="_ $g(^UnitTest.Result, 1) _ "&$NAMESPACE=" _ $zconvert($namespace,"O","URL") quit url } XData html { <html lang="en-US"> <head> <meta charset="UTF-8"/> <meta http-equiv="refresh" content="0; url=!!!"/> <script type="text/javascript">window.location.href = "!!!"</script> </head> <body> If you are not redirected automatically, follow this <a href='!!!'>link to tests</a>. </body> </html> }
ちなみに、ページを使用するとバグが発生しました(アーティファクトを表示するとエラー502)-これが解決策です。
環境をGitLabに接続する
CDスクリプトを実行するには、環境が必要です-アプリケーションを実行するように構成されたサーバー。 まず、InterSystemsプラットフォーム(たとえば、InterSystems IRISですが、すべてがCachéまたはEnsembleで動作します)と共にインストールされたLinuxサーバーがあるとします。 環境をGitLabに接続するには、次のものが必要です。
- GitLabランナーをインストールします 。
- GitLabランナーをGitLabに登録します。
-
gitlab-runner
がInterSystems IRISgitlab-runner
起動できるgitlab-runner
ます。
GitLabランナーのインストールに関する重要な注意-GitLabランナーのインストール後にサーバーのクローンを作成しないでください。 結果は予測不能で望ましくありません。
手順2と3について詳しく説明します。
GitLabランナーをGitLabに登録します。
コマンドの実行後: sudo gitlab-runner register
いくつかのオプションから選択でき、ほとんどの手順は非常に簡単ですが、そのうちのいくつかはコメントする価値があります。
- このランナーのgitlab-ciトークンを入力してください
いくつかのトークンが利用可能です:システム全体(管理設定で利用可能)または1つのプロジェクト(プロジェクト設定で利用可能)。
特定のプロジェクトのCDのランナーを接続するとき、この特定のプロジェクトのトークンを指定する必要があります。
- このランナーのgitlab-ciタグを入力してください(カンマ区切り)
構成CDで、特定のタグを持つ環境で実行されるスクリプトをフィルタリングできます。 したがって、最も単純なケースでは、環境の名前となる1つのタグを指定します。
- エグゼキューターを入力してください:ssh、docker + machine、docker-ssh + machine、kubernetes、docker、parallels、virtualbox、docker-ssh、shell
Dockerを使用するかどうかに関係なく、スクリプトを実行するシェルを選択します。
gitlab-runner
がInterSystems IRIS gitlab-runner
起動できるgitlab-runner
ます。
GitLabに接続したら、 gitlab-runner
ユーザーがInterSystems IRISを呼び出すことを許可する必要があります。 これを行うには:
-
gitlab-runner
ユーザーには、irissession
またはcsession
を呼び出すirissession
必要csession
。 これを行うには、irisusr
グループまたはcacheusr
コマンドに追加します:usermod -a -G cacheusr gitlab-runner
- InterSystems IRISで、
gitlab-runner
ユーザーを作成し、 CDスクリプトを実行する権限(データベースへの書き込みなど)を付与します。 - OS認証を有効にします 。
ポイント2と3の代わりに、たとえばユーザー/パスワードの転送など、他のアプローチを使用することもできますが、OS認証のオプションの方がより望ましいようです。
CD構成
それでは、連続配信設定の作成を始めましょう。 まず、環境と計画について説明します。
環境
いくつかの環境とそれに対応するブランチがあります。
環境 | 支店 | 配送 | 誰がコミットできますか | 誰がマージできるか |
---|---|---|---|---|
テスト | マスター | 自動 | 開発者、所有者 | 開発者、所有者 |
経験者 | preprod | 自動 | 誰も | 所有者 |
産業用 | 製品 | ボタンに触れるだけで | 誰も | 所有者 |
作業計画
- 問題の記述、開発、および自動テスト
- 所有者がタスクを設定します
- 開発者は機能ブランチを作成し、そこで問題を解決するコードをコミットします
- 開発者は機能ブランチをマスターにマージします
- 新しいコードは自動的にテスト環境に配信され、テストされます。
- 経験豊富な環境での配達
- 開発者がpreprodのマスターコードをマージする要求を作成します
- 所有者がマスターからプリプロッドへのコードのマージを承認
- 新しいコードは実験環境に自動的に配信されます。
- 産業環境での配送
- 開発者(または所有者)は、コードをpreprodからprodにマージする要求を作成します
- 所有者がpreprodコードをprodにマージすることを承認します
- 所有者が「展開」ボタンをクリックします
- アプリケーションの新しいバージョンは自動環境に展開されます
グラフィカル形式でも同じ:
アプリ
テストアプリケーションは2つの部分で構成されています。
- InterSystemsプラットフォームのREST API
- Webクライアントアプリケーション(HTML + JS + CSS)
ステージ
上記の計画から、連続配信構成で決定する必要がある手順を特定できます。
- サーバーへのアップロードアップロードコード
- テストは単体テストを実行します
- クライアントが収集するパッケージ
- Deployは、アセンブルされたクライアントをWebサーバーディレクトリに移動します。
.gitlab-ci.yml
設定を開始しましょう:
stages: - load - test - package - deploy
スクリプト
構成の次の部分はスクリプトです。 ドキュメンテーション
負荷
サーバーコードをロードするload server
スクリプトから始めましょう。
load server: environment: name: test url: http://test.hostname.com only: - master tags: - test stage: load
ここで何が起こっていますか?
-
load server
はスクリプトの名前です。 - 次に、スクリプトが実行される環境について説明します。
-
only: master
-master
ブランチで新しいコミットが行われた場合にのみスクリプトを実行します。 -
tags: test
このようなタグを持つランナーで実行するスクリプトを送信します。 -
stage
スクリプトが属するステージを決定します。 -
script
実行するコマンドを決定します。 この場合、isc.git.GitLab
クラスのload
メソッドがisc.git.GitLab
ます。
次に、 isc.git.GitLab
クラスを作成する必要があります。 すべてのエントリポイントは次のようになります。
ClassMethod method() { try { // code halt } catch ex { write !,$System.Status.GetErrorText(ex.AsStatus()),! do $system.Process.Terminate(, 1) } }
この方法は、2つの方法でのみ完了できます。
-
halt
コマンドは、現在のプロセスを停止します。これは、スクリプトが正常に完了したと見なされます。 -
$system.Process.Terminate
て、エラーでプロセスを終了すると、スクリプトの実行時にエラーが発生します。
ダウンロードコードは次のとおりです。
/// Do a full load /// do ##class(isc.git.GitLab).load() ClassMethod load() { try { set dir = ..getDir() do ..log("Importing dir " _ dir) do $system.OBJ.ImportDir(dir, ..getExtWildcard(), "c", .errors, 1) throw:$get(errors,0)'=0 ##class(%Exception.General).%New("Load error") halt } catch ex { write !,$System.Status.GetErrorText(ex.AsStatus()),! do $system.Process.Terminate(, 1) } }
このメソッドは、他の2つのメソッドを呼び出します。
- getExtWildcard-ダウンロードする拡張機能のリストを取得する方法。
- getDirは、リポジトリーを使用してフォルダーを取得する方法です。
しかし、リポジトリのあるディレクトリをどのように取得できますか?
GitLabがスクリプトを実行すると、多くの環境変数が定義されます 。 それらの1つはCI_PROJECT_DIR
リポジトリーのルートへのCI_PROJECT_DIR
パスです。 そのため、 getDir
メソッドで取得できます。
ClassMethod getDir() [ CodeMode = expression ] { ##class(%File).NormalizeDirectory($system.Util.GetEnviron("CI_PROJECT_DIR")) }
テスト
テスト実行スクリプトは次のとおりです。
tests: environment: name: test url: http://test.hostname.com only: - master tags: - test stage: test script: csession IRIS "##class(isc.git.GitLab).test()" artifacts: paths: - tests.html
何が変わった? もちろん、スクリプトの名前とコードですが、 artifacts
も追加されています。 アーティファクトとは、スクリプトが正常に完了した後にスクリプトに添付される一連のファイルとフォルダーです。 この場合、テストが完了した後、ユーザーをテスト結果にリダイレクトするHTMLページを生成できます。
テストスクリプトとダウンロードスクリプトの類似性に注意してください。 すべてのスクリプトで同じ環境などのスクリプトの部分は、個別のブロックに分割できます。 テスト環境を定義します。
.env_test: &env_test environment: name: test url: http://test.hostname.com only: - master tags: - test
これで、 tests
スクリプトは次のようになります。
tests: <<: *env_test script: csession IRIS "##class(isc.git.GitLab).test()" artifacts: paths: - tests.html
次に、 単体テストを呼び出す対応するサーバーコードを記述します( Habrの記事 )。
/// do ##class(isc.git.GitLab).test() ClassMethod test() { try { set tests = ##class(isc.git.Settings).getSetting("tests") if (tests'="") { set dir = ..getDir() set ^UnitTestRoot = dir $$$TOE(sc, ##class(%UnitTest.Manager).RunTest(tests, "/nodelete")) $$$TOE(sc, ..writeTestHTML()) throw:'..isLastTestOk() ##class(%Exception.General).%New("Tests error") } halt } catch ex { do ..logException(ex) do $system.Process.Terminate(, 1) } }
tests
— . , .
writeTestHTML
( ) - -.
Package
-, REST API:
<html> <head> <script type="text/javascript"> function initializePage() { var xhr = new XMLHttpRequest(); var url = "${CI_ENVIRONMENT_URL}:57772/MyApp/version"; xhr.open("GET", url, true); xhr.send(); xhr.onloadend = function (data) { document.getElementById("version").innerHTML = "Version: " + this.response; }; var xhr = new XMLHttpRequest(); var url = "${CI_ENVIRONMENT_URL}:57772/MyApp/author"; xhr.open("GET", url, true); xhr.send(); xhr.onloadend = function (data) { document.getElementById("author").innerHTML = "Author: " + this.response; }; } </script> </head> <body onload="initializePage()"> <div id = "version"></div> <div id = "author"></div> </body> </html>
"", ${CI_ENVIRONMENT_URL}
. (npm), . :
package client: <<: *env_test stage: package script: envsubst < client/index.html > index.html artifacts: paths: - index.html
Deploy
, index.html
-.
deploy client: <<: *env_test stage: deploy script: cp -f index.html /var/www/html/index.html
以上です!
? , . :
stages: - load - test .env_test: &env_test environment: name: test url: http://test.hostname.com only: - master tags: - test .env_preprod: &env_preprod environment: name: preprod url: http://preprod.hostname.com only: - preprod tags: - preprod .script_load: &script_load stage: load script: csession IRIS "##class(isc.git.GitLab).loadDiff()" load test: <<: *env_test <<: *script_load load preprod: <<: *env_preprod <<: *script_load
.
結論
— , , , . , . , .
InterSytems, InterSystems IRIS Data Platform ( Caché Ensemble), , , , .