老司机实战Windows Server Docker:5 Windows Server Dockerfile葵花宝典
前面兩篇(簡單運維1、簡單運維2)介紹了一些Windows Server Docker相關的基本運維知識。今天這一篇,Windows Server Dockerfile葵花寶典,涵蓋了許多典型場景的Windows Server下的Dockerfile實例,并且每一個都包含可直接運行的代碼實例,完全開源,并且新示例持續添加中。也希望大家能一起貢獻經驗。
示例源碼
Github Repo: windows-dockerfile-lab
所有示例均經過Windows Server 2016環境實際測試。如果你需要了解如何配置一個Windows Server Docker的測試環境,可以參考本系列的第一篇。
咱們從一些基礎的Dockerfile命令說起:
ENTRYPOINT和CMD
源碼:entrypoint_and_cmd
FROM microsoft/windowsservercore#usually, ENTRYPOINT should be fixed and CMD to be overridden when necessary #always use the exec form instead of shell mode for ENTRYPOINT and CMD commands and paramsENTRYPOINT ["ping", "-n", "3"] CMD ["baidu.com"]#to build, execute: docker build -t entrypoint_and_cmd . #to override CMD, execute: docker run --rm entrypoint_and_cmd 360.cn每一個Dockerfile必須的兩個命令是FROM命令,和ENTRYPOINT/CMD。FROM命令無需多說,就是指定當前docker image的爹是誰。這里重點說說,ENTRYPOINT和CMD命令。ENTRYPOINT和CMD都可以用來指定docker容器實例啟動時執行的啟動程序,這兩個命令可以分別使用,但是一般推薦組合使用。
以上面的示例為例,這里組合使用了ENTRYPOINT和CMD,運行時的效果是什么呢?
如果我們在命令行執行docker run entrypoint_and_cmd啟動這個docker容器,它就相當于執行了下面這一行命令:
ping -n 3 baidu.com而如果我們用docker run entrypoint_and_cmd 360.cn啟動這個容器,則相當于執行了下面這條命令:
ping -n 3 360.cn也就是說,通過CMD指定的部分,在執行docker run時,如果在參數中的image名字后面指定了一些參數,這些參數會覆蓋Dockerfile中定義的CMD后面的命令。
限于篇幅,本文中演示的示例,都是解決相同問題的最佳實踐或者推薦實踐。但是不會擴展講太多為什么這是最佳實踐,但是我會附上一些參考資料,感興趣的朋友可以自行閱讀。以上面的ENTRYPOINT和CMD的區別問題,更多延伸內容,請參考這篇文章:Dockerfile: ENTRYPOINT vs CMD
ADD和COPY
源碼:add_and_copy
FROM microsoft/windowsservercore#on windows, ADD & COPY almost the sameADD test_folder /test_folder_add COPY test_folder /test_folder_copyADD test_folder /test_folder_add2 COPY test_folder /test_folder_copy2#
unlike on linux, ADD doesn't support uncompressing zip files on windowsADD test_folder.zip / COPY test_folder.zip /ADD test_folder.zip /test_folder_add2.zip COPY test_folder.zip /test_folder_copy2.zip#
how to ADD or COPY files containing space in the nameADD ["file name with space.txt", "/"] COPY ["file name with space.txt", "/file name with space copy.txt"]#
overwriting files also worksADD test.txt /test_overwrite_add.txt ADD test_overwrite_add.txt /test_overwrite_add.txtCOPY test.txt /test_overwrite_copy.txt COPY test_overwrite_copy.txt /test_overwrite_copy.txt#
add files with wildchar also worksADD wildchar* / RUN del wildchar* COPY wildchar* /#the only obvious difference of ADD and COPY on windows is, you can use ADD for downloading files, but not COPYADD https://github.com/jquery/jquery/archive/3.2.1.zip / ADD https://github.com/jquery/jquery/archive/3.2.1.zip /3.2.1_2.zipENTRYPOINT ["cmd", "/c", "dir"]#to build, execute: docker build -t add_and_copy .#
to run, execute: docker run --rm add_and_copy
ADD和COPY也是幾乎每個Dockerfile都會用到的命令,它們的作用其實非常類似,就是將文件或者目錄從docker client執行的機器復制到正在編譯的docker image中。這兩個命令只有一些細小差別:
ADD命令可用于從一個URL下載文件,而COPY命令不行;
在Linux下的docker中,ADD命令還有一個特殊能力,就是如果ADD一個zip壓縮包的話,docker build時它能做自動解壓縮,但是在Windows Docker下,沒有這個效果;
合并和減少RUN命令
源碼:merge_and_reduce_run
FROM microsoft/windowsservercore#run 2 commands in sequence even if the firts one fails, but not the second fails RUN cd notexists & md folder1#run 2 commands in sequence only if both succeed RUN md folder2 && md folder3#run 2 commands in sequence only if at least one succeeds RUN md folder4 || cd notexists#if one line of RUN is too long, breakdown into multiple lines with \ at the end #so that it is more friendly for code review RUN echo 1 \ ? ? ? ? 2 \ ? ? ? ? 3 ENTRYPOINT ["cmd", "/c", "dir"]#to build, execute: docker build -t merge_and_reduce_run . #to run CMD, execute: docker run --rm merge_and_reduce_run在執行docker build的時候,我們一定能注意到它有step 1,step 2……每個step對于docker build來說,實際上就是創建一個臨時的docker image,只執行了一個RUN,所以,如果我們不注意,將每個細小的步驟都寫一個RUN的話,最后就會發現,我們的Dockerfile的build變得非常慢,因為步驟太多了。所以,我們應該合理地合并沒必要分開RUN的命令。
但是,合并的兩個命令到一個RUN里,其實沒表面那么簡單。以上面的示例為例,前面三個主要是說如何合并多個windows cmd命令:
&連接的兩個命令代表即使前一個命令執行失敗,只要后一個命令執行成功,這個RUN就不算失敗;
&&連接的兩個命令代表兩個命令都必須執行成功,整個RUN才成功;
||連接的兩個命令代表只要任意一個執行成功,就算整個RUN成功;
注意,所謂RUN成功指的是,如果RUN失敗的話,docker build就會中斷執行。因此,我們要根據實際情況,只允許可以失敗的命令失敗,確保重要的命令必須成功。
當然,除了合并windowns cmd命令,我們也可以合并多個powershell命令到一個RUN,例如:
RUN powershell -command "command1;command2;command3"或者,如果有比較復雜的多個命令,我們最好把多個命令寫成一個.cmd或者.ps1腳本,這樣,Dockerfile就只需要一個RUN了。
另外,對于一行RUN太長的情況,最好通過""分割成多行書寫,這主要是為了方便做代碼的Review,這樣在diff工具里,能更清晰的顯示到底改了什么參數。
到目前為止的sample,其實都不不算Windows Docker特定的案例,相比Linux下,其實都很類似,屬于基礎中的基礎。下面,我們就開始介紹一些Windows下特有的案例:
unzip
源碼:unzip
FROM microsoft/windowsservercoreCOPY test_folder.zip /#unzipRUN powershell -Command "expand-archive -Path'c:\test_folder.zip' -DestinationPath 'c:\'"ENTRYPOINT ["cmd", "/c", "dir"]
#to build, execute: docker build -t unzip .#to run CMD, execute: docker run --rm unzip
前面我們說到,ADD命令在Windows Docker下不支持解壓縮zip文件。那么,在Windows下如何解壓縮呢?最簡單的方法,就是使用Expand-Archive這個powershell命令。
set_hosts
源碼:set_hosts
FROM microsoft/iis#install ASP.NET 4.5RUN dism /online /enable-feature /all /featurename:IIS-ASPNET45 /NoRestart#deploy webapp COPY iis-demo /inetpub/wwwroot/iis-demo RUN /windows/system32/inetsrv/appcmd.exe add app /site.name:"Default Web Site"/path:"/iis-demo" /physicalPath:"c:\inetpub\wwwroot\iis-demo"#set entrypoint script COPY scripts /scripts ENTRYPOINT ["C:\\scripts\\SetHostsAndStartMonitoring.cmd"]#to build, execute: docker build -t set_hosts . #to run, execute: docker run --rm --env-file ./hosts.env set_hosts
在前面的文章中,我們提到過Windows下的Docker目前不支持docker run的--add-host參數。所以,這里我們提供了一個基于環境變量,這里用一個環境變量文件(./hosts.env ),設置Windows系統的hosts文件的方法。其中讀取環境變量并設置hosts的代碼,其實就是下面的powershell腳本:
If ($env:HOSTS) { ?$hosts = $env:HOSTS.Replace(",", "`r`n"); ?$hosts| Set-Content "C:\Windows\System32\drivers\etc\hosts""Applied hosts: `r`n" + $hosts; }
gacutil
源碼:gacutil
FROM microsoft/windowsservercore#copy minimized gacutil binary to container COPY tools.zip / RUN powershell -Command "expand-archive -Path 'c:\tools.zip' -DestinationPath 'c:\'"#install a DLL to GAC with gacutil COPY Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll / RUN /tools/gacutil.exe /i Microsoft.VisualStudio.QualityTools.UnitTestFramework.dllENTRYPOINT ["cmd", "/c", "/tools/gacutil.exe", "/l", "Microsoft.VisualStudio.QualityTools.UnitTestFramework"]#to build, execute: docker build -t gacutil . #to run, execute: docker run --rm gacutilgacutil是常用的通過命令行注冊.NET DLL到GAC的工具。但是這個工具包含在.Net Framework SDK中,并不包含于.NET Framework的分發庫中。而為了注冊幾個DLL而讓docker容器里面安裝一個臃腫的.NET SDK實在有點難受。因此,這個示例包含了從.NET Framework SDK中抽取出來的單獨的gacutil工具,只有94k大小。
enable_eventlog
源碼:enable_eventlog
FROM microsoft/windowsservercore#enable eventlog RUN powershell.exe -command Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\WMI\Autologger\EventLog-Application Start 1# list latest 10 system eventlog CMD powershell -command "Get-EventLog system -newest 10 | Format-List"#to build, execute: docker build -t enable_eventlog . #to run, execute: docker run --rm enable_eventlog這個太簡單了,不多解釋了,就是通過powershell設置了一個注冊表值。
enable_wcf
源碼:enable_wcf
FROM microsoft/iis#install ASP.NET 4.5RUN dism /online /enable-feature /all /featurename:IIS-ASPNET45 /NoRestart#install WCF features, here we only enabled http and tcp protocol #for the full list of features you can enable, execute "dism /online /get-features" on a windows server machine RUN dism /online /enable-feature /featurename:WCF-HTTP-Activation45 /all RUN dism /online /enable-feature /featurename:WCF-TCP-Activation45 /all#enable WCF protocols RUN /windows/system32/inetsrv/appcmd.exe set config -section:system.applicationHost/sites /[@0].[@0].enabledProtocols:"http,net.tcp"ENTRYPOINT ["c:\\ServiceMonitor.exe", "w3svc"]#to build, execute: docker build -t enable_wcf . #to run, execute: docker run --rm enable_wcf在遠古的SOA時代(好像就在眼前,悲傷),WCF是.NET下最熱門的技術之一,現在是江河日下了。不過,如何通過命令行enable WCF并設置IIS里的protocols呢?
另外,提一下,上面這行ENTRYPOINT ["c:\ServiceMonitor.exe", "w3svc"]其實不是必須的,因為microsoft/iis這個image其實默認就是執行的這個ENTRYPOINT。這其實是在監控w3svc這個Windows Service的運行,我們當然也可以用它來監控我們自己的Windows Service的,這里順便提一下。
set_iis_ssl
源碼:set_iis_ssl
FROM microsoft/iis#install ASP.NET 4.5RUN dism /online /enable-feature /all /featurename:IIS-ASPNET45 /NoRestart#setup SSL in IISADD iis-test.pfx \iis-test.pfxADD SetSSL.ps1 \SetSSL.ps1#set entrypoint scriptADD SetSSLAndStart.cmd \SetSSLAndStart.cmd ENTRYPOINT ["C:\\SetSSLAndStart.cmd"]#to build, execute: docker build -t set_iis_ssl . #to run, execute: docker run --rm -e "SSL_PASS=test" -p 443:443 set_iis_ssl通過命令行設置IIS的SSL,這個可能干過的不多,但現在SSL幾乎是大多數主流網站的標配了,在docker容器里部署網站,也是必不可少的。其中,主要的邏輯,在SetSSLAndStart.cmd中調用SetSSL.ps1執行:
cmd /c "certutil -p %SSL_PASS% -importPFX c:\iis-test.pfx"$addr=[System.Net.Dns]::GetHostAddresses([System.Environment]::MachineName).IPAddressToString $env:ssl_addr=$addr[1]+":443"cmd /c 'netsh http add sslcert ipport=%ssl_addr%
certstorename=MY certhash=C97E4D29C00D9B250EADCFE27D50F09FA76599B0 appid="
{4dc3e181-e14b-4a21-b022-59fc669b0914}"'New-WebBinding -name "Default Web Site"
-Protocol https ?-HostHeader * -Port 443 -SslFlags 1
需要注意的是,這里SSL_PASS包含的證書密碼,是讀取的一個環境變量。因為,pfx格式的證書中包含非常重要的私鑰,我們不可以將密碼寫在腳本中,必須在docker run的時候傳入。另外,netsh http add sslcert的參數中,certhash這個參數的值,這里hardcode了iis-test.pfx這個證書的hash值,如果你要安裝的是你自己的證書,需要用你自己證書的hash值替換。對于已經安裝于當前機器的自定義證書,我們可以通過下面的powershell命令,列出所有的證書和它們的hash值:
Get-ChildItem cert:\LocalMachine\My再有,這里的SSL我們是設置到機器當前的ip,除了將SSL通過ipport參數綁定到ip,我們也可以通過hostnameport將SSL綁定到hostname,具體請參考netsh http add sslcert的相關文檔。另外,這個示例的部分代碼參考了這篇文章
本篇完!不過,本文今后還會持續更新,后面有新的Windows Server Dockerfile的武功心法,我會陸續補充到這篇文章,大家也可以關注相關的Github Repo獲取更新。
相關文章:
老司機實戰Windows Server Docker:1 初體驗之各種填坑
老司機實戰Windows Server Docker:2 docker化現有iis應用的正確姿勢
老司機實戰Windows Server Docker:3 單節點Windows Docker服務器簡單運維(上)
老司機實戰Windows Server Docker:4 單節點Windows Docker服務器簡單運維(下)
.Net大戶的選擇:Windows Container在攜程的應用
Docker4Dev #6 使用 Windows Container 運行.net應用
Docker基礎入門及示例
Linux+Nginx+Asp.net Core部署
原文地址:http://www.cnblogs.com/teddyma/p/Windows-Server-Docker-5.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的老司机实战Windows Server Docker:5 Windows Server Dockerfile葵花宝典的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GitHub 贡献第一的微软开源软件列表
- 下一篇: 编写高效率的C#代码