前面两篇(简单运维1、简单运维2)介绍了一些Windows Server Docker相关的基本运维知识。今天这一篇,Windows Server Dockerfile葵花宝典,涵盖了许多典型场景的Windows Server下的Dockerfile实例,并且每一个都包含可直接运行的代码实例,完全开源,并且新示例持续添加中。也希望大家能一起贡献经验。 示例源码Github Repo: windows-dockerfile-lab 所有示例均经过Windows Server 2016环境实际测试。如果你需要了解如何配置一个Windows Server Docker的测试环境,可以参考本系列的第一篇。 咱们从一些基础的Dockerfile命令说起: ENTRYPOINT和CMDFROM 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容器,它就相当于执行了下面这一行命令:
而如果我们用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
ADD和COPY也是几乎每个Dockerfile都会用到的命令,它们的作用其实非常类似,就是将文件或者目录从docker client执行的机器复制到正在编译的docker image中。这两个命令只有一些细小差别:
合并和减少RUN命令FROM microsoft/windowsservercore#run 2 commands in sequence even if the firts one fails, but not the second failsRUN cd notexists & md folder1#run 2 commands in sequence only if both succeedRUN md folder2 && md folder3#run 2 commands in sequence only if at least one succeedsRUN 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 reviewRUN 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失败的话,docker build就会中断执行。因此,我们要根据实际情况,只允许可以失败的命令失败,确保重要的命令必须成功。 当然,除了合并windowns cmd命令,我们也可以合并多个powershell命令到一个RUN,例如:
或者,如果有比较复杂的多个命令,我们最好把多个命令写成一个.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
在前面的文章中,我们提到过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
gacutil是常用的通过命令行注册.NET DLL到GAC的工具。但是这个工具包含在.Net Framework SDK中,并不包含于.NET Framework的分发库中。而为了注册几个DLL而让docker容器里面安装一个臃肿的.NET SDK实在有点难受。因此,这个示例包含了从.NET Framework SDK中抽取出来的单独的gacutil工具,只有94k大小。 enable_eventlogFROM microsoft/windowsservercore#enable eventlogRUN powershell.exe -command Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\WMI\Autologger\EventLog-Application Start 1# list latest 10 system eventlogCMD 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
在远古的SOA时代(好像就在眼前,悲伤),WCF是.NET下最热门的技术之一,现在是江河日下了。不过,如何通过命令行enable WCF并设置IIS里的protocols呢? 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.cmdENTRYPOINT ['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执行:
需要注意的是,这里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获取更新。 |
|