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_maintenanceをTERMINATEにする必要があります。サーバCIでの利用を想定しているので、preemptibleでいきたいと思います。
環境変数については
export GCP_PROJECT_ID=xxx export GCP_EMAIL=yyy export GCP_KEY_LOCATION=zzz
上記の環境変数に入れる値は事前にGCP上で作成する必要があります。こちらの記事が参考になりそうです。
Terraform で GCE インスタンスを立ち上げてみた #gcpja #gcpug - えいのうにっき
取得したら設定して起動させたいところなのですが、その前にサーバにログインするときの公開鍵の設定が必要です。

メタデータ > 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-googleとvagrant-awsで大きく違う点がありました。それは認証ファイルがあるかです。GCEの場合秘密鍵の情報が書かれたjsonかp12ファイルが必要ですが、AWSではaccess_keyとsecret_keyだけです。CirclrCIではファイルを安全に受け渡す方法がわからなかったので、はまりました。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分半)なので充分早いのではないでしょうか。
参考
追記(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