From bbe4f6ec5aadc42bc532296fd74485d306a67242 Mon Sep 17 00:00:00 2001 From: mfenderov Date: Wed, 17 Apr 2024 11:00:53 +0200 Subject: [PATCH] feat: post update --- posts/20240331-lets-build.md | 112 +++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/posts/20240331-lets-build.md b/posts/20240331-lets-build.md index 41acc7e..45c0e9c 100644 --- a/posts/20240331-lets-build.md +++ b/posts/20240331-lets-build.md @@ -5,7 +5,7 @@ date: 31.03.2024 So, the tech. Oh yes, the most important part — the tech. -I'm going to use stuff I'm most comfortable with, which is happened to be the most widespread tech stack in the world: +I'm going to use stuff I'm most comfortable with, which happens to be the most widespread tech stack in the world: Angular frontend, Java + String backend, and all that on top of AWS. Let's begin with infrastructure — to keep things simple, I'm using AWS Amplify to run frontend, and AWS AppRunner to run @@ -14,15 +14,21 @@ For now, there's no need for anything more complex than this. ### AWS Amplify -AWS Amplify hooks up to frontend repository via GitHub webhook. -And everytime anything is pushed into `main` branch, Amplify gets notified and the CI/CD machinery kicks in. -Amplify is smart enough to understand that it's connected to angular app (this actually doesn't matter, +I'm not the frontend expert by any means, but even I know, that FE is mostly static stuff. +And the best way to serve static stuff is via S3. +The problem is — I don't want to spend time configuring all that now. +S3 Bucket policy, pipelines, roles — I can configure all of that, but why? + +This is where the Serverless shines. +[AWS Amplify](https://aws.amazon.com/amplify/) hooks up to the frontend repository via GitHub webhook. +And every time anything is pushed into `main` branch, Amplify gets notified and the internal CI/CD machinery kicks in. +Amplify is smart enough to understand that it's connected to the angular app (this actually doesn't matter, because it builds a project with a silly `npm run build` script). -Build artifact is then stored into AWS S3 bucket +Build artifact is then stored in AWS S3 bucket (unfortunately, or not, this bucket is not accessible) -and then exposed via cloudfront distribution(also not accessible). -By "not accessible" I mean that it's not created under my account, I can't look at it nor touch it. +and then exposed via CloudFront distribution(also not accessible). +By "not accessible" I mean that it's not created under my account, I can't look at it or touch it. It exists, but somewhere within the bowels of AWS. Serverless, right? @@ -32,6 +38,20 @@ It just works. I have a strong impression that AWS S3 powers at least half of the internet, and so I'm trusting it to host my amazing frontend. +A couple of clicks more and the custom domain is attached. + +Voilà! + +My FE is running under http://buyallmemes.com. + +Minimum configuration, maximum profit. + +And this is just the tip of the iceberg. +With a couple of clicks more, Amplify could be integrated with GitHub PRs. +It will spin a new env per PR created, and when PR is merged - it will tear the env down. +Some organizations I've worked for could only dream about such a feature. +And here it is out of the box. + ### AWS AppRunner After the first blog post, I had no backend for my blog application. @@ -42,8 +62,8 @@ After the first blog post, I had no backend for my blog application. — Alright, let's have it. -Building backend is straightforward. -Code here, code there — I'm doing this for the last 15 years, so I'm feeling somewhat comfortable. +Building the backend is straightforward. +Code here, code there — I've been doing this for the last 15 years, so I'm feeling somewhat comfortable. The real question is "How to run it?" EKS? @@ -61,29 +81,49 @@ I can't access my service from the outside. Oh, no... networking. Something is not right with the VPC setup. Subset seems fine. -Security groups and routing tables are also "looks fine." +Security groups and routing tables also "look fine." Damn it, something silly is not right, and I can't find it. Screw it — a task stopped, task definition deleted, cluster deleted. ECS is also too complex. -While in bed and half asleep, I was browsing through AWS Console app on my phone. +While in bed and half asleep, I was browsing through the AWS Console app on my phone. + Eureka! -AWS Q. AWS AI assistant. + +[AWS Q](https://aws.amazon.com/q/). AWS AI assistant. This is exactly what they built it for — so that idiots like me could ask questions like mine. -The answer was instant — AWS AppRunner. -Next morning I logged in into AWS AppRunner, clicked a few buttons, -selected a hello world image from ECR, "deploy" and... it worked. -My hello world backend is running in a matter of 2–3 minutes. +The answer was instant — [AWS AppRunner](https://aws.amazon.com/apprunner/). + +The next morning I logged in to AWS AppRunner, and clicked a few buttons: + +- create service +- select an image from private ECR +- selected a Hello World image from ECR +- deploy + +And... it worked. +My Hello World backend is running in a matter of minutes. +No complex configurations, and no networking. This is why I love AWS. -I've hidden my app deployment via a custom domain `api.buyallmemes.com` by fiddling with Route 53 hosted zone. +I've hidden my app deployment via a custom domain http://api.buyallmemes.com by fiddling with Route 53 hosted zone +and clicking a couple of buttons in the App Runner. Thankfully, I know a couple of tricks around DNS. +A couple of clicks more, +and now the App Runner will automatically redeploy my backend application as soon +as a new image version is published to ECR. +All I need to do is to setup GitHub Action to build and publish images to my ERC registry. +Easy. + +Once again, no roles, no policies, only profit. + Now, it's time to build the real backend. ### Java + Spring = ❤️ -The choice of tech for the backend is super easy. There's no choice really. +The choice of tech for the backend is super easy. +There's no choice really. There's only one true kind, and it's Java + Spring. I'm starting with an extremely simple setup: one REST endpoint that returns a list of posts. What is a post? @@ -94,12 +134,12 @@ However, I do need something — Zalando Problem library https://github.com/zala I'm sure you're aware of Zalando as an internet cloth retailer, but you might not be aware that they have quite a few cool bits of software. Problem Library is one of those bits. -It's a small library with a single purpose — unify an approach for expressing errors in REST API. +It's a small library with a single purpose — to unify an approach for expressing errors in REST API. Instead of figuring out every time what to return in case of error, or returning gibberish (like a full Spring Web stack stace in case of 500), -zalando/problem library suggests returning their little `Problem` structure. +the zalando/problem library suggests returning their little `Problem` structure. Naturally, a library has an awesome integration with Spring, so there's very little configuration required. -Use it, do yourself (and your REST API consumers) a favor. +Use it, and do yourself (and your REST API consumers) a favor. Another one of those hidden gems is a Zalando RESTful API Guidelines https://opensource.zalando.com/restful-api-guidelines/ — read it. @@ -114,9 +154,9 @@ Let's focus on making things work. Damn it, I need a database to store posts! Or do I? -Hmm, why the hell would I need an enterprise grade DB (like PostgreSQL) to store a single post - sounds absurd. +Hmm, why the hell would I need an enterprise-grade DB (like PostgreSQL) to store a single post - sounds absurd. I will store it on disk as part of the source code! -My IDE is a perfect .MD editor. +My IDE is the perfect `.MD` editor. Git will provide me with all the version control I ever need. I can just branch out of the `main`, write whatever I want, and then merge it back when it's ready to be published. And it's free! @@ -126,7 +166,7 @@ but for now, this is not a big deal, so this mechanism will suffice. I've set AWS AppRunner to automatically detect and deploy the newest image versions of my backend. So I don't have to do much manual stuff, besides building an image. -Btw, how do I suppose to build and push image into ECR? +Btw, how am I supposed to build and push the image into ECR? I'm not writing Dockerfile — that's for sure. Google Jib, https://github.com/GoogleContainerTools/jib. @@ -140,34 +180,30 @@ but for the sake of exercise, I decided to publish everything on GitHub). Alright, for now, that's enough. I have a running Angular frontend and Java backend. -Frontend knows how to talk with backend. -Backend return list of posts, which are store in `resources` folder. +Frontend knows how to talk with the backend. +The backend returns a list of posts, which are stored in the `resources` folder. Backend logic is rather silly - Read files from `resources/blog/posts` project folder -- Load each file content as string into a Post object +- Load each file content as a string into a Post object - Sort loaded posts by filename in descending order And yes, I've introduced `fileName` attribute to the `Post`. And that's about it. -I already established minimal flow of work. +I already established a minimal flow of work. At the moment, there's little to talk about. There's little code and one cute unit test. I guess this is worth talking about — I'm a huge fan of TDD. I love my tests. -At the moment, I have only one but crucial test that covers two most important aspects — REST endpoint and that posts +At the moment, I have only one crucial test that covers the two most important aspects — REST endpoint and posts are properly ordered. I decided to use file naming as a sort parameter. -Each new post-file will be prefixed by the current date, -so I could easily sort them in reverse order to show the latest posts on top, and oldest at the bottom. +Each new post file will be prefixed by the current date, +so I could easily sort them in reverse order to show the latest posts on top and the oldest at the bottom. Since I'm a backend guy, I prefer to keep such logic at the back. -I don't want to spend much time on frontend, so I will try to keep it as lean as possible. +I don't want to spend much time on the frontend, so I will try to keep it as lean as possible. Saying that, the more I think about it, the more I realize that I should've gone with something like a thymeleaf, -and build everything within the backend app, but what's done is done. +and built everything within the backend app, but what's done is done. Having a separate frontend app is not without its benefits anyway. -Plus, I can definitely benefit from expanding my horizons beyond backend and Java. - - - - +Plus, I can definitely benefit from expanding my horizons beyond the backend and Java.