[{"data":1,"prerenderedAt":706},["ShallowReactive",2],{"/ja-jp/blog/using-child-pipelines-to-continuously-deploy-to-five-environments/":3,"navigation-ja-jp":39,"banner-ja-jp":454,"footer-ja-jp":467,"Olivier Dupré":677,"next-steps-ja-jp":691},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":29,"_id":32,"_type":33,"title":34,"_source":35,"_file":36,"_stem":37,"_extension":38},"/ja-jp/blog/using-child-pipelines-to-continuously-deploy-to-five-environments","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"子パイプラインを使用して5つの環境に継続的にデプロイする","使用するGitLabワークフローを最小限に抑えつつ、複数の環境（事前の準備なしに一時的に利用できるsandboxなど）への継続的デプロイを管理する方法を解説します。","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097012/Blog/Hero%20Images/Blog/Hero%20Images/AdobeStock_397632156_3Ldy1urjMStQCl4qnOBvE0_1750097011626.jpg","https://about.gitlab.com/blog/using-child-pipelines-to-continuously-deploy-to-five-environments","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"子パイプラインを使用して5つの環境に継続的にデプロイする\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Olivier Dupré\"}],\n        \"datePublished\": \"2024-09-26\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22,"updatedDate":28},[18],"Olivier Dupré","2024-09-26","DevSecOpsチームでは、複数の環境にまたがる継続的デプロイを管理する機能が必要となることがあります。その場合、ワークフローを変更せずに、デプロイを行えるようにする必要があります。[GitLab DevSecOpsプラットフォーム](https://about.gitlab.com/)なら、事前の準備なしに一時的に利用できるsandboxを使用して工数を最小限に抑えるアプローチなどを通じて、こうしたニーズに対応できます。この記事では、Terraformを使って複数の環境上でインフラの継続的デプロイを行う方法についてご紹介します。\n\nこの手法は、[Pulumi](https://www.pulumi.com/)や[Ansible](https://www.ansible.com/)のような別の技術を使用したInfrastructure as Code（IaC）でも、どのような言語で書かれたソースコードでも、または多様な言語が混在するモノレポであっても、あらゆるプロジェクトに簡単に適用できます。\n\nこのチュートリアルの終了時には、以下のような環境をデプロイするパイプラインが完成します。\n\n* 各フィーチャーブランチの一時的な**レビュー（review）**環境。\n* 簡単に消去可能で、mainブランチからデプロイされる**統合（integration）**環境。\n* **品質管理（qa）**環境。同様にmainブランチからデプロイされ、品質管理プロセスを実行します。\n* タグ付けされるたびにデプロイされる**ステージング（staging）**環境。これは本番環境前の最後のステージです。\n* ステージング環境の直後の**本番（production）**環境。今回はデモ用に手動でトリガーしますが、継続的にデプロイされるようにすることも可能です。\n\n>この記事で使用されるフローチャートの説明は以下のとおりです。\n> * 角が丸いボックスはGitLabブランチです。\n> * 四角のボックスは環境です。\n> * 矢印上のテキストは、あるボックスから次のボックスへのアクションを指します。\n> * ひし形のボックスは決定ステップです。\n\n\u003Cpre class=\"mermaid\">\nflowchart LR\n    A(main) -->|新機能| B(feature_X)\n\n    B -->|自動デプロイ| C[review/feature_X]\n    B -->|マージ| D(main)\n    C -->|破棄| D\n\n    D -->|自動デプロイ| E[integration]\n    E -->|手動| F[qa]\n\n    D -->|タグ付け| G(X.Y.Z)\n    F -->|検証| G\n\n    G -->|自動デプロイ| H[staging]\n    H -->|手動| I{plan}\n    I -->|手動| J[production]\n\u003C/pre>\n\nステップごとに、[理由](#why)と[行うこと](#what)を説明した上で、[方法](#how)をご紹介します。これにより、このチュートリアルを完全に理解し、正確に実行しやすくなります。\n\n## 理由\n\n* [継続的インテグレーション](https://about.gitlab.com/topics/ci-cd/#what-is-continuous-integration-ci)はほぼ事実上の業界標準と言えます。ほとんどの企業は、CIパイプラインを実装済みであるか、その実践の標準化を検討しています。\n\n* また、CIパイプラインの最後にリポジトリまたはレジストリにアーティファクトをプッシュする[継続的なデリバリー](https://about.gitlab.com/topics/ci-cd/#what-is-continuous-delivery-cd)も一般的です。\n\n* 継続的デプロイメントはさらに進んで、これらのアーティファクトを自動的にデプロイしますが、その普及はまだ限定的です。導入されている場合、主にアプリケーション分野で見られます。インフラの継続的デプロイメントに関しては、状況がやや不明瞭で、複数の環境の管理に重きが置かれる傾向があります。一方で、インフラのコードをテストし、セキュリティを確保し、検証することはより難しいとされています。この分野は、DevOpsがまだ成熟に至っていない分野のひとつです。ほかの分野としては、セキュリティのシフトレフトが挙げられます。具体的には、セキュリティチームの介入、さらに重要なことに、セキュリティ上のリスクへの対応をデリバリーライフサイクルの早期に組み込み、DevOpsから***DevSecOps***へと発展させる取り組みのことです。\n\nこのような概況を踏まえ、本チュートリアルでは、インフラにDevSecOpsをシンプルかつ効果的に導入するシナリオに取り組みます。5つの環境にリソースをデプロイする例を交えながら、開発から本番環境へと段階的に進めていきます。\n\n__注__：個人的にはFinOpsアプローチを採用し、環境の数を減らすことを推奨していますが、開発環境、ステージング環境、本番環境以外の環境を保持すべき場合もあります。そのため、これからご紹介する例をご自身のニーズに合わせて調整してください。\n\n## 行うこと\n\nクラウド技術の台頭により、IaCの利用が促進されています。この分野を最初に開拓したのは、AnsibleとTerraformでした。OpenTofu、Pulumi、AWS CDK、Google Deploy Managerを始めとする多くの会社がその後に続きました。\n\nIaCを定義することは、インフラストラクチャを安全にデプロイするのに最適なソリューションです。目標を達成できるまで必要なだけ、テスト、デプロイ、再実行を繰り返し行えます。\n\n残念なことに、ターゲット環境ごとに複数のブランチやリポジトリを保持している企業がよくあります。これが原因で問題が生じます。こういった企業では、プロセスの実施が徹底されていません。本番環境のコードベースへの変更が、その前の環境で正しくテストされているかどうかも確認できません。その結果、ある環境から別の環境へ流れるだけになります。\n\nこのチュートリアルが必要だと気づいたのは、あるカンファレンスに参加した際に、本番環境へのデプロイ前にインフラストラクチャを十分にテストするワークフローがないと参加者全員から聞いたときです。みなが、本番環境で直接コードにパッチを適用することもあると言っていました。確かにこの方法は手っ取り早いですが、果たして安全でしょうか？前の環境にフィードバックをどう戻すのでしょうか？また副次効果が生じないようにするにはどうすればよいのでしょうか？新たな脆弱性が本番環境にあまりにも早くプッシュされることで会社がリスクにさらされないようにするには、どのように管理すべきでしょうか？\n\nここで重要なのは、DevOpsチームが本番環境に直接デプロイするのは*なぜ*かということです。パイプラインの効率性や速度を向上できる可能性があるためでしょうか？自動化できないのでしょうか？それどころか、*本番環境以外で正確にテストする方法がない*からなのでしょうか？\n\n次のセクションでは、インフラストラクチャを自動化し、ほかの人に影響を及ぼす環境にコードがプッシュされる前に、DevOpsチームが効果的かつ確実にテストを実施するための方法をご説明します。また、コードがどのように保護され、エンドツーエンドでデプロイが管理されているかも確認していきます。\n\n## 方法\n\n前述のとおり、現在では多くのIaC言語が存在しているため、この記事だけで客観的に*すべて*を取り上げることはできません。そのため、この記事ではバージョン1.4で実行される基本的なTerraformコードを使用します。IaC言語そのものではなく、貴社のエコシステムに適用できるプロセスに注目してください。\n\n### Terraformコード\n\nまずは、基本的なTerraformコードから始めましょう。\n\n仮想ネットワークであるAWSの仮想プライベートクラウド（VPC）にデプロイしたいと思います。VPCには、パブリックサブネットとプライベートサブネットをデプロイします。名前からわかるように、これらはメインVPCのサブネットです。最後に、パブリックサブネットにAmazon Elastic Cloud Compute（EC2）インスタンス（仮想マシン）を追加します。\n\nこれは、比較的簡単な方法で4つのリソースをデプロイする方法を示しています。コードではなく、パイプラインに焦点を当てることがここでの目的です。\n\nここで目指すリポジトリの完成形は、以下のとおりです。\n\n![リポジトリの完成図](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097033/Blog/Content%20Images/Blog/Content%20Images/image5_aHR0cHM6_1750097033415.png)\n\nステップごとに行っていきましょう。\n\nまずは、`terraform/main.tf`ファイルでリソースをすべて宣言します。\n\n```terraform\nprovider \"aws\" {\n  region = var.aws_default_region\n}\n\nresource \"aws_vpc\" \"main\" {\n  cidr_block = var.aws_vpc_cidr\n\n  tags = {\n    Name     = var.aws_resources_name\n  }\n}\n\nresource \"aws_subnet\" \"public_subnet\" {\n  vpc_id     = aws_vpc.main.id\n  cidr_block = var.aws_public_subnet_cidr\n\n  tags = {\n    Name = \"Public Subnet\"\n  }\n}\nresource \"aws_subnet\" \"private_subnet\" {\n  vpc_id     = aws_vpc.main.id\n  cidr_block = var.aws_private_subnet_cidr\n\n  tags = {\n    Name = \"Private Subnet\"\n  }\n}\n\nresource \"aws_instance\" \"sandbox\" {\n  ami           = var.aws_ami_id\n  instance_type = var.aws_instance_type\n\n  subnet_id = aws_subnet.public_subnet.id\n\n  tags = {\n    Name     = var.aws_resources_name\n  }\n}\n```\n\nご覧のとおり、このコードではいくつかの変数が必要となるため、`terraform/variables.tf`ファイルでそれらを宣言します。\n\n```terraform\nvariable \"aws_ami_id\" {\n  description = \"The AMI ID of the image being deployed.\"\n  type        = string\n}\n\nvariable \"aws_instance_type\" {\n  description = \"The instance type of the VM being deployed.\"\n  type        = string\n  default     = \"t2.micro\"\n}\n\nvariable \"aws_vpc_cidr\" {\n  description = \"The CIDR of the VPC.\"\n  type        = string\n  default     = \"10.0.0.0/16\"\n}\n\nvariable \"aws_public_subnet_cidr\" {\n  description = \"The CIDR of the public subnet.\"\n  type        = string\n  default     = \"10.0.1.0/24\"\n}\n\nvariable \"aws_private_subnet_cidr\" {\n  description = \"The CIDR of the private subnet.\"\n  type        = string\n  default     = \"10.0.2.0/24\"\n}\n\nvariable \"aws_default_region\" {\n  description = \"Default region where resources are deployed.\"\n  type        = string\n  default     = \"eu-west-3\"\n}\n\nvariable \"aws_resources_name\" {\n  description = \"Default name for the resources.\"\n  type        = string\n  default     = \"demo\"\n}\n```\n\nすでにIaC側に関しては、これでほぼ準備が整いました。しかしながら、これではTerraformの状態を共有できません。ご存知ない方のために大まかに説明すると、Terraformは以下を行うことで動作します。\n\n* `plan`により、インフラストラクチャの現在の状態とコートで定義されている内容の差分を確認してから、差分を出力します。\n* `apply`により、`plan`の差分を適用して、状態を更新します。\n\n最初のラウンドでは状態は空で、その後、Terraformによって適用されたリソースの詳細（IDなど）が挿入されます。\n\n問題は、その状態がどこに保存されるかということです。また、複数のデベロッパーがコード上で共同作業を行えるようにするにはどうすればよいのでしょうか？\n\n解決策はとても簡単で、GitLabを利用して、[Terraform HTTPバックエンド](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html)を介して状態を保存して共有するだけです。\n\nこのバックエンドを使用するには、まずはもっともシンプルな`terraform/backend.tf`ファイルを作成します。次のステップは、パイプラインで処理します。\n\n```terraform\nterraform {\n  backend \"http\" {\n  }\n}\n```\n\nこれで、4つのリソースをデプロイするための最低限のTerraformコードができあがりました。変数の値は実行する際に指定するので、後でご説明します。\n\n### ワークフロー\n\nこれから以下のワークフローを実装します。\n\n\u003Cpre class=\"mermaid\">\nflowchart LR\n    A(main) -->|新機能| B(feature_X)\n\n    B -->|自動デプロイ| C[review/feature_X]\n    B -->|マージ| D(main)\n    C -->|破棄| D\n\n    D -->|自動デプロイ| E[integration]\n    E -->|手動| F[qa]\n\n    D -->|タグ付け| G(X.Y.Z)\n    F -->|検証| G\n\n    G -->|自動デプロイ| H[staging]\n    H -->|手動| I{plan}\n    I -->|手動| J[production]\n\u003C/pre>\n\n1. **フィーチャー**ブランチを作成します。これにより、コードに対して継続的にすべてのスキャナーが実行され、コンプライアンスとセキュリティを確保できます。このコードは、現在のブランチの名前が付けられた一時的な環境`review/feature_branch`に継続的にデプロイされます。これは、デベロッパーと運用チームが誰にも影響を与えることなくコードをテストできる安全な環境です。また、ここでコードレビューやスキャナーの実行などのプロセスを実施し、コードの品質とセキュリティが許容範囲内であることを確認し、資産が危険にさらされることのないようにします。このブランチでデプロイされたインフラストラクチャは、ブランチが閉じられると自動的に破棄されます。これにより予算範囲内に収めやすくなります。\n\n\u003Cpre class=\"mermaid\">\nflowchart LR\n    A(main) -->|新機能| B(feature_X)\n\n    B -->|自動デプロイ| C[review/feature_X]\n    B -->|マージ| D(main)\n    C -->|破棄| D\n\u003C/pre>\n\n2. 承認されると、フィーチャーブランチはmainブランチに**マージ**されます。これは[保護ブランチ](https://docs.gitlab.com/ee/user/project/protected_branches.html)であり、誰もプッシュできません。本番環境への変更リクエストをすべて十分にテストするために必要です。このブランチも継続的にデプロイされます。ここでのターゲットは`integration`環境です。この環境をもう少し安定させるために、削除は自動化されておらず、手動でトリガーできるようになっています。\n\n\u003Cpre class=\"mermaid\">\nflowchart LR\n    D(main) -->|自動デプロイ| E[integration]\n\u003C/pre>\n\n3. ここから次のデプロイをトリガーするには、手動による承認が必要となります。これにより、mainブランチが`qa`環境にデプロイされます。ここでパイプラインからの削除を防ぐルールを設定します。何しろすでに3つ目のこの環境はかなり安定しているはずなので、このルールは誤って削除されるのを防ぐことを目的とします。貴社のプロセスに合わせて、お好きなようなルールを調整してください。\n\n\u003Cpre class=\"mermaid\">\nflowchart LR\n    D(main)-->|自動デプロイ| E[integration]\n    E -->|手動| F[qa]\n\u003C/pre>\n\n4. 次に進むには、コードに**タグ付け**する必要があります。[保護タグ](https://docs.gitlab.com/ee/user/project/protected_tags.html)を使用して、特定のユーザーのみが最後の2つの環境にデプロイできるようにします。これにより、`staging`環境へのデプロイが即座にトリガーされます。\n\n\u003Cpre class=\"mermaid\">\nflowchart LR\n    D(main) -->|タグ付け| G(X.Y.Z)\n    F[qa] -->|検証| G\n\n    G -->|自動デプロイ| H[staging]\n\u003C/pre>\n\n5. ついに`production`に到達しました。インフラストラクチャに関して言うと、（10%や25%など）段階的にデプロイするのは難しい場合が多いため、インフラストラクチャ全体をデプロイします。ただし、この最後のステップで行う手動トリガーで、このデプロイを制御します。そして、この極めて重要な環境を最大限に制御できるようにするために、[保護環境](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)として管理します。\n\n\u003Cpre class=\"mermaid\">\nflowchart LR\n    H[staging] -->|手動| I{plan}\n    I -->|手動| J[production]\n\u003C/pre>\n\n### パイプライン\n\n上記の[ワークフロー](#the-workflow)を実装するために、2つの[ダウンストリームパイプライン](https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html)とともにパイプラインを構築します。\n\n#### メインパイプライン\n\nまずは、メインパイプラインから始めましょう。メインパイプラインは、**フィーチャーブランチへのプッシュ**、**デフォルトブランチへのマージ**、または**タグ付け**が発生すると、必ず自動的にトリガーされます。*このパイプライン*によって、`dev`、`integration`、`staging`環境に対する真の**継続的デプロイ**を実現できます。プロジェクトのルートにある`.gitlab-ci.yml`ファイルで宣言します。\n\n![リポジトリのターゲット](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097033/Blog/Content%20Images/Blog/Content%20Images/image1_aHR0cHM6_1750097033417.png)\n\n```yml\nStages:\n  - test\n  - environments\n\n.environment:\n  stage: environments\n  variables:\n    TF_ROOT: terraform\n    TF_CLI_ARGS_plan: \"-var-file=../vars/$variables_file.tfvars\"\n  trigger:\n    include: .gitlab-ci/.first-layer.gitlab-ci.yml\n    strategy: depend            # Wait for the triggered pipeline to successfully complete\n    forward:\n      yaml_variables: true      # Forward variables defined in the trigger job\n      pipeline_variables: true  # Forward manual pipeline variables and scheduled pipeline variables\n\nreview:\n  extends: .environment\n  variables:\n    environment: review/$CI_COMMIT_REF_SLUG\n    TF_STATE_NAME: $CI_COMMIT_REF_SLUG\n    variables_file: review\n    TF_VAR_aws_resources_name: $CI_COMMIT_REF_SLUG  # Used in the tag Name of the resources deployed, to easily differenciate them\n  rules:\n    - if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n\nintegration:\n  extends: .environment\n  variables:\n    environment: integration\n    TF_STATE_NAME: $environment\n    variables_file: $environment\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n\nstaging:\n  extends: .environment\n  variables:\n    environment: staging\n    TF_STATE_NAME: $environment\n    variables_file: $environment\n  rules:\n    - if: $CI_COMMIT_TAG\n\n#### TWEAK\n# This tweak is needed to display vulnerability results in the merge widgets.\n# As soon as this issue https://gitlab.com/gitlab-org/gitlab/-/issues/439700 is resolved, the `include` instruction below can be removed.\n# Until then, the SAST IaC scanners will run in the downstream pipelines, but their results will not be available directly in the merge request widget, making it harder to track them.\n# Note: This workaround is perfectly safe and will not slow down your pipeline.\ninclude:\n  - template: Security/SAST-IaC.gitlab-ci.yml\n#### END TWEAK\n\n```\n\nこのパイプラインは、`test`と`environments`の2つのステージのみを実行します。前者は、*TWEAK（微調整）*により、スキャナーを実行するために必要です。後者では、上記で定義したケース（ブランチへのプッシュ、デフォルトブランチへのマージ、タグ付け）ごとに異なる変数セットを持つ子パイプラインがトリガーされます。\n\nここで子パイプラインに[strategy:depend](https://docs.gitlab.com/ee/ci/yaml/index.html#triggerstrategy)キーワードで依存を追加します。これにより、デプロイの完了後にGitLabのパイプラインビューが更新されます。\n\nご覧のとおりベースとなるジョブが[無効になる](https://docs.gitlab.com/ee/ci/jobs/#hide-jobs)ように定義し、特定の変数とルールで拡張して、ターゲット環境ごとに単一のデプロイメントだけがトリガーされるようにしています。\n\n[定義済み変数](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)に加え、定義する必要がある新たな2つのエントリを使用します。\n1. 各環境[固有の変数](#the-variable-definitions)：`../vars/$variables_file.tfvars`\n2. [子パイプライン](#the-child-pipeline)。`.gitlab-ci/.first-layer.gitlab-ci.yml`で定義\n\nまずは、簡単な方、つまり変数の定義から始めましょう。\n\n### 変数の定義\n\nここでは、2つのソリューションを組み合わせてTerraformに変数を提供します。\n\n* 1つ目は、[.tfvarsファイル](https://developer.hashicorp.com/terraform/language/values/variables#variable-definitions-tfvars-files)を使用して、機密性の低い入力をすべて行う方法です。これはGitLab内に保存する必要があります。\n\n![Terraformに変数を提供するための1つ目のソリューション](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097034/Blog/Content%20Images/Blog/Content%20Images/image2_aHR0cHM6_1750097033419.png)\n\n* 2つ目は、プレフィックスに`TF_VAR`を付けた[環境変数](https://developer.hashicorp.com/terraform/language/values/variables#environment-variables)を使用する方法です。変数を挿入するこの2つ目の方法は、[変数をマスク](https://docs.gitlab.com/ee/ci/variables/#mask-a-cicd-variable)し、[保護](https://docs.gitlab.com/ee/ci/variables/#protect-a-cicd-variable)し、さらに[スコープの環境を設定する](https://docs.gitlab.com/ee/ci/environments/index.html#limit-the-environment-scope-of-a-cicd-variable)GitLabの機能とも関係する、**機密情報の漏えいを防ぐ**強力なソリューションです（本番環境のプライベートClassless Inter-Domain Routing（CIDR）で非常に機密性が高いデータをやり取りすると考えられる場合は、この方法で保護すれば、本番環境、および保護ブランチやタグに対して実行されるパイプラインでのみ利用できるようにし、ジョブのログでその値がマスクされるようにすることができます）。\n\n![Terraformに変数を提供するための2つ目のソリューション](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097034/Blog/Content%20Images/Blog/Content%20Images/image4_aHR0cHM6_1750097033422.png)\n\nまた、各変数ファイルを変更できるユーザーを設定するために、[`CODEOWNERS`ファイル](https://docs.gitlab.com/ee/user/project/codeowners/)で各変数ファイルを管理する必要があります。\n\n```\n[Production owners] \nvars/production.tfvars @operations-group\n\n[Staging owners]\nvars/staging.tfvars @odupre @operations-group\n\n[CodeOwners owners]\nCODEOWNERS @odupre\n```\n\nこの記事は、Terraformのトレーニング用ではないため、詳しく説明せず、ここでは`vars/review.tfvars`ファイルを紹介するだけに留めます。当然ながら、これに続く環境ファイルもほぼ同じです。ここでは機密性の低い変数とその値を設定するだけです。\n\n```shell\naws_vpc_cidr = \"10.1.0.0/16\"\naws_public_subnet_cidr = \"10.1.1.0/24\"\naws_private_subnet_cidr = \"10.1.2.0/24\"\n```\n\n#### 子パイプライン\n\n実際の作業はこのパイプライン内で行われます。そのため、最初のパイプラインよりも少し複雑です。しかしながら、力を合わせれば何でも乗り越えられます！\n\n[メインパイプライン](#the-main-pipeline)の定義で説明したように、ダウンストリームパイプラインは`.gitlab-ci/.first-layer.gitlab-ci.yml`で宣言されています。\n\n![ファイルで宣言されているダウンストリームパイプライン](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097033/Blog/Content%20Images/Blog/Content%20Images/image3_aHR0cHM6_1750097033424.png)\n\n小さなステップに分けて説明します。最後に全体像が見えるはずです。\n\n##### Terraformコマンドを実行してコードを保護する\n\nまずは、Terraformのパイプラインを実行したいと思います。GitLabはオープンソースであるため、Terraform用のテンプレートもオープンソースです。そのため、このテンプレートを含めるだけで済みます。以下のスニペットを使用して行えます。\n\n```yml\ninclude:\n  - template: Terraform.gitlab-ci.yml\n```\n\nこのテンプレートは、planとapplyが行われる前に、Terraformによるフォーマットのチェックとコードの検証を実行します。デプロイしたものを破棄することもできます。\n\nさらに、GitLabは統合された単一のDevSecOpsプラットフォームであるため、このテンプレート内に2つのセキュリティスキャナーを自動的に組み込み、コード内の潜在的な脅威を検出し、次の環境にデプロイされる前に警告を発します。\n\nこれでコードの確認、保護、ビルド、デプロイが完了したので、いくつかの便利な技をご紹介します。\n\n##### ジョブ間でキャッシュを共有する\n\nジョブの結果をキャッシュして、後続のパイプラインジョブで再利用します。これはとても簡単で、以下のコードを追加するだけで行えます。\n\n```yml\ndefault:\n  cache:  # Use a shared cache or tagged runners to ensure terraform can run on apply and destroy\n    - key: cache-$CI_COMMIT_REF_SLUG\n      fallback_keys:\n        - cache-$CI_DEFAULT_BRANCH\n      paths:\n        - .\n```\n\nここでは、コミットごとに異なるキャッシュを定義し、必要に応じてmainブランチ名にフォールバックするようにします。\n\n使用しているテンプレートをよく見ると、ジョブの実行タイミングを制御するルールがあることがわかります。全ブランチですべての制御（QAとセキュリティの両方）を実行したいと思います。そのため、次にこれらの設定を上書きします。\n\n##### すべてのブランチで制御を実行する\n\nGitLabテンプレートは強力な機能で、テンプレートの一部のみを上書きできます。品質チェックとセキュリティチェックが必ず実行されるよう、一部のジョブのルールを上書きしたいと思います。これらのジョブ向けに定義するその他すべては、テンプレートで定義された内容のままにします。\n\n```yml\nfmt:\n  rules:\n    - when: always\n\nvalidate:\n  rules:\n    - when: always\n\nkics-iac-sast:\n  rules:\n    - when: always\n\niac-sast:\n  rules:\n    - when: always\n```\n\nこれで品質とセキュリティの制御を実施できたため、[ワークフロー](#the-workflow)内のメインの環境（integrationとstaging）とreview環境の動作に違いを付けたいと思います。まずはメインの環境の振る舞いを定義し、review環境用にこの設定を微調整していきましょう。\n\n##### integrationとstaging環境への継続的デプロイ\n\n前述のように、この2つの環境にmainブランチとタグをデプロイしたいため、そのように制御するルールを`build`と`deploy`の両方のジョブに追加します。そして、`integration`環境でのみ`destroy`を有効にします。`staging`環境は重要度が高いため、ワンクリックで削除できないようにします。この操作はエラーを引き起こしやすく、避けたいと考えています。\n\n最後に、`deploy`ジョブを`destroy`ジョブにリンクして、GitLab GUIから直接環境を`stop`できるようにします。\n\nここで使用する`GIT_STRATEGY`は、破棄する際にRunner内のソースブランチからコードが取得されることを防ぎます。これは、ブランチが手動で削除された場合は失敗するため、キャッシュを使用して、Terraformの命令を実行するために必要なものすべてを取得します。\n\n```yml\nbuild:  # terraform plan\n  environment:\n    name: $TF_STATE_NAME\n    action: prepare\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n    - if: $CI_COMMIT_TAG\n\ndeploy: # terraform apply --> automatically deploy on corresponding env (integration or staging) when merging to default branch or tagging. Second layer environments (qa and production) will be controlled manually\n  environment: \n    name: $TF_STATE_NAME\n    action: start\n    on_stop: destroy\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n    - if: $CI_COMMIT_TAG\n\ndestroy:\n  extends: .terraform:destroy\n  variables:\n    GIT_STRATEGY: none\n  dependencies:\n    - build\n  environment:\n    name: $TF_STATE_NAME\n    action: stop\n  rules:\n    - if: $CI_COMMIT_TAG  # Do not destroy production\n      when: never\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $TF_DESTROY == \"true\" # Manually destroy integration env.\n      when: manual\n```\n前述のとおり、これは`integration`と`staging`環境へのデプロイというニーズに即しています。しかしながら、デベロッパーがほかの人に影響を及ぼさずに、自分のコードに触れて検証できる一時的な環境がまだ不足しています。そのため、次は`review`環境へのデプロイを行います。\n\n##### review環境への継続的デプロイ\n\nreview環境へのデプロイは、`integration`や`staging`環境へのデプロイと大差はありません。そこで、ここでもGitLabの機能を活用して、ジョブ定義の一部のみを上書きします。\n\nまずは、これらのジョブがフィーチャーブランチでのみ実行されるようルールを設定します。\n\n次に、`deploy_review`ジョブを`destroy_review`ジョブにリンクします。これにより、GitLabユーザーインターフェイスから**手動で**環境を停止できるようになりますが、さらに重要なのは、フィーチャーブランチの完了時に**環境の破棄が自動的にトリガー**されるようになります。これは、運用にかかる費用を抑えるのに効果的な、優れたFinOpsプラクティスです。\n\nTerraformでは、インフラストラクチャの構築時と同様に、破棄する際にもplanファイルが必要なため、`destroy_review`から`build_review`に依存を追加して、そのアーティファクトを取得します。\n\n最後に、ご覧のとおり、環境の名前を`$environment`に設定します。これは、[メインパイプライン](#the-main-pipeline)で`review/$CI_COMMIT_REF_SLUG`に設定され、`trigger:forward:yaml_variables:true`という命令により、その子パイプラインに転送されます。\n\n```yml\nbuild_review:\n  extends: build\n  rules:\n    - if: $CI_COMMIT_TAG\n      when: never\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: on_success\n\ndeploy_review:\n  extends: deploy\n  dependencies:\n    - build_review\n  environment:\n    name: $environment\n    action: start\n    on_stop: destroy_review\n    # url: https://$CI_ENVIRONMENT_SLUG.example.com\n  rules:\n    - if: $CI_COMMIT_TAG\n      when: never\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: on_success\n\ndestroy_review:\n  extends: destroy\n  dependencies:\n    - build_review\n  environment:\n    name: $environment\n    action: stop\n  rules:\n    - if: $CI_COMMIT_TAG  # Do not destroy production\n      when: never\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH   # Do not destroy staging\n      when: never\n    - when: manual\n```\n\nさて、まとめると、これで次のことを行うパイプラインができました。\n\n* 一時的なreview環境へのデプロイ。フィーチャーブランチの完了時に、自動的にクリーンアップされます\n* **デフォルトブランチ**から`integration`への継続的デプロイ\n* **タグ**から`staging`への継続的デプロイ\n\nさらにレイヤを追加し、今回は手動でのトリガーをもとに`qa`と`production`環境にデプロイされるようにしましょう。\n\n##### qaとproduction環境への継続的デプロイ\n\n誰もが本番環境に継続的デプロイしたいわけではないため、次の2つのデプロイには手動による検証を追加します。単に**CD**の観点で考えた場合、このトリガーを追加することはありませんが、ほかのトリガーからジョブを実行する方法を学ぶ機会として捉えてください。\n\nこれまでデプロイを実行する際は、必ず[メインパイプライン](#the-main-pipeline)から[子パイプライン](#the-child-pipeline)を開始してきました。\n\nデフォルトブランチとタグからさらにデプロイを実行したいため、これらの追加ステップ用に別のレイヤを追加します。新たな手順は必要ありません。[メインパイプライン](#the-main-pipeline)で行ったのとまったく同じプロセスを再度繰り返します。この方法だと、必要な数だけレイヤを操作できます。中には最大で9つの環境がある例も見たことがあります。環境の数を抑えることの利点についてはあらためて説明しませんが、このプロセスを使用することで、初期段階から最終的なデリバリーまで、同じパイプラインを非常に簡単に実装できます。その上、パイプラインの定義をシンプルに保ちつつ、コストをかけずに維持できる小さな塊に分割可能です。\n\nここでは変数の競合を防ぐために、新しいvar名を使用してTerraformの状態と入力ファイルを識別しています。\n\n```yml\n.2nd_layer:\n  stage: 2nd_layer\n  variables:\n    TF_ROOT: terraform\n  trigger:\n    include: .gitlab-ci/.second-layer.gitlab-ci.yml\n    # strategy: depend            # Do NOT wait for the downstream pipeline to finish to mark upstream pipeline as successful. Otherwise, all pipelines will fail when reaching the pipeline timeout before deployment to 2nd layer.\n    forward:\n      yaml_variables: true      # Forward variables defined in the trigger job\n      pipeline_variables: true  # Forward manual pipeline variables and scheduled pipeline variables\n\nqa:\n  extends: .2nd_layer\n  variables:\n    TF_STATE_NAME_2: qa\n    environment: $TF_STATE_NAME_2\n    TF_CLI_ARGS_plan_2: \"-var-file=../vars/$TF_STATE_NAME_2.tfvars\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n\nproduction:\n  extends: .2nd_layer\n  variables:\n    TF_STATE_NAME_2: production\n    environment: $TF_STATE_NAME_2\n    TF_CLI_ARGS_plan_2: \"-var-file=../vars/$TF_STATE_NAME_2.tfvars\"\n  rules:\n    - if: $CI_COMMIT_TAG\n```\n\n**ここで重要なテクニックは、新しいダウンストリームパイプラインに使用するstrategyの設定です。**`trigger:strategy`はデフォルトの値のままにしておきます。そうしなければ、[メインパイプライン](#the-main-pipeline)は、[孫パイプライン](#the-grand-child-pipeline)が完了するまで待機することになります。手動トリガーだと、非常に長い時間かかり、パイプラインダッシュボードが読みづらく、理解しにくくなる可能性があります。\n\nここでインクルードした`.gitlab-ci/.second-layer.gitlab-ci.yml`ファイルが何なのか疑問に感じた方もいらっしゃるかもしれません。こちらは次のセクションで説明します。\n\n##### 1つ目のレイヤのパイプラインに関する全定義\n\n1つ目のレイヤの全詳細（`.gitlab-ci/.first-layer.gitlab-ci.yml`に保存）を確認したい場合は、以下のセクションを参照してください。\n\n```yml\nvariables:\n  TF_VAR_aws_ami_id: $AWS_AMI_ID\n  TF_VAR_aws_instance_type: $AWS_INSTANCE_TYPE\n  TF_VAR_aws_default_region: $AWS_DEFAULT_REGION\n\ninclude:\n  - template: Terraform.gitlab-ci.yml\n\ndefault:\n  cache:  # Use a shared cache or tagged runners to ensure terraform can run on apply and destroy\n    - key: cache-$CI_COMMIT_REF_SLUG\n      fallback_keys:\n        - cache-$CI_DEFAULT_BRANCH\n      paths:\n        - .\n\nstages:\n  - validate\n  - test\n  - build\n  - deploy\n  - cleanup\n  - 2nd_layer       # Use to deploy a 2nd environment on both the main branch and on the tags\n\nfmt:\n  rules:\n    - when: always\n\nvalidate:\n  rules:\n    - when: always\n\nkics-iac-sast:\n  rules:\n    - if: $SAST_DISABLED == 'true' || $SAST_DISABLED == '1'\n      when: never\n    - if: $SAST_EXCLUDED_ANALYZERS =~ /kics/\n      when: never\n    - when: on_success\n\niac-sast:\n  rules:\n    - if: $SAST_DISABLED == 'true' || $SAST_DISABLED == '1'\n      when: never\n    - if: $SAST_EXCLUDED_ANALYZERS =~ /kics/\n      when: never\n    - when: on_success\n\n###########################################################################################################\n## Integration env. and Staging. env\n##  * Auto-deploy to Integration on merge to main.\n##  * Auto-deploy to Staging on tag.\n##  * Integration can be manually destroyed if TF_DESTROY is set to true.\n##  * Destroy of next env. is not automated to prevent errors.\n###########################################################################################################\nbuild:  # terraform plan\n  environment:\n    name: $TF_STATE_NAME\n    action: prepare\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n    - if: $CI_COMMIT_TAG\n\ndeploy: # terraform apply --> automatically deploy on corresponding env (integration or staging) when merging to default branch or tagging. Second layer environments (qa and production) will be controlled manually\n  environment: \n    name: $TF_STATE_NAME\n    action: start\n    on_stop: destroy\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n    - if: $CI_COMMIT_TAG\n\ndestroy:\n  extends: .terraform:destroy\n  variables:\n    GIT_STRATEGY: none\n  dependencies:\n    - build\n  environment:\n    name: $TF_STATE_NAME\n    action: stop\n  rules:\n    - if: $CI_COMMIT_TAG  # Do not destroy production\n      when: never\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $TF_DESTROY == \"true\" # Manually destroy integration env.\n      when: manual\n###########################################################################################################\n\n###########################################################################################################\n## Dev env.\n##  * Temporary environment. Lives and dies with the Merge Request.\n##  * Auto-deploy on push to feature branch.\n##  * Auto-destroy on when Merge Request is closed.\n###########################################################################################################\nbuild_review:\n  extends: build\n  rules:\n    - if: $CI_COMMIT_TAG\n      when: never\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: on_success\n\ndeploy_review:\n  extends: deploy\n  dependencies:\n    - build_review\n  environment:\n    name: $environment\n    action: start\n    on_stop: destroy_review\n    # url: https://$CI_ENVIRONMENT_SLUG.example.com\n  rules:\n    - if: $CI_COMMIT_TAG\n      when: never\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: on_success\n\ndestroy_review:\n  extends: destroy\n  dependencies:\n    - build_review\n  environment:\n    name: $environment\n    action: stop\n  rules:\n    - if: $CI_COMMIT_TAG  # Do not destroy production\n      when: never\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH   # Do not destroy staging\n      when: never\n    - when: manual\n###########################################################################################################\n\n###########################################################################################################\n## Second layer\n##  * Deploys from main branch to qa env.\n##  * Deploys from tag to production.\n###########################################################################################################\n.2nd_layer:\n  stage: 2nd_layer\n  variables:\n    TF_ROOT: terraform\n  trigger:\n    include: .gitlab-ci/.second-layer.gitlab-ci.yml\n    # strategy: depend            # Do NOT wait for the downstream pipeline to finish to mark upstream pipeline as successful. Otherwise, all pipelines will fail when reaching the pipeline timeout before deployment to 2nd layer.\n    forward:\n      yaml_variables: true      # Forward variables defined in the trigger job\n      pipeline_variables: true  # Forward manual pipeline variables and scheduled pipeline variables\n\nqa:\n  extends: .2nd_layer\n  variables:\n    TF_STATE_NAME_2: qa\n    environment: $TF_STATE_NAME_2\n    TF_CLI_ARGS_plan_2: \"-var-file=../vars/$TF_STATE_NAME_2.tfvars\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n\nproduction:\n  extends: .2nd_layer\n  variables:\n    TF_STATE_NAME_2: production\n    environment: $TF_STATE_NAME_2\n    TF_CLI_ARGS_plan_2: \"-var-file=../vars/$TF_STATE_NAME_2.tfvars\"\n  rules:\n    - if: $CI_COMMIT_TAG\n###########################################################################################################\n```\n\nこの段階で、すでに3つの環境に問題なくデプロイしています。個人的にはこのアプローチが理想的でおすすめです。ただし、もっと多くの環境が必要であれば、CDパイプラインに追加してください。\n\n`trigger:include`というキーワードでダウンストリームパイプラインをインクルードしていることはすでにお気づきだと思います。これにより、`.gitlab-ci/.second-layer.gitlab-ci.yml`ファイルがインクルードされます。ほぼ同じパイプラインを実行したいため、当然ながら先ほど詳しく説明したものと内容は非常に似ています。ここで[孫パイプライン](#the-grand-child-pipeline)を定義する主な利点は、それ自体が独立しているため、変数やルールを非常に定義しやすくことです。\n\n### 孫パイプライン\n\nこの2つ目のレイヤとなるパイプラインは、まったく新しいパイプラインです。そのため、1つ目のレイヤの定義を模倣しつつ、以下を行う必要があります。\n\n* [Terraformテンプレートのインクルード](#run-terraform-commands-and-secure-the-code)。\n* [セキュリティチェックの実施](#run-controls-on-all-branches)。Terraformの検証は1つ目のレイヤと重複するものの、セキュリティスキャナーにより以前にスキャナーが実行されたときにはまだ存在していなかった脅威を見つけられる可能性があります（stagingへのデプロイの数日後にproductionへのデプロイを行う場合など）。\n* [buildとdeployジョブを上書きして特定のルールを設定](#cd-to-review-environments)。早すぎる削除を防ぐために、`destroy`ステージは自動化されないようになったことにご注意ください。\n\n上述のとおり、`TF_STATE_NAME`と`TF_CLI_ARGS_plan`は、[メインパイプライン](#the-main-pipeline)から[子パイプライン](#the-child-pipeline)に渡されています。これらの値を[子パイプライン](#the-child-pipeline)から[孫パイプライン](#the-grand-child-pipeline)に渡すには、別の変数名が必要でした。そのため、子パイプラインでは変数名の末尾に`_2`を付け足し、`before_script`の実行中に適切な変数に値をコピーしています。\n\n各ステップについては説明済みであるため、ここでは細かいところは省き、直接グローバルな2つ目のレイヤの定義（`.gitlab-ci/.second-layer.gitlab-ci.yml`に保存）の全体像をご確認ください。\n\n```yml\n# Use to deploy a second environment on both the default branch and the tags.\n\ninclude:\n  template: Terraform.gitlab-ci.yml\n\nstages:\n  - validate\n  - test\n  - build\n  - deploy\n\nfmt:\n  rules:\n    - when: never\n\nvalidate:\n  rules:\n    - when: never\n\nkics-iac-sast:\n  rules:\n    - if: $SAST_DISABLED == 'true' || $SAST_DISABLED == '1'\n      when: never\n    - if: $SAST_EXCLUDED_ANALYZERS =~ /kics/\n      when: never\n    - when: always\n\n###########################################################################################################\n## QA env. and Prod. env\n##  * Manually trigger build and auto-deploy in QA\n##  * Manually trigger both build and deploy in Production\n##  * Destroy of these env. is not automated to prevent errors.\n###########################################################################################################\nbuild:  # terraform plan\n  cache:  # Use a shared cache or tagged runners to ensure terraform can run on apply and destroy\n    - key: $TF_STATE_NAME_2\n      fallback_keys:\n        - cache-$CI_DEFAULT_BRANCH\n      paths:\n        - .\n  environment:\n    name: $TF_STATE_NAME_2\n    action: prepare\n  before_script:  # Hack to set new variable values on the second layer, while still using the same variable names. Otherwise, due to variable precedence order, setting new value in the trigger job, does not cascade these new values to the downstream pipeline\n    - TF_STATE_NAME=$TF_STATE_NAME_2\n    - TF_CLI_ARGS_plan=$TF_CLI_ARGS_plan_2\n  rules:\n    - when: manual\n\ndeploy: # terraform apply\n  cache:  # Use a shared cache or tagged runners to ensure terraform can run on apply and destroy\n    - key: $TF_STATE_NAME_2\n      fallback_keys:\n        - cache-$CI_DEFAULT_BRANCH\n      paths:\n        - .\n  environment: \n    name: $TF_STATE_NAME_2\n    action: start\n  before_script:  # Hack to set new variable values on the second layer, while still using the same variable names. Otherwise, due to variable precedence order, setting new value in the trigger job, does not cascade these new values to the downstream pipeline\n    - TF_STATE_NAME=$TF_STATE_NAME_2\n    - TF_CLI_ARGS_plan=$TF_CLI_ARGS_plan_2\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n    - if: $CI_COMMIT_TAG && $TF_AUTO_DEPLOY == \"true\"\n    - if: $CI_COMMIT_TAG\n      when: manual\n###########################################################################################################\n```\n\nこれで**準備完了です。** 本番環境にデプロイする前に、ジョブの実行を管理する方法は自由に変更できます。たとえば、GitLabの機能を活用して、本番環境へのデプロイ前に[ジョブを遅延させる](https://docs.gitlab.com/ee/ci/jobs/job_control.html#run-a-job-after-a-delay)設定をすることも可能です。\n\n## 実際に試す\n\nついに目標を達成できました。**フィーチャーブランチ**、**mainブランチ**、**タグ**だけで、**5つの異なる環境へのデプロイ**を管理できるようになりました。\n* パイプラインの効率とセキュリティを確保するために、GitLabのオープンソーステンプレートを集中的に再利用しました。\n* GitLabテンプレートの機能を活用して、個別に制御が必要なブロックだけを上書きしました。\n* パイプラインを小さな塊に分割し、ニーズに完全に合うようにダウンストリームパイプラインを制御しました。\n\nここからは、自由に進めてください。たとえば、[trigger:rules:changes](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges)キーワードを使って、ソフトウェアのソースコードのダウンストリームパイプラインをトリガーするように、メインパイプラインを簡単に更新することも可能です。また、発生した変更に応じて、別の[テンプレート](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/)を使用できます。その方法はまた別の機会に。","engineering",[23,24,25,26,27],"CI/CD","CI","CD","DevSecOps platform","tutorial","2025-06-12",{"slug":30,"featured":6,"template":31},"using-child-pipelines-to-continuously-deploy-to-five-environments","BlogPost","content:ja-jp:blog:using-child-pipelines-to-continuously-deploy-to-five-environments.yml","yaml","Using Child Pipelines To Continuously Deploy To Five Environments","content","ja-jp/blog/using-child-pipelines-to-continuously-deploy-to-five-environments.yml","ja-jp/blog/using-child-pipelines-to-continuously-deploy-to-five-environments","yml",{"_path":40,"_dir":41,"_draft":6,"_partial":6,"_locale":7,"data":42,"_id":450,"_type":33,"title":451,"_source":35,"_file":452,"_stem":453,"_extension":38},"/shared/ja-jp/main-navigation","ja-jp",{"logo":43,"freeTrial":48,"sales":53,"login":58,"items":63,"search":394,"minimal":428,"duo":441},{"config":44},{"href":45,"dataGaName":46,"dataGaLocation":47},"/ja-jp/","gitlab logo","header",{"text":49,"config":50},"無料トライアルを開始",{"href":51,"dataGaName":52,"dataGaLocation":47},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":54,"config":55},"お問い合わせ",{"href":56,"dataGaName":57,"dataGaLocation":47},"/ja-jp/sales/","sales",{"text":59,"config":60},"サインイン",{"href":61,"dataGaName":62,"dataGaLocation":47},"https://gitlab.com/users/sign_in/","sign in",[64,108,206,211,316,376],{"text":65,"config":66,"cards":68,"footer":91},"プラットフォーム",{"dataNavLevelOne":67},"platform",[69,75,83],{"title":65,"description":70,"link":71},"最も包括的かつAIで強化されたDevSecOpsプラットフォーム",{"text":72,"config":73},"プラットフォームを詳しく見る",{"href":74,"dataGaName":67,"dataGaLocation":47},"/ja-jp/platform/",{"title":76,"description":77,"link":78},"GitLab Duo（AI）","開発のすべてのステージでAIを活用し、ソフトウェアをより迅速にビルド",{"text":79,"config":80},"GitLab Duoのご紹介",{"href":81,"dataGaName":82,"dataGaLocation":47},"/ja-jp/gitlab-duo/","gitlab duo ai",{"title":84,"description":85,"link":86},"GitLabが選ばれる理由","GitLabが大企業に選ばれる理由10選",{"text":87,"config":88},"詳細はこちら",{"href":89,"dataGaName":90,"dataGaLocation":47},"/ja-jp/why-gitlab/","why gitlab",{"title":92,"items":93},"利用を開始：",[94,99,104],{"text":95,"config":96},"プラットフォームエンジニアリング",{"href":97,"dataGaName":98,"dataGaLocation":47},"/ja-jp/solutions/platform-engineering/","platform engineering",{"text":100,"config":101},"開発者の経験",{"href":102,"dataGaName":103,"dataGaLocation":47},"/ja-jp/developer-experience/","Developer experience",{"text":105,"config":106},"MLOps",{"href":107,"dataGaName":105,"dataGaLocation":47},"/ja-jp/topics/devops/the-role-of-ai-in-devops/",{"text":109,"left":110,"config":111,"link":113,"lists":117,"footer":188},"製品",true,{"dataNavLevelOne":112},"solutions",{"text":114,"config":115},"すべてのソリューションを表示",{"href":116,"dataGaName":112,"dataGaLocation":47},"/ja-jp/solutions/",[118,143,166],{"title":119,"description":120,"link":121,"items":126},"自動化","CI/CDと自動化でデプロイを加速",{"config":122},{"icon":123,"href":124,"dataGaName":125,"dataGaLocation":47},"AutomatedCodeAlt","/ja-jp/solutions/delivery-automation/","automated software delivery",[127,130,134,139],{"text":23,"config":128},{"href":129,"dataGaLocation":47,"dataGaName":23},"/ja-jp/solutions/continuous-integration/",{"text":131,"config":132},"AIアシストによる開発",{"href":81,"dataGaLocation":47,"dataGaName":133},"AI assisted development",{"text":135,"config":136},"ソースコード管理",{"href":137,"dataGaLocation":47,"dataGaName":138},"/ja-jp/solutions/source-code-management/","Source Code Management",{"text":140,"config":141},"自動化されたソフトウェアデリバリー",{"href":124,"dataGaLocation":47,"dataGaName":142},"Automated software delivery",{"title":144,"description":145,"link":146,"items":151},"セキュリティ","セキュリティを損なうことなくコードをより迅速に完成",{"config":147},{"href":148,"dataGaName":149,"dataGaLocation":47,"icon":150},"/ja-jp/solutions/security-compliance/","security and compliance","ShieldCheckLight",[152,156,161],{"text":153,"config":154},"セキュリティとコンプライアンス",{"href":148,"dataGaLocation":47,"dataGaName":155},"Security & Compliance",{"text":157,"config":158},"ソフトウェアサプライチェーンの安全性",{"href":159,"dataGaLocation":47,"dataGaName":160},"/ja-jp/solutions/supply-chain/","Software supply chain security",{"text":162,"config":163},"コンプライアンスとガバナンス",{"href":164,"dataGaLocation":47,"dataGaName":165},"/ja-jp/solutions/continuous-software-compliance/","Compliance and governance",{"title":167,"link":168,"items":173},"測定",{"config":169},{"icon":170,"href":171,"dataGaName":172,"dataGaLocation":47},"DigitalTransformation","/ja-jp/solutions/visibility-measurement/","visibility and measurement",[174,178,183],{"text":175,"config":176},"可視性と測定",{"href":171,"dataGaLocation":47,"dataGaName":177},"Visibility and Measurement",{"text":179,"config":180},"バリューストリーム管理",{"href":181,"dataGaLocation":47,"dataGaName":182},"/ja-jp/solutions/value-stream-management/","Value Stream Management",{"text":184,"config":185},"分析とインサイト",{"href":186,"dataGaLocation":47,"dataGaName":187},"/ja-jp/solutions/analytics-and-insights/","Analytics and insights",{"title":189,"items":190},"GitLabが活躍する場所",[191,196,201],{"text":192,"config":193},"Enterprise",{"href":194,"dataGaLocation":47,"dataGaName":195},"/ja-jp/enterprise/","enterprise",{"text":197,"config":198},"スモールビジネス",{"href":199,"dataGaLocation":47,"dataGaName":200},"/ja-jp/small-business/","small business",{"text":202,"config":203},"公共機関",{"href":204,"dataGaLocation":47,"dataGaName":205},"/ja-jp/solutions/public-sector/","public sector",{"text":207,"config":208},"価格",{"href":209,"dataGaName":210,"dataGaLocation":47,"dataNavLevelOne":210},"/ja-jp/pricing/","pricing",{"text":212,"config":213,"link":215,"lists":219,"feature":303},"関連リソース",{"dataNavLevelOne":214},"resources",{"text":216,"config":217},"すべてのリソースを表示",{"href":218,"dataGaName":214,"dataGaLocation":47},"/ja-jp/resources/",[220,253,275],{"title":221,"items":222},"はじめに",[223,228,233,238,243,248],{"text":224,"config":225},"インストール",{"href":226,"dataGaName":227,"dataGaLocation":47},"/ja-jp/install/","install",{"text":229,"config":230},"クイックスタートガイド",{"href":231,"dataGaName":232,"dataGaLocation":47},"/ja-jp/get-started/","quick setup checklists",{"text":234,"config":235},"学ぶ",{"href":236,"dataGaLocation":47,"dataGaName":237},"https://university.gitlab.com/","learn",{"text":239,"config":240},"製品ドキュメント",{"href":241,"dataGaName":242,"dataGaLocation":47},"https://docs.gitlab.com/","product documentation",{"text":244,"config":245},"ベストプラクティスビデオ",{"href":246,"dataGaName":247,"dataGaLocation":47},"/ja-jp/getting-started-videos/","best practice videos",{"text":249,"config":250},"インテグレーション",{"href":251,"dataGaName":252,"dataGaLocation":47},"/ja-jp/integrations/","integrations",{"title":254,"items":255},"検索する",[256,261,265,270],{"text":257,"config":258},"お客様成功事例",{"href":259,"dataGaName":260,"dataGaLocation":47},"/ja-jp/customers/","customer success stories",{"text":262,"config":263},"ブログ",{"href":264,"dataGaName":5,"dataGaLocation":47},"/ja-jp/blog/",{"text":266,"config":267},"リモート",{"href":268,"dataGaName":269,"dataGaLocation":47},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":271,"config":272},"TeamOps",{"href":273,"dataGaName":274,"dataGaLocation":47},"/ja-jp/teamops/","teamops",{"title":276,"items":277},"つなげる",[278,283,288,293,298],{"text":279,"config":280},"GitLabサービス",{"href":281,"dataGaName":282,"dataGaLocation":47},"/ja-jp/services/","services",{"text":284,"config":285},"コミュニティ",{"href":286,"dataGaName":287,"dataGaLocation":47},"/community/","community",{"text":289,"config":290},"フォーラム",{"href":291,"dataGaName":292,"dataGaLocation":47},"https://forum.gitlab.com/","forum",{"text":294,"config":295},"イベント",{"href":296,"dataGaName":297,"dataGaLocation":47},"/events/","events",{"text":299,"config":300},"パートナー",{"href":301,"dataGaName":302,"dataGaLocation":47},"/ja-jp/partners/","partners",{"backgroundColor":304,"textColor":305,"text":306,"image":307,"link":311},"#2f2a6b","#fff","ソフトウェア開発の未来への洞察",{"altText":308,"config":309},"ソースプロモカード",{"src":310},"/images/navigation/the-source-promo-card.svg",{"text":312,"config":313},"最新情報を読む",{"href":314,"dataGaName":315,"dataGaLocation":47},"/ja-jp/the-source/","the source",{"text":317,"config":318,"lists":320},"Company",{"dataNavLevelOne":319},"company",[321],{"items":322},[323,328,334,336,341,346,351,356,361,366,371],{"text":324,"config":325},"GitLabについて",{"href":326,"dataGaName":327,"dataGaLocation":47},"/ja-jp/company/","about",{"text":329,"config":330,"footerGa":333},"採用情報",{"href":331,"dataGaName":332,"dataGaLocation":47},"/jobs/","jobs",{"dataGaName":332},{"text":294,"config":335},{"href":296,"dataGaName":297,"dataGaLocation":47},{"text":337,"config":338},"経営陣",{"href":339,"dataGaName":340,"dataGaLocation":47},"/company/team/e-group/","leadership",{"text":342,"config":343},"チーム",{"href":344,"dataGaName":345,"dataGaLocation":47},"/company/team/","team",{"text":347,"config":348},"ハンドブック",{"href":349,"dataGaName":350,"dataGaLocation":47},"https://handbook.gitlab.com/","handbook",{"text":352,"config":353},"投資家向け情報",{"href":354,"dataGaName":355,"dataGaLocation":47},"https://ir.gitlab.com/","investor relations",{"text":357,"config":358},"トラストセンター",{"href":359,"dataGaName":360,"dataGaLocation":47},"/ja-jp/security/","trust center",{"text":362,"config":363},"AI Transparency Center",{"href":364,"dataGaName":365,"dataGaLocation":47},"/ja-jp/ai-transparency-center/","ai transparency center",{"text":367,"config":368},"ニュースレター",{"href":369,"dataGaName":370,"dataGaLocation":47},"/company/contact/","newsletter",{"text":372,"config":373},"プレス",{"href":374,"dataGaName":375,"dataGaLocation":47},"/press/","press",{"text":54,"config":377,"lists":378},{"dataNavLevelOne":319},[379],{"items":380},[381,384,389],{"text":54,"config":382},{"href":56,"dataGaName":383,"dataGaLocation":47},"talk to sales",{"text":385,"config":386},"サポートを受ける",{"href":387,"dataGaName":388,"dataGaLocation":47},"/support/","get help",{"text":390,"config":391},"カスタマーポータル",{"href":392,"dataGaName":393,"dataGaLocation":47},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":395,"login":396,"suggestions":403},"閉じる",{"text":397,"link":398},"リポジトリとプロジェクトを検索するには、次にログインします",{"text":399,"config":400},"GitLab.com",{"href":61,"dataGaName":401,"dataGaLocation":402},"search login","search",{"text":404,"default":405},"提案",[406,409,414,416,420,424],{"text":76,"config":407},{"href":81,"dataGaName":408,"dataGaLocation":402},"GitLab Duo (AI)",{"text":410,"config":411},"コード提案（AI）",{"href":412,"dataGaName":413,"dataGaLocation":402},"/ja-jp/solutions/code-suggestions/","Code Suggestions (AI)",{"text":23,"config":415},{"href":129,"dataGaName":23,"dataGaLocation":402},{"text":417,"config":418},"GitLab on AWS",{"href":419,"dataGaName":417,"dataGaLocation":402},"/ja-jp/partners/technology-partners/aws/",{"text":421,"config":422},"GitLab on Google Cloud",{"href":423,"dataGaName":421,"dataGaLocation":402},"/ja-jp/partners/technology-partners/google-cloud-platform/",{"text":425,"config":426},"GitLabを選ぶ理由",{"href":89,"dataGaName":427,"dataGaLocation":402},"Why GitLab?",{"freeTrial":429,"mobileIcon":433,"desktopIcon":438},{"text":49,"config":430},{"href":431,"dataGaName":52,"dataGaLocation":432},"https://gitlab.com/-/trials/new/","nav",{"altText":434,"config":435},"GitLabアイコン",{"src":436,"dataGaName":437,"dataGaLocation":432},"/images/brand/gitlab-logo-tanuki.svg","gitlab icon",{"altText":434,"config":439},{"src":440,"dataGaName":437,"dataGaLocation":432},"/images/brand/gitlab-logo-type.svg",{"freeTrial":442,"mobileIcon":446,"desktopIcon":448},{"text":443,"config":444},"GitLab Duoの詳細について",{"href":81,"dataGaName":445,"dataGaLocation":432},"gitlab duo",{"altText":434,"config":447},{"src":436,"dataGaName":437,"dataGaLocation":432},{"altText":434,"config":449},{"src":440,"dataGaName":437,"dataGaLocation":432},"content:shared:ja-jp:main-navigation.yml","Main Navigation","shared/ja-jp/main-navigation.yml","shared/ja-jp/main-navigation",{"_path":455,"_dir":41,"_draft":6,"_partial":6,"_locale":7,"title":456,"button":457,"config":462,"_id":464,"_type":33,"_source":35,"_file":465,"_stem":466,"_extension":38},"/shared/ja-jp/banner","GitLab Duo Agent Platformがパブリックベータ版で利用可能に！",{"text":458,"config":459},"詳しく見る",{"href":460,"dataGaName":461,"dataGaLocation":47},"/gitlab-duo/agent-platform/","duo banner",{"layout":463},"release","content:shared:ja-jp:banner.yml","shared/ja-jp/banner.yml","shared/ja-jp/banner",{"_path":468,"_dir":41,"_draft":6,"_partial":6,"_locale":7,"data":469,"_id":673,"_type":33,"title":674,"_source":35,"_file":675,"_stem":676,"_extension":38},"/shared/ja-jp/main-footer",{"text":470,"source":471,"edit":477,"contribute":482,"config":487,"items":492,"minimal":665},"GitはSoftware Freedom Conservancyの商標です。当社は「GitLab」をライセンスに基づいて使用しています",{"text":472,"config":473},"ページのソースを表示",{"href":474,"dataGaName":475,"dataGaLocation":476},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":478,"config":479},"このページを編集",{"href":480,"dataGaName":481,"dataGaLocation":476},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":483,"config":484},"ご協力をお願いします",{"href":485,"dataGaName":486,"dataGaLocation":476},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":488,"facebook":489,"youtube":490,"linkedin":491},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[493,516,570,603,637],{"title":65,"links":494,"subMenu":499},[495],{"text":496,"config":497},"DevSecOpsプラットフォーム",{"href":74,"dataGaName":498,"dataGaLocation":476},"devsecops platform",[500],{"title":207,"links":501},[502,506,511],{"text":503,"config":504},"プランの表示",{"href":209,"dataGaName":505,"dataGaLocation":476},"view plans",{"text":507,"config":508},"Premiumを選ぶ理由",{"href":509,"dataGaName":510,"dataGaLocation":476},"/ja-jp/pricing/premium/","why premium",{"text":512,"config":513},"Ultimateを選ぶ理由",{"href":514,"dataGaName":515,"dataGaLocation":476},"/ja-jp/pricing/ultimate/","why ultimate",{"title":517,"links":518},"ソリューション",[519,524,527,529,534,539,543,546,549,554,556,558,560,565],{"text":520,"config":521},"デジタルトランスフォーメーション",{"href":522,"dataGaName":523,"dataGaLocation":476},"/ja-jp/topics/digital-transformation/","digital transformation",{"text":153,"config":525},{"href":148,"dataGaName":526,"dataGaLocation":476},"security & compliance",{"text":140,"config":528},{"href":124,"dataGaName":125,"dataGaLocation":476},{"text":530,"config":531},"アジャイル開発",{"href":532,"dataGaName":533,"dataGaLocation":476},"/ja-jp/solutions/agile-delivery/","agile delivery",{"text":535,"config":536},"クラウドトランスフォーメーション",{"href":537,"dataGaName":538,"dataGaLocation":476},"/ja-jp/topics/cloud-native/","cloud transformation",{"text":540,"config":541},"SCM",{"href":137,"dataGaName":542,"dataGaLocation":476},"source code management",{"text":23,"config":544},{"href":129,"dataGaName":545,"dataGaLocation":476},"continuous integration & delivery",{"text":179,"config":547},{"href":181,"dataGaName":548,"dataGaLocation":476},"value stream management",{"text":550,"config":551},"GitOps",{"href":552,"dataGaName":553,"dataGaLocation":476},"/ja-jp/solutions/gitops/","gitops",{"text":192,"config":555},{"href":194,"dataGaName":195,"dataGaLocation":476},{"text":197,"config":557},{"href":199,"dataGaName":200,"dataGaLocation":476},{"text":202,"config":559},{"href":204,"dataGaName":205,"dataGaLocation":476},{"text":561,"config":562},"教育",{"href":563,"dataGaName":564,"dataGaLocation":476},"/ja-jp/solutions/education/","education",{"text":566,"config":567},"金融サービス",{"href":568,"dataGaName":569,"dataGaLocation":476},"/ja-jp/solutions/finance/","financial services",{"title":212,"links":571},[572,574,576,578,581,583,587,589,591,593,595,597,599,601],{"text":224,"config":573},{"href":226,"dataGaName":227,"dataGaLocation":476},{"text":229,"config":575},{"href":231,"dataGaName":232,"dataGaLocation":476},{"text":234,"config":577},{"href":236,"dataGaName":237,"dataGaLocation":476},{"text":239,"config":579},{"href":241,"dataGaName":580,"dataGaLocation":476},"docs",{"text":262,"config":582},{"href":264,"dataGaName":5},{"text":584,"config":585},"お客様の成功事例",{"href":586,"dataGaLocation":476},"/customers/",{"text":257,"config":588},{"href":259,"dataGaName":260,"dataGaLocation":476},{"text":266,"config":590},{"href":268,"dataGaName":269,"dataGaLocation":476},{"text":279,"config":592},{"href":281,"dataGaName":282,"dataGaLocation":476},{"text":271,"config":594},{"href":273,"dataGaName":274,"dataGaLocation":476},{"text":284,"config":596},{"href":286,"dataGaName":287,"dataGaLocation":476},{"text":289,"config":598},{"href":291,"dataGaName":292,"dataGaLocation":476},{"text":294,"config":600},{"href":296,"dataGaName":297,"dataGaLocation":476},{"text":299,"config":602},{"href":301,"dataGaName":302,"dataGaLocation":476},{"title":317,"links":604},[605,607,609,611,613,615,617,621,626,628,630,632],{"text":324,"config":606},{"href":326,"dataGaName":319,"dataGaLocation":476},{"text":329,"config":608},{"href":331,"dataGaName":332,"dataGaLocation":476},{"text":337,"config":610},{"href":339,"dataGaName":340,"dataGaLocation":476},{"text":342,"config":612},{"href":344,"dataGaName":345,"dataGaLocation":476},{"text":347,"config":614},{"href":349,"dataGaName":350,"dataGaLocation":476},{"text":352,"config":616},{"href":354,"dataGaName":355,"dataGaLocation":476},{"text":618,"config":619},"Sustainability",{"href":620,"dataGaName":618,"dataGaLocation":476},"/sustainability/",{"text":622,"config":623},"ダイバーシティ、インクルージョン、ビロンギング（DIB）",{"href":624,"dataGaName":625,"dataGaLocation":476},"/ja-jp/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":357,"config":627},{"href":359,"dataGaName":360,"dataGaLocation":476},{"text":367,"config":629},{"href":369,"dataGaName":370,"dataGaLocation":476},{"text":372,"config":631},{"href":374,"dataGaName":375,"dataGaLocation":476},{"text":633,"config":634},"現代奴隷制の透明性に関する声明",{"href":635,"dataGaName":636,"dataGaLocation":476},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":54,"links":638},[639,641,643,645,650,655,660],{"text":54,"config":640},{"href":56,"dataGaName":57,"dataGaLocation":476},{"text":385,"config":642},{"href":387,"dataGaName":388,"dataGaLocation":476},{"text":390,"config":644},{"href":392,"dataGaName":393,"dataGaLocation":476},{"text":646,"config":647},"ステータス",{"href":648,"dataGaName":649,"dataGaLocation":476},"https://status.gitlab.com/","status",{"text":651,"config":652},"利用規約",{"href":653,"dataGaName":654,"dataGaLocation":476},"/terms/","terms of use",{"text":656,"config":657},"プライバシーに関する声明",{"href":658,"dataGaName":659,"dataGaLocation":476},"/ja-jp/privacy/","privacy statement",{"text":661,"config":662},"Cookieの設定",{"dataGaName":663,"dataGaLocation":476,"id":664,"isOneTrustButton":110},"cookie preferences","ot-sdk-btn",{"items":666},[667,669,671],{"text":651,"config":668},{"href":653,"dataGaName":654,"dataGaLocation":476},{"text":656,"config":670},{"href":658,"dataGaName":659,"dataGaLocation":476},{"text":661,"config":672},{"dataGaName":663,"dataGaLocation":476,"id":664,"isOneTrustButton":110},"content:shared:ja-jp:main-footer.yml","Main Footer","shared/ja-jp/main-footer.yml","shared/ja-jp/main-footer",[678],{"_path":679,"_dir":680,"_draft":6,"_partial":6,"_locale":7,"content":681,"config":685,"_id":687,"_type":33,"title":688,"_source":35,"_file":689,"_stem":690,"_extension":38},"/en-us/blog/authors/olivier-dupr","authors",{"name":18,"config":682},{"headshot":683,"ctfId":684},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1750713474/cj6odchlpoqxbibenvye.png","4VIckvQsyfNxEtz4pM42aP",{"template":686},"BlogAuthor","content:en-us:blog:authors:olivier-dupr.yml","Olivier Dupr","en-us/blog/authors/olivier-dupr.yml","en-us/blog/authors/olivier-dupr",{"_path":692,"_dir":41,"_draft":6,"_partial":6,"_locale":7,"header":693,"eyebrow":694,"blurb":695,"button":696,"secondaryButton":700,"_id":702,"_type":33,"title":703,"_source":35,"_file":704,"_stem":705,"_extension":38},"/shared/ja-jp/next-steps","より優れたソフトウェアをより速く提供","フォーチュン100企業の50%以上がGitLabを信頼","インテリジェントなDevSecOpsプラットフォームで\n\n\nチームの可能性を広げましょう。\n",{"text":49,"config":697},{"href":698,"dataGaName":52,"dataGaLocation":699},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":54,"config":701},{"href":56,"dataGaName":57,"dataGaLocation":699},"content:shared:ja-jp:next-steps.yml","Next Steps","shared/ja-jp/next-steps.yml","shared/ja-jp/next-steps",1754424546635]