GKEとGitLabを使ってマージリクエストごとにHTTPS環境を作る(β版)

Adways Advent Calendar 2017 12日目の記事です。

http://blog.engineer.adways.net/entry/advent_calendar_2017


はじめに

こんにちは、アドテクノロジーDivの弓場(ゆば)です。

Gitで開発している人であればマージリクエストごとに動く開発環境があればレビューがしやすいのではないかと考える人は多いかと思います。
最近自社のプロダクトにGCPを採用し始めましたので、GKEを使ってこの環境を構築できないかと思い作ってみました。
弊社ではGitLabをGitホスティングサービスに使用しており、この記事でもGitLabのCI/CDの仕組みを使った環境構築になっています。

※ 現バージョン(1.7.8-gke.0)のGKE + GCLBのL7の構成はベータ版になっているため、将来的にこの構成は使えなくなる可能性があります。
 そのためタイトルにβ版を入れました
 https://github.com/kubernetes/ingress-gce/blob/master/BETA_LIMITATIONS.md#glbc-beta-limitations

TL;DR

  • Gitブランチごとに自動的にHTTPS環境が作られるようになってレビューがしやすくなり、HTTPSの機能のテストも容易に行えるようになった
  • GitLabはCI環境にKubernetesをサポートしているため、環境変数の準備の手間が少なくて済むのでオススメです
    CI, Docker Registryも一緒に用意出来るというメリットもあります
    また、CIのRunnerをDockerコンテナで動かすことも出来るため、Runner用のマシンを用意せずに動かすことができます
  • 証明書にワイルドカードが必要なのでLet's Encryptは使えなかった
    (Let's Encryptを使用する場合はKube-Legoイメージを使用したDeployを立てて、IngressのPath「/.well-known/acme-challenge/*」を渡してあげると上手くいきます)
  • GKEの環境構築が面倒くさい人はGoogle App Engineが最近カスタムドメインの自動証明書発行機能がリリースされたのでそちらも検討してみるといいかも知れません

始める前に

今回は事前に「ドメイン名」と「ワイルドカード証明書のCRTファイルとKEYファイル」を持っている前提でお話します。
この記事を見ながら作業する場合は事前に作成しておいてください。

また、GCPも事前に使える前提で話しを進めていきますため、gcloudコマンドも必要になります。

今回の記事で使う各ツール、サービスのバージョンは以下の通りです。

  • Kubernetes 1.7.8-gke.0
  • GitLab Community Edition 10.0.4
  • Nuxt V1.0.0-RC11
  • Google Cloud SDK 183.0.0

構築手順

1.GCP側の準備

まず、GCPで外部 IP アドレスの予約を行います。

「VPCネットワーク」→ 「外部 IP アドレス」の順で表示して予約登録を行います。
今回は "feature-global-ip" という名前で作成します。
f:id:AdwaysEngineerBlog:20171218142106p:plain

外部IPアドレスの予約をした後、しばらく経つとIPアドレスが割り振られますのでこれをDNS登録に使用します。

次に、GCPでDNSの設定と予約したIPアドレスのAレコードの設定を行います。
「ネットワーク サービス」 → 「Cloud DNS」の順に表示してゾーン作成を行います。
ネームサーバー連携用のレコードセットの追加と先ほど予約したIPアドレスへのAレコード登録が完了したらGCP側は準備完了です。
f:id:AdwaysEngineerBlog:20171218142120p:plain

最後に、GKEのクラスタを作成します。

「Kubernetes Engine」を選択して、「クラスタを作成」を選択します。
f:id:AdwaysEngineerBlog:20171218142131p:plain

特に変更する箇所はありませんが、課金が気になる人はサイズの項目を"1"に変更しておくといいかもしれません。
クラスタを作成するとKubernetes クラスタの一覧画面に戻りますので作成が完了するまで待ちましょう。

クラスタの作成完了後、GitLabと連携するためKubernetesの"クラスタURL"をメモします。
"クラスタURL"はクラスタ詳細画面のエンドポイントにあります。

2.GitLab側での準備

GitLab側では以下のものを作成、設定していきます。

  • プロジェクト(リポジトリ)
  • GitLabとKubernetesの連携設定

では、まずリポジトリの作成から行います。
GitLabのトップページを表示した後、ヘッダーの「+」ボタンから「New Project」を選択してプロジェクト作成画面を表示します。
f:id:AdwaysEngineerBlog:20171218142227p:plain

今回は国内のフロントエンド業界で使われ始めている"Nuxt"を使ったアプリケーションを動かします。
templateはBlankのままにして適当に各項目を入力して「Create project」を選択します。
これでプロジェクトの作成は完了です。

次にGitLab CI IntegrationからKubernetesを有効にして、CI上でGKEと連携できるようにします。

「Settings」→ 「Integrations」の順で選択し、画面下のほうにある「Kubernetes」を選択します。
f:id:AdwaysEngineerBlog:20171218142245p:plain

入力画面に遷移したら各項目を入力していきます。

  • Active → チェック
  • API URL → GKEのエンドポイントを入力 (例: https://35.201.1.1
  • CA Certificate → GKEのca.crtを入力
    ca.crtは GKEのクラスタ詳細画面の「証明情報を表示」から確認できます
  • Project namespace → プロジェクトの名前をそのまま入力
  • Token → クラスタのAPIからTokenを取得する
    1. secret一覧を取得する。 usernameとpasswordはGKEのクラスタ詳細画面の「証明情報を表示」に記載されているものを使用します
      curl -u "username:password" https://エンドポイント/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy/api/v1/secret/default/
    2. secret名を加えたAPIにアクセスする
      curl -u "username:password" https://エンドポイント/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy/api/v1/secret/default/default-token-xxxxx
    3. data.token に記載されている値を入力する

入力が完了したら「Save changes」で保存します。これでKubernetesの有効化は完了です。

3.ソースコードをPUSHしてマージリクエスト画面でCIの動作を監視する

今回事前にソースコードを作成しておいたのでこれをcloneしてGitLabにPushします。
https://github.com/yubaken/nuxt-kube

※ 今回用意したソースコードは仕事の合間に急いで作ったものなので、別のGitLab上にPushしても動かない可能性があります

ですがその前にCIを動かすための設定が不十分のため".gitlab-ci.yml"を編集します。
さらに今回はマージリクエストを行いたいため、「develop」ブランチを切ってから編集します。

variables:
  KUBE_DOMAIN: # 自分のドメイン名に変更する 例: example.com #
  CI_REGISTRY_TAG: ${CI_BUILD_REF_SLUG}
  GOOGLE_CLOUD_REGISTORY: gcr.io/example-project
  GKE_CI_REGISTRY: https://gcr.io
  GKE_CI_REGISTRY_IMAGE: ${GOOGLE_CLOUD_REGISTORY}/${CI_PROJECT_NAME}
  GKE_CI_REGISTRY_USER: oauth2accesstoken
  GKE_CI_REGISTRY_PASSWORD: # gcloud auth application-default print-access-token で表示される値を入力する #
...
review:
  image: "$CI_REGISTRY_IMAGE/runner-base-image:latest"
  stage: review
...
  variables:
    GLOBAL_STATIC_IP_NAME: feature-global-ip
    TLS_SECRET_NAME: wildcard-feature-tls
    TLS_CRT: # 変更箇所 証明書のCRTファイルの中身をbase64エンコードする #
    TLS_KEY: # 変更箇所 証明書のKEYファイルの中身をbase64エンコードする #

※ このような認証情報などはYAMLファイルに定義するのではなく、GitLabの「Settings」→ 「CI/CD」にあるSecret variablesに設定しておいたほうが安全です。今回は説明しやすくするため省きました

設定の変更が完了したらソースコードをコミットしてGitLabにPushします。
プッシュした後プロジェクトの画面を表示すると「Create merge request」のボタンが上部に表示されますので、選択してその後「Submit merge request」を選択します。
f:id:AdwaysEngineerBlog:20171218142447p:plain

そうするとマージリクエスト画面にCIの進捗が表示されますので、それが全て完了するまで待ちます。
f:id:AdwaysEngineerBlog:20171218142506p:plain

CIが全て完了すると画面上に開発環境のURLが表示されますのでクリックするとNuxtのサンプルページが表示されました。
※ CIが完了してもGKEとGCLBの設定が完了するまで5~10分ほど時間がかかります。
f:id:AdwaysEngineerBlog:20171218142521p:plain f:id:AdwaysEngineerBlog:20171218142532p:plain

GKEに何をさせているのか

※この記事は2017年12月時点での内容になります 公式では"work in progress" と明記されているため今後この動き自体が変わる可能性があります
https://github.com/kubernetes/ingress-gce

ingress-gceの仕組みを使ってKubernetesのIngressを元に、GCLBに対して以下のようなL7ロードバランサを構築させています
Global Forwarding Rule → Target Http Proxy → Url Map → Backend Service → Instance Group

恐らくIngressは1ドメインに1Ingressという形で立てていくのが理想なのかも知れませんが、ingress-gecでは1つのipアドレスに対して複数のingressを立てることが出来ません。
そのため、今回はUrlMapを動的に増やしたり、減らしたりすることによってワイルドカードドメインに対応させています。

GitLabのCIについて

GitLabは".gitlab-ci.yml"というファイルをディレクトリのルートに配置してPushするとCIが自動的に動く仕組みになっています。
一般的なCIのRunnerはPCマシンをRunnerにしていますが、今回DockerコンテナをRunnerにしてCIから処理を流させるようにしました。
こうすることのメリットは、Runnerに必要なライブラリやツールなどをDockerfileに定義出来るという点にありマシン内を汚さずに済む点があります。
また、公式のDockerイメージを使えばDockerfile自体作らずに済むという点もあります。

そして、何も用意していないCIであればAPI通信や環境変数などを全て自前で用意しなければならないと思いますが、
GitLab CIのKubernetes Integrationを使用すればKubernetesへのデプロイとKubernetesデプロイに必要な環境変数をいくつか自動的に設定してくれるメリットもありました。
これは公式でもAutoDeployという名前で構築手順の動画がアップロードされています。
https://www.youtube.com/watch?v=1iXFbchozdY

おわりに

今回はお金のかかるワイルドカード証明書を使用しましたが、来月にはLet's Encryptがワイルドカード証明書に対応するので、今後はこのような環境を無料で構築できそうです。
また私の参画しているプロジェクトでしかこの仕組みは動いていませんが、今後は弊社のいろいろなプロジェクトにも使えるよう検討していきます。