読者です 読者をやめる 読者になる 読者になる

tjinjin's blog

いつかすごいエンジニアになることを目指して、日々学んだことを書いていきます。

GCEでサーバCIをやってみる

About

vagrant-googleを使ってサーバCIできるか検証してみました。

課題

  • AWSを使ってCIすると遅い気がする…突然、サーバの起動に数十分かかることもある…
  • dockerを使ってCIすると速いのだけれど、docker独自のハマりポイントがあったりそもそも使い方としてふさわしくなさそう…
  • gcpをisuconで触り始めたので、無料枠あるうちにためしてみるかー

という感じです。

GCE(Google Cloud Engine)とは

Google Cloud Platform上で構築できるVM環境です。AWSでいうEC2という理解です。料金体系が違い10分毎に料金が請求されるみたいです。

すべてのマシン タイプでにおいて、最低の使用料金として 10 分間が請求されます。たとえば、インスタンスを 2 分間実行した場合でも、10 分間の使用量が請求されます。最初の 10 分を経過後、インスタンスは 1 分ごとに直近の分に切り上げられて請求されます。たとえば、11.25 分実行されたインスタンスには、12 分の使用量が請求されます。

Google Compute Engine - クラウド コンピューティングと IaaS — Google Cloud Platform

また Preemptible(プリエンプティブル?)インスタンスというものがあり、最大70%割引されます。割引されるので、下記の制約があります

  • 24時間以上連続稼働できない。
  • 突然の死の可能性がある。(ただしstopするだけでterminateではないらしい)
  • いつも使えるわけではないらしい

詳細はこちら

Creating a Preemptible VM Instance - Compute Engine — Google Cloud Platform

ローカル環境でインスタンスを構築する

環境

まずはvagrant-googleをインストールしましょう。

$ vagrant plugin install vagrant-google
...
Installed the plugin 'vagrant-google (0.2.2)'!

mitchellh/vagrant-google · GitHub

次に設定ファイルを作ります。

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|

  config.vm.define :gcp do |gcp|
    gcp.vm.box = "gce"
    gcp.vm.box_url = "https://github.com/mitchellh/vagrant-google/raw/master/google.box"
    gcp.vm.synced_folder ".", "/vagrant", disabled: true

    gcp.vm.provider :google do |google, override|
      google.google_project_id = ENV['GCP_PROJECT_ID']
      google.google_client_email = ENV['GCP_EMAIL']
      google.google_json_key_location = ENV['GCP_KEY_LOCATION']
      #p12の場合はこっちで
      #google.google_key_location= ENV['GCP_KEY_LOCATION']

      google.name = "hogehoge"
      google.zone = "asia-east1-a"
      google.machine_type = "n1-standard-1"

      google.image = "centos-6-v20140619"
      google.disk_size = "10"

      google.preemptible = true
      google.on_host_maintenance = "TERMINATE"

      google.auto_restart = false

      override.ssh.username = 'circleci'
      override.ssh.private_key_path = '~/.ssh/id_gcp-circleci'
    end
  end
end

はまったポイントは下記でしょうか

  • image(AWSでいうところのAMI)を指定するのですが、何を指定すればいいのかわかりませんでした。centos-6-YYYYMMDDという形式みたいなのですが、imageの一覧があるページが見つかりませんでした。
  • preemptible = true とするとon_host_maintenanceTERMINATEにする必要があります。サーバCIでの利用を想定しているので、preemptibleでいきたいと思います。

環境変数については

export GCP_PROJECT_ID=xxx
export GCP_EMAIL=yyy
export GCP_KEY_LOCATION=zzz

上記の環境変数に入れる値は事前にGCP上で作成する必要があります。こちらの記事が参考になりそうです。

Terraform で GCE インスタンスを立ち上げてみた #gcpja #gcpug - えいのうにっき

取得したら設定して起動させたいところなのですが、その前にサーバにログインするときの公開鍵の設定が必要です。

f:id:cross_black777:20151031183351p:plain

メタデータ > SSHキータブ > 編集ボタンを押し、公開鍵を登録します。

これをやらないと、vagrant up後にssh接続待ちで止まってしまいます。同じ現象が起きた際には疑ってみるとよさそうです。

$ vagrant up --provider=google gcp
Bringing machine 'gcp' up with 'google' provider...
==> gcp: Launching an instance with the following settings...
==> gcp:  -- Name:            hogehoge
==> gcp:  -- Type:            n1-standard-1
==> gcp:  -- Disk type:       pd-standard
==> gcp:  -- Disk size:       10 GB
==> gcp:  -- Disk name:
==> gcp:  -- Image:           centos-6-v20140619
==> gcp:  -- Zone:            asia-east1-a
==> gcp:  -- Network:         default
==> gcp:  -- Metadata:        '{}'
==> gcp:  -- Tags:            '[]'
==> gcp:  -- IP Forward:
==> gcp:  -- External IP:
==> gcp:  -- Preemptible:     true
==> gcp:  -- Auto Restart:    false
==> gcp:  -- On Maintenance:  TERMINATE
==> gcp:  -- Autodelete Disk: true
==> gcp:  -- Scopes:
==> gcp: Waiting for instance to become "ready"...
==> gcp: Machine is booted and ready for use!
==> gcp: Waiting for SSH to become available...
==> gcp: Machine is ready for SSH access!

無事起動しました!起動が早い気がする!!!

circleciから起動させてみる

ローカルで検証した段階でvagrant-googlevagrant-awsで大きく違う点がありました。それは認証ファイルがあるかです。GCEの場合秘密鍵の情報が書かれたjsonかp12ファイルが必要ですが、AWSではaccess_keyとsecret_keyだけです。CirclrCIではファイルを安全に受け渡す方法がわからなかったので、はまりました。Travis CIにはあるみたいですね。

Encrypting Files - Travis CI

publicなリポジトリに生の設定ファイルを置くわけにもいかなかったので、せめて暗号化したファイルをアップロードすることにしました。

ファイルを暗号化する

CircleCIに秘密鍵は設定できるので、その秘密鍵を使ってファイル暗号化用の公開鍵を作成します。

$ openssl rsa -in <サーバログインの秘密鍵> -pubout -out pub.pem
$ openssl rsautl -encrypt -pubin -inkey pub.pem -in <GCP用の秘密鍵> -out <GCP用の秘密鍵>.encrypt # errorになる

作成した公開鍵を使ってファイルを暗号化しようとしたのですが、ファイルサイズが117B?を超えるとエラーになってしまうようです。

そこで共通鍵方式に切り替えたのですが、せっかく公開鍵の暗号化を学んだので組み合わせることにし最終的に下記のようになりました。

# ランダムな文字列が書かれたファイルを使って共通鍵でGCEアカウントの秘密ファイルを暗号化
$ openssl aes-256-cbc -e -in <GCP用の秘密鍵> -out <GCP用の秘密鍵>.encrypt -pass file:<ランダムな文字列>
# ランダムな文字列が書かれたファイルをサーバのログインに利用する秘密鍵で暗号化する
$ openssl rsautl -encrypt -pubin -inkey pub.pem -in <GCP用の秘密鍵> -out <GCP用の秘密鍵>.encrypt

暗号強度的に問題ないか若干自信がないです…

一応これでファイルを暗号化できました。CircleCI上では、まずランダムの文字列ファイルを復号したのち、それを利用して<GCP用の秘密鍵>を復号します。

CircleCIで動かしてみる

CircleCIの設定をする

Project SettingsからEnvironment VariablesとSSH Permissionsを設定します。

あとはcircle.ymlを作成してpushすれば完成です。

machine:
  timezone:
    Asia/Tokyo

dependencies:
  cache_directories:
    - ~/cache
  pre:
    - |
      echo "$GCP_PROJECT_ID" >> "$GCP_KEY_LOCATION"
      VERSION=1.7.4
      mkdir ~/cache
      cd ~/cache

      if [ ! -f vagrant_${VERSION}_x86_64.deb ]; then
        wget https://dl.bintray.com/mitchellh/vagrant/vagrant_${VERSION}_x86_64.deb
      fi
      sudo dpkg -i vagrant_${VERSION}_x86_64.deb
      if ! vagrant plugin list | fgrep -q vagrant-google; then
        vagrant plugin install vagrant-google
      fi

      cd ~/$CIRCLE_PROJECT_REPONAME
      ./script/decrypt.sh

      vagrant up --provider=google
      vagrant ssh-config gcp >> ~/.ssh/config

test:
  pre:
    - bundle exec knife solo bootstrap gcp

  post:
    - vagrant destroy -f

これで無事cookが実行されました。

まとめ

vagrant-googleを使ってCircleCIでサーバCIさせてみる検証でした。publicなリポジトリに暗号化してあってもファイルを置くのは若干抵抗があるので、privateなリポジトリのみでやったほうがいいかもしれません。(検証後すぐにサービスアカウントを削除しました。)CI時間やお金の試算はしっかりやってないのですが、サーバにcookするまでの時間を比較してもaws(約4分)に比べGCE(約1分半)なので充分早いのではないでしょうか。

参考

github.com

追記(10/31 21:00)

ファイルを暗号化して受け渡す部分ですが、あとで確認したところjsonファイルの内容を環境変数に登録し、その情報を一旦ファイルに書き出してあげればファイルをアップロードしなくてすみました。生ファイルが気になる時は、ご指摘いたただいようにbase64で加工するなりしてもいいかもしれません。

-      ./script/decrypt.sh
+      #./script/decrypt.sh
+
+      echo $GCP_KEY > $GCP_KEY_LOCATION
 
       vagrant up --provider=google
       vagrant ssh-config gcp >> ~/.ssh/config