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

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

问题一:如此部署的应用,在宿主机外部,只能通过宿主机的ip加一个个特定的端口来访问每个容器内的应用,这显然是不满足实际需求的。

问题二:相比于将应用直接部署在有UI界面的Windows Server,因为每个应用部署于自己的Windows Docker容器,当应用运行时发生各种问题时,比如,cpu高,内存高,访问变慢等等,如何才能方便地排查问题呢?即使你愿意一个个容器attach上去,也因为它没有UI,远没有传统有UI界面的Windows Server上容易。所以,我们必须有必要的工具来更方便的监控容器的运行。

下篇

  • 负载均衡和反向代理

  • 日志解析和监控

负载均衡和反向代理

要解决问题一:

  • 首先,我们需要一个机制,当用户访问我们的ip或域名时,服务器能根据不同的域名,或者不同的子路径,在相同的端口比如80或者443,从每个应用容器返回内容——这就是反向代理;

  • 接着,我们不希望我们的应用在更新、系统维护、或者局部故障时无法提供服务,所以,每个部署的应用不能是单点,那么,如果同一个应用部署了多个容器实例,如何才能让他们Serve相同的域名和URL请求呢?——这就是负载均衡;

通常,支持反向代理的组件,往往也同时提供负载均衡功能。例如:F5、nginx、Apache2、HAProxy甚至IIS的ARR等。不同的方案可能侧重点略有不同,我们可以根据实际情况选择不同的方案。另外,既然我们的应用部署在Windows Docker服务器,那么最好我们所用的代理组件同样能部署在Windows Docker容器,这样我们就能用一致的流程和工具来管理。下面的示例中,我们选择Apache2,实现一个部署于Windows Docker部署的反向代理加负载均衡器。

为了演示负载均衡,我们新建一个two-instances-demo目录,其中docker-compose.yml里为iis-demo添加两个不同内部ip的容器实例,再添加一个apache容器,它的Dockerfile定义在apache目录中。

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

Apache的Dockerfile,很简单,只是安装和覆盖默认conf,然后,运行https.exe服务。

FROM microsoft/windowsservercore:latestADD vc_redist.x64.exe /vc_redist.x64.exe
RUN /vc_redist.x64.exe /qADD httpd-2.4.25-x64-vc14-r1.zip /
RUN powershell -executionpolicy bypass -Command "expand-archive -Path 'c:\httpd-2.4.25-x64-vc14-r1.zip' -DestinationPath 'c:\'"COPY conf /confRUN del c:\Apache24\conf\httpd.conf
RUN del c:\Apache24\conf\extra\httpd-vhosts.confRUN copy "c:\conf\httpd.conf" "c:\Apache24\conf\"RUN copy "c:\conf\extra\httpd-vhosts.conf" "c:\Apache24\conf\extra\"ENTRYPOINT ["C:\\Apache24\\bin\\httpd.exe"]

然后,我们打开一个命令窗口,在two-instances-demo目录下执行docker-compose up,稍作等待,等容器运行起来,然后,在宿主机外部,注意一定是从宿主机外部(原因上一篇文章有解释),访问宿主机的ip地址下的/iis-demo/路径,可以看到,iis-demo的默认页面内容能够被正常显示,说明反向代理和负载均衡已经正常运行了。

其他备注:

  • 这里的Apache配置是出于演示目的,抛砖引玉,极度简化的版本,如果用于真实环境,请根据Apache官方文档以和相应的性能测试,做必要调整;

  • 除了Apache之外,nginx可以运行于Windows,但是性能不佳,官方不推荐在Windows下使用;

  • HAProxy只能运行于Linux;

  • IIS的ARR,反向代理功能尚可,但是负载均衡依赖于IIS的集群,限制颇多,如果同一个应用的容器只需要部署单个实例的话可以考虑;

  • 这里在docker-compose.yml中定义同一个应用的两个服务的做法,只是在不使用docker的集群化部署时的简化做法,如果应用了Swarm集群,或者使用了其他支持集群和自动扩展的容器编排方案,是可以直接通过docker-compose的scale参数,让一个docker-compose服务运行多个实例的,这个我们以后会聊到;

日志解析和监控

要解决问题二:

  • 首先,我们需要一个方便的机制查看宿主机上每个容器运行时的CPU,内存,IO等资源开销,还要能保留这些运行情况的历史纪录,便于回溯和问题的排查;

  • 其次,我们需要一个方便的机制查看宿主机上每个容器内的系统和应用产生的日志,例如:操作系统日志、IIS访问日志、应用的异常日志等;

对于容器的运行时的性能指标,docker的命令行工具,提供了docker stats命令,可以查看每个容器实时的CPU、内存、IO能指标,我们可以考虑定时将它们收集保存起来,用于集中化的监控。

另外,玩过Linux下docker的小伙伴们肯定知道,docker会将每个容器内运行时打印到console的内容,都记录在宿主机的docker日志目录中,而大多数Linux容器部署应用,大多会将应用自己的日志也打印到console,这样,所有的日志都可以包含在docker宿主机的容器日志中。这有什么好处呢?好处就是,我们可以在宿主机上,配置日志解析工具,比如Logstash或fluentd,解析和forward所有日志。

在Windows Docker下,由于Windows的基因问题,一方面,大多数应用都是基于IIS的应用,没办法将日志直接打印到console,另一方面,IIS本身的日志和Windows的EventLog也无法方便地配置把它们打印到console,所以,一般的做法是,需要把这些日志所在的目录,mount到宿主机,然后,再在宿主机上统一解析。特别对于Windows EventLog,它在Windows文件系统的格式无法被简单读取和解析,因此,我们一般需要用到一些地第三方工具,如nxlog和Elastic公司Beats(这两个工具都是免费开源的)将解析后的Windows EventLog保存为易于解析的格式,比如JSON格式。

对于经过解析的日志,现在比较流行的做法是把它导入Elasticsearch,这样就可以方便通过kibana,grafana这样的工具,可视化查看,远程实时监控了。

本想将相关组件都做成Windows Docker镜像,方便大家能直接下载运行的,无奈这些组件都比较大,动辄几十上百兆,国内的网络下,不FQ的情况下,我本机下载都很费劲,想必,做成Docker镜像,大家运行的体验也不会很好,所以,就先不做了。下面简单介绍一下这几个工具的使用,并分享一些核心的配置脚本,给大家做个参考。

首先是对IIS Log和Windows EventLog的解析,以nxlog为例:

nxlog的Windows版安装完之后,是一个Windows Service。它的配置文件在C:\Program Files (x86)\nxlog\conf目录下,每次更改nxlog.conf文件,都需要重启nxlog service使配置生效。最经常的用法,一般是将原始的IIS的W3C格式的日志,还有Windows EventLog解析为JSON格式,然后经过Logstash中转之后保存到Elasticsearch。

下面是一个典型的解析IIS W3C格式Log的nxlog.conf文件:

define ROOT C:\Program Files (x86)\nxlogModuledir %ROOT%\modulesCacheDir %ROOT%\dataPidfile %ROOT%\data\nxlog.pidSpoolDir %ROOT%\dataLogFile %ROOT%\data\nxlog.log<Extension json>    Module      xm_json
</Extension><Extension w3c>    Module      xm_csv    Fields      $log_date, $log_time, $log_site_id, $log_server_name, $log_server_ip, $log_http_method, $log_path, $log_query, $log_port, $log_user_name, $log_client_ip, $log_http_version, $log_user_agent, $log_referer, $log_domain_name, $log_http_status, $log_http_substatus, $log_win32_status, $log_response_size, $log_request_size, $log_time_taken    FieldTypes  string, string, string, string, string, string, string, string, integer, string, string, string, string, string, string, integer, integer, integer, integer, integer, integer    Delimiter   ' 'EscapeControl FALSEUndefValue -
</Extension><Input in-iis>    Module      im_file    File        "C:\\temp\\iislogs\\\\u_ex*.log"SavePos         TrueReadFromLast    FalseActiveFiles     10Exec            if $raw_event =~ /^#/ drop(); else { w3c->parse_csv(); if ($log_date) $log_request_timestamp = $log_date + " " + $log_time; else drop(); if ($log_referer) $log_referer = lc($log_referer); if ($log_path) { $log_path = lc($log_path); if $log_path =~ /(\.[^.]+)/ $log_request_type = $1; else $log_request_type = "unknown"; } if ($log_user_agent) $log_user_agent = replace($log_user_agent, "+", " "); if ($log_domain_name) $log_domain_name = replace($log_domain_name, ":80", ""); };
</Input><Output iis>    Module      om_tcp    Exec        $raw_event = to_json();    Host        localhost    Port        5151</Output><Route out_iis>    Path    in-iis => iis
</Route>

它的第一部分Extension W3C定义了哪些W3C字段需要解析;第二部分iis input调用w3c扩展组件,解析指定目录的日志文件,做必要的规整;第三部分定义了如何保存解析结果,将解析后的消息,保存为JSON格式的键值对,然后写入一个Logstash的TCP输入端口。

下面是一个典型的nxlog解析Windows EventLog的例子:

define ROOT C:\Program Files (x86)\nxlog Moduledir %ROOT%\modules CacheDir %ROOT%\data Pidfile %ROOT%\data\nxlog.pid SpoolDir %ROOT%\data LogFile %ROOT%\data\nxlog.log<Extension _syslog>    Module      xm_syslog</Extension><Extension _json>   Module xm_json</Extension><Input eventlog>    Module      im_msvistalog        ReadFromLast TRUE    SavePos     FALSE    Query       <QueryList>\                    <Query Id="0">\                        <Select Path="System">*</Select>\                        <Select Path="Application">*</Select>\                    </Query>\                </QueryList>    </Input><Output out>    Module      om_file    File    "C:\temp\EventLog\" + $Hostname + ".json"    <Exec>        if out->file_size() > 20M        {             $newfile = "C:\temp\EventLog\" + $Hostname + "_" + strftime(now(), "%Y%m%d%H%M%S") + ".json";             out->rotate_to($newfile);        }     </Exec>    Exec        to_json();</Output><Route 1>    Path        eventlog => out</Route>

这里,第一部分我们定义了如何从Windows EventLog中筛选消息;第二部分,定义了如何以每20M为大小,分割保存最新的EventLog为JSON。

将JSON格式的数据通过Logstash保存到Elasticsearch非常简单,网上示例比比皆是,这理解不举例了。

最后分享一个Logstash的配置文件,用于每隔30秒,收集宿主机上所有docker容器的性能指标,并且以JSON格式,保存到Elasticsearch:

input {exec {command => "C:\temp\get-docker-stats.cmd"interval => 30codec => line {}}
}filter {grok {match => { "message" => "%{WORD:container_id} %{WORD:container_name} %{NUMBER:cpu_percent}% %{NUMBER:mem} %{WORD:mem_unit} %{NUMBER:net_in} %{WORD:net_in_unit} / %{NUMBER:net_out} %{WORD:net_out_unit} %{NUMBER:block_in} %{WORD:block_in_unit} / %{NUMBER:block_out} %{WORD:block_out_unit}" }}mutate {convert => {        "cpu_percent" => "float""mem" => "float""net_in" => "float""net_out" => "float""block_in" => "float""block_out" => "float"}}  #calc memory bytesif [mem_unit] == "KiB" { ruby { code => "event.set('mem_bytes',event.get('mem').to_f*1024)" } }  if [mem_unit] == "MiB" { ruby { code => "event.set('mem_bytes',event.get('mem').to_f*1024*1024)" } }  if [mem_unit] == "GiB" { ruby { code => "event.set('mem_bytes',event.get('mem').to_f*1024*1024*1024)" } }  #calc net_in bytesif [net_in_unit] == "kB" { ruby { code => "event.set('net_in_bytes',event.get('net_in').to_f*1000)" } }  if [net_in_unit] == "MB" { ruby { code => "event.set('net_in_bytes',event.get('net_in').to_f*1000*1000)" } }  #calc net_out bytesif [net_out_unit] == "kB" { ruby { code => "event.set('net_out_bytes',event.get('net_out').to_f*1000)" } }  if [net_out_unit] == "MB" { ruby { code => "event.set('net_out_bytes',event.get('net_out').to_f*1000*1000)" } }  #calc block_in bytesif [block_in_unit] == "kB" { ruby { code => "event.set('block_in_bytes',event.get('block_in').to_f*1000)" } }  if [block_in_unit] == "MB" { ruby { code => "event.set('block_in_bytes',event.get('block_in').to_f*1000*1000)" } }  #calc block_out bytesif [block_out_unit] == "kB" { ruby { code => "event.set('block_out_bytes',event.get('block_out').to_f*1000)" } }  if [block_out_unit] == "MB" { ruby { code => "event.set('block_out_bytes',event.get('block_out').to_f*1000*1000)" } }mutate {remove_field => ["mem", "mem_unit", "net_in", "net_in_unit", "net_out", "net_out_unit", "block_in", "block_in_unit", "block_out", "block_out_unit", "message", "command"]}
}output {elasticsearch {hosts => ["localhost"]index => "logstash-docker-stats-log-%{+YYYY.MM.dd}"timeout => 30workers => 1}
}

其中,get-docker-stats.cmd文件真正执行docker stats命令,获取所有正在运行的容器的性能指标,其中具体的命令如下:

@echo off
docker stats --no-stream --format "{{.Container}} {{.Name}} {{.CPUPerc}} {{.MemUsage}} {{.NetIO}} {{.BlockIO}}"

所有JSON格式的监控数据保存到Elasticsearch以后,使用kibana或者grafana进行数据的展示、设置监控警报等等,就相对比较简单了,目前也非常流行,网上应该是能找到非常多的示例的,使用上也不存在Linux和Windows的区别,这里就不详述了。对这方面有疑问的同学,我们可以私下交流。

单节点Windows Docker服务器简单运维下篇完。

我们简单回顾一下,在最近的上下两篇中,我们介绍了运维一个单节点Windows Docker服务器的主要思路和常用工具。这些思想和工具,也是更复杂的docker集群模式下的运维的基础。运维的水很深,Windows Docker的运维,对大多数公司来说,也都还只是在摸索的过程中。

前面的示例中,虽然尽可能不依赖于Linux下的容器,但毕竟Linux下的容器和各种支持工具,现在已经非常成熟了,在实际的部署中,还是应该根据实际情况和Windows Docker结合使用,只要能解决问题的工具和方法就都是极好的!

下次见!

相关文章:

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

  • 老司机实战Windows Server Docker:2 docker化现有iis应用的正确姿势

  • 老司机实战Windows Server Docker:3 单节点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-3.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/326312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2018蓝桥杯省赛---java---C---4( 第几个幸运数)

题目描述 思路分析 直接暴力 代码实现 package TEST;public class Main {public static void main(String[] args) {long n 59084709587505L, cnt 0;for (long a 1; a < n; a * 3)for (long b 1; b < n; b * 5)for (long c 1; c < n; c * 7)if (a * b * c <…

MySQL year()函数

转载自 MySQL year()函数 MySQL YEAR函数简介 YEAR()函数接受date参数&#xff0c;并返回日期的年份。请参阅YEAR()函数的语法&#xff1a; YEAR(date);YEAR()函数返回一个指定日期的年份值&#xff0c;范围为1000到9999&#xff0c;如果日期为零&#xff0c;YEAR()函数返回…

MySQL协议.NET Core实现(一)

一个有技术追求的研发团对&#xff0c;无论使用什么框架、什么工具、什么语言&#xff0c;团队里应该有人有能力把控所使用框架、工具、语言的每一个核心功能的实现细节。团队里的每个成员应该根据自身所长挑选其中一块做深入研究&#xff0c;并把研究成果分享给团队&#xff0…

2018蓝桥杯省赛---java---C---7(缩位求和)

题目描述 问题描述在电子计算机普及以前&#xff0c;人们经常用一个粗略的方法来验算四则运算是否正确。 比如&#xff1a;248 * 15 3720 把乘数和被乘数分别逐位求和&#xff0c;如果是多位数再逐位求和&#xff0c;直到是1位数&#xff0c;得 2 4 8 14 > 1 4 5; 1 …

龙芯linux内核,龙芯的linux kernel,内核开发与编译

在很久很久以前&#xff0c;linux被视为geek极客的玩具。自行升级Linux内核&#xff0c;对普通用户来说&#xff0c;简直是天方夜谭。曾经的曾经&#xff0c;升级内核需要很多纷繁复杂的步骤&#xff0c;也需要花费很多的时间。但是&#xff0c;现在不一样了。内核的安装可以方…

2018蓝桥杯省赛---java---C---8(等腰三角形)

题目描述 问题描述本题目要求你在控制台输出一个由数字组成的等腰三角形。 具体的步骤是&#xff1a;先用1,2,3&#xff0c;…的自然数拼一个足够长的串 用这个串填充三角形的三条边。从上方顶点开始&#xff0c;逆时针填充。 比如&#xff0c;当三角形高度是8时&#xff1a;1…

Git,Git Flow,GitLab使用指南

高效利用一次蹲坑时间&#xff0c;看看如何使用Git Flow进行高效开发&#xff0c;什么才是Git提交的正确姿势&#xff0c;怎样使用GitLab进行Code Review&#xff1a; 使用Git Flow高效开发&#xff1b;Git提交正确姿势&#xff0c;Commit message编写指南&#xff1b;使用Git…

arm linux gcc 编译,Linux arm-linux-gcc交叉编译环境配置

Linux下的arm-linux-gcc交叉编译环境安装安装arm-linux-gcc(1) 打开终端&#xff0c;使用sudo命令进入从超级管理员&#xff1a;sudo su输入超级管理员密码。(2) 使用cd命令进入桌面&#xff1a;cd Desktop(3)复制arm-linux-gcc-4.4.3.tar.gz安装包到Ubuntu桌面下面(4)打开终端…

2019蓝桥杯省赛---java---C---1(求和)

题目描述 代码实现 package TEST;public class Main {public static void main(String[] args) {int cnt 0;for (int i 1; i < 2019; i)if (check(i)) cnt i;System.out.print(cnt);}static boolean check(int n) {String an"";if (a.contains("2")…

使用EntityFrameworkCore实现Repository, UnitOfWork,支持MySQL分库分表

昨天&#xff08;星期五&#xff09;下班&#xff0c;19&#xff1a;00左右回到家&#xff0c;洗个澡&#xff0c;然后20&#xff1a;30左右开始写代码&#xff0c;写完代码之后&#xff0c;上床看了《生活大爆炸10季》17、18两集&#xff0c;发现没有更新到19集&#xff0c;瞄…

2019蓝桥杯省赛---java---C---2(矩阵切割)

题目描述 代码实现 package TEST;import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int max sc.nextInt();int min sc.nextInt();int sum 0, temp;while (max ! 0 && min ! 0) {if (max …

期待微软平台即服务技术Service Fabric 开源

微软的Azure Service Fabric的官方博客在3.24日发布了一篇博客 Service Fabric .NET SDK goes open source &#xff0c;介绍了社区呼声最高的Service Fabric开源的情况以及当前的情况&#xff0c;这次开源了Service Fabric的.NET SDK部分&#xff0c;主要是两个&#xff1a; …

2019蓝桥杯省赛---java---C---4(质数)

题目描述 代码实现 package TEST; public class Main {public static void main(String[] args) {int cnt 0;for (int i 2; ; i )if(check(i)){cnt ;if(cnt 2019){System.out.println(i);break;}}}public static boolean check(int n){//判断一个数是否为质数for (int i 2…

eclipse下载与安装步骤详解,包含解决错误(最全最详细)

以前一直用的是myeclipse,今天有幸接触eclipse,那我们就先来安装的配置一下&#xff0c;下载地址&#xff1a;点击下载密码&#xff1a;h0kg&#xff0c;下载完成以后就可以安装了&#xff0c;首先我们来先安装jdk1.7, 打开jdk的安装包 双击即可&#xff0c; 直接点击下一步&…

CoreCLR文档翻译 - GC的设计

此文档来源于CoreCLR的BOTR(The Book of the Runtime), 点击打开原文一切著作权归微软公司所有 GC的设计 作者: Maoni Stephens (maoni0) - 2015 提示: 推荐看 The Garbage Collection Handbook 这本书学习更多关于GC的知识 (在文章底部的链接中) 组件结构 在GC中有两个主…

CoreCLR源码探索(四) GC内存收集器的内部实现 分析篇

在这篇中我将讲述GC Collector内部的实现, 这是CoreCLR中除了JIT以外最复杂部分&#xff0c;下面一些概念目前尚未有公开的文档和书籍讲到。 为了分析这部分我花了一个多月的时间&#xff0c;期间也多次向CoreCLR的开发组提问过&#xff0c;我有信心以下内容都是比较准确的&am…

vue开源项目

转载自 vue开源项目 一、前台UI组件库 1.Element 优点&#xff1a;中文文档&#xff0c;ui种类比较全&#xff0c;ui设计简洁清晰 缺点&#xff1a;不够有特点 2.iView 优点&#xff1a;和element的UI很相似&#xff0c;有一些多的补充&#xff0c;可以相互替换 缺点&am…

linux跑循环脚本占内存,Linux下实现脚本监测特定进程占用内存情况

Linux系统下&#xff0c;我们可以利用以下命令来获取特定进程的运行情况&#xff1a;cat /proc/$PID/status其中PID是具体的进程号&#xff0c;这个命令打印出/proc/特定进程/status文件的内容&#xff0c;信息比较多&#xff0c;包含了物理内存/虚拟内存的使用状况&#xff0c…

如何在vm虚拟机里面安装win10操作系统

首先打开虚拟机&#xff0c;点击创建虚拟机 然后选择典型即可&#xff01; 选择稍后安装操作系统 然后选择win10 64位 . 然后在找个路径&#xff1a; 默认60GB即可&#xff0c;也可以更改大小&#xff1a; 最后点击完成&#xff1a; 接下来我们需要用U盘制作一个启动盘…