先日、Google Compute Engine(以下、GCE) のインスタンスの利用料が1秒単位の課金に変更されたという発表がありました!!
GCE は高性能でスケーラブルな仮想インスタンスのサービスです。今までは最低10分で1分単位の課金でしたが、これからは最小1分で1秒あたりの課金で利用できるようになります!
そこで今回は、コスト削減に直結する、インスタンスの起動・停止についてです。
使いたいときだけに起動し、作業が終わったら停止できるように、Compute Engine APIを使って、GAEからインスタンスを起動・停止してみましたので、ご紹介します!
目次
GAEからComputeEngineAPIを使って、GCEインスタンスの作成/停止/起動をしてみる!
以下の流れで進めます。
- Compute Engine APIを有効にする
- 認証周りを理解する
- GCEインスタンスを作成する
- GCEインスタンスのステータスを確認する
- GCEインスタンスを停止する
- GCEインスタンスを起動する
3~6の各処理は以下のサーブレットを登録して呼び出します。
作成:TestCreateInstanceServlet
確認:TestCheckStatusServlet
停止:TestStopInstanceServlet
起動:TestStartInstanceServlet
各サーブレットは内容が同じなので、2つ目以降はメソッドのみを紹介します。
1.Compute Engine APIを有効にする
まずは、Compute Engine API を有効にします。
画面左上のメニューアイコンをクリックします。
左側のメニューから「APIとサービス」→「APIとサービスの有効化」→「Compute Engine API」→「有効にする」を選択
2.認証周りを理解する
GCPでは、どのリソースに対して誰がどのようなアクセス権(役割)を持つか定義することで、アクセス制御を管理しています。
GAEのデフォルトサービスアカウント
GCPプロジェクトに GAEアプリケーションが含まれている場合、デフォルトの App Engine サービス アカウントがプロジェクトに自動的に作成されます。
実際にコードをデプロイした後に確認してみます。
コンソール画面の左画面から、「IAMと管理者」→「IAM」を選択します。
App Engine app default service account というアカウントが確認できます。
このデフォルトサービスアカウントには作成時に自動的に「プロジェクトの編集者」の役割が付与されています。プロジェクトの編集者はGCPプロジェクト内の全てのリソースに対して編集ができます。ここでGCEリソースの編集が出来る権限がない場合は、今回のようなGAEからのGCEインスタンスの起動・停止などの操作はできません。
アプリケーションのデフォルト認証情報(ADC)
上記のサービスアカウントの権限に加えて、Compute Engine API を使うにあたり、GoogleAPIを呼び出すための認証(アクセス認証)が必要になります。
今回、その認証にはアプリケーションのデフォルト認証情報(ADC)を使用します。
アプリケーションのデフォルト認証情報は、Google APIクライアントライブラリで提供されています。これを使えば、OAuth2.0認証資格情報が簡単に取得できます。特に今回のようにGoogle App EngineまたはGoogle Compute Engineの仮想マシンにデプロイされるアプリケーションを構築する場合に推奨される方法です。
基本的にはこの1行です。
Credential credential = GoogleCredential.getApplicationDefault();
これによって、アプリケーションのデフォルト認証を取得できます。権限取得は、コードが実行されている環境に寄って異なります。GAEで実行している場合は、GAEのデフォルトサービスアカウントの認証が付与されます。
また、デフォルト認証情報を取得する際には、明示的に必要なスコープを指定する必要があります。以下を参考に必要に応じて要求されたスコープを指定します。
アクセススコープ
Google Compute Engine API を OAuth2.0認証でアクセスするとき次のスコープを指定して、GAEからGCEに対する操作範囲を指定します。今回はデフォルトサービスアカウントを利用するため、そこにIAMで割り当てられている権限以上の権限は使えません。
Compute Engine API のクライアントライブラリでは、GCEの操作以外にも利用可能なスコープがあるのでいくつか紹介します。
スコープ | 権限内容 |
---|---|
https://www.googleapis.com/auth/cloud-platform | すべての Google Cloud Platform リソースへの完全アクセス権 |
https://www.googleapis.com/auth/compute | Google Compute Engine メソッドへのフル コントロール アクセス権 |
https://www.googleapis.com/auth/compute.readonly | Google Compute Engine メソッドへの読み取り専用アクセス権 |
https://www.googleapis.com/auth/devstorage.full_control | Google Cloud Storageの フル コントロール アクセス権 |
https://www.googleapis.com/auth/devstorage.read_only | Google Cloud Storage への読み取り専用アクセス権 |
https://www.googleapis.com/auth/devstorage.read_write | Google Cloud Storageの データの読み取りと変更を可能にするアクセス権 |
今回はGCEへの操作範囲を指定します。
Compute EngineAPI クライアントライブラリでは
COMPUTE = "https://www.googleapis.com/auth/compute"
というかたちでスコープが提供されています。
今回の認証周りだけを切り抜くとこのようになります。
//デフォルト認証情報取得(今回はGAEのデフォルトサービスアカウント)
GoogleCredential credential = GoogleCredential.getApplicationDefault();
// スコープを指定し、本プログラム上でのAPI操作範囲を指定
if (credential.createScopedRequired()) {
credential = credential.createScoped(Arrays.asList(ComputeScopes.COMPUTE));
}
以上のように、権限設定は以下の3ステップです。
- 認可範囲をIAMで指定
- 実行者としての認可をコード中に記載
- 実行時にはIAMで実行者に割り当てられた範囲内でスコープを指定
以上でスコープで指定した範囲内のAPIを実行出来るようになります。これは他のAPIを呼び出す際にも同様ですね。
3.GCEインスタンスを作成する
GCPプロジェクト「apps-gcp-test」に、以下のGCEインスタンスを1台作成します。
インスタンス名 | test |
---|---|
ゾーン | asia-northeast1-b |
ネットワーク | default |
ディスクタイプ | 標準の永続ディスク |
イメージ | debian-9 |
instances().insert メソッド
リクエストされたデータを使用して、指定されたプロジェクトにインスタンスリソースを作成します。
リクエストパラメータ
必要なパラメータは2つです。
- roject : プロジェクトID(string)
- zone :インスタンスのゾーン(string)
ここで紹介するリクエストパラメータは以下のようにHTTPリクエストに含まれる値です。(今回はクライアントライブラリを利用するのでHTTPリクエストは使いません。)
リクエストボディ
インスタンスを作成するために最低限必要なプロパティは5つです。
プロパティ | 内容 |
---|---|
name | 作成したいインスタンス名(string) |
machineType | n1-standard-1などのマシンタイプ(string)。 zone / zone / machineTypes / machine-typeの形式で記述します。 例: zones/asia-northeast1-b/machineTypes/n1-standard-1 |
disks[].initializeParams.sourceImage | ディスクを作成するためのソースイメージ(string)。 パブリックオペレーティングシステムイメージを使用してディスクを作成するには、そのファミリ名でイメージを指定します。 例:最新のDebian 9イメージを使用するには、family/debian-9と指定します。 projects/debian-cloud/global/images/family/debian-9 |
networkInterfaces[] | ネットワークインタフェースの構成の配列(list)。 この中に下にあるnetworkを入れます |
networkInterfaces[].network | インスタンスのネットワークリソースのURL(string) 例:デフォルトのネットワークの場合 global/networks/default |
では、実際に今回使ったファイルの中身を紹介します。
web.xml(今回試してみるインスタンス作成/確認/停止/起動の全てを含みます。)
<servlet>
<servlet-name>TestCreateInstance</servlet-name>
<servlet-class>jp.co.yoshidumi.testproject.TestCreateInstanceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestCreateInstance</servlet-name>
<url-pattern>/create</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>TestStartInstance</servlet-name>
<servlet-class>jp.co.yoshidumi.testproject.TestStartInstanceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestStartInstance</servlet-name>
<url-pattern>/start</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>TestStopInstance</servlet-name>
<servlet-class>jp.co.yoshidumi.testproject.TestStopInstanceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestStopInstance</servlet-name>
<url-pattern>/stop</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>TestCheckStatus</servlet-name>
<servlet-class>jp.co.yoshidumi.testproject.TestCheckStatusServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestCheckStatus</servlet-name>
<url-pattern>/check</url-pattern>
</servlet-mapping>
上記で定義したTestCreateInstanceを以下のように instances().insert メソッドを使って実装します。
@SuppressWarnings("serial")
public class TestCreateInstanceServlet extends HttpServlet {
private Compute service;
private static final HttpTransport HTTP_TRANSPORT = new UrlFetchTransport();
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final String APPLICATION_NAME = "TEST_MANAGE_INSTANCE";
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
doPost(req, resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// プロジェクト名、ゾーンを指定する。
String project = "apps-gcp-test";
String zone = "asia-northeast1-b";
// リクエストボディを作成して、ネットワークやディスク情報のプロパティを指定します。
Instance requestBody = new Instance();
// インスタンス名、マシンタイプを指定する。
String instanceName = "test";
String machineType = "zones/asia-northeast1-b/machineTypes/n1-standard-1";
requestBody.setName(instanceName);
requestBody.setMachineType(machineType);
// ディスクを指定する。Debian9イメージを使用する。
AttachedDisk disk = new AttachedDisk();
List disks = new ArrayList<>();
disk.setBoot(true);
disk.setAutoDelete(true);
disk.setType("PERSISTENT");
AttachedDiskInitializeParams initializeParams = new AttachedDiskInitializeParams();
AttachedDiskInitializeParams sourceImage = initializeParams.setSourceImage("projects/debian-cloud/global/images/family/debian-9");
AttachedDisk diskImage = disk.setInitializeParams(sourceImage);
disks.add(diskImage);
requestBody.setDisks(Collections.singletonList(disk));
// ネットワークを指定する。
NetworkInterface networkInterface = new NetworkInterface();
List networkInterfaces = new ArrayList<>();
networkInterface.setNetwork("global/networks/default");
AccessConfig accessConfig = new AccessConfig();
List accessConfigs = new ArrayList<>();
accessConfig.setName("external-nat");
accessConfig.setType("ONE_TO_ONE_NAT");
accessConfigs.add(accessConfig);
networkInterface.setAccessConfigs(accessConfigs);
networkInterfaces.add(networkInterface);
requestBody.setNetworkInterfaces(networkInterfaces);
// 認証情報取得
GoogleCredential credential = createCredential();
// Compute Engine オブジェクトを作成
this.service = new Compute.Builder(
HTTP_TRANSPORT, JSON_FACTORY, null).setApplicationName(APPLICATION_NAME)
.setHttpRequestInitializer(credential).build();
// instances().insertをたたいてインスタンスを作成する
Insert createInstance = service.instances().insert(project, zone, requestBody);
createInstance.execute();
}
// アプリケーションのデフォルト認証情報を取得する
public GoogleCredential createCredential() throws IOException {
GoogleCredential credential = GoogleCredential.getApplicationDefault();
// スコープを指定し、本プログラム上でのGCEに対する操作範囲を指定
if (credential.createScopedRequired()) {
credential = credential.createScoped(Arrays.asList(ComputeScopes.COMPUTE));
}
return credential;
}
}
ブラウザから以下のリクエストをして、インスタンスを作成します。
http://{GCPのアプリケーションID}.appspot.com/insert
作成したインスタンスはGCPコンソール画面でも簡単に確認ができるのですが、今回はComputeEngineAPIを使って、同じようにGAEからインスタンスのステータスを確認してみます。
4.GCEインスタンスのステータスを確認する
作成したインスタンスが起動しているかどうか、GAEからAPIをたたいて確認します。
instances().get メソッド
指定されたインスタンスのリソース情報を返します。
リクエストパラメータ
必要なパラメータは3つです。
- project : プロジェクトID(string)
- zone :インスタンスのゾーン(string)
- instance : インスタンス名(string)
instances().get メソッドで以下のように実装します。
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// プロジェクト名、ゾーン名、インスタンス名をパラメータとして登録
String project = "apps-gcp-test";
String zone = "asia-northeast1-b";
String instance = "test";
// 認証情報取得
GoogleCredential credential = createCredential();
// Compute Engine オブジェクトを作成
this.service = new Compute.Builder(
HTTP_TRANSPORT, JSON_FACTORY, null).setApplicationName(APPLICATION_NAME)
.setHttpRequestInitializer(credential).build();
// instances().getをたたいてインスタンスの情報を取得
Get getStatue = service.instances().get(project, zone, instance);
Instance execute = getStatue.execute();
// インスタンスのステータスをログに出す
LOGGER.info("testインスタンスのステータス: " + execute.getStatus());
}
ブラウザから以下のリクエストをして、インスタンスのステータスを確認します。
http://{GCPのアプリケーションID}.appspot.com/check
ステータスをログに出したので、コンソール画面から確認してましょう。
画面左上のメニューアイコンをクリックします。
左側のメニューから「ロギング」→「ログ」を選択して画面遷移します。
ステータスが「RUNNING」になっているので、インスタンス作成は成功です!
これでプログラム中でインスタンスの起動状態を確認することが出来ました。
5.GCEインスタンスを停止する
先程作成した起動中のインスタンスを停止します。
instances().stop メソッド
実行中のインスタンスを停止し、正常にシャットダウンし、後でインスタンスを起動できるようにします。
リクエストパラメータ
必要なパラメータは3つです。
- project : プロジェクトID(string)
- zone :インスタンスのゾーン(string)
- instance : インスタンス名(string)
instances().stop メソッドで以下のように実装します。
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// プロジェクト名、ゾーン名、インスタンス名をパラメータとして登録
String project = "apps-gcp-test";
String zone = "asia-northeast1-b";
String instance = "test-instance";
// 認証情報取得
GoogleCredential credential = createCredential();
// Compute Engine オブジェクトを作成
this.service = new Compute.Builder(
HTTP_TRANSPORT, JSON_FACTORY, null).setApplicationName(APPLICATION_NAME)
.setHttpRequestInitializer(credential).build();
// instances().stopをたたく
Stop stopInstance = service.instances().stop(project, zone, instance);
stopInstance.execute();
}
}
ブラウザから以下のリクエストをして、インスタンスを停止します。
http://{GCPのアプリケーションID}.appspot.com/stop
上の[3.GCEインスタンスのステータスを確認する]と同じようにインスタンスの状況を確認してみます。
ブラウザから以下のリクエストをします。
http://{GCPのアプリケーションID}.appspot.com/check
ログを見るとステータスが「TERMINATED」に変更しています。
これでインスタンスは停止状態です。
6.GCEインスタンスを起動する
今度は先程停止させたインスタンスを起動します。
手順内容は先程とほとんど同じです。
instances().start メソッド
停止したインスタンスを開始します。
リクエストパラメータ
必要なパラメータは3つです。(停止時に指定したものと同じです。)
- project : プロジェクトID(string)
- zone :インスタンスのゾーン(string)
- instance : インスタンス名(string)
instances().start メソッドで以下のように実装します。
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// プロジェクト名、ゾーン名、インスタンス名をパラメータとして登録
String project = "apps-gcp-test";
String zone = "asia-northeast1-b";
String instance = "test-instance";
// 認証情報取得
GoogleCredential credential = createCredential();
// Compute Engine オブジェクトを作成
this.service = new Compute.Builder(
HTTP_TRANSPORT, JSON_FACTORY, null).setApplicationName(APPLICATION_NAME)
.setHttpRequestInitializer(credential).build();
// instances().startをたたく
Start startInstance = service.instances().start(project, zone, instance);
startInstance.execute();
}
ブラウザから以下のリクエストをして、インスタンスを起動します。
http://{GCPのアプリケーションID}.appspot.com/start
再びインスタンスの状況を確認してみます。ブラウザから以下のリクエストをします。
http://{GCPのアプリケーションID}.appspot.com/check
ログを見るとステータスが「RUNNING」に変更しています。
これでインスタンスは起動完了です!
まとめ
いかがでしたでしょうか?
今回はGCEインスタンスの作成、停止&起動、インスタンスの状態の確認を全てGAEからComputeEngineAPIを使って、実行してみました!
今回のようにGAEからGCEインスタンスが操作できれば、GAEのCronを使うことで、自動起動や自動停止などのスケジューリングもでき、業務時間だけインスタンスを起ち上げて作業をする、ということも簡単にできるようになります。
今回は4つのメソッドを使って、ほとんど同じ流れで実装をしています。
ポイントを簡単にまとめます。
- プロジェクト名などのパラメータを登録
- 認証情報を取得する
- Compute Engine オブジェクトを作成
- APIをたたいてインスタンスを操作する
APIでGCEインスタンスを起動/停止できれば、コスト削減をしたい場合にも最適です!
是非試してみてください!!