Kubernetes上で簡単に開発環境を複製できる仕組みを作った話

この記事は[Rakuten Rakuma Advent Calendar 2022](https://qiita.com/advent-calendar/2022/rakuten_rakuma)の６日目の記事です。

こんにちは！！楽天ラクマ SREチームのkakepiと申します！！

今回は

**「Kubernetes上で簡単に開発環境を複製できる仕組みを作った話」**

について書いていきたいと思います

## 背景

ラクマでは日々エンジニアを採用しており、エンジニアの人数はどんどん増えています。
しかしながら**開発環境は１つしか存在せず、利用待ちの状態が発生する**という状況になっていました。
また、今までは環境はVMで構築していましたがコンテナ化を進めており、これを機に開発環境が簡単に複製できる仕組みも作りたいねという話になりました。

:::note info
なお、ラクマでは現在もエンジニアを絶賛募集中です！
[リクルートサイト](https://careers.fril.jp/)をご覧いただき、興味のある方は是非ご応募ください！
:::

## 対応策

解決する仕組みを実現にするにあたって、以下のような要件を定義しました

* エンジニアだけではなく、デザイナーも含めた**誰でも利用可能な仕組み**であること
    * 複製する方法がコマンドベースの作業であったり、一定の知識が無いと使えない仕組みとなってしまうことは避けました
* **コストを意識**して必要なときだけ立ち上げることができること
    * 無尽蔵に環境を建てられてしまうと、その分のコストがかかるので使い終わったら簡単に消すことができる仕組みも意識しました


### 開発環境複製の流れ

開発環境は以下の方法で作成できるようになっています。

#### ①開発環境が必要なエンジニアがGithub上でissueを起票する

利用したい人は環境複製用リポジトリにてissueを起票します。
また、自身がテストしたいアプリケーションが含まれたイメージをissueの説明欄を編集して記載します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299667/8d45e11b-5412-1411-09e1-98244976094c.png)


#### ②issue起票をトリガーにGitHubActions作動

issueを起票をトリガーに、同じく環境複製用リポジトリに設定しているGitHubActionsが作動します。
構築が完了すると起票したissueに作成した旨のコメントが自動的に行われます。
![スクリーンショット 2022-12-05 18.14.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299667/03522866-fb4a-330f-d4ee-a7cf8004f3ff.png)


#### ③複製した環境にはブラウザ上でヘッダーの設定をしてアクセス

どの環境も同じドメインでアクセスします。
ではどうやって特定の環境にアクセスするかというと、環境別に一意なヘッダーを付与することでアクセスできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299667/5e832eed-347d-5d01-3e92-23f705d33c0f.png)

## 具体的な仕組み

利用の流れを説明したところで具体的な仕組みを解説していきます

#### 全体像

大まかな全体像は以下です
![スクリーンショット 2022-12-05 17.24.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299667/bbff94b6-a14b-8702-6171-52c55f3df60b.png)


利用している技術スタックは以下です。各技術の詳細な説明はここでは割愛します。

- Kubernetes
- Github Actions
- ArgoCD
- Kustomize
- Istio


#### GitHubActionsは何をしているのか？

GithubActionsはざっくり以下のようなことをしています

- 環境名の決定
  - 複数の環境を一意にできるように環境名を決定します。
  - **issue番号を参照して`env-[issue番号]`といった環境名にします**
- テンプレートディレクトリをコピーして専用のディレクトリを作成
- 専用ディレクトリ内のマニフェストに定義されている環境名(`replace-env-name`)を今回決定した環境名（`env-[issue番号]`）にリネーム
- 専用ディレクトリ内のマニフェストに定義されているイメージをissueの説明欄に記載されたイメージタグに変更

**テンプレートディレクトリをコピーして専用のディレクトリを作成する工程**をさらに詳しく解説します。

manifestの実装には**kustomize**を採用しており、baseディレクトリは以下のようになっています。
中身はアプリケーションmanifestリポジトリをサブモジュールとして取り入れています。

<img width="1600" alt="スクリーンショット 2022-12-03 10.34.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299667/20669eee-8857-c972-9a61-2674536999ca.png">

次にoverlays配下ですが、ここでテンプレートディレクトリが登場します。
![スクリーンショット 2022-12-03 10.26.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299667/d5cdca19-de88-1ebb-bbaf-91cebfe18a41.png)

テンプレートディレクトリの中身はシンプルで、特定のリソースに対してpatchをあてています。
この部分は専用の環境にアクセスできる仕組みにつながるので後述します。
![スクリーンショット 2022-12-05 6.24.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299667/68f382b0-f449-e4ea-740a-cd1263844332.png)

また、テンプレートディレクトリを複製する際に、ArgoCDのapplicationリソースを定義したマニフェストも複製します。
```yaml:サンプル
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: replace-env-name
  namespace: argocd
spec:
  project: development
  source:
    repoURL: git@github.com:XXXX.git
    targetRevision: main
    path: overlays/replace-env-name
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
```

これでArgoCDは複製されたディレクトリを検知し、自動的に同期されます。

#### どうやってそれぞれの環境にアクセスできる仕組みになっているのか？

先程のoverlays配下のpatchについて解説します。
**ラクマではIstioを採用しており、VirtualServiceリソースを使用しています。**
ここでHTTPMatchRequestにheadersを指定することで各専用環境にアクセスできる仕組みとなっています。
前述のとおり、ディレクトリを複製する際に`replace-env-name`という文字列を自身の環境名（`env-[issue番号]`）にリネームしています。

```yaml:サンプル
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: XXX
spec:
  http:
    - name: main
      match:
        - name: version
          headers:
            env:
              exact: replace-env-name
          withoutHeaders:
            $patch: delete
    - name: canary
      match:
        - name: version
          headers:
            env:
              exact: replace-env-name
          withoutHeaders:
            $patch: delete
```

イメージ
![baibain-access.drawio (1).drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299667/b58af2e2-5f8b-9161-85a3-ca6d370c1635.png)


### その他の機能

上記の仕組みに加えて、以下のような機能も備えています。

#### 環境が不要になったらissueをclose、環境はその時点で削除

エンジニアが環境が不要になった場合、issueをcloseすれば環境が削除されるような仕組みとなっています。
ロジックは至ってシンプルでテンプレートディレクトリから複製したディレクトリを削除しているだけです。
ArgoCDがそれをトリガーに環境を削除します。ArgoCDってつくづく便利ですよね。

また、同じ環境を再度使いたい場合はcloseしたissueをreopenすれば良いです。

#### 毎晩issueが一括close

環境が不要になってもcloseし忘れるというシチュエーションも十分に考えられるので、毎日０時に起票されているissueは一括クローズするようにしています。

## リリース後

リリース後、早速いろいろな方が利用してくださいました。
特に、デザイナーの方が軽くデザインを確認したい場合にとても有効なようでした
また、要望/改善コーナーのようなコンテンツをWebページ上に設けて継続して改善を試みました。


## 課題

本仕組みですが、まだまだ課題は残っています。

- 環境作成が完了するまでに時間がかかる（１０分）
  - issueを起票してから環境が作成されるまでに１０分ほどかかります。エンジニアには起票したから別作業などをしてもらっていますが、ここは高速化したいところです。
- データリソース（DBやキャッシュ）も複製する
  - 現在は各開発環境で共通のデータソースを利用しています。これらもコンテナ化し、各開発環境に専用のデータソースを用意したいと思っています。

## おわりに

VMに比べてKubernetesは非常に複製が容易であるという印象を受けました。
上記のような課題はまだ残っているので解決しつつ、より使いやすい開発環境をエンジニアに提供できれば良いなと思っています！