GSP644
概要
リリーさんは 12 年前、獣医クリニック チェーン「Pet Theory」を開業しました。現在、Pet Theory では請求書を DOCX 形式でクライアントに送っていますが、ファイルを開けないという苦情が多く寄せられています。クライアント満足度の向上に向けて現状を打開するため、Lily さんは IT 部門の Patrick さんに代替案の調査を依頼しました。
運用チームのメンバーが 1 人しかいないため、望ましいのは運用中のメンテナンスに手間がかからないコスト効率の良いソリューションです。さまざまな選択肢を検討した結果、Patrick さんは Cloud Run を使用することにしました。
Cloud Run はサーバーレスであるため、すべてのインフラストラクチャ管理を抽象化し、オーバーヘッドを心配せずにアプリケーションのビルドに集中できます。しかも、Google サーバーレス プロダクトとして、使用しない場合はゼロにスケールできコストが発生しません。また、コンテナに基づくカスタム バイナリ パッケージを使用できるため、一貫性のある分離されたアーティファクトをビルドできるようになりました。
このラボでは、Cloud Storage に保存されているファイルを PDF に自動的に変換して別のフォルダに保存する、PDF コンバータ ウェブアプリを Cloud Run 上でビルドします。
アーキテクチャ
この図は、使用するサービスの概要と、それらがどのようにつながっているかを示しています。

目標
このラボでは、次の方法について学びます。
- Node JS アプリケーションをコンテナに変換する。
- Google Cloud Build でコンテナをビルドする。
- クラウドでファイルを PDF ファイルに変換する Cloud Run サービスを作成する。
- Cloud Storage でイベント処理を使用する。
前提条件
これは中級レベルのラボです。コンソール環境とシェル環境に精通していることを前提としています。Firebase の使用経験は役立ちますが、必須ではありません。このラボを受講する前に、次の Google Skills ラボを完了しておくことをおすすめします。
準備ができたら下にスクロールし、手順に沿ってラボ環境を設定します。
設定と要件
[ラボを開始] ボタンをクリックする前に
こちらの説明をお読みください。ラボには時間制限があり、一時停止することはできません。タイマーは、Google Cloud のリソースを利用できる時間を示しており、[ラボを開始] をクリックするとスタートします。
このハンズオンラボでは、シミュレーションやデモ環境ではなく実際のクラウド環境を使って、ラボのアクティビティを行います。そのため、ラボの受講中に Google Cloud にログインおよびアクセスするための、新しい一時的な認証情報が提供されます。
このラボを完了するためには、下記が必要です。
- 標準的なインターネット ブラウザ(Chrome を推奨)
注: このラボの実行には、シークレット モード(推奨)またはシークレット ブラウジング ウィンドウを使用してください。これにより、個人アカウントと受講者アカウント間の競合を防ぎ、個人アカウントに追加料金が発生しないようにすることができます。
- ラボを完了するための時間(開始後は一時停止できません)
注: このラボでは、受講者アカウントのみを使用してください。別の Google Cloud アカウントを使用すると、そのアカウントに料金が発生する可能性があります。
ラボを開始して Google Cloud コンソールにログインする方法
-
[ラボを開始] ボタンをクリックします。ラボの料金をお支払いいただく必要がある場合は、表示されるダイアログでお支払い方法を選択してください。
左側の [ラボの詳細] ペインには、以下が表示されます。
- [Google Cloud コンソールを開く] ボタン
- 残り時間
- このラボで使用する必要がある一時的な認証情報
- このラボを行うために必要なその他の情報(ある場合)
-
[Google Cloud コンソールを開く] をクリックします(Chrome ブラウザを使用している場合は、右クリックして [シークレット ウィンドウで開く] を選択します)。
ラボでリソースがスピンアップし、別のタブで [ログイン] ページが表示されます。
ヒント: タブをそれぞれ別のウィンドウで開き、並べて表示しておきましょう。
注: [アカウントの選択] ダイアログが表示されたら、[別のアカウントを使用] をクリックします。
-
必要に応じて、下のユーザー名をコピーして、[ログイン] ダイアログに貼り付けます。
{{{user_0.username | "Username"}}}
[ラボの詳細] ペインでもユーザー名を確認できます。
-
[次へ] をクリックします。
-
以下のパスワードをコピーして、[ようこそ] ダイアログに貼り付けます。
{{{user_0.password | "Password"}}}
[ラボの詳細] ペインでもパスワードを確認できます。
-
[次へ] をクリックします。
重要: ラボで提供された認証情報を使用する必要があります。Google Cloud アカウントの認証情報は使用しないでください。
注: このラボでご自身の Google Cloud アカウントを使用すると、追加料金が発生する場合があります。
-
その後次のように進みます。
- 利用規約に同意してください。
- 一時的なアカウントなので、復元オプションや 2 要素認証プロセスは設定しないでください。
- 無料トライアルには登録しないでください。
その後、このタブで Google Cloud コンソールが開きます。
注: Google Cloud のプロダクトやサービスにアクセスするには、ナビゲーション メニューをクリックするか、[検索] フィールドにサービス名またはプロダクト名を入力します。
Cloud Shell をアクティブにする
Cloud Shell は、開発ツールと一緒に読み込まれる仮想マシンです。5 GB の永続ホーム ディレクトリが用意されており、Google Cloud で稼働します。Cloud Shell を使用すると、コマンドラインで Google Cloud リソースにアクセスできます。
-
Google Cloud コンソールの上部にある「Cloud Shell をアクティブにする」アイコン
をクリックします。
-
ウィンドウで次の操作を行います。
- Cloud Shell 情報ウィンドウで操作を進めます。
- Cloud Shell が認証情報を使用して Google Cloud API を呼び出すことを承認します。
接続した時点で認証が完了しており、プロジェクトに各自の Project_ID、 が設定されます。出力には、このセッションの PROJECT_ID を宣言する次の行が含まれています。
Your Cloud Platform project in this session is set to {{{project_0.project_id | "PROJECT_ID"}}}
gcloud は Google Cloud のコマンドライン ツールです。このツールは、Cloud Shell にプリインストールされており、タブ補完がサポートされています。
- (省略可)次のコマンドを使用すると、有効なアカウント名を一覧表示できます。
gcloud auth list
- [承認] をクリックします。
出力:
ACTIVE: *
ACCOUNT: {{{user_0.username | "ACCOUNT"}}}
To set the active account, run:
$ gcloud config set account `ACCOUNT`
- (省略可)次のコマンドを使用すると、プロジェクト ID を一覧表示できます。
gcloud config list project
出力:
[core]
project = {{{project_0.project_id | "PROJECT_ID"}}}
注: Google Cloud における gcloud ドキュメントの全文については、gcloud CLI の概要ガイドをご覧ください。
タスク 1. タスクについて
Pet theory では、PDF に変換された請求書をクライアントが確実に開けるようにしたいと考えています。さらに、チームはこの変換を自動化して、オフィス マネージャーである Lisa さんの作業負荷を最小限に抑えたいと考えています。
Pet Theory のコンピュータ コンサルタントである Ruby さんは、IT 部門の Patrick さんから次のメッセージを受け取りました。
|

Patrick さん(IT 管理者)
|
Ruby さん、お世話になっております。
こちらで調べてみたところ、さまざまなファイル形式を PDF に変換するには LibreOffice が良さそうです。
サーバーのメンテナンスを行う必要なく、クラウドで LibreOffice を実行することは可能でしょうか?
Patrick
|
|
Ruby さん(ソフトウェア コンサルタント)
|
Patrick さん、ご連絡ありがとうございます。
そのようなご要望であれば、ぜひおすすめしたいものがあります。
Next 19 で発表された Cloud Run です。YouTube の動画で視聴したのですが、サーバーレス環境で Cloud Run を使用して LibreOffice を実行できるようです。サーバーのメンテナンスも不要です。
設定に役立つリソースをいくつかお送りしますので、ご確認のほどよろしくお願いいたします。
Ruby
|
Patrick さんが Cloud Run の設定とデプロイを行えるようサポートしましょう。
タスク 2. Cloud Run API を有効にする
-
ナビゲーション メニュー(
)を開き、[API とサービス] > [ライブラリ] をクリックします。検索バーに「Cloud Run」と入力し、検索結果のリストで Cloud Run Admin API を選択します。
-
API が有効になっていない場合は、[有効にする] をクリックしてから、ブラウザの [戻る] ボタンを 2 回クリックします。コンソールは次のようになります。

タスク 3. シンプルな Cloud Run サービスをデプロイする
Ruby さんは開発した Cloud Run プロトタイプを、Patrick さんに Google Cloud にデプロイしてもらいたいと思っています。ここで、Patrick さんが Pet Theory の PDF Cloud Run サービスを確立するのをサポートしましょう。
- 新しい Cloud Shell セッションを開き、次のコマンドを実行して Pet Theory のリポジトリのクローンを作成します。
git clone https://github.com/rosera/pet-theory.git
- 次に、現在の作業ディレクトリを lab03 に変更します。
cd pet-theory/lab03
- Cloud Shell コードエディタまたは任意のテキスト エディタで
package.json を編集します。"scripts" セクションに以下のように "start": "node index.js", を追加します。
...
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
...
- 次に、Cloud Shell で次のコマンドを実行して、変換スクリプトで使用するパッケージをインストールします。
npm install express
npm install body-parser
npm install child_process
npm install @google-cloud/storage
- 次に、
lab03/index.js ファイルを開いてコードを確認します。
アプリケーションは、HTTP POST を受け入れる Cloud Run サービスとしてデプロイされます。POST リクエストがアップロードされたファイルに関する Pub/Sub 通知である場合は、サービスによりファイルの詳細がログに書き込まれます。そうでない場合は、サービスにより単に文字列「OK」が返されます。
-
lab03/Dockerfile というファイルを確認します。
上記のファイルはマニフェストと呼ばれ、Docker コマンドがイメージをビルドするためのレシピを提供します。各行は、Docker に次の情報の処理方法を指示するコマンドで始まります。
- 最初のリストは、ベースイメージが、作成されるイメージのテンプレートとしてノードを使用する必要があることを示しています。
- 最後の行は、実行するコマンドを示しています。この例では、"npm start" を参照しています。
- REST API をビルドしてデプロイするには、Google Cloud Build を使用します。次のコマンドを実行して、ビルドプロセスを開始します。
gcloud builds submit \
--tag gcr.io/$GOOGLE_CLOUD_PROJECT/pdf-converter
このコマンドは、コードでコンテナをビルドし、プロジェクトの Artifact Registry にそのコンテナを配置します。
-
Cloud コンソールに戻り、ナビゲーション メニュー(
)で [すべてのプロダクトを表示] をクリックします。[CI / CD] セクションで、[Artifact Registry] > [リポジトリ] を選択します。ホストされているコンテナが表示されます。
-
gcr.io リポジトリを開きます。ホストされているコンテナが表示されます。

完了したタスクをテストする
[進行状況を確認] をクリックして、上記のタスクを実行したことを確認します。
シンプルな REST API をビルドする
- コードエディタのタブに戻り、Cloud Shell で次のコマンドを実行してアプリケーションをデプロイします。
gcloud run deploy pdf-converter \
--image gcr.io/$GOOGLE_CLOUD_PROJECT/pdf-converter \
--platform managed \
--region {{{ project_0.default_region | Region }}} \
--no-allow-unauthenticated \
--max-instances=1
デプロイが完了すると、次のようなメッセージが表示されます。
Service [pdf-converter] revision [pdf-converter-00001] has been deployed and is serving 100 percent of traffic at https://pdf-converter-[hash].a.run.app
- アプリに簡単にアクセスできるように、アプリ用の環境変数
$SERVICE_URL を作成します。
SERVICE_URL=$(gcloud beta run services describe pdf-converter --platform managed --region {{{ project_0.default_region | Lab Region }}} --format="value(status.url)")
echo $SERVICE_URL
完了したタスクをテストする
[進行状況を確認] をクリックして、上記のタスクを実行したことを確認します。
Cloud Run のリビジョンを作成する
- 新しいサービスに匿名の POST リクエストを行います。
curl -X POST $SERVICE_URL
これにより、「Your client does not have permission to get the URL」というエラー メッセージが表示されます。これは「匿名ユーザーはサービスを呼び出せない」ということですので、表示されても問題ありません。
- 次に、承認されたユーザーとしてサービスを呼び出してみます。
curl -X POST -H "Authorization: Bearer $(gcloud auth print-identity-token)" $SERVICE_URL
「OK」という応答が得られれば、Cloud Run サービスは正常にデプロイされています。このタスクは以上で完了です。
タスク 4. 新しいファイルがアップロードされたときに Cloud Run サービスをトリガーする
Cloud Run サービスが正常にデプロイされたので、Ruby さんは Patrick さんにデータを変換するためのステージング領域を作成してもらうことにします。Cloud Storage バケットは、イベント トリガーを使用して、ファイルがアップロードされて処理が必要になったときにアプリケーションに通知します。
- 次のコマンドを実行して、アップロードされたドキュメント用のバケットを Cloud Storage に作成します。
gsutil mb gs://$GOOGLE_CLOUD_PROJECT-upload
- 変換後の PDF に使用する別のバケットを次のように作成します。
gsutil mb gs://$GOOGLE_CLOUD_PROJECT-processed
- 次に Cloud コンソールのタブに戻り、ナビゲーション メニューを開いて、[Cloud Storage] > [バケット] を選択します。バケットが作成されたことを確認します(プラットフォームによって使用される他のバケットも確認できます)。
完了したタスクをテストする
[進行状況を確認] をクリックして、上記のタスクを実行したことを確認します。
2 つの Cloud Storage バケットを作成する
- Cloud Shell で次のコマンドを実行して、新しいファイルがドキュメント バケットへのアップロードを完了するたびに、Pub/Sub 通知を送信するように Cloud Storage に指示します。
gsutil notification create -t new-doc -f json -e OBJECT_FINALIZE gs://$GOOGLE_CLOUD_PROJECT-upload
通知にはトピック「new-doc」というラベルが付けられます。
完了したタスクをテストする
[進行状況を確認] をクリックして、上記のタスクを実行したことを確認します。
ストレージ バケットからの通知を処理するための Pub/Sub トピックを作成する
- Pub/Sub が Cloud Run サービスをトリガーするために使用する新しいサービス アカウントを作成します。
gcloud iam service-accounts create pubsub-cloud-run-invoker --display-name "PubSub Cloud Run Invoker"
- 新しいサービス アカウントに PDF コンバータ サービスを呼び出す権限を付与します。
gcloud beta run services add-iam-policy-binding pdf-converter --member=serviceAccount:pubsub-cloud-run-invoker@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com --role=roles/run.invoker --platform managed --region {{{ project_0.default_region | Lab Region }}}
- 次のコマンドを実行して、プロジェクト番号を見つけます。
gcloud projects list
名前が「qwiklabs-gcp-」で始まるプロジェクトを探します。プロジェクト番号の値は次のコマンドで使用します。

-
PROJECT_NUMBER 環境変数を作成します。[project number] は直前のコマンドで取得したプロジェクト番号に置き換えます。
PROJECT_NUMBER=[project number]
- プロジェクトで Cloud Pub/Sub 認証トークンを作成できるようにします。
gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT --member=serviceAccount:service-$PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com --role=roles/iam.serviceAccountTokenCreator
注:
上記のコマンドの実行時に「サービス アカウントが存在しない」といったエラーが表示された場合は、Cloud Pub/Sub API を有効にします。すでに有効になっている場合は、いったん無効にしてから再度有効にします。その後、上記のコマンドを再実行してください。
- 最後に、Pub/Sub サブスクリプションを作成して、トピック「new-doc」でメッセージが公開されるときはいつでも PDF コンバータを実行できるようにします。
gcloud beta pubsub subscriptions create pdf-conv-sub --topic new-doc --push-endpoint=$SERVICE_URL --push-auth-service-account=pubsub-cloud-run-invoker@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
完了したタスクをテストする
[進行状況を確認] をクリックして、上記のタスクを実行したことを確認します。
Pub/Sub サブスクリプションを作成する
タスク 5. ファイルが Cloud Storage にアップロードされたときに Cloud Run サービスがトリガーされるかを確認する
Ruby さんは、アプリケーションが期待どおりに動作していることを確認するために、名前を付けたストレージ バケットにテストデータをアップロードして、Cloud Logging を確認するよう Patrick さんに依頼します。
- テストファイルをアップロード バケットにコピーします。
gsutil -m cp gs://spls/gsp644/* gs://$GOOGLE_CLOUD_PROJECT-upload
-
アップロードが完了したら、Cloud コンソールのタブに戻り、ナビゲーション メニューを開いて、[すべてのプロダクトを表示] をクリックします。[オブザーバビリティ] セクションで [ロギング] を選択します。
-
[すべてのリソース] プルダウンで、[Cloud Run のリビジョン] を指定して結果をフィルタし、[適用] をクリックします。[クエリを実行] をクリックします。
-
[クエリ結果] で、file: で始まるログエントリを探してクリックします。新しいファイルがアップロードされると、Pub/Sub が Cloud Run サービスに送信するファイルデータのダンプが表示されます。
-
このオブジェクトでアップロードしたファイルの名前を確認します。

注:
「file」で始まるログエントリが表示されない場合は、ページの下部にある [新しいログを読み込む] ボタンをクリックしてください。
- 次に、コードエディタのタブに戻り、Cloud Shell で次のコマンドを実行して、
upload ディレクトリ内のファイルを削除し、ディレクトリをクリーンアップします。
gsutil -m rm gs://$GOOGLE_CLOUD_PROJECT-upload/*
タスク 6. コンテナ
Patrick さんは、すべてのクライアントが請求書を開けるように請求書のバックログを PDF に変換する必要があります。そこで、メールで Ruby さんにサポートを求めることにしました。
|

Patrick さん(IT 管理者)
|
Ruby さん、お世話になっております。
いただいた情報をもとにこのプロセスを自動化したうえで、PDF を請求書の形式として使用できるようにすることも可能だと思います。
昨日少し時間をかけてソリューションをコーディングし、必要なことを実行するための Node.js スクリプトを作成しました。ご確認いただけますか?
Patrick
|
Patrick さんは、ファイルから PDF を生成するために作成した次のコード フラグメントを Ruby に送信します。
const {promisify} = require('util');
const exec = promisify(require('child_process').exec);
const cmd = 'libreoffice --headless --convert-to pdf --outdir ' +
`/tmp "/tmp/${fileName}"`;
const { stdout, stderr } = await exec(cmd);
if (stderr) {
throw stderr;
}
Ruby さんは Patrick さんに返事を書きます。
|

Ruby さん(ソフトウェア コンサルタント)
|
Patrick さん、ご連絡ありがとうございます。
Cloud Run はコンテナを使用するため、その形式でアプリケーションを用意する必要があります。次のステップとしてアプリケーションの Dockerfile マニフェストを作成してください。
作成したコードでは LibreOffice を使用します。そのソフトウェアをインストールするためのコマンドをお送りいただけますか?それをコンテナに含める必要があります。
Ruby
|
|

Patrick さん(IT 管理者)
|
Ruby さん、お世話になっております。
早速ありがとうございます。LibreOffice をオフィスのサーバーにインストールするのに、通常は以下のコマンドを使っています。
apt-get update -y && apt-get install -y libreoffice && apt-get clean
他にも必要な情報がありましたら、ご連絡ください。
Patrick
|
コンテナをビルドするには、いくつかのコンポーネントを統合する必要があります。

マニフェストを更新する
すべてのファイルが特定されたら、マニフェストを作成できます。Ruby さんがコンテナの設定とデプロイを行うのをサポートしましょう。
LibreOffice のパッケージがコンテナに含まれていなかったので、追加する必要があります。
Patrick さんからアプリケーションのビルドに使用するコマンドが提供されたので、Ruby さんはそのコマンドを Dockerfile 内に RUN コマンドとして追加します。
新しいバージョンの PDF 変換サービスをデプロイする
-
index.js ファイルを開き、ファイルの先頭に次のパッケージ要件を追加します。
const {promisify} = require('util');
const {Storage} = require('@google-cloud/storage');
const exec = promisify(require('child_process').exec);
const storage = new Storage();
-
app.post('/', async (req, res) を次のコードに置き換えます。
app.post('/', async (req, res) => {
try {
const file = decodeBase64Json(req.body.message.data);
await downloadFile(file.bucket, file.name);
const pdfFileName = await convertFile(file.name);
await uploadFile(process.env.PDF_BUCKET, pdfFileName);
await deleteFile(file.bucket, file.name);
}
catch (ex) {
console.log(`Error: ${ex}`);
}
res.set('Content-Type', 'text/plain');
res.send('\n\nOK\n\n');
})
-
次に、LibreOffice ドキュメントを処理する次のコードをファイルの末尾に追加します。
// ファイルの存在を確認するヘルパー関数(非同期に fs.promises を使用)
async function fileExists(filePath) {
try {
await fs.promises.access(filePath); // ファイルが存在しない場合はエラーをスロー
return true;
} catch (err) {
return false;
}
}
async function downloadFile(bucketName, fileName) {
// 1. ファイルが存在するかどうかを確認
const fileExistsLocally = await fileExists(`/tmp/${fileName}`);
// 2. 存在する場合は削除
if (fileExistsLocally) {
console.log(`File exists locally. Deleting: ${fileName}`);
await fs.promises.unlink(`/tmp/${fileName}`); // 非同期のファイル操作に fs.promises を使用
console.log(`File deleted.`);
} else {
console.log(`File does not exist locally: ${fileName}`);
}
// 3. ストレージ バケットからダウンロード
const options = { destination: `/tmp/${fileName}` };
await storage.bucket(bucketName).file(fileName).download(options);
console.log(`File downloaded: ${fileName}`);
}
async function convertFile(fileName) {
const cmd = 'libreoffice --headless --convert-to pdf --outdir /tmp ' +
`"/tmp/${fileName}"`;
console.log(cmd);
const { stdout, stderr } = await exec(cmd);
if (stderr) {
throw stderr;
}
console.log(stdout);
pdfFileName = fileName.replace(/\.\w+$/, '.pdf');
return pdfFileName;
}
async function deleteFile(bucketName, fileName) {
await storage.bucket(bucketName).file(fileName).delete();
}
async function uploadFile(bucketName, fileName) {
await storage.bucket(bucketName).upload(`/tmp/${fileName}`);
}
-
index.js ファイルが次のようになっていることを確認します。
注:
フォーマット エラーを回避するために、index.js ファイル内のすべてのコードをこのサンプルコードに置き換えることをおすすめします。
const {promisify} = require('util');
const {Storage} = require('@google-cloud/storage');
const exec = promisify(require('child_process').exec);
const storage = new Storage();
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log('Listening on port', port);
});
app.post('/', async (req, res) => {
try {
const file = decodeBase64Json(req.body.message.data);
await downloadFile(file.bucket, file.name);
const pdfFileName = await convertFile(file.name);
await uploadFile(process.env.PDF_BUCKET, pdfFileName);
await deleteFile(file.bucket, file.name);
}
catch (ex) {
console.log(`Error: ${ex}`);
}
res.set('Content-Type', 'text/plain');
res.send('\n\nOK\n\n');
})
function decodeBase64Json(data) {
return JSON.parse(Buffer.from(data, 'base64').toString());
}
// ファイルの存在を確認するヘルパー関数(非同期に fs.promises を使用)
async function fileExists(filePath) {
try {
await fs.promises.access(filePath); // ファイルが存在しない場合はエラーをスロー
return true;
} catch (err) {
return false;
}
}
async function downloadFile(bucketName, fileName) {
// 1. ファイルが存在するかどうかを確認
const fileExistsLocally = await fileExists(`/tmp/${fileName}`);
// 2. 存在する場合は削除
if (fileExistsLocally) {
console.log(`File exists locally. Deleting: ${fileName}`);
await fs.promises.unlink(`/tmp/${fileName}`); // 非同期のファイル操作に fs.promises を使用
console.log(`File deleted.`);
} else {
console.log(`File does not exist locally: ${fileName}`);
}
// 3. ストレージ バケットからダウンロード
const options = { destination: `/tmp/${fileName}` };
await storage.bucket(bucketName).file(fileName).download(options);
console.log(`File downloaded: ${fileName}`);
}
async function convertFile(fileName) {
const cmd = 'libreoffice --headless --convert-to pdf --outdir /tmp ' +
`"/tmp/${fileName}"`;
console.log(cmd);
const { stdout, stderr } = await exec(cmd);
if (stderr) {
console.log(`Conversion Failed: ${stderr}`);
throw stderr;
}
console.log(`Conversion Success: ${stdout}`);
pdfFileName = fileName.replace(/\.\w+$/, '.pdf');
return pdfFileName;
}
async function deleteFile(bucketName, fileName) {
await storage.bucket(bucketName).file(fileName).delete();
}
async function uploadFile(bucketName, fileName) {
await storage.bucket(bucketName).upload(`/tmp/${fileName}`);
}
-
メインロジックは次の関数に含まれています。
const file = decodeBase64Json(req.body.message.data);
await downloadFile(file.bucket, file.name);
const pdfFileName = await convertFile(file.name);
await uploadFile(process.env.PDF_BUCKET, pdfFileName);
await deleteFile(file.bucket, file.name);
ファイルがアップロードされるときはいつでも、このサービスがトリガーされます。上の関数はそれぞれ、次のタスクを実行します(各行 1 タスクずつ)。
- Pub/Sub 通知からファイルの詳細を抽出します。
- Cloud Storage からローカルのハードドライブにファイルをダウンロードします。これは実際には物理ディスクではなく、ディスクのように動作する仮想メモリのセクションです。
- ダウンロードしたファイルを PDF に変換します。
- PDF ファイルを Cloud Storage にアップロードします。環境変数
process.env.PDF_BUCKET には、PDF を書き込む Cloud Storage バケットの名前が含まれています。後述するサービスをデプロイする際、この変数に値を割り当てます。
- 元のファイルを Cloud Storage から削除します。
index.js の残りの部分では、このトップレベルのコードによって呼び出される関数を実装します。
ここでサービスをデプロイし、PDF_BUCKET 環境変数を設定します。LibreOffice の処理用に、2 GB の RAM を指定することもおすすめします(--memory オプションのある行を参照)。
-
次のコマンドを実行してコンテナをビルドします。
gcloud builds submit \
--tag gcr.io/$GOOGLE_CLOUD_PROJECT/pdf-converter
注:
Cloud Build API を有効にするポップアップが表示された場合は、「Y」と入力します。
完了したタスクをテストする
[進行状況を確認] をクリックして、上記のタスクを実行したことを確認します。
REST API 用の別のビルドを作成する
-
最新バージョンのアプリケーションをデプロイします。
gcloud run deploy pdf-converter \
--image gcr.io/$GOOGLE_CLOUD_PROJECT/pdf-converter \
--platform managed \
--region {{{ project_0.default_region | Lab Region }}} \
--memory=2Gi \
--no-allow-unauthenticated \
--max-instances=1 \
--set-env-vars PDF_BUCKET=$GOOGLE_CLOUD_PROJECT-processed
コンテナに LibreOffice の部分が含まれているため、このビルドは以前のビルドよりも時間がかかります。待ってる間、立ち上がってストレッチでもしましょう。
[進行状況を確認] をクリックして、目標に沿って進んでいることを確認します。
新しいリビジョンを作成する
タスク 7. PDF 変換サービスをテストする
-
デプロイ コマンドが完了したら、次のコマンドを実行して、サービスが正しくデプロイされたことを確認します。
curl -X POST -H "Authorization: Bearer $(gcloud auth print-identity-token)" $SERVICE_URL
-
「OK」という応答が得られれば、更新された Cloud Run サービスは正常にデプロイされています。LibreOffice は、DOCX、XLSX、JPG、PNG、GIF などの多くのファイル形式を PDF に変換できます。
-
アップロードを実行するスクリプトを作成します。
cat <<'EOF' > copy_files.sh
#!/bin/bash
SOURCE_BUCKET="gs://spls/gsp644"
DESTINATION_BUCKET="gs://${GOOGLE_CLOUD_PROJECT}-upload" # 実際のバケット名に置き換える
DELAY=5
# ソースバケット内のファイルの一覧を取得
files=$(gsutil ls "$SOURCE_BUCKET")
# ファイルをループ処理
for file in $files; do
# ソースファイルの完全パスを構築
source_file_path="$file"
# ファイルを宛先バケットにコピー
gsutil cp "$source_file_path" "$DESTINATION_BUCKET"
# コピーが成功したかどうかを確認
if [ $? -eq 0 ]; then # $? は前のコマンドの終了ステータス
echo "Copied: $source_file_path to $DESTINATION_BUCKET"
else
echo "Failed to copy: $source_file_path"
fi
# 5 秒間スリープ
sleep $DELAY
done
echo "All files copied!"
EOF
-
次のコマンドを実行して、いくつかのサンプル ファイルをアップロードします。
bash copy_files.sh
-
Cloud コンソールに戻り、ナビゲーション メニューを開いて [Cloud Storage] > [バケット] を選択します。-upload バケットを開き、[更新] ボタンを数回クリックして、ファイルが PDF に変換されるときに、ファイルが 1 つずつ削除される様子を確認します。
-
次に、左側のメニューから [バケット] をクリックし、名前が「-processed」で終わるバケットをクリックします。そのバケットにはすべてのファイルの PDF バージョンが含まれています。PDF ファイルを開いて、適切に変換されていることを確認してください。
注:
すべての変換済み PDF ファイルが -processed バケット内に揃っていない場合は、コマンドを再実行してください。お疲れさまでした
Pet Theory で、元のファイルのアーカイブを PDF に変換するシステムが確立されました。元のファイルを「upload」バケットにアップロードするだけで、PDF コンバータ サービスによりファイルが変換され、「processed」バケットに PDF として書き込まれます。
「Serverless Cloud Run Development」コースで、サーバーレスに関する学習を続けてください。架空のビジネス シナリオの登場人物を支援してサーバーレスへの移行計画を進めていきます。
Google Cloud トレーニングと認定資格
Google Cloud トレーニングと認定資格を通して、Google Cloud 技術を最大限に活用できるようになります。必要な技術スキルとベスト プラクティスについて取り扱うクラスでは、学習を継続的に進めることができます。トレーニングは基礎レベルから上級レベルまであり、オンデマンド、ライブ、バーチャル参加など、多忙なスケジュールにも対応できるオプションが用意されています。認定資格を取得することで、Google Cloud テクノロジーに関するスキルと知識を証明できます。
マニュアルの最終更新日: 2025 年 12 月 31 日
ラボの最終テスト日: 2025 年 12 月 31 日
Copyright 2026 Google LLC. All rights reserved. Google および Google のロゴは Google LLC の商標です。その他すべての企業名および商品名はそれぞれ各社の商標または登録商標です。