DevOps Practices for Seamless and Faster App Deployment
Why do we need DevOps?
Development and operations practices - in short DevOps practices - are used to optimize the process of software development. They provide smooth and reliable flow of its different stages, including testing, deployment, application support, and others.
You can find a detailed description of what DevOps is in the article DevOps Best Practices: 10 Benefits of Robust IT Infrastructure.
DevOps provides a set of automation tools for software engineers, aimed to make the lives of developers and their customers easier. Besides thorough testing, DevOps practices allow
- eliminating bug occurrence during deployment of app updates
- significant optimization of the workflow and decrease of time required for app deployment. This may literally save the customers thousands of USD
- seamless deployment process that omits app downtime, and thus is unnoticeable for the client
- and much more...
The main goal of DevOps workflow is to make product deployment on-time, fast, and seamless for the customer.
What DevOps services are there?
DevOps services may include the following:
- DevOps plan composition
- infrastructure cost estimation
- infrastructure setup: at Apiko we use Amazon Web Services (AWS), and sometimes work with Google Cloud (more often just for app support)
- continuous integration and continuous deployment (CI/CD) pipeline setup. CI/CD pipeline is a set of steps that are automatically executed to deploy the project to staging or production environments. When a developer pushes any changes on a particular branch, the task is passed on to a process called runner. The runner executes the tasks that we give it, e.g.
- checks if the code meets all the rules set by static code checkers (e.g. SonarQube, Lint, dependency checkers, self-coded JS tests, E2E tests, etc.)
- looks for bugs
- performs data migration
- builds the application
- deploys the app to a chosen environment
- infrastructure monitoring and alerting setup
- sets up infrastructure metrics: processor load, memory load, etc.
- based on monitored metrics, sets up corresponding alerts
- Google Lighthouse setup. This tool analyses your site's 4 main criteria according to which Google ranks websites: performance, accessibility, best practices, and SEO
- Docker setup
- And more...
You can find out much more from a real-life example of DevOps services at Apiko.
How does CI/CD pipeline work?
Simply put, CI/CD pipeline automates common software development steps and the transitions between them. A generalized workflow is shown in a diagram below.
CI/CD works in the following way:
- You have a repository with code
- Make a commit
- Build an app
- Run the tests if any. *They run on the servers of GitHub or of other CI/CD tools that you use. The tests import a particular function, run it and check the result. The more integration or unit tests you have, the smaller is a chance of deploying a “broken” app to staging or production. After all the tests have been run, and everything was successful proceed to
- Code review for all environments you have: dev, staging (optional), production.
Improvements DevOps introduces to deployment workflow:
- All work is done within one repository, so it becomes a “single source of truth.” Currently, the most popular code repositories with in-built CI/CD plans are GitHub, Bitbucket, and GitLab
- Build application inside repository with encrypted variables to provide more security (DevOps tools include GitHub Actions, GitLab, Travis CI, Circle CI, Jenkins, Bitbucket, and others)
- Run multiple tests in CI tool to ensure high code quality
- After all tests are done: automatic DEPLOY!
DevOps pipeline for web applications at Apiko
DevOps pipelines are created according to the project's needs, and here is an example of how they may work for app frontend and backend.
- Code changes are pushed to GitHub Repository.
- The tests are run within GitHub Actions.
- The application is built.
- Frontend (e.g. React) apps (“Build React app job” in the diagram) are pushed to S3 Bucket, and are hosted there
- App backend, and more complex apps that require server-side rendering (e.g. the ones built with Next.js framework) are pushed to Docker. Then they are moved to a private registry (it ensures higher security), e.g. to Amazon Elastic Container Registry (ECR)
- Amazon Elastic Container Service (ECS) Task Definition with an updated image creates deployment to ECS. It works with each task as with a separate service - it's an ideal option if you need to scale your application quickly (Kubernetes also works the same way).
- Application Load Balancer (ALB) handles all traffic.
- The containers are pushed to staging and production.
Pipeline configuration files are mostly written in Yaml. It's a declarative language, also used by many CI/CD tools, including GitHub, Kubernetes, CircleCI, Ansible, etc. It's easier to read compared to JSON, and they may be utilized interchangeably as well.
GitHub Actions: main features and peculiarities
At Apiko we chose Github Actions because
- most of our projects are hosted on GitHub, so we can keep everything we need (code, pipelines, variables) within one repository
- there are a lot of "actions" prebuilt
- it's easy to use.
Basic features of GitHub Actions
- GitHub variables are called “secrets.” These secrets are encrypted by default, they are not shown within GitHub, and cannot be extracted from there. This contributes greatly to project security. The downside is that you will not be able to edit files in case of e.g. misclick or misprint.
- Concurrency: the next deploy will start once the previous one is finished. This feature is implemented to avoid a situation when a code that is being deployed counts on the changes brought by the previous deploy, which has not finished yet. Most likely it would result in multiple bugs, which concurrency setup prevents.
- Notifications setup, e.g. Slack notifications
- AWS credentials setup to push the app from Docker to private repository
Here's an example of how a well-set-up GitHub Actions pipeline may look.
Containerisation: Git + Docker
Initially, containerisation was not very convenient. If you needed to run a few instances of the same programme, or store app’s frontend and backend on one server, it was necessary to install a virtual machine (VM). It basically imitates a whole new computer with its own operating system that often is different from the one installed on your real pc, and provides you with a safe environment and extended functionality.
Compared to Docker, it has two similar layers - infrastructure and host operating system.
- creates one or several VMs and monitors how they are running
- if they are not running properly, hypervisor finds out a reason and restarts the VMs.
Guest OS means that inside one operating system there is another OS with its own libraries, dependencies, etc.
- checks if the containers are present, and if they are running properly
- creates an environment with its own libraries and dependencies
- uses the core of the OS on which it is running.
A file that runs the code in a Docker container is called Docker image. It may be better not to multiply such files but to use a single Docker image for all customer environments: staging, beta and production. Developers can use Docker images with dev dependencies, for example, automatically restart the node server with nodemon without having to start it explicitly by typing the node commands.
For staging and production environments we recommend using production package installation (like npm install --production in JS), to have a production-ready image with all needed dependencies.
- It decreases the chance of messing up with different dependencies. Let's say, you accidently forgot to add some necessary production dependency. Then in a dev environment everything works fine. However, the pipeline crashes, because something has not been set up.
- It provides similar environments on staging and production, but with different variables (keys, database, etc.) So, if an app functions perfectly on staging, it is sure to work as planned on production.
Dockerfile example with prod dependencies:
- npm install --production
- pass prod/staging environment variables
- npm run prod (node index.js)
Dockerfile example with dev dependencies:
- pass dev environment variables
- npm run dev (nodemon index.js)
How to speed up app deployment with fastlane and GitHub Actions
Fastlane is a platform that faces different aspects of app deployment, and makes it smoother and faster due to its automation.
One of the pet peeves with app deployment is code signing. It's more challenging for projects with large teams of developers, as it becomes difficult to keep track of the required credentials for multiple signing identities.
Besides taking a lot of time to be set up on each device, they may get mixed up, duplicated, damaged, expired, etc., and should be manually renewed and uploaded. Even if you manage to keep them all in apple pie order, most often you have to multiply the price of each code signing certificate by the number of app developers.
That's where the fastlane match comes in handy.
Major benefits of fastlane match
- It takes just about 50 minutes to set up a whole new project
- Get the code signing certificates required for app deployment which you can share with other teammates and use on multiple devices
- It saves hours for the developers as they don’t have to do project setup on each device
- It eliminates compatibility issues
- Automatic fixing of expired or damaged credentials
- Significantly reduced app cost due to:
- shorter development time
- paying just for one set of code signing certificates instead of purchasing them for each team member
Steps to set up a React Native app CD with fastlane and GitHub Actions
- Configure App Store Connect API and save App Store keys
- Configure Google Play Store credentials
- Setup fastlane match and build with provided certificates
- Setup fastlane deploy to TestFlight and Play Store
- Setup CI/CD pipelines
If you are new to fastlane, it's a good idea to check out its official documentation to set up your application deployment. A configuration with match looks like this:
Here is how you can set up GitHub environment variables, and configure CD pipeline:
Let's sum it up!
DevOps practices introduce numerous solutions to turn everyday software development challenges into a smooth and productive process. They increase the level of automation, and assure high app quality and reliability. It does take time to get familiar with CI/CD tools, but once properly set up, you are sure to get
- seamless software development workflow
- decreased time to production
- decreased app cost
If you have any questions about setting up a DevOps workflow, or would like someone to do it for you, feel free to contact the Apiko team! We are eager to share our experience.