前言

上一篇老司机实战Windows Server Docker:1 初体验之各种填坑介绍了安装docker服务过程中的一些小坑。这一篇,我们来填一些稍大一些的坑:如何docker化一个现有的iis应用。

问题分析

听说Windows支持原生docker了,大家一定都很兴奋。然而,大家想过没有,Windows Server Docker最适合什么场景呢?部署.NET Core应用?为什么不选择Linux下的docker?正常的决策者脑袋被门挤了才会花钱额外买Windows Server的license,用来部署.NET Core吧?所以,在本人看来,Windows Server Docker最大的价值,还是在于部署传统基于WindowsServerCore的应用。这样的应用一般有两大类,一类是基于iis的网站应用;另一类是Windows Service。本文主要关注基于iis的应用的docker部署。

那么,部署一个iis应用到docker,是不是只要起一个iis的docker容器实例,远程连接,并且,copy文件进去,能通过容器内的iis访问就行了?如果,有人问这样的问题,那么,说明他还完全没有容器的思维。上面说的这个,其实就成了将容器当虚拟机用了,这将极大地限制了docker原有的灵活扩展能力。因此,可以说是使用Windows docker最糟糕的姿势之一了。

要正确部署一个iis应用到Windows Server Docker,并不是表面那么简单。限于篇幅,并且为了更专注,本文先不涉及容器编排、负载均衡、images的构建和管理等问题(这些要考虑的问题还有很多,以后我们单独聊),这里只关注如何将一个基于iis的应用正确运行于单个Windows Server Docker实例中。即便如此,一般至少也要解决下面这些问题:

  • Dockerfile:如何通过Dockerfile部署应用文件和设置操作系统和IIS配置,如何为不同的运行环境(开发,测试,生产)配置不同参数;
  • 查看系统日志:典型的系统日志包含IIS Logs、Windows Event Log和应用的异常日志;
  • 重启容器实例:当容器实例重启时,如何保证被部署的应用能保持之前的工作状态,能继续服务;
  • 网络路由:包括容器内部如何访问外部系统、docker宿主机如何访问容器内部、外部系统如何访问容器内部;

应用示例

为便于理解和演示,我在github上写了一个简单的示例应用:windows-docker-iis-demo

这个应用只包含一个页面,在我本机运行时,显示类似下面的内容:

Hello Docker!

Configuration:
env1=Dev (from appSettings in web.config)
env2=Dev (from OS environment variable) Content of C:\Windows\System32\drivers\etc\hosts: # Localhost (DO NOT REMOVE)
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback

其中env1为web.config中的appSettings值,env2读取的系统环境变量,页面最下面打印出当前Windows系统的的hosts。

定义Dockerfile如下:

FROM microsoft/iis

# install ASP.NET 4.5
RUN dism /online /enable-feature /all /featurename:IIS-ASPNET45 /NoRestart # enable windows eventlog
RUN powershell.exe -command Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\WMI\Autologger\EventLog-Application Start 1 # set IIS log fields
RUN /windows/system32/inetsrv/appcmd.exe set config /section:system.applicationHost/sites /siteDefaults.logFile.logExtFileFlags:"Date, Time, ClientIP, UserName, SiteName, ServerIP, Method, UriStem, UriQuery, HttpStatus, Win32Status, TimeTaken, ServerPort, UserAgent, Referer, HttpSubStatus" /commit:apphost # deploy webapp
COPY publish /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
ADD SetHostsAndStartMonitoring.cmd \SetHostsAndStartMonitoring.cmd
ENTRYPOINT ["C:\\SetHostsAndStartMonitoring.cmd"] # declare volumes
VOLUME ["c:/inetpub/logs/LogFiles"]

我们分别来理解一下Dockerfile每一段的含义:

  • 首先是安装ASP.NET 4.5;
  • 接着,开启Windows EventLog;
  • 第三步,我们修改了默认的IIS Logs字段列表;
  • 第四步,将当前目录下的publish目录的内容复制到容器的/inetpub/wwwroot/iis-demo目录,并且在iis中添加对应的iis-demo应用;
  • 第五步,设置一个自定义的启动脚本;
  • 最后,声明了一个VOLUME,以便将IIS Logs保存到容器外的宿主机上;

启动脚本SetHostsAndStartMonitoring.cmd的内容如下:

powershell -executionpolicy bypass -Command "If ($env:HOSTS) { $hosts = $env:HOSTS.Replace(':', ' ').Replace(',', '\r\n'); $hosts | Set-Content 'C:\Windows\System32\drivers\etc\hosts'; 'Applied hosts: ' + $hosts" }

powershell -executionpolicy bypass -Command "if ($env:env1) { (Get-Content 'c:\inetpub\wwwroot\iis-demo\web.config').replace('Dev', $env:env1) | Set-Content 'c:\inetpub\wwwroot\iis-demo\web.config' };

c:\ServiceMonitor.exe w3svc

其中,第一部分读取HOSTS这个系统环境变量,覆盖当前系统的hosts文件;第二步读取env1环境变量,覆盖web.config中的对应配置;最后调用继承自microsoft/iis image的ServiceMonitor.exe命令,监控iis主进程,直到其退出。

下面,我们来试着build这个docker。因为到目前为止(本系列的第一篇+第二篇),我们还只能从这台Windows Server机器上执行docker命令,以后的文章会讲到如何从远程server build以及如何集成到CI工具进行build,这里先绕过。我们在VS2015中编译这个webapp,并且发布到publish目录。然后,复制整个windows-docker-iis-demo目录到这台docker宿主机的C盘根目录,以便进行docker build。这个当然不是build docker image的正常姿势,只是因为我们还没提到其他方式,我们先粗糙一点,把它build出来,以便可以运行。

在我们的Windows Server 2016机器上,打开一个administrator模式的powershell窗口,cd到c:\windows-docker-iis-demo目录,然后执行docker build命令制作image:

docker build -t iis-demo:1.0 .

编译成功后,执行docker images,可以看到多了一个iis-demo:1.0的docker image。接着,让我们在宿主机的C盘创建一个temp目录(为下面的volume使用,mount到容器内部的iis日志),然后执行下面的命令运行一个iis-demo的docker instance:

docker run --ip 172.24.128.2 -p 80 -v "c:/temp:c:/inetpub/logs/LogFiles" -e "env1=LIVE1" -e "env2=LIVE2" -e "HOSTS=1.2.3.4:TEST.COM" iis-demo:1.0

这里的参数分别表示:

  • 指定容器ip=172.24.128.2
  • 允许80端口被外部访问
  • 将容器内的c:/inetpub/logs/LogFiles目录mount到宿主机的c:/temp
  • 设置3个系统环境变量env1,env2,HOSTS

稍等片刻,等待容器实例运行,然后在宿主机的浏览器中访问,可以看到如下的内容:

Hello Docker!

Configuration:
env1=LIVE1 (from appSettings in web.config)
env2=Dev (from OS environment variable) Content of C:\Windows\System32\drivers\etc\hosts:
1.2.3.4 TEST.COM

对比前面在开发环境运行的结果,我们可以看到有一些有意思又诡异的区别:

  • 首先,通过前面的启动脚本SetHostsAndStartMonitoring.cmd读取的环境变量env1和HOSTS都生效了;
  • 然而,在程序中运行时读取的环境变量env2没有生效(这个好坑人!!意味着,无法直接在webapp中读取docker run传递进来的环境变量。一开始怀疑是因为webapp的进程启动时间早于docker run指定的环境变量生效的时间,但是即使进到容器里recycle 对应的apppool,还是不生效,具体原因有待后续验证了);

另外,在宿主机的c:\temp目录,我们可以看到从容器实例写道外部的iis log。

好了,看看至此我们已经解决了哪些最开始提到的问题了:

  • 首先,我们实现了在docker run时,指定不同的参数,传递进容器,比如覆盖web.config中的设置,又比如,设置了额外的hosts文件中的dns解析;
  • 对于希望方便查看的日志,我们可以通过volume,mount到宿主机的目录;
  • 同样的,我们也可以mount应用自己的数据到宿主机,这样容器实例重启时,应用的状态也能保持;
  • 因为可以在docker run时传入参数被应用读取,我们可以用同一个docker image,在不同的环境(开发、测试、生产)指定不同的参数,比如,数据库连接字串;
  • 网络方面,关于如何从外部系统访问容器内部,我们会在后续篇章详细讨论,这里,因为可以将自定义hosts传递进容器,所以容器访问外部系统的任何地址,都不用担心无法解析;

应该说,我们已经解决了大多数前面提到但实例运行时需要解决的问题了。然而,别忘了,这一篇里,我们只针对单服务器,单容器实例。在实际的部署案例中,是绝不允许单点,无法扩展的。

后面几篇,我会展开讲讲这一篇跳过的一些非常重要的话题,例如网络配置、远程管理、负载均衡、实时监控、以及更高级的容器编排和集群实现等等,敬请期待!

下一篇 3 单节点Windows Docker服务器简单运维(上)

老司机实战Windows Server Docker:2 docker化现有iis应用的正确姿势的更多相关文章

  1. 老司机实战Windows Server Docker:3 单节点Windows Docker服务器简单运维(上)

    经过上两篇实战Windows Server Docker系列文章,大家对安装Windows Docker服务以及如何打包现有IIS应用为docker镜像已经有了基本认识.接下来我们来简单讲讲一些最基本 ...

  2. 老司机实战Windows Server Docker:1 初体验之各种填坑

    前言 Windows Server 2016正式版发布已经有近半年时间了,除了看到携程的同学分享了一些Windows Server Docker的实践经验,网上比较深入的资料,不管是中文或英文的,都还 ...

  3. 老司机实战Windows Server Docker:4 单节点Windows Docker服务器简单运维(下)

    上篇中,我们主要介绍了使用docker-compose对Windows Docker单服务器进行远程管理,编译和部署镜像,并且设置容器的自动启动.但是,还有一些重要的问题没有解决,这些问题不解决,就完 ...

  4. 老司机实战Windows Server Docker:5 Windows Server Dockerfile葵花宝典

    前面两篇(简单运维1.简单运维2)介绍了一些Windows Server Docker相关的基本运维知识.今天这一篇,Windows Server Dockerfile葵花宝典,涵盖了许多典型场景的W ...

  5. Windows server 2016安装Docker EE

    Windows server 2016安装Docker EE 下载 windows server 2016 180天评估版本. 地址:https://www.microsoft.com/en-us/e ...

  6. windows server 2016安装docker

    最近微软发布了windows server 2016,并原生支持docker,本文通过一系列的步骤,来学习怎么在windows server 2016安装docker. 1.下载 windows se ...

  7. SSL安装方法一:在Windows Server 2008安装SSL证书(IIS 7.0)

    购买的是GlobalSign 公司的通配符域名型SSL 大致的意思就是“通配符公用名填写*.域名.com,这个下面的所有子域名是不受数量限制的,*可以换成任意字符” 1 生成数字证书签名请求文件(CS ...

  8. Windows Server 2016 安装Docker

    使用Windows自带的Hyper-V 而不是安装Docker Enterprise. 废话不多说,撸起袖子开干 管理员权限打开PowerShell (因为server版本默认是cmd不是ps,所以需 ...

  9. 实战 Windows Server 2012 群集共享卷

    群集共享卷,简单理解就是一个针对Hyper-V优化的一个分布式访问文件系统,群集中的节点可以同时写到一个磁盘,并且是受控访问,以避免写数据的冲突.此功能和Vmware  的VMFS是类似的.此功能从W ...

随机推荐

  1. 设计模式之单一职责原则(SRP)

    自己之前写过一些关于设计模式的博客,但是大部分都写得比较匆忙.现在正好趁年前有时间,笔者打算好好地整理一下自己这块知识结构.开篇的第一个原则就是设计原则里面最简单的一个原则--单一职责原则. 想必大家 ...

  2. HDU2602(背包)

    Bone Collector Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  3. C#进阶系列——使用Advanced Installer制作IIS安装包(一:配置IIS和Web.config)

    前言:大过年的,写篇文章不容易,还是给自己点个赞~~年前找了下.net安装包的制作方法,发现Visual Studio自带的制作工具使用起来非常麻烦,需要单独下载安装包,并且什么激活认证等等屁事相当麻 ...

  4. SQLSERVER 中实现类似Mysql的 INSERT ON DUPLICATE KEY UPDATE

    通过SQLServer创建索引时,有一个IGNORE_DUP_KEY的选项,可以类似实现. IGNORE_DUP_KEY = { ON | OFF } 指定对唯一聚集索引或唯一非聚集索引执行多行插入操 ...

  5. Git使用规范

    团队开发中,遵循一个合理.清晰的Git使用流程,是非常重要的.为了团队研发的规范性,特制定此规范.当然不同的团队可能有着不同的约定,此规范仅供参考. 目录 1.基本原则 2.建议流程 基本原则 不到万 ...

  6. PLSQL DEVELOPER 使用的一些技巧【转】

    1.登录后默认自动选中My Objects 默认情况下,PLSQL Developer登录后,Brower里会选择All objects,如果你登录的用户是dba,要展开tables目录,正常情况都需 ...

  7. QML Object Attributes QML对象属性

    QML Object Attributes Every QML object type has a defined set of attributes. Each instance of an obj ...

  8. docker - 容器里安装ssh

    docker安装ssh 通过命令行安装 pull ubuntu镜像 docker pull ubuntu:latest 启动并进入bash docker run -it -d ubuntu:laste ...

  9. winform连接oracle时Oracle.DataAccess.dll版本问题

    1.通用TestOracle.zip部署到iis上,或直接运行程序测试当前全局程序集 protected void Button1_Click(object sender, EventArgs e) ...

  10. hibernate 为什么持久化类时必须提供一个不带参数的默认构造函数

    因为hibernate框架会调用这个默认构造方法来构造实例对象..即Class类的newInstance方法 这个方法就是通过调用默认构造方法来创建实例对象的 ,另外再提醒一点,如果你没有提供任何构造 ...