コピペワールド

hirooka.pro

Google Cloud Platform

Google App Engine Standard (Java 8 runtime) で動作する Spring Boot アプリケーションと Google Cloud SQL との連携

更新日:

ついにベータとはいえ Google App Engine Standard Environment に Java 8 runtime が来たとのことで、Spring Boot 2.0.0.BUILD-SNAPSHOT と Cloud SQL (MySQL, PostgreSQL) で試してみました。Servlet 3.1 とのことなので、Spring Boot 2.0.0.BUILD-SNAPSHOT もそのまま使えます。

GitHub には既に GoogleCloudPlatform による Google App Engine Standard (Java 8) と Cloud SQL の連携のサンプルはあるのですが、Maven のプロジェクトです。

Cloud SQL sample for Google App Engine
https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java8/cloudsql

Gradle でも何とかならないのかと試してみました。結果としては Gradle でも期待通りにデプロイできました。

前準備

  • Google Cloud SDK で gcloud components install app-engine-java
  • Gradle で管理されている Executable jar な Spring Boot アプリケーション
  • 第2世代 Cloud SQL

API Manager Google Cloud SQL API 有効にする
App Engine

build.gradle

Google App Engine Standard には war をデプロイすることになります。Jetty 9 の上で動くとのことなので、Tomcat を exclude して Jetty を適用しています。例えば下記の通り。MySQL と PostgreSQL 用のライブラリも両方適用しています。そして、Google App Engine Gradle plugin を使用しています。

Google App Engine Gradle plugin
https://github.com/GoogleCloudPlatform/app-gradle-plugin

group 'pro.hirooka'
version '1.0.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.google.cloud.tools.appengine'
apply plugin: 'war'

sourceCompatibility = 1.8
targetCompatibility = 1.8

buildscript {
    repositories {
        mavenCentral()
        maven { url 'http://repo.spring.io/release' }
        maven { url 'http://repo.spring.io/milestone' }
        maven { url 'http://repo.spring.io/snapshot' }
        jcenter()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.0.0.BUILD-SNAPSHOT'
        classpath 'com.google.cloud.tools:appengine-gradle-plugin:+'
    }
}

repositories {
    mavenCentral()
    maven { url 'http://repo.spring.io/release' }
    maven { url 'http://repo.spring.io/milestone' }
    maven { url 'http://repo.spring.io/snapshot' }
}

configurations {
    compile.exclude module: 'spring-boot-starter-tomcat'
}

dependencies {
    compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0'
    compile 'org.springframework.boot:spring-boot-starter-web'
    providedRuntime 'org.springframework.boot:spring-boot-starter-jetty'
    compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
    compile 'org.springframework.boot:spring-boot-devtools'
    testCompile 'org.springframework.boot:spring-boot-starter-test'
    compile group: 'org.modelmapper', name: 'modelmapper', version: '1.0.0'
    compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.16.18'
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile 'com.google.api-client:google-api-client-appengine:+'
    compile 'com.google.cloud.sql:mysql-socket-factory-connector-j-6:+'
    compile 'com.google.cloud.sql:postgres-socket-factory:1.0.3'
    compile group: 'org.postgresql', name: 'postgresql', version: '42.1.3'
}

SpringBootServletInitializer

ビルドの成果物として jar ではなく war を生成するために、SpringBootServletInitializer を継承したクラスを作成しておきます。例えば、

package spring_boot_gcp;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
}

なお、main メソッドを持つクラスは Executable jar のアプリケーションのときに作成したそのままで何も変更していません。

package spring_boot_gcp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

appengine-web.xml

src/main/webapp/WEB-INF の下に作成します。例えば下記の通り。system-properties で、application.yml の profiles を使用して MySQL と PostgreSQL を切り替えるようにしています。

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <threadsafe>true</threadsafe>
    <runtime>java8</runtime>
    <use-google-connector-j>true</use-google-connector-j>
    <system-properties>
        <property name="spring.profiles.active" value="gae-std-postgresql"/>
    </system-properties>
</appengine-web-app>

application.yml

例えば下記のように profiles で MySQL と PostgreSQL を切り替えるようにします。

spring:
  profiles:
    active: localhost
---
spring:
  profiles: gae-std-mysql
  datasource:
    url: jdbc:mysql://google/spring_boot_gcp?cloudSqlInstance=PROJECT_ID:asia-northeast1:spring-boot-gcp-mysql&socketFactory=com.google.cloud.sql.mysql.SocketFactory
    username: user
    password: user1234
    driverClassName: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
  thymeleaf:
    cache: false
    mode: HTML
---
spring:
  profiles: gae-std-postgresql
  datasource:
    url: jdbc:postgresql://google/spring_boot_gcp?socketFactoryArg=PROJECT_ID:asia-northeast1:spring-boot-gcp-postgresql&socketFactory=com.google.cloud.sql.postgres.SocketFactory
    username: user
    password: user1234
  jpa:
    hibernate:
      ddl-auto: create
  thymeleaf:
    cache: false
    mode: HTML

デプロイ

Gradle でタスク appengineDeploy を実行します。なんと 30 秒くらいで App Engine Standard Environment へのデプロイが完了します。App Engine Flexible Environment だと Compute Engine からの Docker ということもあるのか、同じアプリケーションをデプロイすると 5 分くらいかかります。これが App Engine Standard Environment を動かしている Borg の威力でしょうか。

appengine-web.xml で MySQL と PostgreSQL を切り替えて試してみましたが、両方とも正常にアプリケーションが動いているようでした。

何はともあれ、選択肢が広がって良かったです。

-Google Cloud Platform

Copyright© hirooka.pro , 2018 All Rights Reserved.