絶対に止められない超重要サービスをGitOpsで安全に開発できるようにしている話

自己紹介と前置き

こんにちは、BIGLOBE谷山です。

ここ最近はコロナの影響もあって、2,3か月くらいほぼ出社していないので 出勤経路を忘れかけています。

現在携わっているProjectは、何十台ものサーバから構成されるRADIUSシステムを、オンプレミスからAWSに移行し、かつ一部機能はサーバレス化するというProjectです。

RADIUSシステムは240万人超の会員様が利用している認証システムなので絶対に落とせません。なので、より安全に運用していくために「GitOps」の仕組みを取り入れようと開発を進めています。

f:id:biglobe-style:20200609095205p:plain:w300
モバイルコア技術部
テクノロジー開発グループ
谷山 大介
(まだ外に出れたころに山に行った時の写真です。)

GitOpsとは

そもそもGitOpsって何って話ですよね。

コード管理でGitを使うのは一般化しているかと思いますが、GitOpsでは、 Git管理しているコードを中心としてDevOpsを回していく考え方で、私は以下のように理解しています。

  • Git上のコードを確認すれば過去を含めてAWSに構築した環境の構成がわかる状態にする。

    • Single source of truthとしてのgit。excelによる管理表からの脱却
  • gitへのアクション(push, merge, etc...)をトリガーとして、開発・運用作業をgit上で完結させることを目指す

    • 作業品質/開発環境の平準化による属人化の回避

RADIUSシステムでは以下を組み合わせることで、GitOpsの実現を目指しています。

Git repository -> GitLab
CI/CD/CT tool -> GitLab Runner
Cloud service -> AWS


DR(Disaster Recovery)サイトをGCP等別のクラウド環境に移す、といったことを考えたときに楽に移行できるように、コードの管理環境は外部に用意することにしました。

以下のようなイメージでGitOpsを行います。開発者は、AWSの環境を直接操作することはなく、GitLabへのコードのpushのみで開発を行います。.gitlab-ci.ymlで「決められた手順」を「実行する」ため、オペレーションミスなど、人的なミスが抑止できます。運用期間が長くなると、「実績のある手順」を「繰り返し使える」ので、より強固なシステムに育てられると考えています。

f:id:biglobe-style:20200609095559p:plain:w800

GitLab Runnerとは

GitLab Runnerとは、GitLab.comが提供しているCI/CDのためのツールです。 GitLabのProjectに配置した.gitlab-ci.ymlに書かれたPipelineの内容をgitlab-runnerが インストールされたサーバが自動で実行し、実行結果をGitLabのProjectに送信します。

gitlab-runnerのインストールされたサーバは、GitLab.comが提供しているものも使用可能ですが、 自分で構築したものを登録することも可能なので、AWS環境に構築したEC2インスタンスを登録しておくこともできます。IAM roleでgitlab-runnerサーバに必要な権限を付与しておけるので、使い勝手も上がります。

.gitlab-ci.ymlの定義はやらないといけないのは手間ですが、

  • 開発者が各自で手元に開発環境を用意する必要が無い

  • Pipelineを常に同じサーバで実行させることができるので、環境差分/手順差分を限りなく減らせる

ので、作業品質/開発環境の平準化による属人化の回避をすることができます。

GitLab Runner実行例

イメージが湧きやすいように、サンプルコードを用意しました。

GitLab RunnerでコンパイルするためのC言語のプログラムのサンプルです。(sample_code.c) 見ていただいた感謝の気持ちをプログラムに詰めます。

f:id:biglobe-style:20200609095820p:plain:w800

.gitlab-ci.ymlのサンプルです。 build stageでsample_code.cをコンパイルして、test stageでコンパイルされたコードを実行するPipelineを定義します。

f:id:biglobe-style:20200609095919p:plain:w800

これらのコードを、GitLabに作成したProjectのレポジトリに保存します。
.gitlab-ci.ymlが配置されたらそのまま自動でPipelineが実行されます。
実行されたPipelineの結果は、GitLabのProjectページで確認出来ます。Pipelineのページは以下のような形で表示されます。Pipelineの各Job(build, test)が成功しているのがわかります。

f:id:biglobe-style:20200609100016p:plain:w800

画面で表示されているbuildのボタンを押すと、build jobの実行結果画面が開けます。 Docker executorとして、gcc:latestのDockerコンテナが起動し、makeコマンドでsample_code.cをコンパイルしているのが確認できます。
また、jobで作成した成果物(ここでいうとsample_code.cをコンパイルしたbinary)が、artifactsに保存されているのがわかります。(明示的に設定しないと、jobの成果物は自動で次のjobに引き継がれることは無い)

f:id:biglobe-style:20200609100055p:plain:w800

test stageも確認してみましょう。
sample_codeのbinaryを実行して、実行した結果が出力されているのがわかります。 今回は簡単なプログラムだったのでそのまま実行して結果を確認していますが、例えばデータ処理のプログラムであれば、テストケースを事前に用意し、処理を実行してエラーだった場合testをfailさせることもできます。

f:id:biglobe-style:20200609145148p:plain:w800

上記のプログラムを使ったSample Projectです。

.gitlab-ci.ymlのtemplateは、GitLab.comのほうでも提供されているのでこちらも是非参考にすると良いと思います。
https://docs.gitlab.com/ee/ci/examples/#cicd-templates

Gitのbranch戦略

Git管理には、いくつか有名なbranch管理フローがあります。

  • git flow

  • gitlab flow

  • github flow

RADIUS開発チームでは、gitlab flowをベースにしたbranch戦略にすることにしました。 以下が、今回取るflowとなります。目下開発進行中なので、運用して行く中で改良して行く予定です

f:id:biglobe-style:20200609100415p:plain:w800

master,stg,devは各環境に対応するbranchで、 feature/XX,fix/XXはtopic branchです。
※masterは商用環境に対応

terraformの場合であれば、branchの種類によって以下のように実行内容を変えます。

【topic branch】

  • terraform init

  • terraform plan

以下をbranchに対応する環境で実行

  • terraform init

  • terraform plan

  • terraform apply

展開されたリソースに問題が無ければ、stgへのmergeを行い、同じくstg環境の確認をします。 これも問題なければ、masterへのmergeを行い、商用環境にリソースの展開を行います。 もしどこかのタイミングで問題が起きれば、mergeをrevertし、元に戻ったコードでのPipelineを実行することで環境も元に戻せます。 商用環境で問題なくリリースが完了したらtag打ちをして、バージョン管理できるようにします。

terraformはリソースの変更箇所によってはリソースを削除してから再作成するため、実際の運用ではrevertしても問題が起きないのをstg環境で事前に試験をして確認することも重要です。

Projectの分割単位

システムすべてのリソースを1つのProjectでまとめて開発するのは現実的ではないので、 適切な単位にGitのProjectを分割する必要があります。

RADIUSシステムでは、以下のようにProjectを分割しています。

  • gitlab-ci

    • 開発対象: .gitlab-ci.ymlのtemplate開発。Project種別に合わせたミニマムなコードを配置しておき、templateに問題がないか試験する。テストに通った時点でtagを作成し、templateを使用するProjectはtagを指定してtemplateを使う。

    • Pipelineの内容: 開発するProject種別によって変わる。

  • lambda

    • 開発対象: Lambda向けのCやRuby,Pythonのプログラム開発。プログラム単位でProjectを分割する

    • Pipelineの内容: build(必要なもののみ), test, push(zip化とS3への保存)。このProjectではaws環境へのリリースは行わない

  • terraform

    • 開発対象: terraformのtfファイルそのものの開発。aws系のProjectで作成されたzipファイルを使ったLambdaの展開や、EC2インスタンスのDeployを行う。サブシステム単位でProjectを分割する

    • Pipelineの内容: init, plan, apply(環境branchのみ)

  • container

    • 開発対象: Dockerfileの開発

    • Pipelineの内容: build, test, push

  • kubernetes

    • 開発対象: Kubernetes Manifest

    • Pipelineの内容: pull, test, deploy(環境branchのみ)

プログラム単体であればツールを使った単体テストを実施し、Kubernetesの試験であればDB等も含めた接続テストを実施する等、Projectの種別によって実施するテストのレイヤを変えます。 templateの内容は、includeする側で上書きできるので、完全に共通化できないjobのscriptは各Projectで上書きします。

運用イメージ

これまで説明してきた内容を元にしたGitOpsの運用イメージが以下になります イラストからは省略しましたが、topic branchのコードについては、開発環境を使って既存の環境に影響を与えない範囲でテストします。

f:id:biglobe-style:20200609134938p:plain:w800

商用環境のgitlab-runnerは、設定時にaccess-levelをref_protectedに設定して、protectされたbranchのjob以外では動かないようにするなど、セキュリティ対策も必要です。 より安全にしておくのであれば、不要な時間はrunnerのVMを停止しておくことも有効だと思います。

今後の目標

今まさに開発進行中で、「している(Doing)」の状態なので、いつか「した(Done)」記事を公開できればいいなと思っています。 開発においては、各開発Projectの中で試せるUnit testだけでなく、System test用のProjectを作成して、構築済み環境に対してSystem testを自動で実行できる仕組みも作りたいです。

まとめ

今回はGitLab + GitLab Runner + AWS構成でのGitOpsを紹介しました。 今ちょうどCI/CDやろうと思ってた、という方にちょっとでも参考になれば幸いです。

CI/CDについて詳しい方、もっとこういう設計をしたらいいのに、と思う方、 弊社は引き続き採用を進めてます。興味がございましたら以下のリンクを参照してください。

hrmos.co