12/08 Google Cloud Run

2020/12/19に公開

この記事は、Serverless Hello World Advent Calendar 2020の8日目です。

Google Cloud RunでHTTP APIを実装します。
クイックスタート: ビルドとデプロイを参考にすすめていきます。

アプリケーションの作成

$ mkdir severless-helloworld-cloudrun
$ cd severless-helloworld-cloudrun
$ npm init
npm init                                                <1:52>
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (severless-helloworld-cloudrun) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /home/masa/work/serverless-helloworld/07/severless-helloworld-cloudrun/package.json:

{
  "name": "severless-helloworld-cloudrun",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) 
$ npm i express
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN severless-helloworld-cloudrun@1.0.0 No description
npm WARN severless-helloworld-cloudrun@1.0.0 No repository field.

+ express@4.17.1
added 50 packages from 37 contributors and audited 50 packages in 3.38s
found 0 vulnerabilities
index.js
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  const { name = 'World' } = req.query
  res.send(`Hello ${name}!`);
});

const port = process.env.PORT || 8080;
app.listen(port, () => {
  console.log(`helloworld: listening on port ${port}`);
});

コンテナ化

Dockerfile.dockerignoreを用意して、docker buildします。

Dockefile
FROM node:12-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
CMD [ "node", "index.js" ]
.dockerignore
Dockerfile
.dockerignore
node_modules
npm-debug.log
$ docker build . -t nekoruri/severless-helloworld-cloudrun
[+] Building 2.2s (10/10) FINISHED                                                                                                             
 => [internal] load build definition from Dockerfile                                                                                      0.0s
 => => transferring dockerfile: 38B                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                         0.0s
 => => transferring context: 34B                                                                                                          0.0s
 => [internal] load metadata for docker.io/library/node:12-slim                                                                           2.0s
 => [internal] load build context                                                                                                         0.0s
 => => transferring context: 99B                                                                                                          0.0s
 => [1/5] FROM docker.io/library/node:12-slim@sha256:4b562ee4fca96f0f762f489e61cdf7f89c0b5746928d9c374b7ffba3a1944dc5                     0.0s
 => CACHED [2/5] WORKDIR /usr/src/app                                                                                                     0.0s
 => CACHED [3/5] COPY package*.json ./                                                                                                    0.0s
 => CACHED [4/5] RUN npm install --only=production                                                                                        0.0s
 => CACHED [5/5] COPY . ./                                                                                                                0.0s
 => exporting to image                                                                                                                    0.0s
 => => exporting layers                                                                                                                   0.0s
 => => writing image sha256:36c5d161ca45d604d6ce2b05c887b562ba8867bf3320a284f3891cd51f0d101a                                              0.0s
 => => naming to docker.io/nekoruri/severless-helloworld-cloudrun                                   

ローカルで実行確認

コンテナを起動してcURLで叩いてみます。

$ docker run -d -p 8080:8080 nekoruri/severless-helloworld-cloudrun
$ curl -v 'http://localhost:8080/?name=world'
*   Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /?name=world HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 12
< ETag: W/"c-00hq6RNueFa8QiEjhep5cJRHWAI"
< Date: Fri, 18 Dec 2020 17:10:23 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< 
* Connection #0 to host localhost left intact
Hello world!%

コンテナイメージのビルド

gloud buildsでコンテナイメージをCloud build上でビルドし、Container Registryに保存します。

$ gcloud builds submit --tag gcr.io/$(gcloud config get-value project)/helloworld
Creating temporary tarball archive of 331 file(s) totalling 1.6 MiB before compression.
Uploading tarball of [.] to [gs://helloworld-297620_cloudbuild/source/1608311903.149551-d32ac04771814cea99e4bbfc5fcee5bf.tgz]
Created [https://cloudbuild.googleapis.com/v1/projects/helloworld-297620/builds/3ed17100-7bb2-44d8-963e-2d6dbb9ca59b].
Logs are available at [https://console.cloud.google.com/cloud-build/builds/3ed17100-7bb2-44d8-963e-2d6dbb9ca59b?project=211935878594].
------------------------------------------------------------- REMOTE BUILD OUTPUT -------------------------------------------------------------
starting build "3ed17100-7bb2-44d8-963e-2d6dbb9ca59b"

FETCHSOURCE
Fetching storage object: gs://helloworld-297620_cloudbuild/source/1608311903.149551-d32ac04771814cea99e4bbfc5fcee5bf.tgz#1608311902769496
Copying gs://helloworld-297620_cloudbuild/source/1608311903.149551-d32ac04771814cea99e4bbfc5fcee5bf.tgz#1608311902769496...
/ [1 files][522.2 KiB/522.2 KiB]                                                
Operation completed over 1 objects/522.2 KiB.                                    
BUILD
Already have image (with digest): gcr.io/cloud-builders/docker
Sending build context to Docker daemon  19.97kB
Step 1/6 : FROM node:12-slim
12-slim: Pulling from library/node
e50c3c9ef5a2: Pulling fs layer
7d035f3b6068: Pulling fs layer
5d71f0da6484: Pulling fs layer
a264fbab4a3a: Pulling fs layer
5108ca6b9c63: Pulling fs layer
a264fbab4a3a: Waiting
5108ca6b9c63: Waiting
7d035f3b6068: Verifying Checksum
7d035f3b6068: Download complete
a264fbab4a3a: Verifying Checksum
a264fbab4a3a: Download complete
5108ca6b9c63: Verifying Checksum
5108ca6b9c63: Download complete
e50c3c9ef5a2: Verifying Checksum
e50c3c9ef5a2: Download complete
5d71f0da6484: Verifying Checksum
5d71f0da6484: Download complete
e50c3c9ef5a2: Pull complete
7d035f3b6068: Pull complete
5d71f0da6484: Pull complete
a264fbab4a3a: Pull complete
5108ca6b9c63: Pull complete
Digest: sha256:4b562ee4fca96f0f762f489e61cdf7f89c0b5746928d9c374b7ffba3a1944dc5
Status: Downloaded newer image for node:12-slim
 ---> 9f75429649ae
Step 2/6 : WORKDIR /usr/src/app
 ---> Running in 967fe48cca8f
Removing intermediate container 967fe48cca8f
 ---> 89a83b2e3060
Step 3/6 : COPY package*.json ./
 ---> e03787facd60
Step 4/6 : RUN npm install --only=production
 ---> Running in f40e08478359
npm WARN severless-helloworld-cloudrun@1.0.0 No description
npm WARN severless-helloworld-cloudrun@1.0.0 No repository field.

added 50 packages from 37 contributors and audited 50 packages in 1.875s
found 0 vulnerabilities
Removing intermediate container f40e08478359
 ---> 735a7f1b3a75
Step 5/6 : COPY . ./
 ---> 6cddc80bd764
Step 6/6 : CMD [ "node", "index.js" ]
 ---> Running in 6038364cb1c1
Removing intermediate container 6038364cb1c1
 ---> 0225c53bd9ae
Successfully built 0225c53bd9ae
Successfully tagged gcr.io/helloworld-297620/helloworld:latest
PUSH
Pushing gcr.io/helloworld-297620/helloworld
The push refers to repository [gcr.io/helloworld-297620/helloworld]
6d4168a0b1f5: Preparing
9dafb23f2e74: Preparing
8612ccf117e0: Preparing
046c7a5214ca: Preparing
30514665193f: Preparing
49ad56da7e6f: Preparing
2c317e148151: Preparing
79c320b5a45c: Preparing
e4b1e8d0745b: Preparing
49ad56da7e6f: Waiting
2c317e148151: Waiting
79c320b5a45c: Waiting
e4b1e8d0745b: Waiting
30514665193f: Layer already exists
49ad56da7e6f: Layer already exists
2c317e148151: Layer already exists
79c320b5a45c: Layer already exists
e4b1e8d0745b: Layer already exists
046c7a5214ca: Pushed
6d4168a0b1f5: Pushed
8612ccf117e0: Pushed
9dafb23f2e74: Pushed
latest: digest: sha256:d789108b015ded0cf7836962a217ea80e8d916870ca2615f7415056f541506b5 size: 2201
DONE
-----------------------------------------------------------------------------------------------------------------------------------------------

ID                                    CREATE_TIME                DURATION  SOURCE                                                                                           IMAGES                                         STATUS
3ed17100-7bb2-44d8-963e-2d6dbb9ca59b  2020-12-18T17:18:23+00:00  25S       gs://helloworld-297620_cloudbuild/source/1608311903.149551-d32ac04771814cea99e4bbfc5fcee5bf.tgz  gcr.io/helloworld-297620/helloworld (+1 more)  SUCCESS

デプロイ

Cloud Runにデプロイします。

$ gcloud run deploy --image gcr.io/$(gcloud config get-value project)/helloworld --platform managed
Service name (helloworld):  
API [run.googleapis.com] not enabled on project [211935878594]. Would 
you like to enable and retry (this will take a few minutes)? (y/N)?  y

Enabling service [run.googleapis.com] on project [211935878594]...
Operation "operations/acf.fe66fc95-1258-43c2-951a-219a1905d41e" finished successfully.
Please specify a region:
 [1] asia-east1
 [2] asia-east2
 [3] asia-northeast1
 [4] asia-northeast2
 [5] asia-northeast3
 [6] asia-south1
 [7] asia-southeast1
 [8] asia-southeast2
 [9] australia-southeast1
 [10] europe-north1
 [11] europe-west1
 [12] europe-west2
 [13] europe-west3
 [14] europe-west4
 [15] europe-west6
 [16] northamerica-northeast1
 [17] southamerica-east1
 [18] us-central1
 [19] us-east1
 [20] us-east4
 [21] us-west1
 [22] cancel
Please enter your numeric choice:  3

To make this the default region, run `gcloud config set run/region asia-northeast1`.

Allow unauthenticated invocations to [helloworld] (y/N)?  y

Deploying container to Cloud Run service [helloworld] in project [helloworld-297620] region [asia-northeast1]
✓ Deploying new service... Done.                                                                                                              
  ✓ Creating Revision...                                                                                                                      
  ✓ Routing traffic...                                                                                                                        
  ✓ Setting IAM Policy...                                                                                                                     
Done.                                                                                                                                         
Service [helloworld] revision [helloworld-00001-qat] has been deployed and is serving 100 percent of traffic.                                 
Service URL: https://helloworld-ljzijb4fpq-an.a.run.app

動作確認

ではさっそく叩いてみます。

$ curl -v 'https://helloworld-ljzijb4fpq-an.a.run.app?name=world'
*   Trying 216.239.36.53:443...
* TCP_NODELAY set
* Connected to helloworld-ljzijb4fpq-an.a.run.app (216.239.36.53) port 443 (#0)

...(省略)...

> GET /?name=world HTTP/2
> Host: helloworld-ljzijb4fpq-an.a.run.app
> user-agent: curl/7.68.0
> accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200 
< x-powered-by: Express
< content-type: text/html; charset=utf-8
< etag: W/"c-00hq6RNueFa8QiEjhep5cJRHWAI"
< x-cloud-trace-context: 79bb0955b694a0975129203bf98d4c43
< date: Fri, 18 Dec 2020 17:25:49 GMT
< server: Google Frontend
< content-length: 12
< alt-svc: h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
< 
* Connection #0 to host helloworld-ljzijb4fpq-an.a.run.app left intact
Hello world!%

無事動いていることが確認できました。

追記(2020-12-26)

2020-12-16のアップデートで、1コマンドでビルドとデプロイができるようになっていました。

$ gcloud beta run deploy --source . --platform managed
Service name: helloworld
Please specify a region:
 [1] asia-east1
 [2] asia-east2
 [3] asia-northeast1
 [4] asia-northeast2
 [5] asia-northeast3
 [6] asia-south1
 [7] asia-southeast1
 [8] asia-southeast2
 [9] australia-southeast1
 [10] europe-north1
 [11] europe-west1
 [12] europe-west2
 [13] europe-west3
 [14] europe-west4
 [15] europe-west6
 [16] northamerica-northeast1
 [17] southamerica-east1
 [18] us-central1
 [19] us-east1
 [20] us-east4
 [21] us-west1
 [22] cancel
Please enter your numeric choice:  3

To make this the default region, run `gcloud config set run/region asia-northeast1`.

Building using Dockerfile and deploying container to Cloud Run service [helloworld] in project [helloworld-297620] region [asia-northeast1]
✓ Building and deploying... Done.                                                                                                                 
  ✓ Uploading sources...                                                                                                                          
  ✓ Building Container... Logs are available at [https://console.cloud.google.com/cloud-build/builds/3972926d-f31a-413c-be7d-f4c3a36198fc?project=
  211935878594].                                                                                                                                  
  ✓ Creating Revision...                                                                                                                          
  ✓ Routing traffic...                                                                                                                            
Done.                                                                                                                                             
Service [helloworld] revision [helloworld-00002-kam] has been deployed and is serving 100 percent of traffic.
Service URL: https://helloworld-ljzijb4fpq-an.a.run.app

Discussion