DockerFile executing powershell file cannot be found - docker

I'm attempting to get an asp.net MVC application running using docker for windows. Which I've configured to use windows containers.
I'm using VS2017 to add docker support which generates the DockerFile and the docker-compose project.
The default DockerFile consists of:
FROM microsoft/aspnet:4.6.2
ARG source
WORKDIR /inetpub/wwwroot
COPY ${source:-obj/Docker/publish} .
Creating an image configured to run IIS with .net 4.6.2
This works fine, however I need to configure SSL. Using this example (https://github.com/MicrosoftDocs/Virtualization-Documentation/blob/live/windows-container-samples/iis-https/Dockerfile) and changing my file to:
FROM microsoft/aspnet:4.6.2
ARG source
WORKDIR /inetpub/wwwroot
COPY ${source:-obj/Docker/publish} .
RUN powershell.exe -Command " \
Import-Module IISAdministration; \
$cert = New-SelfSignedCertificate -DnsName demo.contoso.com -CertStoreLocation cert:\LocalMachine\My; \
$certHash = $cert.GetCertHash(); \
$sm = Get-IISServerManager; \
$sm.Sites[\"Default Web Site\"].Bindings.Add(\"*:443:\", $certHash, \"My\", \"0\"); \
$sm.CommitChanges();"
Works fine. However this is a self-signed cert and I wish to use a cert I copy to the image that I can also install into my cert store on my own machine for it to be trusted.
I've changed the DockerFile to:
FROM microsoft/aspnet:4.6.2
ARG source
WORKDIR /inetpub/wwwroot
COPY ${source:-obj/Docker/publish} .
WORKDIR /
COPY ${source:-server.pfx} ./server.pfx
RUN powershell.exe ls;
RUN powershell.exe -executionpolicy bypass -Command " \
Import-Module WebAdministration; \
$pwd = ConvertTo-SecureString -String \"server\" -Force -AsPlainText; \
Import-PfxCertificate -CertStoreLocation \"cert:\\LocalMachine\\Root\\b455d81e2414ec1da38f4de105b98066506cf86e\" -Password $pwd -FilePath \"c:\\server.pfx\"; \
Import-PfxCertificate -CertStoreLocation \"cert:\\LocalMachine\\My\\b455d81e2414ec1da38f4de105b98066506cf86e\" -Password $pwd -FilePath \"c:\\server.pfx\"; \
$cert = Get-Item "cert:\LocalMachine\My\b455d81e2414ec1da38f4de105b98066506cf86e"; \
New-WebBinding -Name \"Default Web Site\" -Protocol \"https\" -IPAddress \"*\" -Port 443; \
New-Item \"IIS:\\SslBindings\\0.0.0.0!443\" -Value $cert;"
Which feels like it should work. The powershell ls command shows the server.pfx file sitting on the c: drive of the container. However when it runs the powershell command setting up the certificate it errors saying it cannot find the file.
I've also run into issues where I've put the powershell into an actual script file, copied it onto the container, executed it, its all gone through fine but nothing seems to have happened.
If I use this chaps docker hub image it works as expect and he's using an actual powershell file:
https://hub.docker.com/r/rgarita/aspnet-ssl/
https://github.com/rgarita/aspnet-ssl-docker/tree/master/4.6.2
I'm at a bit of a loss with this, not sure if anyone has any ideas or have already gone through pain similar to this?
Thanks in advance,
Jon

Related

Gitlab CICD cannot add service

For my projects I sometimes need to unittest my code against a ms sql server.
At the moment, I created a monstrous docker image, containing all the tools I need, including the sql server.
Now this image got to about 15 GB in size, which is really not cool.
So I'm trying to use the GitLab CICD Service part (as described here https://docs.gitlab.com/ee/ci/services/index.html).
But when I'm trying to add my (custom) image as a service I keep getting the health check issue:
*** WARNING: Service runner-hrtjgacu-project-1489-concurrent-0-2ae3f3cd2099f19a-gitlab.mydomain.lcaol__windowsdockerimages__mssql-0 probably didn't start properly.
Health check error:
service "runner-hrtjgacu-project-1489-concurrent-0-2ae3f3cd2099f19a-gitlab.mydomain.local__windowsdockerimages__mssql-0-wait-for-service" health check: exit code 1
Health check container logs:
2023-01-19T09:58:09.913696500Z FATAL: No HOST or PORT found
Service container logs:
*********
Now I found something online about needing to expose the ports of the service in the Dockerfile, so I tried that, but that did not help.
This is what my MSSQL DockerFile currently looks like:
FROM mcr.microsoft.com/windows/servercore:ltsc2019
# Download Links:
ENV exe "https://go.microsoft.com/fwlink/?linkid=840945"
ENV box "https://go.microsoft.com/fwlink/?linkid=840944"
ENV sa_password="_" \
attach_dbs="[]" \
ACCEPT_EULA="Y" \
sa_password_path="C:\ProgramData\Docker\secrets\sa-password"
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# make install files accessible
COPY start.ps1 /
WORKDIR /
RUN Invoke-WebRequest -Uri $env:box -OutFile SQL.box ; \
Invoke-WebRequest -Uri $env:exe -OutFile SQL.exe ; \
Start-Process -Wait -FilePath .\SQL.exe -ArgumentList /qs, /x:setup ; \
.\setup\setup.exe /q /ACTION=Install /INSTANCENAME=MSSQLSERVER /FEATURES=SQLEngine /UPDATEENABLED=0 /SQLSVCACCOUNT='NT AUTHORITY\System' /SQLSYSADMINACCOUNTS='BUILTIN\ADMINISTRATORS' /TCPENABLED=1 /NPENABLED=0 /IACCEPTSQLSERVERLICENSETERMS ; \
Remove-Item -Recurse -Force SQL.exe, SQL.box, setup
RUN stop-service MSSQLSERVER ; \
set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.MSSQLSERVER\mssqlserver\supersocketnetlib\tcp\ipall' -name tcpdynamicports -value '' ; \
set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.MSSQLSERVER\mssqlserver\supersocketnetlib\tcp\ipall' -name tcpport -value 1433 ; \
set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql14.MSSQLSERVER\mssqlserver\' -name LoginMode -value 2 ;
HEALTHCHECK CMD [ "sqlcmd", "-Q", "select 1" ]
EXPOSE 1433/tcp
EXPOSE 4022/tcp
EXPOSE 135/tcp
EXPOSE 1434/tcp
EXPOSE 1434/udp
CMD .\start -sa_password $env:sa_password -ACCEPT_EULA $env:ACCEPT_EULA -attach_dbs \"$env:attach_dbs\" -Verbose
And this is is my .gitlab-ci.yml file:
default:
image:
name: gitlab.mydomain.local:4567/windowsdockerimages/basicnetframeworkimage:latest
tags:
- windows
- docker
# The stages during this build
stages:
- build
build:
stage: build
script:
- echo "Hello world"
- ping mssql
- dotnet run
services:
- name: gitlab.mydomain.local:4567/windowsdockerimages/mssql
alias: mssql
Anyone who can help me get closer to the solution?
*Note, the ping mssql also fails, the container in which we run cannot find the DNS
entry
I would use an existing official image for MSSQL server (https://hub.docker.com/_/microsoft-mssql-server) and run unit tests in one container and the database in another one.

Dockerizing a Windows Service

I am new to docker and I have an application including a set of windows services (.NET). I d like to run it into a docker container. What should I do ?
I have successfully put a Windows Service into a docker container using the following Dockerfile. Replace MyWindowsServiceName with the name of your own windows service.
# escape=\
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.7.2-windowsservercore-1709
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
COPY ["MyWindowsServiceName/bin/Release/", "/Service/"]
WORKDIR "C:/Service/"
RUN "C:/Service/InstallUtil.exe" /LogToConsole=true /ShowCallStack MyWindowsServiceName.exe; \
Set-Service -Name "\"MyWindowsServiceName\"" -StartupType Automatic; \
Set-ItemProperty "\"Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MyWindowsServiceName\"" -Name AllowRemoteConnection -Value 1
ENTRYPOINT ["powershell"]
CMD Start-Service \""MyWindowsServiceName\""; \
Get-EventLog -LogName System -After (Get-Date).AddHours(-1) | Format-List ;\
$idx = (get-eventlog -LogName System -Newest 1).Index; \
while ($true) \
{; \
start-sleep -Seconds 1; \
$idx2 = (Get-EventLog -LogName System -newest 1).index; \
get-eventlog -logname system -newest ($idx2 - $idx) | sort index | Format-List; \
$idx = $idx2; \
}
NOTE1: My windows service logs to the Windows Event system. So this file contains some nice code at the end to print EventLog information to the console, as per Docker convention. You may or may not need this part for your own service. If not, only use the first line minus the '\'.
NOTE2: The name of a windows service may be different to its executable name. That is, 'MyWindowsServiceName.exe' could have a service name of 'My Windows Service Name' or 'Fred', you need to know both.
Starting from the answer provided by QA Collective, this is what worked for me.
Note 1 No InstallUtil.exe required
Note 2 the "Get-Event..." part from the last part of the code is just to keep the process alive so that the container will continue to run.
Note 3 You can set the StartupType as automatic and remove the Start-Service from the CMD.
Note 4 When you provide the BinaryPathName Make sure to provide the FULL PATH. It stores it in the registries and if you provide a relative path, it won't know where to run it from and you'd get some nasty errors that will make you cry - what happened to me.
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
COPY ["Release/", "/Service/"]
WORKDIR "C:/Service/"
RUN New-Service -Name "\"MyService"\" -StartupType "\"Manual"\" -BinaryPathName "\"C:\\Service\\MyService.exe"\";
ENTRYPOINT ["powershell"]
cmd Start-Service "MyService"; \
Get-EventLog -LogName System -After (Get-Date).AddHours(-1) | Format-List ;\
$idx = (get-eventlog -LogName System -Newest 1).Index; \
while ($true) \
{; \
start-sleep -Seconds 1; \
$idx2 = (Get-EventLog -LogName System -newest 1).index; \
get-eventlog -logname system -newest ($idx2 - $idx) | sort index | Format-List; \
$idx = $idx2; \
}
In general, you should choose a base image which has the necessary libraries already installed on it as much as possible instead of taking a very base image such as plain Linux or Windows and installing on it.
In your case, select a docker image which has .NET installed on it.This image for instance The ideal flow is as follows.
Select the Docker Image you want to use
Include a Dockerfile in the root location of your project
Include commands in Dockerfile to copy the code or the executable on to the image
Specify a start command
Build the Image docker build -t YourRepoName . Run this at the location of your Dockerfile
Test it docker run YourImage
Dockerfile This is one of the dockerfiles I wrote for Springboot. You may use it for reference. Please note, I am copy only the jar file here onto my Container and not the source code since at the point of building the docker container, the jar file is available. You may choose to include commands for copying the source code and creating the executable inside the Dockerfile.

Can't install node in docker image microsoft/dotnet:2.1-sdk for windows architecture

I asked this question originally at: https://github.com/aspnet/aspnet-docker/issues/349 as a part of the deprecation announcement, and I am hoping the SO community may have a good answer for this:
I am trying to use the windows side for a SPA build using the microsoft/dotnet:2.1-sdk. I know, I may be the only one trying to stay on the windows side for an ASP.NET Core Application, but our swarm initially will only have windows servers running in the native OS mode and not Hyper-V mode.
As a consequence, I need to install node.js for windows (because node.js/grunt/gulp are no longer a part of the image like they were in the microsoft/aspnetcore:2.0 image) and I tried:
RUN msiexec.exe /a https://nodejs.org/dist/v8.11.3/node-v8.11.3-x64.msi /quiet
but msiexec.exe isn't in the c:\windows\system32 of this image or in any other directory for that matter.
curl also is not in this image so I can't use that to download anything, and even if I could how do I un-tar or unzip anything?
I can't run a powershell invocation of:
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
to try to install chocolatey to install node from there as System.Net.WebClient is not available in this image.
I guess my question is is there any container native way to get node.js installed internally without having to download something outside the container, copying it in, and then executing it. Kinda defeats the purpose of a multistage build if I have to do that or at least in my opinion makes it an ugly solution.
instead curl use powershell's Invoke-WebRequest
instead unzip use Expand-Archive
installing MSI in nanoserver is not possible. For solution see: Powershell Silent Install in Nano Server with Docker
Working off the answer from Miq I was able to create an initial Dockerfile which uses the 2.1 SDK image and pulls in node manually, here is what it looks like:
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /app
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ENV NODE_VERSION 8.11.3
ENV NODE_FULL_NAME node-v8.11.3-win-x64
#install node and npm as MS no longer does this in any .NET Core nanoserver based images since 2.1
RUN New-Item -ItemType directory -Path /build; \
Invoke-WebRequest https://nodejs.org/dist/v${env:NODE_VERSION}/${env:NODE_FULL_NAME}.zip -OutFile /build/${env:NODE_FULL_NAME}.zip; \
Expand-Archive -LiteralPath /build/${env:NODE_FULL_NAME}.zip /build; \
$newPath = (Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).path; \
$nodeFullName = ${env:NODE_FULL_NAME}; \
$newPath = $newPath + ';/build/' + $nodeFullName; \
Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath;
They added Tar and Curl to the base runtimes.
FROM microsoft/dotnet:2.2-aspnetcore-runtime-nanoserver-1803 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
#Download the package we want and unzip it to our destination
RUN curl.exe -o node.zip https://nodejs.org/dist/v10.15.3/node-v10.15.3-win-x64.zip && \
mkdir "C:\\Program Files\\node" && \
tar.exe -xf node.zip -C "C:\\Program Files\\node" --strip-components=1
A different approach, using ADD and running as Administrator so I can set the PATH.
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-nanoserver-1903 as base
USER Administrator
WORKDIR /app
EXPOSE 80
EXPOSE 443
EXPOSE 1433
ADD https://nodejs.org/dist/v12.9.1/node-v12.9.1-win-x64.zip C:\\build\\node-v12.9.1-win-x64.zip
RUN mkdir "C:\\Program Files\\node" && \
  tar.exe -xf C:\\build\\node-v12.9.1-win-x64.zip -C "C:\\Program Files\\node" --strip-components=1
RUN setx /M PATH "C:\Program Files\node;%PATH%"

Error running erlang in Windows Container

I'm attempting to get RabbitMQ up and running inside a Windows container but without a whole lot of luck. I've copied into the container the installation directories for RabbitMQ and Erlang but when I attempt to run erl.exe I'm told that beam.smp.dll is not able to be loaded.
PS C:\Program Files\erl8.2\bin> .\erl.exe
Unable to load emulator DLL
(C:\Program Files\erl8.2\erts-8.2\bin\beam.smp.dll)
Running the same command on the same installation directory on the host machine works just fine. I've checked that the file exists and that the checksums match. My bet is that there is some subtile difference in how the container loads the file and how the host loads the file. I'm just not sure where to even start looking.
Here is my Dockerfile which works. I can connect to RabbitMQ and the logs show it is running correctly, and I can login to the management UI using the guest/guest login from my host using IP/hostname of container.
# start with this container as the base
FROM microsoft/windowsservercore
# erlang installer download url
ENV erlang_download_url "http://erlang.org/download/otp_win64_19.3.exe"
# erlang will install to this location and rabbitmq will use this environment variable to locate it
ENV ERLANG_HOME c:\\erlang
# rabbitmq version used in download url and to rename folder extracted from zip file
ENV rabbitmq_version "3.6.9"
# rabbitmq zip package download url
ENV rabbit_download_url "https://www.rabbitmq.com/releases/rabbitmq-server/v3.6.9/rabbitmq-server-windows-$rabbitmq_version.zip"
# setup powershell options for RUN commands
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# download and install erlang using silent install option, and remove installer when done
RUN Invoke-WebRequest -Uri $env:erlang_download_url -OutFile erlang_install.exe ; \
Start-Process -Wait -FilePath .\erlang_install.exe -ArgumentList /S, /D=$env:ERLANG_HOME ; \
Remove-Item -Force erlang_install.exe
# download and extract rabbitmq, and remove zip file when done
RUN Invoke-WebRequest -Uri $env:rabbit_download_url -OutFile rabbitmq.zip ; \
Expand-Archive -Path .\rabbitmq.zip -DestinationPath "c:\\" ; \
Remove-Item -Force rabbitmq.zip
# remove version from rabbitmq folder name
RUN Rename-Item c:\rabbitmq_server-$env:rabbitmq_version c:\rabbitmq
# enable managment plugin
RUN c:\rabbitmq\sbin\rabbitmq-plugins.bat enable rabbitmq_management --offline
# tell rabbitmq where to find our custom config file
ENV RABBITMQ_CONFIG_FILE "c:\rabbitmq"
RUN ["cmd", "/c", "echo [{rabbit, [{loopback_users, []}]}].> c:\\rabbitmq.config"]
# run server when container starts - container will shutdown when this process ends
CMD "c:\rabbitmq\sbin\rabbitmq-server.bat"

Adding SSL certificates to Website to Docker

I have a website that runs on ssl i.e. https, I want to deploy it to Docker Windows Containers with Docker Desktop for Windows. So I wanted to ask how can it be done, I have added the certificates to the container, and when I use
RUN powershell -NoProfile -Command certmgr.exe -add MyCert.cer -s -r localMachine trustedpublisher
It gives this error.
certmgr.exe : The term 'certmgr.exe' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling of the
name, or if a path was included, verify that the path is correct and try again.
So can you explain how would it be done?
certmgr.exe
needs Visual Studio so it cant be run in Containers. Following is a way to do it if it helps anyone. Add this in the docker file when you are creating the image
RUN mkdir C:\cert
#cert folder contains the certificates YourCertificate.cer & Name.pfx
ADD cert/ /cert
RUN powershell -NoProfile -Command \
certutil -addstore "Root" "C:/cert/YourCertificate.cer"
RUN powershell -NoProfile -Command \
certutil -importpfx -p "password" "C:/cert/Name.pfx"
RUN powershell -NoProfile -Command \
New-WebBinding -Name "YourWebsite" -IP "*" -Port 1234 -Protocol https
RUN powershell -NoProfile -Command \
get-item cert:\LocalMachine\MY\thumbprint-of-your-cert | New-Item 0.0.0.0!1234
1234 is the port which you can bind with your website. It will bind your website to the certificate.

Resources