<p>在之前我有专门写两篇文章介绍过<a href="https://www.cnblogs.com/edisonchou/p/exceptionless_deployment_on_production_env_introduction.html" target="_blank">Exceptionless这款开源日志项目的使用和部署</a>,但是当时是基于4.1.0版本(2017年的release),时隔两年多Exceptionless也推出了5.0.0版本。</p>
<h1>一、关于Exceptionless 5.0.0</h1>
<p> Exceptionless 是一个开源的实时的好用的日志收集框架,它将日志收集变得简单易用并且不需要了解太多的相关技术细节及配置。但是之前的版本将其Web和API绑定在了Windows平台通过IIS运行,对于已经步入云原生时代的我们显得有点格格不入。5.0.0的发布解决了这一痛点,其最大的变化就是基于ASP.NET Core重写并支持跨平台,也就是说当初我们设想的要是能够基于Docker部署在Linux服务器下就更好了的愿望已经实现了,在此真心<em><strong>感谢Exceptionless项目的各位贡献者</strong></em>。本文就Exceptionless 5.0.0版本介绍一下快速地部署开发环境和生产环境,相信对有兴趣的朋友会有一点帮助。</p>
<p> 至此我也可以将我们之前的Exceptionless从Windows Server迁移到Linux上了!</p>
<h1>二、快速本地部署步骤</h1>
<h2>2.1 安装Docker 18.09+</h2>
<p> 由于Exceptionless 5.0.0的一个前置要求是Docker版本(CE)在18.09及以上,因此我们需要安装一个18.09+的Docker CE版本到Linux服务器上,如果你之前安装了可以跳过此步骤,但如果版本小于18.09,那么请清理掉老版本升级到新版本,升级版本可以参考以下步骤。</p>
<p> 实验环境:<a href="https://www.aliyun.com/product/ecs" target="_blank">阿里云ECS主机</a>,CentOS 7.4</p>
<p> (1)清理已有Docker老版本</p>
<p> 停止Docker老版本:</p>
<div class="cnblogs_code">
<pre>systemctl stop docker</pre>
</div>
<p> 卸载软件包:</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #0000ff;">yum</span><span style="color: #000000;"> erase docker
docker</span>-<span style="color: #000000;">client
docker</span>-client-<span style="color: #000000;">latest
docker</span>-<span style="color: #000000;">common
docker</span>-<span style="color: #000000;">latest
docker</span>-latest-<span style="color: #000000;">logrotate
docker</span>-<span style="color: #000000;">logrotate
docker</span>-<span style="color: #000000;">selinux
docker</span>-engine-<span style="color: #000000;">selinux
docker</span>-<span style="color: #000000;">engine
docker</span>-ce</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p> 删除相关配置文件:</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">find</span> /etc/systemd -name <span style="color: #800000;">'</span><span style="color: #800000;">docker</span><span style="color: #800000;">'</span> -exec <span style="color: #0000ff;">rm</span> -<span style="color: #000000;">f {} ;
</span><span style="color: #0000ff;">find</span> /etc/systemd -name <span style="color: #800000;">'</span><span style="color: #800000;">docker</span><span style="color: #800000;">'</span> -exec <span style="color: #0000ff;">rm</span> -<span style="color: #000000;">f {} ;
</span><span style="color: #0000ff;">find</span> /lib/systemd -name <span style="color: #800000;">'</span><span style="color: #800000;">docker</span><span style="color: #800000;">'</span> -exec <span style="color: #0000ff;">rm</span> -<span style="color: #000000;">f {} ;
</span><span style="color: #0000ff;">rm</span> -rf /var/lib/<span style="color: #000000;">docker #删除以前已有的镜像和容器,非必要,慎删
</span><span style="color: #0000ff;">rm</span> -rf /var/run/docker </pre>
</div>
<p> (2)安装Docker 18.09+</p>
<p> 软件包安装:</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">yum</span> <span style="color: #0000ff;">install</span> -y <span style="color: #0000ff;">yum</span>-utils device-mapper-persistent-data lvm2</pre>
</div>
<p> 添加yum源:</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">yum</span>-config-<span style="color: #000000;">manager
</span>--add-<span style="color: #000000;">repo
https:</span><span style="color: #008000;">//</span><span style="color: #008000;">download.docker.com/linux/centos/docker-ce.repo</span></pre>
</div>
<p> 查看可安装的版本:目前最新版本已经是19.03</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">yum</span> list docker-ce --showduplicates | <span style="color: #0000ff;">sort</span> -r</pre>
</div>
<p> 安装指定版本:18.09</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">yum</span> <span style="color: #0000ff;">install</span> docker-ce docker-ce-18.09.9-3.el7 -y</pre>
</div>
<p> 启动Docker并设置开机自启动:</p>
<div class="cnblogs_code">
<pre><span style="color: #000000;">systemctl start docker
systemctl enable docker</span></pre>
</div>
<p> 查看Docker版本:</p>
<div class="cnblogs_code">
<pre>docker version </pre>
</div>
<p> 你可以看到已经是18.09版本了:</p>
<p> <img style="width: 70%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190929225710500-226633119.png" alt=""></p>
<h2>2.2 下载Exceptionless 5.0.0 Release包</h2>
<p> 传送门:<a href="https://github.com/exceptionless/Exceptionless/releases" target="_blank">Exceptionless release</a></p>
<p> <img style="width: 60%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930143735712-2018898135.png" alt=""></p>
<h2>2.3 安装Exceptionless 5.0.0</h2>
<p> (1)修改docker-compose.yml文件,设置外部访问地址/域名(适配你的服务器IP地址 或 域名+SSL证书,这里我直接修改为我的阿里云服务器的外网IP地址,参考我的注释)</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre>version: <span style="color: #800000;">'</span><span style="color: #800000;">3.4</span><span style="color: #800000;">'</span><span style="color: #000000;">
services:
api:
depends_on:
</span>-<span style="color: #000000;"> elasticsearch
</span>-<span style="color: #000000;"> redis
build:
context: .
target: api
image: exceptionless</span>/<span style="color: #000000;">api:latest
restart: on</span>-<span style="color: #000000;">failure
environment:
EX_AppMode: Production
EX_BaseURL: http:</span><span style="color: #008000;">//</span><span style="color: #008000;">192.168.16.170:5100 #UI地址,修改这里的IP地址为你的服务器IP地址</span>
EX_ConnectionStrings__Cache: provider=<span style="color: #000000;">redis
EX_ConnectionStrings__Elasticsearch: server</span>=http:<span style="color: #008000;">//</span><span style="color: #008000;">elasticsearch:9200</span>
#EX_ConnectionStrings__Email: smtps:<span style="color: #008000;">//</span><span style="color: #008000;">user:password@smtp.host.com:587</span>
EX_ConnectionStrings__MessageBus: provider=<span style="color: #000000;">redis
#EX_ConnectionStrings__Metrics: provider</span>=statsd;server=<span style="color: #000000;">statsd;
EX_ConnectionStrings__Queue: provider</span>=<span style="color: #000000;">redis
EX_ConnectionStrings__Redis: server</span>=redis,abortConnect=<span style="color: #0000ff;">false</span><span style="color: #000000;">
EX_ConnectionStrings__Storage: provider</span>=folder;path=/app/<span style="color: #000000;">storage
EX_RunJobsInProcess: </span><span style="color: #800000;">'</span><span style="color: #800000;">false</span><span style="color: #800000;">'</span><span style="color: #000000;">
ports:
</span>- <span style="color: #800080;">5000</span>:<span style="color: #800080;">80</span> # This can be commented out <span style="color: #0000ff;">if</span><span style="color: #000000;"> using reverse proxy.
volumes:
</span>- appdata:/app/<span style="color: #000000;">storage
jobs:
depends_on:
</span>-<span style="color: #000000;"> api
build:
context: .
target: job
image: exceptionless</span>/<span style="color: #000000;">job:latest
restart: on</span>-<span style="color: #000000;">failure
environment:
EX_AppMode: Production
EX_BaseURL: http:</span><span style="color: #008000;">//</span><span style="color: #008000;">192.168.16.170:5100 #UI地址,修改这里的IP地址为你的服务器IP地址</span>
EX_ConnectionStrings__Cache: provider=<span style="color: #000000;">redis
EX_ConnectionStrings__Elasticsearch: server</span>=http:<span style="color: #008000;">//</span><span style="color: #008000;">elasticsearch:9200</span>
#EX_ConnectionStrings__Email: smtps:<span style="color: #008000;">//</span><span style="color: #008000;">user:password@smtp.host.com:587</span>
EX_ConnectionStrings__MessageBus: provider=<span style="color: #000000;">redis
#EX_ConnectionStrings__Metrics: provider</span>=statsd;server=<span style="color: #000000;">statsd;
EX_ConnectionStrings__Queue: provider</span>=<span style="color: #000000;">redis
EX_ConnectionStrings__Redis: server</span>=redis,abortConnect=<span style="color: #0000ff;">false</span><span style="color: #000000;">
EX_ConnectionStrings__Storage: provider</span>=folder;path=/app/<span style="color: #000000;">storage
volumes:
</span>- appdata:/app/<span style="color: #000000;">storage
ui:
image: exceptionless</span>/<span style="color: #000000;">ui:latest
environment:
AppMode: Production
EX_ApiUrl: http:</span><span style="color: #008000;">//</span><span style="color: #008000;">192.168.16.170:5000 #API地址,修改这里的IP地址为你的服务器IP地址</span>
#EX_Html5Mode: <span style="color: #800000;">'</span><span style="color: #800000;">false</span><span style="color: #800000;">'</span><span style="color: #000000;">
#EX_EnableSsl: </span><span style="color: #800000;">'</span><span style="color: #800000;">false</span><span style="color: #800000;">'</span><span style="color: #000000;">
#EX_EnableAccountCreation: </span><span style="color: #800000;">'</span><span style="color: #800000;">false</span><span style="color: #800000;">'</span><span style="color: #000000;">
ports:
</span>- <span style="color: #800080;">5100</span>:<span style="color: #800080;">80</span> # This can be commented out <span style="color: #0000ff;">if</span><span style="color: #000000;"> using reverse proxy.
reverseproxy:
depends_on:
</span>-<span style="color: #000000;"> api
</span>-<span style="color: #000000;"> ui
image: valian</span>/docker-nginx-auto-<span style="color: #000000;">ssl
restart: on</span>-<span style="color: #000000;">failure
ports:
</span>- <span style="color: #800080;">80</span>:<span style="color: #800080;">80</span><span style="color: #000000;">
</span>- <span style="color: #800080;">443</span>:<span style="color: #800080;">443</span><span style="color: #000000;">
volumes:
</span>- ssldata:/etc/resty-auto-<span style="color: #000000;">ssl
environment:
ALLOWED_DOMAINS: </span><span style="color: #800000;">'</span><span style="color: #800000;">(ex-ui|ex-api).mydomainn.com</span><span style="color: #800000;">'</span><span style="color: #000000;">
SITES: </span><span style="color: #800000;">'</span><span style="color: #800000;">ex-ui.mydomainn.com=ui;ex-api.mydomainn.com=api</span><span style="color: #800000;">'</span><span style="color: #000000;">
elasticsearch:
image: exceptionless</span>/elasticsearch:<span style="color: #800080;">1</span><span style="color: #000000;">
restart: on</span>-<span style="color: #000000;">failure
environment:
cluster.name: </span><span style="color: #800000;">'</span><span style="color: #800000;">exceptionless</span><span style="color: #800000;">'</span><span style="color: #000000;">
bootstrap.memory_lock: </span><span style="color: #800000;">'</span><span style="color: #800000;">true</span><span style="color: #800000;">'</span><span style="color: #000000;">
discovery.type: single</span>-<span style="color: #000000;">node
ES_JAVA_OPTS: </span><span style="color: #800000;">'</span><span style="color: #800000;">-Xms512m -Xmx512m</span><span style="color: #800000;">'</span><span style="color: #000000;">
xpack.security.enabled: </span><span style="color: #800000;">'</span><span style="color: #800000;">false</span><span style="color: #800000;">'</span><span style="color: #000000;">
xpack.graph.enabled: </span><span style="color: #800000;">'</span><span style="color: #800000;">false</span><span style="color: #800000;">'</span><span style="color: #000000;">
xpack.watcher.enabled: </span><span style="color: #800000;">'</span><span style="color: #800000;">false</span><span style="color: #800000;">'</span><span style="color: #000000;">
ports:
</span>- <span style="color: #800080;">9200</span>:<span style="color: #800080;">9200</span>
- <span style="color: #800080;">9300</span>:<span style="color: #800080;">9300</span><span style="color: #000000;">
ulimits:
memlock:
soft: </span>-<span style="color: #800080;">1</span><span style="color: #000000;">
hard: </span>-<span style="color: #800080;">1</span><span style="color: #000000;">
volumes:
</span>- esdata:/usr/share/elasticsearch/<span style="color: #000000;">data
kibana:
depends_on:
</span>-<span style="color: #000000;"> elasticsearch
image: exceptionless</span>/kibana:<span style="color: #800080;">1</span><span style="color: #000000;">
restart: on</span>-<span style="color: #000000;">failure
environment:
xpack.security.enabled: </span><span style="color: #800000;">'</span><span style="color: #800000;">false</span><span style="color: #800000;">'</span><span style="color: #000000;">
ports:
</span>- <span style="color: #800080;">5601</span>:<span style="color: #800080;">5601</span><span style="color: #000000;">
redis:
image: redis:alpine
restart: on</span>-<span style="color: #000000;">failure
ports:
</span>- <span style="color: #800080;">6379</span>:<span style="color: #800080;">6379</span><span style="color: #000000;">
volumes:
esdata:
driver: local
appdata:
driver: local
ssldata:
driver: local</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<blockquote>
<p><em><strong>Note:</strong></em>在这个docker-compose.yml中定义了Exceptionless的最小化运行环境,但官方建议生产环境使用ElasticSearch集群,并适当修改ElasticSearch的内存限制。如果你的量很小真的不大,那么这个最小化的运行环境也够用了,没必要为了高可用而高可用。 </p>
</blockquote>
<p> 此外,貌似官方已经将exceptionless/elasticsearch:1这个镜像移除了,大家可以使用这个镜像:<strong>edisonsaonian/exceptionless-elasticsearch:1</strong></p>
<p> (2)将Release包上传到阿里云服务器,然后通过SSH执行一下shell命令(首先cd到这个release包的目录下)启动Exceptionless 5.0.0。</p>
<div class="cnblogs_code">
<pre>docker-compose up -d</pre>
</div>
<p> 整个过程会比较漫长,因为会经过34个Steps,拉取很多镜像,类似于Redis,ElasticSearch,Kibana及.NET Core SDK等等,请耐心等待。最终效果如下所示:</p>
<p> <img style="width: 60%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144146148-1348602911.png" alt=""></p>
<p> <img style="width: 70%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144232365-1751032748.png" alt=""></p>
<h2>2.4 使用Exceptionless 5.0.0</h2>
<p> (1)访问你的服务器IP:5100 即可访问Exceptionless Web管理登录界面,如果你能看到,那么代表部署成功了。注册一个账号,然后登陆吧。</p>
<p> <img style="width: 70%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144336477-464695945.png" alt="" width="738" height="312"></p>
<p> (2)可以看到主页是全新的中文管理界面,感谢贡献者的努力</p>
<p> <img style="width: 70%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144419343-853462229.png" alt="" width="688" height="308"></p>
<p> <img style="width: 50%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144440956-1851854497.png" alt=""></p>
<p> (3)创建一些示例项目,如“XDP.Product.API”,并获取API Key</p>
<p> <img style="width: 50%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144546508-572370284.png" alt=""></p>
<p> (4)在你的ASP.NET Core WebAPI项目中配置API Key,并向Exceptionless API(这里是5000端口)发送Log</p>
<p> (5)在Exceptionless中查看Log</p>
<p> <img style="width: 70%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144628540-1858599463.png" alt="" width="687" height="256"></p>
<p> <img style="width: 70%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144646360-749223515.png" alt="" width="783" height="411"></p>
<h1>三、遗留问题:Email通知配置</h1>
<p> 使用过Exceptionless的童鞋都知道,Exceptionless提供了强大的Email通知机制,可以为用户提供及时的严重错误通知和每日报告。</p>
<p> <img style="width: 50%; border: 1px solid #dddddd; border-radius: 5px;" src="https://img2018.cnblogs.com/blog/381412/201909/381412-20190930144751848-1328868168.png" alt="" width="531" height="411"></p>
<p> 在上面介绍的安装基础上,根据官方Wiki文档,按理说我们只需要确保docker-compose.yml中的api和jobs的AppMode为Production模式并设置SMTP就可以开启Email通知。</p>
<div id="7338-1569824950029" class="block-view code-view yne-code-theme-default" data-language="javascript" data-theme="default">
<div class="para-text">
<div class="cnblogs_code">
<pre><span style="color: #000000;">EX_AppMode: Production
EX_ConnectionStrings__Email: smtps:</span><span style="color: #008000;">//</span><span style="color: #008000;">edisonchou7%40qq.com:zltqvl2321ed@smtp.qq.com:465</span></pre>
</div>
</div>
</div>
<div> 这里的%40是@的转义替代,在Exceptionless中会使用Decode进行解码为@,原因好像是因为它是通过@符号分割前方的用户名+密码和后方的Host+Port,也是醉了。</div>
<div> 设置完成后,通过以下命令重启docker:</div>
<div id="8469-1569825103131" class="block-view code-view yne-code-theme-default" data-language="javascript" data-theme="default">
<div class="para-text">
<div class="cnblogs_code">
<pre>docker-compose up -d</pre>
</div>
</div>
</div>
<div> docker-compose会自动帮我们重启更改过的容器,比如api和jobs。</div>
<div> 但是,我试了很多次都发现还是无法正常发送Email通知,由于不影响使用,也就暂时没去深究了,有解决的朋友可以告知并<strong>分享一下解决办法</strong>,谢谢。</div>
<h1>四、小结</h1>
<p> 本文介绍了Exceptionless 5.0.0的容器化本地部署,主要参考自Exceptionless的Self-Host文档。</p>
<h1>参考资料</h1>
<p>1、<a href="https://github.com/exceptionless/Exceptionless/releases" target="_blank">Exceptionless release</a></p>
<p>2、<a href="https://github.com/exceptionless/Exceptionless/wiki/Self-Hosting" target="_blank">Exceptionless Self-Hosting Documention</a></p>
<p> </p>
<div id="Copyright">