Building Microservices in Swift and Kitura: Part 1 of 3
Swift on the server is that even a thing? It is, and at notonthehighstreet.com we have had a Swift service using the Kitura framework in production since April this year.
When Swift was open sourced in back in December 2015, and Apple released the Swift port to Linux I was initially quite excited and immediately started to look at the possibilities for building a microservice with Swift.
Unfortunately the conclusion of my research at the time http://nicholasjackson.io/2015/12/05/microservices-with-swift/ was that the lack of Foundation, which is required for networking to other services, and a missing robust HTTP server, was a showstopper. I did contemplate the possibility of porting a web server written in C from another source, and interfacing this into Swift, but I did not have the security experience to ensure that this would be resilient to vulnerabilities, and finding the time to do this work with my current project commitments was not possible.
I loved Swift since I first started using it when it was in beta, and when we first used it to rebuild the M&S iPhone app. With a heavy heart, I parked this dream for a while and got on with a few other things.
You are probably thinking “typical developers, always chasing the next shiny product”, and in some ways you may be right - we were interested in checking out the latest technology as we see it as is important to remain current. Our primary motivation, however, was to solve a very real problem that we have, and that probably quite a few other companies have.
We split our teams into vertical silos, with each being a full stack team. However, we treat apps as a vertical stack comprising of only app developers, because apps don’t need back-end services right?
Anyone who has ever worked in a team building iPhone and Android applications which have been dependent upon another team for the backend APIs, will tell you what absolute horror it is to be bound to someone else’s product development roadmap. Sure, you could build your own APIs - why not just learn Ruby or another language so you can do that? That is an approach for sure, the only barriers to success are to learn a new language, which you do not understand, and deploy a new backend service using technologies and platforms, which you do not understand. I can not see how that could go wrong!
The unfamiliarity with a different platform problem still exists with Swift, but since the language barrier is gone there is a faster route to success. Knowing at least how to build the code gives quite a considerable comfort when adopting a new practice.
Then there is speed - Swift is pretty comparable to the performance of C. With the compiler optimizations that you get from LLVM the speed and stability far exceeds the capabilities that the average C programmer could achieve. I also firmly believe that the language features of Swift offer faster time of development and greater maintainability. So we dug in and started to build our first service.
Building our first service in Swift
We chose the Kitura framework without much hesitation as the code was very actively maintained by people who know what they are doing, there was also a wonderful community and active Gitter group. At first look, the patterns that were employed were very sensible. The router supported good RESTful standards and allowed chained middleware - I think I read somewhere that the ExpressJS framework from Node.js influenced the patterns.
At NOTHS we had built enough microservices to know what we need to support and maintain them in production. The most important things for us are:
- Ability to containerize the application with Docker;
- Monitoring and Logging using Logstash and Datadog;
- Capability for CI/CD using a Docker-based workflow;
- High throughput and stability.
Ability to containerize the application
This step was simple thanks to the ability of Swift to run on Ubuntu. The only disadvantage is that the current release does not allow static linking which means you are bound to using a base container like Ubuntu. There is a pull request which has just been accepted to enable static linking, this means that Docker containers built on minimal Alpine Linux base containers are not far away.
Monitoring and Logging using Logstash and Datadog
We extensively use statsD as our protocol for sending small metrics to DataDog, and following this pattern for a Swift microservice was essential to the success of this project. A little Googling did not uncover any open source packages for statsD with Swift 3.0, however this was to be expected as it is not something which is used in the standard iOS development use case. The newness of the language also meant there was not yet an open source contribution. Looking at the statsD platform, the message protocol is somewhat simplistic and could be implemented easily so we decided this would be a good thing to build. The problem with this lay in the fact that we needed to send this data using a UDP socket. The lack of sockets in the standard framework for Linux meant we had to look at an alternate source. The BlueSocket library from IBM supported TCP but unfortunately not UDP, so we needed another project which would wrap C sockets to send these UDP messages. This was not too much of a problem as we had the skills to perform this work, and while we moaned about this at the time, we had to remind ourselves that this was an Alpha release.
Capability for CI/CD using a Docker-based workflow
We use Docker for our build process due to the level of quality it gives us; the process is that all code is built inside a Docker container and the resultant binary is then packaged into a production container. When running the functional tests we run this against the production container, not an intermediary, as not only do we want to verify the integration with the code but the packaging too. All of these builds should be runnable on both a developers machine as well as CI by using the same commands to allow minimal setup with the only requirements the ability to run bash scripts and Docker. Kitura posed no problems in this department, and we were able to modify a build script that was used for a Go microservice in a very short time.
High throughput and stability
Initially, this was always going to be a leap of faith, as until we had spent some time building a service we would not know how it would perform. We were fairly sure that things would be ok as the language is fast and we had high trust in the Kitura framework being up to the job. As it turned out, the service performed very well and in a later post, we will examine this throughput in greater depth.
The first service we built was a service which managed stock levels for a pop-up shop that notonthehighstreet.com were running. It was an event which would take place over 3 days, and would be a beta for more events of this kind. I worked with Ivan Andriollo https://twitter.com/ivanandriollo who was part of our Apps team, he was building a simple point of sale app which would be used to take payments. My service would provide the stock levels and with every sale the POS system would update the service. It needed to be fast and it needed to be stable - we could not afford any downtime. To mitigate risk, we implemented a backup where the app would cache data locally. It worked, and not only did it work - it worked fantastically, with zero downtime during the event and, looking at the metrics in Datadog, this thing was fast.
This encouraged us to push things further, and the rest of the apps team were incredibly interested in what we had done. We therefore decided to build another service, this time one which would service our current iOS and AppleTV apps by providing config so we could update homepage elements and other features without having to submit for app store approval. Now the gloves were off, this new service had to be completely stable, fast, and deal with hundreds of thousands of requests per day. Downtime was out of the question we make real money from our apps, and an outage would not be a pleasant conversation with the CTO. So what did we do? I paired with Kiera, who had just joined the team. She was an experienced Swift developer working with the language since the beta, but regarding microservices, the first task was to explain what a microservice was. Is it possible for an experienced app developer, but a novice server side engineer, to pick this up? It certainly was!
In the next post we will look deeper into the source code and why we used some of the patterns, but for now you can check out the service we actually have running in production as we thought, why not open source it? We will walk through this service and we will hear from Kiera herself on the challenges she faced but most of all how much she enjoyed the experience.