In the final part of his series, the author provides a retrospective of using Heroku for the very first time, detailing the new design and lessons learned.
In the "Moving Away From AWS and Onto Heroku" article, I provided an introduction of the application I wanted to migrate from Amazon's popular AWS solution to Heroku. Subsequently, the "Destination Heroku" article illustrated the establishment of a new Heroku account and focused on introducing a Java API (written in Spring Boot) connecting to a ClearDB instance within this new platform-as-a-service (PaaS) ecosystem.
The third article in the series ("Using Heroku for Static Web Content") provided a manner in which static web files could also be serviced by Heroku. My primary goal with this series is to find a solution that allows me to focus my limited time on providing business solutions instead of getting up to speed with DevOps processes.
With everything now running in Heroku, it is time to take a step back and perform a retrospective on my new application journey.
From an application and design perspective, there was literally no change to the application, now running 100% in Heroku:
The image above is an identical copy of the image which was in part one of this series. My point in reporting this information is that I was able to very easily pick up my existing application in AWS S3 and AWS Elastic Beanstalk and migrate the exact source code over to Heroku.
While the application was very easy to port over to Heroku, there were changes in the manner by which the application is updated and deployed now.
Parts two and three talked about how I simply needed to execute one additional git-based command in order to deploy changes to the API and client instances running on Heroku:
git push Heroku
Since parts two and three of the series were published, I took a few minutes to understand more about GitLab CI/CD and have introduced some very simple automations. Now, when a pull-request is merged into the
master branch of my Spring Boot API repository, a
.gitlab-ci.yml file similar to the one shown below is automatically executed:
stages: - build - deploy maven-build: image: maven:3-jdk-8 stage: build script: "mvn package -B -DskipTests" deploy: stage: deploy image: ruby:latest script: - apt-get update -qy - apt-get install -y ruby-dev - gem install dpl - dpl --provider=heroku --app=$HEROKU_APP --api-key=$HEROKU_API_KEY only: - master
In the example above, the GitLab CI/CD process uses a Java 8 container which includes Maven to build the Spring Boot framework which serves as the API for my application. Once built, the CI/CD process will utilize Ruby to perform the deployment to application name which matches the $HEROKU_APP variable using the API key provided in the $HEROKU_API_KEY variable.
As a result, my work on this project is 100% feature-based now. I can follow a Git-Flow strategy, where I create a feature branch for my work. Then, when ready, I will issue a pull-request and merge my changes into the master branch. At that point (and only that point) the CI/CD pipeline will fire and push the changes to Heroku, which will deploy a new version of the API.
The Angular client repository was also updated to include a pipeline similar to the one listed below:
image: node:8.10.0 cache: paths: - node_modules/ stages: - deploy_production Production: image: ruby:latest only: - master stage: deploy_production script: - apt-get update -qy - apt-get install -y ruby-dev - gem install dpl - dpl --provider=heroku --app=$HEROKU_APP --api-key=$HEROKU_API_KEY
For the client, there is really only a deployment stage, which also utilizes Ruby and the same variables to merge into the Heroku target repository. Once the push is complete, Heroku automatically deploys a new version of the web client.
GitLab users: This work is actually automated if the Heroku CI functionality is enabled for your repository. For users which enable this functionality, all updates to the master branch which successfully pass the testing stage can be auto-deployed.
With all of the changes in place, deployments are easy and allow me to focus on adding new features to the application. In fact, at eighteen minutes past the hour, I received a text from my mother-in-law indicating that the application was not working correctly. Within a few minutes, I stashed my changes and created Issues in GitLab and a bugfix branch (from the master repository). In this case, I needed to make a minor change to the API and the Angular client.
Within 15 minutes, the issue was identified, fixed, and validated using local instances of the API and the Angular client. The code for both repositories were checked in and I went ahead and created a PR, even though I am the only person working on this project. Once the branch was merged into both repositories, the CI/CD processed kicked off. Not even five minutes later, both applications in Heroku were restarted and functional.
Using Heroku, I was able to resolve the issue and deploy a fix in less than twenty minutes. During that time, I focused on development and standard git usage. Zero time was spent trying to understand and remember DevOps-related items which have nothing to do with providing features and support for my application owner.
Of course, another option would be to revert to an earlier deployment of the services. Heroku provides an impressive manner by which a hosted application can be rolled back to a prior state. In fact, it is as easy as clicking a link in the application:
In the screenshot below, clicking the "Roll back to here" link will initiate the process of reverting the service to a prior state.
Using the CLI, the "heroku releases" command provides a summary of deployments:
$ heroku releases === amhs Releases - Current: v12 v12 Deploy 1somekey firstname.lastname@example.org 2020/05/24 14:34:51 -0400 v11 Deploy 2somekey email@example.com 2020/05/23 15:21:56 -0400
Now, if I wish to roll back to v11, I simply execute the following command:
heroku rollback v11
As always, rolling back should be reserved to rare cases and not be considered a permanent solution.
When I finished part three of this series, I was confident that Heroku was going to be my destination for this application. In order to save costs, I decided to shut down my Elastic Beanstalk instance and set the maximum instances to zero. My thinking is that I would incur zero costs, since the application was not running. Turns out, my assumption was incorrect, and I ended up getting an invoice for $18.49, which covers the database cost and charges related to keeping the Elastic Beanstalk instance available. There were also small charges for AWS S3. Again, time was required in order to understand a far more complex billing structure than I really need ... or have time to worry about.
On the Heroku side, I decided to upgrade to the Hobby plan for both applications. The $14 a month that I expect to pay is a 40% savings over my standard charges from AWS. I don't have to worry about the database right now, since the usage is really low and all of the historical data in the application only accounts for 6% of the database size for the Ignite option. Nothing to worry about there at this point.
I opted to use the Hobby plan primarily to avoid my mother-in-law having to wait for the system to spin up. She only uses the application a few days per month, so I am certain every time she attempts to access the system, she would have to wait. Tip from experience, it is never a good thing to make your mother-in-law wait. :)
I also enjoy the application metrics that are displayed starting with the Hobby plan:
Having insight into the current response time and basic usage levels is all I really need for my application.
On the database side, the (free) Ignite MySQL ClearDB instance provides a basic dashboard that also meets my needs:
While the Node.js approach is working quite well for me, I wanted to bring up the heroku-buildpack-static project, which is designed for single page static web applications similar to my Angular client. To read more about this pretty cool project, Terence Lee created a getting started document.
Had I found this project earlier on my journey, I would have considered using this approach. While the buildpack is experimental and not a product by the Heroku team, I feel like the stability is certainly at a level by which I would trust for my application.
While in college, I met a guy named Stacy. At the time, we both were serious about establishing a professional position in the music industry. However, fate had a different journey for us. I ended up working in Information Technology, and Stacy returned to college to become a dentist. Fast forward thirty years and we are both established in our careers. We still talk when time allows.
My point in bringing up Stacy is that he always gives his time to his friends and family. This extends beyond dental care, spanning a wide range of assistance that he has provided over the years. In fact, it was his inspiration which led me to want to use my skills and abilities to provide a better application experience for my mother-in-law. While she may not understand everything involved with providing her an improved way to do business, she is grateful for my assistance.
At the same time, I feel like the team at Heroku has a similar mission as Stacy and myself. They use their expertise to provide a platform where developers can quickly establish an application. Once in place, supporting and enhancing the application is as simple as checking in the code itself. Truly, this approach has provided exactly what I needed.
Regardless of what we do in life, it is important to remain focused. One way to remain focused is to remove competing priorities. With AWS, I felt like I needed to continue to understand aspects of their ecosystem, which really did not lead to new features for my application. With Heroku, my entire time can be spent providing value to my customer.
Have a really great day!