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

时间:2022-01-17 04:43:49

经过上两篇实战Windows Server Docker系列文章,大家对安装Windows Docker服务以及如何打包现有IIS应用为docker镜像已经有了基本认识。接下来我们来简单讲讲一些最基本的运维问题。鉴于到目前为止我们只谈到单服务器部署。这里暂时不涉及集群模式下的复杂生产环境运维。

本主题将涵盖下面这些主要内容,由于这个主题包含的内容较多,将分成上下两篇发布:

上篇

  • 远程操作Windows Docker服务器
  • 自动化Docker编译和部署
  • Windows Docker网络配置和端口映射

下篇

  • 负载均衡和反向代理
  • 日志解析和监控

远程操作Windows Docker服务器

和Linux下的Docker服务默认通过TCP 2375/2376端口执行docker命令不同,Windows Docker服务默认只支持本机基于命名管道来执行docker命令。如果安装完Windows Docker服务后,在命令窗口执行netstat -an,可以看到,服务器并不监听2375或者2376这样的端口。当然,我们可以通过修改daemon.json这个配置文件(关于如何修改daemon.json文件,请参考本系列的第一篇),来开启2375/2376端口。2375端口和linux下的2375端口一样,用于非TLS的远程连接;而2376则支持基于证书的安全连接。对于,开发测试环境,一般开启2375端口就行,而对于生产环境,则应该使用2376端口,基于TLS安全连接来远程管理Docker服务器。

要开启2375端口,只需要如下在daemon.json添加hosts配置项,然后重启Docker服务:

{
"hosts": ["tcp://0.0.0.0:2375"]
}

要开启2376端口也类似,就是稍微复杂一点,需要生成和配置证书,具体的可以参考微软的这篇官方文档这里就不详述了。

开启2375端口之后,可以通过在命令窗口执行如下的netstat命令,确保2375端口已经正确监听:

netstat -an | findstr 2375

如果返回类似下面,就说明已经在监听了:

  TCP    0.0.0.0:2375           0.0.0.0:0              LISTENING
TCP [::]:2375 [::]:0 LISTENING

只是开始监听还不够,要允许外部机器能够远程访问本机的2375端口,我们还要添加防火墙的Inbound规则,可以通过下面的powershell命令开启:

New-NetFirewallRule -DisplayName 'Docker TCP Inbound' -Profile @('Domain', 'Public', 'Private') -Direction Inbound -Action Allow -Protocol TCP -LocalPort 2375

接下来,我们就可以尝试从安装了Docker client的远程机器,访问这台Windows Docker服务器了。Docker client有各种跨平台版本,所以只要是相兼容的版本,我既可以从Windows的docker client,也可以从linux的docker client通过2375端口远程访问docker服务器。

当我们从当前机器,比如你的开发机访问一台远程docker服务器时,我们需要为docker client指定目标HOST。一般有两种方式:一种是可以通过在每个docker命令执行时,添加-H server_name_or_ip参数,另一种是设置DOCKER_HOST环境变量,这样,就不需要每个docker命令都带-H参数了。例如,下面的命令会列出远程服务器1.2.3.4的所有docker images:

docker -H 1.2.3.4 images
docker -H tcp://1.2.3.4 images
docker -H tcp://1.2.3.4:2375 images

上面的几种指定host的语法都是可以的,如果使用默认的2375端口,最简单的就可以用第一种,少打不少字符。如果要指定DOCKER_HOST环境变量,那么必须使用完整的格式,比如:

SET DOCKER_HOST=tcp://1.2.3.4:2375

开启远程访问之后,当需要从开发或者编译环境编译和发布docker镜像时,我们就不需要像上一篇中那么蛋疼的先copy发布出来的应用网站文件到docker服务器,然后在服务器上执行docker build了,只需要直接从开发环境执行docker build,就能直接在远程docker服务器上生成和运行docker镜像了。

自动化Docker编译和部署

docker的命令行工具,有许多子命令和参数。如果要经常编译和运行docker镜像,我们当然不希望每次都手打所有参数,例如,下面的命令编译一个docker镜像,并运行一个docker容器,每次手打一边实在没啥乐趣:

docker build -t iis-demo:1.0 .
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

为了简化这个过程,通常我们会写一些带参数的powershell脚本;或者,我们可以使用另一个docker的必备命令行工具:docker-compose

docker-compose是一个docker官方发布的docker容器编排工具,用于通过yml格式的配置文件来简化docker命令的执行。例如,上面这两个编译并运行docker容器的脚本,如果我们定义如下这个docker-compose.yml配置文件:

version: "2.1"
services:
iis-demo:
build: .
image: "iis-demo:1.0"
ports:
- "80"
networks:
nat:
ipv4_address: 172.24.128.2
volumes:
- "c:/temp:c:/inetpub/logs/LogFiles"
environment:
- "env1=LIVE1"
- "env2=LIVE2"
- "HOSTS=1.2.3.4:TEST.COM"
networks:
nat:
external: true

那么,我们就可以在docker-compose.yml所在的目录,通过下面这一条命令,就能自动编译docker镜像,并且运行一个docker容器了:

docker-compose up

是不是超级简单?事实上,docker-compose几乎包装了所有通过docker命令行可以执行的参数,而且,一个docker-compose.yml文件,可以包含多个相关的docker镜像的配置,比如,你的这个应用可能依赖于某一个DB,或者另一个应用的,那么,写在一个yml文件里,就能很方便的一键编译,一键发布,一键关闭这些相关容器。限于篇幅,本文就不细说docker-compose各种功能了,大家可以参看官方文档和示例自行摸索。

这里简单讲一下,在windows下,如何安装docker-compose。

  • 首先,docker-compose是一个客户端工具,也就是,它应该安装于安装了docker client的机器,并不需要安装于docker服务器;
  • 对Windows版本的docker-compose来说,它就是一个单个文件的.exe文件,你只需要从官网下载Windows版本的.exe文件,将文件名改成docker-compose.exe,然后随便放到任何方便访问的目录就可以了。我比较懒,一般直接丢到c:\windows\system32目录,那样就不需要设置额外的PATH路径,就能在任何当前目录下,在命令窗口直接执行了。

有了,docker-compose,我们也可以很简单的设置我们的docker服务器每次机器启动之后,自动运行指定的docker容器,只需要配置一个机器的启动脚本,每次机器启动后自动运行docker-compose up,就可以了。

有人可能想问,如何为windows设置自动启动脚本?一个简单的方法是,使用NSSM这个小工具。例如,下载nssm的exe到本地后,只需要在命令行执行:

nssm install "Docker Startup" command param1 param2 ...

就能将指定的命令变成自动开机执行的windows service了。是不是很方便?网上介绍NSSM也文章也很多的,大家可以搜索一些,这里我就给几个link,不过多介绍了:

Windows Docker网络配置和端口映射

微软官方,主要就这一篇文档介绍了windows容器的网络设置。介绍得其实并不是很深入。主要来说,它介绍了Windows Docker支持的几种网络模式,例如:nat、transparent、overlay、l2bridge。其中,最常用的,大家最容易理解的,也是Windows Docker服务安装后的默认模式是nat模式。简单的说,就是:

  • 宿主机上运行的所有的容器,都属于一个子网段,每个容器运行时,可以静态指定网段内可用的ip,或者自动分配一个网段内的ip;
  • 子网段内的ip,在宿主机外部无法直接访问,只能从宿主机上直接访问;
  • 可以通过docker命令的-p参数,设置静态或者动态的宿主机ip到容器ip的端口映射,这样宿主机的外部网络,就可以通过宿主机的ip和端口,间接访问到容器内的网站了;

例如,在上面的示例中的docker run --ip 172.24.128.2 -p 80 ...里,-p 80这个参数就会导致docker命令执行时,动态分配一个宿主机上的端口,映射到容器ip的80端口上。如果我们执行上面的docker run命令,或者docker-compose up命令后,在命令窗口执行docker ps,可以看到下面的正在运行的容器:

0f0e07424d80        iis-demo:latest     "C:\\SetHostsAndSta..."   5 minutes ago       Up 4 minutes        0.0.0.0:39924->80/tcp        windowsdockeriisdemo_iis-demo_1

其中,0.0.0.0:39924就是指的,对宿主机上任意ip的39924端口的访问,已经被映射到这个容器的子网ip的80端口了。

但是,这里有个坑,很深的坑,让我一度差点怀疑人生,甚至重装了机器的坑!!!——如果你在宿主机上的浏览器里,访问比如http://localhost:39924/iis-demo,按这个端口映射,你天真的以为就能访问到容器内的网站,尤其示玩过linux下容器的小伙伴,更绝对会认为它能访问。但是,它其实不能!!什么原因呢?我为此一度差点崩溃,最后,在某个windows docker的github issuer中,有开发人员解释说,这个是因为windows版本的实现问题,在宿主机本机,这个映射的端口无法访问,但是,在宿主机外部,访问宿主机上的ip加这个端口是可以访问的!!只允许从外部访问!!WTF?这是什么鬼的道理?不过,反正,现状就是如此,也只能忍了。

除了使用动态分配的端口,如果我们把参数改成例如-p 80:80,那么就能通过宿主机的ip:80端口,访问到容器内的网站了。再提醒一遍,记住,只能从宿主机外部访问!!

微软的官方文档,除了介绍了几种常见的网络模式如何配置,还提到了一些Windows版本的Docker相对于linux版本的docker没有实现的功能,下面的这些docker命令的参数,对Windows Docker无效:

  • --add-host
  • --dns-opt
  • --dns-search
  • --aux-address
  • --internal
  • --ip-range

其中,最遗憾的就是--add-host参数没有实现。--add-host参数原本是用于为容器内的系统的hosts文件添加静态dns解析的,这是一个非常常用的功能,没有它,部署到容器内的应用可能无法解析某些服务器名称或者域名。当然,我们可以自己实现类似功能,例如,在上一篇 docker化现有iis应用中,我们就实现了一个通过环境变量,传入,并设置hosts的简单功能,从而避免了这个参数没实现带来的痛点。

上篇完

下篇 4 单节点Windows Docker服务器简单运维(下)