<?xml version="1.0" encoding="utf-8"?>

<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator>
  <link href="https://www.softwaredeveloper.blog/tag/docker/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://www.softwaredeveloper.blog/tag/docker" rel="alternate" type="text/html" hreflang="en" />
  <updated>2026-02-26T06:03:45+00:00</updated>
  <id>https://www.softwaredeveloper.blog/tag/docker</id>

  
  
  

  
    <title type="html">Software Developer Blog | </title>
  

  
    <subtitle>Software craftsmanship and agile project management</subtitle>
  

  

  
    
      
    
  

  
  

  
    
    
    
    <entry>
      <title type="html">Optimize Docker .NET Core project and docker-compose solution to reduce build image time</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg</url>
          <title>Optimize Docker .NET Core project and docker-compose solution to reduce build image time</title>
          <link>https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image" rel="alternate" type="text/html" title="Optimize Docker .NET Core project and docker-compose solution to reduce build image time" />
      <published>2020-03-20T14:00:00+00:00</published>
      <updated>2020-06-12T20:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image">&lt;p&gt;&lt;em&gt;Docker&lt;/em&gt; has built in great cache mechanism, but to be able to use it, you have to understand how it works.
Let’s dive into it, to build &lt;em&gt;.NET Core Docker&lt;/em&gt; images faster.&lt;/p&gt;

&lt;h2 id=&quot;how-docker-cache-works&quot;&gt;How Docker cache works&lt;/h2&gt;
&lt;p&gt;First we should understand how &lt;em&gt;Docker&lt;/em&gt; cache works. This part is described nice in &lt;a href=&quot;https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache&quot;&gt;official documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;Starting with a base image that is already in the cache, the next instruction is compared against all child images derived from that base image to see if one of them was built using the exact same instruction. If not, the cache is invalidated.&lt;/li&gt;
    &lt;li&gt;In most cases simply comparing the instruction in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; with one of the child images is sufficient. However, certain instructions require a little more examination and explanation.&lt;/li&gt;
    &lt;li&gt;For the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ADD&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; instructions, the contents of the file(s) in the image are examined and a checksum is calculated for each file. The last-modified and last-accessed times of the file(s) are not considered in these checksums. During the cache lookup, the checksum is compared against the checksum in the existing images. If anything has changed in the file(s), such as the contents and metadata, then the cache is invalidated.&lt;/li&gt;
    &lt;li&gt;Aside from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ADD&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; commands, cache checking will not look at the files in the container to determine a cache match. For example, when processing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN apt-get -y update&lt;/code&gt; command the files updated in the container will not be examined to determine if a cache hit exists. In that case just the command string itself will be used to find a match.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;Once the cache is invalidated, all subsequent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; commands will generate new images and the cache will not be used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So it always checks if given instruction is started in exactly the same situation as in the past.
If it is, it doesn’t start it, but this layer is taken from cache.
If there is even &lt;em&gt;one bit&lt;/em&gt; changed, for example one copied file differs, then cache is invalidated and instruction is invoked &lt;em&gt;truly&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-to-decrease-image-build-time&quot;&gt;How to decrease image build time&lt;/h2&gt;
&lt;p&gt;To build images faster, put everything which will rarely change, above things which change often.
For example, when you compile your application inside &lt;em&gt;Dockerfile&lt;/em&gt;, then your source files often change, which invalidates cache all the way down.
But you probably download some dependencies, for example &lt;em&gt;Nuget&lt;/em&gt; packages, which change
less often than source code, so you can put it before compilation. This way as long as you stick to existing dependencies, they are taken from cache.
When you add some new dependency or change version of existing one, cache is invalidated and packages are downloaded from the Internet (and new cache is created).&lt;/p&gt;

&lt;h3 id=&quot;ignore-what-you-dont-need&quot;&gt;Ignore what you don’t need&lt;/h3&gt;
&lt;p&gt;When you copy files from host to &lt;em&gt;build context&lt;/em&gt;, Docker calculates checksum for them. If exactly the same files were copied to exactly
the same directory in Docker image, then this layer (as every operation in Dockerfile creates new layer) is taken from cache.
So copy only those files which you need, because you can invalidate cache in vain, with files which are not required in your image, for example
every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;, or files generated by your IDE and so on.
Remember, that &lt;em&gt;.dockerignore&lt;/em&gt; file has to be located in &lt;em&gt;build context&lt;/em&gt; root. Example rules:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*/bin
*/obj
.dockerignore // Docker files
.env
.git // Git files
.gitignore // Git files
.gitattributes // Git files
.vs // IDE files
.vscode // IDE files
**/.toolstarget
.idea // IDE files
**/Samples // Additional files
build
Docker // Docker scripts and docker-compose files (in this example in &apos;Docker&apos; directory, in context root level)
**/Dockerfile // Docker files
**/docker-compose // Docker files
*Test*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;try-to-create-your-own-cache-yarn-build-example&quot;&gt;Try to create your own cache (yarn build example)&lt;/h3&gt;
&lt;p&gt;When you know where is your bottleneck and there is no obvious way to reduce it, try to figure out some none obvious solution.
For example a few projects in one of products which I maintain, have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn install&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn build&lt;/code&gt; sewn into &lt;em&gt;csproj&lt;/em&gt; file, like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;Target Name=&quot;PublishRunWebpack&quot; AfterTargets=&quot;ComputeFilesToPublish&quot; &amp;gt;
  &amp;lt;Exec WorkingDirectory=&quot;$(SpaRoot)&quot; Command=&quot;yarn install&quot; /&amp;gt;
  &amp;lt;Exec WorkingDirectory=&quot;$(SpaRoot)&quot; Command=&quot;yarn build&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;but this was extremely slow - it took (in &lt;em&gt;Docker for Windows&lt;/em&gt;) around 4 minutes.
And web files in those projects were changed rarely, usually C# code got changed, so I wanted to reuse &lt;em&gt;yarn&lt;/em&gt; build results. I didn’t find a way to use &lt;em&gt;npm&lt;/em&gt;/&lt;em&gt;yarn&lt;/em&gt; cache in this situation, so I
cached it myself. I moved copying web files to the top of Dockerfile, like this:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app/WebClient/WebFiles&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; WebClient/WebFiles . # Taken form cache as long as you don&apos;t change files in WebFiles directory&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;yarn &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Taken form cache as long as you don&apos;t change files in WebFiles directory&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;yarn build &lt;span class=&quot;c&quot;&gt;# Taken form cache as long as you don&apos;t change files in WebFiles directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and changed &lt;em&gt;csproj&lt;/em&gt; like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;Target Name=&quot;PublishRunWebpack&quot; AfterTargets=&quot;ComputeFilesToPublish&quot; Condition=&quot; &apos;$(Configuration)&apos; == &apos;Release&apos; And !Exists(&apos;$(ProjectDir)WebFiles/build&apos;) &quot;&amp;gt;
  &amp;lt;Exec WorkingDirectory=&quot;$(SpaRoot)&quot; Command=&quot;yarn install&quot; /&amp;gt;
  &amp;lt;Exec WorkingDirectory=&quot;$(SpaRoot)&quot; Command=&quot;yarn build&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So Docker cached &lt;em&gt;yarn&lt;/em&gt; build output and it was invalidated only when web files changed. With target condition, C# project reused already existing &lt;em&gt;yarn&lt;/em&gt; build output.
Of course, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY WebClient/WebFiles&lt;/code&gt; was on top of my Dockerfile, every time when some web files changed, it invalidated cache all the way down,
so if it was changed often, I would do it some different way, but in my case it worked as expected.
And I know that in theory incorrect build directory could exist somehow, but it’s only theory, because in &lt;em&gt;Docker&lt;/em&gt; situation is repeatable and on
developer machines publish wasn’t invoked that way or another, only debug configuration, so in practice it’s not a risk. Maybe with &lt;a href=&quot;http://www.digitallycreated.net/Blog/67/incremental-builds-in-msbuild-and-how-to-avoid-breaking-them&quot;&gt;incremental build&lt;/a&gt; I could be able to achieve the same with a bit cleaner way - if your case is similar to mine, you can try it.&lt;/p&gt;

&lt;h3 id=&quot;reuse-what-you-can&quot;&gt;Reuse what you can&lt;/h3&gt;
&lt;p&gt;If there is something which you can reuse, do it.
For example when I have multiple &lt;em&gt;Docker&lt;/em&gt; images in one solution, I build them all at once, instead of building every project in separation.
I have observed, that this way it’s faster. For example, I have solution &lt;em&gt;Application.sln&lt;/em&gt; which contains 5 executable projects (which are packed to separate &lt;em&gt;Docker&lt;/em&gt; images)
and additional projects like tests, usage samples and so on. So to lower number of projects to build, I have copied it to &lt;em&gt;Application-minimal.sln&lt;/em&gt; and left only necessary projects.
Then in every &lt;em&gt;Dockerfile&lt;/em&gt;, instead of compiling only one, chosen project, like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet publish PROJECT_NAME -c Release -o out&lt;/code&gt;
I compile whole solution like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet publish Application-minimal.sln -c Release&lt;/code&gt;. After compilation, I copy whole build output (with all projects) 
to final image (see &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image#docker-multi-stage-build&quot;&gt;&lt;em&gt;Docker&lt;/em&gt; multi-stage build explained&lt;/a&gt;), 
for example this way: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=build-env /app/build .&lt;/code&gt;. Finally, at the very bottom of &lt;em&gt;Dockerfile&lt;/em&gt; I invalidate cache specifying which project I want to run:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app/ProjectA/publish # Pay attention to have correct path, especially if you set custom build output path.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;ProjectA.dll&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Everything above is reused from cache, so when you want to build &lt;em&gt;n&lt;/em&gt; projects, only first project takes a while to compile, other projects are taken from cache practically whole.
In my case I’ve observed that it works faster than doing it in separation (running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet publish PROJECTX&lt;/code&gt; for every project). It will probably be faster in your case too, but as with every optimization,
I encourage to measure it before applying :)&lt;/p&gt;

&lt;h3 id=&quot;experiment-with-caching-nuget-dependencies&quot;&gt;Experiment with caching nuget dependencies&lt;/h3&gt;
&lt;p&gt;In your developer PC, you have nuget cached locally. But in &lt;em&gt;Docker&lt;/em&gt; you have clean situation every time you invoke build, so nuget packages are taken from the internet every time, so it’s good to cache them,
because we don’t change project dependencies as often as project source files.
When you compile only one project, then situation is clear, from project directory you can run:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; *.csproj ./ # Taken form cache as long as you don&apos;t change your project file&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet restore &lt;span class=&quot;c&quot;&gt;# Taken form cache as long as you don&apos;t change your project file &lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Copy everything else and build&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But when you want to restore files for &lt;a href=&quot;#reuse-what-you-can&quot;&gt;whole solution&lt;/a&gt;, then it gets more complicated, because (at the time of writing) you cannot point
to &lt;em&gt;.sln&lt;/em&gt; file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet restore&lt;/code&gt; command, and you cannot point to multiple &lt;em&gt;csproj&lt;/em&gt; files neither.
Fortunately, you can resolve it in many ways, for example with simple bash script:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;projectName &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.csproj&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# It can be saved as one-linear straight in Dockerfile&lt;/span&gt;
	dotnet restore &lt;span class=&quot;nv&quot;&gt;$projectName&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;used this way:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; restore-all.sh . # Taken form cache always&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x restore-all.sh
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; */*.csproj ./ # Taken form cache as long as you don&apos;t change your csproj files&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;./restore-all.sh &lt;span class=&quot;c&quot;&gt;# Taken form cache as long as you don&apos;t change your csproj files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;alternatively-build-image-from-dedicated-container&quot;&gt;Alternatively: build image from dedicated container&lt;/h3&gt;
&lt;p&gt;Some people do not compile application in &lt;em&gt;Dockerfile&lt;/em&gt;, but on their own PC and just put build output to image.
This is faster, but it has one big downside: you’re not 100% sure that image is properly built, and generally speaking
how it was made, so I’ll never wont to put such image on production. But I’ve also heard about another solution - to create
container dedicated to create &lt;em&gt;input&lt;/em&gt; to &lt;em&gt;Docker&lt;/em&gt; image… Sounds a bit like overengineering, and probably in many cases it is, but maybe
sometimes it’s worth doing it. You have more options in container, for example you can mount directories, so there is no problem with any cache you need.
You can even use already existing cache from host machine. I’ve never tried such approach, above methods work well
for me, but maybe sometimes it’s worth to organize &lt;em&gt;CI/CD pipeline&lt;/em&gt; this way.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Proper caching in &lt;em&gt;Dockerfile&lt;/em&gt; is quite broad subject, so here I only touched it in generals. The point of this article 
is not only to give you some ready to use tricks (like those with &lt;em&gt;yarn&lt;/em&gt; or &lt;em&gt;Nuget&lt;/em&gt; packages), but also to show you how cache in &lt;em&gt;Docker&lt;/em&gt; works and encourage you to investigate your own situation.
Pay attention to every build layer, measure which step takes too much time and where is your bottleneck.
Sometimes with one line moved in &lt;em&gt;Dockerfile&lt;/em&gt;, or one file added to &lt;em&gt;.dockerignore&lt;/em&gt; you can make (really) big difference.
And sometimes with small investigation you can resolve huge problem, for example &lt;em&gt;.dockerignore&lt;/em&gt; file stored somewhere else than build context root directory.&lt;br /&gt;
If you have feeling that cache caused some problem (which in theory may be the case, but I never had such situation) you can always check
how &lt;em&gt;Docker&lt;/em&gt; builds with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;no-cache&lt;/code&gt; parameter, which forces to build everything from scratch.&lt;/p&gt;

&lt;p&gt;I hope the article was helpful for you, please publish it on Facebook or Twitter and of course ask me anything, which is still not clear.
If you know some other trick related to cache, please share it through comments, someone for sure will appreciate it :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="docker" />
      
        <category term="dotnet" />
      

      
        <summary type="html">Docker has built in great cache mechanism, but to be able to use it, you have to understand how it works. Let’s dive into it, to build .NET Core Docker images faster.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Initialize MS SQL in Docker container - create database at startup</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg</url>
          <title>Initialize MS SQL in Docker container - create database at startup</title>
          <link>https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container" rel="alternate" type="text/html" title="Initialize MS SQL in Docker container - create database at startup" />
      <published>2019-12-27T06:00:00+00:00</published>
      <updated>2019-12-30T15:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container">&lt;p&gt;&lt;em&gt;Microsoft SQL Server&lt;/em&gt; is available for Linux so we can run it from &lt;em&gt;Docker&lt;/em&gt; container, but usually we need to initialize database at startup, which currently is a bit tricky.&lt;/p&gt;

&lt;p&gt;If you have seen my &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database&quot;&gt;post which explains &lt;em&gt;Docker&lt;/em&gt; compose&lt;/a&gt;,
then you know that &lt;em&gt;MySQL&lt;/em&gt; team has prepared very convenient way to run initialization scripts when DB has started.
You only need to pass your script to proper directory and &lt;em&gt;MySQL&lt;/em&gt; will run it when it’s successfully stared, so you can for example create database.&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;em&gt;Microsoft SQL Server&lt;/em&gt; currently doesn’t have such a mechanism, and &lt;a href=&quot;https://hub.docker.com/_/microsoft-mssql-server&quot;&gt;official documentation&lt;/a&gt;
redirects to only one demo, which is coupled with &lt;em&gt;node.js&lt;/em&gt; and without &lt;em&gt;node&lt;/em&gt; it doesn’t work.&lt;/p&gt;

&lt;p&gt;So I have created &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/mssql-docker-initialization-demo&quot;&gt;simplified demo&lt;/a&gt;, and here will explain how it works step by step.&lt;/p&gt;

&lt;h3 id=&quot;running-ms-sql-server-from-docker-container-without-initialization&quot;&gt;Running MS SQL Server from &lt;em&gt;Docker&lt;/em&gt; container without initialization&lt;/h3&gt;
&lt;p&gt;First let’s take a look at &lt;a href=&quot;https://hub.docker.com/_/microsoft-mssql-server&quot;&gt;official information&lt;/a&gt; of how to start Microsoft SQL Server in container:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;ACCEPT_EULA=Y&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;SA_PASSWORD=yourStrong(!)Password&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;MSSQL_PID=Express&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 1433:1433 &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; mcr.microsoft.com/mssql/server:2017-latest-ubuntu 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I prefer to manage base image and environment variables in text file, so let’s put all above to &lt;em&gt;Dockerfile&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/mssql/server:2017-latest-ubuntu&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; SA_PASSWORD yourStrong(!)Password&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ACCEPT_EULA Y&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; MSSQL_PID Express&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now we can add this file to &lt;em&gt;Git&lt;/em&gt;,&lt;br /&gt;
build this image - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build -t db-demo .&lt;/code&gt;&lt;br /&gt;
and run container in simpler way:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 1433:1433 &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; db-demo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This way we run clean &lt;em&gt;MS SQL server&lt;/em&gt;.&lt;br /&gt;
We can additionally add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXPOSE 1433&lt;/code&gt; entry to &lt;em&gt;Dockerfile&lt;/em&gt;, to be able to access it from composed containers.&lt;/p&gt;

&lt;p&gt;We should also use explicit image version (not &lt;em&gt;latest&lt;/em&gt;) to be sure that this &lt;em&gt;Dockerfile&lt;/em&gt; will always be valid (new versions will not break down &lt;em&gt;Docker build&lt;/em&gt; or runtime initialization in container).
So for example let’s choose version: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM mcr.microsoft.com/mssql/server:2017-CU17-ubuntu&lt;/code&gt; (all tags list is usually given in &lt;a href=&quot;https://hub.docker.com/_/microsoft-mssql-server&quot;&gt;&lt;em&gt;Docker Hub&lt;/em&gt; image description&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;how-to-initialize-ms-sql-server-in-docker-container&quot;&gt;How to initialize MS SQL server in &lt;em&gt;Docker&lt;/em&gt; container&lt;/h2&gt;
&lt;p&gt;Now it’s time to add extra stuff to have initialization in our server.&lt;/p&gt;

&lt;p&gt;The hack is to run at the same time &lt;em&gt;SQL Server&lt;/em&gt; and script which will sleep 90seconds (to be sure that server already started) then do whole initialization (we’ll see how).
It’s not my idea, this is solution currently recommended by Microsoft and nothing better comes to my mind neither.&lt;/p&gt;

&lt;p&gt;First we override &lt;em&gt;Dockerfile&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD&lt;/code&gt; entry from base image (which runs &lt;em&gt;SQL server&lt;/em&gt;), with our own &lt;em&gt;CMD&lt;/em&gt; command, which runs our custom script:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CMD /bin/bash ./entrypoint.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and this &lt;em&gt;entrypoint.sh&lt;/em&gt; script looks like this:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Run Microsoft SQl Server and initialization script (at the same time)&lt;/span&gt;
/usr/src/app/run-initialization.sh &amp;amp; /opt/mssql/bin/sqlservr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Which means: run &lt;em&gt;run-initialization.sh&lt;/em&gt; scripts and at the same time run &lt;em&gt;SQL Server&lt;/em&gt;.
Base image runs server exactly the same way, which we can check with &lt;em&gt;Docker inspect&lt;/em&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker inspect mcr.microsoft.com/mssql/server:2017-CU17-ubuntu | grep CMD&lt;/code&gt;.
The only difference is that we add our script here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: server command must be after &lt;em&gt;&amp;amp;&lt;/em&gt;, otherwise &lt;em&gt;Docker&lt;/em&gt; container will stop when &lt;em&gt;run-initialization.sh&lt;/em&gt; script finishes job.
In &lt;a href=&quot;https://github.com/twright-msft/mssql-node-docker-demo-app/blob/d9d64c9bfbe33091dbfd86913e3103be883c9000/entrypoint.sh&quot;&gt;example referenced by &lt;em&gt;Microsoft&lt;/em&gt;&lt;/a&gt; this order is reversed, but container works because of &lt;em&gt;node.js&lt;/em&gt; server running.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note2: be sure that you pass to &lt;em&gt;Docker&lt;/em&gt; image script files with Unix line endings, otherwise you may &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-run-problem-no-such-file-or-directory-or-other-strange-message&quot;&gt;encounter strange bugs&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So now let’s see how &lt;em&gt;run-initialization.sh&lt;/em&gt; script looks like:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Wait to be sure that SQL Server came up&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;90s

&lt;span class=&quot;c&quot;&gt;# Run the setup script to create the DB and the schema in the DB&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Note: make sure that your password matches what is in the Dockerfile&lt;/span&gt;
/opt/mssql-tools/bin/sqlcmd &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; localhost &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; sa &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; yourStrong&lt;span class=&quot;o&quot;&gt;(!)&lt;/span&gt;Password &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; master &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; create-database.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We assume that after 90 seconds DB server will already be ready to use, then invoke &lt;em&gt;sqlcmd&lt;/em&gt; utility to run &lt;em&gt;SQL&lt;/em&gt; script - 90 seconds is time suggested by Microsoft, in my local environments (for development and tests) I’ve set 30seconds and never had problem with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: credentials passed to &lt;em&gt;sqlcmd&lt;/em&gt; tool must match those given as environment variable in &lt;em&gt;Dockerfile&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s it, in your SQL script you can do whatever you need to do as startup. In our demo we just create database with schema:&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DemoData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DemoData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Products&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProductName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nvarchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the end we need to copy those scripts inside &lt;em&gt;Docker&lt;/em&gt; image. First we’ll create new directory for them with &lt;em&gt;RUN&lt;/em&gt; entry in &lt;em&gt;Dockerfile&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /usr/src/app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;then copy files there:&lt;/p&gt;
&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /usr/src/app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . /usr/src/app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and to be sure that &lt;em&gt;run-initialization.sh&lt;/em&gt; script will be treated as executable we use &lt;em&gt;chmod&lt;/em&gt; command:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x /usr/src/app/run-initialization.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it, whole &lt;em&gt;Dockerfile&lt;/em&gt; is available in &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/mssql-docker-initialization-demo/blob/master/Dockerfile&quot;&gt;mentioned GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-to-initialize-database-with-data&quot;&gt;How to initialize database with data&lt;/h2&gt;
&lt;p&gt;If you need to have database filled with data, there are several ways to achieve that.&lt;/p&gt;

&lt;h3 id=&quot;docker-commit&quot;&gt;&lt;em&gt;Docker&lt;/em&gt; commit&lt;/h3&gt;
&lt;p&gt;If it’s only for debugging purposes you can do &lt;em&gt;Docker commit&lt;/em&gt; to save container state as new image, to recreate it whenever you want.
It can be useful for example when you reproduced bug and don’t want to lose it during debugging. Just save database state, then you can recreate whole environment with bug already &lt;em&gt;prepared&lt;/em&gt;, as many times as you need.
But this way is not recommended for production, because you don’t see explicitly what is in this data so it’s hard to maintain.&lt;/p&gt;

&lt;h3 id=&quot;docker-volume&quot;&gt;&lt;em&gt;Docker&lt;/em&gt; volume&lt;/h3&gt;
&lt;p&gt;You can have your database files already on disk and new server instance just &lt;em&gt;mounted&lt;/em&gt; to it with &lt;em&gt;Docker volume&lt;/em&gt;.
It’s good solution, because you will probably use volume that way or another, to easily persist database.&lt;/p&gt;

&lt;h3 id=&quot;script-database-schema-and-data&quot;&gt;Script database schema and data&lt;/h3&gt;
&lt;p&gt;You can script whole database with data included, straight from &lt;em&gt;Microsoft SQL Server Management studio&lt;/em&gt;, just click on database name, choose &lt;em&gt;Tasks&lt;/em&gt; -&amp;gt; &lt;em&gt;Generate Scripts…&lt;/em&gt; and
you can choose in advanced options &lt;em&gt;Types of data to scripts - Schema and data&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/mssms-generate-scrips1.jpg&quot; alt=&quot;Mssms screnshot with generate scripts tool shown&quot; title=&quot;Choose Generate Scripts...&quot; /&gt;
&lt;img src=&quot;/assets/images/mssms-generate-scrips2.jpg&quot; alt=&quot;Mssms screnshot with advanced generate scripts tool options shown&quot; title=&quot;Choose what you want to have inside scripts: data, schema or both&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can also separate scripts to create schema (empty database) and to add data (fill database), and just run &lt;em&gt;sqlcmd&lt;/em&gt; utility several times:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;90s
/opt/mssql-tools/bin/sqlcmd &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; localhost &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; sa &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; yourStrong&lt;span class=&quot;o&quot;&gt;(!)&lt;/span&gt;Password &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; master &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; create-database.sql
/opt/mssql-tools/bin/sqlcmd &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; localhost &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; sa &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; yourStrong&lt;span class=&quot;o&quot;&gt;(!)&lt;/span&gt;Password &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; master &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; fill-database.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To do so first generate script with &lt;em&gt;Schema only&lt;/em&gt;, then once again with &lt;em&gt;Data only&lt;/em&gt;. I prefer do it this way to separate schema from data, but it’s just my preference,
I feel that it’s cleaner this way but don’t have any tangible evidence that it’s better.&lt;/p&gt;

&lt;h2 id=&quot;troubleshooting-ms-sql-docker-initialization&quot;&gt;Troubleshooting &lt;em&gt;MS SQL Docker&lt;/em&gt; initialization&lt;/h2&gt;
&lt;p&gt;Like I have written before, always remember to &lt;strong&gt;set Unix &lt;em&gt;EOL&lt;/em&gt;&lt;/strong&gt; in files which you copy to &lt;em&gt;Docker&lt;/em&gt; image, otherwise you may have &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-run-problem-no-such-file-or-directory-or-other-strange-message&quot;&gt;incomprehensible bugs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moreover, at the moment of writing this article, &lt;em&gt;Microsoft SQL Server Management Studio&lt;/em&gt; must be restarted every time container is stopped and recreated from scratch, because
&lt;a href=&quot;https://www.softwaredeveloper.blog/mssms-invalid-urn-filter-on-server&quot;&gt;&lt;em&gt;MSSMS&lt;/em&gt; caches internal DB name and doesn’t refresh it, so there is mismatch&lt;/a&gt;. That way or another, &lt;strong&gt;upgrade &lt;em&gt;MSSMS&lt;/em&gt; to the newest version&lt;/strong&gt;, because as far as I know
older versions works even worse.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Starting new &lt;em&gt;Docker&lt;/em&gt; container with &lt;em&gt;Microsoft SQL Server&lt;/em&gt; is really simple. Initializing DB server could be equally simple &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database#mysql-db&quot;&gt;like in MySQL&lt;/a&gt;, but
unfortunately Microsoft didn’t get this done so far, so at this moment we need to use ugly hack with sleep. Luckily workaround is already prepared, and I hope that
after reading this article it’s easy to understand how exactly it works.&lt;/p&gt;

&lt;p&gt;If you want to see &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/Docker-compose-dotnet-core-and-mssql&quot;&gt;MS SQL Server database composed up with Dotnet Core app&lt;/a&gt;, I have created demo for it as well (even if you are not interested in &lt;em&gt;.NET&lt;/em&gt;, you may want
to see &lt;em&gt;SQL Server&lt;/em&gt; in &lt;em&gt;Docker Compose&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Please tell me your feelings in comment, about &lt;em&gt;MS SQL server&lt;/em&gt; in &lt;em&gt;Docker&lt;/em&gt; in general, or about this article if you have any suggestions, or share it with others :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="docker" />
      
        <category term="sql" />
      

      
        <summary type="html">Microsoft SQL Server is available for Linux so we can run it from Docker container, but usually we need to initialize database at startup, which currently is a bit tricky.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Docker run problem no such file or directory (or other strange message)</title>
      
      <link href="https://www.softwaredeveloper.blog/docker-run-problem-no-such-file-or-directory-or-other-strange-message" rel="alternate" type="text/html" title="Docker run problem no such file or directory (or other strange message)" />
      <published>2019-12-23T08:00:00+00:00</published>
      <updated>2019-12-23T08:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/docker-run-problem-no-such-file-or-directory-or-other-strange-message</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/docker-run-problem-no-such-file-or-directory-or-other-strange-message">&lt;p&gt;When you cannot start Docker container correctly and have problems like &lt;em&gt;No such file or directory&lt;/em&gt; or some other unintuitive message, there is one thing you should check first…&lt;/p&gt;

&lt;h2 id=&quot;correct-line-endings-eol-in-files-copied-to-docker&quot;&gt;Correct line endings (&lt;em&gt;EOL&lt;/em&gt;) in files copied to Docker&lt;/h2&gt;
&lt;p&gt;Check if you pass to Docker image scripts (or other text files) with &lt;em&gt;Unix&lt;/em&gt; line endings (&lt;em&gt;LF&lt;/em&gt;)!
&lt;img src=&quot;/assets/images/notepadpp-unix-line-endings.png&quot; alt=&quot;Notepad++ screenshot with Unix EOL conversion option shown&quot; title=&quot;Set Unix EOL&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With wrong &lt;em&gt;EOL&lt;/em&gt; you will be lucky if message somehow indicates it, for example:&lt;br /&gt;
&lt;em&gt;line 3: &lt;strong&gt;$’\r’:&lt;/strong&gt; command not found&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;but it often looks in a way which gives no clue, what is wrong.&lt;/p&gt;

&lt;p&gt;I once spent more than 2 hours because of it, so I hope that with this blog post you will not have to :)&lt;/p&gt;

&lt;p&gt;If you have problem which looks similar but is not caused by wrong &lt;em&gt;EOL&lt;/em&gt;, please share it in comment, I will try to help you figure it out,
then update this post for others.&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="quick-tips" />
      
        <category term="docker" />
      

      
        <summary type="html">When you cannot start Docker container correctly and have problems like No such file or directory or some other unintuitive message, there is one thing you should check first…</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Docker logs from last run container</title>
      
      <link href="https://www.softwaredeveloper.blog/docker-logs-from-last-run-container" rel="alternate" type="text/html" title="Docker logs from last run container" />
      <published>2019-12-23T06:00:00+00:00</published>
      <updated>2019-12-23T06:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/docker-logs-from-last-run-container</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/docker-logs-from-last-run-container">&lt;p&gt;Running Docker containers is what we do often, so it’s good to know how to do it to see Docker logs immediately.&lt;/p&gt;

&lt;h3 id=&quot;running-docker-container-in-foreground-mode&quot;&gt;Running Docker container in foreground mode&lt;/h3&gt;
&lt;p&gt;When you run container without &lt;em&gt;detach&lt;/em&gt; flag (&lt;em&gt;-d&lt;/em&gt;) then you usually need to stop container when you want to
close terminal or type another command. You can &lt;a href=&quot;https://docs.docker.com/engine/reference/run/#foreground&quot;&gt;manipulate this with &lt;em&gt;-a&lt;/em&gt; flag&lt;/a&gt;,
but it’s easier just to run in &lt;em&gt;detached&lt;/em&gt; mode.&lt;/p&gt;

&lt;h3 id=&quot;running-docker-container-in-detached-mode&quot;&gt;Running Docker container in detached mode&lt;/h3&gt;
&lt;p&gt;When you run container in &lt;em&gt;detached&lt;/em&gt; mode (with &lt;em&gt;–detach&lt;/em&gt;, &lt;em&gt;-d&lt;/em&gt; or &lt;em&gt;-d=true&lt;/em&gt; flag) you can safely close
terminal or type another command without affecting running containers, but in this mode by default you don’t see logs.&lt;/p&gt;

&lt;p&gt;If you know your container name you can quickly run &lt;em&gt;docker logs -f CONTAINER_NAME&lt;/em&gt; but when you don’t know it (which is often the case), 
then many people usually end up with this solution: quickly run &lt;em&gt;docker ps&lt;/em&gt;, check container name, and quickly pass it to &lt;em&gt;docker logs&lt;/em&gt; command.
With terminal with poor autocompletion this takes long.&lt;/p&gt;

&lt;h2 id=&quot;how-to-see-docker-logs-from-last-ran-container&quot;&gt;How to see docker logs from last ran container?&lt;/h2&gt;
&lt;p&gt;We usually don’t focus on &lt;em&gt;Docker run&lt;/em&gt; output, which in detached mode is… container ID.
So we don’t need to use &lt;em&gt;Docker ps&lt;/em&gt; to check container name which just got started, we already have it’s ID, which can be used in &lt;em&gt;Docker logs&lt;/em&gt; command
as well. So we can copy and paste it after &lt;em&gt;Docker run&lt;/em&gt;. Example:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker run &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; app
d6a2d30a9a85abf110192e944963bb0ce157dde143139c24abb4bbf038403451
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker logs &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; d6a2d30a9a85abf110192e944963bb0ce157dde143139c24abb4bbf038403451
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s already more convenient, but… better solution is to use it without need to do copy and paste.
How? Simply use &lt;em&gt;xargs&lt;/em&gt; to pass &lt;em&gt;Docker run&lt;/em&gt; output (container id), to &lt;em&gt;Docker logs&lt;/em&gt;. Example:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; app | xargs docker logs &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And we have it :) Very simple, very convenient.&lt;/p&gt;

&lt;h3 id=&quot;why-pass--f-flag-to-docker-logs-command&quot;&gt;Why pass &lt;em&gt;-f&lt;/em&gt; flag to Docker logs command?&lt;/h3&gt;
&lt;p&gt;In all previous examples I have passed &lt;em&gt;-f&lt;/em&gt; flag to &lt;em&gt;Docker logs&lt;/em&gt; command, which stand for &lt;em&gt;follow&lt;/em&gt;.
It’s not necessary, but without it Docker will only show already created logs.
With &lt;em&gt;follow&lt;/em&gt; option, it will show already created logs and will attach our terminal to see new logs, which is what we usually want, to monitor what is going on.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;As Docker comes from Linux world it’s meant to be used from command line and is optimized for this.
So having container ID returned from &lt;em&gt;Docker run&lt;/em&gt; command (in detached mode) is probably meant to be passed to next command (or script), so we can
safely use it to follow logs immediately.&lt;/p&gt;

&lt;p&gt;Let me know in comment if you like this trick or share it with others with following buttons or direct url :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="quick-tips" />
      
        <category term="docker" />
      

      
        <summary type="html">Running Docker containers is what we do often, so it’s good to know how to do it to see Docker logs immediately.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Docker compose introduction - Dotnet core app composed with MySQL Database</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg</url>
          <title>Docker compose introduction - Dotnet core app composed with MySQL Database</title>
          <link>https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database" rel="alternate" type="text/html" title="Docker compose introduction - Dotnet core app composed with MySQL Database" />
      <published>2019-09-13T06:00:00+00:00</published>
      <updated>2019-09-13T06:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database">&lt;p&gt;Introduction to docker-compose - basic aspects explained with .NET Core app and MySQL DB composed together.&lt;/p&gt;

&lt;p&gt;If you are familiar with &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image&quot;&gt;basics of creating Docker images&lt;/a&gt;, now
it’s time to learn how to compose more than one Docker container together. Once again we’ll learn with .NET Core 2.2 example app, but
you should be able to understand Docker compose concepts here, even if you’re not familiar with C# language.&lt;/p&gt;

&lt;p&gt;Whole solution is available in &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/Docker-compose-dotnet-core-and-mysql&quot;&gt;SoftwareDeveloperBlog repo on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;docker-compose-file&quot;&gt;Docker-compose file&lt;/h2&gt;
&lt;p&gt;Heart of Docker composing is one text file (in &lt;em&gt;yaml&lt;/em&gt; format).&lt;br /&gt;
By default it’s named &lt;em&gt;docker-compose.yml&lt;/em&gt; (or &lt;em&gt;docker-compose.yaml&lt;/em&gt; if you prefer),
but unlike &lt;em&gt;Dockerfile&lt;/em&gt; this name can be changed and specified as command parameter.&lt;/p&gt;

&lt;h3 id=&quot;where-to-keep-docker-compose-file&quot;&gt;Where to keep docker-compose file?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Short answer&lt;/strong&gt; - it doesn’t matter, put it where it’s convenient for you.&lt;br /&gt;
&lt;strong&gt;Long answer&lt;/strong&gt; - some people advice to put it along with deployment scripts, for example in your CI pipeline, because composing is strictly
related with deploying. Moreover, this way you can compose images from different repositories.&lt;br /&gt;
&lt;strong&gt;Where I put &lt;em&gt;docker-compose.yml&lt;/em&gt; file?&lt;/strong&gt; So far I’ve never containerized system spread across multiple repositories, so I put it along with sources, to
have it under version control, next to solution file (&lt;em&gt;.sln&lt;/em&gt;) - to &lt;em&gt;manage&lt;/em&gt; it from the &lt;em&gt;highest perspective&lt;/em&gt; (above project directories).
Usually I also create solution directory called &lt;em&gt;Docker&lt;/em&gt;, referencing docker-compose and &lt;em&gt;.dockerignore&lt;/em&gt; files, to be able to edit it from my IDE.&lt;/p&gt;

&lt;h3 id=&quot;docker-compose-file-example&quot;&gt;Docker-compose file example&lt;/h3&gt;
&lt;p&gt;In our example we will use following &lt;em&gt;docker-compose.yml&lt;/em&gt; file, &lt;a href=&quot;#where-to-keep-docker-compose-file&quot;&gt;kept as I usually do&lt;/a&gt;, next to solution file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./Db&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Aspnetcoreapp/Dockerfile&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8080:80&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;db&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;First we specify docker-compose file &lt;strong&gt;format version&lt;/strong&gt;. I just took the newest version, I don’t see any reasons to take older ones
while creating new system.&lt;/p&gt;

&lt;p&gt;Then we specify our &lt;strong&gt;services&lt;/strong&gt;. We could specify other aspects, for example networks, volumes, configs, 
but it’s too wide for this introduction.&lt;/p&gt;

&lt;p&gt;Every service has its name, and this &lt;strong&gt;service name is important&lt;/strong&gt;, because we will use it to resolve container ip address and to refer it from other service (for example to specify dependencies).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build parameter&lt;/strong&gt; can just point to directory with Dockerfile (like in &lt;em&gt;db&lt;/em&gt; service in example above) - then this directory is also taken as build context.
If you want to use different directory for build context, than the one with Dockerfile inside, you can separate &lt;em&gt;context&lt;/em&gt; and &lt;em&gt;dockerfile&lt;/em&gt; options, like in &lt;em&gt;app&lt;/em&gt; service above.
If you wonder why to do that, I explained &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image&quot;&gt;choosing build context in previous post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Specify &lt;strong&gt;ports&lt;/strong&gt; to bind port inside container with one from host machine, like in usual &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image#docker-run-command&quot;&gt;&lt;em&gt;Docker run&lt;/em&gt; command&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lastly we specify that our app &lt;strong&gt;depends on&lt;/strong&gt; db, with &lt;em&gt;depends_on&lt;/em&gt; option, taking service name as value. This way we determine services start and stop order (and few other things, not mentioned here for simplification).&lt;/p&gt;

&lt;h2 id=&quot;docker-compose-commands&quot;&gt;Docker-compose commands&lt;/h2&gt;
&lt;p&gt;Managing composed system is easy, you don’t need to remember many commands and parameters.&lt;/p&gt;

&lt;h3 id=&quot;docker-compose-up&quot;&gt;Docker-compose up&lt;/h3&gt;
&lt;p&gt;From directory with &lt;em&gt;docker-compose.yml&lt;/em&gt; file you can just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt; command. 
It will run containers in order forced by &lt;em&gt;depends_on&lt;/em&gt; configuration (if specified).
By default images are build only when you run &lt;em&gt;docker-compose up&lt;/em&gt; command for the first time, so if you have changed something, you should
extend it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--build&lt;/code&gt; parameter.
If you want to run it from different location then the one with &lt;em&gt;docker-compose.yml&lt;/em&gt; file, or if you named compose file not default way, or if you want to pass more than one compose file at once, pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f FILE_PATH&lt;/code&gt; parameter.
Last parameter which I use often is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; to detach console from containers. 
There are other commands to start composed containers, but usually we use this one, so I don’t mention them in this introduction. If you’re curious, they are
described well &lt;a href=&quot;https://docs.docker.com/compose/faq/#whats-the-difference-between-up-run-and-start&quot;&gt;in official documentation&lt;/a&gt;.
Usually I run &lt;em&gt;docker-compose up&lt;/em&gt; command this way:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker-compose up &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;docker-compose-down&quot;&gt;Docker-compose down&lt;/h3&gt;
&lt;p&gt;If you want to stop composed containers, remove them and theirs networks, just type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose down&lt;/code&gt; from directory with &lt;em&gt;docker-compose.yml&lt;/em&gt; file.
If you want just stop containers without cleaning everything, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose stop&lt;/code&gt; command.
Note, that if you have stopped containers with &lt;em&gt;CTRL+C&lt;/em&gt; shortcut in attached terminal, you didn’t clean stuff, you just stopped containers and finally will need to run &lt;em&gt;docker-compose down&lt;/em&gt; as usual.&lt;/p&gt;

&lt;h2 id=&quot;example-system&quot;&gt;Example system&lt;/h2&gt;
&lt;p&gt;Now with mandatory knowledge, lets create example system.
As mentioned in &lt;a href=&quot;#introduction&quot;&gt;introduction&lt;/a&gt;, whole repo is available &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/Docker-compose-dotnet-core-and-mysql&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;net-core-app&quot;&gt;.NET Core app&lt;/h3&gt;
&lt;p&gt;First we create simple ASP.NET Core application.
Small startup configuration:&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Startup&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetCompatibilityVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompatibilityVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Version_2_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHostingEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDevelopment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseMvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Program entry point:&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;CreateWebHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWebHostBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateWebHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WebHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UseStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Controller with one &lt;em&gt;GET&lt;/em&gt; method:&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProductsController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControllerBase&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProductsProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProductsProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exception during providing products, maybe DB is not fully initialized yet? &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;
                              &lt;span class=&quot;s&quot;&gt;$&quot;Try again in a few minutes and if it doesn&apos;t help, check your docker-compose configuration.\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Controller is coupled with &lt;em&gt;Product&lt;/em&gt; type:&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Controller is also coupled with &lt;em&gt;ProductsProvider&lt;/em&gt; class, which uses &lt;em&gt;Dapper&lt;/em&gt; to access MySQL DB.
Note, that we &lt;strong&gt;provide docker-compose service name as DB address&lt;/strong&gt; (as &lt;a href=&quot;#docker-compose-file-example&quot;&gt;mentioned earlier&lt;/a&gt; - that’s why service name is important). Be careful not to make typo - &lt;strong&gt;service name is case-sensitive&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProductsProvider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CONN_STRING&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Server=db;Port=3306;Database=product-db;Uid=root; Pwd=myPass;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QUERY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SELECT Id, Name, Description FROM product&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MySqlConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CONN_STRING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QUERY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Dockerfile&lt;/em&gt; is written following &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image&quot;&gt;&lt;em&gt;Docker image from multi project .NET Core solution&lt;/em&gt; post&lt;/a&gt;.
Note that &lt;em&gt;Aspnetcoreapp&lt;/em&gt; is our project name, and project directory name at the same time:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish Aspnetcoreapp &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/aspnet:2.2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=build-env /app/Aspnetcoreapp/out .&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;Aspnetcoreapp.dll&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;mysql-db&quot;&gt;MySQL DB&lt;/h3&gt;
&lt;p&gt;We could point DB image in docker-compose.yml file and configure environment variables and so on there, but I prefer to 
configure service via Dockerfile. So we just create new directory with Dockerfile. It can be located anywhere, but I prefer the same level as C# project.
So I just create new &lt;em&gt;Db&lt;/em&gt; directory, next to project directory (&lt;em&gt;Aspnetcoreapp&lt;/em&gt;).
If you want to see this Dockerfile in IDE, you can reference it in solution directory.&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mysql:5.7.13&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; *.sql /docker-entrypoint-initdb.d&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; MYSQL_ROOT_PASSWORD myPass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;EXPOSE&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 3306&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With above &lt;em&gt;Dockerfile&lt;/em&gt; we do several things:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Specify which version of MySQL image to use&lt;/li&gt;
  &lt;li&gt;Set root user password to &lt;em&gt;myPass&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Expose 3306 port to be able to access it from our app&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Initialize database&lt;/strong&gt;, by copying SQL scripts to &lt;em&gt;/docker-entrypoint-initdb.d&lt;/em&gt; directory inside DB container, because 
&lt;a href=&quot;https://hub.docker.com/_/mysql#initializing-a-fresh-instance&quot;&gt;official MySQL image description tells us, that scripts 
from this directory are invoked when DB is started for the first time&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next we need some &lt;em&gt;.sql&lt;/em&gt; script located next to &lt;em&gt;Dockerfile&lt;/em&gt; to initialize our database. We use &lt;em&gt;*.sql&lt;/em&gt; wildcard, so name doesn’t matter, it just need to end up with &lt;em&gt;.sql&lt;/em&gt; extension - for example &lt;em&gt;Init.sql&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`product-db`&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*!40100 COLLATE &apos;latin1_swedish_ci&apos; */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`product-db`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`product`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;`Id`&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;`Name`&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;`Description`&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`Id`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;`Id`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COLLATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;latin1_swedish_ci&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IGNORE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Dependency Injection Principles, Practices, and Patterns&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Book by Steven van Deursen and Mark Seemann&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Agile Software Development, Principles, Patterns, and Practices&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Book by Robert C. Martin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;docker-composeyml-and-dockerignore&quot;&gt;Docker-compose.yml and .dockerignore&lt;/h3&gt;
&lt;p&gt;We take &lt;em&gt;docker-compose.yml&lt;/em&gt; file described &lt;a href=&quot;#docker-compose-file-example&quot;&gt;earlier in this article&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./Db&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Aspnetcoreapp/Dockerfile&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8080:80&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Very last thing - &lt;em&gt;.dockerignore&lt;/em&gt; file located next to solution (&lt;em&gt;.sln&lt;/em&gt;) file. If you don’t know what is this file or why it has to be in
this location, I’ve explained it in &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image#where-to-keep-dockerignore-file&quot;&gt;previous post&lt;/a&gt;.
Mine currently looks like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.dockerignore
.env
.git
.gitignore
.vs
.vscode
*/bin
*/obj
**/.toolstarget
.idea
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;running-composed-system&quot;&gt;Running composed system&lt;/h3&gt;
&lt;p&gt;To run this example system, just go to solution directory, type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt; command (&lt;a href=&quot;#docker-compose-up&quot;&gt;explained earlier&lt;/a&gt;) and after a moment (DB initialization takes a while) you
can open browser and see data pulled from DB with &lt;em&gt;http://localhost:8080&lt;/em&gt; GET method:
&lt;img src=&quot;/assets/images/dotnet-core-with-mysql-app.jpg&quot; alt=&quot;Browser with products taken from DB seen as JSON array&quot; title=&quot;See data from DB in Browser&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;persisting-db-data&quot;&gt;Persisting DB data&lt;/h3&gt;
&lt;p&gt;As you know, killed container is killed along with the data, which of course is not what we want from production database. To persist
changes which we introduced to DB during production runtime, we need to use &lt;em&gt;volumes&lt;/em&gt;, but I thought that it’s too much for simple introduction
article. If you succeeded previous steps, now you can &lt;a href=&quot;https://docs.docker.com/compose/compose-file/#volume-configuration-reference&quot;&gt;read about docker volumes&lt;/a&gt;.
Maybe some day I will explain it in whole different post.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;As you see, composing in general is simple, and basically requires one text file which instructs Docker how to run and couple containers together.&lt;/p&gt;

&lt;p&gt;I hope you find this introduction useful. 
If there is something not clear, ask me a question in comment, and if you think it can help other too, share this article on &lt;a href=&quot;https://www.facebook.com/sharer/sharer.php?u=https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database&quot;&gt;Facebook&lt;/a&gt; or &lt;a href=&quot;https://twitter.com/share?text=Docker+compose+introduction+-+Dotnet+core+app+with+MySQL+Database&amp;amp;url=https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database&quot;&gt;Twitter&lt;/a&gt; :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="docker" />
      
        <category term="dotnet" />
      
        <category term="sql" />
      

      
        <summary type="html">Introduction to docker-compose - basic aspects explained with .NET Core app and MySQL DB composed together.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Multi-project .net core solution in Docker image</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg</url>
          <title>Multi-project .net core solution in Docker image</title>
          <link>https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image" rel="alternate" type="text/html" title="Multi-project .net core solution in Docker image" />
      <published>2019-07-27T08:00:00+00:00</published>
      <updated>2020-03-20T16:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image">&lt;p&gt;Creating Docker images from dotnet solution with project references is easy when you understand basics of Docker commands, but writing proper Dockerfile can be tricky for beginners.&lt;/p&gt;

&lt;p&gt;Most of the examples show how to dockerize dotnet project, assuming that it has no local dependencies.
So let’s analyse what can we do, when our project has references to other projects from solution.
We will start with diving into simple example without dependencies first, to understand what changes we introduce and why.&lt;br /&gt;
If you just want to &lt;a href=&quot;#solution&quot;&gt;skip to solution&lt;/a&gt; and copy-paste it, of course you can do it, but it’s not recommended, because 
sooner or later you will be blocked with another obstacle due to not understanding what is happening, and in result you will waste more time.
This article is good to start learning Docker instructions and commands, because everything is explained with short, plain language.
We will use &lt;em&gt;.net core 2.2&lt;/em&gt; as it’s current version at the moment of writing.&lt;/p&gt;

&lt;p&gt;Example dockerized dotnet core application is &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/Docker-dotnet-core-multi-project-solution&quot;&gt;available on GitHub&lt;/a&gt;, feel free to use it for your needs.&lt;/p&gt;

&lt;h2 id=&quot;analysis-of-official-docker-example&quot;&gt;Analysis of official Docker example&lt;/h2&gt;
&lt;p&gt;Official &lt;a href=&quot;https://docs.docker.com/engine/examples/dotnetcore&quot;&gt;dockerize an .NET Core application article&lt;/a&gt; shows us this Dockerfile located in project folder (where &lt;em&gt;.csproj&lt;/em&gt; file is stored):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Copy csproj and restore as distinct layers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; *.csproj ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet restore

&lt;span class=&quot;c&quot;&gt;# Copy everything else and build&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out

&lt;span class=&quot;c&quot;&gt;# Build runtime image&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/aspnet:2.2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=build-env /app/out .&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;PROJECT_NAME.dll&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and those two commands, to be run from project folder where Dockerfile is located:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker build &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; aspnetapp &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
docker run &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:80 &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; myapp aspnetapp&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;dockerfile-from-instruction&quot;&gt;Dockerfile FROM instruction&lt;/h3&gt;
&lt;p&gt;Our Dockerfile starts with &lt;em&gt;FROM&lt;/em&gt; instruction:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env&lt;/code&gt;&lt;br /&gt;
which means that we base our image on official &lt;em&gt;Microsoft Dotnet Core SDK&lt;/em&gt; in version 2.2.
We use SDK at this moment, not production runtime, because we will compile our application in Docker during building image.
So you don’t even need to have .net core SDK installed on your host machine, this Dockerfile is prepared in a way that you won’t compile 
your app for Docker image yourself - Docker will compile it. You could use binaries built on your host machine, but it’s not safe - it may not work due to compatibility troubles.
Why there is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AS build-env&lt;/code&gt; instruction - we will come to this later (in &lt;a href=&quot;#docker-multi-stage-build&quot;&gt;Docker multi-stage build&lt;/a&gt; section).&lt;/p&gt;

&lt;h3 id=&quot;dockerfile-workdir-instruction&quot;&gt;Dockerfile WORKDIR instruction&lt;/h3&gt;
&lt;p&gt;In second line we see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WORKDIR /app&lt;/code&gt; instruction which means, that following RUN, CMD, ENTRYPOINT, COPY and ADD instructions in our Dockerfile
will be executed in &lt;em&gt;/app&lt;/em&gt; directory. If it doesn’t exist it will be created (even if it wouldn’t be used).&lt;/p&gt;

&lt;h3 id=&quot;dockerfile-copy-instruction&quot;&gt;Dockerfile COPY instruction&lt;/h3&gt;
&lt;p&gt;Next we see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY *.csproj ./&lt;/code&gt; instruction, which means that all csproj files from Docker build context will be copied to workdir (&lt;em&gt;/app&lt;/em&gt;) directory
inside Docker image. &lt;a href=&quot;#docker-build-command&quot;&gt;Docker build command will be explained later&lt;/a&gt;, but in short - build context is the directory from your host
machine, pointed in Docker build command. If you point &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; path, the directory where you execute command is taken.
So in our case we copy only one &lt;em&gt;csproj&lt;/em&gt; file, because we run build command with project directory set as build context.&lt;/p&gt;

&lt;h3 id=&quot;dockerfile-run-instruction&quot;&gt;Dockerfile RUN instruction&lt;/h3&gt;
&lt;p&gt;Next is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet restore&lt;/code&gt; instruction, which simply runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet restore&lt;/code&gt; command in our workdir (&lt;em&gt;/app&lt;/em&gt;) directory.
At this moment inside &lt;em&gt;/app&lt;/em&gt; directory in our image, is nothing but &lt;em&gt;.csproj&lt;/em&gt; of our project, because we copied only it
in previous step, but it’s enough for restoring nuget dependencies.&lt;/p&gt;

&lt;h3 id=&quot;copy-and-compile-app-source&quot;&gt;Copy and compile app source&lt;/h3&gt;
&lt;p&gt;Again we see &lt;a href=&quot;#dockerfile-copy-instruction&quot;&gt;COPY instruction&lt;/a&gt; - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY . ./&lt;/code&gt; to copy everything from our build context - in our case 
it means project files (.cs files etc.), because we run &lt;a href=&quot;#docker-build-command&quot;&gt;docker build command&lt;/a&gt; with project directory set as build context.
Then with &lt;a href=&quot;#dockerfile-run-instruction&quot;&gt;run instruction&lt;/a&gt; - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet publish -c Release -o out&lt;/code&gt; we simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet publish&lt;/code&gt; in our workdir (&lt;em&gt;/app&lt;/em&gt;) 
directory inside image, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c Release -o out&lt;/code&gt; parameters. This dotnet command
compiles our app with release configuration and publishes results in &lt;em&gt;out&lt;/em&gt; directory (in our case &lt;em&gt;/app/out&lt;/em&gt;).
We can compile source because we &lt;a href=&quot;#dockerfile-from-instruction&quot;&gt;base this image on developer’s sdk&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;docker-multi-stage-build&quot;&gt;Docker multi-stage build&lt;/h3&gt;
&lt;p&gt;Once again we see &lt;a href=&quot;#dockerfile-from-instruction&quot;&gt;FROM instruction&lt;/a&gt;, which sets on which image we base our image…
How it’s possible to specify it again, with different base? It’s quite fresh Docker feature (since Docker 17.05 version) called multi-stage builds.
When we use FROM keyword again, we mean that previous image specified above is temporary, and was used only to serve some purpose.
In our case it was made only to compile our application - that’s why we used SDK as base image. Now we specify base image again and this time
we are preparing our &lt;em&gt;real&lt;/em&gt; image - the one which will be deployed to production, and this one doesn’t base on SDK, only on production runtime,
which gives smaller size in result. We will just copy our compiled app from temporary image. So again we specify working directory to &lt;em&gt;/app&lt;/em&gt;
catalog and then we copy our binaries - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=build-env /app/out .&lt;/code&gt; which means copy files from &lt;em&gt;/app/out/&lt;/em&gt; from &lt;em&gt;build-env&lt;/em&gt; image
(that’s why we gave it a name in the first line) to current working directory (&lt;em&gt;/app&lt;/em&gt;).&lt;/p&gt;

&lt;h3 id=&quot;dockerfile-entrypoint-instruction&quot;&gt;Dockerfile ENTRYPOINT instruction&lt;/h3&gt;
&lt;p&gt;Very last instruction in this Dockerfile is &lt;em&gt;ENTRYPOINT&lt;/em&gt;, which (in simple terms) specifies a command that will be executed when the container starts.
So in our case - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENTRYPOINT [&quot;dotnet&quot;, &quot;PROJECT_NAME.dll&quot;]&lt;/code&gt; - Docker will run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROJECT_NAME.dll&lt;/code&gt; parameter (which should be
replaced to our project name of course), to start our app.&lt;/p&gt;

&lt;h3 id=&quot;docker-build-command&quot;&gt;Docker build command&lt;/h3&gt;
&lt;p&gt;With such Dockerfile, we are told to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build -t aspnetapp .&lt;/code&gt; command in project directory (where Dockerfile is stored).
Option: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-t name&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--tag name&lt;/code&gt;) is not mandatory - it allows to tag image (to name it and optionally give it a tag in ‘name:tag’ format), so don’t focus on it,
and look on this command this way: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build .&lt;/code&gt;, because important thing is after options - build context parameter.
Build context is path on host machine which will be accessible during building image for Dockerfile instructions.
In our case it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; path which means that the directory where we run this command is passed as build context.
Because we are told to run this command in project directory (where &lt;em&gt;.csproj&lt;/em&gt; file is stored), our project files are passed as build context.&lt;/p&gt;

&lt;h3 id=&quot;docker-run-command&quot;&gt;Docker run command&lt;/h3&gt;
&lt;p&gt;Docker run command creates container from image.
Image is readonly &lt;em&gt;manual&lt;/em&gt; for Docker, to create container, and container is working &lt;em&gt;virtual machine&lt;/em&gt; where our app lives.
We can think about it this way: image is like a class in object-oriented programming, and container is like an instance, created from this class.
So we can create as many containers (instances) as we want, and it doesn’t affect image (class) - image is only necessary to let Docker know how to create container.
We are told to run it this way:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -d -p 8080:80 --name myapp aspnetapp&lt;/code&gt;&lt;br /&gt;
Without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--detach&lt;/code&gt; option (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt;) we will start seeing app console output from container. 
With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--publish&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&lt;/code&gt;) option we bind container’s port(s) to the host (by default with TCP, but you can specify UDP and SCTP as well).
With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name&lt;/code&gt; option we assign a name to the container (without this option Docker will choose some funny name for us).
At the end we pass image name, which Docker will read to create container. Because we named our image &lt;em&gt;aspnetapp&lt;/em&gt;, we use this name here.&lt;/p&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;
&lt;h3 id=&quot;proper-docker-commands&quot;&gt;Proper Docker commands&lt;/h3&gt;
&lt;p&gt;Once we understand what happens in basic example, let’s see how to change it, to make it work when our project has references to other
solution projects.&lt;br /&gt;
The problem is of course, that we run Docker build command from project directory passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; path as build context.
This means, that files only from this directory will be accessible during building image, and depending projects are of course in other
directories. We have several options to fix this. We can move Dockerfile one level up (to solution directory) and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build&lt;/code&gt; from there.
But it’s recommended to have Dockerfile in project directory, to be able to have more than one Dockerfile in solution (for different projects).
You could also run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build&lt;/code&gt; as before (from project directory), but change build context path to one level up (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;..&lt;/code&gt;).
In my view more elegant is third solution - to &lt;strong&gt;run docker build from solution directory&lt;/strong&gt;, pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; as build context and to specify which Dockerfile we want
to read with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--file&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt;) option, &lt;strong&gt;like this&lt;/strong&gt;:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build -f PROJECT_DIRECTORY/Dockerfile -t IMAGE_NAME .&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;how-to-adjust-dockerfile&quot;&gt;How to adjust Dockerfile&lt;/h3&gt;
&lt;p&gt;Next we need to adjust Dockerfile, because the one from official example assume that we have project directory as build context.
My version looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish PROJECT_NAME &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/aspnet:2.2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=build-env /app/PROJECT_NAME/out .&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;PROJECT_NAME.dll&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I have skipped restoring nuget packages as single step to simplify, restore is included in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet publish&lt;/code&gt; and if it fails due to nuget failure,
error message is legible. But if you have many nuget dependencies you may want to have separate step, because this way Docker treats it as distinct layer and reuses it
if none of &lt;em&gt;csproj&lt;/em&gt; file changed, which gives &lt;a href=&quot;https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image&quot;&gt;smaller build time&lt;/a&gt; (I have described it in &lt;a href=&quot;https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image&quot;&gt;another post&lt;/a&gt;). In this demo restoring nugets is fast enough to skip it, but remember about this if you have long building time (in particular - &lt;em&gt;Restore completed in…&lt;/em&gt; time).&lt;/p&gt;

&lt;p&gt;So we copy all projects (because build context is now solution directory) to &lt;em&gt;/app&lt;/em&gt; directory inside container.
Next from &lt;em&gt;/app&lt;/em&gt; workdir we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet publish&lt;/code&gt; command, specifying which project to compile -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet publish PROJECT_NAME -c Release -o out&lt;/code&gt; - here PROJECT_NAME is the directory name with &lt;em&gt;.csproj&lt;/em&gt; file inside.&lt;/p&gt;

&lt;p&gt;Other instructions stays untouched with one small change - during copying compiled app from temporary image, this time we need to pass project name to path:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=build-env /app/PROJECT_NAME/out .&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;where-to-keep-dockerignore-file&quot;&gt;Where to keep .dockerignore file&lt;/h3&gt;
&lt;p&gt;Official article says to add &lt;em&gt;.dockerignore&lt;/em&gt; file to project directory, to make build context as small as possible and lower risk of invalidating cache, which of course is
reasonable. But Docker CLI looks for &lt;em&gt;.dockerignore&lt;/em&gt; file in root directory of the build context, so now we need to move it to solution
directory. But in my view it’s even better, because we don’t need to create and maintain many &lt;em&gt;.dockerignore&lt;/em&gt; files for many projects,
we keep one for all of them. Example rules:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;*/bin
*/obj
.dockerignore
.env
.git
.gitignore
.vs
.vscode
**/.toolstarget
.idea&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;When I had to dockerize .net core app for the first time I just took Dockerfile from mentioned article, copy-pasted Docker commands
and when I faced obstacle I tried to solve it without analysis of how Docker works. After wasting some time this way, I wasted time again - trying
to just copy-paste solution from the internet - again without analysis of what I do, and without success too. Then I learned (once again in my life…), that haste didn’t save
time, but do the opposite - waste it. 
Because I didn’t find proper article nor tutorial to start I came though official documentation and manuals, which are written nice, but have too many
details for beginners.
This article shows essentials of analysis I took and explains basics with plain language. I hope this way it is good to start writing proper Dockerfile,
without having trouble with situation like the one presented here - dotnet project with references to other projects from solution, nor with any other obstacle.&lt;/p&gt;

&lt;p&gt;What to do when you have already containerized your app, but need to use some dependent system, for example DB?
You can compose them together with docker-compose, which I have described with simple &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database&quot;&gt;.NET Core app and MySQL DB as composed system example&lt;/a&gt;, in next article.&lt;/p&gt;

&lt;p&gt;Don’t hesitate to write comment whether it was helpful for you, or to share it on Facebook or Twitter :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="docker" />
      
        <category term="dotnet" />
      

      
        <summary type="html">Creating Docker images from dotnet solution with project references is easy when you understand basics of Docker commands, but writing proper Dockerfile can be tricky for beginners.</summary>
      
    </entry>
  
</feed>
