GRANT: Hey, Martin,
what are you doing? MARTIN: Hey, Grant. We get a lot of questions
about whether one should use Cloud Functions or Cloud Run. When should one use which one? GRANT: Yeah, let's
show some examples. MARTIN: Sounds great. [MUSIC PLAYING] [SPARKLING SOUNDS] CritterWatch has
come to us and asked, should they use Cloud Functions
or Cloud Run for their service? They build wildlife cameras. So you can put one of their
cameras in your backyard or out in the forest. And whenever an animal passes
in front of the camera, it takes a picture and
uploads it to the cloud. Now, that picture--
those pictures uploaded to the cloud-- they
want machine learning to work on those pictures,
identify what species of animals were in the pictures,
and then insert these sightings into a Firestore database. So, Grant, should they use
Cloud Functions or Cloud Run? What do you think? GRANT: Well, looking
at the selection grid, they probably should
use Cloud Functions. I mean, they could
use Cloud Run. But it requires a little
bit more configuration. And with sending a Google
Cloud Storage event to Pub/Sub, to Cloud Run, it's
a little bit more complicated, where Cloud
Functions does this automatically. MARTIN: Got it. What would the code
look like for this? GRANT: Now, let's go
dive into VS code. MARTIN: Yeah. So here's the code
for the function that analyzes the image. Now, Grant, what happens here in
the beginning of the function? GRANT: Yeah, so we're
going to set up the Vision API and the Firestore API. And then we're going
to go and track a couple of the animals, things
you might see in your backyard, like raccoons, alpacas,
peacocks, dogs, geckos. MARTIN: Wow, your backyard
is a lot more exciting than mine, Grant. GRANT: And then we'll go
and define a function. So this is triggered whenever
a Cloud Storage bucket-- there's a change there. MARTIN: Oh, so
we, as developers, don't call this function. Google Cloud Platform calls it. Is that right? GRANT: Yes, it detects
the event automatically and calls this function. MARTIN: When-- and
the event being-- GRANT: So whenever a
new file is uploaded to this particular bucket. MARTIN: OK, so that is, a
camera out there in the field uploads an image. Got it. And then what does this
function do with the image? GRANT: So then with
the image event, it goes and uses the Cloud
Vision API against that file. And with the result, it
returns a JSON object with a bunch of different labels
of what it finds in the image. So it could be things
that aren't animals. But we only really
care about the animals. MARTIN: Oh, that's right. It could be a fence, or lawn
furniture, or things like that. But it seems like we extract-- GRANT: Yes, like cars. MARTIN: Car-- right, we extract
only the animal tags here. Cool. And then it looks
like down on line 30, we do something with that
extracted animal tag. GRANT: Yes, and
so then we check. If it's tracked
animal, then we upload to the Firestore database,
in the collections, with the animal name
and then with our data. MARTIN: OK, cool, so
that's a one liner that just creates the
record in the database. Love that. OK, cool, the code looks like
it's doing the right thing, I think. But who knows? Does it actually
work in real life? GRANT: Million dollar question. MARTIN: Another one, yes. GRANT: Let's deploy it. OK, so we have our handy
dandy deploy script. MARTIN: Oh, I love
those deploy scripts. So this is-- you could have
typed out the full command. But it's often easier
to have a deploy script. Also, this is kind of
the script that you would put at the end of
your CI/CD pipeline, right? Typically, you
wouldn't type it in. But your CI/CD system would-- whenever there's an
update to the main branch, it would probably run
something like this. And I see you pulled it up. What happens here on
these lines, Grant? GRANT: Yeah, so we're going to
use gcloud functions deploy, providing our function name. MARTIN: Mm-hmm. GRANT: And then we're
going to specify a runtime. In this case, we're
using nodejs10. We could provide Python
3.7 or Ruby or even .NET. And then we're going to
specify a couple parameters for the trigger. We're going to trigger
whenever a new file is uploaded to the CritterWatcher's
bucket, and only trigger this whenever the
object is finally uploaded. MARTIN: So finalized
means the upload is done. Is that right? GRANT: Yes. MARTIN: OK, cool. In the terminal layer, it looks
like your deployment script finished. Now, how do we
exercise this code? How do we make it run to see if
it actually inserts something into Firestore? GRANT: Well, we can go and
trigger the function just using the browser. MARTIN: Oh, cool, so you will
pretend to be a camera, huh? GRANT: Yes. MARTIN: OK. GRANT: So I mean,
so let's go and add some images to our
camera, into our bucket, and see if our function works. MARTIN: Great. GRANT: So I'm going to just
drag and drop a couple files. MARTIN: Uh-huh, very good. GRANT: And-- MARTIN: Oh, they all uploaded. GRANT: Very nice. MARTIN: All right, so now
for each of these four files, our analyze image
function should have been triggered once. And the analyzed image
inserts into Firestore. So if everything
worked, we should see four records in Firestore. Is that right? GRANT: Yes. If we go check our database
and refresh and go see, we should have some new records. MARTIN: Oh, I see
alpaca, dog, gecko. Can you make the
font bigger, Grant? GRANT: Yeah, let's go. MARTIN: Thank you. GRANT: We can see, all of
our images are classified. MARTIN: Excellent. So alpaca, there-- can we see? What does the alpaca
photo look like? GRANT: So, yeah, just
in the photos URL-- see? A wild alpaca. MARTIN: Oh, look
at that [LAUGHS].. So are we sure, though, that
this is an alpaca and not a llama? GRANT: Oh, yeah, I'm sure. At least, that's what the
tour guides told me in Cusco, in Peru. MARTIN: Oh, really--
you took this picture? GRANT: Yeah, I took the photo. And so I'm sure we
have the rights to it. And we can use it. And you can even download it. MARTIN: Ah, excellent. I will sleep a
lot better tonight knowing that we haven't
violated somebody's copyright. This is great. Also, I feel really good about-- we have a tour guide and we
have the Vision API both saying, it's an alpaca-- [LAUGHTER] --and not a llama. GRANT: Mm-hmm. MARTIN: OK, cool. So this event-driven
function now works. CritterWatch can use this. All the cameras will
call it implicitly when they upload images that
they're taking in the field. Now, CritterWatch is
really happy with this. It just runs. It's not a lot of code. But eventually, they
discover that they will want to have a REST API
in front of this database. And the reason is that they
will have applications-- web applications,
mobile applications-- where you can see photos of-- I want to see all
the alpacas that have gone through my backyard. So they want to put that REST
API in front of the database. They also want to be able
to do traffic splitting, so canary deployments,
like, they deploy a new version of the REST API. And they want to be
able to then send only, for example, 1% or 10% of the
traffic to the new version. They also, of course, want
to put their own domain name in front of this REST API. So it would be
CritterWatchAPI.com or something. And finally, their CTO
has said that Containers is strategic for them. Now again, we have
Cloud Functions, and we have Cloud Run. Given these requirements,
what should they use to build their
REST API, Grant? GRANT: Good question, Martin. Well, looking at
the selection grid, it looks like they should
probably use Cloud Run. I mean, they could
use Cloud Functions. People say, Cloud
Functions is easier. But it doesn't support traffic
splitting or custom demands, for example. So looking at the
API rows of the grid, the check boxes clearly
show that Cloud Run makes a lot of sense. I mean, we could use App Engine. But they wanted Containers. So let's go with Cloud Run. MARTIN: OK, Cloud Run it is. What would the code look
like for this API, Grant? GRANT: Now, let's go
to our code editor. MARTIN: All right, so we're
looking at another index.js. You like Node, Grant, right? GRANT: Yes, I love Node. MARTIN: [LAUGHS] Me, too-- I'm happy you picked Node. All right, so what's
happening here? This seems to be a little
more at the top of the file than we saw in the
previous example. GRANT: Yeah, so whereas
with Cloud Functions, you have to just
write your code, and you don't worry about your
server as much, with Cloud Run, you have to take
care of everything, like listening to your port and
setting up a server yourself. MARTIN: Ah, I see. So the express
framework-- that is a library for
listening to HTTP calls that are coming in, right? GRANT: Yes. MARTIN: Cool. GRANT: And so we go and
set up express here. You set up an app
listening to port 8080. And we're going to also
use the Firestore API. And so-- MARTIN: All right. GRANT: Yes. MARTIN: Very good, so there
is a little more setup there in the beginning than for
Cloud Functions, it looks like. But then the real action
starts on line 11. Could you walk us through? What happens there
on line 11, Grant? GRANT: Sure. So whenever we receive a
get request to animals, we'll go and take
the animal name. We'll find it in our
Firestore database if we did find something,
if it's not empty. Then we're just going to get
the actual sighting and return status success with the
sighting from the database. Otherwise, we're going
to just say, 404-- we couldn't find this animal-- status failure, and say,
the animal wasn't found. MARTIN: OK, and animalName,
up there on line 11, has a colon in front of it. What does that mean? GRANT: Yes, so this means
it's a parameter for the URL. So it could be animals/fish
or animals/hummingbird. MARTIN: Ah, OK. So this is-- OK, you would
put your animal name in there. Got it. All right, well, I don't
know about you, Grant. But I feel another million
question coming up. GRANT: They come
up pretty often. MARTIN: [LAUGHS] Yes. Would this work? GRANT: Does it blend? MARTIN: Yes. GRANT: Let's try it out. MARTIN: OK. GRANT: So I'll just go into
our critter-api and go, deploy. MARTIN: There it is. Ah, you have another
little deploy script there. You really like those
deploy scripts, huh? GRANT: Pretty useful. MARTIN: Yeah, what does
this one look like? So this one's a little
bit more complicated, as you mentioned before. You have to set
up your container. MARTIN: Mm-hmm. GRANT: So here on
line 3, we're going to run gcloud builds
submit, passing a tag, which is useful for just
labeling our container, at gcr.io and our
project name and then also passing our project. MARTIN: OK, so that
piece, lines 3, 4, and 5-- they don't actually
deploy the code. But they build the container. Is that right? GRANT: Yeah, they build the
container with the Docker file provided. And it just sets that little
Docker file and container ready to be deployed whenever. MARTIN: OK, very good. So it's not serving traffic yet. I assume that's what line 7
and onwards are all about. GRANT: Yeah, and so now,
once you have your container, it's all ready to go. Then you can go deploy
it to Cloud Run. So you just write
gcloud run deploy, providing your service
name, critterwatch-rest-api, providing the image we
just built from before. MARTIN: Mm-hmm, yeah. GRANT: We're going to
run on platform managed rather than Kubernetes,
for example, and provide our region. We're going to allow
anybody to access this API with
allow-unauthenticated, and also just provide
our project again. MARTIN: OK, very good. So again, this
would probably sit at an end of a-- at the
end of a CI/CD pipeline. But we're running it
interactively here to show how it works. I wonder, is it done
deploying now, do you think? GRANT: I think so. It looks like we have a-- MARTIN: Oh, yeah, look at that. GRANT: --little link. MARTIN: How would
we exercise this and see if it actually works? GRANT: Yes. Let's go check out the URL. So if we go check, for
example, animals/gecko-- MARTIN: OK, animals/gecko--
oh, there was a gecko sighting? I guess there was. GRANT: I guess there
was a gecko sighting. MARTIN: Oh, look at that. GRANT: Is that a gecko? [LAUGHTER] Oh, it's so cute. MARTIN: Now, I agree. But my top question is, do
we actually have the rights to this picture? Who took the-- GRANT: Oh, yeah, I-- MARTIN: Who took this picture? GRANT: I took it, myself. MARTIN: Oh. GRANT: It was in Central
America, somewhere. MARTIN: Ah, lots of
geckos there, huh? GRANT: Lots of--
yeah, especially-- they are quite loud
during the night. MARTIN: [LAUGHS] OK, cool, and
just to see that the other case works as well, let's search
for /animals/cat, for example. Let's see what we get. GRANT: /cat-- ooh,
didn't find any cats. MARTIN: Ah, not found--
well, that is as expected. Great. OK, so now, this is
how we can build a REST API with Cloud Run. Now, they wanted
traffic splitting, so when they deploy
a new version, only send a little
traffic to that. How would they set that
up with this REST API? GRANT: So good question-- so here we have our
Cloud Run Service. MARTIN: Mm-hmm. GRANT: And we can go into it. And under Revisions, we can
see the Manage Traffic button. MARTIN: A-ha, and I also see
the list of versions there. It looks like the top version
is getting 100% of traffic. Is that right? GRANT: Yeah, so we're
at the bleeding edge. We're saying all
of the traffic-- hey, whatever we just
pushed to production-- let's have all of
our users go there. MARTIN: Oh, the most
dangerous version-- all right, so now if we want to
dial back the risk a little bit and send-- GRANT: Yeah. MARTIN: --most of the traffic
to the previous version, what would we do? GRANT: So we can do the
Manage traffic button. There's also, of
course, the CLI. But let's say, we
probably only just want 10% of our traffic to go
to the newest version. MARTIN: Yeah, that
seems a lot safer. Let's do that. GRANT: So we can just
press Save there. And it's creating this
new configuration, this new revision and
background pretty quick. Now, only 10% of the traffic
whenever you go to this URL will get to the latest revision. MARTIN: Ah,
excellent, very good. And as you say, this can be
done with the command line interface. So this could also be at
the end of a CI/CD pipeline, for example. OK, great. So that's traffic splitting,
canary deployments. They can do that. The other thing they asked
about was custom domains. Now, they have this long
URL that we see here, that has a hash in it
and things like that. But they want
CritterWatchAPI.com, for example. How would they put that
in front of the URL-- of the API? GRANT: Yeah, so they'd
use a custom domain. So could you walk me
through that, Martin? MARTIN: Ah, yes, let's see. This is-- if you click
back, Service details. GRANT: Oh, yeah. MARTIN: Yes, exactly. And then it's next to Create
Service at the top there. GRANT: Oh, this funny button-- I always forget about it. [LAUGHTER] MARTIN: And usually--
now, we have blown up the font size really big. Usually, it says
Custom Domains on it. But that was taken out. And then we can Add a
domain mapping here. And a domain mapping takes
two things, of course-- what service should accept
traffic coming to this URL? And then what domain
should catch these? So of course, here,
we would set up just as you do with
any other service, like App Engine
or anything else. You would set up
a verified domain and walk through the steps
here in Cloud Platform to verify the domain. And when you're done, now,
your new custom domain points to this service. GRANT: Awesome. That's a really cool feature. MARTIN: Yeah, yeah,
it's very useful. All right, so I think
that explains it all. Hopefully, that's clear. So, Grant, actually,
I have something to deal with over here. Could you wrap up this
episode for us, please? GRANT: Yeah, sure. Well, thanks for watching. Please leave a
comment down below. This episode was actually
based off a comment. So we like to read
all your comments. And please suggest maybe a
topic for a future episode. Wait. Wait. Are you getting this? I just got a notification
from my CritterWatch camera. Apparently, there's some animal. I wonder if it's one of those
pesky little raccoons getting in my trash again. Let me check the camera. Oh, Martin! [MUSIC PLAYING]