We started our internship at Ordina in February 2021 which lasted for 15 weeks. The internship was a part of our thesis, which was necessary to graduate. During this internship, we developed an Android application to assist drivers while driving trough a trajectory control. Alongside the application, we also developed an API using Spring Boot. These two components made it possible to learn and use Kubernetes. Certain technologies were installed on the Kubernetes cluster to enable metrics and centralized logging. During the internship, we were introduced to a wide range of technologies that we’ve never used during our bachelor’s degree.
Azure DevOps has a plethora of technologies to provide assistance throughout the development cycle. We used a handful of these technologies for this project, including Azure Repos, Azure Pipelines, Azure Artifacts and Azure Boards. It also allowed us to work with scrum using the built-in boards, which ensured that the mentors could track our progress. Overall, Azure DevOps is a very useful and feature-rich platform to use.
Our first experience with Java was in highschool, where we learned most of the programming basics. In college, we only used Java during our Android Development course. Because of this, we weren’t that well versed when it came down to Java specific features. While working on the API, we learned quite a lot about Java, but also some programming principles.
The Spring framework is a Java platform that provides comprehensive infrastructure support for developing Java applications. We started out by creating a small RESTful API using one of the tutorials on the official Spring website. Alongside the tutorial we also used the extensive online documentation.
In college, we had a .NET course where we used ASP.NET to build an API. We quickly discovered while working with Spring Boot that our preference goes to Spring instead of ASP.NET. One of the reasons is that Spring Boot does a lot for the developer while in ASP.NET you have to do more things manually.
The API’s database is a PostgreSQL database, which is used to store the collected data, the recorded trajectory controls and the drivers. Flyway is used to keep our database structure up-to-date using migrations.
To develop the API, we used the API-first design principle. This approach includes developing APIs that are consistent and reusable. This can be achieved by creating an API contract that describes the capabilities and behavior of the API. This ensures that more time is spent thinking about the design of the API. It often included additional planning and collaboration with our mentors. We drew up this contract ourselves. Later we found that there is a Swagger Editor that uses the Open API type. Since we already had a contract we decided not to use it. They provided feedback on the design of the API before any code was written. The first code that was written were tests. This method is called Test Driven Development (TDD). Specific attention was paid to the API contract, as the tests are based on them. TDD also allows for bugs to be found at an early stage because after writing code, the developers can run the tests and thus check whether the code works or not.
The user can add trajectory controls themselves via the record button. Once the user has passed the starting point of their trajectory control, they must press this button to start the process. During this process the starting point is saved and a checkpoint is added every 100 meters. When the user passes the end point of their trajectory control, they need to press this button again to define the end point and the maximum speed. The application will ask the user for the maximum speed. This will be automatically filled in by a speculation performed by the application. Once the process is finished, the trajectory control will be stored in the database via the API.
Android Native was already known because of the Android Development subject. As a result, we did not encounter too many problems in terms of development. The biggest problem we have experienced is that Android Native is not that user friendly in terms of design. If we were to start over, we would prefer a Hybrid approach like Ionic or NativeScript instead of Android Native.
The figure above shows the block diagram of this project. This diagram represents all the components of this internship. Each component will be briefly discussed below. All applications required for this internship run within a single Kubernetes cluster. Inside this cluster, Argo CD is used to keep everything up-to-date. The Android application is connected to the TC Guard API using Contour. This is also used to expose the Graylog dashboard to the public. TC Guard and Graylog are connected so that the logs from TC Guard can be sent to Graylog. The data collected by the Android application is stored in the PostgreSQL database via the TC Guard API. When the application needs this data, it is retrieved again via the TC Guard API. The last block connected to the TC Guard API is Prometheus. Prometheus is used to observe the API. The resulting data is then displayed in the Grafana dashboard.
Argo CD is a Continuous Delivery GitOps tool for Kubernetes. Within the internship assignment, this is used to install Helm charts on the Kubernetes cluster. If a change is made on the Azure DevOps repository, Argo CD will detect this and implement the changes on the Kubernetes cluster via a rolling update.
It was quite satisfying to observe the changes being applied.
Argo CD made managing our applications much easier.
For example when we updated Graylog, the only thing we had to do was change the version number in a
.yaml file and Argo CD took care of everything else.
Ingress provides routing rules to manage remote users’ access to services in a Kubernetes cluster. With Ingress, simple rules can be set for routing traffic without exposing every service on the endpoint. While setting up Ingress, a few issues surfaced due to our inexperience with Kubernetes. Because the online documentation of Ingress nginx was rather limited, setting up Ingress took a fair amount of time. Afterwards, Ingress nginx was replaced by Contour. By this time we had more experience with Kubernetes. Because of this, the switch to Contour went smoothly.
The centralized logging system aims to obtain the logs of the API and the Android application in a central place so that it would be easier to detect errors. To obtain and view the logs in a central place, Graylog is used. We chose Graylog because it is much easier to use than Kibana. We knew that Kibana has more features than Graylog, but for this project it was not required.
Prometheus was used to observe the API. Prometheus collects metrics from targets by scraping given HTTP endpoints. Actuator is a Spring Boot plugin that is used to manage and monitor your application by using HTTP endpoints. This makes it possible for Prometheus to gather metrics and monitor the health of the application. Grafana then uses Prometheus as a source to display the metrics on a custom dashboard.
We were unsure how to add a target to Prometheus while it was running on Kubernetes.
Because we were not sure how to modify the config file, a lot of time was lost.
To solve this problem we had to create a secret that contains the target.
This secret should then be added to the
When rebooting the Prometheus pod, the target was added.
During this internship there were a lot of new technologies covered such as Spring, Kubernetes, Graylog and so on. The internship assignment consisted of three main parts including the Android application, the Spring Boot API and Kubernetes. As described earlier, the biggest problem that we experienced was that Android Native is not that user friendly in terms of design. If we were to start over, we would go for a Hybrid approach like Ionic or NativeScript instead of Android Native. Once we had the basics of the application, we started working on the Spring Boot API. Spring was completely new to us, but because of the complete and structured online documentation, the learning process went smoothly with some guidance from our mentors. The mentors had a few remarks. The remarks were justified, they were about our structure and programming style. In order to improve the structure, Domain-Driven Design was recommended by our mentors. As a last important part, we deployed the API on Kubernetes. Besides the API, a few other things were implemented on the Kubernetes cluster such as Graylog and Grafana. Kubernetes was completely new to us as it was not covered in the graduate program Application Development. We found that the learning curve is quite steep in the beginning because it were all new concepts. Because of the proper guidance from our mentors, we got through this initial period. At the end of the internship, working with Kubernetes didn’t scare us anymore and dealing with it was less difficult.
We would like to thank Frederick Bousson for offering us this wonderful opportunity. Furthermore, we would like to thank Jeff Mesens and Nils Devos. Thanks to their contribution we were able to successfully complete this project. Without their help, we could not have made such a great evolution.