Real World Cloud Migrations: CDNs are an easy improvement to legacy apps
I'm doing a quiet backend migration/update to my family of sites. If I do it right, there will be minimal disruption. Even though I'm a one person show (plus Mandy my podcast editor) the Cloud lets me manage digital assets like I'm a whole company with an IT department. Sure, I could FTP files directly into production, but why do that when I've got free/cheap stuff like Azure DevOps.
As I'm one person, I do want to keep costs down whenever possible and I've said so in my "Penny Pinching in the Cloud" series. However, I do pay for value, so I'll try to keep costs down whenever possible, but I will pay for things I feel are useful or provide me with a quality product and/or save me time and hassle. For example, Azure Pipelines gives one free Microsoft-hosted CI/CD pipeline and 1 hosted job with 1800 minutes a month. This should be more than enough for my set up.
Additionally, sometimes I'll shift costs in order to both save money and improve performance as when I moved my Podcast's image hosting over to an Azure CDN in 2013. Fast forward 6 years and since I'm doing this larger migration, I wanted to see how easy it would be to migrate even more of my static assets to a CDN.
Make a Storage Account and ensure that its Access Level is Public if you intend folks to be able to read from it over HTTP GETs.
With in the Azure Portal you can make a CDN Profile that uses Microsoft, Akamai, or Verizon for the actual Content Distribution Network. I picked Microsoft's because I have experience with Akamai and Verizon (they were solid) and I wanted to see how the new Microsoft one does. It claims to put content within 50ms of 60 countries.
You'll have a CDN endpoint host name that points to an Origin. That Origin is the Origin of your content. The Origin can be an existing place you have stuff so the CDN will basically check there first, cache things, then serve the content. Or it can be an existing WebApp or in my case, Storage.
I didn't want to make things too complex, so I have a single Storage Account with a few containers. Then I mapped custom endpoints for both my blog AND my podcast, but they share the same storage. I also took advantage of the free SSL Certs so images.hanselman.com and images.hanselminutes.com are both SSL. Gotta get those "A" grades from https://securityheaders.com right?
Then, just grab the Azure Storage Explorer. It's free and cross platform. In fact, you can get Storage Explorer as an app that runs locally, or you can check it out in the Azure Portal/in-browser as well, here. I've uploaded all my main assets (images used in my CSS, blog, headers, etc).
I've settled on a basic scheme where anything that was "/images/foo.png" is now "https://images.hanselman.com/blog/foo.png" or /main/ or /podcast/ depending on who is using it. This made search and replaces reliable and easy.
It's truly a less-than-an-hour operation to enable a CDN on an existing site. While I've chose to use Azure Storage as my backing store, you can just use your existing site's /images folder and just change your markup to pull from the CDN's URL. I recommend you make a simple cdn.yourdomain.com or images.yourdomain.com. This can also easily be enabled with a CNAME in less than an hour. Having your images at another URL via a subdomain CNAME also allows for even more download parallelism from the browser.
This is all in my Staging so you won't see it quite yet until I flip the switch on the whole migration.
Exploring future possibilities for dynamic image content
I haven't yet moved all my blog content images (a few gigs, but many thousands of images) to a CDN as I don't want to change my existing publishing workflow with Open Live Writer. I'm considering a flow that keeps the images uploading to the Web App but then using the Custom Origin Path options in the Azure CDN that will have images get picked up Web App then get served by the CDN. In order to manage 17 years of images though, I'd need to catch URLs like this:
https://images.hanselman.com/blog/Windows-Live-Writer/0409a9d5fae6_F552/image_9.png
and redirect them to
https://images.hanselman.com/blog/content/binary/Windows-Live-Writer/0409a9d5fae6_F552/image_9.png
As I see it, there's a few options to get images from a GET of /blog/content/binary to be served by a CDN:
- Dynamically Rewrite the URLs on the way OUT (as the HTML is generated)
- CPU expensive, but ya it's cached in my WebApp
- Rewriting Middleware to redirect (301?) requests to the new images location
- Easiest option, but costs everyone a 301 on all image GETs
- Programmatically change the stored markup (basically forloop over my "database," search and replace, AND ensure future images use this new URL)
- A hassle, but a one time hassle
- Not sure about future images, I might have to change my publishing flow AND run the process on new posts occasionally.
What are your thoughts on if I should go all the way and manage EVERY blog image vs the low hanging fruit of shared static assets? Worth it, or overkill?
The learning process continues!
Sponsor: Seq delivers the diagnostics, dashboarding, and alerting capabilities needed by modern development teams - all on your infrastructure. Download now.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
OTOH, if you can adapt your code to serverless (no web server - your code only runs when its invoked), I've been hearing a lot about Azure Functions or AWS Lambda as potential pay-for-what-you-use solutions.
One model, for dynamic functionality, would be a function which responds to a URL and returns something suitable for display in the browser (I'm glossing over the configuration). Alternatively, you could have the function code write static content to blob storage and serve it from there. (I've done the latter, serving content from an S3 bucket and believe Azure recently introduced something to it.)
Azure Functions: https://docs.microsoft.com/en-us/azure/azure-functions/functions-overview
Lambda: https://aws.amazon.com/lambda/
Comments are closed.
However the further we go the more I tend to see that for private hosting Azure is way too expensive. E.g. our AppInsights instances are eating about 100$ per month... Just logging/monitoring. Of course it's nothing for enterprise, but for private usage I would think twice if it's important to log something so hardly for me.
For enterprise there are a lot of other savers, like maintenance costs, licenses, etc. However for private usage - it's not something significant in regards to maintaining.
I agree, it's up to you what balance you choose, which Azure services you use and how you use them, but for example.. I'm running quite small "private" app, which is not massively used WebApp, with small DB. And it is eating almost whole my MSDN subscription given monthly quote (about 100$). For private hosting (e.g. Wordpress-level blog, not widely-known), I guess even 20-30$ is already expensive.
Maybe it's just a feeling, or maybe I'm doing something wrong, or maybe it's just historically, that .NET hosting is expensive. Can you elaborate something on it, dispel my doubts about it? ;)
It would be also really interesting if you could present your numbers when you done migrating to cloud. :)