The Akka.CQRS sample used in this workshop uses a build system built on top of Petabridge’s standard dotnet new
template.
One thing you’ll need to do multiple times during the course of this workshop is build new versions of your Docker images - this document outlines how the Akka.CQRS build system works and how to do this.
Update RELEASE_NOTES.md
The first step to publishing a new Docker image for all of the Akka.CQRS projects is to update the top entry of the RELEASE_NOTES.md
file:
#### 0.1.0 May 26 2019 ####
* Fixed `NullReferenceException` when recovering `MatchAggregatorSnapshot` records with no price and volume updates.
* Fixed issue with BSON serialization for `MatchAggregatorSnapshot` records.
To bump the version number on your Docker images, add a new entry using this format to the top of the _RELEASE_NOTES.md markdown file along with a description of your changes:
#### 0.1.1 May 27 2019 ####
* Reduced verbosity of the logging system.
#### 0.1.0 May 26 2019 ####
* Fixed `NullReferenceException` when recovering `MatchAggregatorSnapshot` records with no price and volume updates.
* Fixed issue with BSON serialization for `MatchAggregatorSnapshot` records.
The version number at the top of the RELEASE_NOTES.md
file dictates the version number added to all of the Docker image tags we’re going to create, so choose it accordingly!
Run build.cmd
on Windows or build.sh
on OS X / Linux
Once RELEASE_NOTES.md
is updated, we then need to execute the following command:
./build.cmd Docker [phobos] [remoteRegistry={YOUR_REMOTE_DOCKER_URL}]`
Advanced Usage: Pass in the
phobos
flag on the commandline if you want to add Phobos Akka.NET actor monitoring and tracing support to this Docker image - otherwise it’ll be left off by default.You can also pass in a URL to a remote registry, such as
myazurecontainerregistery.acr.io
, to have Docker images tagged for that remote Docker image registry via the./build.cmd Docker remoteRegistry=myazurecontainerregistery.acr.io
parameter.However, you will need to make sure your build server or your local development machine has completed the necessary
docker login
steps before you run this command.
Under the covers, this command executes the following workflow:
Call dotnet publish
on Every .Service
or .Web
.csproj
We only want to publish our .NET Core applications as Docker images, not our unit tests and and libraries, thus that’s why we have the following filtering code-in place in our build.fsx
file:
Target "PublishCode" (fun _ ->
let projects = !! "src/**/*.Service.csproj" // publish services and web only
++ "src/**/*.Web.csproj"
let runSingleProject project =
DotNetCli.Publish
(fun p ->
{ p with
Project = project
Configuration = configuration
VersionSuffix = overrideVersionSuffix project
AdditionalArgs = ["--no-restore --output bin/Release/netcoreapp2.1/publish"] // would be ideal to change publish dir via MSBuild
})
projects |> Seq.iter (runSingleProject)
)
The dotnet publish
command packs all of the binaries and other resources our application needs to run into a single set of folders: bin/Release/netcoreapp2.1/publish
typically. This is what is typically used for all kinds of .NET Core binary deployments and Docker is no exception.
Call docker build
on every .Service
or .Web
.csproj
Once all of the content is published into the bin/Release/netcoreapp2.1/publish
folder for each project, our Dockerfiles can be written to copy those binaries into the working directory our application will use inside each Docker container:
FROM microsoft/dotnet:2.1-sdk AS base
WORKDIR /app
# should be a comma-delimited list
ENV CLUSTER_SEEDS "[]"
ENV CLUSTER_IP ""
ENV CLUSTER_PORT "6055"
ENV MONGO_CONNECTION_STR "" #MongoDb connection string for Akka.Persistence
COPY ./bin/Release/netcoreapp2.1/publish/ /app
# 9110 - Petabridge.Cmd
# 6055 - Akka.Cluster
EXPOSE 9110 6055
# Install Petabridge.Cmd client
RUN dotnet tool install --global pbm
# Needed because https://stackoverflow.com/questions/51977474/install-dotnet-core-tool-dockerfile
ENV PATH="${PATH}:/root/.dotnet/tools"
# RUN pbm help
CMD ["dotnet", "Akka.CQRS.Pricing.Service.dll"]
This Dockerfile is from the Akka.CQRS.Pricing.Service. As you can see, the COPY
instruction is what copies the output from the dotnet publish
command we executed a step earlier.
Mapping .csproj Names to Docker Images
let mapDockerImageName (projectName:string) =
match projectName with
| "Akka.CQRS.TradeProcessor.Service" -> Some("akka.cqrs.tradeprocessor")
| "Akka.CQRS.TradePlacers.Service" -> Some("akka.cqrs.traders")
| "Akka.CQRS.Pricing.Service" -> Some("akka.cqrs.pricing")
| "Akka.CQRS.Pricing.Web" -> Some("akka.cqrs.pricing.web")
| _ -> None
The reason why we do this is because Docker has a specific convention it enforces for image names:
An image name is made up of slash-separated name components, optionally prefixed by a registry hostname. The hostname must comply with standard DNS rules, but may not contain underscores. If a hostname is present, it may optionally be followed by a port number in the format :8080. If not present, the command uses Docker’s public registry located at registry-1.docker.io by default. Name components may contain lowercase letters, digits and separators. A separator is defined as a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator.
Since all image names must be lowercase, we have to convert the normal Pascal case used by .NET namespaces into something the Docker runtime will accept, hence this mapping function.
Tagging Docker Images
Now for the final stage, tagging the Docker images. This is done using the version number from the ‘RELEASE_NOTES.md’ file, the mapped Docker image name, and the remoteRegistry
value if you provided one.
let buildDockerImage imageName projectPath =
let args =
if(hasBuildParam "remoteRegistry") then
StringBuilder()
|> append "build"
|> append "-t"
|> append (imageName + ":" + releaseNotes.AssemblyVersion)
|> append "-t"
|> append (imageName + ":latest")
|> append "-t"
|> append (remoteRegistryUrl + "/" + imageName + ":" + releaseNotes.AssemblyVersion)
|> append "-t"
|> append (remoteRegistryUrl + "/" + imageName + ":latest")
|> append "."
|> toText
else
StringBuilder()
|> append "build"
|> append "-t"
|> append (imageName + ":" + releaseNotes.AssemblyVersion)
|> append "-t"
|> append (imageName + ":latest")
|> append "."
|> toText
ExecProcess(fun info ->
info.FileName <- "docker"
info.WorkingDirectory <- Path.GetDirectoryName projectPath
info.Arguments <- args) (System.TimeSpan.FromMinutes 5.0) (* Reasonably long-running task. *)
All Docker images will automatically be tagged with the version number you provided AND the latest
tag.
Upon successfully building your Docker images, you should see output similar to the following (using 0.1.0
in our RELEASE_NOTES.md
, in this case):
- Read more about:
- Akka.NET
- Case Studies
- Videos
Observe and Monitor Your Akka.NET Applications with Phobos
Did you know that Phobos can automatically instrument your Akka.NET applications with OpenTelemetry?
Click here to learn more.