Before we get started, if you don't know about Docker, you should definitely have a read up about Docker.
Docker containers wrap up software and its dependencies which allows developers to build, ship and run applications anywhere.
Prerequisites
Before starting this exercise, we had a basic build plan that clones our repo and runs a dotnet publish
.
We had to use a linux build agent in bamboo to use specific tools (docker, gcloud, ssh, etc.)
Docker configuration files
To "dockerize" our app, we need both a Dockerfile
and a docker-compose-<environment>.yml
Dockerfile
A simple text file that contains the commands a user can call to assemble an image, that can execute command-line instructions in succession
docker-compose.yml
A tool for defining and running multi-container Docker applications, across multiple isolated environments
docker-compose transformation
We need to add our image name & tag to the docker-compose file so that our build server knows what image to deploy later on
Building the Docker image
Build the my-console-app image with the branch & build number as the tag
docker build -t gcr.io/my-org/my-console-app:${bamboo.repository.git.branch}-${bamboo.buildNumber} .
Push to our docker registry
Push the image to the gcloud docker registry, so that we can pull it & use it later on
gcloud docker -- push gcr.io/my-org/my-console-app:${bamboo.repository.git.branch}-${bamboo.buildNumber}
Deployment
As we want this to run on a scheduled basis (once a day), we don't want the containter to be always running. We'll deploy it once per day, the container will auto start and execute our code & then close the container when completed
Docker swarm
Docker Swarm is a clustering and scheduling tool for Docker containers. With Swarm, developers can establish and manage a cluster of Docker nodes as a single virtual system.
Create deployment directory on our docker swarm
ssh dev@${bamboo.SwarmMasterIp} '[[ -d deployments/${bamboo.deploy.project} ]] || mkdir deployments/${bamboo.deploy.project}'
Deploy to docker swarm (& run the container)
Remote onto our docker swarm and download the version of the container we want to deploy
ssh -p 14122 dev@${bamboo.SwarmMasterIp} 'gcloud docker -- pull gcr.io/my-org/my-console-app:${bamboo.planRepository.branch}-${bamboo.buildNumber}'
(Secure) Copy the docker-compose file to our docker node
scp -P 14122 docker-compose-production.yml dev@${bamboo.SwarmMasterIp}:~/deployments/${bamboo.deploy.project}
Deploy our application 'stack' to the swarm using the config defined in our compose file
ssh -p 14122 dev@${bamboo.SwarmMasterIp} 'docker stack deploy -c ~/deployments/${bamboo.deploy.project}/docker-compose-production.yml myorg-prod --with-registry-auth'
Issues & concerns
The main issues I'd encountered while doing the above were:
- Unsure that correct files were copied to the container
A quick resolution for this was to add the following line to our Dockerfile:
RUN ls
or to view every file on the container:RUN find "$PWD"
- Incorrect RUN command
A hard one to find but I got there eventually - as I'd copied the RUN command from the microsoft docs, I assumed I was having problems elsewhere - it turns out that our container didn't like the
RUN ["dotnet", "my-app.dll"]
command, as a./
was missing at the start of our dll path! - Using the correct image
For most popular docker images, there are many different tags which are build differently with different features in the container. We could have used the
aspnet
image as we do in all of our other docker applications (which are all APIs) but in our case, it doesn't make sense as we don't need all of the tools that are included in the image. We swapped to use thedotnet-runtime
image (which is almost 50% smaller), meaning a faster pipeline (i.e. building, pushing and pulling the image)
Debugging
To view the logs of your container while / after it has ran, portainer is a useful tool that lets you view service & container logs. If this tool isn't available, you can do this directly by ssh'ing onto the swarm and viewing service logs:
ssh dev@$DockerSwarmNode
docker service logs <serviceID>
You can find the serviceID from running either of the following:
docker ps
docker ps | grep my-app
Remoting onto containers
Not applicable for our scenario but useful for long running tasks or persisting containers such as API's.
docker exec -t -i <containerID> /bin/bash
ls