paint-brush
Octopus と TeamCity で CI/CD を効率化する方案 に@socialdiscoverygroup
4,171 測定値
4,171 測定値

Octopus と TeamCity で CI/CD を効率化する方法

Social Discovery Group13m2024/05/14
Read on Terminal Reader

長すぎる; 読むには

明確で組織化されたプロセスがなければ、開発者は効果的に共同作業を行うのに苦労し、ソフトウェア更新の提供が遅れる可能性があることは周知の事実です。この記事では、Social Discovery Group チームが、TeamCity と Octopus を組み合わせて便利で柔軟な CI/CD パイプラインを構築する方法を紹介します。
featured image - Octopus と TeamCity で CI/CD を効率化する方法
Social Discovery Group HackerNoon profile picture
0-item


明確で体系的なプロセスがなければ、開発者が効果的にコラボレーションできず、ソフトウェア更新の提供が遅れる可能性があることは周知の事実です。数年前、Social Discovery Group チームは、最適ではない CI/CD プロセスという課題に直面しました。当時、チームはそれぞれ長所を持つ TeamCity と Octopus を使用していました。たとえば、Octopus はデプロイメントに便利で、TeamCity は自動テストに適しており、プロジェクトのビルドにも十分に便利です。構成が最大限に便利で柔軟である、包括的で視覚的に魅力的な CI/CD パイプラインを構築するには、ツールを組み合わせて使用する必要があります。いくつかのプロジェクトのコードは、Bitbucket のローカル リポジトリに保存されていました。SDG チームはこの問題を調査し、既存のツールを使用してプロセスを最適化することを決定しました。


主な最適化目標:

  1. TeamCity から指定された環境への自動ビルドとデプロイメント。
  2. ビルドの命名: ビルドを名前で区別するために、マスター ブランチに「release」が追加されます。
  3. 各サービスのそれぞれのテスト環境の対応するブランチにプッシュするときに、自動的にビルドおよびデプロイされます。
  4. ステージング環境、そして本番環境へのデプロイメントの前に、テスト環境と開発環境へのデプロイメントを完了する必要があるプロセスを確立します。これは Octopus で実装されました。


SDG チームは、ビルドと自動テストには TeamCity を便用し、デプロイメントには Octopus を便用することを決定しました。


TeamCity に実装されたもの:

  1. TeamCity の無料バージョンでは 3 つのエージェントを运行できますが、これは SDG チームには比较でした。新しいエージェントをインストールしてプールに追加し、テンプレートに適用しました。
  2. TeamCity の近期バージョンを利用した時点で、チームは Ubuntu Server で作業していました。スクリーンショットには、チームが利用した追加のプラグインが显示されています。



  1. チームはツールから、レポート作成用の Allure 2.14.0 や Nuget 5.5.1 などのプラグインを追加しました。
  2. 同様のタスクの実行を簡素化するために、SDG チームは、NuGet とサービスという異なるタイプのデプロイメント用に複数のテンプレートを作成しました。
  3. これらの各テンプレートには、以下のスクリーンショットに反映されているように、複数のステップが含まれています。


NuGet の展開は次のようになります。




ブランチがマスターであるかどうかに応じて、リリースに「-release」が追加されたことに特别注意してください (手順 3、4)。


サービスの展開は以下で確認できます。


各サービスでは、システム変数 (サービス名、%build.number% など) に基づいて対応する変数が置き換えられました。


Docker ビルド ステップの例をスクリーンショットに示します。


各プロジェクト リポジトリには対応する Dockerfile が含まれていました。


前述のように、ステップ 4 と 5 の違いは次のとおりです。



%deploymentTarget%変数は、デプロイメント中に Octopus の対応するステージ (Test、Dev など) が渡される環境パラメータとして機能しました。開発チームのそれぞれのブランチ (構成済み) に変更がプッシュされると、対応するテスト環境へのビルドとソフトウェアのデプロイメントが自動的に実行されました。設定は、以下のスクリーンショットに表示されています。Octopus に接続するには、octopus.apiKey と octopus.url の 2 つのグローバル パラメータを追加する必要がありました。



さらに、SDG チームは、接続セクションのすべてのプロジェクトに相通の NuGet リポジトリとコンテナー レジストリを接続しました。


さらに、SDG は、メール信息セクションでメール信息を設定し、バックアップ セクションでバックアップを設定し、必备なグループを弄成し、適切なロールを割り当て、必备なグループにユーザーを追加することを推奨しています。主なセットアップは了しており、結論として、チームは按期的に的提升を確認し、TeamCity を月に 1 回的提升することを推奨しています。


次に、Social Discovery Group チームは Octopus の構成に移りました。この記事では、インストールの詳細、根本的なユーザー権限の設定、その他の側面については説明しません。これらは自分で簡単に実行できるからです。チームはすぐに、ライブラリ セクションで構成されているライフサイクルに取り組みました。一下のスクリーンショットでは、SDG チームのフローの例を見ることができます。



次に、チームは変数セットでテーマごとに必不可少なすべての変数グループを制作しました。変数ごとに値が設定され、環境、ターゲット、ターゲット ロール (タグ) への依存関係が確立されました。次のスクリーンショットに例を示します。



Kubernetes のクラスターはターゲットとして機能し、ターゲット ロールは対応するクラスターまたはコンピューター環境に添付されたタグでした。これらはすべてインフラストラクチャ セクションで構成できます。


プロジェクトをグループ化し、プロジェクトにデプロイされたサービス、ステージ、バージョンを指出する便利店加盟なダッシュボードを設定することもできます。 SDG の展開プロセスは次のようになります。すべてのテスト ステージが 1 つのステップに統合され、ステージ ステージとライブ ステージについても同様に共享性のテンプレートが制成されました。


以下のスクリーンショットは、SDG チームでこれがどのように見えるかを示しています。

右側では、前述のライフサイクルが選択されています。パッケージのデプロイ ステージには、かなりシンプルなデフォルト設定が含まれています。

Deploy Raw Kubernetes Yaml ステージでは、SDG チームはユニバーサルな自己的記述 Yaml テンプレートを施用しました。この例 (Kubernetes スクリプト) については、以上で詳しく説明します。赤でマークされた対応するパラメータも置き換えられました。不必要なグローバル変数グループが [変数] -> [変数セット] メニューで接続され、プロジェクト具有の変数が [変数] -> [プロジェクト] メニューで設定され、優先順位が高くなっていることに小心してください。


この記事では、SDG チームは、プロジェクトへのロゴの追加、トリガーの設定、その他の細かい詳細などについては省略することにしました。2 つの至关重要なメニュー項目に热点を当てましょう。1 - リリース。ここでは、其他のリリースのバージョンと弄成日をいつでも確認できます。この情報はプロジェクト ダッシュボードにも觉得されます。2 - 変数 -> プレビュー。ここでは、対応するステージでどの変数が置き換えられるかを確認できます。





最も很重要な地方、つまり Kubernetes クラスターへの Yaml テンプレートのデプロイに移ります。これらは、ライブラリ -> ステップ テンプレート セクションで制作されました。下例は、SDG チームがパラメータを适用したスクリーンショットです。パラメータごとに、タグ、タイプ、デフォルト値を選択できるほか、説明を追加することもできます。これは強く推奨されます。




この場合のコードは次のようになります。


 apiVersion: apps/v1 kind: Deployment metadata: name: '#{Octopus.Project.Name | ToLower}' namespace: #{Octopus.Environment.Name | ToLower} labels: Octopus.Kubernetes.DeploymentName: '#{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}' spec: replicas: #{Replicas} strategy: type: RollingUpdate rollingUpdate: maxSurge: 25% maxUnavailable: 25% revisionHistoryLimit: 10 progressDeadlineSeconds: 600 selector: matchLabels: Octopus.Kubernetes.DeploymentName: '#{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}' template: metadata: labels: Octopus.Kubernetes.DeploymentName: '#{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}' spec: volumes: #{if usesidecar} - name: dump-storage persistentVolumeClaim: claimName: dumps-#{Octopus.Environment.Name | ToLower} #{/if} #{if MountFolders} #{each folder in MountFolders} - name: volume-#{folder | ToBase64 | Replace "\W" X | ToLower} hostPath: path: #{folder} type: DirectoryOrCreate #{/each} #{/if} - name: logs-volume hostPath: path: #{LogsDir} type: DirectoryOrCreate - name: appsettings secret: secretName: #{Octopus.Project.Name | ToLower} #{if Secrets} #{each secret in Secrets} - name: #{secret.name} secret: secretName: #{secret.name} #{/each} #{/if} #{if usesidecar} - name: diagnostics emptyDir: {} - name: dumps configMap: name: dumps defaultMode: 511 #{/if} containers: - name: #{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}-container image: #{DockerRegistry}/projectname.#{Octopus.Project.Name | ToLower}:#{Octopus.Release.Notes} #{if resources} resources: #{each resource in resources} #{resource.Key}: #{each entry in resource.Value} #{entry.Key}: #{entry.Value} #{/each} #{/each} #{/if} ports: - name: http containerPort: 80 protocol: TCP env: - value: "Development" name: "ASPNETCORE_ENVIRONMENT" - name: DD_ENV value: "#{Octopus.Environment.Name | ToLower}" - name: DD_SERVICE value: "#{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}" - name: DD_VERSION value: "1.0.0" - name: DD_AGENT_HOST value: "#{DatadogAgentHost}" - name: DD_TRACE_ROUTE_TEMPLATE_RESOURCE_NAMES_ENABLED value: "true" - name: DD_RUNTIME_METRICS_ENABLED value: "true" volumeMounts: #{if usesidecar} - name: dump-storage mountPath: /tmp/dumps #{/if} #{if MountFolders} #{each folder in MountFolders} - mountPath: #{folder} name: volume-#{folder | ToBase64 | Replace "\W" X | ToLower} #{/each} #{/if} - mountPath: #{LogsDir} name: logs-volume #{if usesidecar} - name: diagnostics mountPath: /tmp #{/if} - name: appsettings readOnly: true mountPath: /app/appsettings.json subPath: appsettings.json #{if Secrets} #{each secret in Secrets} - name: #{secret.name} readOnly: true mountPath: #{secret.mountPath} subPath: #{secret.subPath} #{/each} #{/if} readinessProbe: httpGet: path: hc port: http scheme: HTTP initialDelaySeconds: #{InitialDelaySeconds} imagePullPolicy: IfNotPresent securityContext: {} #{if usesidecar} - name: sidecar image: '#{DockerRegistry}/monitor:3' command: - /bin/sh args: - '-c' - while true; do . /app/init.sh; sleep 1m;done env: - name: USE_MEMORY value: '2048' - name: PROJECT value: "#{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}" resources: {} volumeMounts: - name: diagnostics mountPath: /tmp - name: dump-storage mountPath: /tmp/dumps - name: dumps mountPath: /app/init.sh subPath: init.sh shareProcessNamespace: true #{/if} affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: environment operator: In values: - "#{Node}" --- apiVersion: v1 kind: Service metadata: name: #{Octopus.Project.Name | ToLower} namespace: #{Octopus.Environment.Name | ToLower} labels: Octopus.Kubernetes.DeploymentName: '#{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}' spec: type: ClusterIP selector: Octopus.Kubernetes.DeploymentName: '#{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}' ports: - name: http port: 80 targetPort: http protocol: TCP --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: ingress.kubernetes.io/ssl-redirect: 'false' nginx.ingress.kubernetes.io/ssl-redirect: 'false' cert-manager.io/cluster-issuer: "letsencrypt-cluster-issuer" cert-manager.io/renew-before: '#{LetsencryptRenewBefore}' kubernetes.io/ingress.class: nginx #{if IngressAnnotations} #{each annotation in IngressAnnotations} #{annotation.Key}: #{annotation.Value} #{/each} #{/if} name: #{Octopus.Project.Name | ToLower} namespace: #{Octopus.Environment.Name | ToLower} labels: Octopus.Kubernetes.DeploymentName: '#{Octopus.Project.Name | ToLower}-#{Octopus.Environment.Name | ToLower}' spec: tls: #{if ExternalHost} #{each host in ExternalHost} - hosts: - #{host} secretName: #{Octopus.Project.Name | ToLower}-#{host | ToBase64 | Replace "\W" X | ToLower}-tls #{/each} #{/if} rules: #{if ExternalHost} #{each host in ExternalHost} - host: '#{host}' http: paths: - path: / pathType: ImplementationSpecific backend: service: name: #{Octopus.Project.Name | ToLower} port: name: http #{/each} #{/if} #{if usesidecar} --- apiVersion: v1 kind: ConfigMap metadata: name: dumps namespace: #{Octopus.Environment.Name | ToLower} data: init.sh: |- #!/usr/bin/env bash mem=$(ps aux | awk '{print $6}' | sort -rn | head -1) mb=$(($mem/1024)) archiveDumpPath="/tmp/dumps/$PROJECT-$(date +"%Y%m%d%H%M%S").zip" fullPathGc="/tmp/$PROJECT-$(date +"%Y%m%d%H%M%S").dump" echo "mem:" $mb" project:" $PROJECT "use:" $USE_MEMORY if [ "$mb" -gt "$USE_MEMORY" ]; then export USE_MEMORY=$(($USE_MEMORY*2)) pid=$(dotnet-dump ps | awk '{print $1}') dotnet-dump collect -p $pid -o $fullPathGc zip $fullPathGc.zip $fullPathGc mv $fullPathGc.zip $archiveDumpPath rm $fullPathGc fi #{/if}


Octopus のすべての変数は、コード内で次の形式で指定されています: '#{Octopus.Project.Name | ToLower}' 。最後の部分は小文字に変換することを示します。


最後の構成ファイルは、.NET サービスが特殊のメモリ利摄入量制限に達したときにその状態を自動的に另存するために制作されました。これにより、開発中にメモリ リークを特殊し、サービスを很快に修整するのに大きく役立ちました。


最終的に、サービス ダッシュボードは次のようになりました。


開発チームとテストチームは、このダッシュボードでの作業が极为に连锁便利店だと感じました。


最適化結果:


  1. SDG チームは、開発のスピードと利便性を大幅に向上させる効率的な CI/CD プロセスを構築しました。チームはこのプロセス フレームワーク内でかなり長い期間作業しました。
  2. SDG は、便利な TeamCity ツールに自動テストを導入し、サービスの展開も自動化しました。
  3. チームは、ユーザーフレンドリーな Octopus ダッシュボードを通じてアクセス権を設定し、展開と環境を管理することができました。
その後、SDG は Octopus に他の多くの機能を実装しました。たとえば、スケジュールに従って夜間にクラスターを自動的にシャットダウンする機能などです。


しかし、完璧さの最求には限界がありません。Social Discovery Group チームは、Azure DevOps を習得することで開発を進めました。Helm 上の 1 つのエコシステム内で、さらに收录的かつ効率的な Azure DevOps プロセスを構築しました。これについては次の記事で説明します。


Octopus と TeamCity を运行して CI/CD をセットアップおよび最適化した経験をぜひお聞かせください。ご意見やヒントを共设してください。
바카라사이트 바카라사이트 온라인바카라