An ASP.net Web app running in containers orchestrated by Docker Compose. In this workshop, all commands are run from the root folder of project unless otherwise noted.
We have prepared Git branches with solutions for each part - they are written out in bold text, e.g.
- Install
git
- Install the latest version of
.Net Core
. - Install the latest version of
Docker
. - Install
Visual Studio Code
. - Run
docker run --rm microsoft/dotnet:2.1-sdk-alpine
. - Run
docker run --rm microsoft/dotnet:2.1-aspnetcore-runtime-alpine
. - Install postman.
In this part you will create a simple ASP.Net web app and web API, familiarising yourself with their architectures.
mkdir Workshop
cd Workshop
dotnet new mvc --no-https
code .
- When Visual Studio Code asks if you want to add "required assets to build and debug", press yes.
Ctrl + Shift + B
to build (Shift + ⌘ + B
on a Mac)F5
to debug- You should see the default ASP.Net website template running in your browser.
Now you'll add a simple 2048 game and high score services.
- Add the content of the
Game
folder to yourwwwroot
folder. - Verify that the game runs on http://localhost:5000/Game.html.
- Add
<li><a href="Game.html">Game</a></li>
toviews/Shared/_Layout.cshtml
. - Verify that the game can be opened from the menu
- Create a directory called
Services
- In it, create the interface of a simple high score service,
IHighScoreService.cs
. - Implement it in a new file called
HighScoreService.cs
in the same directory. - Add it as a singleton to the service collection in
Startup.cs
- Create
HighScoreController
. Check that it works by visiting http://localhost:5000/api/highscore (You might need to restart the server). - Verify the value is 0, posting a high score of 1 using Postman and content type JSON, and verifying that the new high score is indeed one.
- Create a new JS class in
high_score_manager.js
inwwwroot/js
- The class should have a
setHighScore
and agetHighScore
method which should use your highscore service to get and update the highscore - Add this file to
Game.html
's script imports. - Add
HighScoreManager
toapplication.js
. - Use the
HighScoreManager
ingame_manager.js
. - Verify that
scores/highScore.json
is written to disk in the bin-folder.
Now we'll host the webapp in a Docker container
- Update
Program.cs
and add.UseUrls("http://*:5000")
. - Create a
Dockerfile
to build and then contain the application - Create a
.dockerignore
file to copy the minimum needed files to the build context - Run
docker build -t dips/workshop .
to build the Docker image. The-t
switch tells Docker to associate the tagdips/workshop
with the newly built image. - Run
docker run -p 5000:5000 dips/workshop
to start the new container with our app. The-p
switch tells Docker to connect one of our computers's (the host) network ports to one on our new container. - Verify that the app works in the browser.
- By default, Docker containers will run in the background even if we interrupt them with Ctrl+C -
docker ps
gives us a list of the currently running containers, anddocker kill <id>
can shut down a container.
- Run
docker run -p 5000:5000 -v <Path-To-Project>/scores:/app/scores dips/workshop
- For Windows, you need to enable drive sharing in Docker settings. If you're using Docker Toolbox on Windows, this path has to be inside
C:\Users
.
We don't want the website and API in the same app. Let's fix this.
- Create a new folder called
web
in the root folder. - Move all files and folders to
web
, exceptLICENSE
andreadme.md
. - Recreate
.dockerignore
if it got lost in the moving process
- Create a new folder called
api
in the root folder. - Navigate into the
api
folder and create a new appdotnet new webapi --no-https
- Open the project in VS Code
code .
- Update
Program.cs
and add.UseUrls("http://*:5000")
. - Move
IHighScoreService
,HighScoreService
andHighScoreController
to the new app. - Move
services.AddSingleton<IHighScoreService, HighScoreService>();
to the newStartup
. - Remove
using Workshop.Services;
from the originalStartup
. - Verify that the new service works using Postman.
All commands here are called from the api
-folder.
- Create a
Dockerfile
to build and then contain the application - Create a
.dockerignore
file to copy the minimum needed files to the build context - In
Startup.cs
, addservices.AddCors();
topublic void ConfigureServices(IServiceCollection services)
andapp.UseCors(builder => builder.WithOrigins("http://localhost").AllowAnyMethod().AllowAnyHeader().AllowCredentials());
topublic void Configure(IApplicationBuilder app, IHostingEnvironment env)
beforeapp.UseMvc();
. - Run
docker build -t dips/api .
to build the docker image - Run
docker run -p 5000:5000 -v <Path-To-Project>/scores:/app/scores dips/api
to run the app through the container - Verify it works using Postman.
- Create a new file,
docker-compose.yml
, in the root folder. - Navigate to the
web
folder and rundocker build -t dips/workshop .
- Navigate back to the root folder and run
docker-compose up
. - Enjoy your apps working together.
Congratulations! You now have a multi-container web application orchestrated by Docker Compose. Note how the app's concerns have been separated across the containers: so long as the API is available, our game doesn't care about how scores are stored. If we wanted, we could implement new high-score service backed by a proper database, and transparently replace the old service on-the-fly.
- Right now, we have to manually set up CORS headers to allow the user to submit high scores, due to the same-origin policy. A far better way of handling this is to set up a reverse proxy - like Traefik or Nginx - to handle user requests and pass them on to the correct container. Add a reverse proxy to your application to intercept requests to
api/highscore
and pass them to theapi
container. - While Docker gives you command-line tools to manage running containers, they can get a bit hairy if you're running lots of applications with multiple containers. To make your life a bit easier, there are services which provide GUIs for container management, such as Portainer. Extend your
docker-compose.yml
file with a Portainer service to manage the containers from your browser.