Friday, April 05, 2019

Deploying Angular 6 + Spring Boot + Postgres to AWS

You have completed your Angular application and now you are looking for some deployment options... Among many in existence today, you can find choices like AWS, GCP, Azure, PCF, etc. and few other in the cloud. In this post, I will explain what I needed to do to deploy my services to AWS and keep the cost low (or non existent with AWS free tier). There is always an option to get full 'by the book' services and pay for those, but it is better to understand what your options are when deploying the application and then understand how your revenue is going to go against your expenses. As it is, My Open Invoice application is now designed to operate for a single contractor. To add another company a new URL and setup is needed. This can, of course, be upgraded with a few tweaks in the data model and the application itself. We can leave that to some other blog, though. For now, let me introduce you to my simple architecture:
  1. Front end of the application is Angular 6 built on Google Material design capable of rendering on both desktop and mobile devices
  2. Middle tier is Spring Boot 2
  3. Database is Postgres (H2 as test database)
  4. Application has cache that is currently used only for RSS feed (EH Cache)
  5. Authentication and authorization are done through JWT with Bearer token.
Amazon offers several options for hosting your web application, like EC2 instance and S3 bucket. There is then CloudFront which is used to cache your content (dist folder) and front is with HTTPS access. S3 has durability and redundancy, Route53 has DNS covered and RDS has your database. Elastic Beanstalk is used for EC2 instance generation, auto scaling and Load Balancing setup. CloudWatch is used for log tracing and then there are several other options that you can turn on for multiple instances, load balancing, reporting, caching etc. 
My goal here is to create something that won't drain your money away but still give you a decent application with the ability to have backups. I will also mention the option that would be needed for more robust solution.

This is my current setup:

Let us start by explaining components that I have used:

  • Angular 6
    • This is a client facing application that is using a template that is both desktop and mobile app friendly. This app is compiled into /dist directory with production setting and code optimization, and uploaded to S3 bucket.
  • S3 Bucket
    • This is standard AWS S3 bucket in one zone. You can create zone replication but Amazon already guarantees 99.999999999% durability on single zone in case you worry about your data. You could even go to S3-IA for lower cost, but storage is very low for hosting one small web app anyway. This is going to host two items for us:
      • Angular 6 code (you can keep this private with access only to CloudFront)
      • Beanstalk repo for WAR files 
  • CloudFront
    • This service is our fronting service to enable routing to /index.html, content distribution closest to your customer location and HTTPS certificate termination and routing for our Angular app. There are few important items here worth mentioning:
      • you need 400 and 404 (at least) routing enabled to /index.html 200 HTTP code or your app will not work properly
      • you can provide your certificate here if you have your own domain registered (you can use Amazon ACM to generate and provide certificate here)
  • CloudWatch
    • This service is enabled by default and will track and report your usage for your AWS components. Max resolution is 1 minute. Depending on configuration and logging amount different charges may apply. Modest logging should remain relatively cheap or non existent. Here is the pricing.
  • Elastic Beanstalk
    • This is your PaaS. You will use this as an entry point to create your configuration, environment, VPC, load balancing, auto scaling groups, RDS, etc. This can be done in an easy and convenient way. You can save hours of work by deploying this way. There are a few important items to consider:
      • I have created my database separately from this config. There is a difference if you want to use Tomcat (e.g.) to manage your DB connection or your application. There is also more flexibility to configure RDS individually. 
      • I am using Nginx as a reverse proxy and Tomcat 8 to serve content (WAR). One important item is that since I am using Spring Boot application, you need to pass in -D style properties to overwrite Spring ones and NOT environment ${} variables. Took me good 1 hour to figure what 'environment' means in Beanstalk.
      • I did not turn on load balancing as this costs money even in free tier. You can alternatively load balance with Route53, but you will need to connect directly to EC2 instances and this limits auto-scaling option.
      • If you want to increase instance numbers, I did not find an option to change auto-scale Beanstalk other than enable LB or Time-based Scaling. But, you can also log into auto-scaling directly and increase Max instances to your desired number and also configure trigger for activation. This, however, will not help a lot as you would need load balancer to connect to these. The only other option would be to have ElasicIP address on EC2 instances and DNS balance on those, but I honestly did not try this.
      • When deploying WAR file, you need to create custom 443 port nginx.conf in addition with uploaded certificates (I got mine from this SSL site for free). You will need .ebextensions in WAR file with all configurations and certificates. EC2 config is rebuilt on every restart so you will lose 443 port if you do not have this enabled. This is ONLY needed if you do not have LB. Otherwise, LB will take care of 443 port for you (after you configure it).
      • You need to open 443 port to your EC2 security group (one will be created by EBS if you already do not have one). This needs to be accessible from Your Angular app will connect directly to servers using this rule.
    • RDS
      • My choice of RDS is Postgres for prod and H2 for dev. On the side note, I was amazed how fast and compatible H2 is with SQL standards in terms of functions (I notice this every time :)). Postgres was closest for some custom queries for capability that I needed (compared with e.g. MySQL). RDS was created in only 1 zone with minimal sized instance, and security group was opened from EC2 to this group by name for desired port. RDS access was limited only to EC2 and if needed access can be done directly for DB management through port forwarding.
    • Route 53
      • I have registered domain through Amazon and AWS created a Hosted Zone for me. Inside, I have created ALIAS record  pointing to Beanstalk for both www., api. and naked domain name. If you choose option to point directly to EC2, you can do that, and you can always choose load balancing using DNS (though this is not primary balancing method on AWS).
    • Auto Scaling Group
      • This is created by Beanstalk and in general, you have two options. One is for the single instance and one for the load balanced. Again, load balanced instance will cost you so use only if you need it.
This is just one way of setting up you environment and for a small application it works for me. I have all the backups so I am not too worried about my downtime if any.

As suggested, there are a few items to change if you need more robust environment. Couple of them would be:
  1. Enable LB on Elastic Beanstalk and have your system balance across at least 2 zones.
  2. Have at least 2 zone deployments, but depends on your clientele, hosting in each major zone would be beneficial (and then use Geolocation/proximity to optimize content delivery)
  3. Right now, I have everything deployed in one VPC, but depending on your needs and layout, you may want more than one and then deciding which would be private and public and where to use Gateways, and how to connect VPCs.
  4. API Gateway is always and option for more complex environments where HTTPS also can terminate (with your certificate). This adds a layer of abstraction if you are using microservices from multiple points and with various infrastructure.
  5. RDS needs to be deployed on Multi-AZ with read replicas enabled. Backup is already enabled even for 1 instance. A possibility exists to use NoSQL database that is automatically deployed into Multi-AZ (like DynamoDB).
There are many ways things can be configured on Amazon, so it is worth a while to investigate and try it out. It is not expensive to test configurations to figure out what is the right one for you, but it may be expensive to try to correct everything down the road once you realize that it was not set up properly. Amazon offers option to pay for only what you use, so why not try it?

So far, my costs were for registering my domain and a small Route53 charge ($0.50 per hosted zone).

If you have a better setup in mind, please let me know. I am always trying to learn new and better ways of optimizing data, infrastructure and cost.