Java スタートガイド

このチュートリアルは、初めてクラウドでアプリをビルドする人を対象にしています。エンジニアやウェブ デベロッパーなどが、Google Cloud に適用できるアプリ開発の重要なコンセプトを学習する場合に役立ちます。

目標

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

このドキュメントの手順は、リソース使用量が Google Cloud の無料枠ティアの制限内に収まるように設計されています。 料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Firestore データベースをネイティブ モードで作成するには、次の手順に沿って操作してください。
    1. Cloud Console の Firestore Viewer ページに移動します。
      Firestore Viewer に移動
    2. [Cloud Firestore モードの選択] 画面から [ネイティブ モードを選択] をクリックします。
    3. Firestore データベースのロケーションを選択します。このロケーション設定は、Cloud プロジェクトのデフォルトの Google Cloud リソースのロケーションです。このロケーションは、ロケーション設定を必要とする Cloud プロジェクト内の Google Cloud サービス(特にデフォルトの Cloud Storage バケットおよび Cloud Run アプリ)で使用されます。
    4. [データベースを作成] をクリックします。
  7. Enable the Cloud Run Admin, Cloud Storage, Cloud Logging, and Error Reporting APIs.

    Enable the APIs

  8. サンプル リポジトリのクローンを作成して、Cloud Shell でサンプル アプリケーションを開きます。
    Cloud Shell に移動

    Cloud Shell を使用すると、ブラウザからコマンドラインで直接 Google Cloud リソースにアクセスできます。

  9. サンプルコードをダウンロードしてアプリのディレクトリに移動するには、[続行] をクリックします。
  10. Cloud Shell で、gcloud ツールを構成して新しい Google Cloud プロジェクトを使用します。

    # Configure gcloud for your project
    gcloud config set project PROJECT_ID
    

    PROJECT_ID は、Cloud Console を使用して作成した Google Cloud プロジェクト ID に置き換えます。

    Google Cloud CLI は、コマンドラインから Google Cloud リソースを操作する基本的な方法です。このチュートリアルでは、gcloud ツールを使用してアプリをデプロイおよびモニタリングします。

アプリを実行する

  1. すでに Cloud Shell を使用し、Java 11 を使うように構成している場合は、シェルの Java 代替、JAVA_HOMEPATH 環境変数で Java 8 を指定して更新します。
  2. bookshelf/1-cloud-run ディレクトリに移動してアプリを実行します。
    GOOGLE_CLOUD_PROJECT=PROJECT_ID mvn -Plocal clean jetty:run-exploded
    
    PROJECT_ID は、作成した Google Cloud プロジェクトの ID に置き換えます。
  3. Cloud Shell で、[ウェブでプレビュー] をクリックし、[ポート 8080 でプレビュー] を選択します。新しいウィンドウが開き、実行中のアプリが表示されます。

アプリを Cloud Run にデプロイする

Google Cloud には、コードを実行する際に使用できるオプションがいくつか用意されています。この例では、Cloud Run を使用してスケーラブルなアプリを Google Cloud にデプロイします。Cloud Run では、サーバーを管理する必要はなく、トラフィックの急増に対応するために自動的にスケーリングします。

  1. Jib を使用してイメージをビルドします。
    mvn package jib:build -Dimage=gcr.io/PROJECT_ID/bookshelf

    PROJECT_ID は、作成した Google Cloud プロジェクト ID に置き換えます。

  2. イメージをデプロイします。
    gcloud run deploy bookshelf --image gcr.io/PROJECT_ID/bookshelf \
    --platform managed --region us-central1 --allow-unauthenticated
    PROJECT_ID は、作成した Google Cloud プロジェクト ID に置き換えます。

デプロイが成功すると、Cloud Run で実行しているアプリに次のフォーマットでエンドポイントが出力されます。

https://bookshelf-abcdefghij-uc.a.run.app

アプリがこのリンク(以降 YOUR_CODE_RUN_URL)で表示できるようになりました。ウェブブラウザにこの URL を入力すると、アプリが表示されます。

Bookshelf アプリのホームページ

Firestore でデータを永続化する

Cloud Run インスタンスに情報を保存することはできません。インスタンスを再起動すると情報が失われ、新しいインスタンスが作成されたときにはその情報は存在しないためです。そこで、すべてのインスタンスが読み取りおよび書き込みするデータベースを使用します。

Google Cloud にはデータを保存する際に使用できるオプションがいくつか用意されています。この例では、Firestore を使用して各書籍のデータを保存します。Firestore はフルマネージドでサーバーレスの NoSQL ドキュメント データベースで、データを保存および照会できます。Firestore はアプリのニーズを満たすために自動でスケールし、使用しない場合はゼロにスケールします。最初の書籍を追加しましょう。

  1. ウェブブラウザで YOUR_CODE_RUN_URL にアクセスします。
  2. デプロイしたアプリ用の書籍を作成するには、[Add book] をクリックします。

    Bookshelf アプリに書籍を追加する
  3. [Title] 項目に「Moby Dick」と入力します。
  4. [Author] 項目に「Herman Melville」と入力します。
  5. [Save] をクリックします。Bookshelf アプリにエントリが追加されました。

    Moby Dick の Bookshelf アプリのエントリ
  6. Cloud Console で Firestore ページを更新するには、[更新] をクリックします。データが Firestore に表示されます。Bookshelf アプリでは、各書籍が一意の ID とともに Firestore ドキュメントとして、すべて Firestore コレクション内に保存されます。このチュートリアルでは、コレクションを書籍集(books)と呼びます。Firestore ドキュメントの例。

Firestore は Firestore クライアント ライブラリを使用して書籍集を保存します。Firestore ドキュメントを取得する例を次に示します。

public class FirestoreDao implements BookDao {
  private CollectionReference booksCollection;

  public FirestoreDao() {
    Firestore firestore = FirestoreOptions.getDefaultInstance().getService();
    booksCollection = firestore.collection("books");
  }

  private Book documentToBook(DocumentSnapshot document) {
    Map<String, Object> data = document.getData();
    if (data == null) {
      System.out.println("No data in document " + document.getId());
      return null;
    }

    return new Book.Builder()
        .author((String) data.get(Book.AUTHOR))
        .description((String) data.get(Book.DESCRIPTION))
        .publishedDate((String) data.get(Book.PUBLISHED_DATE))
        .imageUrl((String) data.get(Book.IMAGE_URL))
        .createdBy((String) data.get(Book.CREATED_BY))
        .createdById((String) data.get(Book.CREATED_BY_ID))
        .title((String) data.get(Book.TITLE))
        .id(document.getId())
        .build();
  }

  @Override
  public String createBook(Book book) {
    String id = UUID.randomUUID().toString();
    DocumentReference document = booksCollection.document(id);
    Map<String, Object> data = Maps.newHashMap();

    data.put(Book.AUTHOR, book.getAuthor());
    data.put(Book.DESCRIPTION, book.getDescription());
    data.put(Book.PUBLISHED_DATE, book.getPublishedDate());
    data.put(Book.TITLE, book.getTitle());
    data.put(Book.IMAGE_URL, book.getImageUrl());
    data.put(Book.CREATED_BY, book.getCreatedBy());
    data.put(Book.CREATED_BY_ID, book.getCreatedById());
    try {
      document.set(data).get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }

    return id;
  }

  @Override
  public Book readBook(String bookId) {
    try {
      DocumentSnapshot document = booksCollection.document(bookId).get().get();

      return documentToBook(document);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return null;
  }

  @Override
  public void updateBook(Book book) {
    DocumentReference document = booksCollection.document(book.getId());
    Map<String, Object> data = Maps.newHashMap();

    data.put(Book.AUTHOR, book.getAuthor());
    data.put(Book.DESCRIPTION, book.getDescription());
    data.put(Book.PUBLISHED_DATE, book.getPublishedDate());
    data.put(Book.TITLE, book.getTitle());
    data.put(Book.IMAGE_URL, book.getImageUrl());
    data.put(Book.CREATED_BY, book.getCreatedBy());
    data.put(Book.CREATED_BY_ID, book.getCreatedById());
    try {
      document.set(data).get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void deleteBook(String bookId) {
    try {
      booksCollection.document(bookId).delete().get();
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
  }

  private List<Book> documentsToBooks(List<QueryDocumentSnapshot> documents) {
    List<Book> resultBooks = new ArrayList<>();
    for (QueryDocumentSnapshot snapshot : documents) {
      resultBooks.add(documentToBook(snapshot));
    }
    return resultBooks;
  }

  @Override
  public Result<Book> listBooks(String startTitle) {
    Query booksQuery = booksCollection.orderBy("title").limit(10);
    if (startTitle != null) {
      booksQuery = booksQuery.startAfter(startTitle);
    }
    try {
      QuerySnapshot snapshot = booksQuery.get().get();
      List<Book> results = documentsToBooks(snapshot.getDocuments());
      String newCursor = null;
      if (results.size() > 0) {
        newCursor = results.get(results.size() - 1).getTitle();
      }
      return new Result<>(results, newCursor);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return new Result<>(Lists.newArrayList(), null);
  }

  @Override
  public Result<Book> listBooksByUser(String userId, String startTitle) {
    Query booksQuery =
        booksCollection.orderBy("title").whereEqualTo(Book.CREATED_BY_ID, userId).limit(10);
    if (startTitle != null) {
      booksQuery = booksQuery.startAfter(startTitle);
    }
    try {
      QuerySnapshot snapshot = booksQuery.get().get();
      List<Book> results = documentsToBooks(snapshot.getDocuments());
      String newCursor = null;
      if (results.size() > 0) {
        newCursor = results.get(results.size() - 1).getTitle();
      }
      return new Result<>(results, newCursor);
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    return new Result<>(Lists.newArrayList(), null);
  }
}

Firestore の使用方法の詳細については、Firestore にデータを追加するをご覧ください。

Cloud Storage にアップロードしたファイルを保存する

書籍を追加したので、書籍の表紙の画像を追加します。ファイルをインスタンスに保存することはできず、データベースは画像ファイルに適していません。そこで Cloud Storage を使用します。

Cloud Storage は Google Cloud 向けのメインの blob ストアです。Cloud Storage を使用して、Google Cloud で共有するアプリのアセットをホストできます。Cloud Storage を使用するには、データを保存する基本的なコンテナである Cloud Storage バケットを作成する必要があります。

  1. Cloud Console の Cloud Storage ブラウザページに移動します。

    Cloud Storage ブラウザページに移動

  2. [バケットを作成] をクリックします。
  3. [バケットの作成] ダイアログで、Google Cloud のプロジェクト ID を文字列 _bucket に付け加えてバケットの名前を入力して、名前を YOUR_PROJECT_ID_bucket のようにします。この名前はバケット名の要件を満たしている必要があります。他のすべての項目はデフォルト値のままにします。
  4. [作成] をクリックします。
  5. バケットの作成後、ユーザーが閲覧できるようにオブジェクトを一般公開する必要があります。オブジェクトを一般公開するには、データの一般公開をご覧ください。
  6. [Edit book] をクリックして、書籍の表紙としてアップロードする画像を選択します。たとえば、このパブリック ドメインの画像を使用します。
    Moby Dick の書籍の表紙
  7. [Save] をクリックします。Bookshelf アプリのエントリがあるホームページにリダイレクトされます。
    Moby Dick の Bookshelf アプリのエントリ

Bookshelf アプリは、Cloud Storage クライアント ライブラリを使用して、アップロードされたファイルを Cloud Storage に送信します。

public class CloudStorageHelper {

  private final Logger logger = Logger.getLogger(CloudStorageHelper.class.getName());
  private static Storage storage = null;

  static {
    storage = StorageOptions.getDefaultInstance().getService();
  }

  /**
   * Uploads a file to Google Cloud Storage to the bucket specified in the BUCKET_NAME environment
   * variable, appending a timestamp to end of the uploaded filename.
   */
  public String uploadFile(FileItemStream fileStream, final String bucketName)
      throws IOException, ServletException {
    checkFileExtension(fileStream.getName());

    System.out.println("FileStream name: " + fileStream.getName() + "\nBucket name: " + bucketName);

    DateTimeFormatter dtf = DateTimeFormat.forPattern("-YYYY-MM-dd-HHmmssSSS");
    DateTime dt = DateTime.now(DateTimeZone.UTC);
    String dtString = dt.toString(dtf);
    final String fileName = fileStream.getName() + dtString;

    // the inputstream is closed by default, so we don't need to close it here
    @SuppressWarnings("deprecation")
    BlobInfo blobInfo =
        storage.create(
            BlobInfo.newBuilder(bucketName, fileName)
                // Modify access list to allow all users with link to read file
                .setAcl(new ArrayList<>(Arrays.asList(Acl.of(User.ofAllUsers(), Role.READER))))
                .build(),
            fileStream.openStream());
    logger.log(
        Level.INFO, "Uploaded file {0} as {1}", new Object[] {fileStream.getName(), fileName});
    // return the public download link
    return blobInfo.getMediaLink();
  }

  /** Checks that the file extension is supported. */
  private void checkFileExtension(String fileName) throws ServletException {
    if (fileName != null && !fileName.isEmpty() && fileName.contains(".")) {
      String[] allowedExt = {".jpg", ".jpeg", ".png", ".gif"};
      for (String ext : allowedExt) {
        if (fileName.endsWith(ext)) {
          return;
        }
      }
      throw new ServletException("file must be an image");
    }
  }
}

Cloud Storage の使用方法の詳細については、入門ガイドのリストをご覧ください。

Google Cloud のオペレーション スイートを使用してアプリをモニタリングする

アプリをデプロイし、書籍を作成して変更を加えました。ユーザーのためにこのようなイベントをモニタリングするには、アプリケーション パフォーマンス管理を使用します。

Cloud Logging でログをモニタリングする

  1. Google Cloud で、[ログ エクスプローラ] に移動します。

    [ログ エクスプローラ] に移動

    アプリをリアルタイムでモニタリングできます。アプリに問題が発生した場合は、まずこちらの画面を確認してください。

    Stackdriver ログビューア
  2. [リソース] プルダウン リストで、[Cloud Run のリビジョン, bookshelf] を選択します。

Error Reporting でエラーをモニタリングする

  1. Cloud Console の [Error Reporting] ページに移動します。
    [Error Reporting] ページに移動
    Error Reporting では、アプリ内のエラーと例外が強調表示され、そのアラートを設定できます。
  2. ブラウザで、アプリの /errors URL にアクセスします。
    YOUR_CODE_RUN_URL/errors

    これにより、新しいテストの例外が生成され、Google Cloud のオペレーション スイートに送信されます。

  3. Cloud Console の [Error Reporting] ページに戻り、しばらくすると新しいエラーが表示されます。[自動再読み込み] をクリックすると、ページを手動で更新する必要がなくなります。

    Error Reporting のエラー メッセージ。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

プロジェクトの削除

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

次のステップ