Community-sourced Serverless Blogging
Presented by: Sam Rhea
Originally aired on January 27, 2022 @ 3:00 PM - 3:30 PM EST
Learn how to create a new serverless blog with Hugo, deployed to Workers Sites with Wrangler. We'll configure it with GitHub actions for automated deployments when PRs are submitted. And then the fun really begins!
We'll invite people to submit PRs live on the show, review and merge — and you'll see them become posts!
English
Serverless
Blogs
Workers
Transcript (Beta)
Hello, good morning, good afternoon and good evening. My name is Sam Rhea. I'm part of the Cloudflare Lisbon office.
And today I'm hosting a segment here on Cloudflare TV that I call open source community blogging.
And I'm really excited to talk about what this means and why this is important to me.
Because I think that the way that blogging has evolved on the Internet, it's been traditionally an avenue for so many people to share new ideas and speak to a new and wider audience.
But oftentimes it can be very difficult. It can be difficult to find the right place to share content.
It can be difficult to find the funds to pay for the servers to share the content.
Or it can be difficult to find the right platform to use so that you don't have gatekeeper issues or problems about control over the content that you're producing.
And today I'm going to walk through a pretty simple workflow, where in the next 30 minutes, we're going to post a blog live together.
And we're going to do it in a way that anyone can contribute.
And I think that adds a lot of value to there's an important step here where everything that we're building is open source.
And one thing I think that makes this pretty unique is that when your blog is open source, when the ideas that you're creating and communicating are shared with the world, in a version control system, you can really understand how people's ideas evolve.
You can see drafts early and later and iterative versions of a given blog post or given new idea.
And you can understand how the author was able to take this from an outline all the way to something published.
And I think it becomes a good record for how people think about ideas when they communicate them.
And on the community part of the title of the segment, when it is open source, you can have people submit PRs.
It's a good way to have guests author content on your blog, or a good way to have people post questions to have a discussion.
Today to accomplish this, we're going to use a few different pieces.
We're going to use Cloudflare Workers, which is a serverless platform that can also serve static sites, including a Hugo generated blog.
We're going to use GitHub and GitHub Actions, GitHub for storing and open sourcing a blog and GitHub Actions for automated deployments to Cloudflare.
And we're also going to use a tool called Cloudflare Wrangler.
Cloudflare Wrangler is a tool that you can use to publish worker scripts, including this blog, to all of the data centers in 200 cities around the world that Cloudflare maintains.
So I'm going to go ahead and share my screen.
And this is going to be a pretty in depth walkthrough.
And if you have any questions at any time, please feel free to post them to send them in.
There's a link at the bottom of your screen where you can send them our way and I'll try to respond in real time or at the end of the segment.
All right.
Okay, so you should now be able to see my screen and I'm going to pull up Visual Studio Code.
And the first thing I'm going to do is kind of walk through what this stack looks like at a high level.
And the reason I have old and new here is I want to draw a comparison between how I used to run my own personal blog, and how the blog that we'll be using in this demo operates, and highlight why these decisions were made.
I'm going to move this over a bit in the event that it's getting blocked by the video in the top corner.
So previously, I ran a blog that looked very similar to most blogs on the Internet.
I had a CMS platform, in this case, the open source platform WordPress.
But I also had these two layers at the bottom here that you see here, compute and storage.
And that was hosted on GCP, but it was pretty expensive.
Pretty expensive relative to the cost I was willing to pay for my own kind of silly little personal blog.
This ran me about 20 to $25 a month to effectively keep a server warm in GCP.
And as much as I'd like to believe that it's a very popular blog, it's not.
That's a somewhat inefficient use of web server to kind of constantly be ready to serve content, when the reality is it was a somewhat periodically read blog to give it kind of more praise than it's worth.
So when looking at making this easier, making this better, I wanted a model that was not necessarily reliant on a server to always be on and always be available, a server where I was constantly paying the bill to keep that warm.
Instead, I wanted a blog that could respond as the audience grew.
And a serverless platform gives me an opportunity to do that.
In a serverless platform, in an edge network like Cloudflare's, my blog is effectively a cached response that's ready to be served, the content of that blog.
And in the event of workers, when you, an audience member, make a request to this blog, that in a Cloudflare data center near you, responds with that content there at the edge without ever having to go all the way back to a centralized server, which means it's both really performant.
And also, in my case, I don't have to worry about a server staying on, staying enabled, I don't have to worry about a GCP bill that could spike.
Instead, it's all served there at the edge of Cloudflare's network.
Here in the CMS layer, though, I want to highlight this as well.
You can serve a WordPress static site in Cloudflare Workers. But in this particular case, I chose instead to use Hugo.
And I'll show you the framework and how this is implemented in a second.
But Hugo is a static site generator. And it's optimized for speed.
My blog, like a lot of blogs, and the one that we're going to be working on here today, does not need to be terribly complex.
It's mostly markdown files with the content that I'm writing, as well as some images, whether they're images of diagrams like this, or screenshots in tutorials, but it's a pretty simple site.
And Hugo gives me a framework to very easily take those markdown files, take the images, and then get a CSS layer to stylize them and present them served through Cloudflare workers in a pretty performant blog.
I'll show you what the end product looks like here.
Real quick. I'm spelling my own name wrong, because I can't see the URL bar.
So here you can see it's pretty performant. And I can load different blog posts pretty quickly.
I'm here in our Lisbon office. And this is getting served from data center near me, whereas the GCP server I was running was all the way back in the US.
But how do I get here? How do we build something like this that's performant that all of us can contribute to in an open source repository?
So during this demo, I've got a blog post here. And we're going to publish this live.
If you have any interest in joining in, you are more than welcome to submit a PR.
I've already submitted this branch in the GitHub repository for this blog post.
I'll bring that up here. And while I work on this and walk through this workflow, feel free to open a PR against it.
I really want this to be something collaborative.
And at the end of today, so there will be a few hours, I'm going to publish this to production.
But for the time we're going to iterate here in staging.
So I'm going to pull up that GitHub repository. It's townlake slash blog dash Sam Ray here.
Okay.
So Hugo, back to Hugo. So Hugo, like I mentioned earlier, consists of a few basic pieces.
The first is the content of the blog. And this is the most important one.
And one value add thing about Hugo is that it's pretty portable. Because I'm writing all this in fairly agnostic markdown files, I'm able to, if I ever wanted to migrate to a new framework like Gatsby, I can lift and shift these pretty quickly.
But for now, Hugo takes care of everything that I need. I'm able to give the blog post some metadata, which the Hugo blog template that I'm using will be able to pick up.
And here I have some of the content that I'll be using. Write that down.
You can see a bit more complex version of this here, where if you're not familiar with markdown, it's a pretty great way to structure written content in a way that you have a lot of control over that can be represented in HTML.
Okay. So that's the content piece. There's also space to add your images here in another folder.
But what else do we have? The first I'm going to pull up is the configuration.
So Hugo takes a config file that when it generates the blog itself, it reads from all these inputs.
So I'm able to pretty significantly customize how I want Hugo to read my blog post.
And that includes giving it a Twitter handle, telling it what theme to use, and also things like a description and a title, and even a GitHub social media sharing links.
And then as far as what makes it look the way that it does.
Hugo is both open source and also has a number of open sourced themes.
And Hugo themes are what Hugo uses to style your website. In this case, I'm using a theme called Hugo cactus theme.
And this is, I'm going to just point out, Nick, and the cactus authors who built this theme, it's a theme that is a really clean and lightweight framework for a blog post.
But one thing again about Hugo is that I'm able to pull this theme down, and then customize it a little bit.
In my case, I've made a few changes.
But you can see here, there's templates for how the lists are structured, how single pages are structured.
And Hugo is going to take all of these ingredients.
It's going to take the theme, the content, as well as the config to generate a site.
And I'll show you what that looks like locally first. So here in my terminal, I'm going to run a pretty short command, Hugo server D.
And that's going to start a local web server running here at port 1313, that actually has my Hugo site here in real time.
So I'm going to pull that up, and we'll see what it looks like.
Maybe that URL parking. So you can see here, this is a version of my site that's running locally here on my laptop.
So this is not published right now. And we can actually see the blog post that we've been working on together.
You can see the first line. But if you remember, we wrote a second line here during this demo.
And the reason we don't see it yet is because this isn't saved.
So I'm going to go ahead and save that. And you'll see that reflected over here.
So now when I'm writing this blog post, I can actually see in real time, the changes I'm making reflected here.
I'll make another change, you see the date here, it's June 13.
Today's actually the 15th. So I'll make that change as well.
And I'm going to pull over another thing I use in a lot of blog posts where I specify the goals and the time that it takes to complete this.
So what are the goals?
We want to build a live blog post during Cloudflare TV, and publish it to an open source repo.
And hopefully, someone else will contribute to this post, and we'll merge it.
Time to complete. Well, it better be 30 minutes because we only have 30 minutes for the segment.
All right, I'm going to save that.
And you can see it reflected here on the post itself.
Great. So I've got the blog, the ingredients I need to generate a Hugo blog.
I can see what it looks like locally here on my laptop. But now I want to get it to the Internet.
And there's a few options I have for getting this to the Internet.
And there's a few options I have for just getting it to Cloudflare Workers on the Internet.
But I'm going to talk about one in particular that makes this really easy.
And it makes it possible for all of us to contribute to a common blog without all of us needing to know the secrets to the API tokens to deploy to that blog.
So Cloudflare has a tool called Wrangler. And Cloudflare Wrangler is an open sourced command line tool that you can use to take worker scripts that you're building in your IDE, or entire sites like what we're building here in Hugo.
And it will package up the public contents of that site or package up the scripts that you've written and publish them to your Cloudflare account.
And I could do that locally.
I could pull up my terminal and start a new one to actually convert this site into something published from Wrangler running here on my own command line.
But that has a few limitations that for a community-based blog, I don't necessarily want to introduce.
The first is that for anyone to publish, for anyone to submit content and publish to a blog like this, is they would have to have Wrangler as well as Wrangler with my Cloudflare API token, because we're publishing it to a site that is served through Cloudflare's network.
And of course, I trust everyone here in the audience, but it's not a good practice to distribute your API token really widely.
There's also an ergonomic problem there. If I want people to feel like they can contribute, I need to then help them understand how to use the command line.
And for some people that might not be as easy.
They need to install a new command line tool. Maybe they're on a machine where, maybe they're on an iPad even, a machine where they're not able to do that.
They need to understand how to use it.
They need to understand how to configure it and to publish it.
And while it's very easy to do with Wrangler, that's not, there's an easier path that we can all take to make this automatically published to the Cloudflare network without all of us using a command line tool.
And that path involves GitHub Actions.
So I'm going to show you a section of this blog post, I mean of this repository that we haven't looked at yet.
Here you'll see a YAML file.
And if you're unfamiliar with YAML, it's a type of configuration file format. And in this YAML file, what we're telling GitHub in this instance is all the steps to take this blog post and actually publish it to Cloudflare's network.
And this is pretty incredible.
What we're describing here in each of these steps are manual actions that you or I would have had to take start to finish to get this blog post published.
And in documenting it like this, we can actually teach GitHub to take all these steps for us, which is really exciting.
So what I'm going to do is walk through what these different steps are and you'll see the components that involve Hugo, the components that involve Cloudflare, and eventually we'll actually run this all the way through live.
The first is we're telling GitHub use an Ubuntu machine.
In our case, we're telling it to use the latest version because there's not a particular concern if there's a new version of Ubuntu that's going to break this, that would be somewhat easy to catch and we would just specify the last stable one.
But in general, this is a fairly simple set of actions.
So I have some confidence that the latest version of Ubuntu is going to be happy with these.
But if you wanted to specify a version, you could. The first is telling the GitHub action to actually check out the branch.
And again, the best way to think about this is what if I were reading a set of instructions to you with a totally clean laptop, a laptop that has nothing on it just yet, and asking you to go ahead and follow the steps to this blog post.
And whenever I think about configuration like this, I think back to a lesson I learned in sixth grade in Austin, Texas, where I grew up.
I had a teacher in sixth grade who did an exercise where we had to document how to make a peanut butter and jelly sandwich.
And the exercise was pretty simple, just said, hey, write down the steps to build a peanut butter and jelly sandwich.
And all of us kind of giggled and we wrote them down, at least what we thought were the right steps.
And then the teacher picked a few of the submissions at random.
And on her desk had peanut butter, a jar of peanut butter, a jar of jelly, and a loaf of bread.
And she would read through our attempts to describe how to build a peanut butter and jelly sandwich and follow them explicitly.
And that frequently involved somewhat comical outcomes, because you would have, say, an instruction that says put the peanut butter on the bread, but no prior instruction said unscrew the peanut butter or open the loaf of bread and pull out a slice.
So the teacher would just take the jar of peanut butter and put it on the bread, and then do the same for the jelly.
And that really highlighted to me the importance of when you're documenting something, whether it is for a human audience, or in this case, an automated audience with GitHub Actions, there's a lot of value in being really descriptive, to making sure that if someone were following these steps, whether it's a tutorial or a YAML file for GitHub Actions, that there is no ambiguity.
In this case, we want this to be very descriptive and very explicit, so that people can follow this or the machine can follow this without running into issues.
So the first is check out the branch. The next is actually telling it to download Hugo.
You saw here earlier when I published the local version of the site, I was using the Hugo command line tool.
This is telling it the exact same thing, go get the same Hugo tool.
And up here, you'll see we're using an environment variable where we're targeting this particular Hugo version.
And this was something that someone had submitted, I'm grateful for, as a pull request to this YAML file, which is another magical thing about working on a blog in this way, is that if someone wants to not just change, submit better content for the blog, they can also submit improvements to how the blog is deployed.
And one reason this is valuable is that there was a breaking change released in a version of Hugo that caused some issues with images in the blog.
And this way, we know that we're relying on a Hugo version that we know works.
If we ever wanted to update it down the road, we could update it once, or it would be used multiple times here.
And the next step is telling the machine to install Hugo. This really is the unscrew the cap of peanut butter step.
Download is not sufficient, you actually need to de-package the download and install it here on this machine that's been made available in the GitHub action.
And then next is something that's really fun.
This is where we introduce Wrangler. And you'll see in each of these, we're giving them a name.
And this name is what will be presented in the logs you'll see in a minute.
And then we're telling it to run these steps. So Wrangler is that command line tool that I mentioned earlier, where we're using it to package up something from, in this case, a Hugo static site and publish it to Cloudflare's network.
But again, this is a clean machine, we're telling it everything from scratch.
And everything from scratch includes using NPM to install Wrangler. And now that we have Hugo and we have Wrangler, we can actually take the steps to publish and build and publish this site.
So Hugo has a pretty simple command used to take the repository that we've checked out up here and build it, just Hugo.
And Wrangler, we're using the config Wrangler command, we're calling it that. And we're taking an API token.
And this API token is what I was talking about earlier, where I don't want to share this with the world.
It's not a good practice. Instead, I've added it as a secret in my GitHub repository.
And this is a Cloudflare API token that is scoped just to edit workers and workers KV, those are all the features that I need for the particular host name that we're using.
And I'm using that token, and I'm running a command Wrangler publish, to send this content to the Internet.
I'm going to pause there. I'm going to point out one thing that happens to me, it happens to all of us.
Not every blog post is ready to go to the public Internet immediately.
Sometimes we need to see it out there, maybe get some feedback, sit on an idea for a little while.
And for that, we want a staging version.
And this is where other Cloudflare features, something called access, as well as some other triggers within GitHub actions come into play.
And here, you'll see a few differences, but it's the same YAML file format.
In this case, what we're telling GitHub is, whenever I publish a branch that starts with draft slash, which we've got draft slash Cloudflare TV on the branch that we're working with, follow these steps instead.
Whereas this is triggered on a push to master, so effectively a merge.
This is triggered whenever I update a draft slash branch.
And you'll recognize the same steps here. But instead of publishing to blog.hostname.com, the public facing blog, it's running a different environment.
You'll see a Hugo dash dash environment staging, as well as a Wrangler publish dash dash in staging.
And what we're telling it there is, hey, instead of going to blog.hostname.com, follow a different set of instructions.
And in this case, the instructions are going to blog-staging.hostname.com, which means we're actually deploying this to an entirely separate subdomain.
We'll see what that looks like in a second.
So here we go. We've got our Hugo site, which has content, config, styling.
We've got this blog post that we're going to share in staging.
We're not quite ready to share it with the world. And we've got our Wrangler config and our GitHub action config to tell it, hey, this is draft slash, so publish this to staging.
So I'm going to go ahead and commit these changes. Live TV.
I'm going to send this up. Let's go see what this looks like in GitHub. So you can see here, we've pushed a branch draft slash Cloudflare dash TV.
And like we told the GitHub action earlier, we want that to trigger the staging deployment workflow.
So let's go ahead and pull up GitHub actions and see what's going on here.
So you can see changes on live TV is the name that we gave it earlier. And it's got a job in progress.
Let's see what that looks like. Here you can see GitHub running through the steps that we prescribed in that YAML file.
You can see it download Hugo.
And again, these are the outputs you would get if you downloaded Hugo or pretty close to them.
You don't need to scroll through all of that. And you can see it walking through the steps to install Hugo, install Wrangler, build the site, and then run these steps to actually publish it.
We'll give it a minute.
And while it's building, I'm going to show you how we can protect this website as well.
Because again, we want staging to be something we're testing with, we're iterating on, we don't want to show it to the entire world.
So what happens when I go to blog-staging?
How do I keep this from being published to the entire world, but accessible to people I want?
If you see there, that happened really fast.
I'll do it again. When I go to blog-staging, it tells me log in with Cloudflare access instead.
Cloudflare access is a feature within Cloudflare, a product within Cloudflare, that acts like a bouncer at the door for your subdomain, your path, your apex domain.
And this bouncer checks for identity on every request.
So in the Cloudflare access dashboard, which we don't really have time to get into, but I'll describe it here.
What we've said is blog-staging.samrae.com can only be accessible to a few users.
In this case, one of those users is my own personal email that I'm going to log in with Google.
And when I clicked Gmail, what it did is it recognized that I have a Gmail token in my browser.
So it redirected me through the Google OAuth dance, and I was already logged in with my Gmail account.
And that particular user is allowed to reach this site. So it sent me back here.
And I can now see the staging version of this site with the content that we were iterating on earlier.
And it should match up to what was running locally, and all of that without any command line tools.
And if we come see here, we already know that this was a success, but everything is green.
And it's pretty neat that you can actually see it walk through all the steps that we would have been taking manually in the command line.
And now what we've done is we've made it such that anyone who's submitting code to this blog, and even if they're doing it in the GitHub editor itself, if they wanted to come in and make a change directly here in one of these posts, this would trigger the build steps that we just described.
And that's pretty powerful. What we're doing is we're reducing the barrier to sharing content on the Internet, all in a fast and performant and secure way, and making it easier for anyone to share an idea.
And not just share an idea from themselves, but share an idea in a collaborative way.
You know, get open source GitHub repository that anyone can contribute to, and anyone can help add to the content that's being shared on this blog.
So thank you for your time today.
I know we have a few minutes for questions, going to see if we have any.
One other thing I do want to say is if you want to contribute to this post at any time today, please feel free to do so.
Grab this branch and add whatever content that you like.
I'll be publishing this at the end of today. I'm going to see if we have any questions.
Okay, we've got one question.
The question is, what happens if the build fails? If the build fails, if any of the GitHub action steps fail, you can actually go through the logs and see what occurred.
And you can attempt to remediate that based on the errors that GitHub is going to catch.
If you come see here in some of the history, I've actually had some pretty failed builds before when I was first trying to get this set up.
You can see what that looks like, where it's going to tell me this particular step did not succeed.
This log is now expired, but it will give you the details in the build time.
Great. Well, thank you for your time.
I'm really excited about a world where all of us are able to contribute our ideas in written form in a way that's lasting and able to reach a new audience, all in a really accessible and low barrier to entry way.
If you have any questions, please feel free to submit them.
If you'd like to, like I said, contribute to the blog post that we're all building together live today, please go for it.
Thank you so much for attending this segment of Cloudflare TV.
Hope you have a great day.