The main objective of this lab is to learn to build a complete Web infrastructure. This means, we will build a server infrastructure that serves a static Web site and a dynamic HTTP API. The diagram below shows the architecture of the infrastructure that we will build.
graph LR
subgraph Client
B((Browser))
end
subgraph Server
RP(Reverse\nProxy)
SS(Static\nWeb server)
DS(Dynamic\nAPI server)
end
B -.-> RP
RP --> SS
RP --> DS
In addition to the basic requirement of service static and dynamic content, the infrastructure will have the following features:
- Scalability: both the static and the dynamic server will be deployed as a cluster of several instances. The reverse proxy will be configured to distribute the load among the instances.
- Security: the connection between the browser and the reverse proxy will be encrypted using HTTPS.
- Management: a Web application will be deployed to manage the infrastructure. This application will allow to start/stop instances of the servers and to monitor the state of the infrastructure.
- This is a BIG lab and you will need a lot of time to complete it.
- You will work in groups of 2 students and use a Git workflow to collaborate.
- For certain steps you will need to do research in the documentation by yourself (we are here to help, but we will not give you step-by-step instructions!) or you will need to be creative (do not expect complete guidelines).
- Read carefully all the acceptance criteria of each step. They will tell you what you need to do to complete the step.
- After the lab, each group will perform a short demo of their infrastructure.
- You have to write a report with a short descriptioin for each of the steps. Please do that directly in the repo, in one or more markdown files. Start in the README.md file at the root of your directory.
- The report must contain the procedure that you have followed to prove that your configuration is correct (what you did do make the step work and what you would do if you were doing a demo).
Create a GitHub repository for your project. You will use this repository to collaborate with your team mate. You will also use it to submit your work.
Important
Be careful to keep a clear structure of the repository such that the different components are clearly separated.
- You have created a GitHub repository for your project.
- The respository contains a Readme file that you will use to document your project.
The goal of this step is to build a Docker image that contains a static HTTP server Nginx. The server will serve a static Web site. The static Web site will be a single page with a nice looking template. You can use a free template for example from Free-CSS or Start Bootstrap.
- You have created a separate folder in your respository for your static Web server.
- You have a Dockerfile based on the Nginx image. The Dockerfile copies the static site content into the image.
- You have configured the
nginx.conf
configuration file to serve the static content on a port (normally 80). - You are able to explain the content of the
nginx.conf
file. - You can run the image and access the static content from a browser.
- You have documented your configuration in your report.
The goal of this step is to use Docker compose to deploy a first version of the infrastructure with a single service: the static Web server.
In addition to the basic docker compose configuration, we want to be able to rebuild the docker image of the Web server. See the Docker compose Build documentation for this part.
- You have added a docker compose configuration file to your GitHub repo.
- You can start and stop an infrastructure with a single static Web server using docker compose.
- You can access the Web server on your local machine on the respective port.
- You can rebuild the docker image with
docker compose build
- You have documented your configuration in your report.
This step requires a more work. The goal is to build a HTTP API with Javalin. You can implement any API of your choice, such as:
- an API to manage a list of quotes of the day
- an API to manage a list of TODO items
- an API to manage a list of people
Use your imagination and be creative!
The only requirement is that the API supports at all CRUD operations, i.e.: Create, Read, Update, Delete.
Use a API testing tool such as Insomnia, Hoppscotch or Bruno to test all these operations.
The server does not need to use a database. You can store the data in memory. But if you want to add a DB, feel free to do so.
Once you're finished with the implementation, create a Dockerfile for the API server. Then add it as a service to your docker compose configuration.
- Your API supports all CRUD operations.
- You are able to explain your implementation and walk us through the code.
- You can start and stop the API server using docker compose.
- You can access both the API and the static server from your browser.
- You can rebuild the docker image with docker compose.
- You can do demo where use an API testing tool to show that all CRUD operations work.
- You have documented your implementation in your report.
The goal of this step is to place a reverse proxy in front of the dynamic and static Web servers such that the reverse proxy receives all connections and relays them to the respective Web server.
You will use Traefik as a reverse proxy. Traefik interfaces directly with Docker to obtain the list of active backend servers. This means that it can dynamically adjust to the number of running server. Traefik has the particularity that it can be configured using labels in the docker compose file. This means that you do not need to write a configuration file for Traefik, but Traefik will read container configurations from the docker engine through the file /var/run/docker.sock
.
The steps to follow for this section are thus:
- Add a new service "reverse_proxy" to your docker compose file using the Traefik docker image
- Read the Traefik Quick Start documentation to establish the basic configuration.
- Read the Traefik & Docker documentation to learn how to configure Traefik to work with Docker.
- Then implement the reverse proxy:
- relay the requests coming to "localhost" to the static HTTP server
- relay the requests coming to "localhost/api" to the API server. See the Traefik router documentation for managing routes based on path prefixes.
- you will have to remove the
ports
configuration from the static and dynamic server in the docker compose file and replace them withexpose
configuration. Traefik will then be able to access the servers through the internal Docker network.
- You can use the Traefik dashboard to monitor the state of the reverse proxy.
- You can do a demo where you start from an "empty" Docker environment (no container running) and using docker compose you can start your infrastructure with 3 containers: static server, dynamic server and reverse proxy
- In the demo you can access each server from the browser in the demo. You can prove that the routing is done correctly through the reverse proxy.
- You are able to explain in the documentation how you have implemented the solution and walk us through the configuration and the code.
- You are able to explain in the documentation why a reverse proxy is useful to improve the security of the infrastructure.
- You are able to explain in the documentation how to access the dashboard of Traefik and how it works.
- You have documented your configuration in your report.
The goal of this section is to allow Traefik to dynamically detect several instances of the (dynamic/static) Web servers. You may have already done this in the previous step 3.
Modify your docker compose file such that several instances of each server are started. Check that the reverse proxy distributes the connections between the different instances. Then, find a way to dynamically update the number of instances of each service with docker compose, without having to stop and restart the topology.
- You can use docker compose to start the infrastructure with several instances of each server (static and dynamic).
- You can dynamically add and remove instances of each server.
- You can do a demo to show that Traefik performs load balancing among the instances.
- If you add or remove instances, you can show that the load balancer is dynamically updated to use the available instances.
- You have documented your configuration in your report.
By default, Traefik uses round-robin to distribute the load among all available instances. However, if a service is stateful, it would be better to send requests of the same session always to the same instance. This is called sticky sessions.
The goal of this step is to change the configuration such that:
- Traefik uses sticky session for the dynamic server instances (API service).
- Traefik continues to use round robin for the static servers (no change required).
- You do a setup to demonstrate the notion of sticky session.
- You prove that your load balancer can distribute HTTP requests in a round-robin fashion to the static server nodes (because there is no state).
- You prove that your load balancer can handle sticky sessions when forwarding HTTP requests to the dynamic server nodes.
- You have documented your configuration and your validation procedure in your report.
Any real-world web infrastructure must be secured with HTTPS instead of clear-text HTTP. The goal of this step is to configure Traefik to use HTTPS with the clients. The schema below shows the architecture.
graph LR
subgraph Client
B((Browser))
end
subgraph Server
RP(Reverse\nProxy)
SS(Static\nWeb server)
DS(Dynamic\nAPI server)
end
B -. HTTPS .-> RP
RP -- HTTP --> SS
RP -- HTTP --> DS
This means that HTTPS is used for connection with clients, over the Internet. Inside the infrastructure, the connections between the reverse proxy and the servers are still done in clear-text HTTP.
To do this, you will first need to generate an encryption certificate. Since the system is not exposed to the Internet, you cannot use a public certificate such as Let's encrypt, but have to generate a self-signed certificate. You can do this using openssl.
Once you got the two files (certificate and key), you can place them into a folder, which has to be mounted as a volume in the Traefik container. You can mount the volume at any path in the container, for example /etc/traefik/certificates
.
Up to now, you've configured Traefik through labels directely in the docker compose file. However, it is not possible to specify the location of the certificates to Traefik with labels. You have to create a configuration file traefik.yaml
.
Again, you have to mount this file into the Traefik container as a volume, at the location /etc/traefik/traefik.yaml
.
The configuration file has to contain several sections:
- The providers section to configure Traefik to read the configuration from Docker.
- The entrypoints section to configure two endpoints:
http
andhttps
. - The tls section to configure the TLS certificates. Specify the location of the certificates as the location where you mounted the directory into the container (such as
/etc/traefik/certificates
). - In order to make the dashboard accessible, you have to configure the api section. You can remove the respective labels from the docker compose file.
Finally, you have to activate HTTPS for the static and dynamic servers. This is done in the docker compose file. You have to add two labels to each server:
- to activate the HTTPS entrypoint,
- to set TLS to true.
See the Traefik documentation for Docker for these two labels.
After these configurations it should be possible to access the static and the dynamic servers through HTTPS. The browser will complain that the sites are not secure, since the certificate is self-signed. But you can ignore this warning.
If it does not work, go to the Traefik dashboard and check the configuration of the routers and the entrypoints.
- You can do a demo where you show that the static and dynamic servers are accessible through HTTPS.
- You have documented your configuration in your report.
If you sucessfully complete all the steps above, you can reach a grade of 5.0. If you want to reach a higher grade, you can do one or more of the following optional steps.
The goal of this step is to deploy or develop a Web app that can be used to monitor and update your Web infrastructure dynamically. You should be able to list running containers, start/stop them and add/remove instances.
- you use an existing solution (search on Google)
- for extra points, develop your own Web app. In this case, you can use the Dockerode npm module (or another Docker client library, in any of the supported languages) to access the docker API.
- You can do a demo to show the Management UI and manage the containers of your infrastructure.
- You have documented how to use your solution.
- You have documented your configuration in your report.
This is a step into unknow territory. But you will figure it out.
The goal of this step is to change your static Web page to periodically make calls to your API server and show the results in the Web page. You will need JavaScript for this and this functionality is called AJAX.
Keep it simple! You can start by just making a GET request to the API server and display the result on the page. If you want, you can then you can add more features, but this is not obligatory.
The modern way to make such requests is to use the JavaScript Fetch API. But you can also use JQuery if you prefer.
- You have added JavaScript code to your static Web page to make at least a GET request to the API server.
- You can do a demo where you show that the API is called and the result is displayed on the page.
- You have documented your implementation in your report.