DailyMotionでは、3年前に業務環境でKubernetesの実装を開始しました。ただし、複数のクラスターにアプリケーションをデプロイすることは困難が伴うため、過去数年間、対応するツールとワークフローの作成に取り組んできました。

何が発生したか

ここでは、世界中の複数のKubernetesクラスターにアプリケーションをデプロイする方法に焦点を当てます。

Kubernetesオブジェクトの複数のセットを一度にデプロイできるようにするために、Helmを使用して、すべてのChart を個別のGitリポジトリに保存します。 複数のアプリケーションスタックの完全なセットを展開できるようにするために、Umbrellaと呼ばれるChart を使用しました。これは依存関係の宣言をサポートし、コマンドラインを使用してAPI及び対応するサービスを開始できます。

さらに、Helmの上にpythonスクリプトを作成して、いくつかのチェック、Chart のビルド、キーの追加、アプリケーションのデプロイを行いました。これらのタスクはすべて、Dockerイメージを使用した集中型CIプラットフォームによって実現されました。

それでは本題に入っていきましょう。

注意:あなたがこのブログ投稿を読んでいる時点では、Helm 3は最初のRCバージョンをリリースしています。このバージョンは多くの便利な新機能をもたらし、これまでに検討してきた問題を解決します。

Chart 開発ワークフロー

このアプリケーションでは、ブランチワークフローを使用して管理し、それをChart の開発にも適用することにしました。

  • devブランチは、開発クラスターでテストされるChart を作成するために使用されます。
  • 次に、pull requestがマスターにマージされると、ステージング環境で検証されます。
  • 最後に、pull requestを作成し、これらの変更をprodブランチにマージして、これらの変更を本番環境に適用します。

各環境には、Chart を保存するための専用の保存域があります。 また、Chartmuseumを使用して、対応するAPIを公開しました。 このようにして、環境間の分離を強化し、これらのChart が本番環境で「集中」テストを受けていることを確認できます。

図:各環境のChart 保存域

開発者がdevブランチにプッシュすると、新しいバージョンのChart が自動的に開発環境のChartmuseumにプッシュされることに注意してください。この方法では、すべての開発者が同じ開発リポジトリを使用し、他の人のChart の変更を使用しないように、独自のChart バージョンを指定するように細心の注意を払う必要があります。

さらに、Pythonスクリプトはkubevalを使用してKubernetes OpenAPI定義を使用し、Chart をChartmuseumにプッシュする前に対応するKubernetesオブジェクトを確認します。
次の図は、Chart 開発ワークフローの概要です。

. gazr.ioによる品質タスク(lint、unit-test)の定義に従ってタスクフローを設定します。

2.アプリケーションのデプロイに使用されるpythonツールを含むDockerイメージをプッシュします。

3.ブランチ名に基づいて環境を設定します。

4. kubevalを使用してKubernetes yamlを確認します。

5.Chart のバージョンと対応する親子関係を自動的に増やします(変更されたChart によって異なります)。

6.環境に応じてChart をChartmuseumにプッシュします。

クラスターの違いを管理する

クラスター連携

場合によっては、Kubernetesクラスターフェデレーションを使用して、別のAPIでエンドポイントライフKubernetesオブジェクトにアクセスします。しかし、私達が直面している問題は、一部のオブジェクトをフェデレーションアクセスエンドポイントで作成できないため、フェデレーションオブジェクトや他の単一クラスターオブジェクトを維持することがより困難になることです。
この問題を緩和するために、クラスターを個別に管理して、プロセス全体を簡単に終了できるようにしました(v1クラスターフェデレーションを使用し、それに応じてv2が変更されます)。

地理的に分散したプラットフォーム

私達のプラットフォームは現在6つのリージョンにまたがっており、3つの自己構築および3つのクラウドデプロイメントが存在します。

図:分散展開

Helm global value

4つのglobal Helm valueにより、異なるクラスター環境での対応する違いを定義できます。これらは、すべてのクラスターで最小化されるデフォルト値です。

Global value

この情報は、アプリケーションのコンテキストを定義するのに役立ちます。これらの情報は、監視、追跡、ログ記録、および外部呼び出しの生成、拡張などにも使用されます。

  • クラウド:ハイブリッドKubernetesプラットフォームがあります。 たとえば、APIはGCPエリアと自作のデータセンターの両方にデプロイされています。
  • 環境:一部の値は、非本番環境では変更される場合があります。特に、リソース定義と自動拡張構成についてはそうです。
  • エリア:この情報は、クラスターの場所を識別するために使用され、最も近い外部サービスエンドポイントを定義できます。
  • クラスター名:各クラスターの値を定義する必要がある場合使用します。

これは一例です:


{{/* Returns Horizontal Pod Autoscaler replicas for GraphQL*/}}
{{- define "graphql.hpaReplicas" -}}
{{- if eq .Values.global.env "prod" }}
{{- if eq .Values.global.region "europe-west1" }}
minReplicas: 40
{{- else }}
minReplicas: 150
{{- end }}
maxReplicas: 1400
{{- else }}
minReplicas: 4
maxReplicas: 20
{{- end }}
{{- end -}}
view rawhelm_template.yaml hosted with ❤ by GitHub

helmテンプレート例

このロジックは、Kubernetes YAMLが確実に読み取り可能になるようにヘルプテンプレートで定義されていることに注意してください。

アプリケーションを宣言する

デプロイツールは複数のYAMLファイルに基づいています。以下は、各クラスターのサービスと対応する拡張トポロジ(レプリカの数)を宣言する例です。


releases:
  - foo.world
 
foo.world:                # Release name
  services:               # List of dailymotion's apps/projects
    foobar:
      chart_name: foo-foobar
      repo: git@github.com:dailymotion/foobar
      contexts:
        prod-europe-west1:
          deployments:
            - name: foo-bar-baz
              replicas: 18
            - name: another-deployment
              replicas: 3
view rawscaling-v1.yaml hosted with ❤ by GitHub

サービス定義

これは、導入ワークフローのすべてのステップを定義する概略図です。最後のステップは、複数の本番クラスターに同時にデプロイされます。

図:Jenkinsのデプロイ手順

キーはどうするか

セキュリティの分野では、さまざまな場所に分散している可能性のあるすべてのキーを追跡し、それらをパリの統一されたVaultバックグラウンドに保存することに重点を置いています。

私達の導入ツールは、Vaultからキーを取得し、実際の導入中にそれらをHelmに挿入する役割を果たします。

この効果を実現するために、Vaultキーとアプリケーション要件の間に保存されるマッピングを次のように定義しました。


secrets:
     - secret_id: "stack1-app1-password"                                                                                                                                                                                  
       contexts:
         - name: "default"                                                                                                                                                                                         
           vaultPath: "/kv/dev/stack1/app1/test"                                                                                                                                                               
           vaultKey: "password"                                                                                                                                                                                    
         - name: "cluster1"                                                                                                                                                                           
           vaultPath: "/kv/dev/stack1/app1/test"                                                                                                                                                               
           vaultKey: "password"
view rawsecret_example.yaml hosted with ❤ by GitHub
  • Vaultにキーを書き込む際に従うべき一般的なルールを定義しました。
  • キーがコンテキストまたはクラスターに固有である場合は、特別なエントリを追加します(ここでcluster1のコンテキストには独自のキーstack-app1-passwordがあります)。
  • それ以外の場合は、デフォルト値が使用されます
  • リスト内のアイテムごとに、キーと値のペアがKubernetes Secretに挿入されます。このように、Chart のSecretテンプレートは非常にシンプルです。

apiVersion: v1
data:
{{- range $key,$value := .Values.secrets }}
  {{ $key }}: {{ $value | b64enc | quote }}
{{ end }}
kind: Secret
metadata:
  name: "{{ .Chart.Name }}"
  labels:
    chartVersion: "{{ .Chart.Version }}"
    tillerVersion: "{{ .Capabilities.TillerVersion.SemVer }}"
type: Opaque
view rawsecret_template.yaml hosted with ❤ by GitHub

警告と制限

複数の倉庫を使用する

現在、アプリケーション開発プロセスはChart から切り離されています。つまり、開発者は2つのGitリポジトリで作業する必要があります。1つはアプリケーション用で、もう1つはKubernetesでのデプロイ方法を定義するためです。 実際、2つのGitリポジトリは2つのワークフローを意味し、これは新人を混乱させる可能性があります。

Umbrella Chart の管理は混乱を招く

Umbrella Chart について前述したように、これは依存関係を定義し、複数のアプリケーションを迅速に展開するのに役立ちます。ただし、オプション–resue-valuesを使用して、アプリケーションがデプロイされるたびにすべての値が渡されるのを回避する事もできます。これもUmbrella Chart の一部です。
継続的なリリースのワークフローでは、頻繁に変更される値は、コピー数とミラーtag(バージョン)の2つだけです。他のより安定した値の場合、それらを変更するには手動で更新する必要があり、判別が困難です。さらに、Umbrella Chart の小さなエラーがAlibaba Cloudに深刻な結果をもたらす可能性があります。私達は、それを目の当たりにしました。

複数の構成ファイルの更新

新しいアプリケーションを追加する場合、開発者は複数のファイルを変更する必要があります。アプリケーション宣言、キーリスト、アプリケーションがUmbrella Chart の一部である場合は、対応する依存関係に追加します。

Jenkinsの権限がVaultで拡張されている

現在、VaultのすべてのSecretを読み取ることができるAppRoleがあります。

ロールバックプロセスは自動化できません

ロールバックでは、複数のクラスターでコマンドを実行する必要があるため、エラーが発生しやすくなります。この操作とメンテナンス操作は、正しいバージョン番号を使用するために手動で実行します。

私達のGitOpsのビジョン

私達の目標

私達の考え方はデプロイするChart をアプリケーションリポジトリに配置することです。
このワークフローは開発と同じです。たとえば、ブランチがマスターにマージされるたびに、デプロイメントが自動的にトリガーされます。この方法と現在のワークフローの違いは、各部分がGit(アプリケーション自体と、Kubernetesにデプロイされる方法)によって管理されることです。
これには多くの利点があります。

  • 開発者の視点からの理解の難しさを大幅に簡素化します。ローカルChart に変更を適用する方法を学習する方が簡単になります。
  • サービスの展開は、コードと同じ場所で定義されます。
  • Umbrella Chart の管理を削除します。各サービスには独自のHelmリリースがあります。これにより、アプリケーションのライフサイクル管理(ロールバック、アップグレード)がアトミック操作になり、他のサービスは影響を受けません。
  • Chart 管理のためのgit機能の利点:ロールバック、監査ログなど。Chart をロールバックしたい場合は、gitを使用して完了できます。 展開は自動的にトリガーすることもできます。
  • Skaffoldの開発ワークフローを使用することによってもたらされる改善は、開発者が変更を実稼働のようなコンテキストでテストできるようにすることであると私達は考えています。

2段階の移行

開発者はこれらのワークフローを2年以上使用してきたため、できるだけスムーズに移行を完了する必要があります。そのため、目標を達成する前に中間ステップを追加することにしました。
1段階目はとても簡単です。

  • 同様の構造を使用するようにアプリケーションデプロイメントを構成することを保証しますが、DailymotionReleaseという名前の別のオブジェクトを使用します

apiVersion: "v1"
kind: "DailymotionRelease"
metadata:
  name: "app1.ns1"
  environment: "dev"
  branch: "mybranch"
spec:
  slack_channel: "#admin"
  chart_name: "app1"
  scaling:
    - context: "dev-us-central1-0"
      replicas:
        - name: "hermes"
          count: 2
    - context: "dev-europe-west1-0"
      replicas:
        - name: "app1-deploy"
          count: 2
  secrets:
    - secret_id: "app1"
      contexts:
        - name: "default"
          vaultPath: "/kv/dev/ns1/app1/test"
          vaultKey: "password"
        - name: "dev-europe-west1-0"
          vaultPath: "/kv/dev/ns1/app1/test"
          vaultKey: "password"
view rawscalerelease.yaml hosted with ❤ by GitHub

  • アプリケーションごとに1つのRelease(Umbrella Chart なし)
  • Chart はアプリケーションのgitリポジトリに保存されます

私達はすべての開発者の間でこの用語を広めており、移行プロセスはすでに始まっています。最初のステップは引き続きCIプラットフォームによって制御されます。近々、別のブログにて2番目のステップについて説明します。Fitを使用してGitOpsワークフローに移行するにはどうすればよいか。セットアップと、直面する課題(複数の倉庫、鍵)について説明します。ぜひご期待ください。 近年のアプリケーション開発ワークフローがGitOpsに向けてどのように導いてきたかを説明しようとします。この旅はまだ終わりません。今後もブログ投稿を公開していきます。物事をできるだけシンプルに、開発者の習慣に近づけることが正しい選択であると信じています。

PAGE TOP