<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>BOHC!</title>
  
  <subtitle>blog of hechao!</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://hewanyue.com/"/>
  <updated>2020-03-30T14:37:40.884Z</updated>
  <id>https://hewanyue.com/</id>
  
  <author>
    <name>Hechao</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>十分全面的传奇手游搭建教程</title>
    <link href="https://hewanyue.com/blog/bb44dec3.html"/>
    <id>https://hewanyue.com/blog/bb44dec3.html</id>
    <published>2020-03-19T20:04:24.000Z</published>
    <updated>2020-03-30T14:37:40.884Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>&emsp;&emsp;自打自己开始弄手游之后，确实遇到了各种各样的问题。各种坑居多，不过也都算顺利，借助搜索引擎以及各大论坛，差不多都算解决了，还有很多bug，倒也不是短时间内能修复的，而且也无关痛痒，也就懒得再深究了。<br>&emsp;&emsp;在遇到问题通往解决问题的过程里，真的是痛并快乐着，被问题卡住，真的是寝食难安啊，弄明白原理，自己独立解决之后，感觉也真的很好，可很快就会遇到新的问题，这种感觉。。。真的一言难尽。<br>&emsp;&emsp;其实网上关于游戏搭建的资料和解决方案很多，不过论坛上大多都收费，而加各种群请教了很多大佬，要么左顾言他，要么有偿价格不菲，这简直触碰我这白嫖党的底线了~知识不应该是人类共享的么！！！<br>&emsp;&emsp;好了，不扯淡了，凭记忆分享些部分我遇到的问题以及解决方案吧，欢迎讨论补充。</p><h1 id="幽冥传奇"><a href="#幽冥传奇" class="headerlink" title="幽冥传奇"></a>幽冥传奇</h1><p>&emsp;&emsp;手游幽冥传奇目前也是比较火热的，不过里面坑也不算少，需要修复的已知问题有如下问题：</p><ol><li><del>一键双区版的2区没法启动</del> </li><li><del>通天塔每5层闪退</del> </li><li><del>一直提示飘屏广告</del> </li><li><del>英雄技能显示不正确（如战士英雄半月弯刀技能）</del> </li><li><del>很多药水不能使用</del></li><li><del>最低阶装备也全屏通报</del>  </li><li>英雄战力只显示为物攻、魔攻或者道术</li><li>装备合成只能到宙级</li><li>装备一键回收只能到18阶</li><li>（未完待续）</li></ol><p>&emsp;&emsp; 其中修复了前 6 个，还有些问题在陆续更改。先说下这些吧</p><h2 id="一键双区版的2区没法启动"><a href="#一键双区版的2区没法启动" class="headerlink" title="一键双区版的2区没法启动"></a>一键双区版的2区没法启动</h2><p>&emsp;&emsp;造成这个问题的原因是原文件里的配置文件格式有问题，将1区的<code>LogicServer</code>目录复制过去，修改一下端口及区号就可以完美修复了。</p><h2 id="通天塔每5层闪退"><a href="#通天塔每5层闪退" class="headerlink" title="通天塔每5层闪退"></a>通天塔每5层闪退</h2><p>&emsp;&emsp;这个问题应该是之前人修改的时候，在每层加入了元宝奖励，虽然是好心修改，可这也导致每五层的奖励项由原来的3个变为了4个，于是数据库接收到的数据长度就与程序设定的字段数不一致，导致报错退出。<br>修复方案就是，将每5层的元宝奖励去掉，或者去掉经验或者洗练符什么其他的，保持奖励项仍为3个，即可不再报错。文件修改路径为<code>build_pub\Server\LogicServer\data\config\fuben\SkyTower</code>目录<br><img src="https://img-blog.csdnimg.cn/20200320160113169.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><h2 id="飘屏广告"><a href="#飘屏广告" class="headerlink" title="飘屏广告"></a>飘屏广告</h2><p>&emsp;&emsp;如果还没开区，则可以在数据库里修改，连接数据库，找到cq_gstatic库就可以看到了。进服公告也可以在这里修改。<br>如果已经运行一阵子了，那修改数据库就没有用了，也可以修改生成的本地文件来实现修改。文件路径为<code>build_pub\Server\LogicServer\data\notice.txt</code></p><h2 id="英雄技能显示错误"><a href="#英雄技能显示错误" class="headerlink" title="英雄技能显示错误"></a>英雄技能显示错误</h2><p>&emsp;&emsp;技能或者物品的显示错误，其实都是在客户端上修改的，不过我们直接去修改客户的客户端还是比较麻烦的，而这个幽冥传奇恰好也支持热更，我们可以用热更的方式，将正确的命名文件放进去，就可以显示正确了。<br>方案：将<code>www\dev\data\scripts\config\server\language\Zh-CN\Skill.lua</code>中的错误改正，然后用CRC工具算出新更改文件CRC-32的校验值，在list中写入正确的校验值以及字节大小，在打包为list.zip文件，并将新的list.zip文件大小写入version文件中，然后把版本号调高一些就可以自动更新了。</p><h2 id="药品使用不了"><a href="#药品使用不了" class="headerlink" title="药品使用不了"></a>药品使用不了</h2><p>&emsp;&emsp;这是因为服务端配置出了问题。在服务端中定义了物品的各种属性，一般比较常见的是将物品的type定义错了，例如如果type=102，即为材料，是无法使用的，改为type=103就是药品了，就可以正常吃掉了。有些吃掉没有正确效果的。是因为scriptitem没有定义正确。关于物品的一系列设置都在目录<code>build_pub\Server\LogicServer\data\config\item</code>，详细单个物品设置在子目录<code>ItemClass</code>。物品设置比较复杂，欢迎私下交流，这里不占用篇幅过多介绍了。</p><h2 id="低阶物品掉落通告"><a href="#低阶物品掉落通告" class="headerlink" title="低阶物品掉落通告"></a>低阶物品掉落通告</h2><p>&emsp;&emsp;也是在<code>build_pub\Server\LogicServer\data\config\item\ItemClass</code>中具体单个文件设置，将其中一个参数<code>dropBroadcast = -1</code>修改为<code>dropBroadcast = 0</code>即可，不要忘记结尾<code>,</code>，否则会报错，启动不了区服。</p><h1 id="战神引擎"><a href="#战神引擎" class="headerlink" title="战神引擎"></a>战神引擎</h1><p>&emsp;&emsp;复古传奇手游，差不多战神引擎算是目前做的做好得了，真的是完全的复古，复古到我觉得玩起来有点累。。。不过也正是因为对端游的还原度高，导致比较受追捧，所以端和教程虽然满天飞，但大多收费，流出的一些分享的端，也很多是残端，放出来寻求帮助修复的。所以战神引擎搭建的失败几率真的太高了，问题也五花八门，不尽相同。我这里在服务端是完整版本的前提下，来一一解决一下，为后来人提供一些思路与方案。</p><h2 id="mongodb服务"><a href="#mongodb服务" class="headerlink" title="mongodb服务"></a>mongodb服务</h2><p>&emsp;&emsp;一般教程中，第一步就是安装mongodb数据库（配必备的.NET环境其实可有可无），一般都是三步：</p><ul><li>双击安装包mongodb-win32-x86_64-2008plus-ssl-3.4.9，安装，在双击robomongo-0.9.0-rc10-windows-x86_64安装包，第二个其实是连接工具，不装也不影响我们搭建，安装这个是为了方便小白测试和排错的。</li><li>进入mongodb的安装目录下的bin目录找到mongodb的执行文件，创建mongodb服务。也就是按住shift，右键打开命令行工具，输入命令<code>mongod --config &quot;D:\MongoDB\config\mongo.cfg&quot; --serviceName &quot;MongoDB&quot; --install</code></li><li>第三步就是启动mongodb服务了，<code>net start mongodb</code></li></ul><p>&emsp;&emsp; 这个的前两步一般都没什么问题，不会出现任何报错，不过第三步的时候我就遇到了问题，提示说<code>服务没有响应控制功能</code>。<br>&emsp;&emsp; 我检查了下win下的服务项，路径看下图<br> <img src="https://img-blog.csdnimg.cn/20200319190405772.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br> <img src="https://img-blog.csdnimg.cn/20200319190631318.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp; mongodb程序路径和配置文件路径都是对的，如果不对的话，确实会报这个错误，导致服务起不来。可是我这不是啊，实在没有办法，最后我尝试之际使用命令行通过指定配置文件的方式来启动mongodb服务，尝试了下，果然可以以前台进程的方式来启动服务，命令为<code>mongod --config &quot;D:\MongoDB\config\mongo.cfg&quot;</code>,检查了下默认端口27017，也用robomongo测试了下，是可以连接的。于是这一步算是解决了（可这也为之后，一直进不去游戏留下了隐患，之后再提）。</p><h2 id="m2"><a href="#m2" class="headerlink" title="m2"></a>m2</h2><p>&emsp;&emsp; 所谓m2，其实就是mysql服务。不同的版本启动mysql的方式也不太一样，有的直接用的phpstudy 将apache和mysql一起启动的，这也省去之后做nginx的虚拟主机开多个端口了。有的版本是通过脚本命令行方式启动，如果没有可以尝试自己进到mysqld.exe所在目录，用命令行命令，``mysqld.exe –defaults-file=”D:\m2\mysql\my.ini”前台启动就可以了。如果实在起不来，检查下端口占用情况是否3306端口被占用，或者配置文件my.ini指定的数据库程序路径或者数据库路径不正确。一般来说都不会有太大问题。如果端口被占用，或者启动了其他的数据库，可以将其他数据库的端口改为3307，战神引擎这个端口改成3307之后，DBserver改不了端口，也就链接不了数据库了，所以还是改别的应用的端口吧。密码就是默认密码，我试了那么多版本的端，倒是没有人该多密码，密码都是相同。</p><h2 id="m2-0"><a href="#m2-0" class="headerlink" title="m2.0"></a>m2.0</h2><p>&emsp;&emsp; 这个算是引擎的主程序了之前都是配套数据库罢了，mongodb用来储存账号相关信息，用来验证登录，而游戏人物数据都在mysql中。所以这一步的环节，就是各项服务读取数据库信息后并相互连结,里面东西说复杂也复杂，说简单也很简单。对于初学者来说，真是一步一坑，对于老手来说就没什么难度了。每个目录做什么的，我这里就不再废话了，直接就大家关心的各种问题来做解释吧。</p><h3 id="显示获取版本信息失败，请检查网络连接，是否重试"><a href="#显示获取版本信息失败，请检查网络连接，是否重试" class="headerlink" title="显示获取版本信息失败，请检查网络连接，是否重试"></a>显示获取版本信息失败，请检查网络连接，是否重试</h3><p><img src="https://img-blog.csdnimg.cn/20200320020145807.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;这个错误其实很简单，不过这也说明第一步也没弄对啊。一般来说出现这个问题是因为没有获取到<code>project.manifest</code>文件和<code>version.manifest</code>，原因有很多，检查的话可以用浏览器输入你的IP或者域名后跟上面两个文件名，例如<code>http://127.0.0.1/version.manifest</code>（在服务器上的要填公网的ip或者域名）。</p><ul><li>如果能正常访问，说明是客户端修改IP没有修改对，去看下客户端\assets\res\project.manifest文件的ip是否修改正确。</li><li>如果浏览器也不能访问，则大概率是assets目录位置不对或apache配置文件路径设置有问题，当然也有可能是客户端的project.manifest修改的IP不正确，如果能实现用浏览器访问此文件的得到的内容也为访问路径IP，说明应该配置没问题了。</li><li>如果浏览器能访问，手机客户端还有服务器都配置没问题了，还是显示这个报错，那就检查下手机能否联网，或者此APP是否有权限联网，或者用域名的的检测下DNS解析，再或者局域网搭建的看看是不是一个网段（例如忘连接wifi了或者虚拟机设置的NAT模式网卡）</li></ul><p>&emsp;&emsp; 这个报错很容易解决，以上方案应该囊括了所有情况。</p><h3 id="战神引擎更新进度100-后卡住"><a href="#战神引擎更新进度100-后卡住" class="headerlink" title="战神引擎更新进度100%后卡住"></a>战神引擎更新进度100%后卡住</h3><p>&emsp;&emsp;这个问题也很常见，不过原因倒是比较简单。因为战神引擎的设置，更新用的端口是88，而更新之后获取列表及访问登录是用的8088端口，方便我们更新服务器和游戏服务器分离开来。前者大概率是phpstudy一键的，所以不会出什么问题就可以开始更新。而从客户端的<code>project.manifest</code>获取更新服务器信息更新完之后，会从客户端的mir2.zip及mir264.zip中读取游戏服务器（这三个文件是不是很眼熟~）。而在这里出问题要么就是服务端的8088端口无法访问到，要么就是客户端修改<code>mir2.zip</code>及<code>mir264.zip</code>文件时IP没有填对。测试方法还是通过浏览器访问IP:8088端口（英文输入法的冒号），测试是否正常访问，战胜引擎的默认主页是有一个<code>hello world</code>来测试访问的<br><img src="https://img-blog.csdnimg.cn/20200320031606507.png" alt="在这里插入图片描述"><br>&emsp;&emsp;如果可以正常访问，大概率是客户端那两个压缩包里的IP不正确了。教程里一般都写明了，要修改为16位IP，位数不够用0来凑。但是到底怎么算16位IP，而且到底凑在哪，却都没有提到，造成很多误解和困扰。我这里大概说一下，需要注意的有两个地方:</p><ol><li>IP中的<code>.</code>也算一位的，所以实际上数字要有13个。</li><li>如果IP为111.222.123.123，可能大家都知道，在最后一个123前补0，即可凑齐6位，为111.222.123.0123。不过如果IP为111.22.123.123这种的呢，0补在哪。大家都说法是都可以，问题是我将两个0补在22的位置变为0022后还是连不上，改为111.22.0123.0123之后就可以连上了。所以我推测这个原则是先从后面补齐，每个数字也不要超过4位。也就是说1.1.1.1补齐的话，应该是1.0001.0001.0001。上面俩肯定正确，不过最后的结论个人推测，没遇到过这么短的ip，没有实践过。欢迎大家批评指正。</li></ol><p>&emsp;&emsp;如果端口能访问，客户端也修改正确ip，那就很有很能会遇到下面这个报错——<code>获取开发者区服信息失败</code></p><h3 id="提示获取开发者区服信息失败"><a href="#提示获取开发者区服信息失败" class="headerlink" title="提示获取开发者区服信息失败"></a>提示获取开发者区服信息失败</h3><p><img src="https://img-blog.csdnimg.cn/20200320022325445.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;这个算是战神引擎新手搭建很容易遇到的问题。究其原因，是你没办法访问到<code>D:\mud2.0\logincenter\logincenter_win\config\serverlist.json</code>或者访问到之后无法通过PHP传奇登陆参数去登录。但是造成此问题最大的问题是因为你的服务端是残端或者被人动过手脚。。。<br>&emsp;&emsp;真的，我遇到几个版本的战神引擎绝对是有人故意改了配置，改法不尽相同，但都会报这个错误。下面列举我遇到的恶意改动。</p><h4 id="配置文件被做了手脚"><a href="#配置文件被做了手脚" class="headerlink" title="配置文件被做了手脚"></a>配置文件被做了手脚</h4><p>&emsp;&emsp;因为是提示获取开发者区服信息失败，所以我第一个想到的就是文件路径不对，用户客户端定义获取区服信息是通过访问服务器的/serverlist路径。一般来说想访问此路径获取到到此文件有两种方式，一个是在lua程序中定义。另一个是在nginx里面设置alias。<br>&emsp;&emsp;这两种修改的我都看到过，先说第一种。第一种方式访问区服列表的，nginx只起到一个代理的作用，不指定路径，因为在logincerter_win目录中已经定义好了访问规则,如下图所示，定义了访问/serverlist路径则去访问下<code>application/controllers/serverlist.lua</code>,而文件又定义了serverlist.json的位置，所以就可以正常获取区服表了。</p><p><img src="https://img-blog.csdnimg.cn/20200320024601237.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200320025039796.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;不过我遇到有的版本，估计把这个serverlist.lua文件中的路径设置错误，或者把下面开服表内容删掉。因为不同版本开服表信息并不通用，而且名字要相互间匹配，所以如果没有开服表信息或者是错误的，那就比较难解决了。建议换个端试试吧。<br>&emsp;&emsp;第二种修改nginx的改动就比较好修复了。这种版本的在nginx的配置文件中定义了alias，来实现访问<code>/serverlist路径</code>可以获取到区服表，但是如果这一条alias如果被删掉了，或者故意改错，那就无法访问到正确的文件了。我遇到过一个版本，也是72的，正常来说应该访问<code>/serverlist路径</code>，直接定义到serverlist.json文件本身，可他这个配置文件却是定义到config目录本身，难怪我看nginx日志一直报403，权限拒绝。<br><img src="https://img-blog.csdnimg.cn/20200320030105291.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;所以需要将后面路径补齐，正确的为<code>alias D:\mud2.0\logincenter\logincenter_win\config\serverlist.json;</code>,记得nginx配置文件每行分号结尾。<br>&emsp;&emsp;不过也有版本修复了上面的问题后还是不能正常登录，我倒也没发现到底是哪里被做了手脚。不过这个logincenter_win目录倒是通用的，找一个没被做手脚的，将里面的serverlist.json、serverlist.lua换成自己的，就可以用了，非要修复反而很麻烦，毕竟搞破坏比修好容易太多了。</p><h3 id="战神引擎区服列表为空或不显示"><a href="#战神引擎区服列表为空或不显示" class="headerlink" title="战神引擎区服列表为空或不显示"></a>战神引擎区服列表为空或不显示</h3><p>&emsp;&emsp;最开始显示了测试服务器登录，也输入了要进入的区服，还提示了是否进入”XXXX”，点了确定进去之后，区服列表竟然是空的。这个问题大概率是因为serverlist配置的不对，检查下<code>serverlist.json</code>吧，版本名字都要匹配。<br><img src="https://img-blog.csdnimg.cn/20200320033718292.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;客户端如果用的版本不一致到也有可能出现这种情况。</p><h3 id="战神引擎获取该区补丁失败"><a href="#战神引擎获取该区补丁失败" class="headerlink" title="战神引擎获取该区补丁失败"></a>战神引擎获取该区补丁失败</h3><p>&emsp;&emsp;这个问题就是上面的截图中的那个<code>ConfigName条目</code>里填写的文件获取不到了。如果能到这一步，说明端口连通应该是没问题的，应该有两方面原因</p><ol><li>检查nginx配置文件，有没有如下代码<pre><code>      location /downloadconfig {        alias D:\mud2.0\logincenter\ClientConfig;        allow all;        autoindex on;        autoindex_localtime on;    }</code></pre></li><li>查看<code>D:\mud2.0\logincenter\ClientConfig</code>目录下有没有<code>ConfigName条目</code>后面跟着的对应文件，如果有其他名字的文件，改上面的截图中的那个<code>ConfigName条目</code>就可以了。</li></ol><h3 id="战神引擎选择完区服之后不开门"><a href="#战神引擎选择完区服之后不开门" class="headerlink" title="战神引擎选择完区服之后不开门"></a>战神引擎选择完区服之后不开门</h3><p>&emsp;&emsp;好不容易连接上了服务器，区服信息也显示了，历尽千辛万苦到最后，竟然半天没人开门。这真是气到吐血啊。<br>&emsp;&emsp;出现此问题，肯定是因为mongodb出了问题，连接不上。</p><h4 id="mongodb自身问题"><a href="#mongodb自身问题" class="headerlink" title="mongodb自身问题"></a>mongodb自身问题</h4><p>&emsp;&emsp;上面提到，当时启动mongodb是一直报错，后来用命令方式启动起的端口和服务。这样虽然以前台进程方式启动了mongodb服务，也可以通过测试，甚至连接进去查看数据，可以好像战神引擎不认。。。<br>&emsp;&emsp;其实我mongodb一直无法启动的时候就应该想到，为什么无法用服务方式启动。那是因为云主机默认只有一个C盘，我当时不是纯净系统，上面还跑着其他端，没法直接从C盘压缩卷再创建一个D盘，于是我是通过共享目录在映射网络磁盘的方式，生成了一个D盘（其实还是C盘下的一个目录而已），而这样虽然mongodb通过命令行可以启动，但是等到系统服务方式启动的时候，他就不认这个映射关系了（我用subst命令生成本地磁盘也试了，还是不能访问）。就相当于找不到数据库或者配置文件了。类似linux中很多配置文件中不能存在软链接一个原理吧。<br>&emsp;&emsp;所以解决方案就是通过原路径C盘目录下来指定配置文件（毕竟文件本来就在C盘），而不是D盘目录下的那个路径了。或者直接把mongodb整个数据库和配置文件目录都拷贝到C盘任一目录下，毕竟这个服务只是需要一个端口来连接就可以了，具体数据库在哪无所谓的。<br>&emsp;&emsp;修改之后，以服务的方式启动，战神引擎就可以正确连接mongodb数据库，也就可以往里面写入账号信息了。不开门就是因为连接不上mongodb数据库，无法写入信息。</p><h4 id="mongodb连接端口被人改掉了。"><a href="#mongodb连接端口被人改掉了。" class="headerlink" title="mongodb连接端口被人改掉了。"></a>mongodb连接端口被人改掉了。</h4><p>&emsp;&emsp;如果mongodb本身没有问题，还有就是这一种情况，连接mongodb的端口被人换了。 大家都是用的都是别人设置好的脚本来启动，也不需要检查各种配置文件，而有人把其中配置文件的端口换掉，你自己验证还是很难发现的。<br><img src="https://img-blog.csdnimg.cn/20200320035526402.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;请检查此文件中的端口是否是默认端口27017，我遇到了个还是72家族的1.85版本这个端口竟然写的27099，通过27099端口怎么也不可能连接到mongodb去验证的！卖端就算了，还要逼你掏钱修复买教程，真的太损了。</p><h4 id="其他原因"><a href="#其他原因" class="headerlink" title="其他原因"></a>其他原因</h4><p>&emsp;&emsp;毕竟搞破坏太容易，一人作恶，百人都挡不住啊。还很有可能是其他地方也被改掉了，我目前没遇到而已。欢迎大家修改补充。</p><p>&emsp;&emsp;<strong>如果有用，请帮我点赞~</strong></p><p>&emsp;&emsp;本次就说这么多吧，之后有想起来的再令行补充，不过实在修复不了也不必太执着。。。</p>]]></content>
    
    <summary type="html">
    
      自打自己开始弄手游之后，确实遇到了各种各样的问题。各种坑居多，不过也都算顺利，借助搜索引擎以及各大论坛，差不多都算解决了，还有很多bug，倒也不是短时间内能修复的，而且也无关痛痒，也就懒得再深究了。在遇到问题通往解决问题的过程里，真的是痛并快乐着，被问题卡住，真的是寝食难安啊，弄明白原理，自己独立解决之后，感觉也真的很好，可很快就会遇到新的问题，这种感觉。。。真的一言难尽。其实网上关于游戏搭建的资料和解决方案很多，不过论坛上大多都收费，而加各种群请教了很多大佬，要么左顾言他，要么有偿价格不菲，这简直触碰我这白嫖党的底线了~知识不应该是人类共享的么！！！好了，不扯淡了，凭记忆分享些部分我遇到的问题以及解决方案吧，欢迎讨论补充。
    
    </summary>
    
    
      <category term="故障" scheme="https://hewanyue.com/categories/%E6%95%85%E9%9A%9C/"/>
    
    
      <category term="幽冥传奇" scheme="https://hewanyue.com/tags/%E5%B9%BD%E5%86%A5%E4%BC%A0%E5%A5%87/"/>
    
      <category term="战神引擎" scheme="https://hewanyue.com/tags/%E6%88%98%E7%A5%9E%E5%BC%95%E6%93%8E/"/>
    
      <category term="故障解决" scheme="https://hewanyue.com/tags/%E6%95%85%E9%9A%9C%E8%A7%A3%E5%86%B3/"/>
    
  </entry>
  
  <entry>
    <title>VMware安装macOS(没有符合安装资格的软件包)</title>
    <link href="https://hewanyue.com/blog/2721a75e.html"/>
    <id>https://hewanyue.com/blog/2721a75e.html</id>
    <published>2020-02-26T14:54:35.000Z</published>
    <updated>2020-03-30T14:32:12.614Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;因为之前弄了几个ipa，却无法安装到本机（苹果个人开发者账户要年费，免费的开发者账户好像无法申请了，本人信奉能不花钱就不花钱的原则~），又不是专业ios开发，想测试好像只有把自己的手机给越狱了。不过我突然想到VMware好像也可以安装mac，可以用Xcode试试。<br>&emsp;&emsp;之前只是大致了解过所谓黑苹果。也搜了下大致资料，发现也并不难。可当我装好VMware、也用unlock工具解锁完毕之后，安装系统时，总是提示我“没有符合安装资格的软件包，请联系软件制造商以获得帮助”。<br>&emsp;&emsp;网上唯一出现的解决方案，好像就是说时间不同步，要么用ntp命令同步至苹果服务器，要么就直接date命令修改时间。我当时同步前也检查了下date时间，发现也是正确的，不过抱着死马当活马医的态度，还是照做了，果然如我所料，还是不可以。<br>&emsp;&emsp;又无数遍核对步骤和检查错误，感觉与网上所谓的教程做的没有任何出入的，为什么别人都可以成功，而我却一直卡在这了呢？开始思考，虚拟机的原理是应该可以忽视底层硬件的，肯定不是配置的差异，那问题就在两方面：1、我的VMware有问题；2、我的镜像包确实不符合。<br>&emsp;&emsp;我的VMware也是官网下载的，网上使用VMware15.5、15甚至14都是成功安装了的。unlock工具我也找到了很多版本，对应VMware14的以及对应15的版本，而且我这里创建虚拟机的时候已经可以正常显示Apple Mac OS了的（如下图），说明我的VMware已经解锁成功。那问题就应该出在安装包了。<br><img src="https://img-blog.csdnimg.cn/20200226223513219.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;我看的教程里提供的下载链接是OS X10.11，我也就一直安装的的10.11版本，不过我看到有一个博客中提到 <code>VMware 的版本和 MacOS 的新版本是有关联关系的</code>。我突然想到，可以尝试下别的版本，毕竟已经走投无路了。于是决定去换个10.15的版本也就是Catalina。不过Catalina的安装包真的很难找。怪不得网上大部分教程用的都是10.11。<br>&emsp;&emsp;万幸有大佬提供免费的资源，<a href="https://blog.sxbai.com/174.html" target="_blank" rel="external nofollow noopener noreferrer">原帖地址</a>（里面有资源下载链接），感谢感谢。重新创建虚拟机后，果然成功了（贴下图留作纪念），猜测使用其他Catalina的cdr也是可以成功的。<br><img src="https://img-blog.csdnimg.cn/20200226224636904.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;这个问题算是解决了，看来很多帖子、博客都是有问题的，或者因为年代比较久远失效了，正确的大多没有提到mac资源从哪里来，确实比较费劲。特此记录一下，可以让别人少走弯路。</p>]]></content>
    
    <summary type="html">
    
      之前只是大致了解过所谓黑苹果。也搜了下大致资料，发现也并不难。可当我装好VMware、也用unlock工具解锁完毕之后，安装系统时，总是提示我“没有符合安装资格的软件包，请联系软件制造商以获得帮助”。网上唯一出现的解决方案，好像就是说时间不同步，要么用ntp命令同步至苹果服务器，要么就直接date命令修改时间。我当时同步前也检查了下date时间，发现也是正确的，不过抱着死马当活马医的态度，还是照做了，果然如我所料，还是不可以。
    
    </summary>
    
    
      <category term="故障" scheme="https://hewanyue.com/categories/%E6%95%85%E9%9A%9C/"/>
    
    
      <category term="vmware" scheme="https://hewanyue.com/tags/vmware/"/>
    
      <category term="mac" scheme="https://hewanyue.com/tags/mac/"/>
    
  </entry>
  
  <entry>
    <title>关于游戏服务端的搭建</title>
    <link href="https://hewanyue.com/blog/ea0d2f46.html"/>
    <id>https://hewanyue.com/blog/ea0d2f46.html</id>
    <published>2020-02-18T11:09:02.000Z</published>
    <updated>2020-02-19T16:57:46.662Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;前一阵，一场突如其来的疫情，将我们都困在了家中，于是有了大把的时间，来做一些自己感&gt;兴趣的事情。当然，是打游戏咯。<br>&emsp;&emsp;不过只是打游戏，也太low了，完全不符合我们程序员的气质，我们作为网络世界中的众多缔造<br>者之一，仅仅扮演一个渺小的角色，未免太过无趣。于是，我想到自己搭建服务器给自己玩，做自己的神~哈<br>，开玩笑的，其实是为了更好的了解那些手游端游的运作机制啦，就搭建了几款不同的游戏，也算研究研究 常用架构吧。</p><a id="more"></a><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="准备游戏服务架构"><a href="#准备游戏服务架构" class="headerlink" title="准备游戏服务架构"></a>准备游戏服务架构</h3><p>&emsp;&emsp;这些天，我下载了很多游戏的服务架构，页游、手游、端游都有。看到里面的配置和架构也都五花八门。总的来说，一般页游大多是AMP+JAVA，手游遇到很多， JAVA+MongoDB，页游AMP+Erlang+RabbitMQ的组合。端游就更加五花八门了，很多引擎或者自制脚本，使用的数据库种类也很多，MongoDB、mysql、SQL Server等等，不同游戏的架构选择都不同。<br>&emsp;&emsp;因为我们自己肯定没法写出来一个完整的游戏服务包，所以最理想的也是最简便的方法就是使用别人已经写好的现成的服务包，安装配置即可（里面坑巨多，免费分享出来的，完整性和质量就没法苛求了），大部分修复下或者调整下也是能够凑活跑起来的。<br>&emsp;&emsp;不过倒也遇到很多问题，游戏中有很多bug，例如这个任务流程过不去，那个点了没反应，只能边玩边吐槽；还有个手游架设好后，一直没有报错，就一直没管他，运行3天后，有小伙伴说登录不上，才发现服务器数据库崩掉连接不上了，由于搭建好后就没有关注监控这个服务器，甚至不知道是啥时候崩掉的，也就不知道为什么会崩，重新搭建连接完数据库，之前玩的游戏数据也就清空没有（没有设置实时备份数据库），小伙伴们很是扫兴，还好不是实际生产中，这我也很无奈啊。<br>&emsp;&emsp;但总体下来，大家反应都是很不错的，毕竟道具无限，人人都是大佬，也算无聊的假期大家一起有共同消遣了。</p><h3 id="网络环境"><a href="#网络环境" class="headerlink" title="网络环境"></a>网络环境</h3><p>&emsp;&emsp;因为要自己开服务器，如果只是在本地电脑上配置，那其他小伙伴们就没法连接进来，自己游戏里再NP也没人分享可太悲哀了。所以，我们一定要连上外网，需要公网IP，才可以供他人访问。<br>&emsp;&emsp;博主有一个aws的海外云主机，还有一个腾讯云的国内云主机，虽然直接在云主机上搭建可以直接解决公网IP的问题，不过这俩云主机配置都不高，都是1C1G，担心无法完美支撑服务跑起来（花钱升级配置是不可能的~）。于是初步决定在本地先将服务跑起来，用 vpn 打通本地电脑和云主机的网络，配置云主机的反向代理，实现游戏服务器公网搭建。做出规划步骤如下</p><ul><li><input checked disabled type="checkbox"> 本地搭建游戏服务端</li><li><input checked disabled type="checkbox"> 用 vpn 打通本地主机和云主机的网络</li><li><input checked disabled type="checkbox"> 配置云主机的代理转发</li><li><input checked disabled type="checkbox"> 修改本地主机的游戏服务监听端口</li><li><input checked disabled type="checkbox"> 配置客户端</li></ul><h2 id="正式搭建"><a href="#正式搭建" class="headerlink" title="正式搭建"></a>正式搭建</h2><h3 id="本地搭建游戏服务端"><a href="#本地搭建游戏服务端" class="headerlink" title="本地搭建游戏服务端"></a>本地搭建游戏服务端</h3><p>&emsp;&emsp;因为我搭建了很多不同类型的游戏，且每一种其实方法步骤都不一样，需要的配置和环境也都不同，遇到的问题也不尽相同，在这里就没法一一细说了，会在文末将我搭建成功的那些服务包都贴出来，有需要的小伙伴可以自行研究。</p><h3 id="打通网络"><a href="#打通网络" class="headerlink" title="打通网络"></a>打通网络</h3><p>&emsp;&emsp;可以参考我之前博客<a href="https://hewanyue.com/blog/2c6b894f.html">https://hewanyue.com/blog/2c6b894f.html</a>架设 vpn 。协议可以使用UDP，之前我用TCP总是被封掉。搭建好，建立连接后，其实就已经算是打通网络了。<br>&emsp;&emsp;在window cmd命令行或者linux的终端里ping 10.8.0.1 就可以ping通云主机了的。然后也可以尝试用云主机验证 访问本地10.8.0.6上面的http服务之类的。</p><h3 id="配置云主机反向代理"><a href="#配置云主机反向代理" class="headerlink" title="配置云主机反向代理"></a>配置云主机反向代理</h3><p>&emsp;&emsp;虽然这两台机子间是联通了，但是别人访问你的云主机的IP，并不能连接到你本地的服务器主机，所以我们需要在防火墙上配置转发。<br>&emsp;&emsp;例如本地服务器开启的端口为 12345，而我们想让别人访问云主机的 54321端口就可以登录我们的游戏服务，需要填写dnat 还有地址伪装。代码如下：</p><pre><code>iptables -t nat -D PREROUTING -d 0.0.0.0 -p tcp --dport 54321 -j DNAT --to-destination 10.8.0.6:12345</code></pre><p>&emsp;&emsp;如果客户端设置的链接协议是UDP，那就将tcp改为UDP，若是不知道客户端程序写的链接方式到底是什么，可以写两条将这俩都转发了。不过如果不写协议会报错的。</p><pre><code>[root@ip-172-31-39-115 ~]# iptables -t nat -D PREROUTING -d 0.0.0.0 --dport 54321 -j DNAT --to-destination 10.8.0.6:12345iptables v1.8.2 (nf_tables): unknown option &quot;--dport&quot;Try `iptables -h&#39; or &#39;iptables --help&#39; for more information.</code></pre><p>&emsp;&emsp;如果不想改不同的端口，倒也可以简单粗暴的，将访问云主机的所有数据统统转发到本地，但这样的话，云主机的其他功能就都会受到影响，不建议这样（云主机上没有其他服务倒也可以这样）。<br>&emsp;&emsp;还有记得防火墙开启转发功能，否则可能服务器的内网网卡收不到数据报文。</p><pre><code>echo &quot;net.ipv4.ip_forward = 1&quot; &gt;&gt; /etc/sysctl.confsysctl -p</code></pre><p>&emsp;&emsp;有的游戏，本地服务器还配有网站等等例如gm简易工具，需要我们在外网也能访问。可以设置nginx或者apache的反向代理转发，比较简单，这里就不细说了。</p><h3 id="修改本地主机的游戏服务监听IP端口"><a href="#修改本地主机的游戏服务监听IP端口" class="headerlink" title="修改本地主机的游戏服务监听IP端口"></a>修改本地主机的游戏服务监听IP端口</h3><p>&emsp;&emsp;出于安全考虑，很多游戏服务配置的时候，相互之间都是，配置了访问权限的，我下载的大部分游戏都是监听在本地127.0.0.1回环网卡的，如果我们不修改监听地址，从我们构建来的专线的访问数据是没有权限访问我们的服务的，所以我们需要将监听地址修改为10.8.0.6专线网卡的ip上。或者改为0.0.0.0（不建议，有可能端口冲突，造成服务起不来或者报错）。<br>&emsp;&emsp;一般php或者数据库那些地址就不用改了，因为还是从本地读取的。到时候捋一捋就可以确保正确连接了。</p><h3 id="配置客户端"><a href="#配置客户端" class="headerlink" title="配置客户端"></a>配置客户端</h3><p>&emsp;&emsp;因为一般需要连接的服务器IP端口，都是写死在客户端中的，我们修改了服务器的地址，所以还要在客户端中修改为正确的ip地址端口，才可以链接（页游不用，页游没有客户端）。<br>&emsp;&emsp;不同游戏修改方式和路径都不相同，安卓端可以用APKIDE，苹果ipa的可以直接将后缀改为rar，解压后修改对应ip，这里就不细说了。欢迎就具体游戏来讨论。</p><h2 id="附本人搭建测试成功的游戏及网盘分享"><a href="#附本人搭建测试成功的游戏及网盘分享" class="headerlink" title="附本人搭建测试成功的游戏及网盘分享"></a>附本人搭建测试成功的游戏及网盘分享</h2><ul><li>手游剑侠情缘<br>游戏预览：（随便拍的，手边没有安卓机，用的电脑模拟器）</li></ul><iframe height="498" width="510" src="https://player.youku.com/embed/XNDU1MTQyNjcwMA==" frameborder="0" 'allowfullscreen'></iframe><p>安装vmware的centos虚拟机，里面是java程序。<br>&emsp;&emsp;下载地址<br>&emsp;&emsp;剑侠情缘VM一键端<br>链接：<a href="https://pan.baidu.com/s/1D0qOO7XcK2K93BLXCsw-fA" target="_blank" rel="external nofollow noopener noreferrer">https://pan.baidu.com/s/1D0qOO7XcK2K93BLXCsw-fA</a><br>提取码：4371 </p><ul><li><p>手游蓝月传奇<br>游戏预览：<br><img src="https://img-blog.csdnimg.cn/20200218181419653.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200218181431190.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200218181446556.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;这个是在windows环境下运行的，用到PHP、nodejs、mysql、Erlang+rabbitMQ<br>&emsp;&emsp;下载地址<br>&emsp;&emsp;【蓝月传奇】一键端+修改教程+全功能GM网页后台+外网教程<br>链接：<a href="https://pan.baidu.com/s/1wjfmMWaTf2YxZ0rxjCtknw" target="_blank" rel="external nofollow noopener noreferrer">https://pan.baidu.com/s/1wjfmMWaTf2YxZ0rxjCtknw</a><br>提取码：6c8m </p></li><li><p>手游七雄争霸<br>&emsp;&emsp;JAVA游戏，windows环境搭建，用到了memcached、nodejs、PHP<br>&emsp;&emsp;下载地址<br>链接：<a href="https://pan.baidu.com/s/1o-pZb4TWKZWf3vmGk70bfQ" target="_blank" rel="external nofollow noopener noreferrer">https://pan.baidu.com/s/1o-pZb4TWKZWf3vmGk70bfQ</a><br>提取码：78ws</p></li><li><p>手游 幽冥传奇<br>&emsp;&emsp;Windows环境运行，用的战神引擎。<br><img src="https://img-blog.csdnimg.cn/20200219210244785.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200219210259181.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200219210310788.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200219210324740.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200219210334737.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200219210344567.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200219210355741.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;下载地址<br>链接：<a href="https://pan.baidu.com/s/1mLgic84xPkBhkrOiJqx4ZQ" target="_blank" rel="external nofollow noopener noreferrer">https://pan.baidu.com/s/1mLgic84xPkBhkrOiJqx4ZQ</a><br>提取码：ja6l </p></li><li><p>页游 传奇世界<br>&emsp;&emsp;H5游戏，也还不错。<br>&emsp;&emsp;下载地址<br>链接：<a href="https://pan.baidu.com/s/1Zv1vMoD9l1cegh3FRghaXA" target="_blank" rel="external nofollow noopener noreferrer">https://pan.baidu.com/s/1Zv1vMoD9l1cegh3FRghaXA</a><br>提取码：7057</p></li><li><p>页游 赤月传说<br>&emsp;&emsp;下载地址<br>链接：<a href="https://pan.baidu.com/s/1S1RW6JAmR1Xx063XzoJTOA" target="_blank" rel="external nofollow noopener noreferrer">https://pan.baidu.com/s/1S1RW6JAmR1Xx063XzoJTOA</a><br>提取码：i168</p></li><li><p>端游 无极联盟传奇<br>&emsp;&emsp;Windows环境运行，用的GOM引擎。<br>&emsp;&emsp;这个虽然特效还有模式都还不错，不过是个残端，里面没有提供pak密码，不建议安装。<br>&emsp;&emsp;下载地址<br>链接：<a href="https://pan.baidu.com/s/13OOZxWbESveJLXmeS-w1Bw" target="_blank" rel="external nofollow noopener noreferrer">https://pan.baidu.com/s/13OOZxWbESveJLXmeS-w1Bw</a><br>提取码：7nom</p></li><li><p>端游 三国战纪OL（第二季）<br>&emsp;&emsp;Windows环境运行，用的GOM引擎。这个可以完美开服，还算不错。<br>&emsp;&emsp;下载地址<br>链接：<a href="https://pan.baidu.com/s/12x4Kvb-_Pipn6Qs73JkBPw" target="_blank" rel="external nofollow noopener noreferrer">https://pan.baidu.com/s/12x4Kvb-_Pipn6Qs73JkBPw</a><br>提取码：xb6r </p></li></ul><p>&emsp;&emsp;很多游戏搭好之后，玩了也还不错，不过忘记保留截图了。还有一些还没上传，等日后再分享。欢迎交流讨论。</p>]]></content>
    
    <summary type="html">
    
      前一阵，一场突如其来的疫情，将我们都困在了家中，于是有了大把的时间，来做一些自己感兴趣的事情。当然，是打游戏咯。不过只是打游戏，也太low了，完全不符合我们程序员的气质，我们作为网络世界中的众多缔造者之一，仅仅扮演一个渺小的角色，未免太过无趣。于是，我想到自己搭建服务器给自己玩，做自己的神~哈，开玩笑的，其实是为了更好的了解那些手游端游的运作机制啦，就搭建了几款不同的游戏，也算研究研究 常用架构吧。
    
    </summary>
    
    
      <category term="生活" scheme="https://hewanyue.com/categories/%E7%94%9F%E6%B4%BB/"/>
    
    
      <category term="服务端" scheme="https://hewanyue.com/tags/%E6%9C%8D%E5%8A%A1%E7%AB%AF/"/>
    
      <category term="游戏搭建" scheme="https://hewanyue.com/tags/%E6%B8%B8%E6%88%8F%E6%90%AD%E5%BB%BA/"/>
    
      <category term="资源分享" scheme="https://hewanyue.com/tags/%E8%B5%84%E6%BA%90%E5%88%86%E4%BA%AB/"/>
    
  </entry>
  
  <entry>
    <title>linux使用vmware虚拟机玩LOL</title>
    <link href="https://hewanyue.com/blog/c1add9f7.html"/>
    <id>https://hewanyue.com/blog/c1add9f7.html</id>
    <published>2020-01-26T15:16:49.000Z</published>
    <updated>2020-01-26T15:47:12.262Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;自从入坑linux系统，便越陷越深，操作起来确实方便很多。不过linux系统有一个致命的问题，那就是没法玩游戏！</p><a id="more"></a><p>&emsp;&emsp;平常也有罢了，不玩也就算了。不过这一阵子，恰逢春节，却又赶上疫情不能出门，几乎排行榜所有的电影都扫遍了，无可奈何又想起了早就删除了的英雄联盟。<br>&emsp;&emsp;但是现在系统已经是Deepin系统了。用起来很简洁，也比较方便，桌面也很漂亮，给大家看看桌面哈～<br><img src="https://img-blog.csdnimg.cn/20200126220032514.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><h2 id="vmware"><a href="#vmware" class="headerlink" title="vmware"></a>vmware</h2><p>&emsp;&emsp;言归正传，我们需要安装一个window的虚拟机。因为本地已有vmware，所以直接用vmware创建。<br>&emsp;&emsp;没有安装vmware的去官网下载一个linux版本的，网上找一个个激化码就可以正常使用了。链接为<code>https://download3.vmware.com/software/wkst/file/VMware-Workstation-Full-15.5.1-15018445.x86_64.bundle</code>。可以用迅雷或者谷歌自带下载工具下载，也可以用命令行<code>wget</code>命令下载。<br>&emsp;&emsp;这是一个可执行程序文件，在命令行中对此文件，加执行权限，执行</p><pre><code>./VMware-Workstation-Full-15.5.1-15018445.x86_64.bundle</code></pre><p>&emsp;&emsp;安装完毕VMware后，找到window的iso镜像包（要是没有，提前去下载一个）正常安装window系统即可。至少分配4个G内存，开启3D选项（一般是默认开启的）。</p><h2 id="可能遇到的问题"><a href="#可能遇到的问题" class="headerlink" title="可能遇到的问题"></a>可能遇到的问题</h2><h3 id="3D支持"><a href="#3D支持" class="headerlink" title="3D支持"></a>3D支持</h3><p>&emsp;&emsp;开机之后很有可能会vmware的又下角会报错，提示3D不可用<br><img src="https://img-blog.csdnimg.cn/20200126223536120.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;需要我们进入虚拟机的安装目录，找到vmx文件，<code>vim</code>打开编辑，在最后加一行<code>mks.gl.allowBlacklistedDrivers = &quot;TRUE&quot;</code>来强制开启3D加速。再开启虚拟机，就不会报<code>No 3D support is available from host</code>了。如果报<code>mks.gl.allowBlacklistedDrivers</code>之类的错误，可能是引号格式不正确，重新检查修改一下引号就可以了。</p><h3 id="game-error-directx"><a href="#game-error-directx" class="headerlink" title="game_error_directx"></a>game_error_directx</h3><p>&emsp;&emsp;当我下载好wegame，安装好英雄联盟客户端，也能正常组队及开始游戏时，载入进度条到100%后，意外给我弹一个这个报错。<br><img src="https://img-blog.csdnimg.cn/20200126224807765.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;然后游戏也无法重连了，一直报错<code>game_error_directx</code>。<br><img src="https://img-blog.csdnimg.cn/20200126224914381.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;这上来就坑队友了啊，我也很无奈。赶紧用directx修复工具修复一下directx，果然可以进入游戏了。（首先确保第一步的3D支持已开启，否则单独用修复工具修复是无效的）</p><h3 id="卡顿"><a href="#卡顿" class="headerlink" title="卡顿"></a>卡顿</h3><p>&emsp;&emsp;进入游戏后，我发现虽然可以流畅游戏，也没有延迟，问题是每隔几分钟就会有个10秒钟左右的画面卡顿，甚至掉线，然后屏幕就黑了。。。这谁顶得住啊。只能停下，赶紧排查原因。<br>&emsp;&emsp;开始以为是网络卡顿，不过检查了下，发现虚拟机的网络一直没有掉，于是猜测是虚拟机的CPU不响应，于是检测了下CPU的使用率，发现果然在卡顿的几秒钟内，宿主机的CPU使用率很高，难道是有进程占用了CPU，导致虚拟机的程序无法得到响应？<br>&emsp;&emsp;于是查找CPU优化，初步拟定方向是，将CPU核心绑定至虚拟机进程，修改虚拟机优先级等等。不过linux版的VMware没法设置优先级，并不像window版本的可以设置优先级。<br><img src="https://img-blog.csdnimg.cn/20200126230139406.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;于是，可优化选项，只有设置预留内存，以及不使用交换分区了。这时我注意到，默认是使用交换分区的，可能导致我不时卡顿的元凶就是这里了。不过无法直接修改内存选项，提示<code>You must be running Workstation as root to change these preferences.</code>。<br>&emsp;&emsp;于是，只能从命令行切换root用户权限执行vmware，修改后（将默认的Allow some virtual machine memory to be swapped改为第一个），在用普通用户启动vmware，发现果然是继续生效的。<img src="https://img-blog.csdnimg.cn/20200126230657876.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;再次启动虚拟机，进入游戏之后，果然再也没有出现卡顿了。</p><h3 id="关闭工具条"><a href="#关闭工具条" class="headerlink" title="关闭工具条"></a>关闭工具条</h3><p>&emsp;&emsp;在虚拟机中全屏之后，上面还隐藏一个白色的工具条，打游戏偶尔会点到就会切出去，很影响游戏体验。于是我们要想办法将他去掉。<br>&emsp;&emsp;在Edit，Preferences选项下的Display标签中，将全屏显示选项栏这个选项勾选取消。<br><img src="https://img-blog.csdnimg.cn/20200126231230154.png" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20200126231141489.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;现在就可以愉快的进行游戏了，你们能看出来我是在虚拟机中游戏吗<br><img src="https://img-blog.csdnimg.cn/2020012623140020.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>]]></content>
    
    <summary type="html">
    
      自从入坑linux系统，便越陷越深，操作起来确实方便很多。不过linux系统有一个致命的问题，那就是没法玩游戏！
    
    </summary>
    
    
      <category term="故障" scheme="https://hewanyue.com/categories/%E6%95%85%E9%9A%9C/"/>
    
    
      <category term="linux" scheme="https://hewanyue.com/tags/linux/"/>
    
      <category term="vmware" scheme="https://hewanyue.com/tags/vmware/"/>
    
      <category term="LOL" scheme="https://hewanyue.com/tags/LOL/"/>
    
  </entry>
  
  <entry>
    <title>K8s进阶——java集群服务搭建</title>
    <link href="https://hewanyue.com/blog/a9b6fadd.html"/>
    <id>https://hewanyue.com/blog/a9b6fadd.html</id>
    <published>2020-01-14T13:36:56.000Z</published>
    <updated>2020-01-20T07:29:36.332Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;K8s集群搭建完成后，真正完成我们业务的是那些跑在k8s上的pod们。将业务跑在k8s集群只上，我们可以实现根据负载或者资源利用率动态扩容或者缩容我们的后端服务器，更加灵活高效的利用我们的物理设备，且能够实现服务的高可用及故障自治愈，本文将详细介绍以上的具体实现。</p><a id="more"></a><h2 id="实验环境"><a href="#实验环境" class="headerlink" title="实验环境"></a>实验环境</h2><p>&emsp;&emsp;本次演示使用主机系统均为ubuntu1804。</p><table><thead><tr><th align="center">节点</th><th align="center">IP</th></tr></thead><tbody><tr><td align="center">master节点</td><td align="center">192.168.32.18、192.168.32.19</td></tr><tr><td align="center">node节点</td><td align="center">192.168.32.21、192.168.32.22</td></tr><tr><td align="center">etcd</td><td align="center">192.168.32.23、192.168.32.24、192.168.32.25</td></tr><tr><td align="center">harbor</td><td align="center">192.168.32.20</td></tr><tr><td align="center">NFS服务器</td><td align="center">192.168.32.20（复用）</td></tr></tbody></table><p>&emsp;&emsp;如果机器不够，可以选择复用，例如将etcd的三台主机与master主机和harbor主机复用，可以省下来3台主机。推荐node节点的性能高一些，因为一般之后的pod都将运行在node节点上，如果node节点的资源不足，则很有可能无法创建pod，导致服务启动或者扩容失败。<br>&emsp;&emsp;具体K8s集群的搭建，可以参考我之前文章<a href="https://hewanyue.com/blog/8f374cb8.html">使用kubeasz自动化部署K8s</a>。harbor服务器的搭建和部署配置，也可以参考我之前文章<a href="https://hewanyue.com/blog/6187894.html">Docker（五）——Docker镜像仓库</a>。<br>&emsp;&emsp;因为是均为内网环境，建议将网络组件calico的IPIP模式（ip-in-ip叠加模式）关掉，使用calico的BGP模式，以节约大量主机内部访问时封装的性能损耗。具体操作可以参考之前博客<a href="https://hewanyue.com/blog/8f374cb8.html">使用kubeasz自动化部署K8s</a>。</p><h2 id="设计架构"><a href="#设计架构" class="headerlink" title="设计架构"></a>设计架构</h2><p>&emsp;&emsp;我们设计一个简单架构为例，如下图所示。<br><img src="https://img-blog.csdnimg.cn/2020011414135532.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="k8s架构-java服务集群"><br>&emsp;&emsp;一般负载层是使用物理机上的haproxy服务来实现4层负载，而不是在K8s集群内部pod 实现。我们这里也就将他省略不再细说。<br>所以我们需要创建一个nginx集群，以及一个tomcat集群。将动态资源，转发给后端的tomcat服务，前端的静态页面由nginx来处理。为了方便我们对后端服务的修改以及部署，我们还需要一个NFS服务器，来共享静态资源以及动态资源，以实现后端服务数据的实时同步。</p><h2 id="搭建业务镜像架构"><a href="#搭建业务镜像架构" class="headerlink" title="搭建业务镜像架构"></a>搭建业务镜像架构</h2><p>&emsp;&emsp;镜像一般是按层次一级一级实现的，可实现多个业务复用。所以，例如我们将要实现的业务为app1，则我们需要的的镜像，就有对应的app1-nginx及app1-tomcat的业务镜像，以及nginx及tomcat的基础镜像，以及基础系统镜像，我们要一层层的来制作镜像。详细可以参考之前文章<a href="https://hewanyue.com/blog/ddd838e7.html">Docker（三）——镜像制作</a>。</p><pre><code>cd /optmkdir -p k8s/{app1,dockerfile,yaml}cd k8s/dockerfilemkdir {app1,pub-image,system}</code></pre><p>&emsp;&emsp;最终结构如下图所示。</p><pre><code>root@DockerUbuntu18:/opt/k8s/dockerfile# tree -d.├── app1│   ├── app1-nginx│   └── app1-tomcat├── pub-images│   ├── jdk│   │   └── 8│   ├── nginx-base│   └── tomcat-base└── system    └── centos</code></pre><p>&emsp;&emsp;打好的镜像如下所示。</p><pre><code>root@DockerUbuntu18:/opt/k8s/dockerfile# docker imagesREPOSITORY                                          TAG                 IMAGE ID            CREATED             SIZEharbor.micepro.net/base/app1-tomcat                  v1                  c7d87d05f8a4        3 days ago          1.19GBharbor.micepro.net/base/tomcat-base                  v8.5.47             12cf3e12f2d0        3 days ago          1.19GBharbor.micepro.net/base/jdk-base                     v8.212              1582b1e9cfe4        3 days ago          1.16GBharbor.micepro.net/base/app1-nginx                   v1                  4836f9aed79b        3 days ago          940MBharbor.micepro.net/base/nginx-base                   v1.16.1             bfc05a5c23ed        5 days ago          940MBharbor.micepro.net/base/centos-base                  v7.6                bc7e922a8352        5 days ago          753MBharbor.micepro.net/base/centos                       base                f1cb7c7d58b7        10 months ago       202MB</code></pre><h2 id="配置NFS服务器"><a href="#配置NFS服务器" class="headerlink" title="配置NFS服务器"></a>配置NFS服务器</h2><p>&emsp;&emsp;我们计划将动态资源和静态资源都放置至我们的NFS共享存储上，也就是我们的harbor上（任意主机，端口不冲突就可以~）<br>&emsp;&emsp;可以通过包管理工具快速安装一个NFS服务，毕竟这只是一个辅助工具，无需过多关注。</p><pre><code>apt install -y nfs-servermkdir /data/app1/{images,static,ROOT} -pecho &quot;/data/app1 *(rw,no_root_squash)&quot; &gt;&gt; /etc/exportssystemctl enable --now nfs-server</code></pre><p>&emsp;&emsp;将<code>/data/app1</code>目录整个共享出去即可，之后我们可以直接将NFS挂载至pod中，也可以通过PV/PVC的方式挂载到pod中。<br>&emsp;&emsp;我们需要修改权限，通过指定uid的方式，使某个UID在多个主机之间都具有权限。这就要求我们之前在创建基础镜像时，也使用的是相同的UID启动程序。例如我们统一都使用<code>www</code>用户，指定UID为2020。（之前创建的容器以及基础镜像中都要修改服务以UID为2020的www用户启动）</p><h2 id="编写yaml文件"><a href="#编写yaml文件" class="headerlink" title="编写yaml文件"></a>编写yaml文件</h2><p>&emsp;&emsp;此时我们就可以开始着手准备yaml文件，来规划我们的服务pod了。</p><h3 id="namespace"><a href="#namespace" class="headerlink" title="namespace"></a>namespace</h3><p>&emsp;&emsp;首先我们需要新创建一个namespace，来实现与其他服务隔离。</p><pre><code>cd /opt/k8s/mkdir -p yaml/namespace</code></pre><pre><code>vim yaml/namespace/app1.yamlapiVersion: v1 #API版本kind: Namespace #类型为namespacmetadata: #定义元数据  name: app1 #namespace名称</code></pre><p>&emsp;&emsp;然后将这个namespace创建出来，使用命令</p><pre><code>kubectl apply -f /opt/k8s/yaml/namespace/app1.yaml</code></pre><p>&emsp;&emsp;此时使用命令<code>kubectl get ns</code>可以看到我们新创建的namespace</p><pre><code>root@DockerUbuntu18:/opt/k8s# kubectl get nsNAME              STATUS   AGEapp1              Active   20sdefault           Active   3dkube-node-lease   Active   3dkube-public       Active   3dkube-system       Active   3d</code></pre><h3 id="pod"><a href="#pod" class="headerlink" title="pod"></a>pod</h3><p>&emsp;&emsp;先创建好对应的目录，方便后期管理维护。</p><pre><code>cd /opt/k8smkdir -p app1/{nginx,tomcat}</code></pre><p>&emsp;&emsp;然后我们就可以编写pod的yaml文件了。</p><pre><code>cd /opt/k8s/app1/nginx</code></pre><p>&emsp;&emsp;可以将<code>Deployment</code>和<code>server</code>写在一起，也可以分开写。我们这直接都写在一起。</p><pre><code>vim nginx.yamlkind: DeploymentapiVersion: extensions/v1beta1metadata:  labels:    app: app1-nginx-deployment-label  name: app1-nginx-deployment  namespace: app1spec:  replicas: 2  #设置nginx集群数量  selector:    matchLabels:      app: app1-nginx-selector  template:    metadata:      labels:        app: app1-nginx-selector    spec:      containers:      - name: app1-nginx-container        image: harbor.micepro.net/base/app1-nginx:v1        #command: [&quot;/apps/tomcat/bin/run_tomcat.sh&quot;]        imagePullPolicy: IfNotPresent        #imagePullPolicy: Always        ports:        - containerPort: 80          protocol: TCP          name: http        - containerPort: 443          protocol: TCP          name: https        env:        - name: &quot;password&quot;          value: &quot;123456&quot;        - name: &quot;age&quot;          value: &quot;18&quot;        resources:          limits:            cpu: 2            memory: 2Gi          requests:            cpu: 500m#            memory: 1Gi            memory: 200m        volumeMounts:        - name: app1-images          mountPath: /usr/local/nginx/html/webapp/images          readOnly: false        - name: app1-static          mountPath: /usr/local/nginx/html/webapp/static          readOnly: false      volumes:      - name: app1-images        nfs:          server: 192.168.32.20          path: /data/app1/images      - name: app1-static        nfs:          server: 192.168.32.20          path: /data/app1/static---kind: ServiceapiVersion: v1metadata:  labels:    app: app1-nginx-service-label  name: app1-nginx-service  namespace: app1spec:  type: NodePort  ports:  - name: http    port: 80    protocol: TCP    targetPort: 80    nodePort: 30080  - name: https    port: 443    protocol: TCP    targetPort: 443    nodePort: 30443  selector:    app: app1-nginx-selector</code></pre><pre><code>cd /opt/k8s/app1/tomcat</code></pre><p>&emsp;&emsp;编写tomcat的pod的yaml文件</p><pre><code>kind: DeploymentapiVersion: extensions/v1beta1metadata:  labels:    app: app1-tomcat-app1-deployment-label  name: app1-tomcat-app1-deployment  namespace: app1spec:  replicas: 1  selector:    matchLabels:      app: app1-tomcat-app1-selector  template:    metadata:      labels:        app: app1-tomcat-app1-selector    spec:      containers:      - name: app1-tomcat-app1-container        image: harbor.micepro.net/base/tomcat-app1:v1        command: [&quot;/usr/local/tomcat/bin/run_tomcat.sh&quot;]        #imagePullPolicy: IfNotPresent        imagePullPolicy: Always        ports:        - containerPort: 8080          protocol: TCP          name: http        env:        - name: &quot;password&quot;          value: &quot;123456&quot;        - name: &quot;age&quot;          value: &quot;18&quot;        resources:          limits:            cpu: 2            memory: &quot;1000Mi&quot;          requests:            cpu: 500m            memory: &quot;200Mi&quot;        volumeMounts:        - name: app1-myapp          mountPath: /data/tomcat/webapps/ROOT          readOnly: false      volumes:      - name: app1-myapp        nfs:          server: 192.168.32.19          path: /data/app1/ROOT      - name: app1-static        nfs:          server: 192.168.32.19          path: /data/app1/static---kind: ServiceapiVersion: v1metadata:  labels:    app: app1-tomcat-app1-service-label  name: app1-tomcat-app1-service  namespace: app1spec:  type: NodePort  ports:  - name: http    port: 80    protocol: TCP    targetPort: 8080    nodePort: 38080  selector:    app: app1-tomcat-app1-selector</code></pre><h3 id="修改配置文件"><a href="#修改配置文件" class="headerlink" title="修改配置文件"></a>修改配置文件</h3><p>&emsp;&emsp;因为要实现一个通用的nginx+tomcat动静分离web架构，即用户访问的静态页面和图片在由nginx直接响应，而动态请求则基于tomcat的service name转发用户请求到tomcat业务app。<br>&emsp;&emsp;所以我们需要修改当时创建镜像的nginx的配置文件，配置upstream服务器为tomcat的pod，配置如下</p><pre><code>upstream tomcat_webserver {    server app1-tomcat-app1-service.app1.svc.app1.local:8080;}server {    location /myapp {        proxy_pass http://tomcat_webserver;        proxy_set_header Host $host;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        proxy_set_header X-Real-IP $remote_addr;    }}</code></pre><p>&emsp;&emsp;其中pod间通信是基于K8s内部DNS实现的，一般是coreDNS或者kubeDNS，如果没有安装是无法解析的，可以使用kubeasz的第七个playbook安装，可参考之前文章<a href="https://hewanyue.com/blog/8f374cb8.html">使用kubeasz自动化部署K8s</a>。<br>&emsp;&emsp;主机名默认组成结构为release-name-rancher.default.svc.cluster.local，也就是<code>ServerName.NameSpace.svc.CLUSTER_DNS_DOMAIN</code>。<code>ServerName</code>是我们在yaml文件中定义的，<code>NameSpace</code>是我们创建pod时选择的<code>namespace</code>，<code>CLUSTER_DNS_DOMAIN</code>是我们在创建K8s集群时设置的集群名，如果是用kubeasz工具创建的K8s集群，可以去<code>/etc/ansible/hosts</code>文件中查看。<br>&emsp;&emsp;我们可以在pod间测试通信，确保相互之间可以通过主机名通信。</p><pre><code>[root@blog-tomcat-app1-deployment-5b4845ddd8-rr5dq /]# ping app1-nginx-service.app1.svc.cluster.localPING app1-nginx-service.app1.svc.cluster.local (10.68.101.60) 56(84) bytes of data.64 bytes from app1-nginx-service.app1.svc.cluster.local (10.68.101.60): icmp_seq=1 ttl=64 time=0.103 ms64 bytes from app1-nginx-service.app1.svc.cluster.local (10.68.101.60): icmp_seq=2 ttl=64 time=0.314 ms64 bytes from app1-nginx-service.app1.svc.cluster.local (10.68.101.60): icmp_seq=3 ttl=64 time=0.077 ms</code></pre><p>&emsp;&emsp;而且nginx及tomcat中最好将日志格式改为json格式，方便我们收集日志。<br>&emsp;&emsp;tomcat的配置文件<code>/usr/local/tomcat/conf/server.xml</code>，设置<code>appBase=&quot;/data/tomcat/webapps/</code>,日志格式修改如下</p><pre><code>        &lt;Valve className=&quot;org.apache.catalina.valves.AccessLogValve&quot; directory=&quot;logs&quot;               prefix=&quot;localhost_access_log&quot; suffix=&quot;.txt&quot;               pattern=&quot;{&amp;quot;clientip&amp;quot;:&amp;quot;%h&amp;quot;,&amp;quot;ClientUser&amp;quot;:&amp;quot;%l&amp;quot;,&amp;quot;authenticated&amp;quot;:&amp;quot;%u&amp;quot;,&amp;quot;AccessTime&amp;quot;:&amp;quot;%t&amp;quot;,&amp;quot;method&amp;quot;:&amp;quot;%r&amp;quot;,&amp;quot;status&amp;quot;:&amp;quot;%s&amp;quot;,&amp;quot;SendBytes&amp;quot;:&amp;quot;%b&amp;quot;,&amp;quot;Query?string&amp;quot;:&amp;quot;%q&amp;quot;,&amp;quot;partner&amp;quot;:&amp;quot;%{Referer}i&amp;quot;,&amp;quot;AgentVersion&amp;quot;:&amp;quot;%{User-Agent}i&amp;quot;}&quot; /&gt;</code></pre><h3 id="PV-PVC"><a href="#PV-PVC" class="headerlink" title="PV/PVC"></a>PV/PVC</h3><p>&emsp;&emsp;我们还可以通过创建一个创建PV/PVC的方式的方式来代替直接NFS挂载Volume。<br>&emsp;&emsp;PersistentVolume（PV）是集群中由管理员配置的一段网络存储。 它是集群中的资源，就像节点是集群资源一样。 PV是容量插件，如Volumes，但其生命周期独立于使用PV的任何单个pod。 此API对象捕获存储实现的详细信息，包括NFS，iSCSI或特定于云提供程序的存储系统。<br>&emsp;&emsp;PersistentVolumeClaim（PVC）是由用户进行存储的请求。 它类似于pod。 Pod消耗节点资源，PVC消耗PV资源。Pod可以请求特定级别的资源（CPU和内存）。声明可以请求特定的大小和访问模式（例如，可以一次读/写或多次只读）。<br>&emsp;&emsp;PVC和PV是一一对应的。Persistent Volume相对独立于Pods,单独创建。<br>&emsp;&emsp;Persistent Volume对具体的存储进行配置和分配，而Pods等则可以使用Persistent Volume抽象出来的存储资源，不需要知道集群的存储细节。<br>&emsp;&emsp;Persistent Volume和Persistent Volume Claim类似Pods和Nodes的关系，创建Pods需要消耗一定的Nodes的资源。而Persistent Volume则是提供了各种存储资源，而Persistent Volume Claim提出需要的存储标准，然后从现有存储资源中匹配或者动态建立新的资源，最后将两者进行绑定。<br>&emsp;&emsp;以我们这次的演示为例，PV和PVC如下所示。<br>&emsp;&emsp;PV的yaml文件示例</p><pre><code>apiVersion: v1kind: PersistentVolumemetadata:  name: nfsspec:  storageClassName: manual  capacity:    storage: 10Gi  accessModes:    - ReadWriteMany  nfs:    server: 192.168.32.19    path: &quot;/data/app1/static&quot;</code></pre><p>&emsp;&emsp;PVC的yaml文件示例</p><pre><code>apiVersion: v1kind: PersistentVolumeClaimmetadata:  name: nfsspec:  accessModes:    - ReadWriteMany  storageClassName: manual  resources:    requests:      storage: 10Gi</code></pre><p>&emsp;&emsp;pod中引用PVC</p><pre><code>apiVersion: v1kind: Podmetadata:  name: testpv  labels:    role: web-frontendspec:  containers:  - name: web    image: nginx    ports:      - name: web        containerPort: 80    volumeMounts:        - name: nfs          mountPath: &quot;/usr/local/nginx/html/webapp/static&quot;  volumes:  - name: nfs    persistentVolumeClaim:      claimName: nfs</code></pre><h2 id="HPA"><a href="#HPA" class="headerlink" title="HPA"></a>HPA</h2><p>&emsp;&emsp;虽然我们可以使用命令<code>kubectl scale</code>对运行在k8s 环境中的pod 数量进行扩容(增加)或缩容(减小)。</p><pre><code>root@DockerUbuntu18:~# kubectl --help | grep -w scale  scale          Set a new size for a Deployment, ReplicaSet, Replication Controller, or Jobroot@DockerUbuntu18:~# kubectl get deployments -n app1NAME                     READY   UP-TO-DATE   AVAILABLE   AGEapp1-nginx-deployment    1/1     1            1           9dapp1-tomcat-deployment   1/1     1            1           3d4hroot@DockerUbuntu18:~# kubectl scale deployment/app1-tomcat-deployment --replicas=2 -n app1deployment.extensions/app1-tomcat-deployment scaledroot@DockerUbuntu18:~# kubectl get deployments -n app1NAME                     READY   UP-TO-DATE   AVAILABLE   AGEapp1-nginx-deployment    1/1     1            1           9dapp1-tomcat-deployment   2/2     2            2           74s</code></pre><p>&emsp;&emsp;不过在实际生产中，我们肯定没法随时根据负载或者服务需要，手动动态伸缩我们的后端服务器数量，但是我们可以通过命令<code>kubectl autoscale</code>自动控制在k8s集群中运行的pod数量(水平自动伸缩)，想要实现自动上伸缩，需要我们提前设置pod范围及触发条件。<br>&emsp;&emsp;k8s从1.1版本开始增加了名称为HPA(Horizontal Pod Autoscaler)的控制器，用于实现基于pod中资源(CPU/Memory)利用率进行对pod的自动扩缩容功能的实现，早期的版本只能基于Heapster组件实现对CPU利用率做为触发条件，但是在k8s 1.11版本开始使用Metrices Server完成数据采集，然后将采集到的数据通过API（Aggregated API，汇总API），例如metrics.k8s.io、custom.metrics.k8s.io、external.metrics.k8s.io，然后再把数据提供给HPA控制器进行查询，以实现基于某个资源利用率对pod进行扩缩容的目的。<br>&emsp;&emsp;控制管理器默认每隔15s（可以通过–horizontal-pod-autoscaler-sync-period修改）查询metrics的资源使用情况<br>支持以下三种metrics指标类型：<br>&emsp;&emsp;-&gt;预定义metrics（比如Pod的CPU）以利用率的方式计算<br>&emsp;&emsp;-&gt;自定义的Pod metrics，以原始值（raw value）的方式计算<br>&emsp;&emsp;-&gt;自定义的object metrics<br>支持两种metrics查询方式：<br>&emsp;&emsp;-&gt;Heapster<br>&emsp;&emsp;-&gt;自定义的REST API<br>支持多metrics</p><p>（未完待续）</p>]]></content>
    
    <summary type="html">
    
      K8s集群搭建完成后，真正完成我们业务的是那些跑在k8s上的pod们。将业务跑在k8s集群只上，我们可以实现根据负载或者资源利用率动态扩容或者缩容我们的后端服务器，更加灵活高效的利用我们的物理设备，且能够实现服务的高可用及故障自治愈，本文将详细介绍以上的具体实现。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="Dockerfile" scheme="https://hewanyue.com/tags/Dockerfile/"/>
    
      <category term="kubernetes" scheme="https://hewanyue.com/tags/kubernetes/"/>
    
      <category term="yaml" scheme="https://hewanyue.com/tags/yaml/"/>
    
      <category term="集群" scheme="https://hewanyue.com/tags/%E9%9B%86%E7%BE%A4/"/>
    
      <category term="java" scheme="https://hewanyue.com/tags/java/"/>
    
  </entry>
  
  <entry>
    <title>企业级应用——ELK（三）：filebeat</title>
    <link href="https://hewanyue.com/blog/bd240ad7.html"/>
    <id>https://hewanyue.com/blog/bd240ad7.html</id>
    <published>2020-01-06T09:47:24.000Z</published>
    <updated>2020-01-20T07:29:36.382Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;提到ELK，就不得不提到EFK，通常意义上说，EFK是指用filebeat代替logstash形成的新组合。（哈，也有是指Fluentd的，这个我们之后再说）<br>&emsp;&emsp;Filebeat 是基于原先 logstash-forwarder 的源码改造出来的，无需依赖 Java 环境就能运行，安装包10M不到。<br>&emsp;&emsp;而且如果日志的量很大，Logstash 会遇到资源占用高的问题，为解决这个问题，我们引入了Filebeat。Filebeat 是基于 logstash-forwarder 的源码改造而成，用 Golang 编写，无需依赖 Java 环境，效率高，占用内存和 CPU 比较少，非常适合作为 Agent 跑在服务器上，来实现日志转发的功能。</p><a id="more"></a><p>&emsp;&emsp;还是去官网下载<a href="https://www.elastic.co/cn/downloads/beats/filebeat" target="_blank" rel="external nofollow noopener noreferrer">https://www.elastic.co/cn/downloads/beats/filebeat</a>。本次演示还是以最新版的filebeat7.5.1为例（以前版本的filebeat配置文件格式参数上可能有一些改变，不过大同小异）。</p><pre><code class="shell">cd /usr/local/src/wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.5.1-amd64.debdpkg -i filebeat-7.5.1-amd64.deb</code></pre><h2 id="基础配置"><a href="#基础配置" class="headerlink" title="基础配置"></a>基础配置</h2><p>&emsp;&emsp;配置文件也很简单，如果想要写入文件，则配置如下</p><pre><code class="yaml">grep -v &quot;#&quot; /etc/filebeat/filebeat.yml | grep -v &quot;^$&quot;filebeat.inputs:- type: log  paths:    - /var/log/syslog  exclude_lines: [&quot;^DBG&quot;]  exclude_files: [&quot;.gz$&quot;]  tags: &quot;syslog-filebeat&quot;output.file:   path: &quot;/tmp&quot;  filename: &quot;filebeat.txt&quot;</code></pre><p>&emsp;&emsp;<code>paths</code>路径支持正则通配符写法，exclude是设置不匹配的文件格式。而且filebeat也支持同时从多个路径收集写成如下配置</p><pre><code class="yaml">filebeat.inputs:- type: log  paths:    - /var/log/syslog  exclude_lines: [&quot;^DBG&quot;]  exclude_files: [&quot;.gz$&quot;]  tags: &quot;syslog-filebeat&quot;- type: log  paths:    - /var/log/nginx/access.log  exclude_lines: [&quot;^DBG&quot;]  exclude_files: [&quot;.gz$&quot;]  document_type: &quot;nginx-accesslog-filebeatoutput.file:   path: &quot;/tmp&quot;  filename: &quot;filebeat.txt&quot;</code></pre><p>&emsp;&emsp;同样，filebeat支持写入redis和kafka</p><pre><code class="yaml">filebeat.inputs:- type: log  paths:    - /var/log/syslog  exclude_lines: [&quot;^DBG&quot;]  exclude_files: [&quot;.gz$&quot;]    tags: &quot;filebeat-redis-syslog&quot;output.redis:  hosts: [&quot;192.168.32.31:6379&quot;]  key: &quot;filebeat-system-log&quot; #为了后期日志处理，建议自定义 key 名称  db: 1 #使用第几个库  timeout: 5 #超时时间  password: 123456 #redis 密码</code></pre><p>&emsp;&emsp;想要写入kafka则添加output插件，配置如下</p><pre><code class="yaml">filebeat.inputs:- type: log  paths:    - /var/log/syslog  exclude_lines: [&quot;^DBG&quot;]  exclude_files: [&quot;.gz$&quot;]  tags: &quot;filebeat-kafka-syslog&quot;output.kafka: #写入 kafka  hosts: [&quot;192.168.15.11:9092&quot;,&quot;192.168.15.12:9092&quot;,&quot;192.168.15.13:9092&quot;]  topic: &quot;systemlog-1512-filebeat&quot;  partition.round_robin:    reachable_only: true  required_acks: 1 #本地写入完成  compression: gzip #开启压缩  max_message_bytes: 1000000 #消息最大值</code></pre><h2 id="配置详解"><a href="#配置详解" class="headerlink" title="配置详解"></a>配置详解</h2><h3 id="input"><a href="#input" class="headerlink" title="input"></a>input</h3><p>&emsp;&emsp;也就是设置日志收集的来源，需要的属性有<code>type</code>,<code>path</code>，根据<a href="https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-options.html" target="_blank" rel="external nofollow noopener noreferrer">官方文档</a>，现在版本常用写法为，</p><pre><code>filebeat.inputs:- type: log  paths:    - /var/log/system.log    - /var/log/wifi.log- type: log  paths:    - &quot;/var/log/apache2/*&quot;  fields:    apache: true  fields_under_root: true</code></pre><p>&emsp;&emsp;其中type的类型很多</p><ul><li>Log 日志文件，必须有PATH,官方示例如下：</li></ul><pre><code>filebeat.inputs:- type: log   paths:    - /var/log/system.log    - /var/log/wifi.log    - /var/log/*.log- type: log   paths:    - &quot;/var/log/apache2/*&quot;  fields:    apache: true  fields_under_root: true</code></pre><ul><li>Stdin 标准输入，没有PATH，官方示例如下：</li></ul><pre><code>filebeat.inputs:- type: stdin</code></pre><ul><li>Container 容器中日志，必须有PATH，官方示例如下：</li></ul><pre><code>filebeat.inputs:- type: container  paths:     - &#39;/var/lib/docker/containers/*/*.log&#39;</code></pre><ul><li>Kafka 从kafka中读取数据，官方示例如下：</li></ul><pre><code>filebeat.inputs:- type: kafka  hosts: [&quot;&lt;your event hub namespace&gt;.servicebus.windows.net:9093&quot;]  topics: [&quot;&lt;your event hub instance&gt;&quot;]  group_id: &quot;&lt;your consumer group&gt;&quot;  username: &quot;$ConnectionString&quot;  password: &quot;&lt;your connection string&gt;&quot;  ssl.enabled: true</code></pre><ul><li>Redis 从redis中读取数据，官方示例如下：</li></ul><pre><code>filebeat.inputs:- type: redis  hosts: [&quot;localhost:6379&quot;]  password: &quot;${redis_pwd}&quot;</code></pre><ul><li>UDP 开放UDP端口来接受数据，可设置单条最大数据上限，不定义默认为20MiB。</li></ul><pre><code>filebeat.inputs:- type: udp  max_message_size: 10KiB  host: &quot;localhost:8080&quot;</code></pre><ul><li>Docker 也支持直接从容器中读取数据， containers.ids是必须定义说明，可以用*代表所有容器。</li></ul><pre><code>filebeat.inputs:- type: docker  containers.ids:     - &#39;8b6fe7dc9e067b58476dc57d6986dd96d7100430c5de3b109a99cd56ac655347&#39;</code></pre><ul><li>TCP 用法与UDP相同，设置监听的主机和端口。</li></ul><pre><code>filebeat.inputs:- type: tcp  max_message_size: 10MiB  host: &quot;localhost:9000&quot;</code></pre><ul><li>Syslog 监听系统日志，指定传输协议即可。类似TCP和UDP。</li></ul><pre><code>filebeat.inputs:- type: syslog  protocol.udp:    host: &quot;localhost:9000&quot;</code></pre><pre><code>filebeat.inputs:- type: syslog  protocol.tcp:    host: &quot;localhost:9000&quot;</code></pre><ul><li>s3 AWS的对象存储日志，不过多介绍</li><li>NetFlow Cisco设备网络设备的日志，不过多介绍</li><li>Google Pub/Sub google云的订阅发布模式协议数据，不过多介绍。</li></ul><p>&emsp;&emsp;一般来说，我们都写log，就可以满足我们绝大多数场景的使用了。除了<code>type</code>、<code>path</code>这俩常用的input属性外，还有两个设置属性，我们也经常会用到，就是<code>include_lines</code>、<code>exclude_lines</code>，顾名思义，就是包括和排除，配合path中的通配符，可以帮助我们更灵活的指定要收集的日志文件。<br>&emsp;&emsp;还有一个很常用的属性就是<code>tags</code>,可以写多个，用<code>[]</code>括起来就可以。因为在filebeat中因为有自带<code>type</code>关键字，所以我们在之后筛选日志的时候，无法通过type字段来区分不同的日志源了，所以我们可以通过自定义<code>tags</code>字段，来实现之前在logstash上type的功能，这样在我们收集到的日志中，会自动加入<code>tags</code> 标签属性，然后通过logstash的筛选时，就可以对<code>tags</code>关键字做判断了。</p><h3 id="output"><a href="#output" class="headerlink" title="output"></a>output</h3><p>&emsp;&emsp;输出选项有<code>Elasticsearch</code>、<code>Logstash</code>、<code>Kafka</code>、<code>Redis</code>、<code>File</code>、<code>Console</code>、<code>Elastic Cloud</code>。</p><ul><li>File<br>输出到文件中是最简单的设置了，一般用于测试。<pre><code>output.file:path: &quot;/tmp/filebeat&quot;     #输出文件路径filename: filebeat        #输出日志名称，超过大小限制后会自动添加数字后缀#rotate_every_kb: 10000   #每个日志文件大小限制#number_of_files: 7   #路径下最大的储存日志文件数量，超过此值后自动删除最早的日志文件，默认为7。#permissions: 0600    #创建的日志文件的权限</code></pre></li><li>Logstash<br>filebeat支持直接将数据输出值logstash主机。<pre><code>output.logstash:hosts: [&quot;127.0.0.1:5044&quot;]</code></pre>&emsp;&emsp;而logstash主机需要设置输入为beats，才可以顺利接收filebeat的数据。<pre><code>input {beats {  port =&gt; 5044}}</code></pre></li></ul><p>output {<br>  elasticsearch {<br>    hosts =&gt; [“<a href="http://localhost:9200&quot;]" target="_blank" rel="external nofollow noopener noreferrer">http://localhost:9200&quot;]</a><br>    index =&gt; “%{[@metadata][beat]}-%{[@metadata][version]}”<br>  }<br>}</p><pre><code>- redis输出值redis，上面有说明，这里就不详细介绍了。</code></pre><p>output.redis:<br>  hosts: [“localhost”]<br>  password: “my_password”<br>  key: “filebeat”<br>  db: 0<br>  timeout: 5</p><pre><code>- kafka输出至kafka。</code></pre><p>output.kafka:</p><h1 id="initial-brokers-for-reading-cluster-metadata"><a href="#initial-brokers-for-reading-cluster-metadata" class="headerlink" title="initial brokers for reading cluster metadata"></a>initial brokers for reading cluster metadata</h1><p>  hosts: [“kafka1:9092”, “kafka2:9092”, “kafka3:9092”]</p><h1 id="message-topic-selection-partitioning"><a href="#message-topic-selection-partitioning" class="headerlink" title="message topic selection + partitioning"></a>message topic selection + partitioning</h1><p>  topic: ‘%{[fields.log_topic]}’<br>  partition.round_robin:<br>    reachable_only: false</p><p>  required_acks: 1<br>  compression: gzip<br>  max_message_bytes: 1000000</p><pre><code>&amp;emsp;&amp;emsp;也可以输出至kafka的不同的topic中&gt;Rule settings:&gt; topic&gt;    The topic format string to use. If this string contains field references, such as %{[fields.name]}, the fields must exist, or the rule fails. &gt;mappings&gt;    A dictionary that takes the value returned by topic and maps it to a new &gt;name. &gt;default&gt;    The default string value to use if mappings does not find a match. &gt;when&gt;    A condition that must succeed in order to execute the current rule. All the conditions supported by processors are also supported here. &amp;emsp;&amp;emsp;官方示例如下：</code></pre><p>output.kafka:<br>  hosts: [“localhost:9092”]<br>  topic: “logs-%{[beat.version]}”<br>  topics:<br>    - topic: “critical-%{[beat.version]}”<br>      when.contains:<br>        message: “CRITICAL”<br>    - topic: “error-%{[beat.version]}”<br>      when.contains:<br>        message: “ERR”</p><pre><code>- Elaticsearch可以直接将数据输出给elaticsearch服务器，不过一般来说我们不会这样做，一般是会经过logstash来筛选之后再传入elaticsearch。官方示例如下：</code></pre><p>output.elasticsearch:<br>  hosts: [“<a href="https://localhost:9200&quot;]" target="_blank" rel="external nofollow noopener noreferrer">https://localhost:9200&quot;]</a><br>  username: “filebeat_internal”<br>  password: “YOUR_PASSWORD”</p><pre><code>- Console输出至屏幕终端显示。pretty官方的介绍为``If pretty is set to true, events written to stdout will be nicely formatted. The default is false``，示例如下：</code></pre><p>output.console:<br>  pretty: true</p><pre><code></code></pre>]]></content>
    
    <summary type="html">
    
      提到ELK，就不得不提到EFK，通常意义上说，EFK是指用filebeat代替logstash形成的新组合。（哈，也有是指Fluentd的，这个我们之后再说） Filebeat 是基于原先 logstash-forwarder 的源码改造出来的，无需依赖 Java 环境就能运行，安装包10M不到。 而且如果日志的量很大，Logstash 会遇到资源占用高的问题，为解决这个问题，我们引入了Filebeat。Filebeat 是基于 logstash-forwarder 的源码改造而成，用 Golang 编写，无需依赖 Java 环境，效率高，占用内存和 CPU 比较少，非常适合作为 Agent 跑在服务器上，来实现日志转发的功能。
    
    </summary>
    
    
      <category term="linux" scheme="https://hewanyue.com/categories/linux/"/>
    
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="ELK" scheme="https://hewanyue.com/tags/ELK/"/>
    
      <category term="Elasticsearch" scheme="https://hewanyue.com/tags/Elasticsearch/"/>
    
      <category term="Logstash" scheme="https://hewanyue.com/tags/Logstash/"/>
    
      <category term="Filebeat" scheme="https://hewanyue.com/tags/Filebeat/"/>
    
  </entry>
  
  <entry>
    <title>企业级应用——ELK（二）：ELK进阶</title>
    <link href="https://hewanyue.com/blog/ee80297c.html"/>
    <id>https://hewanyue.com/blog/ee80297c.html</id>
    <published>2020-01-03T14:30:18.000Z</published>
    <updated>2020-01-20T07:29:36.384Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;之前我们部署好了ELK的基本架构，也实现了从系统日志以及nginx中收集日志，不过等待我们的问题依然很多：怎么讲收集好的日志放至临时缓存？或者怎么从缓存中提取日志？对于java应用等日志非单行的服务日志该如何收集等等。本文将继续讲解ELK的各种进阶用法。</p><a id="more"></a><h2 id="收集tomcat日志"><a href="#收集tomcat日志" class="headerlink" title="收集tomcat日志"></a>收集tomcat日志</h2><p>&emsp;&emsp;收集tomcat中的日志比较简单，跟nginx一样，将日志序列化为json格式即可。<br>&emsp;&emsp;修改tomcat配置文件,将日志格式修改为如下格式。</p><pre><code>vim /usr/local/tomcat/conf/server.xml</code></pre><pre><code>        &lt;Valve className=&quot;org.apache.catalina.valves.AccessLogValve&quot; directory=&quot;logs&quot;               prefix=&quot;localhost_access_log&quot; suffix=&quot;.txt&quot;               pattern=&quot;{&amp;quot;clientip&amp;quot;:&amp;quot;%h&amp;quot;,&amp;quot;ClientUser&amp;quot;:&amp;quot;%l&amp;quot;,&amp;quot;authenticated&amp;quot;:&amp;quot;%u&amp;quot;,&amp;quot;AccessTime&amp;quot;:&amp;quot;%t&amp;quot;,&amp;quot;method&amp;quot;:&amp;quot;%r&amp;quot;,&amp;quot;status&amp;quot;:&amp;quot;%s&amp;quot;,&amp;quot;SendBytes&amp;quot;:&amp;quot;%b&amp;quot;,&amp;quot;Query?string&amp;quot;:&amp;quot;%q&amp;quot;,&amp;quot;partner&amp;quot;:&amp;quot;%{Referer}i&amp;quot;,&amp;quot;AgentVersion&amp;quot;:&amp;quot;%{User-Agent}i&amp;quot;}&quot; /&gt;</code></pre><p>&emsp;&emsp;此时查看新生成的访问日志，即可看到新生成的日志已经成json格式了。</p><pre><code>tail -f /usr/local/tomcat/logs/localhost_access_log.2020-01-03.txt</code></pre><pre><code>{&quot;clientip&quot;:&quot;192.168.32.1&quot;,&quot;ClientUser&quot;:&quot;-&quot;,&quot;authenticated&quot;:&quot;-&quot;,&quot;AccessTime&quot;:&quot;[03/Jan/2020:19:34:29 +0800]&quot;,&quot;method&quot;:&quot;GET /bg-button.png HTTP/1.1&quot;,&quot;status&quot;:&quot;304&quot;,&quot;SendBytes&quot;:&quot;-&quot;,&quot;Query?string&quot;:&quot;&quot;,&quot;partner&quot;:&quot;http://192.168.32.51:8080/tomcat.css&quot;,&quot;AgentVersion&quot;:&quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0&quot;}</code></pre><p>&emsp;&emsp;不过为了以防万一，可以去网上使用在线json格式校验工具，检查一下格式是否正确。<br><img src="https://img-blog.csdnimg.cn/20200103214555106.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="JSON格式校验"><br>&emsp;&emsp;此时就可以使用logstash工具来收集我们的tomcat日志了。</p><pre><code>vim /etc/logstash/conf.d/tomcat-el.conf</code></pre><p>&emsp;&emsp;不过，想必你们一定也发现了，tomcat访问日志中是带有日期格式的，每天的访问日志文件名是不同的，这要怎么写到path中呢？<br>&emsp;&emsp;哈，别慌，logstash的input-file插件也支持通配符的写法，我们可以写<code>path =&gt; &quot;/usr/local/tomcat/logs/localhost_access_log.*.txt&quot;</code>，如下所示。</p><pre><code>input {  file {    path =&gt; &quot;/usr/local/tomcat/logs/localhost_access_log.*.txt&quot;    stat_interval =&gt; 3    start_position =&gt; &quot;beginning&quot;    codec =&gt; &quot;json&quot;    type =&gt; &quot;tomcat_accesslog&quot;  }}output {#  stdout {#    codec =&gt; rubydebug#  }  if [type] == &quot;tomcat_accesslog&quot; {  elasticsearch {    hosts =&gt; [&quot;192.168.32.41:9200&quot;]    index =&gt; &quot;tomcat_accesslog-%{+YYYY.MM.dd}&quot;  }}}</code></pre><p>&emsp;&emsp;PS：可以分开至不同的配置文件，也可以合并至一个文件中，不过我们习惯不同服务用单独的配置文件来抓取，方便修改。<br>&emsp;&emsp;重启logstash服务，然后去kibana中创建tomcat_accesslog-*的索引，就可以看到tomcat的访问日志了。</p><h2 id="收集JAVA服务的日志"><a href="#收集JAVA服务的日志" class="headerlink" title="收集JAVA服务的日志"></a>收集JAVA服务的日志</h2><p>&emsp;&emsp;JAVA服务中的日志如果不报错还好，还可以是一行一条，但是一旦出现报错信息，一般都是小半屏幕N多行的报错信息，而我们收集日志的很重要的目的就是为了查看这些报错信息，而如果不对这些日志进行处理，还按照一行一条来收集的话，当我们查看这个日志的时候就会很崩溃了——这乱的几乎完全没法看。所以我们要想办法将JAVA服务的日志处理成一行。<br>&emsp;&emsp;刚好强大的logstash也支持换行匹配，我们刚好就以logstash服务本身日志为例</p><pre><code>vim /etc/logstash/conf.d/java-el.conf</code></pre><pre><code>input {  file {    path =&gt; &quot;/logstash/logs/logstash-plain.log&quot;    stat_interval =&gt; 3    start_position =&gt; &quot;beginning&quot;    type =&gt; &quot;java_accesslog&quot;        codec =&gt; multiline {    pattern =&gt; &quot;^\[&quot; #当遇到[开头的行时候将多行进行合并    negate =&gt; true #true 为匹配成功进行操作， false 为不成功进行操作    what =&gt; &quot;previous&quot; #与以前的行合并，如果是下面的行合并就是 next  }}}filter { #日志过滤，如果所有的日志都过滤就写这里，如果只针对某一个过滤就写在 input 里面的日志输入里面}output {  if [type] == &quot;java_accesslog&quot; {  elasticsearch {    hosts =&gt; [&quot;192.168.32.41:9200&quot;]    index =&gt; &quot;java_accesslog-%{+YYYY.MM.dd}&quot;  }}}</code></pre><p>&emsp;&emsp;重启logstash服务，然后去kibana中创建java_accesslog-*的索引，就可以看到java的访问日志了。</p><h2 id="redis"><a href="#redis" class="headerlink" title="redis"></a>redis</h2><p>&emsp;&emsp;从redis读取日志和将收集到的日志储存至redis缓存中，其实使用了input的redis插件和output的redis插件来实现。官方文档地址为： <a href="https://www.elastic.co/guide/en/logstash/current/plugins-outputsredis.html" target="_blank" rel="external nofollow noopener noreferrer">https://www.elastic.co/guide/en/logstash/current/plugins-outputsredis.html</a></p><h3 id="写入redis"><a href="#写入redis" class="headerlink" title="写入redis"></a>写入redis</h3><p>&emsp;&emsp;我们还以tomcat日志及java日志logstash日志本身为例，写入redis缓存中。<br>&emsp;&emsp;先配置好redis服务器如下：</p><pre><code>IP：192.168.32.31PORT：6379auth：123456</code></pre><pre><code>input {  file {    path =&gt; &quot;/usr/local/tomcat/logs/localhost_access_log.*.txt&quot;    stat_interval =&gt; 3    start_position =&gt; &quot;beginning&quot;    codec =&gt; &quot;json&quot;    type =&gt; &quot;tomcat_redis_accesslog&quot;  }  file {    path =&gt; &quot;/logstash/logs/logstash-plain.log&quot;    stat_interval =&gt; 3    start_position =&gt; &quot;beginning&quot;    type =&gt; &quot;java_redis_accesslog&quot;        codec =&gt; multiline {    pattern =&gt; &quot;^\[&quot; #当遇到[开头的行时候将多行进行合并    negate =&gt; true #true 为匹配成功进行操作， false 为不成功进行操作    what =&gt; &quot;previous&quot; #与以前的行合并，如果是下面的行合并就是 next  }}}output {  if [type] == &quot;tomcat_redis_accesslog&quot; {    redis {      data_type =&gt; &quot;list&quot;      key =&gt; &quot;tomcat_redis_accesslog&quot;      host =&gt; &quot;192.168.32.31&quot;      port =&gt; &quot;6379&quot;      db =&gt; &quot;0&quot;      password =&gt; &quot;123456&quot;    }}    if [type] == &quot;java_redis_accesslog&quot; {    redis {      data_type =&gt; &quot;list&quot;      key =&gt; &quot;java_redis_accesslog&quot;      host =&gt; &quot;192.168.32.31&quot;      port =&gt; &quot;6379&quot;      db =&gt; &quot;1&quot;      password =&gt; &quot;123456&quot;    }}}</code></pre><p>&emsp;&emsp;将日志数据以列表的形式储存在redis中，多个不同的日志，储存在不同数据库中。</p><h3 id="读取redis"><a href="#读取redis" class="headerlink" title="读取redis"></a>读取redis</h3><p>&emsp;&emsp;与写入用法大致相同，可以说是怎么写进去的就怎么读出来。形式如下</p><pre><code>input {  redis {    data_type =&gt; &quot;list&quot;    key =&gt; &quot;tomcat_redis_accesslog&quot;    host =&gt; &quot;192.168.32.31&quot;    port =&gt; &quot;6379&quot;    db =&gt; &quot;0&quot;    password =&gt; &quot;123456&quot;    codec =&gt; &quot;json&quot;  }}  redis {    data_type =&gt; &quot;list&quot;    key =&gt; &quot;java_redis_accesslog&quot;    host =&gt; &quot;192.168.32.31&quot;    port =&gt; &quot;6379&quot;    db =&gt; &quot;1&quot;    password =&gt; &quot;123456&quot;    codec =&gt; &quot;json&quot;  }}}output {  if [type] == &quot;tomat_redis_accesslog&quot; {  elasticsearch {    hosts =&gt; [&quot;192.168.32.41:9200&quot;]    index =&gt; &quot;tomcat_redis_accesslog-%{+YYYY.MM.dd}&quot;  }}  if [type] == &quot;java_redis_accesslog&quot; {  elasticsearch {    hosts =&gt; [&quot;192.168.32.41:9200&quot;]    index =&gt; &quot;java_redis_accesslog-%{+YYYY.MM.dd}&quot;  }}}</code></pre><p>&emsp;&emsp;不过需要注意的是input中记得加<code>codec =&gt; &quot;json&quot;</code>，否则无法解析正确各项属性，不方便我们查看和统计日志。而且不知道大家有没有发现，当我们从redis中取出数据的时候，我们的input选项中没有像往常那样写上<code>type =&gt; &quot;tomcat_redis_accesslog&quot;</code>，而是直接在output中做了type的判断。这是因为，redis中的数据是在写入的时候，已经附加了type属性，它在redis中储存时还是会保留type属性的，所以取出来的时候，还是按照之前写入时的type类型取出即可。</p><h2 id="kafka"><a href="#kafka" class="headerlink" title="kafka"></a>kafka</h2><p>&emsp;&emsp;在很多大型互联网公司，都喜欢使用kafka来作为缓存层，因为redis虽然效率很高，但数据不如kafka可靠，kafka更适合大数据量场景使用。这就要视业务实际情况而定了。不过使用kafa来作为中间缓冲区的企业还是大有人在的。<br>&emsp;&emsp;我们可以新开启一个kafka主机，IP为192.168.32.36，使用默认端口9092。kafak的使用方法与redis大致相同。配置文件示例如下</p><pre><code>input {  file {    path =&gt; &quot;/apps/nginx/logs/access_json.log&quot;    stat_interval =&gt; 3    start_position =&gt; &quot;beginning&quot;    codec =&gt; &quot;json&quot;    type =&gt; &quot;nginx_kafka_accesslog&quot;  }}output {  if [type] == &quot;nginx_kafka_accesslog&quot; {  kafka {    bootstrap_servers =&gt; &quot;192.168.32.36:9092&quot; #kafka 服务器地址    topic_id =&gt; &quot;nginx_kafka_accesslog&quot;    codec =&gt; &quot;json&quot;  } }}</code></pre><p>&emsp;&emsp;与写入redis有些区别的就是，kafka中不是<code>key</code>了，而是<code>topic_id</code>。所以读取时也有一些区别，填写的关键字为<code>topics</code>，而且同样做判断时，<code>type</code>是当初写进去的那个属性。</p><pre><code>input {  kafka {    bootstrap_servers =&gt; &quot;192.168.32.36:9092&quot;    topics =&gt; &quot;nginx_kafka_accesslog&quot;    codec =&gt; &quot;json&quot;    consumer_threads =&gt; 1  #线程数，默认值为1，一般设置与分区数量相同    #decorate_events =&gt; true #不写默认是关闭的，开启此属性可以将当前topic、offset、group、partition等信息也带到message中  }}output {  if [type] == &quot;nginx_kafka_accesslog&quot; {  elasticsearch {    hosts =&gt; [&quot;192.168.32.41:9200&quot;]    index =&gt; &quot;nginx_kafka_accesslog-%{+YYYY.MM.dd}&quot;  }}}</code></pre><p>&emsp;&emsp;kafka也支持多文件同时写入，设置不同的<code>topic_id</code>就可以了。</p>]]></content>
    
    <summary type="html">
    
      之前我们部署好了ELK的基本架构，也实现了从系统日志以及nginx中收集日志，不过等待我们的问题依然很多：怎么讲收集好的日志放至临时缓存？或者怎么从缓存中提取日志？对于java应用等日志非单行的服务日志该如何收集等等。本文将继续讲解ELK的各种进阶用法。
    
    </summary>
    
    
      <category term="linux" scheme="https://hewanyue.com/categories/linux/"/>
    
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="ELK" scheme="https://hewanyue.com/tags/ELK/"/>
    
      <category term="Elasticsearch" scheme="https://hewanyue.com/tags/Elasticsearch/"/>
    
      <category term="Logstash" scheme="https://hewanyue.com/tags/Logstash/"/>
    
      <category term="Kibana" scheme="https://hewanyue.com/tags/Kibana/"/>
    
  </entry>
  
  <entry>
    <title>企业级应用——ELK（一）：ELK的部署</title>
    <link href="https://hewanyue.com/blog/a1b53f63.html"/>
    <id>https://hewanyue.com/blog/a1b53f63.html</id>
    <published>2020-01-03T10:00:54.000Z</published>
    <updated>2020-01-20T07:29:36.380Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;ELK是Elasticsearch、Logstash、Kibana的简称，这三者是核心套件，但并非全部。<br>&emsp;&emsp;Elasticsearch是实时全文搜索和分析引擎，提供搜集、分析、存储数据三大功能；是一套开放REST和JAVA API等结构提供高效搜索功能，可扩展的分布式系统。它构建于Apache Lucene搜索引擎库之上。<br>&emsp;&emsp;Logstash是一个用来搜集、分析、过滤日志的工具。它支持几乎任何类型的日志，包括系统日志、错误日志和自定义应用程序日志。它可以从许多来源接收日志，这些来源包括 syslog、消息传递（例如 RabbitMQ）和JMX，它能够以多种方式输出数据，包括电子邮件、websockets和Elasticsearch。<br>&emsp;&emsp;Kibana是一个基于Web的图形界面，用于搜索、分析和可视化存储在 Elasticsearch指标中的日志数据。它利用Elasticsearch的REST接口来检索数据，不仅允许用户创建他们自己的数据的定制仪表板视图，还允许他们以特殊的方式查询和过滤数据。</p><a id="more"></a><h2 id="ELK架构"><a href="#ELK架构" class="headerlink" title="ELK架构"></a>ELK架构</h2><p>&emsp;&emsp;通常来说，只使用这三个组件就可以进行日志收集了，不过在企业实际生产中，需要用到ELK做集中日志收集的话，日志的产生量都是惊人的，所以通常情况下会需要缓存层来防止elasticsearch被压垮。架构如下图所示。（也可以通过filebeat来收集日志。）<br><img src="https://img-blog.csdnimg.cn/20200103144832731.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="ELK架构"></p><h2 id="ELK的部署"><a href="#ELK的部署" class="headerlink" title="ELK的部署"></a>ELK的部署</h2><p>&emsp;&emsp;需要注意的是，ELK的这三个组件版本要一致，否则可能会出现一些不必要的问题。我们这里选用最新版本7.5.1为例，演示主机均为ubuntu1804。</p><h3 id="Elasticsearch"><a href="#Elasticsearch" class="headerlink" title="Elasticsearch"></a>Elasticsearch</h3><p>&emsp;&emsp;我们这里用两台主机来搭建一个elasticsearch集群，一般来说因为他的选举机制，elasticsearch集群都是3、5、7奇数个，不过2台主机也可以使用，我们这里节约主机使用两台主机做演示，IP分别为<code>192.168.32.41</code>、<code>192.168.32.42</code>。</p><p>&emsp;&emsp;官网下载链接为<a href="https://www.elastic.co/cn/downloads/elasticsearch" target="_blank" rel="external nofollow noopener noreferrer">https://www.elastic.co/cn/downloads/elasticsearch</a>。<br>&emsp;&emsp;我们这里选择deb安装包安装，也可以选用源码tar包自己解压安装。</p><pre><code>wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.5.1-amd64.deb</code></pre><p>&emsp;&emsp;这个版本的deb包是自带java环境（openjdk11）的，如果主机已经预制java环境，可以去官网下载no-java的版本，使用jdk8的时候有warning，说未来版本将不支持jdk8，建议使用jdk11及以上。</p><pre><code>dpkg -i elasticsearch-7.5.1-amd64.deb</code></pre><p>&emsp;&emsp;elasticsearch的配置文件路径为<code>/etc/elasticsearch/elasticsearch.yml</code>，需要修改的不多,将集群主机IP设置好就可以了，如下所示</p><pre><code>root@elasticsearch1:~# grep &quot;^[a-Z]&quot; /etc/elasticsearch/elasticsearch.ymlcluster.name: ELK-CLuster      #集群名称，名称相同即属于是同一个集群node.name: node-1                 #本机在集群内的节点名称path.data: /elasticsearch/datapath.logs: /elasticsearch/logsbootstrap.memory_lock: true   #服务启动的时候锁定足够的内存， 防止数据写入swapnetwork.host: 0.0.0.0http.port: 9200discovery.seed_hosts: [&quot;192.168.32.41&quot;,&quot;192.168.32.42&quot;]cluster.initial_master_nodes: [&quot;node-1&quot;,&quot;node-2&quot;]</code></pre><p>&emsp;&emsp;我这里是单独创建了一个日志路径和数据路径，方便管理，并修改属主赋予权限。</p><pre><code>mkdir -p /elasticsearch/{data,logs}chown -R elasticsearch:elasticsearch /elasticsearch</code></pre><p>&emsp;&emsp;除了在配置文件中设置<code>bootstrap.memory_lock: true</code>以外，还需要在启动配置文件中设置允许无限制使用内存，否则启动检查就会报错，导致服务起不来。</p><pre><code>vim /usr/lib/systemd/system/elasticsearch.serviceLimitMEMLOCK=infinity</code></pre><p>&emsp;&emsp;而且根据官方文档<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html" target="_blank" rel="external nofollow noopener noreferrer">https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html</a>，最大30G 以内的内存。<br>&emsp;&emsp;在一般使用中，使用最大内存和最小内存都设置为2G就可以了。</p><pre><code>vim /etc/elasticsearch/jvm.options-Xms2g-Xmx2g</code></pre><p>&emsp;&emsp;另一台主机也同样配置，记得修改<code>node.name</code>，之后就可以启动elasticsearch了。</p><pre><code>systemctl enable --now elasticsearch</code></pre><p>&emsp;&emsp;在任意主机使用curl命令可以检查集群的健康状态。</p><pre><code>curl -sXGET http://192.168.32.41:9200/_cluster/health?pretty=true</code></pre><p>&emsp;&emsp;获取到的是一个 json 格式的返回值，那就可以通过 python 对其中的信息进行分析，例如对 status 进行分析，如果等于 green(绿色)就是运行在正常，等于yellow(黄色)表示副本分片丢失， red(红色)表示主分片丢失。<br>&emsp;&emsp;至此，elasticsearch服务的部署就算是完成了。</p><h3 id="Logstash"><a href="#Logstash" class="headerlink" title="Logstash"></a>Logstash</h3><p>&emsp;&emsp;logstash也是一个基于java的插件式服务，很多功能都是依靠于插件来实现的，我们安装官方的安装包，大部分常用插件都是已经预置了，如果还有其他的功能需求，就需要去官网或者github下载插件了。这些之后再说，我们先去官网上将Logstash7.5.1安装包下载下来并部署上。</p><pre><code>wget https://artifacts.elastic.co/downloads/logstash/logstash-7.5.1.deb</code></pre><p>&emsp;&emsp;logstash也同样需要java环境</p><pre><code>apt updateapt install openjdk-8-jdk</code></pre><p>&emsp;&emsp;或者安装oracle的jdk，生产环境还是推荐使用oracle公司的jdk，更加稳定。然后安装logstash</p><pre><code>dpkg -i logstash-7.5.1.deb</code></pre><p>&emsp;&emsp;对于logstash的配置也很少,不做修改也可以使用，不过我们这里同样还是修改一下数据目录和日志目录</p><pre><code>root@logstash1:~# grep &quot;^[a-Z]&quot; /etc/logstash/logstash.ymlpath.data: /logstash/datapath.logs: /logstash/logs</code></pre><p>&emsp;&emsp;修改属主</p><pre><code>mkdir -p /logstash/{data,logs}chown -R /logstash</code></pre><p>&emsp;&emsp;logstash的默认执行程序路径为<code>/usr/share/logstash/bin/logstash</code>，这其实也是一个shell脚本文件，脚本中调用java的类库。</p><pre><code>root@logstash1:~# /usr/share/logstash/bin/logstash --helpThread.exclusive is deprecated, use Thread::MutexWARNING: Could not find logstash.yml which is typically located in $LS_HOME/config or /etc/logstash. You can specify the path using --path.settings. Continuing using the defaultsUsage:    bin/logstash [OPTIONS]Options:    -n, --node.name NAME          Specify the name of this logstash instance,                                   if no value is given                                  it will default to the current hostname.                                   (default: &quot;logstash1&quot;)    -f, --path.config CONFIG_PATH Load the logstash config from a specific file                                  or directory.  If a directory is given, all                                  files in that directory will be concatenated                                  in lexicographical order and then parsed as a                                  single config file. You can also specify                                  wildcards (globs) and any matched files will                                  be loaded in the order described above.    -e, --config.string CONFIG_STRING Use the given string as the configuration                                  data. Same syntax as the config file. If no                                  input is specified, then the following is                                  used as the default input:                                  &quot;input { stdin { type =&gt; stdin } }&quot;                                  and if no output is specified, then the                                  following is used as the default output:                                  &quot;output { stdout { codec =&gt; rubydebug } }&quot;                                  If you wish to use both defaults, please use                                  the empty string for the &#39;-e&#39; flag.                                   (default: nil)</code></pre><p>&emsp;&emsp;不过常用的选项也就<code>-e</code>和<code>-f</code>，分别是通过命令行指定参数或者通过文件来指定配置参数。我们可以使用命令来测试</p><pre><code>/usr/share/logstash/bin/logstash -e &#39;input { stdin{} } output { stdout{ codec =&gt; rubydebug }}&#39; </code></pre><p><img src="https://img-blog.csdnimg.cn/20200103161950993.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="输出日志"><br>&emsp;&emsp;通过标准输入输入信息，并通过标准输出返回日志信息。同样，我们也可以调用input的file插件和output的file插件实现从文件中读取数据，或者写入文件。这样就可以实现对日志文件的抓取了。我们可以先尝试抓取系统日志如<code>syslog</code>。</p><pre><code>/usr/share/logstash/bin/logstash -e &#39;input { file { path =&gt; &quot;/var/log/syslog&quot;} } output { stdout{ codec =&gt; rubydebug }}&#39; </code></pre><p>&emsp;&emsp;哈，系统日志如果太多，估计会刷屏的。<br>&emsp;&emsp;不过刚才那些都只是一些基本用法而已，而实际生产中，我们肯定不能使用命令行来手动获取数据，我们需要的是一个可靠的服务，来帮我们自动抓取日志并筛选过滤，这就需要我们使用配置文件来设置了。<br>&emsp;&emsp;例如我们要做的抓取本机的nginx的访问日志、错误日志还有系统日志，并传递至之前配好的elasticsearch中。<br>&emsp;&emsp;那就在路径<code>/etc/logstash/conf.d/</code>目录下，创建一个新的配置文件，使用systemd启动时会自动读取conf.d下的配置文件。</p><pre><code>vim /etc/logstash/conf.d/nginx.confinput {  file {     path =&gt; &quot;/var/log/syslog&quot;    stat_interval =&gt; 3    start_position =&gt; &quot;beginning&quot;    type =&gt; &quot;syslog&quot;  }  file {     path =&gt; &quot;/apps/nginx/logs/access_json.log&quot;    stat_interval =&gt; 3    start_position =&gt; &quot;beginning&quot;    codec =&gt; &quot;json&quot;    type =&gt; &quot;nginx_accesslog&quot;  }  file {     path =&gt; &quot;/apps/nginx/logs/error.log&quot;    stat_interval =&gt; 3    start_position =&gt; &quot;beginning&quot;    type =&gt; &quot;nginx_errorlog&quot;  }}output {  if [type] == &quot;syslog&quot; {  elasticsearch {    hosts =&gt; [&quot;192.168.32.41:9200&quot;]    index =&gt; &quot;syslog-%{+YYYY.MM.dd}&quot;  }}  if [type] == &quot;nginx_accesslog&quot; {  elasticsearch {    hosts =&gt; [&quot;192.168.32.41:9200&quot;]    index =&gt; &quot;nginx_accesslog-%{+YYYY.MM.dd}  }}  if [type] == &quot;nginx_errorlog&quot; {  elasticsearch {    hosts =&gt; [&quot;192.168.32.41:9200&quot;]    index =&gt; &quot;nginx_accesslog-%{+YYYY.MM.dd}  }}}</code></pre><p>&emsp;&emsp;logstash支持条件判断，多输入以及多输出，设定type规则，来将每一类日志分类在不同的索引，且支持java的时间变量，可以实现根据日期归档每一天的日志，方便查看和统计。<br>&emsp;&emsp;我们可以使用命令来测试脚本的语法是否正确，如果不加<code>-t</code>可以直接以前台进程的方式启动logstash，不过会占据终端，但测试的时候还是蛮方便的。</p><pre><code>/usr/share/logstash/bin/logstash -f /etc/log/logstash/conf,d/nginx.conf -t</code></pre><p>&emsp;&emsp;不过仅仅是这样，是无法统计具体访问时间、访问ip及访问路径的详细信息的，我们需要将nginx的日志序列化，或者说是储存为json格式。<br>&emsp;&emsp;所以修改nginx的配置文件,将日志格式修改一下。</p><pre><code>http {    include       mime.types;    default_type  application/octet-stream;    #log_format  main  &#39;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &#39;    #                  &#39;$status $body_bytes_sent &quot;$http_referer&quot; &#39;    #                  &#39;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&#39;;    log_format access_json &#39;{&quot;@timestamp&quot;:&quot;$time_iso8601&quot;,&#39;        &#39;&quot;host&quot;:&quot;$server_addr&quot;,&#39;        &#39;&quot;clientip&quot;:&quot;$remote_addr&quot;,&#39;        &#39;&quot;size&quot;:$body_bytes_sent,&#39;        &#39;&quot;responsetime&quot;:$request_time,&#39;        &#39;&quot;upstreamtime&quot;:&quot;$upstream_response_time&quot;,&#39;        &#39;&quot;upstreamhost&quot;:&quot;$upstream_addr&quot;,&#39;        &#39;&quot;http_host&quot;:&quot;$host&quot;,&#39;        &#39;&quot;uri&quot;:&quot;$uri&quot;,&#39;        &#39;&quot;domain&quot;:&quot;$host&quot;,&#39;        &#39;&quot;xff&quot;:&quot;$http_x_forwarded_for&quot;,&#39;        &#39;&quot;referer&quot;:&quot;$http_referer&quot;,&#39;        &#39;&quot;tcp_xff&quot;:&quot;$proxy_protocol_addr&quot;,&#39;        &#39;&quot;http_user_agent&quot;:&quot;$http_user_agent&quot;,&#39;        &#39;&quot;status&quot;:&quot;$status&quot;}&#39;;    access_log  /apps/nginx/logs/access_json.log  access_json;</code></pre><p>&emsp;&emsp;PS:加入的属性名称不要有<code>type</code>，否则会影响到logstash做type判断。然后记得在配置文件中注明输入信息为json格式。看到如下信息，则说明日志被成功拆解。</p><pre><code>{    &quot;http_user_agent&quot; =&gt; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0&quot;,               &quot;path&quot; =&gt; &quot;/apps/nginx/logs/access_json.log&quot;,         &quot;@timestamp&quot; =&gt; 2020-01-03T08:17:47.000Z,       &quot;upstreamhost&quot; =&gt; &quot;-&quot;,                &quot;xff&quot; =&gt; &quot;-&quot;,       &quot;responsetime&quot; =&gt; 0.0,               &quot;size&quot; =&gt; 0,             &quot;status&quot; =&gt; &quot;304&quot;,          &quot;http_host&quot; =&gt; &quot;192.168.32.51&quot;,           &quot;clientip&quot; =&gt; &quot;192.168.32.1&quot;,             &quot;domain&quot; =&gt; &quot;192.168.32.51&quot;,            &quot;tcp_xff&quot; =&gt; &quot;&quot;,               &quot;host&quot; =&gt; &quot;192.168.32.51&quot;,           &quot;@version&quot; =&gt; &quot;1&quot;,                &quot;uri&quot; =&gt; &quot;/index.html&quot;,            &quot;referer&quot; =&gt; &quot;-&quot;,       &quot;upstreamtime&quot; =&gt; &quot;-&quot;}</code></pre><p>&emsp;&emsp;使用<code>systemctl enable --now logstash</code>启动logstash服务，过一会，日志就写入elasticsearch服务器中了，最好将<code>/etc/systemd/system/logstash.service</code>文件的中启动用户组都改为<code>root</code>，避免因为权限问题，导致无法读取数据。<br>&emsp;&emsp;我记得之前遇到过一次，命令行可以正常使用logstash，不过使用systemd启动就一直报错，<code>logstash: could not find java; set JAVA_HOME or ensure java is in PATH</code>,明明环境变量都是正常的，后来在<code>/usr/share/logstash/bin/logstash</code>脚本文件中加了一个<code>JAVA_HOME=/usr/local/jdk</code>环境变量就好了，而之后配的时候就没有遇到这个问题了，这就很奇怪了。</p><h3 id="Kibana"><a href="#Kibana" class="headerlink" title="Kibana"></a>Kibana</h3><p>&emsp;&emsp;日志信息都已经写到elasticsearch服务器中了，不过我们怎么才可以看到具体的日志信息呢？这就需要借助日志展示工具Kibana了。<br>&emsp;&emsp;虽然elasticsearch可视化工具也有不少，例如head、kopf、cerebro等等，不过他们都是监控elasticsearch集群状态的，对日志做展示分析的还是首推开源的官方组件Kibana。<br>&emsp;&emsp;同样下载kibana7.5.1版本</p><pre><code>https://artifacts.elastic.co/downloads/kibana/kibana-7.5.1-amd64.deb</code></pre><p>&emsp;&emsp;配置上也很简单，设置监听IP及端口，设置elasticsearch主机地址，最后再将语言环境改为中文就可以启动kibana服务了。</p><pre><code>root@kibana1:~# grep &quot;^[a-Z]&quot; /etc/kibana/kibana.ymlserver.port: 5601server.host: &quot;0.0.0.0&quot;elasticsearch.hosts: [&quot;http://192.168.32.41:9200&quot;]i18n.locale: &quot;zh-CN&quot;</code></pre><p>&emsp;&emsp;浏览器访问kibana主机的5601端口。<br>&emsp;&emsp;创建索引模式，依次添加索引。<br><img src="https://img-blog.csdnimg.cn/20200103175631933.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="kibana"><br>&emsp;&emsp;然后点左边第一个（最上面）的那个discover图标，就可以看到数据了。点击更改，选择对应的索引，还可以创建各种视图等一系列功能这里就不再详细讲解了。都比较简单。<br>&emsp;&emsp;至此ELK的初步部署就算完成了。</p>]]></content>
    
    <summary type="html">
    
      ELK是Elasticsearch、Logstash、Kibana的简称，这三者是核心套件，但并非全部。 Elasticsearch是实时全文搜索和分析引擎，提供搜集、分析、存储数据三大功能；是一套开放REST和JAVA API等结构提供高效搜索功能，可扩展的分布式系统。它构建于Apache Lucene搜索引擎库之上。 Logstash是一个用来搜集、分析、过滤日志的工具。它支持几乎任何类型的日志，包括系统日志、错误日志和自定义应用程序日志。它可以从许多来源接收日志，这些来源包括 syslog、消息传递（例如 RabbitMQ）和JMX，它能够以多种方式输出数据，包括电子邮件、websockets和Elasticsearch。
    
    </summary>
    
    
      <category term="linux" scheme="https://hewanyue.com/categories/linux/"/>
    
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="ELK" scheme="https://hewanyue.com/tags/ELK/"/>
    
      <category term="Elasticsearch" scheme="https://hewanyue.com/tags/Elasticsearch/"/>
    
      <category term="Logstash" scheme="https://hewanyue.com/tags/Logstash/"/>
    
      <category term="Kibana" scheme="https://hewanyue.com/tags/Kibana/"/>
    
  </entry>
  
  <entry>
    <title>企业级应用——DevOps（一）gitlab的部署与配置</title>
    <link href="https://hewanyue.com/blog/73a033d9.html"/>
    <id>https://hewanyue.com/blog/73a033d9.html</id>
    <published>2019-12-26T14:28:34.000Z</published>
    <updated>2020-01-20T07:29:36.379Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;在企业生产中，DEVOPS这个概念越来越火了，不同公司对此都有不同的理解，但有一点毋庸置疑，提到DEVOPS都绕不开CI/CD。CI是continuous integration的简称，意为持续集成，CD是continuous deployment或者Continuous Delivery的缩写，意为持续部署或持续交付。</p><a id="more"></a><p>&emsp;&emsp;持续集成是指多名开发者在开发不同功能代码的过程当中，可以频繁的将代码行合并到一起并切相互不影响工作。<br>&emsp;&emsp;持续部署是指是基于某种工具或平台实现代码自动化的构建、测试和部署到线上环境以实现交付高质量的产品。持续部署在某种程度上代表了一个开发团队的更新迭代速率。<br>&emsp;&emsp;持续交付是在持续部署的基础之上， 将产品交付到线上环境， 因此持续交付是产品价值的一种交付， 是产品价值的一种盈利的实现。<br>&emsp;&emsp;可以用一个类似戴明环的图来比较形象展示这种生产模式结构。<br><img src="https://img-blog.csdnimg.cn/20191226203217874.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="DevOps"><br>&emsp;&emsp;比较常用到的开源软件有gitlab、Maven、jenkins、saltstack、slastic以及zabbix。其中gitlab和jenkins是最常用的一个组合，也是目前最为火热的结局方案。本文将详细介绍gitlab的部署及使用。</p><h2 id="Gitlib—分布式版本控制系统"><a href="#Gitlib—分布式版本控制系统" class="headerlink" title="Gitlib—分布式版本控制系统"></a>Gitlib—分布式版本控制系统</h2><h3 id="持续集成开源工具"><a href="#持续集成开源工具" class="headerlink" title="持续集成开源工具"></a>持续集成开源工具</h3><p>&emsp;&emsp;我们需要在公司的服务器安装某种程序，该程序用于按照特定格式和方式记录和保存公司多名开发人员不定期提交的源代码，且后期可以按照某种标记及方式对用户提交的数据进行还原，这是我们就需要用到版本控制系统，也就是所谓的持续集成工具。<br>&emsp;&emsp;早期的集中式版本控制系统如CVS(Concurrent Version System)，现已基本淘汰，可能有些公司还在使用SVN(Subversion)作为版本控制系统，不过他的缺点相当明显，集中式管理，太依赖于网络带宽，当大家一起从管理服务器拉代码或提交代码时，遇到网速慢的话，可能提交一个10M的文件就需要5分钟，效率很差。<br>&emsp;&emsp;而gitlab是分布式的版本控制系统，不存在“中央服务器”，每个人的电脑上都是一个完整的版本库，这样，你工作需要提交或者回滚时，就不需要联网了，因为版本库就在你自己的电脑上。需要汇总时，我们将代码提交至gitlab服务器，将每个人的劳动成果也就是分支仓库的代码合并即可。</p><h3 id="部署gitlab"><a href="#部署gitlab" class="headerlink" title="部署gitlab"></a>部署gitlab</h3><p>&emsp;&emsp;具体安装要求及配置可以参考官方文档<br>&emsp;&emsp;<a href="https://about.gitlab.com/install/" target="_blank" rel="external nofollow noopener noreferrer">https://about.gitlab.com/install/</a> # Gitlab 服务的安装文档<br>&emsp;&emsp;<a href="https://docs.gitlab.com/ce/install/requirements.html" target="_blank" rel="external nofollow noopener noreferrer">https://docs.gitlab.com/ce/install/requirements.html</a> #安装环境要求</p><p>&emsp;&emsp;最好先配置好依赖仓库源，可以配置阿里的镜像仓库源。<br>&emsp;&emsp;官方下载deb安装包比较慢，可以使用清华大学的镜像源下载。</p><p>&emsp;&emsp;我们这里使用gitlab-ce_11.11.8来演示。也可以去下载其他版本，ubuntu 国内下载地址： <a href="https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/" target="_blank" rel="external nofollow noopener noreferrer">https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/</a>。</p><pre><code>wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/bionic/main/g/gitlab-ce/gitlab-ce_11.11.8-ce.0_amd64.debdpkg -i gitlab-ce_11.11.8-ce.0_amd64.deb</code></pre><p>&emsp;&emsp;安装可能需要一段时间，耐心等待。<br>&emsp;&emsp;然后修改gitlab的配置文件</p><pre><code class="shell">root@gitlabUbuntu24:~# grep &quot;^[a-Z ]&quot; /etc/gitlab/gitlab.rbexternal_url &#39;http://192.168.32.24&#39; gitlab_rails[&#39;gitlab_email_from&#39;] = &#39;example@qq.com&#39; gitlab_rails[&#39;smtp_enable&#39;] = true gitlab_rails[&#39;smtp_address&#39;] = &quot;smtp.qq.com&quot; gitlab_rails[&#39;smtp_port&#39;] = 465 gitlab_rails[&#39;smtp_user_name&#39;] = &quot;example@qq.com&quot; gitlab_rails[&#39;smtp_password&#39;] = &quot;keyword&quot; gitlab_rails[&#39;smtp_domain&#39;] = &quot;qq.com&quot; gitlab_rails[&#39;smtp_authentication&#39;] = &quot;login&quot; gitlab_rails[&#39;smtp_enable_starttls_auto&#39;] = true gitlab_rails[&#39;smtp_tls&#39;] = true user[&#39;git_user_email&#39;] = &quot;example@#qq.com&quot; alertmanager[&#39;admin_email&#39;] = &#39;example@qq.com&#39;</code></pre><p>&emsp;&emsp;将上面这些选项都设置好。例如使用的是QQ邮箱，则需要去邮箱设置里获取授权码，填在<code>password</code>处。因为gitlab是一般是通过邮件来注册和获取密码的，所以这些配置都要有，邮箱最好填企业邮箱或者公司领导邮箱，以避免员工离职等其他风险。<br>然后使用命令让配置文件生效。</p><pre><code class="shell">    gitlab-ctl reconfigure </code></pre><p>&emsp;&emsp;这时就可以通过命令<code>gitlab-ctl start</code>启动gitlab了。<br>&emsp;&emsp;可以通过<code>gitlab-ctl status</code>命令查看gitlab运行状态，或者后跟组件名称查看具体组件的运行状态。</p><h3 id="gitlab-相关的目录："><a href="#gitlab-相关的目录：" class="headerlink" title="gitlab 相关的目录："></a>gitlab 相关的目录：</h3><pre><code class="shell">    /etc/gitlab #配置文件目录    /run/gitlab #运行 pid 目录    /opt/gitlab #安装目录    /var/opt/gitlab #数据目录</code></pre><h3 id="gitlab常用命令"><a href="#gitlab常用命令" class="headerlink" title="gitlab常用命令"></a>gitlab常用命令</h3><p>&emsp;&emsp;gitlab用法与github几乎相同，这里就不再详细介绍。</p><pre><code class="shell">git config --global user.name “name“ #设置全局用户名git config --global user.email xxx@xx.com #设置全局邮箱git config --global --list #列出用户全局设置git add index.html / . #添加指定文件、 目录或当前目录下所有数据到暂存区git commit -m “11“ #提交文件到工作区git status #查看工作区的状态git push #提交代码到服务器git pull #获取代码到本地git log #查看操作日志vim .gitignore #定义忽略文件git reset --hard HEAD^^ #git 版本回滚， HEAD 为当前版本，加一个^为上一个， ^^为上上一个版本git reflog # #获取每次提交的 ID，可以使用--hard 根据提交的 ID 进行版本回退git reset --hard 5ae4b06 #回退到指定 id 的版本git branch #查看当前所处的分支git checkout -b develop #创建并切换到一个新分支git checkout develop #切换分支</code></pre><h3 id="gitlab数据备份"><a href="#gitlab数据备份" class="headerlink" title="gitlab数据备份"></a>gitlab数据备份</h3><p>&emsp;&emsp;gitlab可以通过命令<code>gitlab-rake</code>来备份数据。数据备份前，需要停止<code>unicorn</code>、<code>sidekiq</code>组件服务。</p><pre><code class="shell">gitlab-ctl stop unicorngitlab-ctl stop sidekiqgitlab-rake gitlab:backup:create #在任意目录即可备份当前 gitlab 数据gitlab-ctl start #备份完成后启动 gitlab</code></pre><p>&emsp;&emsp;备份完成的数据储存在<code>/var/opt/gitlab/backups/</code>路径下,生成与时间相关的tar包。</p><pre><code class="shell">root@gitlabUbuntu24:~# ll /var/opt/gitlab/backups/total 4600drwx------  2 git  root    4096 Dec 25 14:41 ./drwxr-xr-x 20 root root    4096 Dec 25 11:02 ../-rw-------  1 git  git  4700160 Dec 25 14:41 1577256108_2019_12_25_11.11.8_gitlab_backup.tar</code></pre><p>&emsp;&emsp;需要注意的是，命令备份仅仅是备份了gitlab中的数据，通常我们我们的备份脚本中还需要备份以下文件。</p><pre><code class="shell">/var/opt/gitlab/nginx/conf #nginx 配置文件/etc/gitlab/gitlab.rb #gitlab 配置文件/etc/gitlab/gitlab-secrets.json #key 文件</code></pre><p>&emsp;&emsp;使用命令<code>gitlab-rake gitlab:backup:restore BACKUP=备份文件名</code>可以恢复之前的备份数据（文件名到时间戳即可）。同样，执行恢复命令之前也要先关闭<code>unicorn</code>、<code>sidekiq</code>组件服务。恢复时，需要输入yes确认。</p><pre><code class="shell">gitlab-ctl stop unicorngitlab-ctl stop sidekiqgitlab-rake gitlab:backup:restore BACKUP=1577256108_2019_12_25_11.11.8</code></pre><p><img src="https://img-blog.csdnimg.cn/20191226215657234.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="gitlab"></p><h3 id="gitlab的关闭注册"><a href="#gitlab的关闭注册" class="headerlink" title="gitlab的关闭注册"></a>gitlab的关闭注册</h3><p>&emsp;&emsp;服务正常启动之后，就可以访问主机的IP或者域名，登陆gitlab系统了。gitlab和github极为相似，也支持邮箱注册。，第一次访问，会放我们设置管理员账户密码，我们使用管理员账户登录，用户名为root。<br>&emsp;&emsp;gitlab默认是开放注册的，而我们作为企业内部使用的版本管理系统，肯定不希望大家随便创建用户来访问，这样很不便于管理。<br>&emsp;&emsp;我们可以在点击中间的小扳手图标，选择setting设置，将sign-up功能取消，这样登录界面就无法注册了。<br><img src="https://img-blog.csdnimg.cn/20191226220453316.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;注意，小心别关错了，要是把sign-in功能取消就没法登陆了，哪怕使用root权限账号退出去也就没法登陆了。如果真的不幸将gitlab的sign-in功能取消掉，那只能使用命令行的方式修改数据库中设置了。<br>&emsp;&emsp;因为gitlab目前版本默认使用的psql，所以修改数据库的方式，跟mysql不同，具体如下<br>先切换至gitlab-psql用户。</p><pre><code class="shell">su - gitlab-psql</code></pre><p>&emsp;&emsp;使用 -h指定数据库路径 -d指定数据库，连接psql数据库。</p><pre><code class="SQL">psql -h /var/opt/gitlab/postgresql -d gitlabhq_productionUPDATE application_settings SET password_authentication_enabled_for_web=true;\q</code></pre><p>&emsp;&emsp;修改完数据后，重启gitlab服务。</p><pre><code class="shell">exitgitlab-ctl restart</code></pre><p>&emsp;&emsp;此时，web界面就又可以登陆了。</p><h3 id="gitlab的中文汉化"><a href="#gitlab的中文汉化" class="headerlink" title="gitlab的中文汉化"></a>gitlab的中文汉化</h3><p>&emsp;&emsp;gitlab默认语音为英语，有时有些专业数据或者不太常用的选项配置我们可能无法准确理解它的含义，所以将gitlab汉化成中文还是有一定必要的。<br>&emsp;&emsp;gitlab的每个用户，可以点击用户头像，设置，设置偏好，选择语音。<img src="https://img-blog.csdnimg.cn/20191227101448871.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;选择中文之后刷新，就可看到一部分菜单变为了中文。但是也仅仅是菜单变成了中文而已，很多选项和图标都还是英文显示，如果想彻底汉化为中文，则需要我们去下载中文汉化补丁了。<br>&emsp;&emsp;汉化补丁是第三方爱好者提供，github地址是<a href="https://gitlab.com/xhang/gitlab" target="_blank" rel="external nofollow noopener noreferrer">https://gitlab.com/xhang/gitlab</a>，找到对应版本的gitlab汉化包。<br>&emsp;&emsp;gitlabv11.11.8汉化包的下载地址如下</p><pre><code class="shell">wget https://gitlab.com/xhang/gitlab/-/archive/v11.11.8-zh/gitlab-v11.11.8-zh.tar.gz</code></pre><p>&emsp;&emsp;然后先停止gitlab</p><pre><code class="shell">gitlab-ctl stop</code></pre><p>&emsp;&emsp;备份并替换文件</p><pre><code class="shell">tar xvf gitlab-v11.11.8-zh.tar.gzcp -rp /opt/gitlab/embedded/service/gitlab-rails /opt/gitlab-rails.bakcp -rf gitlab-v11.11.8-zh/* /opt/gitlab/embedded/service/gitlab-rails/</code></pre><p>&emsp;&emsp;此时，汉化就完成了，可以直接启动gitlab了。就可以看到页面都变成了中文了，效果图如下。<br><img src="https://img-blog.csdnimg.cn/20191227103139614.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="gitlab仪表盘"></p>]]></content>
    
    <summary type="html">
    
      在企业生产中，DEVOPS这个概念越来越火了，不同公司对此都有不同的理解，但有一点毋庸置疑，提到DEVOPS都绕不开CI/CD。CI是continuous integration的简称，意为持续集成，CD是continuous deployment或者Continuous Delivery的缩写，意为持续部署或持续交付。
    
    </summary>
    
    
      <category term="linux" scheme="https://hewanyue.com/categories/linux/"/>
    
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="DevOps" scheme="https://hewanyue.com/tags/DevOps/"/>
    
      <category term="CI/CD" scheme="https://hewanyue.com/tags/CI-CD/"/>
    
      <category term="gitlab" scheme="https://hewanyue.com/tags/gitlab/"/>
    
  </entry>
  
  <entry>
    <title>企业级应用——监控（一）zabbix的部署</title>
    <link href="https://hewanyue.com/blog/366b680c.html"/>
    <id>https://hewanyue.com/blog/366b680c.html</id>
    <published>2019-12-20T09:34:00.000Z</published>
    <updated>2020-01-20T07:29:36.386Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;常见的开源监控软件有：cacti、nagios、zabbix、smokeping、open-falcon等，本文主要介绍目前使用较多的开源监控软件zabbix，针对容器环境的开源监控软件Prometheus下次再讲解。</p><a id="more"></a><p>&emsp;&emsp;zabbix功能强大，可横向扩展、自定义监控项、支持多种监控方式、可监控网络与服务等。</p><h2 id="zabbix功能简述"><a href="#zabbix功能简述" class="headerlink" title="zabbix功能简述"></a>zabbix功能简述</h2><ul><li>数据采集<br>zabbix是周期性采集时序数据。<br>&emsp;&emsp;采集对象可以有：服务器、路由器、交换机、存储、防火墙、IP、PORT、URL、自定义监控对象…<br>&emsp;&emsp;采集目标：监控项，指标数据（metrics data）<br>&emsp;&emsp;数据采集方式：zabbix-server，zabbix-proxy，zabbix-agent<br>&emsp;&emsp;按照有无代理分类：<br>&emsp;&emsp;&emsp;&emsp;Agentless：SNMP,Telnet,ssh, IPMI, JMX,<br>&emsp;&emsp;&emsp;&emsp;Agent：zabbix agent</li><li>数据存储<br>可以储存历史数据和局势数据，还有阈值。<br>&emsp;&emsp;历史数据: 每个监控项采集到的每个监控值<br>&emsp;&emsp;趋势数据: 趋势表里主要保留某个监控项一个小时内历史数据的最大值、最小值和平均值以及该监控项一个小时内所采集到的数据个数。<br>&emsp;&emsp;阈值：可按照预定义的阈值等级实现分层报警<br>支持的数据库类型有：<br>&emsp;&emsp;SQL: MySQL/MariaDB(Zabbix)<br>&emsp;&emsp;NoSQL：Redis(Open-falcon)<br>&emsp;&emsp;rrd: Round Robin Database(Cacti)</li><li>数据展示<br>可以使用原生的zabbix web界面可以展示graph -&gt; screen -&gt; slideshow(将多个screen以幻灯片的方式进行轮流展示)<br>还支持以zabbix为数据源，在grafana展示更绚丽的界面。</li><li>报警通知<br>支持email,短信,微信,语音等多种方式报警通知，也可以实现故障自治愈。<br>host (host groups) &lt;- templates #从模板继承告警配置<br>host -&gt; items -&gt; triggers -&gt; action (条件-conditions, 操作-operations) #自定义告警配置<h2 id="zabbix架构"><a href="#zabbix架构" class="headerlink" title="zabbix架构"></a>zabbix架构</h2>&emsp;&emsp;在zabbix服务中，一般都包含有<code>zabbix-server</code>、<code>zabbix-agent</code>、<code>zabbix-proxy</code>，及数据库，结构如下图所示：<br><img src="https://img-blog.csdnimg.cn/20191219204138569.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="zabbix结构示意图"><br>&emsp;&emsp;一般都需要在被监控端安装<code>zabbix-agent</code>服务来抓取数据，然后汇总到<code>zabbix-server</code> 端来展示分析监控报警，如果agent过多或者可能不同机房的数据，可以通过<code>zabbix-proxy</code>来暂存收集数据，之后在汇总至<code>zabbix-server</code>端，所以<code>zabbix-proxy</code>端及<code>zabbix-server</code> 端都需要一个mysql数据库来储存即时及历史监控数据的（zabbix-proxy处临时储存）。而且整个体系中最重要的就是数据库了，数据都在数据库中，只要数据库中的数据不丢失，重建一个zabbix监控架构还是比较容易的，所以可以对数据库做主从复制高可用，可参见<a href="https://blog.csdn.net/MicePro/article/details/101352554" target="_blank" rel="external nofollow noopener noreferrer">之前文章</a>。<h2 id="zabbix部署"><a href="#zabbix部署" class="headerlink" title="zabbix部署"></a>zabbix部署</h2><h3 id="zabbix-server"><a href="#zabbix-server" class="headerlink" title="zabbix server"></a>zabbix server</h3><h4 id="包管理工具安装"><a href="#包管理工具安装" class="headerlink" title="包管理工具安装"></a>包管理工具安装</h4>&emsp;&emsp;我们先来搭建<code>zabbix-server</code>端。<br>&emsp;&emsp;对于Ubuntu系统：<br>先下载配置镜像仓库,并apt安装<code>zabbix-server-mysql</code> 、<code>zabbix-frontend-php</code> 及<code>zabbix-agent</code>，其中zabbix-server的二进制程序及启动配置文件都在在<code>zabbix-server-mysql</code>安装包里。<pre><code>wget https://repo.zabbix.com/zabbix/4.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.0-3%2Bbionic_all.debdpkg -i zabbix-release_4.0-3+bionic_all.debapt updateapt -y install zabbix-server-mysql zabbix-frontend-php zabbix-agent</code></pre>&emsp;&emsp;然后开始安装zabbix-server的数据库。可以与zabbix-server复用一台主机，也可以单独一台主机。我们这里使用一台新的主机,apt安装好mariadb（与mysql操作一样）。<br>&emsp;&emsp;先以root身份登陆mysql主机，然后为zabbix创建一个数据库，例如zabbix_server，再创建一个zabbix用户，并授权<pre><code>MariaDB [mysql]&gt; create database zabbix_server character set utf8 collate utf8_bin;MariaDB [mysql]&gt;grant all privileges on zabbix_server.* to zabbix@&quot;192.168.32.%&quot; identified by &quot;zabbix&quot;;MariaDB [mysql]&gt;flush privileges;</code></pre>&emsp;&emsp;然后回到zabbix-server主机，安装一个mysql客户端，并测试可否用之前创建的zabbix账号登录数据库。<pre><code>apt mysql-client -ymysql -uzabbix -pzabbix -h192.168.32.20</code></pre>&emsp;&emsp;确保可以登陆之后，导入数据库表结构<pre><code>zcat /usr/share/doc/zabbix-server-mysql/create.sql.gz | mysql -uzabbix -pzabbix -h192.168.32.20 zabbix_server</code></pre>&emsp;&emsp;此时在登入数据库服务器，使用命令查询，可以看到已经生成了很多表<pre><code>mysql&gt; show tables from zabbix_server;</code></pre>&emsp;&emsp;然后编辑zabbix配置文件<pre><code>vim /etc/zabbix/zabbix_server.conf</code></pre>&emsp;&emsp;修改数据库相关信息，其他的可以不做修改。<pre><code>root@DockerUbuntu19:~# grep &quot;^[a-Z]&quot; /etc/zabbix/zabbix_server.confLogFile=/var/log/zabbix/zabbix_server.logLogFileSize=1PidFile=/var/run/zabbix/zabbix_server.pidSocketDir=/var/run/zabbixDBHost=192.168.32.20DBName=zabbix_serverDBUser=zabbixDBPassword=zabbixStartTrappers=10Timeout=30AlertScriptsPath=/usr/lib/zabbix/alertscriptsExternalScripts=/usr/lib/zabbix/externalscriptsFpingLocation=/usr/bin/fpingFping6Location=/usr/bin/fping6LogSlowQueries=3000</code></pre>&emsp;&emsp;重启zabbix服务。<pre><code>systemctl restart zabbix-server zabbix-agent apache2</code></pre>&emsp;&emsp;此时访问zabbix-server主机对应的IP或者域名,加上路径<code>zabbix/setup.php</code>就可以访问zabbix的web界面进行检查配置了。<br>在环境检查时，可能会有一项报错，提示<code>PHP option date.timezone</code>检查unkown。虽然不影响服务启动，不过我们最好还是将他解决掉。<pre><code>vim /etc/zabbix/apache.conf</code></pre>&emsp;&emsp;搜索<code>timezone</code>,将值改为<code>php_value date.timezone Asia/Shanghai</code>。注意，有两个timezone设置，分别是针对PHP5.版本和PHP7.版本，我们修改7版本的就可以了，也可以都修改。然后重启服务。<pre><code>systemctl restart zabbix-server zabbix-agent apache2</code></pre>&emsp;&emsp;此时，再刷新一下网页，就可以看到所有检查都是OK状态了。正确填写数据库信息和server主机信息，点击Finish，配置就完成了，会自动跳转至登录界面，默认用户名为<code>Admin</code>，默认密码为<code>zabbix</code>。</li></ul><h4 id="编译安装zabbix"><a href="#编译安装zabbix" class="headerlink" title="编译安装zabbix"></a>编译安装zabbix</h4><p>&emsp;&emsp;源码编译安装zabbix的话,先下载源码包。<br>&emsp;&emsp;下载路径为<a href="https://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/" target="_blank" rel="external nofollow noopener noreferrer">https://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/</a>。<br>&emsp;&emsp;我们这里以4.015版本为例</p><pre><code>cd /usr/local/srcwget https://jaist.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/4.0.15/zabbix-4.0.15.tar.gztar xf zabbix-4.0.15.tar.gzcd zabbix-4.0.15/</code></pre><p>&emsp;&emsp;编译安装需要我们自己来创建zabbix用户组。</p><pre><code>groupadd -g 1111 zabbix #创建zabbix用户和组useradd -u 1111 -g 1111 zabbix</code></pre><p>&emsp;&emsp;安装相关依赖的安装包<br>&emsp;&emsp;CentOS:</p><pre><code>yum install gcc libxml2-devel net-snmp net-snmp-devel curl curl-devel php phpbcmath php-mbstring mariadb mariadb-devel -y</code></pre><p>&emsp;&emsp;Ubuntu:</p><pre><code>apt updateapt-get install apache2 apache2-bin apache2-data apache2-utils fontconfig-config fonts-dejavu-core fping libapache2-mod-php libapache2-mod-php7.2 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libfontconfig1 libgd3 libiksemel3 libjbig0 libjpeg-turbo8 libjpeg8 liblua5.2-0 libodbc1 libopenipmi0 libsensors4 libsnmp-base libsnmp30 libsodium23 libssh2-1 libtiff5 libwebp6 libxpm4 php-bcmath php-common php-gd php-ldap php-mbstring php-mysql php-xml php7.2-bcmath php7.2-cli php7.2-common php7.2-gd php7.2-json php7.2-ldap php7.2-mbstring php7.2-mysql php7.2-opcache php7.2-readline php7.2-xml snmpd ssl-cert ttf-dejavu-core libmysqlclient-dev libxml2-dev libxml2 snmp libsnmp-dev libevent-dev openjdk-8-jdk curl libcurl4-openssl-dev</code></pre><p>&emsp;&emsp;然后就可以编译并安装了。</p><pre><code>./configure --prefix=/apps/zabbix_server \--enable-server --enable-agent --with-mysql \--with-net-snmp --with-libcurl --with-libxml2 --enable-javamake install</code></pre><p>&emsp;&emsp;之后流程就与之前一样。</p><h4 id="设置中文web页面及乱码问题"><a href="#设置中文web页面及乱码问题" class="headerlink" title="设置中文web页面及乱码问题"></a>设置中文web页面及乱码问题</h4><p>如果Ubuntu系统安装时为选择中文语言，则web界面大概率是英文界面，可能会对我们的使用有一定影响。<br>点击右上角的用户头像标志，可以选择语音。不过如果系统没有安装中文时，是无法选择中文的。<br><img src="https://img-blog.csdnimg.cn/20191219222043682.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;所以需要我们在ubuntu系统中安装并设置中文简体语言环境.。<br>&emsp;&emsp;1、安装简体中文语言环境</p><pre><code>apt-get install language-pack-zh*</code></pre><p>&emsp;&emsp;2、增加中文语言环境变量</p><pre><code>vim /etc/environmentPATH=&quot;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games&quot;LANG=&quot;zh_CN.UTF-8&quot;</code></pre><p>&emsp;&emsp;3、重新设置本地配置</p><pre><code>dpkg-reconfigure locales</code></pre><p>&emsp;&emsp;此时刷新网页，就可以选择中文<code>Chinese(zh_CN)</code>了，点Update修改保存。此时就可以看到界面变成中文了。不过这时，在图形等界面，大概率会出现字符无法显示的乱码情况，这是由于当前系统有些监控项部分显示有乱码，使由于web界面显示为中文但是系统没有相关字体支持，因此需要相关字体的支持才能正常显示。这时需要我们自己配置一个字体并修改zabbix的font设置。<br>&emsp;&emsp;可以去网上下载字体,也可以从windows中获取已有字体。<br>&emsp;&emsp;在windows中路径为<code>控制面板\外观和个性化\字体</code>（复制至地址栏就可以找到了）<br><img src="https://img-blog.csdnimg.cn/20191220100745893.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="字体"><br>&emsp;&emsp;不过需要注意的是，里面有的字体是otf格式，zabbix无法识别，要选择ttf格式的字体才可以。<br><img src="https://img-blog.csdnimg.cn/20191220101154993.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="otf"><br><img src="https://img-blog.csdnimg.cn/20191220101209572.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="ttf"><br>&emsp;&emsp;将选好的或者下载好的ttf格式字体，拷贝至<code>zabbix-server</code>主机的zabbix安装目录下的fonts目录里。<br>&emsp;&emsp;如果是ubuntu通过apt安装，则为<code>/usr/share/zabbix/assets/fonts</code>，如果是编译安装则可通过<code>find</code>命令搜索一下<code>find /PATH(安装路径) -name fonts</code>。<br>&emsp;&emsp;可以看到里面已经有一个字体文件了的（虽然是个软链接）。</p><pre><code>root@DockerUbuntu19:~# ll /usr/share/zabbix/assets/fonts总用量 11520drwxr-xr-x 2 root root     4096 12月 18 18:59 ./drwxr-xr-x 5 root root     4096 12月 18 17:43 ../lrwxrwxrwx 1 root root       38 12月 18 17:44 graphfont.ttf -&gt; /etc/alternatives/zabbix-frontend-font-rw-r--r-- 1 root root 11785184 12月 18 18:59 simkai.ttf</code></pre><p>&emsp;&emsp;将我们准备好的ttf文件拷贝至此目录，修改zabbix环境变量配置文件(apt安装路径为/usr/share/zabbix/include/defines.inc.php)</p><pre><code>cd /usr/share/zabbix/vim include/defines.inc.php</code></pre><p>&emsp;&emsp;搜索替换<code>graphfont</code>为我们期望的字体名称。一共有两处。都替换了即可。</p><pre><code>#define(&#39;ZBX_GRAPH_FONT_NAME&#39;,          &#39;graphfont&#39;); // font file namedefine(&#39;ZBX_GRAPH_FONT_NAME&#39;,           &#39;simkai&#39;); // font file name- - -#define(&#39;ZBX_FONT_NAME&#39;, &#39;graphfont&#39;);define(&#39;ZBX_FONT_NAME&#39;, &#39;simkai&#39;);</code></pre><p>&emsp;&emsp;不需要重启zabbix及apache，修改后的字体文件即可直接生效。<br>&emsp;&emsp;至此zabbix-server端的配置就完成了。</p><h3 id="zabbix-proxy"><a href="#zabbix-proxy" class="headerlink" title="zabbix proxy"></a>zabbix proxy</h3><h4 id="zabbix-proxy对比zbbbix-server"><a href="#zabbix-proxy对比zbbbix-server" class="headerlink" title="zabbix proxy对比zbbbix server"></a>zabbix proxy对比zbbbix server</h4><table><thead><tr><th align="center">功能</th><th align="center">zabbxy proxy</th><th align="center">zabbix server</th></tr></thead><tbody><tr><td align="center">轻量级</td><td align="center">是</td><td align="center">相对重量级</td></tr><tr><td align="center">图形</td><td align="center">无</td><td align="center">带图形控制界面</td></tr><tr><td align="center">可以独立工作</td><td align="center">是，可以独立采集数据并存储</td><td align="center">是，即数据采集、存储、分析、展示于一体</td></tr><tr><td align="center">易维护</td><td align="center">是，配置完成后基本无需管理</td><td align="center">维护也不难</td></tr><tr><td align="center">独立数据库</td><td align="center">保留少量最近数据</td><td align="center">保留指定时间内的所有数据</td></tr><tr><td align="center">报警通知</td><td align="center">否，代理服务器不发送邮件通知</td><td align="center">支持邮件、短信等告警机制</td></tr></tbody></table><h4 id="zabbix-proxy版本选择"><a href="#zabbix-proxy版本选择" class="headerlink" title="zabbix proxy版本选择"></a>zabbix proxy版本选择</h4><p>&emsp;&emsp;zabbix proxy的大版本必须要和zabbix server版本一致，否则会导致出现zabbix server与zabbix proxy不兼容问题，会提示报错：</p><pre><code>proxy &quot;zabbix-proxy-active&quot; protocol version 3.2 differs from server version 4.0</code></pre><p>&emsp;&emsp;确认下zabbix-server的版本</p><pre><code>root@DockerUbuntu19:/usr/share/zabbix/assets/fonts# zabbix_server -Vzabbix_server (Zabbix) 4.0.15Revision 992445e02c 25 November 2019, compilation time: Nov 25 2019 09:01:31Copyright (C) 2019 Zabbix SIALicense GPLv2+: GNU GPL version 2 or later &lt;http://gnu.org/licenses/gpl.html&gt;.This is free software: you are free to change and redistribute it according tothe license. There is NO WARRANTY, to the extent permitted by law.This product includes software developed by the OpenSSL Projectfor use in the OpenSSL Toolkit (http://www.openssl.org/).Compiled with OpenSSL 1.1.0g  2 Nov 2017Running with OpenSSL 1.1.1  11 Sep 2018</code></pre><p>&emsp;&emsp;所以我们最好也安装相同版本的zabbix-proxy。</p><h4 id="zabbix-proxy部署"><a href="#zabbix-proxy部署" class="headerlink" title="zabbix proxy部署"></a>zabbix proxy部署</h4><p>&emsp;&emsp;因为proxy也需要一个数据库，可以选择复用server端的数据库，或者再另外创建一个。我们这里复用之前server端的数据库。<br>&emsp;&emsp;所以在之前的MariaDB数据库中，新建一个库，并创建对应权限用户。</p><pre><code>MariaDB [(none)]&gt; create database zabbix_proxy;Query OK, 1 row affected (0.01 sec)MariaDB [(none)]&gt; grant all privileges on zabbix_proxy.* to proxy@&quot;172.18.32.%&quot; identified by &quot;proxy&quot;;Query OK, 0 rows affected (0.10 sec)</code></pre><p>&emsp;&emsp;回到proxy主机，然后去官方镜像仓库<a href="https://repo.zabbix.com/zabbix/4.0/" target="_blank" rel="external nofollow noopener noreferrer">https://repo.zabbix.com/zabbix/4.0/</a>找到对应系统、对应版本的安装包路径<br>&emsp;&emsp;CentOS：</p><pre><code>yum install https://repo.zabbix.com/zabbix/4.0/rhel/7/x86_64/zabbix-proxy-mysql-4.0.15-1.el7.x86_64.rpm</code></pre><p>&emsp;&emsp;Ubuntu：</p><pre><code>wget https://repo.zabbix.com/zabbix/4.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.0-3%2Bbionic_all.debdpkg -i zabbix-release_4.0-3+bionic_all.debapt updateapt install zabbix-proxy-mysql</code></pre><p>&emsp;&emsp;导入proxy的数据库表（CentOS和Ubuntu路径可能不同，不过名字都叫schema.sql.gz）</p><pre><code>zcat /usr/share/doc/zabbix-proxy-mysql-4.0.15/schema.sql.gz | mysql -uproxy -pproxy -h192.168.32.20 zabbix_proxy</code></pre><p>&emsp;&emsp;修改配置文件</p><pre><code>vim /etc/zabbix/zabbix_proxy.confProxyMode=1 #0为主动，1为被动Server=192.168.32.19 #zabbix server服务器的地址或主机名Hostname=Zabbix proxy #代理服务器名称，需要与zabbix server添加代理时候的proxyname是一致的！ListenPort=10051 #zabbix proxy监听端口LogFile=/var/log/zabbix/zabbix_proxy.logEnableRemoteCommands=1 #允许zabbix server执行远程命令PidFile=/var/run/zabbix/zabbix_proxy.pidSocketDir=/var/run/zabbixDBHost=192.168.32.20 #数据库服务器地址DBName=zabbix_proxy #使用的数据库名称DBUser=proxy #连接数据库的用户名称DBPassword=proxy #数据库用户密码DBPort=3306 #数据库端口ProxyLocalBuffer=720 #已经提交到zabbix server的数据保留时间ProxyOfflineBuffer=720 #未提交到zabbix server的时间保留时间HeartbeatFrequency=60 #心跳间隔检测时间，默认60秒，范围0-3600秒，被动模式不使用ConfigFrequency=5 #间隔多少秒从zabbix server获取监控项信息DataSenderFrequency=5 #数据发送时间间隔，默认为1秒，范围为1-3600秒，被动模式不使用StartPollers=20 #启动的数据采集器数量CacheSize=2G #保存监控项而占用的最大内存HistoryCacheSize=2G #保存监控历史数据占用的最大内存HistoryIndexCacheSize=128M #历史索引缓存的大小Timeout=30 #监控项超时时间，单位为秒LogSlowQueries=3000 #毫秒，多久的数据库查询会被记录到日志</code></pre><p>&emsp;&emsp;然后在web界面添加proxy，主动模式是proxy端主动向server端推送数据，所以不需要填写IP，被动模式是server端向proxy拉取数据，需要填写proxy端IP或者域名DNS。选在主动代理或者被动代理，视情况而定，要与配置文件中相同。<br><img src="https://img-blog.csdnimg.cn/20191220161035716.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述">&emsp;&emsp;至此，zabbix proxy端也就配置好了。</p><h3 id="zabbix-agent"><a href="#zabbix-agent" class="headerlink" title="zabbix agent"></a>zabbix agent</h3><h4 id="agent工作模式"><a href="#agent工作模式" class="headerlink" title="agent工作模式"></a>agent工作模式</h4><p>&emsp;&emsp;一般来说，zabbix agent与proxy有两种工作模式，一个是主动模式，即被监控端主动向server端或者proxy端发起请求，请求监控项列表，并按照列表的内容主动定时推送监控信息，此时agent端使用随机端口访问server端或者proxy端的固定端口（10051端口）；另一个是被动模式，是server端或者proxy依照设定好的监控项条目，直接去agent段获取相应的数据，此时是agent端打开固定端口（10050）等待请求。<br>&emsp;&emsp;主动模式与被动模式中的主动与被动，是相对于agent端来说的，agent主动的就叫做主动模式，agent端被动的就叫做被动模式。</p><h4 id="zabbix-agent部署"><a href="#zabbix-agent部署" class="headerlink" title="zabbix agent部署"></a>zabbix agent部署</h4><p>&emsp;&emsp;在被监控主机上安装zabbix-agent安装包。流程与之前一样。</p><pre><code>apt install zabbix-agent</code></pre><p>&emsp;&emsp;修改配置文件，主要需要设置<code>Server</code>和<code>ServerActive</code>，其他保持默认就可以了。<code>Server</code>是控制允许哪个主机可以从本机上拉取数据，一般把server端和paroxy端都写上，方便修改(，其实只写proxy端ip就可以了)，<code>ServerActive</code>是设置向哪个主机请求主动监控配置的，如果打算使用被动模式，则可不进行设置此项，而且设置了也不会生效。</p><pre><code>root@DockerUbuntu21:~# grep &quot;^[a-Z]&quot; /etc/zabbix/zabbix_agentd.confPidFile=/var/run/zabbix/zabbix_agentd.pidLogFile=/var/log/zabbix-agent/zabbix_agentd.logLogFileSize=1Server=192.168.32.19,192.168.32.20ListenPort=10050StartAgents=4ServerActive=127.0.0.1Hostname=192.168.32.21Timeout=20Include=/etc/zabbix/zabbix_agentd.conf.d/*.conf</code></pre><p>&emsp;&emsp;重启agent服务，是配置生效</p><pre><code>systemctl restart zabbix-agent</code></pre><h4 id="配置监控"><a href="#配置监控" class="headerlink" title="配置监控"></a>配置监控</h4><p>&emsp;&emsp;之前的一系列操作，只是完成了一个服务基础，之后的操作才是重点，也就是配置监控。<br>&emsp;&emsp;进入zabbix的web管理界面，可以看到有很多项目，我们选择配置——主机——创建主机，来添加我们的监控对象。<br><img src="https://img-blog.csdnimg.cn/20191220163921915.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="创建主机"><br>&emsp;&emsp;在创建主机界面，需要选择一个模版，也就是监控规则，因为我们还没有创建模版，所以可以使用系统自带的模版，可以搜索<code>linux</code>使用自带的一些监控项。<br>&emsp;&emsp;需要注意的是，agent使用工作模式到底是主动模式还是被动模式，就在于监控项的类型是主动还是被动模式。系统默认模版都是被动式的，如果想使用主动式，可以批量修改模版监控项（模版的具体介绍会在之后文章介绍）。<br><img src="https://img-blog.csdnimg.cn/20191220170318287.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20191220170331346.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;而且，<strong>agent端使用的主动式或者被动式方式，与proxy设置的主动模式还是被动模式没有关系</strong>。proxy创建时选择的模式要与proxy配置文件一致，而这个设定只是控制proxy与server之间的关系，而真正控制agent工作模式的就在于这个监控项目的设置了。也就是说如果监控项目都是被动式，<code>ServerActive</code>设不设置都不会生效，如果有一部分项目是主动式，若没有设置正确的<code>ServerActive</code>，则这些项目将会获取不到数据了。<br>&emsp;&emsp;至此，对于zabbix的初步配置就生效了，等一下就可以看到添加的主机都显示绿色的可用状态。<br><img src="https://img-blog.csdnimg.cn/20191220170936800.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述">&emsp;&emsp;之后就可以在图形界面看到图形数据了。</p>]]></content>
    
    <summary type="html">
    
      常见的开源监控软件有：cacti、nagios、zabbix、smokeping、open-falcon等，本文主要介绍目前使用较多的开源监控软件zabbix，针对容器环境的开源监控软件Prometheus下次再讲解。
    
    </summary>
    
    
      <category term="linux" scheme="https://hewanyue.com/categories/linux/"/>
    
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="监控" scheme="https://hewanyue.com/tags/%E7%9B%91%E6%8E%A7/"/>
    
      <category term="zabbix" scheme="https://hewanyue.com/tags/zabbix/"/>
    
  </entry>
  
  <entry>
    <title>使用kubeasz自动化部署K8s</title>
    <link href="https://hewanyue.com/blog/8f374cb8.html"/>
    <id>https://hewanyue.com/blog/8f374cb8.html</id>
    <published>2019-12-18T07:21:12.000Z</published>
    <updated>2020-01-20T07:29:36.400Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;本文使用kubeasz项目基于二进制方式部署和利用ansible-playbook实现自动化部署K8s。</p><a id="more"></a><p>&emsp;&emsp;架构图如下所示<br><img src="https://img-blog.csdnimg.cn/20191214135719739.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;根据kubeasz官方文档中高可用集群所需节点配置如下</p><table><thead><tr><th align="center">角色</th><th align="center">数量</th><th>描述</th></tr></thead><tbody><tr><td align="center">管理节点</td><td align="center">1</td><td>运行ansible/easzctl脚本，可以复用master，建议使用独立节点（1c1g）</td></tr><tr><td align="center">etcd节点</td><td align="center">3</td><td>注意etcd集群需要1,3,5,7…奇数个节点，一般复用master节点</td></tr><tr><td align="center">master节点</td><td align="center">2</td><td>高可用集群至少2个master节点</td></tr><tr><td align="center">node节点</td><td align="center">3</td><td>运行应用负载的节点，可根据需要提升机器配置/增加节点数</td></tr></tbody></table><h2 id="配置集群环境"><a href="#配置集群环境" class="headerlink" title="配置集群环境"></a>配置集群环境</h2><p>&emsp;&emsp;此次部署节点设置如下：<br>ansible安装节点&amp;master1：172.18.32.18<br>master2：172.18.32.19<br>harbor：172.19.32.20<br>node1：172.18.32.21<br>node2：172.18.32.22<br>etcd1：172.18.32.23<br>etcd2：172.18.32.24<br>etcd3：172.18.32.25<br>haproxy1+keepalived：172.18.32.183<br>haproxy2+keepalived：172.18.32.184  VIP：172.18.32.250</p><h3 id="配置免密登录"><a href="#配置免密登录" class="headerlink" title="配置免密登录"></a>配置免密登录</h3><p>&emsp;&emsp;先将ansible安装节点也就是master1主机（可以不复用主机）的秘钥分发至各个主机。</p><pre><code>vim fenfamiyao.sh#!/bin/bash#目标主机列表PASSWORD=&quot;password&quot;PORT=&quot;22&quot;IP=&quot;172.18.32.18172.18.32.19172.18.32.20172.18.32.21172.18.32.22172.18.32.23172.18.32.24172.18.32.25&quot;[ -a /root/.ssh/id_rsa ] || ssh-keygen -t rsa which sshpass &amp;&gt; /dev/null || yum install sshpass -y #适用于CentOSfor node in ${IP};do        sshpass -p $PASSWORD ssh-copy-id -p$PORT -o StrictHostKeyChecking=no ${node}#        if [ $? -eq 0 ];then#                echo &quot;${node} 秘钥 copy 完成,准备环境初始化.....&quot;#                ssh -p$PORT ${node} &quot;mkdir /etc/docker/certs.d/harbor.hechao.com -p&quot;#                echo &quot;Harbor 证书目录创建成功!&quot;#                scp -P$PORT /usr/local/src/harbor/certs/harbor-ca.crt ${node}:/etc/docker/certs.d/harbor.hechao.com/harbor-ca.crt#                echo &quot;Harbor 证书拷贝成功!&quot;#                scp -P$PORT /etc/hosts ${node}:/etc/hosts#                echo &quot;host 文件拷贝完成&quot;#                scp -r -P$PORT /root/.docker ${node}:/root/#                echo &quot;Harbor 认证文件拷贝完成!&quot;#                scp -r -P$PORT /etc/resolv.conf ${node}:/etc/                 echo &quot;镜像加速链接同步!&quot;                 ssh ${node} &quot;mkdir -p /etc/docker&quot;                 scp -r -p$PORT /etc/docker/daemon.json ${node}:/etc/docker/daemon.json#        else#                echo &quot;${node} 秘钥 copy 失败&quot;#        fidone</code></pre><h3 id="安装ansible环境"><a href="#安装ansible环境" class="headerlink" title="安装ansible环境"></a>安装ansible环境</h3><p>&emsp;&emsp;因为要使用ansible批量自动化部署K8s集群，每个主机都要先安装ansible环境。<br>&emsp;&emsp;ubuntu默认的python版本是3.6，而ansible需要python2.7。</p><pre><code>apt updateapt install python2.7ln -s /usr/bin/python2.7 /usr/bin/python</code></pre><p>&emsp;&emsp;master1上安装ansible</p><pre><code>apt install ansible</code></pre><p>&emsp;&emsp;centos一般是自带python2.X环境，所以一般不需要在进行额外操作。<br>&emsp;&emsp;在master1上检查各节点连通性</p><pre><code>ansible all -m ping</code></pre><h2 id="配置kubeasz"><a href="#配置kubeasz" class="headerlink" title="配置kubeasz"></a>配置kubeasz</h2><h3 id="下载kubeasz"><a href="#下载kubeasz" class="headerlink" title="下载kubeasz"></a>下载kubeasz</h3><p>&emsp;&emsp;官方文档地址为<a href="https://github.com/easzlab/kubeasz" target="_blank" rel="external nofollow noopener noreferrer">https://github.com/easzlab/kubeasz</a>，根据适配的K8s版本选择合适的kubeasz版本。根据官方文档，目前支持以下版本</p><blockquote><p>集群版本 kubernetes v1.13, v1.14, v1.15, v1.16<br>操作系统 CentOS/RedHat 7, Debian 9/10, Ubuntu 1604/1804<br>运行时 docker 18.06.x-ce, 18.09.x, containerd 1.2.6<br>网络 calico, cilium, flannel, kube-ovn, kube-router</p></blockquote><p>&emsp;&emsp;我们下载2.0.3版本的kubeasz,并给予执行权限</p><pre><code>curl -C- -fLO --retry 3 https://github.com/easzlab/kubeasz/releases/download/2.0.3/easzupchmod +x easzup</code></pre><p>&emsp;&emsp;可以用<code>file easzup</code>命令看到这是一个ASCII文本文件，其实就是一个shell脚本。</p><pre><code>root@DockerUbuntu18:~# file easzupeaszup: Bourne-Again shell script, ASCII text executable</code></pre><p>&emsp;&emsp;用vim编辑修改此文件。<br>&emsp;&emsp;大部分地方无需修改，不过可以设置docker版本为18.09.9和K8s的版本为v1.15.5，其他地方不用动。</p><pre><code>export DOCKER_VER=18.09.9export K8S_BIN_VER=v1.15.5</code></pre><p>&emsp;&emsp;执行<code>-- help</code>查看脚本使用方法。</p><pre><code>root@DockerUbuntu18:~# ./easzup --help./easzup: illegal option -- -Usage: easzup [options] [args]  option: -{DdekSz}    -C         stop&amp;clean all local containers    -D         download all into /etc/ansible    -S         start kubeasz in a container    -d &lt;ver&gt;   set docker-ce version, default &quot;18.09.9&quot;    -e &lt;ver&gt;   set kubeasz-ext-bin version, default &quot;0.3.0&quot;    -k &lt;ver&gt;   set kubeasz-k8s-bin version, default &quot;v1.15.5&quot;    -m &lt;str&gt;   set docker registry mirrors, default &quot;CN&quot;(used in Mainland,China)    -p &lt;ver&gt;   set kubeasz-sys-pkg version, default &quot;0.3.2&quot;    -z &lt;ver&gt;   set kubeasz version, default &quot;2.0.3&quot;see more at https://github.com/kubeasz/dockerfiles</code></pre><p>&emsp;&emsp;先将<code>/etc/ansible</code>目录下所有自带的配置文件以及hosts文件删掉</p><pre><code>\rm -rf /etc/ansible/*</code></pre><p>&emsp;&emsp;执行脚本下载所有需要的镜像和二进制文件</p><pre><code>./easzup -D</code></pre><p>&emsp;&emsp;稍等片刻之后，下载好需要的镜像和二进制文件，此时就可以开始ansible自动部署安装K8s集群了。</p><h3 id="ansible部署K8s"><a href="#ansible部署K8s" class="headerlink" title="ansible部署K8s"></a>ansible部署K8s</h3><p>&emsp;&emsp;kubeasz工具都已经写好了playbook，只需要设定好hosts文件即可一键安装了。先进入<code>/etc/ansible</code>目录,然后复制提供好的host模版文件并编辑。</p><pre><code>cd /etc/ansible/cp example/hosts.multi-node hostsvim hosts</code></pre><pre><code>root@DockerUbuntu18:/etc/ansible# grep -v ^# hosts|grep -v ^$[etcd]172.18.32.23 NODE_NAME=etcd1172.18.32.24 NODE_NAME=etcd2172.18.32.25 NODE_NAME=etcd3[kube-master]172.18.32.18172.18.32.19[kube-node]172.18.32.21172.18.32.22[harbor][ex-lb][chrony][all:vars]CONTAINER_RUNTIME=&quot;docker&quot;CLUSTER_NETWORK=&quot;calico&quot;SERVICE_CIDR=&quot;10.68.0.0/16&quot;CLUSTER_CIDR=&quot;172.20.0.0/16&quot;NODE_PORT_RANGE=&quot;30000-65000&quot;CLUSTER_DNS_DOMAIN=&quot;cluster.local.&quot;bin_dir=&quot;/usr/kube/bin&quot;ca_dir=&quot;/etc/kubernetes/ssl&quot;base_dir=&quot;/etc/ansible&quot;</code></pre><p>&emsp;&emsp;以为我本地已经搭好harbor服务器了，所以就不要设置harbor服务让他来安装了。除了各节点IP以外，需要修改的就是<code>bin_dir</code>目录，这样就省去了创建软链接的步骤，也省去配置PATH变量了。我选择的网卡是<code>calico</code>，也可以使用默认的<code>flannel</code>，之后具体区别会再详细介绍。<br>&emsp;&emsp;直接执行 <code>playbook</code>剧本90.setup.yml可以直接一键安装完成，不过如果出现问题我们不好排错，推荐一个剧本一个剧本的来跑。</p><ul><li>01.prepare.yml</li></ul><pre><code>ansible-playbook 01.prepare.yml</code></pre><p>&emsp;&emsp;这个剧本总共三个环节，一般来说都不会报错（如果提示软链接创建失败，则可忽略）。<br>&emsp;&emsp;1.如果设置了chrony服务器，则master、node、etcd主机都会向chrony服务器同步时间。<br>&emsp;&emsp;2.控制节点上创建CA、创建集群参数及客户端认证参数<br>&emsp;&emsp;3分发证书工具CFSSL及kubeconfig配置文件</p><ul><li>02.etcd.yml</li></ul><pre><code>ansible-playbook 02.etcd.yml</code></pre><p>&emsp;&emsp;这个剧本是在配置etcd服务器。在3个etcd节点上，创建etcd目录并分发证书，导入之前下载在控制端的二进制程序<code>etcd</code>及<code>etcdctl</code>及导入etcd的systemctl unit文件，设置开机自动启动etcd服务。这步也很简单，一般也不会出现什么问题。</p><ul><li>03.docker.yml</li></ul><p>&emsp;&emsp;因为我们在hosts文件中设置了运行时为<code>docker</code>,所以我们第三个剧本选择<code>03.docker.yml</code>，选择执行<code>03.containerd,yml</code>剧本也不会执行，里面做了条件判断</p><pre><code>root@DockerUbuntu18:/etc/ansible# cat 03.containerd.yml # to install containerd service- hosts:  - kube-master  - kube-node  roles:  - { role: containerd, when: &quot;CONTAINER_RUNTIME == &#39;containerd&#39;&quot; } </code></pre><p>&emsp;&emsp;这个阶段的剧本比较复杂，要在每一个节点上安装docker，在执行这一步之前，先检查一下模版二进制文件的docker版本是否和我们之前预设的一样。</p><pre><code>/etc/ansible/bin/docker -vDocker version 18.09.6, build 481bc77</code></pre><p>&emsp;&emsp;如果不一样，可以去<code>/opt/kube/bin/</code>目录下找一下之前下好的二进制文件，确认下版本。</p><pre><code>/opt/kube/bin/docker -v</code></pre><p>&emsp;&emsp;这个目录下的版本一般是不会错的，将此目录下的二进制文件复制至模版目录<code>/etc/ansible/bin/</code>。</p><pre><code>cp /opt/kube/bin/* /etc/ansible/bin/</code></pre><p>&emsp;&emsp;执行ansible剧本</p><pre><code>ansible-playbook 03.docker.yml</code></pre><ul><li>04.kube-master.yml</li></ul><p>&emsp;&emsp;这个剧本是对两个master节点操作，执行了以下操作<br>&emsp;&emsp;1.创建kubernetes签名请求以及证书和私钥<br>&emsp;&emsp;2.导入配置文件，启动kube-apiserver、kube-controller-manager及kube-scheduler服务<br>&emsp;&emsp;3.设置主节点的kube-apiserver.service文件，并设置apiserver的IP地址并创建配置用户rbac权限。<br>&emsp;&emsp;此时使用命令<code>kubectl get node</code>可以看到两个主节点都是出于ready状态了。</p><pre><code>root@DockerUbuntu18:~# kubectl get nodeNAME           STATUS                     ROLES    AGE    VERSION172.18.32.18   Ready,SchedulingDisabled   master   1m    v1.15.5172.18.32.19   Ready,SchedulingDisabled   master   1m    v1.15.5</code></pre><ul><li>05..kube-node.yml</li></ul><p>&emsp;&emsp;这个剧本主要是将几个node节点加到集群中来。流程比较复杂，大致有以下步骤：<br>&emsp;&emsp;1,创建kube-node目录:/var/lib/kubelet、/var/lib/kube-proxy和/etc/cni/net.d目录<br>&emsp;&emsp;2.导入之前准备好的二进制可执行文件kubectl、kubelet、kube-proxy、bridge、host-local和loopback<br>&emsp;&emsp;3.配置haproxy，监听本地的127.0.0.1：6443端口，代理至两个主节点的6443端口<br>&emsp;&emsp;4.生成node节点的kubelet的配置文件，并分发至各个node节点。kubelet连接主节点的apiserver。<br>&emsp;&emsp;5.node节点连接后，对node节点标记为kubernetes.io/role=node</p><p>&emsp;&emsp;这步很容易出现kubelet服务无法启动，或者一直处于loaded状态，返回值为255。这是因为node节点的kubelet的认证失败。可以去node节点主机上查看服务kubelet服务状态。</p><pre><code>systemctl status kubectljournalctl -u kubelet -e</code></pre><p>&emsp;&emsp;我遇到几次node节点kubelet无法正常启动的情况，但是换了个全新的node节点就可以加进去了，说明问题不是在主节点上，一般将node节点还原至干净系统都可以解决问题。<br>&emsp;&emsp;还有一次返回值255但报错提示是，找不到<code>/run/systemd/resolve/resolv.conf</code>文件，于是创建一个</p><pre><code>mkdir /run/systemd/resolve/;echo &quot;nameserver 233.5.5.5&quot; &gt; /run/systemd/resolve/resolv.conf</code></pre><p>&emsp;&emsp;自己创建一个解析之后问题解决。<br>&emsp;&emsp;顺利的话，使用<code>kubectl get node</code>可以看到主节点和node节点都处于ready状态。</p><pre><code>root@DockerUbuntu18:/etc/ansible# kubectl get nodeNAME           STATUS                     ROLES    AGE   VERSION172.18.32.18   Ready,SchedulingDisabled   master   2h   v1.15.5172.18.32.19   Ready,SchedulingDisabled   master   2h   v1.15.5172.18.32.21   Ready                      node     2h   v1.15.5172.18.32.22   Ready                      node     2h   v1.15.5</code></pre><ul><li>06.network.yml</li></ul><p>&emsp;&emsp;在这个剧本中，主要是配置各个pod之间的网络访问。需要在每个物理节点上都生成一个<code>calico-node</code>服务的pod，再启动一个<code>calico-kube-controllers</code>服务的pod来管理他们，（因为默认主节点设置了不可调度，所以这个calico-kube-contronllers一般是在node节点上生成）。因为使用的是pod方式启动，所以这个剧本中，除了配置calicao证书及私钥外，主要是生成了一个<code>calico DaemonSet yaml</code>文件，路径为<code>/opt/kube/kube-system/calico.yaml</code>，所以之后如果修改网络配置，可以直接在此路径下修改这个yaml文件，例如如果想要修改镜像地址为私有harbor地址，则</p><pre><code>kubectl delete -f /opt/kube/kube-system/calico.yamlvim /opt/kube/kube-system/calico.yamlkubectl apply -f /opt/kube/kube-system/calico.yaml</code></pre><p>&emsp;&emsp;需要注意的是，如果将yaml文件中拉取镜像的地址改为了私有harbor，则需要在yaml文件中加入Secret资源，使用<code>imagePullSecrets</code>拉取。<br>&emsp;&emsp;当我们的node节点不需要跨网段时，通常会选择将IPIP模式（ip-in-ip叠加模式）关掉，使用calico的BGP模式，以节约大量主机内部访问时封装的性能损耗。<br>&emsp;&emsp;查看路由，可以看到目前荣期间通信的网络接口为<code>tunl0</code>。</p><pre><code>root@DockerUbuntu18:~# route -nKernel IP routing tableDestination     Gateway         Genmask         Flags Metric Ref    Use Iface0.0.0.0         172.18.0.1      0.0.0.0         UG    0      0        0 eth0172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0172.18.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0172.20.185.64   0.0.0.0         255.255.255.192 U     0      0        0 *172.20.233.128  172.18.32.19    255.255.255.192 UG    0      0        0 tunl0172.20.250.128  172.18.32.22    255.255.255.192 UG    0      0        0 tunl0172.20.250.192  172.18.32.21    255.255.255.192 UG    0      0        0 tunl0192.168.0.0     0.0.0.0         255.255.0.0     U     0      0        0 eth1</code></pre><p>&emsp;&emsp;于是可以编辑<code>/etc/ansible/roles/calico/defaults/main.yml</code>或者直接修改<code>/opt/kube/kube-system/calico.yaml</code>文件，将其中的 <code>CALICO_IPV4POOL_IPIP</code>修改为off，将<code>name: FELIX_IPINIPMTU</code>属性注释掉，改为<code>FELIX_IPINIPENABLED</code>值为<code>false</code>。</p><pre><code>            - name: CALICO_IPV4POOL_IPIP              value: &quot;off&quot;            - name: FELIX_IPINIPENABLED              value: &quot;false&quot;</code></pre><p>&emsp;&emsp;然后执行<code>kubectl delete -f /opt/kube/kube-system/calico.yaml</code>将网络组件calico的pod都先停掉，<code>reboot</code>重启后，使用命令<code>ifconfig</code>就会发现，之前使用的<code>tunl0</code>网卡就不见了。再使用命令<code>kubectl apply -f /opt/kube/kube-system/calico.yaml</code>，将k8s网络连接起来，使用命令<code>route -n</code>查看路由信息，就会发现，跨主机通信直接使用eth0网卡了。此时模式从IPIP修改为BGP模式。</p><pre><code>root@DockerUbuntu18:~# route -nKernel IP routing tableDestination     Gateway         Genmask         Flags Metric Ref    Use Iface0.0.0.0         172.18.0.1      0.0.0.0         UG    0      0        0 eth0172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0172.18.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0172.20.185.64   0.0.0.0         255.255.255.192 U     0      0        0 *172.20.233.128  172.18.32.19    255.255.255.192 UG    0      0        0 eth0172.20.250.128  172.18.32.22    255.255.255.192 UG    0      0        0 eth0172.20.250.192  172.18.32.21    255.255.255.192 UG    0      0        0 eth0192.168.0.0     0.0.0.0         255.255.0.0     U     0      0        0 eth1</code></pre><ul><li>07.cluster-addon.yml</li></ul><p>&emsp;&emsp;第七步是安装一些功能插件，如在node节点生成dns解析(默认使用的是coredns，可以在<code>roles/cluster-addon/defaults/main.yml</code>文件中设置），安装dashboard（可视化web界面）。我们也可以选择自己安装这些功能插件。<br>&emsp;&emsp;可参考官方文档<a href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#deploying-the-dashboard-ui" target="_blank" rel="external nofollow noopener noreferrer">https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#deploying-the-dashboard-ui</a><br>&emsp;&emsp;dashboard默认是1.6.3版本的，比较老。我们这里手动安装dashboard1.10.1版本，过程如下</p><pre><code>cd /etc/ansible/manifests/dashboardmkdir 1.10.1cp 1.6.3/ui* 1.10.1/cd 1.10.1</code></pre><p>&emsp;&emsp;先下载yaml文档</p><pre><code>wget https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml</code></pre><p>&emsp;&emsp;再创建admin token<code>vim admin-user-sa-rbac.yaml</code></p><pre><code>apiVersion: v1kind: ServiceAccountmetadata:  name: admin-user  namespace: kube-system---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:  name: admin-userroleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: cluster-adminsubjects:- kind: ServiceAccount  name: admin-user  namespace: kube-system</code></pre><p>&emsp;&emsp;创建集群角色<code>vim read-user-sa-rbac.yaml</code></p><pre><code>---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:  name: dashboard-read-clusterrolerules:- apiGroups:  - &quot;&quot;  resources:  - configmaps  - endpoints  - persistentvolumeclaims  - pods  - replicationcontrollers  - replicationcontrollers/scale  - serviceaccounts  - services  - nodes  - persistentvolumeclaims  - persistentvolumes  verbs:  - get  - list  - watch- apiGroups:  - &quot;&quot;  resources:  - bindings  - events  - limitranges  - namespaces/status  - pods/log  - pods/status  - replicationcontrollers/status  - resourcequotas  - resourcequotas/status  verbs:  - get  - list  - watch- apiGroups:  - &quot;&quot;  resources:  - namespaces  verbs:  - get  - list  - watch- apiGroups:  - apps  resources:  - daemonsets  - deployments  - deployments/scale  - replicasets  - replicasets/scale  - statefulsets  verbs:  - get  - list  - watch- apiGroups:  - autoscaling  resources:  - horizontalpodautoscalers  verbs:  - get  - list  - watch- apiGroups:  - batch  resources:  - cronjobs  - jobs  verbs:  - get  - list  - watch- apiGroups:  - extensions  resources:  - daemonsets  - deployments  - deployments/scale  - ingresses  - networkpolicies  - replicasets  - replicasets/scale  - replicationcontrollers/scale  verbs:  - get  - list  - watch- apiGroups:  - policy  resources:  - poddisruptionbudgets  verbs:  - get  - list  - watch- apiGroups:  - networking.k8s.io  resources:  - networkpolicies  verbs:  - get  - list  - watch- apiGroups:  - storage.k8s.io  resources:  - storageclasses  - volumeattachments  verbs:  - get  - list  - watch- apiGroups:  - rbac.authorization.k8s.io  resources:  - clusterrolebindings  - clusterroles  - roles  - rolebindings  verbs:  - get  - list  - watch</code></pre><p>&emsp;&emsp;此时目录结构如下</p><pre><code>root@DockerUbuntu18:/etc/ansible/manifests/dashboard/1.10.1# tree.├── admin-user-sa-rbac.yaml├── kubernetes-dashboard.yaml├── read-user-sa-rbac.yaml├── ui-admin-rbac.yaml└── ui-read-rbac.yaml0 directories, 5 files</code></pre><p>&emsp;&emsp;然后通过yaml文件启动dashboard的pod</p><pre><code>kubectl apply -f .</code></pre><p>&emsp;&emsp;可以通过命令查看pod 是否启动成功。</p><pre><code>kubectl get pods --all-namespaces | grep dashboard</code></pre><p>&emsp;&emsp;看到状态running之后，输入命令开启认证生成登陆用户名密码</p><pre><code>easzctl basic-auth -s</code></pre><pre><code>[INFO]basic-auth for apiserver is enabled!BASIC_AUTH_USER: &#39;admin&#39;BASIC_AUTH_PASS: &#39;4fe554e56c32f27b&#39;[INFO] Action successed : basic-auth basic-auth -s`</code></pre><p>&emsp;&emsp;通过命令<code>kubectl cluster-info</code>查看集群信息来查看登陆url</p><pre><code>kubectl cluster-info</code></pre><p>&emsp;&emsp;使用命令来获取token</p><pre><code>kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk &#39;{print $1}&#39;)</code></pre><p>&emsp;&emsp;这时就可以登陆web界面查看K8s集群信息了。</p>]]></content>
    
    <summary type="html">
    
      本文使用kubeasz项目基于二进制方式部署和利用ansible-playbook实现自动化部署K8s。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="kubernetes" scheme="https://hewanyue.com/tags/kubernetes/"/>
    
      <category term="kubeasz" scheme="https://hewanyue.com/tags/kubeasz/"/>
    
      <category term="dashboard" scheme="https://hewanyue.com/tags/dashboard/"/>
    
  </entry>
  
  <entry>
    <title>使用kubeadm部署安装K8s</title>
    <link href="https://hewanyue.com/blog/107b7ce7.html"/>
    <id>https://hewanyue.com/blog/107b7ce7.html</id>
    <published>2019-12-13T03:08:47.000Z</published>
    <updated>2020-01-20T07:29:36.395Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;本文将介绍通过kubeadm部署K8s集群的详细过程，且通过两个mater节点实现K8s集群的高可用。<br>&emsp;&emsp;本次演示使用 k8s 官方提供的部署工具 kubeadm 自动安装， 需要在 master 和 node 节点上安装 docker 等组件， 然后初始化， 把管理端的控制服务和 node 上的服务都以pod 的方式运行。</p><a id="more"></a><p>&emsp;&emsp;架构结构示意图如下路所示<br><img src="https://img-blog.csdnimg.cn/20191211215135227.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="K8s架构"><br>&emsp;&emsp;环境搭建（master及node节点均为ubuntu1804）：<br>&emsp;&emsp;master1：192.168.32.18<br>&emsp;&emsp;master2：192.168.32.19<br>&emsp;&emsp;harbor：192.168.32.20<br>&emsp;&emsp;node1：192.168.32.21<br>&emsp;&emsp;node2：192.168.32.22<br>&emsp;&emsp;需要禁用 swap， selinux， iptables。</p><pre><code>swapoff -a</code></pre><h2 id="搭建master节点"><a href="#搭建master节点" class="headerlink" title="搭建master节点"></a>搭建master节点</h2><h3 id="安装keepalived"><a href="#安装keepalived" class="headerlink" title="安装keepalived"></a>安装keepalived</h3><p>&emsp;&emsp;可以通过apt快速安装或者源码编译，下面以apt包管理工具安装为例</p><pre><code>apt updateapt install keepalived -ycp /usr/share/doc/keepalived/samples/keepalived.conf.vrrp /etc/keepalived/keepalived.conf</code></pre><p>&emsp;&emsp;然后修改配置文件，实例如下</p><pre><code>! Configuration File for keepalivedglobal_defs {   notification_email {     acassen   }   notification_email_from Alexandre.Cassen@firewall.loc   smtp_server 192.168.200.1   smtp_connect_timeout 30   router_id LVS_DEVEL}vrrp_instance VI_1 {    state MASTER    interface eth0    garp_master_delay 10    smtp_alert    virtual_router_id 32    priority 100    advert_int 1    authentication {        auth_type PASS        auth_pass 1111    }    virtual_ipaddress {        # optional label. should be of the form &quot;realdev:sometext&quot; for        # compatibility with ifconfig.        172.18.32.250 label eth0:1    }}</code></pre><p>&emsp;&emsp;另一个节点也安装keepalived，然后测试VIP是否可以漂移成功。</p><h3 id="安装-docker"><a href="#安装-docker" class="headerlink" title="安装 docker"></a>安装 docker</h3><p>&emsp;&emsp;在主节点上先安装docker，详细可参考<a href="https://blog.csdn.net/MicePro/article/details/103375500" target="_blank" rel="external nofollow noopener noreferrer">之前文章</a>。<br>&emsp;&emsp;可以通过阿里云镜像，使用脚本来安装<code>vim docker1806.sh</code></p><pre><code class="bash">#!/bin/bash# step 1: 安装必要的一些系统工具apt-get updateapt-get -y install apt-transport-https ca-certificates curl software-properties-common# step 2: 安装GPG证书curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -# Step 3: 写入软件源信息sudo add-apt-repository &quot;deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable&quot;# Step 4: 更新并安装Docker-CEapt-get -y updateapt-get -y install docker-ce=18.06.0~ce~3-0~ubuntu# 安装指定版本的Docker-CE:# Step 1: 查找Docker-CE的版本:# apt-cache madison docker-ce#   docker-ce | 17.03.1~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages#   docker-ce | 17.03.0~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages# Step 2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.1~ce-0~ubuntu-xenial)# sudo apt-get -y install docker-ce=[VERSION]</code></pre><pre><code class="bash">bash docker1806.sh</code></pre><h3 id="配置阿里加速器"><a href="#配置阿里加速器" class="headerlink" title="配置阿里加速器"></a>配置阿里加速器</h3><pre><code>vim /etc/docker/daemon.json{        &quot;registry-mirrors&quot;: [&quot;https://360k4x9i.mirror.aliyuncs.com&quot;,&quot;https://registry.docker-cn.com&quot;],        &quot;insecure-registries&quot;: [&quot;https://harbor.local.com&quot;],        &quot;bip&quot;: &quot;10.20.0.1/24&quot;}</code></pre><h3 id="安装kubeadm"><a href="#安装kubeadm" class="headerlink" title="安装kubeadm"></a>安装kubeadm</h3><p>&emsp;&emsp;先配置k8s的镜像源,并安装kubeadm</p><pre><code>curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -  cat  &gt;/etc/apt/sources.list.d/kubernetes.list &lt;&lt;EOFdeb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial mainEOFapt-get updateapt install kubeadm=1.16.1-00 kubectl=1.16.1-00 kubelet=1.16.1-00systemctl start kubelet &amp;&amp; systemctl enable kubelet</code></pre><h3 id="安装K8s"><a href="#安装K8s" class="headerlink" title="安装K8s"></a>安装K8s</h3><p>&emsp;&emsp;因为默认使用的是google的镜像仓库，国内是连接不上的，所以我们最好提前下载好镜像。本次演示安装版本为kubernetes v1.16.1<br>&emsp;&emsp;先查看需要下载的镜像及版本<br><code>kubeadm config images list --kubernetes-version v1.16.1</code></p><pre><code>k8s.gcr.io/kube-apiserver:v1.16.1k8s.gcr.io/kube-controller-manager:v1.16.1k8s.gcr.io/kube-scheduler:v1.16.1k8s.gcr.io/kube-proxy:v1.16.1k8s.gcr.io/pause:3.1k8s.gcr.io/etcd:3.3.15-0k8s.gcr.io/coredns:1.6.2</code></pre><p>&emsp;&emsp;我们先去阿里云镜像仓库提前下载好镜像，可以通过快速实现.。如果有harbor服务器，可以先上传到本地harbor。</p><pre><code class="bash">#!/bin/bashdocker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.3.15-0docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.6.2</code></pre><h3 id="master-初始化"><a href="#master-初始化" class="headerlink" title="master 初始化"></a>master 初始化</h3><p>&emsp;&emsp;因为我们打算做master的高可用。所以我们在master初始化时，要加选项<code>--control-plane-endpoint=172.18.32.250</code>指定``VIP`。只需在一个master节点上做初始化即可。</p><pre><code>kubeadm init \--apiserver-advertise-address=172.18.32.18 \--control-plane-endpoint=172.18.32.250 \--apiserver-bind-port=6443 \--kubernetes-version=v1.16.1 \--pod-network-cidr=10.10.0.0/16 \--service-cidr=10.20.0.0/16 \--service-dns-domain=k8s.local \--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers \--ignore-preflight-errors=swap </code></pre><p>&emsp;&emsp;也可以基于yaml文件而不是用命令行命令来进行初始化。可以使用命令<code>kubeadm init --config kubeadm-init.yaml</code> ，基于文件初始化。<br>&emsp;&emsp;<code>kubeadm config print init-defaults</code> 输出默认初始化配置<br>&emsp;&emsp;<code>kubeadm config print init-defaults &gt; kubeadm-init.yaml</code> 将默认配置输出至文件</p><pre><code>root@k8s-master1:~# cat kubeadm-init.yaml #修改后的初始化文件内容apiVersion: kubeadm.k8s.io/v1beta2bootstrapTokens:- groups:- system:bootstrappers:kubeadm:default-node-tokentoken: abcdef.0123456789abcdefttl: 24h0m0susages:- signing- authenticationkind: InitConfigurationlocalAPIEndpoint:advertiseAddress: 172.18.32.18bindPort: 6443nodeRegistration:criSocket: /var/run/dockershim.sockname: k8s-master1.k8s.localtaints:- effect: NoSchedulekey: node-role.kubernetes.io/master---apiServer:timeoutForControlPlane: 4m0sapiVersion: kubeadm.k8s.io/v1beta2certificatesDir: /etc/kubernetes/pkiclusterName: kubernetescontrolPlaneEndpoint: 172.18.32.250:6443 #添加基于 VIP 的 EndpointcontrollerManager: {}dns:type: CoreDNSetcd:local:dataDir: /var/lib/etcdimageRepository: registry.cn-hangzhou.aliyuncs.com/google_containerskind: ClusterConfigurationkubernetesVersion: v1.16.1networking:dnsDomain: k8s.localpodSubnet: 10.10.0.0/16serviceSubnet: 10.20.0.0/16scheduler: {}</code></pre><p>&emsp;&emsp;初始化成功，记录下来<code>--token</code>和<code>--discovery-token-ca-cert-hash</code>,，之后加入其他节点时需要用到。<br>&emsp;&emsp;如果初始化失败了需要 使用命令kubeadm reset可以清除已有容器数据以便重新安装，PS：此命令如果在安装完成后使用会清除已创建的k8s集群。</p><h3 id="配置kube证书"><a href="#配置kube证书" class="headerlink" title="配置kube证书"></a>配置kube证书</h3><pre><code>mkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/config</code></pre><h3 id="配置网卡"><a href="#配置网卡" class="headerlink" title="配置网卡"></a>配置网卡</h3><pre><code>wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.ymlkubectl apply -f kube-flannel.yml</code></pre><h2 id="加入其他节点"><a href="#加入其他节点" class="headerlink" title="加入其他节点"></a>加入其他节点</h2><p>&emsp;&emsp;其他三台k8s节点也要安装docker及k8s，可以通过脚本快速实现</p><pre><code>vim node.sh#!/bin/bash# step 1: 安装必要的一些系统工具apt-get updateapt-get -y install apt-transport-https ca-certificates curl software-properties-common# step 2: 安装GPG证书curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -# Step 3: 写入软件源信息sudo add-apt-repository &quot;deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable&quot;# Step 4: 更新并安装Docker-CEapt-get -y updateapt-get -y install docker-ce=18.06.0~ce~3-0~ubuntucat &gt; /etc/docker/daemon.json &lt;&lt; EOF{        &quot;registry-mirrors&quot;: [&quot;https://360k4x9i.mirror.aliyuncs.com&quot;,&quot;https://registry.docker-cn.com&quot;],        &quot;insecure-registries&quot;: [&quot;https://harbor.local.com&quot;],        &quot;bip&quot;: &quot;10.20.0.1/24&quot;}EOFcurl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - cat  &gt;/etc/apt/sources.list.d/kubernetes.list &lt;&lt;EOFdeb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial mainEOFapt-get updateapt install kubeadm=1.16.1-00 kubectl=1.16.1-00 kubelet=1.16.1-00 -ysystemctl start kubelet &amp;&amp; systemctl enable kubeletdocker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.16.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.3.15-0docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.6.2systemctl enable --now docker kubelet</code></pre><p>&emsp;&emsp;当前 maste 生成证书用于添加新控制节点：</p><pre><code>kubeadm init phase upload-certs --upload-certs</code></pre><p>&emsp;&emsp;得到<code>--certificate-key</code>的值，也要记录下来。<br>&emsp;&emsp;之后想加入哪个节点，就在哪个节点上操作。先加入另一个master节点。</p><pre><code>kubeadm join 172.18.32.250:6443 --token 89beqy.13jxavbu7yz3187d \--discovery-token-ca-cert-hash sha256:7388af4f1662805a844cce7c1371facb83f32dddb998370d11bfb41957fe75bf \--certificate-key 3630d5719795c77e7071d77a206cc17078c912f9c3915e76e70bb26e75e26178 \--control-plane</code></pre><p>&emsp;&emsp;再加入各个node节点，命令区别是少了<code>--control-plane</code>选项以及控制秘钥。</p><pre><code>kubeadm join 172.18.32.250:6443 --token 89beqy.13jxavbu7yz3187d \--discovery-token-ca-cert-hash sha256:7388af4f1662805a844cce7c1371facb83f32dddb998370d11bfb41957fe75bf</code></pre><p>&emsp;&emsp;之后通过命令,就可以看到4个主机都处于<code>ready</code>状态了。至此k8s集群的搭建就完成了。</p><pre><code>kubectl get node</code></pre>]]></content>
    
    <summary type="html">
    
      本文将介绍通过kubeadm部署K8s集群的详细过程，且通过两个mater节点实现K8s集群的高可用。 本次演示使用 k8s 官方提供的部署工具 kubeadm 自动安装， 需要在 master 和 node 节点上安装 docker 等组件， 然后初始化， 把管理端的控制服务和 node 上的服务都以pod 的方式运行。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="kubeadm" scheme="https://hewanyue.com/tags/kubeadm/"/>
    
      <category term="docker" scheme="https://hewanyue.com/tags/docker/"/>
    
      <category term="kubernetes" scheme="https://hewanyue.com/tags/kubernetes/"/>
    
  </entry>
  
  <entry>
    <title>Docker（五）——Docker镜像仓库</title>
    <link href="https://hewanyue.com/blog/6187894.html"/>
    <id>https://hewanyue.com/blog/6187894.html</id>
    <published>2019-12-07T14:30:04.000Z</published>
    <updated>2020-01-20T07:29:36.327Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;比较常见的docker镜像仓库，有docker官方仓库<a href="https://hub.docker.com/" target="_blank" rel="external nofollow noopener noreferrer">https://hub.docker.com/</a>，和阿里云镜像仓库<code>https://cr.console.aliyun.com/cn-hangzhou/instances/images</code>，可以比较方便的拉取镜像或储存容器镜像。而在企业生产中，绝对部分情况我们都是使用企业内部的镜像仓库，来分发部署我们的代码。本文将详细介绍阿里云仓库还有私有云仓库Registry、Harbor的搭建和使用的详细步骤方法。</p><a id="more"></a><h2 id="阿里云仓库"><a href="#阿里云仓库" class="headerlink" title="阿里云仓库"></a>阿里云仓库</h2><p>&emsp;&emsp;docker官方仓库配置比较简单，而且大部分是默认配置，且速度不如阿里云镜像仓库速度快，所以我这里就不介绍了，使用方式和阿里云容器镜像仓库差不多类似。</p><h3 id="注册账号"><a href="#注册账号" class="headerlink" title="注册账号"></a>注册账号</h3><p>&emsp;&emsp;使用阿里云仓库服务首先要注册阿里云账号，支付宝也可以登陆，比较快捷。点击上面的网址登陆即可。<br><img src="https://img-blog.csdnimg.cn/20191207213751920.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><h3 id="创建仓库"><a href="#创建仓库" class="headerlink" title="创建仓库"></a>创建仓库</h3><p>&emsp;&emsp;先创建一个命名空间，这相当于每个人独立的url，可以以代码类别或者性质命名创建（也可以凭个人喜好），每个账号只能创建5个命名空间，不过也够用了。<br>&emsp;&emsp;然后创建镜像仓库。<br><img src="https://img-blog.csdnimg.cn/2019120721442027.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;地域选择离自己比较近的地域，这样延迟会稍微低一些，选择已创建的命名空间，仓库名的命名一般是服务名或者软件名。公开或者私有看个人请款选择。</p><h3 id="上传镜像"><a href="#上传镜像" class="headerlink" title="上传镜像"></a>上传镜像</h3><p>&emsp;&emsp;有了仓库之后，我们就可以上传镜像了。</p><h4 id="打标签"><a href="#打标签" class="headerlink" title="打标签"></a>打标签</h4><p>&emsp;&emsp;要上传镜像，第一步，要先重新打标签，将阿里云的仓库源的地址，仓库名，以及版本号重新打标签，生成新镜像。例如对已有的haproxy镜像重新打标签，因为我选择的是北京节点，所以打标命令如下：</p><pre><code>docker tag haproxy-base:v1 registry.cn-beijing.aliyuncs.com/【命名空间名称】/【仓库名】:【版本号】</code></pre><h4 id="登陆"><a href="#登陆" class="headerlink" title="登陆"></a>登陆</h4><p>&emsp;&emsp;想上传或者下载镜像一般都需要授权才可以，这就要求我们要用有权限的帐号登陆，才可以上传镜像或者下载镜像。</p><pre><code>root@DockerUbuntu:/opt/dockerfile/web/haproxy/2.0.5# docker login --username=【账号名】 registry.cn-beijing.aliyuncs.comPassword: WARNING! Your password will be stored unencrypted in /root/.docker/config.json.Configure a credential helper to remove this warning. Seehttps://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeededroot@DockerUbuntu:/opt/dockerfile/web/haproxy/2.0.5# </code></pre><h4 id="上传"><a href="#上传" class="headerlink" title="上传"></a>上传</h4><p>&emsp;&emsp;用<code>docker images</code>可以查看所有的镜像，选择已经打了阿里云网址的标签的镜像上传就可以了</p><pre><code>root@DockerUbuntu:/opt/dockerfile/web/haproxy/2.0.5# docker push registry.cn-beijing.aliyuncs.com/【命名空间名称】/【仓库名】:【版本号】The push refers to repository [registry.cn-beijing.aliyuncs.com/命名空间名/仓库名]0ec88a00d427: Pushed 6a6e6f03a1a5: Pushed 965bdb9c5299: Pushed a1d450e33837: Pushed 837dac687863: Pushed b39d6a9ec3e2: Pushed 1e7fbf47b8df: Pushed dc298319f184: Pushed e4809dffd3aa: Pushed b3cdf76b6336: Pushed 2fc5c4732662: Pushed 2d03b9db6c3f: Pushed 89169d87dbe2: Pushed haproxy: digest: sha256:f0b4157cd18498e4bc373e333e4fb0b85a65d00e03de2d65015b0d0da9099af6 size: 3049</code></pre><p>&emsp;&emsp;这时就上传成功了</p><h4 id="拉取镜像"><a href="#拉取镜像" class="headerlink" title="拉取镜像"></a>拉取镜像</h4><p>&emsp;&emsp;其他主机登陆成功之后，通过拉取命令就可以从阿里云端下载镜像了。</p><pre><code>[root@DockerCentOS ~]# docker pull registry.cn-beijing.aliyuncs.com/【命名空间名称】/【仓库名】:【版本号】haproxy: Pulling from xxxxxxxxxx/webac9208207ada: Already exists 75c124fe932b: Pull complete 9ef7eb04bb69: Pull complete c5f97c472240: Pull complete 9dc49af65399: Pull complete a745615abdba: Pull complete ddcf37c0f462: Pull complete 0c406d186167: Pull complete 246fafa1cb32: Pull complete 057e62247ad8: Pull complete 770d7edff222: Pull complete b4064e1ed3ec: Pull complete d0a103ae1f19: Pull complete Digest: sha256:f0b4157cd18498e4bc373e333e4fb0b85a65d00e03de2d65015b0d0da9099af6Status: Downloaded newer image for registry.cn-beijing.aliyuncs.com/xxxxxxxxxx/web:haproxyregistry.cn-beijing.aliyuncs.com/xxxxxxxxxx/web:haproxy</code></pre><p>&emsp;&emsp;这时就可以在<code>docker images</code>的镜像列表中看到刚刚拉取的镜像了。</p><h2 id="搭建私有仓库"><a href="#搭建私有仓库" class="headerlink" title="搭建私有仓库"></a>搭建私有仓库</h2><p>&emsp;&emsp;阿里云镜像仓库虽然很方便，但是在生产环境中，每次都从云端拉取或者上传至云端仓库，太消耗企业带宽，有时候数据繁忙的时候，很有可能会堵塞业务，而且速度也较慢。所以企业中都会基于内部局域网搭建企业内部使用的私有仓库。一般搭建私有仓库有两种解决方案，一个是docker自带的Docker Registry，还有就是由vmware公司开源的harbor。</p><h3 id="Docker-Registry"><a href="#Docker-Registry" class="headerlink" title="Docker Registry"></a>Docker Registry</h3><p>&emsp;&emsp;Docker Registry 作为 Docker 的核心组件之一负责镜像内容的存储与分发， 客户端的 docker pull 以及 push 命令都将直接与 registry 进行交互,最初版本的 registry由Python实现,由于设计初期在安全性， 性能以及API的设计上有着诸多的缺陷，该版本在 0.9 之后停止了开发，由新的项目 distribution（新的 docker register 被称为 Distribution）来重新设计并开发下一代 registry，新的项目由 go 语言开发，所有的 API， 底层存储方式， 系统架构都进行了全面的重新设计已解决上一代registry 中存在的问题， 2016 年 4 月份 rgistry 2.0 正式发布， docker 1.6 版本开始支持 registry 2.0，而八月份随着 docker 1.8 发布， docker hub 正式启用 2.1 版本registry 全面替代之前版本 registry，新版 registry 对镜像存储格式进行了重新设计并和旧版不兼容， docker 1.5 和之前的版本无法读取 2.0 的镜像， 另外， Registry2.4 版本之后支持了回收站机制，也就是可以删除镜像了，在 2.4 版本之前是无法支持删除镜像的，所以如果你要使用最好是大于 Registry 2.4 版本的。<br>&emsp;&emsp;Docker Registry的优势就是比较小（25M），但是功能表比较简单。</p><h4 id="下载-docker-registry-镜像"><a href="#下载-docker-registry-镜像" class="headerlink" title="下载 docker registry 镜像"></a>下载 docker registry 镜像</h4><pre><code>[root@DockerCentOS ~]# docker pull registryUsing default tag: latestlatest: Pulling from library/registryc87736221ed0: Pull complete 1cc8e0bb44df: Pull complete 54d33bcb37f5: Pull complete e8afc091c171: Pull complete b4541f6d3db6: Pull complete Digest: sha256:8004747f1e8cd820a148fb7499d71a76d45ff66bac6a29129bfdbfdc0154d146Status: Downloaded newer image for registry:latestdocker.io/library/registry:latest</code></pre><h4 id="搭建单机仓库"><a href="#搭建单机仓库" class="headerlink" title="搭建单机仓库"></a>搭建单机仓库</h4><p>&emsp;&emsp;先创建授权使用目录</p><pre><code>mkdir -p /docker/auth</code></pre><p>&emsp;&emsp;创建一个用户并创建密码文件</p><pre><code>cd /dockerdocker run --entrypoint htpasswd registry -Bbn Mice 123456 &gt; auth/htpasswd #创建一个用户并生成密码</code></pre><p>&emsp;&emsp;验证用户名密码</p><pre><code>[root@DockerCentOS docker]# cat auth/htpasswdMice:$2y$05$XqNS4BH3gkxodR9MQyhnIuL19uT4wfa6MjUgXvJUYuo0T0o0J8Tzy</code></pre><p>&emsp;&emsp;从registry镜像中启动 docker registry,指定容器名称为<code>registry1</code>，挂载本地/docker/auth目录至容器的/auth目录，传递账号密码变量至容器。</p><pre><code>docker run -d -p 5000:5000 --restart=always \--name registry1 \-v /docker/auth:/auth \-e &quot;REGISTRY_AUTH=htpasswd&quot; \-e &quot;REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm&quot; \-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd\ registry</code></pre><p>&emsp;&emsp;此时如果用我们创建的用户名密码尝试登陆了，记得IP或者域名后面要加<strong>5000端口</strong>，否则会报<strong>502</strong>错误。</p><pre><code>root@DockerUbuntu19:~# docker login 192.168.32.20Username: MicePassword: Error response from daemon: login attempt to http://192.168.32.20/v2/ failed with status: 502 Bad Gateway</code></pre><p>&emsp;&emsp;不过很有可能当你加上5000端口，可能还会有报错。ヾ(≧O≦)〃嗷~</p><pre><code>root@DockerUbuntu19:~# docker login 192.168.32.20:5000Username: MicePassword: Error response from daemon: Get https://192.168.32.20:5000/v2/: http: server gave HTTP response to HTTPS client</code></pre><p>&emsp;&emsp;这是因为我们每一个docker主机要设置允许<code>insecure-registries</code>，加上我们registry仓库的IP或者域名。<br>&emsp;&emsp;同样，可以修改<code>/lib/systemd/system/docker.service</code>启动脚本文件，或者<code>/etc/docker/daemon.json</code>文件，推荐修改<code>/etc/docker/daemon.json</code>文件。</p><pre><code>{  &quot;registry-mirrors&quot;: [&quot;https://360k4x9i.mirror.aliyuncs.com&quot;],  &quot;insecure-registries&quot;: [&quot;192.168.32.19&quot;,&quot;DockerCentOS20:5000&quot;],  &quot;bip&quot;: &quot;10.20.0.1/24&quot;}</code></pre><p>&emsp;&emsp;然后重启docker服务,此时再尝试登陆，就回提示登陆成功。</p><pre><code>root@DockerUbuntu19:~# docker login DockerCentOS20:5000Username: MicePassword: WARNING! Your password will be stored unencrypted in /root/.docker/config.json.Configure a credential helper to remove this warning. Seehttps://docs.docker.com/engine/reference/commandline/login/#credentials-store</code></pre><p>&emsp;&emsp;之后就与阿里云的镜像仓库使用方法相同了，不过速度上会快很多（毕竟内网），可以如果要上传镜像至regist仓库，先打好标签就可以了，下载写明下载仓库源，也就可以正常下载了。ヾ(=ﾟ･ﾟ=)ﾉ喵♪</p><h3 id="Harbor"><a href="#Harbor" class="headerlink" title="Harbor"></a>Harbor</h3><p>&emsp;&emsp;Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器， 由vmware开源，其通过添加一些企业必需的功能特性，例如安全、标识和管理等， 扩展了开源 Docker Distribution。作为一个企业级私有Registry服务器，Harbor 提供了更好的性能和安全。提升用户使用Registry构建和运行环境传输镜像的效率。Harbor支持安装在多个Registry节点的镜像资源复制， 镜像全部保存在私有Registry中，确保数据和知识产权在公司内部网络中管控， 另外，Harbor也提供了高级的安全特性，诸如用户管理，访问控制和活动审计等 ，所以企业生产中，我们更多会选择Harbor。</p><h4 id="下载harbor安装包"><a href="#下载harbor安装包" class="headerlink" title="下载harbor安装包"></a>下载harbor安装包</h4><p>&emsp;&emsp;下载地址： <a href="https://github.com/vmware/harbor/releases" target="_blank" rel="external nofollow noopener noreferrer">https://github.com/vmware/harbor/releases</a><br>&emsp;&emsp;安装文档：<a href="https://github.com/vmware/harbor/blob/master/docs/installation_guide.md" target="_blank" rel="external nofollow noopener noreferrer">https://github.com/vmware/harbor/blob/master/docs/installation_guide.md</a><br>&emsp;&emsp;本次以harbor的1.75版本为例，演示harbor的安装过程。<br>&emsp;&emsp;下载离线包，并解压。</p><pre><code>cd /usr/local/srcwget https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-offline-installer-v1.7.5.tgztar xvf harbor-offline-installer-v1.7.5.tgz</code></pre><p>&emsp;&emsp;也可以下载在线安装包，但里面没有镜像，之后还要去拉取镜像，不适合生产环境。</p><h4 id="安装docker-compose"><a href="#安装docker-compose" class="headerlink" title="安装docker-compose"></a>安装docker-compose</h4><p>&emsp;&emsp;Harbor是需要使用docker编排工具docker-compose安装，docker-compose我们之后会专门介绍。而且对docker-compose版本是有要求的，可以查看上面的官方文档链接查看确切版本。<br><img src="https://img-blog.csdnimg.cn/20191208220914881.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="Harbor要求"><br>&emsp;&emsp;而如果使用包管理工具yum或者apt直接安装的docker-compose可能版本会比较低，我们这里采用python包管理工具<code>python-pip</code>来安装<code>docker-compose</code>。</p><pre><code>apt install python-pip -ypip install docker-compose</code></pre><p>&emsp;&emsp;然后可以通过命令<code>docker compose -v</code>看到docker-compose版本已经是最新稳定版1.25版本了。</p><pre><code>root@DockerUbuntu19:/usr/local/src# docker-compose -vdocker-compose version 1.25.0, build b42d419</code></pre><h4 id="安装Harbor"><a href="#安装Harbor" class="headerlink" title="安装Harbor"></a>安装Harbor</h4><p>&emsp;&emsp;我们习惯于将源码包放在<code>/usr/local/src</code>下，而将主程序放在<code>/usr/local/</code>目录中，所以我们可以将harbor目录的的路径改为<code>/usr/local/harbor</code>，可以通过<code>mv</code>命令，也可以通过软链接方式实现</p><pre><code>ln -sv /usr/local/src/harbor /usr/local/</code></pre><p>&emsp;&emsp;然后修改配置文件<code>harbor.cfg</code></p><pre><code>cd /usr/local/harborvim harbor.cfg</code></pre><p>&emsp;&emsp;修改其中主机名(改为IP或者域名)，管理员登录密码，及邮箱即可。</p><pre><code>hostname = 192.168.32.19email_identity = harboremail_server = smtp.163.comemail_server_port = 25email_username = XXXXXXXXX@163.comemail_password = XXXXXXXXXXemail_from = admin &lt;XXXXXXXX@163.com&gt;harbor_admin_password = XXXXXXXXXXXXX</code></pre><p>&emsp;&emsp;然后更新配置文件中的环境变量到安装文件中,忘记更新环境变量会提示找不到环境变量文件<code>ERROR: Couldn&#39;t find env file: /usr/local/src/harbor/common/config/core/env</code>。</p><pre><code>./prepare</code></pre><p>&emsp;&emsp;此时就可以执行命令,来创建并安装Harbor了。</p><pre><code>docker-compose up -d</code></pre><p>&emsp;&emsp;或者执行官方脚本（两个都可以）</p><pre><code>./install.sh</code></pre><p>&emsp;&emsp;这时候就可以通过浏览器访问我们刚刚搭建的harbor仓库了，至此企业私有仓库就算是搭建好了。<br><img src="https://img-blog.csdnimg.cn/2019120910105297.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>&emsp;&emsp;管理员用户名为admin，密码为我们之前在配置文件中修改的<code>harbor_admin_password</code>。<br>&emsp;&emsp;后期如果需要修改配置文件信息，需要先停止harbor，然后修改信息后，更新配置文件信息至harbor服务，之后再试用<code>docker-compose up -d</code>启动harbor服务即可,流程如下。</p><pre><code>cd /usr/local/harbordocker-compose stopvim harbor.cfg./preparedocker-compose up -d</code></pre><p><img src="https://img-blog.csdnimg.cn/20191209101117514.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述">&emsp;&emsp;推送流程与使用其他云镜像仓库相同，先打标签,开头加上ip/仓库名，然后直接推送即可。</p>]]></content>
    
    <summary type="html">
    
      比较常见的docker镜像仓库，有docker官方仓库[https://hub.docker.com/](https://hub.docker.com/)，和阿里云镜像仓库``https://cr.console.aliyun.com/cn-hangzhou/instances/images``，可以比较方便的拉取镜像或储存容器镜像。而在企业生产中，绝对部分情况我们都是使用企业内部的镜像仓库，来分发部署我们的代码。本文将详细介绍阿里云仓库还有私有云仓库Registry、Harbor的搭建和使用的详细步骤方法。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="Docker" scheme="https://hewanyue.com/tags/Docker/"/>
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="容器" scheme="https://hewanyue.com/tags/%E5%AE%B9%E5%99%A8/"/>
    
      <category term="镜像仓库" scheme="https://hewanyue.com/tags/%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>Docker（四）——容器跨主机网络配置</title>
    <link href="https://hewanyue.com/blog/7e3785f.html"/>
    <id>https://hewanyue.com/blog/7e3785f.html</id>
    <published>2019-12-07T11:32:04.000Z</published>
    <updated>2020-01-20T07:29:36.327Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;跨主机互联是说 A 宿主机的容器可以访问 B 主机上的容器，但是前提是保证各宿主机之间的网络是可以相互通信的， 然后各容器才可以通过宿主机访问到对方的容器， 实现原理是在宿主机做一个网络路由就可以实现 A 宿主机的容器访问 B主机的容器的目的， 复杂的网络或者大型的网络可以使用 google 开源的 k8s 进行互联。本文之后将详细介绍docker网络配置，并演示容器跨主机通信的实现。</p><a id="more"></a><h2 id="docker网络基础"><a href="#docker网络基础" class="headerlink" title="docker网络基础"></a>docker网络基础</h2><p>&emsp;&emsp;之前我们说过，当我们安装完docker应用后，就会自动添加一块虚拟的docker0网卡，并基于docker0网卡，提供了3种可选网络类型供创建的容器使用，分别是bridge(桥接)，host(主机)，none(无外部网络)。其中默认是采用桥接模式，容器中的网卡桥接在docker的网桥上，且通过DHCP自动分配IP，与docker0在同一网段。<br>&emsp;&emsp;当我们每创建一个容器，宿主机上就会新建一个网卡与容器中的网卡相对应，如下图所示。<br><img src="https://img-blog.csdnimg.cn/20191207153539950.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="网桥"><br>&emsp;&emsp;容器桥接模式跨网络访问的结构示意图如下图所示<br><img src="https://img-blog.csdnimg.cn/20191207155538522.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="docker跨主机通信"><br>&emsp;&emsp;想实现不同宿主机上的容器跨主机肯定要经过宿主机来做网络转发，通过设置宿主机静态路由或者修改iptables规则来实现，可这时就面临一个问题：所有的容器服务默认的DHCP网段都是172.17.0.0/16网段，如果node1上的容器想直接访问node2宿主机上的容器，就会被直接当做docker0网桥的内部网段，数据报文根本都不会从node1主机的eth0网卡发出去，也根本到不了node2主机上。这种情况下，无论我们怎么修改iptables规则或者路由规则都无济于事的。所以我们想实现容器跨主机访问，首先要将不同宿主机上的容器分到不同的网段，然后才可以通过路由规则或者iptables进行跳转或转发。</p><h3 id="修改docker网络的网段"><a href="#修改docker网络的网段" class="headerlink" title="修改docker网络的网段"></a>修改docker网络的网段</h3><p>&emsp;&emsp;我们可以对每一个宿主机上的docker配置文件进行修改，实现每个宿主机的docker容器都在不同网段的目的,可以通过以下方式修改（未避免影响， 先在各服务器删除之前创建的所有容器，<code>docker rm -f `docker ps -a -q`</code>）。</p><ul><li>修改启动system脚本文件<code>docker.service</code><pre><code>vim /lib/systemd/system/docker.service</code></pre>&emsp;&emsp;在<code>ExecStart=</code>选项结尾加上<code>--bip=10.1.0.1/24</code>，就指定了10.1.0.0/24网段，然后执行命令重新加载配置文件和重启服务。<pre><code>systemctl daemon-reloadsystemctl restart docker</code></pre>&emsp;&emsp;<strong>注意:<code>不能写10.1.0.0/24，会报错</code></strong>，虽然写网段结尾是0，如10.1.0.0/24更符合我们的习惯，不过确实会报错，报错信息如下：<br>```<br>Dec 07 16:27:58 DockerUbuntu dockerd[14794]: failed to start daemon: Error initializing network controller: Error creating default “bridge” network: failed to allocate gateway (10.10.0.0): Address already in use<br>Dec 07 16:27:58 DockerUbuntu systemd[1]: docker.service: Main process exited, code=exited, status=1/FAILURE<br>Dec 07 16:27:58 DockerUbuntu systemd[1]: docker.service: Failed with result ‘exit-code’.<br>Dec 07 16:27:58 DockerUbuntu systemd[1]: Failed to start Docker Application Container Engine.</li><li><ul><li>Subject: Unit docker.service has failed</li></ul></li><li><ul><li>Defined-By: systemd<br>```</li></ul></li><li>也可以修改daemon.json文件，在里面添加<code>&quot;bip&quot;: &quot;10.2.0.1/24&quot;</code>，如下所示（上面那个是我的阿里云加速器链接，注册阿里账号免费获取，<a href="https://blog.csdn.net/MicePro/article/details/103411387#t0" target="_blank" rel="external nofollow noopener noreferrer">之前文章</a>有详细介绍，需改成自己的或者删掉）：<pre><code>vim /etc/docker/daemon.json</code></pre></li></ul><p>{<br>        “registry-mirrors”: [“<a href="https://xxxxxxxx.mirror.aliyuncs.com&quot;,&quot;https://registry.docker-cn.com&quot;]" target="_blank" rel="external nofollow noopener noreferrer">https://xxxxxxxx.mirror.aliyuncs.com&quot;,&quot;https://registry.docker-cn.com&quot;]</a>,<br>        “bip”: “10.1.0.1/24”<br>}</p><pre><code>&amp;emsp;&amp;emsp;**daemon.json文件是json数据格式，需要遵守json语法，换行记得要加``,``逗号。**&amp;emsp;&amp;emsp;直接重启docker服务后生效</code></pre><p>systemctl restart docker</p><pre><code>&amp;emsp;&amp;emsp;此时看网卡的ip就已经变为了我们设置的网段，之后创建的容器服务器就会自动获取我们设置好的网段中的IP了。</code></pre><p>root@DockerUbuntu:~# ifconfig<br>docker0: flags=4099&lt;UP,BROADCAST,MULTICAST&gt;  mtu 1500<br>        inet 10.10.0.1  netmask 255.255.255.0  broadcast 10.10.0.255<br>        inet6 fe80::42:25ff:fe2b:ecbc  prefixlen 64  scopeid 0x20<link><br>        ether 02:42:25:2b:ec:bc  txqueuelen 0  (Ethernet)<br>        RX packets 0  bytes 0 (0.0 B)<br>        RX errors 0  dropped 0  overruns 0  frame 0<br>        TX packets 41  bytes 3526 (3.5 KB)<br>        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0</p><pre><code>&amp;emsp;&amp;emsp;同样的操作，将node2宿主机中的docker0网段设置为10.2.0.0/24### 修改静态路由&amp;emsp;&amp;emsp;我这里node1的eth0网卡ip为192.168.32.19，node2的eth0网卡ip为192.168.32.20，且两个宿主机之前网络是可以通过eth0网卡相互连接的。添加路由规则如下：&amp;emsp;&amp;emsp;在node1上添加静态路由</code></pre><p>ip r add 10.2.0.0/24 via 192.168.32.20 dev eth0</p><pre><code>&amp;emsp;&amp;emsp;在node2上添加静态路由</code></pre><p>ip r add 10.1.0.0/24 via 192.168.32.19 dev eth0</p><pre><code>### 修改iptables规则&amp;emsp;&amp;emsp;宿主机如果为centos7，则不需要修改iptables规则，而宿主机如果为ubuntu系统则需要添加forward规则来放行。我仔细看了下这两个系统的iptables规则，发现在centos系统docker创建的iptables规则中对``Chain FORWARD``是默认``ACCEPT``，而ubuntu系统中docker创建的iptables对``Chain FORWARD``是默认``DROP``![dockecentosiptables](https://img-blog.csdnimg.cn/20191207185124292.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70)![dockerubuntuiptables](https://img-blog.csdnimg.cn/20191207185139492.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70)&amp;emsp;&amp;emsp;之后两边各启动一个容器就可以实现相互通信或访问了。## docker网络进阶&amp;emsp;&amp;emsp;之前我们演示了通过docker0网卡的桥接方式实现了容器跨主机访问。我们通过修改docker0网卡的网段设置来实现，每个主机上容器的网段不同。&amp;emsp;&amp;emsp;这种实现方式有个问题就是，但当每次我们修改了docker0网段之后，如果之后打算变更网段，之前的容器都将无法与docker0网桥桥接，导致网络不通，不能使用。&amp;emsp;&amp;emsp;对此我们有一个更灵活的方案来实现容器的跨主机通信。那就是我们还可以通过创建一个或多个自定义网络，将新创建的每个容器指定连接到我们创建的这个网络中，这样他们的网段就是我们设置的这个网络的网段，实现每个主机上容器网段都不相同。### 创建自定义网络&amp;emsp;&amp;emsp;可以将我们之前对docker0网卡的修改还原了的（当然，也可以不修改，出于控制变量方便观察考虑，建议修改回去）。&amp;emsp;&amp;emsp;我们可以通过``docker network create``命令来创建一个自定义网络</code></pre><p>root@DockerUbuntu:~# docker network create –help</p><p>Usage:    docker network create [OPTIONS] NETWORK</p><p>Create a network</p><p>Options:<br>      –attachable           Enable manual container attachment<br>      –aux-address map      Auxiliary IPv4 or IPv6 addresses used by Network driver<br>                             (default map[])<br>      –config-from string   The network from which copying the configuration<br>      –config-only          Create a configuration only network<br>  -d, –driver string        Driver to manage the Network (default “bridge”)<br>      –gateway strings      IPv4 or IPv6 Gateway for the master subnet<br>      –ingress              Create swarm routing-mesh network<br>      –internal             Restrict external access to the network<br>      –ip-range strings     Allocate container ip from a sub-range<br>      –ipam-driver string   IP Address Management Driver (default “default”)<br>      –ipam-opt map         Set IPAM driver specific options (default map[])<br>      –ipv6                 Enable IPv6 networking<br>      –label list           Set metadata on a network<br>  -o, –opt map              Set driver specific options (default map[])<br>      –scope string         Control the network’s scope<br>      –subnet strings       Subnet in CIDR format that represents a network segment</p><pre><code>&amp;emsp;&amp;emsp;例如我们在node1创建一个名为web1的桥接网络，网段为10.10.0.0/24,设置网关为10.10.0.1（可设置为此网段内任意ip）。</code></pre><p>root@DockerUbuntu:<del># docker network create -d bridge –subnet 10.10.0.0/24 –gateway 10.10.0.1 web1<br>a817cf36502eea3469e1cb4b9b7577044f8dce96f015ba57a47f6809c00d72c7<br>root@DockerUbuntu:</del># docker network ls<br>NETWORK ID          NAME                DRIVER              SCOPE<br>afd91b2e1731        bridge              bridge              local<br>241d3e94a6b3        host                host                local<br>7cc9cf9eb69e        none                null                local<br>a817cf36502e        web1                bridge              local<br>root@DockerUbuntu:~# </p><pre><code>&amp;emsp;&amp;emsp;可以用``docker network ls``(或``docker network list``)看到网络类型多了一种，也就是我们刚刚创建的web1类型。而用``ifconfig``或者``ip a``命令也可以看到我们的网卡设备里多了一个``br-a817cf36502e``，ip也恰好是我们指定的``10.10.0.0/24``网段。![自定义网络](https://img-blog.csdnimg.cn/20191207192708651.png)&amp;emsp;&amp;emsp;此时我们就可以通过``--net``选项指定我们刚刚创建的web1网络，来创建并启动容器了。</code></pre><p>root@DockerUbuntu:<del># docker run -it -d -p 8080:8080 -p 8009:8009 –net=web1 tomcat-app1:v1<br>afc1e3db8a6a670d30cdd0756af65da74895976ce5ebbf876329b04b452a3710<br>root@DockerUbuntu:</del># </p><pre><code>&amp;emsp;&amp;emsp;同样，在宿主机node2上也创建一个自定义网络web2，然后新创建的容器，也指定网络为web2，再设置静态路由和修改iptables规则放行，也可以实现容器间跨主机访问。</code></pre>]]></content>
    
    <summary type="html">
    
      跨主机互联是说 A 宿主机的容器可以访问 B 主机上的容器，但是前提是保证各宿主机之间的网络是可以相互通信的， 然后各容器才可以通过宿主机访问到对方的容器， 实现原理是在宿主机做一个网络路由就可以实现 A 宿主机的容器访问 B主机的容器的目的， 复杂的网络或者大型的网络可以使用 google 开源的 k8s 进行互联。本文之后将详细介绍docker网络配置，并演示容器跨主机通信的实现。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="Docker" scheme="https://hewanyue.com/tags/Docker/"/>
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="容器" scheme="https://hewanyue.com/tags/%E5%AE%B9%E5%99%A8/"/>
    
      <category term="跨主机通信" scheme="https://hewanyue.com/tags/%E8%B7%A8%E4%B8%BB%E6%9C%BA%E9%80%9A%E4%BF%A1/"/>
    
      <category term="容器网络" scheme="https://hewanyue.com/tags/%E5%AE%B9%E5%99%A8%E7%BD%91%E7%BB%9C/"/>
    
  </entry>
  
  <entry>
    <title>Docker（二）——基础命令详解</title>
    <link href="https://hewanyue.com/blog/5c39f4ef.html"/>
    <id>https://hewanyue.com/blog/5c39f4ef.html</id>
    <published>2019-12-06T07:02:09.000Z</published>
    <updated>2020-01-20T07:29:36.326Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;安装完Docker的服务，我们就可以开始使用Docker了。</p><a id="more"></a><h2 id="Docker镜像"><a href="#Docker镜像" class="headerlink" title="Docker镜像"></a>Docker镜像</h2><p>&emsp;&emsp;之前我们提到，docker是一个运行容器的工具，可以单独隔离每个服务的运行环境，达到互不干扰和节约资源的目的。而docker运行的容器，是基于一层一层的镜像联合挂载构建而成。所以我们需要先有镜像。<br>&emsp;&emsp;所谓镜像，其实可以理解为，一个个的最简化的安装包，里面只集成了一些必备的程序和文件，且每一层和每一层镜像是可以相互一样的，大大的节约了空间，提高了资源利用率。举个最简单的例子，我们从官方下载一个centos系统的镜像包centos:apline，大小才5.55兆，而centos差不多至少200兆了，而centos安装的ISO镜像文件也都差不多1G左右。这是因为容器使用的镜像系统，需要依赖宿主机内核来运行，容器本身是没有内核的，只包含一些基础功能命令而已。可用<code>docker images</code>查看本地镜像，从大小来看，就知道容器确实很精简。</p><pre><code>root@DockerUbuntu:~# docker imagesREPOSITORY          TAG                 IMAGE ID            CREATED             SIZEalpine              latest              965ea09ff2eb        6 weeks ago         5.55MBcentos              latest              0f3e07c0138f        2 months ago        220MBcentos              7.6.1810            f1cb7c7d58b7        8 months ago        202MB</code></pre><p>&emsp;&emsp;我们可以在docker官方镜像仓库<a href="https://hub.docker.com" target="_blank" rel="external nofollow noopener noreferrer">https://hub.docker.com</a>查看官方镜像,也可以通过命令直接搜索centos的可用容器</p><pre><code>docker search centos</code></pre><p>&emsp;&emsp;注意：官方仓库也有很多是个人的镜像，为避免未知风险，一定要采用官方镜像，千万别使用安全性未知或来源不明的的镜像。一般排在第一个是官方镜像。<br>&emsp;&emsp;使用命令<code>docker pull centos</code>就可以自动从docker官方镜像仓库docker.io，下载centos镜像了，因为我们没有指定版本标签，所以这条命令会默认下载<code>centos:latest</code>版本，也就是最新版。生产中我们出于稳定性和便于管理，都会推荐使用指定的稳定版本，而不会采用latest版本（，随着版本发型，之前的latest版本和几个月之后的latest版很有可能就不是一个版本，不利于规范化统一）。我们本次以CentOS7.6中的最新版<code>centos:7.6.1810</code>最为本次演示的版本。下载镜像命令如下：</p><pre><code>docker pull centos:7.6.1810</code></pre><p>&emsp;&emsp;使用<code>docker image rm [ OPTION ] 容器ID</code>可以删除不要的镜像，如果已有容器基于此镜像启动，则会提示错误，无法删除，需先停止容器，或直接加<code>-f选项</code>强制删除镜像，此时容器也会被停止。注意，当容器被停止时，上面的数据也都会丢失，所以需要谨慎关闭容器和删除镜像。<br>&emsp;&emsp;此外，docker官方镜像站，因为在国外(美国，不过应该是有CDN或者国内镜像加速站点的，不过还是有点点慢)，肯定不如阿里的容器镜像仓库速度快。所以我们可以配置阿里的镜像加速器，需要注册阿里账号。<br>&emsp;&emsp;进入阿里网站<a href="http://cr.console.aliyun.com" target="_blank" rel="external nofollow noopener noreferrer">http://cr.console.aliyun.com</a>，登陆之后，可以看到阿里的镜像仓库(之后再细说)还有镜像加速器，点击镜像加速器，可看到如图所示界面：<br><img src="https://img-blog.csdnimg.cn/20191205220137716.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="Docker镜像加速">&emsp;&emsp;执行将下面代码，便可实现镜像加速了。</p><pre><code>sudo mkdir -p /etc/dockersudo tee /etc/docker/daemon.json &lt;&lt;-&#39;EOF&#39;{  &quot;registry-mirrors&quot;: [&quot;https://xxxxxxxx.mirror.aliyuncs.com&quot;]}EOFsudo systemctl daemon-reloadsudo systemctl restart docker</code></pre><p>&emsp;&emsp;之后使用<code>docker info</code>命令就可以看到已经添加镜像仓库成功<br><img src="https://img-blog.csdnimg.cn/20191205220652786.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="docker info"></p><h2 id="Docker容器"><a href="#Docker容器" class="headerlink" title="Docker容器"></a>Docker容器</h2><p>&emsp;&emsp;有了镜像之后我们就可以从中运行容器了。基础的命令有<code>docker pull</code>、<code>docker push</code>、<code>docker create</code>、<code>docker run</code>、<code>docker ps</code>、<code>docker rm</code>、<code>docker start</code>、<code>docker stop</code>、<code>docker images</code>、<code>docker exec</code>、<code>docker inspect</code>等等</p><h3 id="pull-push"><a href="#pull-push" class="headerlink" title="pull push"></a>pull push</h3><pre><code>root@DockerUbuntu:~# docker pull --helpUsage:    docker pull [OPTIONS] NAME[:TAG|@DIGEST]Pull an image or a repository from a registryOptions:  -a, --all-tags                Download all tagged images in the repository      --disable-content-trust   Skip image verification (default true)  -q, --quiet                   Suppress verbose output</code></pre><p>&emsp;&emsp;<code>docker pull</code>命令我们之前也使用过，可以用来拉镜像，相当于命令<code>docker image pull</code>。根据官方帮助信息，我们可以知道，<code>docker pull</code> 如果要拉去指定版本，需要加<code>:tag</code>版本标签，加<code>-a</code>选项可以拉取所有镜像，不过生产中一般都是用的时候公司内部的本地镜像仓库Harbor，所以拉取镜像时,格式要跟公司Harbor服务器的IP或者域名，指定拉取镜像的源仓库，如果不加，则默认从docker官网拉取。镜像名称格式为：<code>Harbor IP/项目名/image 名字:版本号</code></p><pre><code>docker pull 172.18.32.101/centos/centos-base:v1 </code></pre><p>&emsp;&emsp;<code>docker push</code>是用来推送本地镜像至仓库的,相当于命令<code>docker image push</code>，用法格式与pull相似。<br>使用pull推送至公司Harbor时，需要先在docker启动文件<code>/lib/systemd/system/docker.service</code>的<code>ExecStart选项</code>结尾加入参数<code>--insecure-registry</code>，表示加入不安全的镜像仓库，可加多个，示例如下。</p><pre><code>ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 192.168.32.19 --insecure-registry 192.168.32.20</code></pre><h3 id="tag"><a href="#tag" class="headerlink" title="tag"></a>tag</h3><pre><code>root@DockerUbuntu:~# docker tag --helpUsage:    docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]Create a tag TARGET_IMAGE that refers to SOURCE_IMAGEroot@DockerUbuntu:~# </code></pre><p>&emsp;&emsp;<code>docker tag</code>命令可以用来打标记，格式如上。一般为了区分不同版本，我们会在每次制作完镜像后打上不同的标记，而且要分发到不同的镜像仓库时也要重新打一个对应<code>仓库IP/项目名</code>的镜像，才可以<code>push</code>或<code>pull</code>。</p><h3 id="run"><a href="#run" class="headerlink" title="run"></a>run</h3><p>&emsp;&emsp;<code>docker run</code>可以算是最常用的docker基础命令了，意思是创建并运行容器，相当于<code>docker create</code>和<code>docker start</code>的组合。用法格式如下。</p><pre><code>docker run [ OPTION ] 镜像ID [ CMD ]</code></pre><h4 id="OPTION"><a href="#OPTION" class="headerlink" title="OPTION"></a>OPTION</h4><p>&emsp;&emsp;常用选项有<code>-i</code>、<code>-t</code>、<code>-d</code>、<code>-p</code>、<code>-v</code>、<code>-e</code>、<code>--name</code>、<code>--net</code>。</p><ul><li><code>-i</code>，–interactive， Keep STDIN open even if not attached<br>&emsp;&emsp;以交互式方式运行。不过只加-t是没法实现交互式的，通常需要<code>-t</code>参数配合，来给容器加一个<code>伪终端</code>实现交互式。</li><li><code>-t</code>，–tty， Allocate a pseudo-TTY<br>&emsp;&emsp;给容器分配一个伪终端，与<code>-i</code>配合使用，通常写作<code>-it</code>。</li><li><code>-d</code>，–detach，  Run container in background and print container ID<br>&emsp;&emsp;使容器启动时后台运行，如果不加这个选项，则会占据宿主机当前终端，服务类容器等交互式容器一般都会加上这个选项。</li><li><code>-p</code>，–publish， list Publish a container’s port(s) to the host<br>&emsp;&emsp;映射本地端口到容器的指定端口，同时映射多个端口，则写多个<code>-p 宿主机端口:容器端口</code>选项。</li><li><code>-v</code>，–volume， list Bind mount a volume<br>&emsp;&emsp;使用-v 参数， 将宿主机目录映射到容器内部， 默认是可读写的。<code>-v SOUCE:DEST:ro</code>后面加<code>ro</code>则可实现只读。</li><li><code>-e</code>，–env， list Set environment variables<br>&emsp;&emsp;可用<code>-e</code>选项为容器添加启动时的环境变量。虽然大多环境变量都是在制作容器时就写进容器里，不过此选项应用在环境变量经常变化的场景，方面修改。</li><li><code>--name</code>，Assign a name to the container<br>&emsp;&emsp;给创建的容器命名。同一个宿主机上的容器之间可以通过自定义的容器名称相互访问，由于容器在启动的时候其内部 IP 地址是 DHCP 随机分配的，所以如果通过内部访问的话，自定义名称是相对比较固定的，因此设置容器别名比较适用于此场景。</li><li>–link，Add link to another container<br>&emsp;&emsp;为容器设置一个别名，相当于一个路径名。自定义的容器名称可能后期会发生变化， 那么一旦名称发生变化，程序之间也要随之发生变化，比如程序通过容器名称进行服务调用， 但是容器名称发生变化之后再使用之前的名称肯定是无法成功调用， 每次都进行更改的话又比较麻烦， 因此可以使用自定义别名的方式解决，即容器名称可以随意更，只要不更改别名即可。</li><li><code>--net</code><br>&emsp;&emsp;为容器指定网络。容器网络类型，常见的为bridge、host、null，也可以自己创建自定义网络。可用<code>docker network list</code>查看当前已有的网络。<pre><code>root@DockerUbuntu:~# docker network listNETWORK ID          NAME                DRIVER              SCOPE1eb441f702d2        bridge              bridge              local241d3e94a6b3        host                host                local7cc9cf9eb69e        none                null                local</code></pre>&emsp;&emsp;docker服务安装完成后，默认在每个宿主机会生成一个名称为 docker0 的网卡其 IP 地址都是 172.17.0.1/16，并且会生成三种不同类型的网络，也就是bridge、host和null，分别代表网桥（桥接）、宿主机网络和无网络访问。<br>&emsp;&emsp;如果不加<code>--net</code>选项则默认使用bridge，相当于桥接在宿主机的<code>docker0</code>网卡上。<br>&emsp;&emsp;<code>host</code>网络就是指容器使用宿主机网络，所以端口就不能重复使用，多个使用host网络的容器间端口也不能重复。<br>&emsp;&emsp;<code>null</code>网络指容器只有回环网卡，没有外部网卡，无法与外部交流交流，不常使用，适用于某些不需要与外界通信的数据计算或图形、数据处理服务。<br>&emsp;&emsp;还有一种比较特的类型就是<code>container</code>模式，就是指定与一个已有容器共享网络，格式为<code>--net=container:指定名称或 ID</code> 。使用此模式创建的容器需指定和一个已经存在的容器共享一个网络，而不是和宿主机共享网，新创建的容器不会创建自己的网卡也不会配置自己的 IP，而是和一个已经存在的被指定的容器东西 IP 和端口范围，因此这个容器的端口不能和被指定的端口冲突， 除了网络之外的文件系统、进程信息等仍然保持相互隔离，两个容器的进程可以通过 lo 网卡及容器 IP 进行通信。<h4 id="CMD"><a href="#CMD" class="headerlink" title="CMD"></a>CMD</h4>&emsp;&emsp;<code>docker run</code>后面跟的CMD表示指定启动容器的命令，如果不指定，则为容器默认命令。可用 命令<code>docker inspect 容器ID</code>查看容器详细参数查看容器创建时的的CMD。<br>&emsp;&emsp;因为容器中没有守护进程systemd，所以进程编号PID=1的根进程，就是我们启动容器时指定的CMD命令或创建容器设定的默认CMD。如果这个PID=1的守护进程结束，则整个容器就将被关闭。所以我们一般会指定一个可以占据前台终端的服务来作为守护进程，例如<code>bash</code>或者<code>tail -f /etc/hosts</code>命令，对于后台nginx等服务来说，想让他们作为守护进程启动容器，<code>需要将在后台执行的这个选项关闭</code>，如nginx可以通过<code>docker run -it -d nginx:alpine nginx -g &quot;daemon off;&quot;</code>命令，加上<code>-g &quot;daemon off&quot;</code>选项传递参数或者在配置文件中设置<code>daemon off</code>来关闭后台执行选项，否则容器启动就会因为没有前台进程而终止。为了方便我们修改配置或者重启服务，企业生产中我们都采用<code>tail -f /etc/hosts</code>来作为前台进程，这样我们重启服务容器就不受影响了。<h3 id="ps"><a href="#ps" class="headerlink" title="ps"></a>ps</h3><pre><code>root@DockerUbuntu:~# docker ps --help</code></pre></li></ul><p>Usage:    docker ps [OPTIONS]</p><p>List containers</p><p>Options:<br>  -a, –all             Show all containers (default shows just running)<br>  -f, –filter filter   Filter output based on conditions provided<br>      –format string   Pretty-print containers using a Go template<br>  -n, –last int        Show n last created containers (includes all states) (default -1)<br>  -l, –latest          Show the latest created container (includes all states)<br>      –no-trunc        Don’t truncate output<br>  -q, –quiet           Only display numeric IDs<br>  -s, –size            Display total file sizes</p><pre><code>&amp;emsp;&amp;emsp;``docker ps``命令比较简单，较长使用的参数是``-a``、``-q``，分别是显示所有容器（包括为未运行容器，ps默认只显示正在运行的容器）和只显示容器ID。也可以使用``docker ps -aq``来显示所有容器的ID，可配合其他命令如``docker rm``命令使用。``docker rm -f `docker ps -aq` ``删除本地所有容器（很危险，小心操作！）。### rm&amp;emsp;&amp;emsp;``docker rm``命令是用来删除容器的，如果容器正在运行，使用``docker rm``命令删除时会报错提示``Error response from daemon: You cannot remove a running container XXX. Stop the container before attempting removal or force remove``，告诉我们无法删除运行中的容器，需要先将容器停止或者强制删除，使用``docker rm -f``命令强制删除，或者使用下面的停止命令### start stop&amp;emsp;&amp;emsp;``docker start 容器ID``启动容器，可以加选项``-i``，表示启动容器并输出容器内的标准输出至宿主机终端。需要注意的是，如果当时用``docker run``或者``docker create``命令创建容器时，没有加``-it``或``-d``等一些其它选项或者参数的话，在start容器这一步也没法作出更改了的。容器怎么创建的，启动时就会按照创建时设定好的模式运行，这些是没办法再次修改了的，包括启动CMD。&amp;emsp;&amp;emsp;``docker stop 容器ID``终止容器。需要注意：终止容器时，容器内的数据都将丢失，在备份重要数据之前不要终止容器。### images&amp;emsp;&amp;emsp;``docker images``命令其实相当于``docker image ls``,显示本地所有镜像。``docker image CMD``还有很多，之后介绍镜像管理的时候 在详细介绍。### exec&amp;emsp;&amp;emsp;``docker exec``命令可以向一个已运行容器发送命令。用法如下所示。</code></pre><p>[root@DockerCentOS ~]# docker exec –help</p><p>Usage:    docker exec [OPTIONS] CONTAINER COMMAND [ARG…]</p><p>Run a command in a running container</p><p>Options:<br>  -d, –detach               Detached mode: run command in the background<br>      –detach-keys string   Override the key sequence for detaching a container<br>  -e, –env list             Set environment variables<br>  -i, –interactive          Keep STDIN open even if not attached<br>      –privileged           Give extended privileges to the command<br>  -t, –tty                  Allocate a pseudo-TTY<br>  -u, –user string          Username or UID (format: &lt;name|uid&gt;[:&lt;group|gid&gt;])<br>  -w, –workdir string       Working directory inside the container</p><pre><code>&amp;emsp;&amp;emsp;最常使用的格式是``docker exec -it 容器ID bash/sh``，可以进入正在运行的容器。</code></pre>]]></content>
    
    <summary type="html">
    
      安装完Docker的服务，我们就可以开始使用Docker了。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="Docker" scheme="https://hewanyue.com/tags/Docker/"/>
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="容器" scheme="https://hewanyue.com/tags/%E5%AE%B9%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>Docker（三）——镜像制作</title>
    <link href="https://hewanyue.com/blog/ddd838e7.html"/>
    <id>https://hewanyue.com/blog/ddd838e7.html</id>
    <published>2019-12-06T07:02:09.000Z</published>
    <updated>2020-01-20T07:29:36.323Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;在docker使用过程中，其实大部分时间都是花在了打镜像上，因为容器本身底层不可写，顶层可读写缺无法持久化性质，我们如果对容器进行了修改，想要进行横向扩容，快速部署时，一般需要重新制作镜像，在分发到其他主机或终端。（虽然也可以将数据储存在NFS和宿主机本地，而不是容器内部来方便的修改配置文件及保存数据等。）<br>&emsp;&emsp;docker中镜像的制作方式一般手工修改后导出和通过Dockerfile生成两种方式。</p><a id="more"></a><h2 id="手动制作镜像"><a href="#手动制作镜像" class="headerlink" title="手动制作镜像"></a>手动制作镜像</h2><p>&emsp;&emsp;因为镜像本身的不可修改性，有时候官方镜像中使用的工具的版本可能不是那么符合我们的生产环境，我们就需要自己制作镜像了。一般来说，我们都是基于官方镜像，作出修改来符合自身实际场景中使用，然后在导出保存为我们自己的镜像。<br>&emsp;&emsp;以一个tomcat容器为例，我们如果需要tomcat8的容器，可以直接从官网拉取tomcat8的镜像<code>docker pull tomcat:8.5.49-jdk8-openjdk</code>修改完成后，还可以使用命令<code>docker commit</code>将已有容器制作为镜像.</p><pre><code>root@DockerUbuntu:/opt/dockerfile/web/tomcat/tomcat-apps/app1# docker commit --helpUsage:    docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]Create a new image from a container&#39;s changesOptions:  -a, --author string    Author (e.g., &quot;John Hannibal Smith &lt;hannibal@a-team.com&gt;&quot;)  -c, --change list      Apply Dockerfile instruction to the created image  -m, --message string   Commit message  -p, --pause            Pause container during commit (default true)</code></pre><p>&emsp;&emsp;<code>-a</code>添加镜像制作人信息，<code>-m</code>添加备注信息，<code>-p</code>选项是默认选项，在制作为镜像时暂停容器，<code>-c</code>使用Dockerfile指令来创建镜像，Dockerfile之后我们会详细讲解。例如</p><pre><code>docker commit -a &quot;example@163.com&quot; -m &quot;tomcat app1 v1&quot; --change=&quot;EXPOSE 8080 8009&quot; f5f8c13d0f9f centos-tomcat-app1:v1</code></pre><h2 id="Dockerfile"><a href="#Dockerfile" class="headerlink" title="Dockerfile"></a>Dockerfile</h2><p>&emsp;&emsp;不过如果业务场景要求的配置场景要修改nginx的编译参数或者要求底层是centos7系统这就没法更改了（官方镜像一般都是debian系统），我们只能修改最上层的镜像。此时我们可以通过分层构建的方式来制作镜像(，毕竟docker镜像本来就是分层构建的)。<br>&emsp;&emsp;DockerfileDockerFile 可以说是一种可以被 Docker 程序解释的脚本， DockerFile 是由一条条的命令组成的，每条命令对应 linux 下面的一条命令， Docker 程序将这些 DockerFile 指令再翻译成真正的 linux 命令，其有自己的书写方式和支持的命令， Docker 程序读取 DockerFile 并根据指令生成 Docker 镜像，相比手动制作镜像的方式， DockerFile 更能直观的展示镜像是怎么产生的，有了写好的各种各样 DockerFile 文件，当后期某个镜像有额外的需求时，只要在之前的DockerFile 添加或者修改相应的操作即可重新生成新的 Docke 镜像，避免了重复手动制作镜像的麻烦。<br>&emsp;&emsp;Docker中常用到的命令令有<code>FROM</code>（指定基础镜像名称）,<code>MAINTAINER</code>（镜像作者署名及联系方式）,<code>USER</code>（切换用户身份，初始一般为root）,<code>WORKDIR</code>（指定或切换工作目录），<code>ADD</code>（将当前宿主机目录的文件拷贝至容器指定位置，tar包可以自动解压）,<code>RUN</code>(运行命令，其实就是shell命令，可执行多条，用&amp;&amp;符号连接),<code>ENV</code>（设置环境变量）,<code>CMD</code>（设置默认镜像启动命令，要可以占据前台，否则基于此镜像启动的容器会直接停止），之后我们结合实际例子一一说明。</p><p>&emsp;&emsp;例如我们使用Dockerfile来分层构建定制的tomcat镜像来运行app1服务（当然，也可以一步到位），步骤如下：</p><ol><li>构建目录架构<br>我们通常将Dockerfile文件都放置在<code>/opt/</code>目录下<pre><code>mkdir /opt/dockerfile/{web/{nginx,tomcat,jdk},system/{centos,ubuntu,redhat}} -pv</code></pre></li><li>构建系统镜像<pre><code>cd /opt/dockerfile/system/centosmkdir 7.6cd 7.6docker pull centos:7.6.1810</code></pre>&emsp;&emsp;创建Dockerfile文件,注意D要大写<pre><code>vim Dockerfile#CentOS 7.6 镜像FROM centos:7.6.1810MAINTAINER Mice example@163.comRUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpmRUN yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotopCMD [&quot;bash&quot;]</code></pre>&emsp;&emsp;创建制作镜像脚本，来生成镜像。当然，也可以直接使用<code>docker build</code>命令配合<code>-t</code>参数（指定标签名）直接将当前目录的Dockerfile制作为镜像，使用脚本是为了日后修改不至于每次名称都不一样，可以保证每次打的镜像的名称和版本号统一，否则以后镜像多了会乱（脚本中也可以在标签中加上时间）。<pre><code>vim build_centos.sh#!/bin/bashdocker build -t centos-base:v7.6.1810 .</code></pre>&emsp;&emsp;当前目录结构为<pre><code>root@DockerUbuntu:/opt/dockerfile/system/centos/7.6# tree.├── build_centos.sh└── Dockerfile</code></pre></li></ol><p>0 directories, 2 files</p><pre><code>&amp;emsp;&amp;emsp;执行命令``bash build_centos.sh``来创建第一层镜像``centos-base:v7.6.1810``</code></pre><p>root@DockerUbuntu:/opt/dockerfile/system/centos/7.6# bash build_centos.sh<br>Sending build context to Docker daemon  3.072kB<br>Step 1/5 : FROM centos:7.6.1810<br> —&gt; f1cb7c7d58b7<br>Step 2/5 : MAINTAINER Mice <a href="mailto:example@163.com" target="_blank" rel="external nofollow noopener noreferrer">example@163.com</a><br> —&gt; Using cache<br> —&gt; 3899d2446806<br>Step 3/5 : RUN rpm -ivh <a href="http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm" target="_blank" rel="external nofollow noopener noreferrer">http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm</a><br> —&gt; Using cache<br> —&gt; 5a72857ed63d<br>Step 4/5 : RUN yum install -y vim wget tree lrzsz gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop<br> —&gt; Using cache<br> —&gt; 705fed38cb94<br>Step 5/5 : CMD [“bash”]<br> —&gt; Running in aea451be0461<br>Removing intermediate container aea451be0461<br> —&gt; 160b9544f121<br>Successfully built 160b9544f121<br>Successfully tagged centos-base:v7.6.1810</p><pre><code>&amp;emsp;&amp;emsp;有时到yum那步会提示报错，无法解析IP。多执行几次脚本，多试几次就可以了。他会有缓存自动保存镜像，已经写好的层数会自动缓存的，如上面的`` ---&gt; Using cache``。 3. 构建适合版本的jdk镜像</code></pre><p>cd /opt/dockerfile/web/jdk/<br>mkdir 8u212<br>cd 8u212</p><pre><code>&amp;emsp;&amp;emsp;将准备好的jdk压缩包``jdk-8u212-linux-x64.tar.gz``放入此目录，然后还是编写``Dockerfile``以及``build_jdk.sh``脚本</code></pre><p>vim Dockerfile<br>#JDK 8u212<br>FROM centos-base:v7.6.1810<br>MAINTAINER Mice <a href="mailto:example@163.com" target="_blank" rel="external nofollow noopener noreferrer">example@163.com</a><br>ADD jdk-8u212-linux-x64.tar.gz /usr/local/src/<br>ADD env.sh /etc/profile.d/<br>RUN ln -sv /usr/local/src/jdk1.8.0_212 /usr/local/jdk &amp;&amp; groupadd www -g 2019 &amp;&amp; useradd www -u 2019 -g www</p><p>ENV JAVA_HOME /usr/java/default<br>ENV PATH $JAVA_HOME/bin:$PATH<br>ENV JRE_HOME $JAVA_HOME/jre<br>ENV CLASSPATH $JAVA_HOME/lib/:$JRE_HOME/lib/</p><p>RUN rm -rf /etc/localtime &amp;&amp; ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &amp;&amp; echo “Asia/Shanghai” &gt; /etc/timezone</p><pre><code></code></pre><p>vim build_jdk.sh<br>#!/bin/bash<br>docker build -t jdk-base:v1.8.0_212 .</p><pre><code>&amp;emsp;&amp;emsp;需要注意的是，因为ENV环境变量是当前用户（当前终端）有效，也就是说对于容器本身来说，他运行的环境变量是已经通过ENV可以设置好，可是如果发生故障，我们需要连接进入容器时，这个ENV就对我们当前终端无效了，我们可能就无法使用那些ENV设置了的PATH变量了，所以我们需要添加一个``环境变量配置文件``至``/etc/profile.d目录``（也可直接替换profile文件），以便这些环境变量在我们连接至容器后也可以生效。</code></pre><p>vim env.sh<br>JAVA_HOME=/usr/local/jdk<br>JRE_HOME=$JAVA_HOME/jre<br>CLASSPATH=$JAVA_HOME/lib/:$JRE_HOME/lib/<br>PATH=$PATH:$JAVA_HOME/bin</p><pre><code>&amp;emsp;&amp;emsp;目录结构如下所示</code></pre><p>root@DockerUbuntu:/opt/dockerfile/web/jdk/8u212# tree<br>.<br>├── build_jdk.sh<br>├── Dockerfile<br>├── env.sh<br>└── jdk-8u212-linux-x64.tar.gz</p><p>0 directories, 4 files</p><pre><code>&amp;emsp;&amp;emsp;还是通过build脚本，构建jdk容器。 4. 构建适合版本的tomcat镜像先创建版本目录(，为日后可能需要不同版本的tomcat弄好框架)。</code></pre><p>cd /opt/dockerfile/web/tomcat/<br>mkdir 8.5.47<br>cd 8</p><pre><code>&amp;emsp;&amp;emsp;将准备好的tomcat源码包拷到这个目录，或者wget下载</code></pre><p>wget <a href="http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.47/bin/apache-tomcat-8.5.47.tar.gz" target="_blank" rel="external nofollow noopener noreferrer">http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.47/bin/apache-tomcat-8.5.47.tar.gz</a></p><pre><code>&amp;emsp;&amp;emsp;制作Dockerfile</code></pre><p>vim Dockerfile<br>#tomcat 8-jdk 1.8.0_212-centos 7.6<br>FROM jdk-base:v1.8.0_212<br>MAINTAINER Mice <a href="mailto:example@163.com" target="_blank" rel="external nofollow noopener noreferrer">example@163.com</a></p><p>ENV TZ “Asia/Shanghai”<br>ENV LANG en_US.UTF-8<br>ENV TERM xterm<br>ENV TOMCAT_MAJOR_VERSION 8<br>ENV TOMCAT_MINOR_VERSION 8.5.47<br>ENV CATALINA_HOME /apps/tomcat<br>ENV APP_DIR ${CATALINA_HOME}/webapps</p><p>RUN mkdir /apps<br>ADD apache-tomcat-8.5.47.tar.gz /apps/<br>RUN ln -sv /apps/apache-tomcat-8.5.47 /apps/tomcat</p><pre><code>&amp;emsp;&amp;emsp;老规矩，创建build脚本</code></pre><p>vim build_tomcat.sh<br>#!/bin/bash<br>docker build -t tomcat-base:v8.5.47 .</p><pre><code>&amp;emsp;&amp;emsp;然后执行脚本打镜像~ 5. 放置项目app1至指定目录构建镜像&amp;emsp;&amp;emsp;到现在，tomcat容器的环境都配置好了，不过里面还没有跑服务，所以可以针对不同的业务，创建不同的镜像，而他们底层都是共用的一个基础镜像``tomcat-base:v8.5.47``，所有底层镜像因为都是只读的，所以可以复用而互不干扰，也不会重复占用多余的空间。还是先创建业务APP目录。</code></pre><p>mkdir -pv /opt/dockerfile/web/tomcat/myapps/app1/<br>cd /opt/dockerfile/web/tomcat/myapps/app1/</p><pre><code>&amp;emsp;&amp;emsp;因为当app1有变化时，tomcat服务可能会需要重启才会生效变化，而如果tomcat服务是容器的启动进程(即PID=1的进程)时，重启tomcat会导致容器终止，容器里面在运行的数据及session都会丢失。所以我们使用``tail -f``命令来作为这个容器的守护进程来启动容器。我们可以通过构建一个脚本``run_tomcat.sh``来实现，启动tomcat并让tail -f 最为前台进程。</code></pre><p>vim run_tomcat.sh<br>#!/bin/bash<br>su - www -c “/apps/tomcat/bin/catalina.sh start”<br>su - www -c “tail -f /etc/hosts”</p><pre><code>&amp;emsp;&amp;emsp;且为了安全考虑，我们打算让tomcat服务以www用户身份启动，所以在构建容器时，需要注意权限问题，Dockerfile如下：</code></pre><p>vim Dockerfile</p><h1 id="tomcat-apps"><a href="#tomcat-apps" class="headerlink" title="tomcat-apps"></a>tomcat-apps</h1><p>FROM tomcat-base:v8.5.47<br>ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh<br>ADD app1/* /apps/tomcat/webapps/myapp/<br>RUN chown <a href="http://www.www" target="_blank" rel="external nofollow noopener noreferrer">www.www</a> /apps/ -R<br>EXPOSE 8080 8009<br>CMD [“/apps/tomcat/bin/run_tomcat.sh”]</p><pre><code>&amp;emsp;&amp;emsp;而且我们把``run_tomcat.sh``传进去后，要以脚本启动的话，要对脚本加执行权限，这样传进去的时候也是有执行权限的。</code></pre><p>chmod +x run_tomcat.sh</p><pre><code>&amp;emsp;&amp;emsp;然后构建build脚本</code></pre><p>vim build_app1.sh<br>#!/bin/bash<br>docker build -t tomcat-app1:v1 .</p><pre><code>&amp;emsp;&amp;emsp;然后将APP1的程序代码拷贝至当前目录，结构示意图如下：</code></pre><p>root@DockerUbuntu:/opt/dockerfile/web/tomcat/myapps/app1# tree<br>.<br>├── build_app1.sh<br>├── Dockerfile<br>├── app1<br>│   └── index.html<br>└── run_tomcat.sh</p><p>1 directory, 4 files</p><pre><code>&amp;emsp;&amp;emsp;就可以执行build脚本打镜像啦。</code></pre><p>bash build_app1.sh</p><pre><code>&amp;emsp;&amp;emsp;需要注意的是，启动镜像时记得加``端口映射``，命令如下</code></pre><p>docker run -it -d -p 8080:8080 -p 8009:8009 tomcat-app1:v1</p><pre><code>&amp;emsp;&amp;emsp;此时通过``ss -tanl``命令就可以看到8080、80009端口已经被docker proxy监听了。说明服务启动成功。如果宿主机为centos，或者redhat（ubuntu默认是开启的），可能会需要先打开内核参数ip_forward选项，否则会报错网络不可用:</code></pre><p>WARNING: IPv4 forwarding is disabled. Networking will not work.</p><pre><code>&amp;emsp;&amp;emsp;那就开启IP转发。</code></pre><p>vim /etc/sysctl.conf<br>net.ipv4.ip_forward=1</p><pre><code>&amp;emsp;&amp;emsp;然后``sysctl -p``生效就可以正常使用容器网络了。## 注意事项&amp;emsp;&amp;emsp;需要注意的是，``RUN``命令是类似启动一个新的进程或者是shell，来执行每一次命令，执行完毕后此次``RUN``进程结束，下一次是一个全新的``RUN``进程了，相互之间不会联系。这么说可能大家无法理解，举个最简单的例子吧，就是当我们想要编译安装haproxy的时候，需要进入编译目录然后执行``make``命令，编译完后还要在编译目录执行``make install``命令。所以可以写成</code></pre><p>RUN cd /usr/localk/haproxy-2.0.5 &amp;&amp; make –xxx参数选项省略xxxx  &amp;&amp; make install</p><pre><code>&amp;emsp;&amp;emsp;但是如果我们像写脚本那样，写成</code></pre><p>RUN cd /usr/localk/haproxy-2.0.5<br>RUN make –xxx参数选项省略xxxx<br>RUN make install</p><pre><code>&amp;emsp;&amp;emsp;或者</code></pre><p>RUN cd /usr/localk/haproxy-2.0.5 &amp;&amp; make –xxx参数选项省略xxxx<br>RUN make install</p><pre><code>&amp;emsp;&amp;emsp;就会报错找不到路径，因为没一条命令都是一层独立的镜像，每层镜像开始都会在默认的初始目录，所以如果打算将``make``和``make install``分开写，则两次都加``cd /usr/local/haproxy-2.0.5``,或者直接切换工作目录，如下所示。</code></pre><p>WORKDIR /usr/local/src/haproxy-2.0.5<br>RUN make ARCH=x86_64 <br>TARGET=linux-glibc USE_PCRE=1 <br>USE_OPENSSL=1 <br>USE_ZLIB=1 <br>USE_CPU_AFFINITY=1 <br>USE_LUA=1 <br>LUA_INC=../lua-5.3.5/src/ <br>LUA_LIB=../lua-5.3.5/src/ <br>PREFIX=/apps/haproxy<br>RUN make install PREFIX=/apps/haproxy</p><pre><code>&amp;emsp;&amp;emsp;``WORKDIR``和``USER``很类似，都是修改后，对后续Dockerfile指令有效。且``WORKDIR``还支持相对路径，例如</code></pre><p>WORKDIR /a<br>WORKDIR b<br>WORKDIR c</p><pre><code>&amp;emsp;&amp;emsp;则最终的工作目录为``/a/b/c``。</code></pre>]]></content>
    
    <summary type="html">
    
      在docker使用过程中，其实大部分时间都是花在了打镜像上，因为容器本身底层不可写，顶层可读写缺无法持久化性质，我们如果对容器进行了修改，想要进行横向扩容，快速部署时，一般需要重新制作镜像，在分发到其他主机或终端。（虽然也可以将数据储存在NFS和宿主机本地，而不是容器内部来方便的修改配置文件及保存数据等。） docker中镜像的制作方式一般手工修改后导出和通过Dockerfile生成两种方式。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="Docker" scheme="https://hewanyue.com/tags/Docker/"/>
    
      <category term="Dockerfile" scheme="https://hewanyue.com/tags/Dockerfile/"/>
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="容器" scheme="https://hewanyue.com/tags/%E5%AE%B9%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>Docker（一）——基础概念及部署</title>
    <link href="https://hewanyue.com/blog/eaa1ac6e.html"/>
    <id>https://hewanyue.com/blog/eaa1ac6e.html</id>
    <published>2019-12-04T01:58:21.000Z</published>
    <updated>2020-01-20T07:29:36.321Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;在企业生产应用中，docker容器技术及k8s的编排管理工具的使用率越来越高，这项技术甚至已经改变了很多企业的架构与框架流程，因为容器技术的出现，可以将应用以集装箱的方式打包交付，使应用在不同的团队中共享，通过镜像的方式应用可以部署于任何环境中。这样避免了各团队之间的协作问题的出现，成为企业实现DevOps目标的重要工具，而且以容器方式交付的Docker技术支持不断地开发迭代，提升了产品开发和交付速度，极大的方便了业务的横向扩容。<br>&emsp;&emsp;且与KVM虚拟化技术不同的是，Docker直接移植于Linux内核之上，通过运行Linux进程将底层设备虚拟隔离，这样系统性能的损耗也要比虚拟机低的多，几乎可以忽略。同时，Docker应用容器的启停非常高效，可以支持大规模的分布系统的水平扩展，真正给企业开发带来福音。</p><a id="more"></a><h2 id="Docker简介"><a href="#Docker简介" class="headerlink" title="Docker简介"></a>Docker简介</h2><h3 id="Docker-是什么"><a href="#Docker-是什么" class="headerlink" title="Docker 是什么"></a>Docker 是什么</h3><p>&emsp;&emsp;首先 Docker 是一个在 2013 年开源的应用程序并且是一个基于 go 语言编写是一个开源的 PAAS 服务(Platform as a Service， 平台即服务的缩写)， go 语言是由google 开发， docker 公司最早叫 dotCloud 后由于 Docker 开源后大受欢迎就将公司改名为 Docker Inc， 总部位于美国加州的旧金山， Docker 是基于 linux 内核实现， Docker 最早采用 LXC 技术(LinuX Container 的简写， LXC 是 Linux 原生支持的容器技术， 可以提供轻量级的虚拟化， 可以说 docker 就是基于 LXC 发展起来的，提供 LXC 的高级封装，发展标准的配置方法)， 而虚拟化技术 KVM(Kernelbased Virtual Machine) 基于模块实现， Docker 后改为自己研发并开源的 runc 技术运行容器。</p><h3 id="Dokcer与虚拟机技术对比"><a href="#Dokcer与虚拟机技术对比" class="headerlink" title="Dokcer与虚拟机技术对比"></a>Dokcer与虚拟机技术对比</h3><p>&emsp;&emsp;Docker 相比虚拟机的交付速度更快， 资源消耗更低， Docker 采用客户端/服务端架构，使用远程 API 来管理和创建 Docker 容器，其可以轻松的创建一个轻量级的、 可移植的、自给自足的容器， docker 的三大理念是 build(构建)、ship(运输)、 run(运行)， Docker 遵从 apache 2.0 协议，并通过（namespace 及cgroup 等）来提供容器的资源隔离与安全保障等，所以 Docke 容器在运行时不需要类似虚拟机（空运行的虚拟机占用物理机 6-8%性能）的额外资源开销，因此可以大幅提高资源利用率,总而言之 Docker 是一种用了新颖方式实现的轻量级虚拟机.类似于 VM 但是在原理和应用上和 VM 的差别还是很大的，并且 docker的专业叫法是应用容器(Application Container)。<br>| 优势 | Docker | 虚拟机 |<br>|–|–|–|<br>| 资源利用率更高 |  一台物理机可以运行数百个容器 | 但是一般只能运行数十个虚拟机 |<br>| 开销更小 |  不需要启动单独的虚拟机占用硬件资源 |  需要启动单独的虚拟机占用硬件资源 |<br>| 启动速度更快 | 可以在数秒内完成启动 | 需要几十秒甚至数分钟 |</p><p>&emsp;&emsp;使用虚拟机是为了更好的实现服务运行环境隔离， 每个虚拟机都有独立的内核，虚拟化可以实现不同操作系统的虚拟机，但是通常一个虚拟机只运行一个服务， 很明显资源利用率比较低且造成不必要的性能损耗， 我们创建虚拟机的目的是为了运行应用程序，比如 Nginx、 PHP、 Tomcat 等 web 程序， 使用虚拟机无疑带来了一些不必要的资源开销，但是容器技术则基于减少中间运行环节带来较大的性能提升。</p><h3 id="Docker-的组成"><a href="#Docker-的组成" class="headerlink" title="Docker 的组成"></a>Docker 的组成</h3><ul><li>Docker 主机(Host)： 一个物理机或虚拟机，用于运行 Docker 服务进程和容器。</li><li>Docker 服务端(Server)： Docker 守护进程， 运行 docker 容器。</li><li>Docker 客户端(Client)： 客户端使用 docker 命令或其他工具调用 docker API。</li><li>Docker 仓库(Registry): 保存镜像的仓库，类似于 git 或 svn 这样的版本控制系统，官方仓库: <a href="https://hub.docker.com/" target="_blank" rel="external nofollow noopener noreferrer">https://hub.docker.com/</a></li><li>Docker 镜像(Images)： 镜像可以理解为创建实例使用的模板。</li><li>Docker 容器(Container): 容器是从镜像生成对外提供服务的一个或一组服务。</li></ul><p>详细介绍参见官方文档<a href="https://docs.docker.com/engine/docker-overview/" target="_blank" rel="external nofollow noopener noreferrer">https://docs.docker.com/engine/docker-overview/</a></p><h2 id="Docker实现需要解决的问题"><a href="#Docker实现需要解决的问题" class="headerlink" title="Docker实现需要解决的问题"></a>Docker实现需要解决的问题</h2><p>&emsp;&emsp;容器技术确实有很多优点，不过当使用多个容器时带来的以下问题怎么解决:<br>&emsp;&emsp;1.怎么样保证每个容器都有不同的文件系统并且能互不影响？<br>&emsp;&emsp;2.一个 docker 主进程内的各个容器都是其子进程，那么实现同一个主进程下不同类型的子进程？ 各个进程间通信能相互访问(内存数据)吗？<br>&emsp;&emsp;3.每个容器怎么解决 IP 及端口分配的问题？<br>&emsp;&emsp;4.多个容器的主机名能一样吗？<br>&emsp;&emsp;5.每个容器都要不要有 root 用户？怎么解决账户重名问题？<br>&emsp;&emsp;这就不得不引入一个Namespace的概念了。</p><h3 id="Namespace"><a href="#Namespace" class="headerlink" title="Namespace"></a>Namespace</h3><p>&emsp;&emsp;namespace 是 Linux 系统的底层概念， 在内核层实现，即有一些不同类型的命名空间被部署在核内， 各个 docker 容器运行在同一个 docker 主进程并且共用同一个宿主机系统内核，各 docker 容器运行在宿主机的用户空间， 每个容器都要有类似于虚拟机一样的相互隔离的运行空间， 但是容器技术是在一个进程内实现运行指定服务的运行环境， 并且还可以保护宿主机内核不受其他进程的干扰和影响， 如文件系统空间、网络空间、进程空间等。<br>| 隔离类型 | 实现功能 | 系统调用参数 | 内核版本 |<br>|–|–|–|–|<br>| MNT Namespace(mount) | 提供磁盘挂载点和文件系统的隔离能力 | CLONE_NEWNS | Linux 2.4.19 |<br>| IPC Namespace(Inter-Process Communication) |  提供进程间通信的隔离能力 | CLONE_NEWIPC | Linux 2.6.19 |<br>| UTS Namespace(UNIX Timesharing System) | 提供主机名隔离能力 | CLONE_NEWUTS | Linux 2.6.19 |<br>| PID Namespace(Process Identification) | 提供进程隔离能力 | CLONE_NEWPID | Linux 2.6.24 |<br>| Net Namespace(network) | 提供网络隔离能力 | CLONE_NEWNET | Linux 2.6.29 |<br>| User Namespace(user) | 提供用户隔离能力 | CLONE_NEWUSER | Linux 3.8 |</p><ul><li>MNT Namespace：每个容器都要有独立的根文件系统有独立的用户空间， 以实现在容器里面启动服务并且使用容器的运行环境，容器里面是不能访问宿主机的资源， 宿主机是使用了 chroot 技术把容器锁定到一个指定的运行目录里面。</li><li>IPC Namespace：一个容器内的进程间通信， 允许一个容器内的不同进程的(内存、 缓存等)数据访问，但是不能跨容器访问其他容器的数据。</li><li>UTS Namespace：UTS namespace（UNIX Timesharing System 包含了运行内核的名称、版本、底层体系结构类型等信息）用于系统标识， 其中包含了 hostname 和域名domainname ， 它使得一个容器拥有属于自己 hostname 标识，这个主机名标识独立于宿主机系统和其上的其他容器。</li><li>PID Namespace：Linux 系统中，有一个 PID 为 1 的进程(init/systemd)是其他所有进程的父进程， 那么在每个容器内也要有一个父进程来管理其下属的子进程，那么多个容器的进程通 PID namespace 进程隔离(比如 PID 编号重复、 器内的主进程生成与回收子进程等)。</li><li>Net Namespace：每一个容器都类似于虚拟机一样有自己的网卡、 监听端口、 TCP/IP 协议栈等，Docker 使用 network namespace 启动一个 vethX 接口，这样你的容器将拥有它自己的桥接 ip 地址，通常是 docker0，而 docker0 实质就是 Linux 的虚拟网桥,网桥是在 OSI 七层模型的数据链路层的网络设备，通过 mac 地址对网络进行划分，并且在不同网络直接传递数据。</li><li>User Namespace：User Namespace 允许在各个宿主机的各个容器空间内创建相同的用户名以及相同的用户 UID 和 GID， 只是会把用户的作用范围限制在每个容器内，即 A 容器和 B 容器可以有相同的用户名称和 ID 的账户，但是此用户的有效范围仅是当前容器内， 不能访问另外一个容器内的文件系统，即相互隔离、互补影响、 永不相见。</li></ul><p>&emsp;&emsp;通过这些内核级功能的实现，docker才可以正常工作，实现不同容器间的各种资源的隔离，形成共享同一组硬件及内核上却互不影响的独立应用级虚拟化系统。不过此时，我们还面临一个问题，就是资源使用率的控制。如果一个容器因为BUG或代码本身等原因导致无限制的使用宿主机上的资源，将会导致宿主机CPU或者内存不足进而极有可能影响到其他容器的正常运行。所以我们需要对每个容器的资源利用做一个限制，我们通过内核中的Linux Cgroups功能来限制每个容器能使用的资源的上限。</p><h3 id="Linux-Cgroups"><a href="#Linux-Cgroups" class="headerlink" title="Linux Cgroups"></a>Linux Cgroups</h3><p>&emsp;&emsp;Linux Cgroups 的全称是 Linux Control Groups， 它最主要的作用，就是限制一个进程组能够使用的资源上限，包括 CPU、内存、磁盘、网络带宽等等。此外，还能够对进程进行优先级设置，以及将进程挂起和恢复等操作。<br>&emsp;&emsp;Cgroups 在内核层默认已经开启，可通过下列命令验证查看Cgroups设置<br>&emsp;&emsp;CentOS7.6:</p><pre><code>[root@localhost ~]# cat /boot/config-3.10.0-957.el7.x86_64 |grep CGROUPCONFIG_CGROUPS=y# CONFIG_CGROUP_DEBUG is not setCONFIG_CGROUP_FREEZER=yCONFIG_CGROUP_PIDS=yCONFIG_CGROUP_DEVICE=yCONFIG_CGROUP_CPUACCT=yCONFIG_CGROUP_HUGETLB=yCONFIG_CGROUP_PERF=yCONFIG_CGROUP_SCHED=yCONFIG_BLK_CGROUP=y# CONFIG_DEBUG_BLK_CGROUP is not setCONFIG_NETFILTER_XT_MATCH_CGROUP=mCONFIG_NET_CLS_CGROUP=yCONFIG_NETPRIO_CGROUP=y[root@localhost ~]# </code></pre><p>&emsp;&emsp;ubuntu1804中:</p><pre><code>root@DockerUbuntu:~# cat /boot/config-4.15.0-70-generic |grep CGROUPCONFIG_CGROUPS=yCONFIG_BLK_CGROUP=y# CONFIG_DEBUG_BLK_CGROUP is not setCONFIG_CGROUP_WRITEBACK=yCONFIG_CGROUP_SCHED=yCONFIG_CGROUP_PIDS=yCONFIG_CGROUP_RDMA=yCONFIG_CGROUP_FREEZER=yCONFIG_CGROUP_HUGETLB=yCONFIG_CGROUP_DEVICE=yCONFIG_CGROUP_CPUACCT=yCONFIG_CGROUP_PERF=yCONFIG_CGROUP_BPF=y# CONFIG_CGROUP_DEBUG is not setCONFIG_SOCK_CGROUP_DATA=yCONFIG_NETFILTER_XT_MATCH_CGROUP=mCONFIG_NET_CLS_CGROUP=mCONFIG_CGROUP_NET_PRIO=yCONFIG_CGROUP_NET_CLASSID=yroot@DockerUbuntu:~# </code></pre><p>&emsp;&emsp;可以看到内核较新的 ubuntu 支持的功能更多。</p><h4 id="cgroups-参数具体解释："><a href="#cgroups-参数具体解释：" class="headerlink" title="cgroups 参数具体解释："></a>cgroups 参数具体解释：</h4><pre><code>blkio：块设备 IO 限制。cpu：使用调度程序为 cgroup 任务提供 cpu 的访问。cpuacct：产生 cgroup 任务的 cpu 资源报告。cpuset：如果是多核心的 cpu，这个子系统会为 cgroup 任务分配单独的 cpu和内存。devices：允许或拒绝 cgroup 任务对设备的访问。freezer：暂停和恢复 cgroup 任务。memory：设置每个 cgroup 的内存限制以及产生内存资源报告。net_cls：标记每个网络包以供 cgroup 方便使用。ns：命名空间子系统。perf_event：增加了对每 group 的监测跟踪的能力，可以监测属于某个特定的group 的所有线程以及运行在特定 CPU 上的线程。</code></pre><p>&emsp;&emsp;可以通过命令<code>ll /sys/fs/cgroup/</code>查看系统Cgroups</p><h2 id="Docker依赖的技术"><a href="#Docker依赖的技术" class="headerlink" title="Docker依赖的技术"></a>Docker依赖的技术</h2><p>&emsp;&emsp;Docker容器技术如果真正想在企业中应用，还必须依赖下面的一些技术，会在之后的文章中详细介绍。</p><h3 id="容器网络："><a href="#容器网络：" class="headerlink" title="容器网络："></a>容器网络：</h3><p>&emsp;&emsp;docker 自带的网络 docker network 仅支持管理单机上的容器网络， 当多主机运行的时候需要使用第三方开源网络，例如 calico、 flannel 等。</p><h3 id="服务发现："><a href="#服务发现：" class="headerlink" title="服务发现："></a>服务发现：</h3><p>&emsp;&emsp;容器的动态扩容特性决定了容器 IP 也会随之变化， 因此需要有一种机制可以自动识别并将用户请求动态转发到新创建的容器上， kubernetes 自带服务发现功能，需要结合 kube-dns 服务解析内部域名。</p><h3 id="容器监控："><a href="#容器监控：" class="headerlink" title="容器监控："></a>容器监控：</h3><p>&emsp;&emsp;可以通过原生命令 docker ps/top/stats 查看容器运行状态，另外也可以使<br>heapster/ Prometheus 等第三方监控工具监控容器的运行状态。</p><h3 id="数据管理："><a href="#数据管理：" class="headerlink" title="数据管理："></a>数据管理：</h3><p>&emsp;&emsp;容器的动态迁移会导致其在不同的 Host 之间迁移，因此如何保证与容器相关的数据也能随之迁移或随时访问，可以使用逻辑卷/存储挂载等方式解决。</p><h3 id="日志收集："><a href="#日志收集：" class="headerlink" title="日志收集："></a>日志收集：</h3><p>&emsp;&emsp;docker 原生的日志查看工具 docker logs， 但是容器内部的日志需要通过 ELK 等专门的日志收集分析和展示工具进行处理。</p><h2 id="Docker部署"><a href="#Docker部署" class="headerlink" title="Docker部署"></a>Docker部署</h2><p>&emsp;&emsp;我们初步知道了Docker的概念和实现原理，也知道了Docker在使用中可能会遇到的问题，说了这么多，现在我们先将Docker部署一下。<br>&emsp;&emsp;Docker常见的安装方式有三种，可以通过rpm包下载，或者二进制安装，也可以通过epel源安装。<br>&emsp;&emsp;官方 rpm 包下载地址:<br>&emsp;&emsp;<a href="https://download.docker.com/linux/centos/7/x86_64/stable/Packages/" target="_blank" rel="external nofollow noopener noreferrer">https://download.docker.com/linux/centos/7/x86_64/stable/Packages/</a><br>&emsp;&emsp;二进制下载地址：<br>&emsp;&emsp;<a href="https://download.docker.com/" target="_blank" rel="external nofollow noopener noreferrer">https://download.docker.com/</a><br><a href="https://mirrors.aliyun.com/docker-ce/linux/static/stable/x86_64/" target="_blank" rel="external nofollow noopener noreferrer">https://mirrors.aliyun.com/docker-ce/linux/static/stable/x86_64/</a><br>&emsp;&emsp;阿里镜像下载地址：<br>&emsp;&emsp;<a href="https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/" target="_blank" rel="external nofollow noopener noreferrer">https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/</a><br>&emsp;&emsp;Ubuntu的安装docker-ce阿里云镜像仓库方式如下（使用 apt-get 进行安装）:</p><pre><code># step 1: 安装必要的一些系统工具sudo apt-get updatesudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common# step 2: 安装GPG证书curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -# Step 3: 写入软件源信息sudo add-apt-repository &quot;deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable&quot;# Step 4: 更新并安装Docker-CEsudo apt-get -y updatesudo apt-get -y install docker-ce# 安装指定版本的Docker-CE:# Step 1: 查找Docker-CE的版本:# apt-cache madison docker-ce#   docker-ce | 17.03.1~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages#   docker-ce | 17.03.0~ce-0~ubuntu-xenial | https://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages# Step 2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.1~ce-0~ubuntu-xenial)# sudo apt-get -y install docker-ce=[VERSION]</code></pre><p>&emsp;&emsp;<strong>注意</strong>：在与 kubernetes 结合使用的时候，要安装经过 kubernetes 官方测试通过的 docker版本， 避免出现不兼容等未知的及不可预估的问题发生， kubernetes 测试过的docker 版本可以在 github 查询， 具体如下：<br><a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.14.md#external-dependencies" target="_blank" rel="external nofollow noopener noreferrer">https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.14.md#external-dependencies</a><br>&emsp;&emsp;安装完成后就可以用<code>systemctl start docker</code>命令启动Docker了。<br>&emsp;&emsp;然后用<code>docker info</code>命令来验证当前容器信息</p><pre><code>docker info</code></pre><pre><code>Containers: 0 #当前主机运行的容器总数Running: 0 #有几个容器是正在运行的Paused: 0 #有几个容器是暂停的Stopped: 0 #有几个容器是停止的Images: 0 #当前服务器的镜像数Server Version: 18.09.9 #服务端版本Storage Driver: overlay2 #正在使用的存储引擎Backing Filesystem: extfs #后端文件系统，即服务器的磁盘文件系统Supports d_type: true #是否支持 d_typeNative Overlay Diff: true #是否支持差异数据存储Logging Driver: json-file #日志类型Cgroup Driver: cgroupfs #Cgroups 类型Plugins: #插件Volume: local #卷Network: bridge host macvlan null overlay # overlay 夸主机通信Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog #日志类型Swarm: inactive #是否支持 swarmRuntimes: runc #已安装的容器运行时Default Runtime: runc #默认使用的容器运行时Init Binary: docker-init #初始化容器的守护进程，即 pid 为 1 的进程containerd version: 894b81a4b802e4eb2a91d1ce216b8817763c29fb #版本runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f # runc 版本init version: fec3683 #init 版本Security Options: #安全选项Apparmor #安全模块， https://docs.docker.com/engine/security/apparmor/seccomp #审计(操作)， https://docs.docker.com/engine/security/seccomp/Profile: default #默认的配置文件Kernel Version: 4.15.0-55-generic #宿主机内核版本Operating System: Ubuntu 18.04.3 LTS #宿主机操作系统OSType: linux #宿主机操作系统类型Architecture: x86_64 #宿主机架构CPUs: 2 #宿主机 CPU 数量Total Memory: 3.83GiB #宿主机总内存Name: DockerUbuntu #宿主机 hostnameID: ZFPD:UIA5:SR6E:Y6SS:52QL:5MPT:VDY3:ATVI:QMVG:HAFF:MN74:2HPD #宿主机IDDocker Root Dir: /var/lib/docker #宿主机数据保存目录Debug Mode (client): false #client 端是否开启 debugDebug Mode (server): false #server 端是否开启 debugRegistry: https://index.docker.io/v1/ #镜像仓库Labels: #其他标签Experimental: false #是否测试版Insecure Registries: #非安全的镜像仓库127.0.0.0/8Live Restore Enabled: false #是否开启活动重启(重启 docker-daemon 不关闭容器)Product License: Community Engine #产品许可信息</code></pre><p>在结尾可能会有类似<code>warning警报</code>,</p><pre><code>WARNING: No swap limit support</code></pre><p>这是提示说我们没有开启 swap 资源限制，这就需要我们通过修改内核参数，来限制swap资源的使用。</p><pre><code>vim /etc/default/grub</code></pre><pre><code>GRUB_CMDLINE_LINUX=&quot;net.ifnames=0 biosdevname=0 cgroup_enable=memory swapaccount=1&quot;</code></pre><pre><code> update-grub reboot</code></pre><p>&emsp;&emsp;重启生效。至此，docker工具部署就完成了~</p>]]></content>
    
    <summary type="html">
    
      在企业生产应用中，docker容器技术及k8s的编排管理工具的使用率越来越高，这项技术甚至已经改变了很多企业的架构与框架流程，因为容器技术的出现，可以将应用以集装箱的方式打包交付，使应用在不同的团队中共享，通过镜像的方式应用可以部署于任何环境中。这样避免了各团队之间的协作问题的出现，成为企业实现DevOps目标的重要工具，而且以容器方式交付的Docker技术支持不断地开发迭代，提升了产品开发和交付速度，极大的方便了业务的横向扩容。 且与KVM虚拟化技术不同的是，Docker直接移植于Linux内核之上，通过运行Linux进程将底层设备虚拟隔离，这样系统性能的损耗也要比虚拟机低的多，几乎可以忽略。同时，Docker应用容器的启停非常高效，可以支持大规模的分布系统的水平扩展，真正给企业开发带来福音。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="Docker" scheme="https://hewanyue.com/tags/Docker/"/>
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="容器" scheme="https://hewanyue.com/tags/%E5%AE%B9%E5%99%A8/"/>
    
      <category term="虚拟化" scheme="https://hewanyue.com/tags/%E8%99%9A%E6%8B%9F%E5%8C%96/"/>
    
  </entry>
  
  <entry>
    <title>KVM虚拟化</title>
    <link href="https://hewanyue.com/blog/f989902e.html"/>
    <id>https://hewanyue.com/blog/f989902e.html</id>
    <published>2019-11-29T14:28:35.000Z</published>
    <updated>2020-01-20T07:29:36.336Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;KVM 是Kernel-based Virtual Machine的简称，是一个开源的系统虚拟化模块，自Linux 2.6.20之后集成在Linux的各个主要发行版本中，KVM目前已成为学术界的主流 VMM (virtual machine monitor，虚拟机监视器，也称为hypervisor)之一。 </p><a id="more"></a><p>&emsp;&emsp;可参考红帽官方对kvm的定义：<a href="https://www.redhat.com/zh/topics/virtualization/what-is-KVM" target="_blank" rel="external nofollow noopener noreferrer">https://www.redhat.com/zh/topics/virtualization/what-is-KVM</a><br>&emsp;&emsp;KVM的虚拟化需要硬件支持（如Intel VT技术或者AMD V技术)，是基于硬件的完全虚拟化，而Xen早期则是基于软件模拟的半虚拟化，新版本则是支持基于硬件支持的完全虚拟化，但Xen本身有自己的进程调度器，存储管理模块等，所以代码较为庞大，广为流传的商业系统虚拟化软件VMware ESXI系列是Full-Virtualization，（IBM文档：<a href="http://www.ibm.com/developerworks/cn/linux/l-using-kvm/" target="_blank" rel="external nofollow noopener noreferrer">http://www.ibm.com/developerworks/cn/linux/l-using-kvm/</a> ）<br>&emsp;&emsp;简单地说，KVM就是基于硬件支持实现，将单台主机，分隔成多个互不干扰的虚拟主机的技术，不受困于底层操作系统，可以为每个主机提供单独的、所需的的运行环境，如下图所示：<br><img src="https://img-blog.csdnimg.cn/20191129111043897.png" alt="KVM示意图"><br>&emsp;&emsp;如果每个节点都是用共享存储如NAS，则可实现跨主机的虚拟机快速迁移，这也是我们在生产环境中经常采用的架构。本文将以下面架构，来具体展示KVM在实际生产中的应用。<br><img src="https://img-blog.csdnimg.cn/20191129142617354.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="KVMweb负载均衡">&emsp;&emsp;三个主机，其中两个主机node1和node2上运行KVM，另一台是NAS共享存储服务器，通过10.0.0.0/16网段内网连接。在node1上的用KVM构建虚拟机H1和W1分别运行haproxy+keepalived服务和nginx服务，其中H1桥接方式到node1的两块网卡上，W1桥接到内网网卡eth1上。node2节点中也是如此。于是，将Client端请求通过haproxy反向代理只内网的web服务集群中，保证负载均衡和高可用，哪怕一个物理节点服务器down掉也不影响客户服务访问。<br>&emsp;&emsp;NAS服务器的搭建很容易，这里就先不详细介绍了，本文之后主要介绍KVM服务的配置及虚拟机集群的搭建。</p><h2 id="宿主机环境准备："><a href="#宿主机环境准备：" class="headerlink" title="宿主机环境准备："></a>宿主机环境准备：</h2><p>&emsp;&emsp;KVM需要宿主机CPU必须支持虚拟化功能，因此如果是在vmware workstation上使用虚拟机做宿主机，那么必须要在虚拟机配置界面的处理器选项中开启虚拟机化功能（VMware支持嵌套虚拟化，而KVM不支持，大部分云服务器如阿里云就是基于KVM技术二次研发制作的，所以云服务器都不支持从中再建虚拟机）。<br>&emsp;&emsp;KVM模块因为已经被集成到linux内核之中，所以我们只需要安装KVM管理工具就可以直接使用KVM创建虚拟机了。<br>&emsp;&emsp;可用命令验证是否开启虚拟化（也可通过lscpu命令,grep -o选项只看匹配词本身，也可以用egrep代替grep -E）</p><pre><code>[root@localhost ~]# grep -Eo &quot;vmx|svm&quot; /proc/cpuinfo | wc -lvmxvmx</code></pre><h3 id="KVM软件包安装"><a href="#KVM软件包安装" class="headerlink" title="KVM软件包安装"></a>KVM软件包安装</h3><p>&emsp;&emsp;在Ubuntu 18.04中：<br>&emsp;&emsp;可参考官网<a href="https://ubuntu.com/server/docs/virtualization-libvirt" target="_blank" rel="external nofollow noopener noreferrer">https://ubuntu.com/server/docs/virtualization-libvirt</a></p><pre><code>apt install qemu-kvm virt-manager libvirt-daemon-systemkvm-ok #验证是否支持kvm</code></pre><pre><code>INFO: /dev/kvm existsKVM acceleration can be used</code></pre><p>&emsp;&emsp;CentOS 7.X：</p><pre><code>yum install qemu-kvm qemu-kvm-tools libvirt libvirt-client virt-manager virt-install</code></pre><pre><code>systemctl start libvirtdsystemctl enable libvirtd</code></pre><h3 id="网络环境配置"><a href="#网络环境配置" class="headerlink" title="网络环境配置"></a>网络环境配置</h3><p>&emsp;&emsp;为了让三个节点间虚拟机可以直接相互之间通信，需要将KVM上的虚拟机以桥接模式与宿主机的网卡连接，这就需要我们提前在宿主机上配置好网桥。<br>&emsp;&emsp;<strong>若宿主机为Ubuntu18.04：</strong></p><pre><code>sudo vim /etc/netplan/01-netcfg.yaml</code></pre><pre><code># This file describes the network interfaces available on your system# For more information, see netplan(5).network:  version: 2  renderer: networkd  ethernets:    eth0:      dhcp4: no      dhcp6: no    eth1:      dhcp4: no      dhcp6: no  bridges:    br0:      dhcp4: no      dhcp6: no      addresses: [172.18.0.75/16]      gateway4: 172.18.0.1      nameservers:        addresses: [180.76.76.76]      interfaces:        - eth0    br1:      dhcp4: no      dhcp6: no      addresses: [10.0.0.75/16]      interfaces:        - eth1</code></pre><p>&emsp;&emsp;修改网卡配置后，使用<code>netplan apply</code>命令使配置文件的修改生效</p><pre><code>netplan apply</code></pre><p>&emsp;&emsp;<strong>若宿主机为CentOS：</strong></p><pre><code>cd /etc/sysconfig/network-scripts/</code></pre><pre><code>vim ifcfg-eh0</code></pre><pre><code>TYPE=EthernetBOOTPROTO=staticNAME=eth0DEVICE=eth0ONBOOT=yesBRIDGE=br0</code></pre><pre><code>vim ifcfg-eh1</code></pre><pre><code>TYPE=EthernetBOOTPROTO=staticNAME=eth1DEVICE=eth1ONBOOT=yesBRIDGE=br1</code></pre><pre><code>vim ifcfg-br0</code></pre><pre><code>TYPE=BridgeBOOTPROTO=staticNAME=br0DEVICE=br0ONBOOT=yesIPADDR=172.18.32.75NETMASK=255.255.0.0GATEWAY=172.18.0.1DNS1=180.76.76.76</code></pre><pre><code>vim ifcfg-br1</code></pre><pre><code>TYPE=BridgeBOOTPROTO=staticNAME=br1DEVICE=br1ONBOOT=yesIPADDR=10.0.0.75NETMASK=255.255.0.0</code></pre><p>&emsp;&emsp;重启网络服务，生效配置</p><pre><code>systemctl restart network</code></pre><p>&emsp;&emsp;<code>ip a</code>命令或<code>ifconfig</code>查看桥接网卡是否生效</p><h2 id="KVM虚拟机创建"><a href="#KVM虚拟机创建" class="headerlink" title="KVM虚拟机创建"></a>KVM虚拟机创建</h2><h3 id="创建虚拟磁盘"><a href="#创建虚拟磁盘" class="headerlink" title="创建虚拟磁盘"></a>创建虚拟磁盘</h3><pre><code>[root@localhost ~]# ll /var/lib/libvirt/images/ #默认保存虚拟机磁盘的路径total 0[root@localhost ~]# </code></pre><p>&emsp;&emsp;使用<code>qemu-img create</code>命令可以创建磁盘,如果创建raw格式磁盘文件，则理解占据实际大小，若创建qcow2稀疏格式磁盘，则磁盘文件会随着使用的增大而增大</p><pre><code>qemu-img create -f raw /var/lib/libvirt/images/CentOS7.raw 10G建磁盘</code></pre><pre><code>ll -h /var/lib/libvirt/images/CentOS7.raw-rw-r--r-- 1 root root 10G Nov 29 16:34 /var/lib/libvirt/images/centos.raw</code></pre><pre><code>qemu-img create -f qcow2 /var/lib/libvirt/images/CentOS7.qcow2 10G</code></pre><pre><code>ll -h /var/lib/libvirt/images/CentOS7.qcow2-rw-r--r-- 1 root root 193K Nov 29 16:36 /var/lib/libvirt/images/centos.qcow2</code></pre><p>&emsp;&emsp;我们选用qcow2格式的磁盘，先创建H1虚拟机，</p><pre><code>qemu-img create -f qcow2 /var/lib/libvirt/images/H1.qcow2 10G</code></pre><h3 id="创建虚拟机"><a href="#创建虚拟机" class="headerlink" title="创建虚拟机"></a>创建虚拟机</h3><p>&emsp;&emsp;先导入ISO光盘镜像文件,可以使用共享存储上的ISO文件，也可本机导入，本机上传的话一般习惯性放到<code>/usr/local/src/</code>目录下<br>&emsp;&emsp;使用<code>virt-install</code>命令创建虚拟机，参数可<code>virt-install --help</code>查看帮助信息</p><pre><code>virt-install --help</code></pre><pre><code>usage: virt-install --name NAME --ram RAM STORAGE INSTALL [options]使用指定安装介质新建虚拟机。optional arguments:-h, --help show this help message and exit--version show program&#39;s version number and exit--connect URI 使用 libvirt URI 连接到 hypervisor通用选项:-n NAME, --name NAME 客户端事件名称--memory MEMORY 配置虚拟机内存分配。例如：--memory 1024 (in MiB)--memory 512,maxmemory=1024--vcpus VCPUS 为虚拟机配置的 vcpus 数。例如：--vcpus 5--vcpus 5,maxcpus=10,cpuset=1-4,6,8--vcpus sockets=2,cores=4,threads=2,--cpu CPU CPU 型号及功能。例如：--cpu coreduo,+x2apic--cpu host--metadata METADATA 配置虚拟机元数据。例如：--metadata name=foo,title=&quot;My pretty title&quot;,uuid=...--metadata description=&quot;My nice long description&quot;安装方法选项:--cdrom CDROM 光驱安装介质-l LOCATION, --location LOCATION安装源(例如：nfs:host:/path、http://host/pathftp://host/path)--pxe 使用 PXE 协议从网络引导--import 在磁盘映像中构建虚拟机--livecd 将光驱介质视为 Live CD-x EXTRA_ARGS, --extra-args EXTRA_ARGS附加到使用 --location 引导的内核的参数--initrd-inject INITRD_INJECT使用 --location 为 initrd 的 root添加给定文件--os-variant DISTRO_VARIANT在其中安装 OS 变体的虚拟机，比如&#39;fedora18&#39;、&#39;rhel6&#39;、&#39;winxp&#39; 等等。--boot BOOT 配置虚拟机引导设置。例如：--boot hd,cdrom,menu=on--boot init=/sbin/init (for containers)--idmap IDMAP 为 LXC 容器启用用户名称空间。例如：--idmap uid_start=0,uid_target=1000,uid_count=10设备选项:--disk DISK 使用不同选项指定存储。例如：--disk size=10 (new 10GiB image in default location)--disk /my/existing/disk,cache=none--disk device=cdrom,bus=scsi--disk=?-w NETWORK, --network NETWORK配置虚拟机网络接口。例如：--network bridge=mybr0--network network=my_libvirt_virtual_net--network network=mynet,model=virtio,mac=00:11...--network none--network help--graphics GRAPHICS 配置虚拟机显示设置。例如：--graphics vnc--graphics spice,port=5901,tlsport=5902--graphics none--graphics vnc,password=foobar,port=5910,keymap=ja--controller CONTROLLER配置虚拟机控制程序设备。例如：--controller type=usb,model=ich9-ehci1--input INPUT 配置虚拟机输入设备。例如：--input tablet--input keyboard,bus=usb--serial SERIAL 配置虚拟机串口设备--parallel PARALLEL 配置虚拟机并口设备--channel CHANNEL 配置虚拟机沟通频道--console CONSOLE 配置虚拟机与主机之间的文本控制台连接--hostdev HOSTDEV 将物理 USB/PCI/etc主机设备配置为与虚拟机共享--filesystem FILESYSTEM将主机目录传递给虚拟机。例如：--filesystem /my/source/dir,/dir/in/guest--filesystem template_name,/,type=template--sound [SOUND] 配置虚拟机声音设备模拟--watchdog WATCHDOG 配置虚拟机 watchdog 设备--video VIDEO 配置虚拟机视频硬件。--smartcard SMARTCARD配置虚拟机智能卡设备。例如：--smartcard mode=passthrough--redirdev REDIRDEV 配置虚拟机重定向设备。例如：--redirdev usb,type=tcp,server=192.168.1.1:4000--memballoon MEMBALLOON配置虚拟机 memballoon 设备。例如：--memballoon model=virtio--tpm TPM 配置虚拟机 TPM 设备。例如：--tpm /dev/tpm--rng RNG 配置虚拟机 RNG 设备。例如：--rng /dev/random--panic PANIC 配置虚拟机 panic 设备。例如：--panic default虚拟机配置选项:--security SECURITY 设定域安全驱动器配置。--numatune NUMATUNE 为域进程调整 NUMA 策略。--memtune MEMTUNE 为域进程调整内粗策略。--blkiotune BLKIOTUNE为域进程调整 blkio 策略。--memorybacking MEMORYBACKING为域进程设置内存后备策略。例如：--memorybacking hugepages=on--features FEATURES 设置域 &lt;features&gt; XML。例如：--features acpi=off--features apic=on,eoi=on--clock CLOCK 设置域 &lt;clock&gt; XML。例如：--clock offset=localtime,rtc_tickpolicy=catchup--pm PM 配置 VM 电源管理功能--events EVENTS 配置 VM 生命周期管理策略--resource RESOURCE 配置 VM 资源分区（cgroups）虚拟化平台选项:-v, --hvm 客户端应该是一个全虚拟客户端-p, --paravirt 这个客户端一个是一个半虚拟客户端--container 这台虚拟机需要一个容器客户端--virt-type HV_TYPE 要使用的管理程序名称(kvm、qemu、xen等等)--arch ARCH 模拟的 CPU 构架--machine MACHINE 要模拟的机器类型其它选项:--autostart 引导主机时自动启动域。--wait WAIT 等待安装完成的分钟数。--noautoconsole 不要自动尝试连接到客户端控制台--noreboot 完成安装后不要引导虚拟机。--print-xml [XMLONLY]输出所生成域 XML，而不是创建虚拟机。--dry-run 完成安装步骤，但不要创建设备或者定义虚拟机。--check CHECK 启用或禁用验证检查。例如：--check path_in_use=off--check all=off-q, --quiet 禁止无错误输出-d, --debug 输入故障排除信息</code></pre><p>&emsp;&emsp;我们使用命令创建虚拟机(网卡配置选择default的话默认是nat模式)</p><pre><code>virt-install --virt-type kvm \--name H1 --ram 1024 --vcpus 2 \--cdrom=/usr/local/src/CentOS-7-x86_64-Minimal-1908.iso \--disk=/var/lib/libvirt/images/H1.qcow2 \--network network=default \--graphics vnc,listen=0.0.0.0 \--noautoconsole</code></pre><p>&emsp;&emsp;输入<code>virt-manage</code>r可开启xshellmanager的终端窗口如下图<img src="https://img-blog.csdnimg.cn/20191129164736147.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="H"><br>&emsp;&emsp;双击图标打开，可以看到已经开始自动从光盘启动安装了，配置完毕，手动安装CentOS7系统。<br>如果输入<code>virt-manage</code>不能启动xmanager的窗口，检查一下项目</p><ul><li>确认已经安装xmanager</li><li>xshell文件—属性—连接—隧道—设置转发x11到xmanager</li><li>ssh服务配置文件是否开启X11Forwarding选项 设置为yes</li></ul><p>&emsp;&emsp;以上都没问题，建议重装xmanager，我就是配置都对，但是输入<code>virt-manage</code>命令没反应，重装了一下xmanager和xshell之后就可以正常使用了。</p><p>&emsp;&emsp;安装好CentOS7系统重启后，在vrit-manager的窗口中点击虚拟机info，给H1虚拟机添加一块网卡，并将两块网卡分别选择为主机的桥接网卡br0和br1，如下图所示<br><img src="https://img-blog.csdnimg.cn/20191129173237598.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01pY2VQcm8=,size_16,color_FFFFFF,t_70" alt="H1net"><br>&emsp;&emsp;在虚拟机中配置两个网卡的IP，配置分别如下</p><pre><code>vi /etc/sysconfig/network-scripts/ifcfg-eth0TYPE=&quot;Ethernet&quot;BOOTPROTO=&quot;static&quot;IPADDR=&quot;172.18.32.85&quot;GATEWAY=&quot;172.18.0.1&quot;DNS1=&quot;180.76.76.76&quot;DEFROUTE=&quot;yes&quot;NAME=&quot;eth0&quot;DEVICE=&quot;eth0&quot;ONBOOT=&quot;yes&quot;</code></pre><pre><code>vi /etc/sysconfig/network-scripts/ifcfg-eth1TYPE=&quot;Ethernet&quot;BOOTPROTO=&quot;static&quot;IPADDR=&quot;10.0.0.85&quot;DEFROUTE=&quot;no&quot;NAME=&quot;eth1&quot;DEVICE=&quot;eth1&quot;ONBOOT=&quot;yes&quot;</code></pre><p>&emsp;&emsp;此时H1的基本配置就算完成了。<br>&emsp;&emsp;查看<code>/var/lib/libvirt/images/</code>目录下，看到已经有一个镜像了了</p><pre><code>[root@localhost ~]# ll /var/lib/libvirt/images/total 1594308-rw-r--r-- 1 root root 1632632832 Nov 29 18:54 H1.qcow2</code></pre><p>&emsp;&emsp;可以直接将此文件cp一份来当web1的磁盘文件</p><pre><code>cd /var/lib/libvirt/images/cp H1.qcow2 W1.qcow2</code></pre><p>&emsp;&emsp;也可以在node2节点上的H2和web2虚拟机的磁盘文件。<br>我们直接拷贝改名后，执行<code>virt-install</code>命令，创建另外三个虚拟机。</p><pre><code>virt-install --virt-type kvm \--name W1 --ram 1024 --vcpus 2 \--cdrom=/usr/local/src/CentOS-7-x86_64-Minimal-1908.iso \--disk=/var/lib/libvirt/images/W1.qcow2 \--network network=default \--graphics vnc,listen=0.0.0.0 \--noautoconsole</code></pre><p>&emsp;&emsp;然后将W1强制终止，因为第一次是默认从光驱启动的，我们已经有系统了，不需要重装，重启后就发现已经是装好的系统，与H1一模一样的。<br>&emsp;&emsp;然后修改主机名为web1，将网卡桥接在eth1上,修改网卡配置。</p><pre><code>TYPE=&quot;Ethernet&quot;BOOTPROTO=&quot;static&quot;IPADDR=&quot;10.0.0.175&quot;PREFIX=&quot;16&quot;DEFROUTE=&quot;yes&quot;NAME=&quot;eth0&quot;DEVICE=&quot;eth0&quot;ONBOOT=&quot;yes&quot;</code></pre><p>&emsp;&emsp;对node2上的两个虚拟机采用同样方式创建。<br>&emsp;&emsp;之后分别通过脚本装HAProxy+keepadlived服务和nginx+PHP服务。<br>&emsp;&emsp;修改完几个服务的配置文件(可参考之前文章<a href="https://blog.csdn.net/MicePro/article/details/102901024" target="_blank" rel="external nofollow noopener noreferrer">企业级应用：负载均衡层——haproxy(一)</a>)之后，就可以将通过客户机就可以访问，后端web服务器了。</p><h2 id="附一键安装脚本"><a href="#附一键安装脚本" class="headerlink" title="附一键安装脚本"></a>附一键安装脚本</h2><h3 id="HAProxy一键安装脚本"><a href="#HAProxy一键安装脚本" class="headerlink" title="HAProxy一键安装脚本"></a>HAProxy一键安装脚本</h3><pre><code>[root@HAProxy1 haproxy]# tree.├── haproxy-2.0.8.tar.gz├── haproxy84.cfg├── haproxy.cfg├── haproxy.service├── haproxy.sh└── lua-5.3.5.tar.gz0 directories, 6 files[root@HAProxy1 haproxy]# bash haproxy.sh</code></pre><pre><code>#!/bin/bashDST=&quot;/apps&quot;[ -a haproxy.cfg ] || { echo &#39; the absence of haproxy.conf&#39; ;exit 1;}[ -a haproxy-2.0.8.tar.gz ] || { echo &#39; the absence of haproxy-2.0.8.tar.gz&#39; ;exit 1;}[ -a haproxy.service ] || { echo &#39; the absence of haproxy.service&#39; ;exit 2;}[ -a /usr/lib/systemd/system/haproxy.service ] &amp;&amp; { echo &#39; haproxy is aready installed&#39; ;exit 3;}id haproxy &amp;&gt;/dev/null &amp;&amp; { echo &#39;user haproxy is exist&#39; ; exit 4;} || useradd -r -s /sbin/nologin -u 79 haproxyyum install -y libtermcap-devel ncurses-devel libevent-devel readline-devel gcc make[ -a lua-5.3.5.tar.gz ] || wget http://www.lua.org/ftp/lua-5.3.5.tar.gztar xvf lua-5.3.5.tar.gzcd lua-5.3.5make linux testyum install gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel -ymkdir -p $DST/haproxy#[ -a haproxy-2.0.8.tar.gz ] || wget http://www.haproxy.org/download/2.0/src/haproxy-2.0.8.tar.gzcd ..tar xvf haproxy-2.0.8.tar.gzsleep 1cd haproxy-2.0.8make ARCH=x86_64 \TARGET=linux-glibc USE_PCRE=1 \USE_OPENSSL=1 \USE_ZLIB=1 \USE_SYSTEMD=1 \USE_CPU_AFFINITY=1 \USE_LUA=1 \LUA_INC=../lua-5.3.5/src/ \LUA_LIB=../lua-5.3.5/src/ \PREFIX=$DST/haproxymake install PREFIX=$DST/haproxycat &gt; /usr/lib/systemd/system/haproxy.service &lt;&lt; &quot;END&quot;[Unit]Description=HAProxy Load BalancerAfter=syslog.target network.target[Service]ExecStartPre=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -c -qExecStart=/usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /var/lib/haproxy/haproxy.pidExecReload=/bin/kill -USR2 $MAINPID[Install]WantedBy=multi-user.target\ENDsed -i &quot;s@/usr@$DST/haproxy@&quot; /usr/lib/systemd/system/haproxy.servicemkdir -p /etc/haproxycd ..cp haproxy.cfg /etc/haproxy/sed -i &quot;s@chroot /apps/haproxy@chroot $DST/haproxy@&quot; /etc/haproxy/haproxy.cfgmkdir -p /var/lib/haproxychown 99.99 /var/lib/haproxy/ -Rcat &gt;&gt; /etc/sysctl.conf &lt;&lt; ENDnet.ipv4.ip_forward = 1net.ipv4.ip_nonlocal_bind = 1ENDsysctl -p #修改内核参数之后生效，systemctl daemon-reload 是重读启动脚本systemctl enable --now haproxysystemctl status haproxy</code></pre><h3 id="Nginx-PHP一键安装脚本"><a href="#Nginx-PHP一键安装脚本" class="headerlink" title="Nginx+PHP一键安装脚本"></a>Nginx+PHP一键安装脚本</h3><pre><code>[root@Web1 web]# tree.├── nginx│   ├── nginx-1.16.1.tar.gz│   ├── nginx.conf│   ├── nginx.service│   └── nginx.sh├── php-fpm│   ├── php-7.3.10.tar.xz│   └── php-fpm.sh└── web.sh2 directories, 7 files[root@Web1 web]# bash web.sh</code></pre><pre><code>vim web.sh#!/bin/bashcd nginxbash nginx.shcd ../php-fpmbash php-fpm.sh</code></pre><pre><code>vim nginx/nginx.sh#!/bin/bashDST=&quot;/apps&quot;[ -a nginx.conf ] || { echo &#39; the absence of nginx.conf&#39; ;exit 1;}[ -a nginx.service ] || { echo &#39; the absence of nginx.service&#39; ;exit 2;}[ -a /usr/lib/systemd/system/nginx.service ] &amp;&amp; { echo &#39; nginx is aready installed&#39; ;exit 3;}id nginx &amp;&gt;/dev/null &amp;&amp; { echo &#39;user nginx is exist&#39; ; exit 4;} || useradd -r -s /sbin/nologin -u 80 nginxyum install -y gcc gcc-c++ glibc glibc-devel pcre pcre-devel openssl openssl-devel systemd-devel net-tools iotop bc zip unzip zlib-devel bash-completion nfs-utils automake libxml2 libxml2-devel libxslt libxslt-devel perl perl-ExtUtils-Embed   vim lrzsz tree psmisc wget || { echo &#39;Dependencies is not installed&#39;;exit 5;}#mkdir -p $DST/nginxmkdir -p /data/apps/nginxln -s /data/apps/  /apps[ -a nginx-1.16.1.tar.gz ] || wget https://nginx.org/download/nginx-1.16.1.tar.gztar xvf nginx-1.16.1.tar.gzcd nginx-1.16.1./configure --prefix=$DST/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module --with-select_module --with-file-aiomake -j 4 &amp;&amp; make installcp ../nginx.conf $DST/nginx/conf/cp ../nginx.service /usr/lib/systemd/system/systemctl enable --now nginxsystemctl status nginx</code></pre><pre><code>vim nginx/nginx.service[Unit]Description=The nginx HTTP and reverse proxy serverAfter=network.target remote-fs.target nss-lookup.target[Service]Type=forkingPIDFile=/apps/nginx/logs/nginx.pid# Nginx will fail to start if /run/nginx.pid already exists but has the wrong# SELinux context. This might happen when running `nginx -t` from the cmdline.# https://bugzilla.redhat.com/show_bug.cgi?id=1268621ExecStartPre=/usr/bin/rm -f /apps/nginx/logs/nginx.pidExecStartPre=/apps/nginx/sbin/nginx -tExecStart=/apps/nginx/sbin/nginxExecReload=/bin/kill -s HUP $MAINPIDKillSignal=SIGQUITTimeoutStopSec=5KillMode=processPrivateTmp=true[Install]WantedBy=multi-user.target</code></pre><pre><code>vim nginx/nginx.confuser  nginx;worker_processes  2;worker_cpu_affinity 0001 0010;worker_priority -10;error_log  logs/error.log  info;error_log  /apps/nginx/logs/error.log  error;events {    worker_connections  65536;    use epoll;    accept_mutex on;    multi_accept on;}http {    include       mime.types;    default_type  application/octet-stream;    log_format access_json &#39;{&quot;@timestamp&quot;:&quot;$time_iso8601&quot;,&#39;        &#39;&quot;host&quot;:&quot;$server_addr&quot;,&#39;        &#39;&quot;clientip&quot;:&quot;$remote_addr&quot;,&#39;        &#39;&quot;size&quot;:$body_bytes_sent,&#39;        &#39;&quot;responsetime&quot;:$request_time,&#39;        &#39;&quot;upstreamtime&quot;:&quot;$upstream_response_time&quot;,&#39;        &#39;&quot;upstreamhost&quot;:&quot;$upstream_addr&quot;,&#39;        &#39;&quot;http_host&quot;:&quot;$host&quot;,&#39;        &#39;&quot;uri&quot;:&quot;$uri&quot;,&#39;        &#39;&quot;domain&quot;:&quot;$host&quot;,&#39;        &#39;&quot;xff&quot;:&quot;$http_x_forwarded_for&quot;,&#39;        &#39;&quot;referer&quot;:&quot;$http_referer&quot;,&#39;        &#39;&quot;tcp_xff&quot;:&quot;$proxy_protocol_addr&quot;,&#39;        &#39;&quot;http_user_agent&quot;:&quot;$http_user_agent&quot;,&#39;        &#39;&quot;status&quot;:&quot;$status&quot;}&#39;;    access_log  /apps/nginx/logs/access_json.log  access_json;    sendfile        on;    keepalive_timeout  65 65;    server_tokens off;    charset utf-8;    gzip  on;    server {        listen       80;        server_name  www.example.net;        location = / {            root   html;            index  index.html index.htm;        }        error_page   500 502 503 504  /50x.html;        location = /50x.html {            root   html;        }        location ~ ^/(status|ping)$ {             include fastcgi_params;             fastcgi_pass 127.0.0.1:9000;             fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;             fastcgi_hide_header X-Powered-By;        }        location ~ \.php$ {            root           html;            fastcgi_pass   127.0.0.1:9000;            fastcgi_index  index.php;            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;            include        fastcgi_params;        }    }}</code></pre><pre><code>vim php-fpm/php-fpm.sh#!/bin/bashDST=&quot;/apps&quot;[ -a php-*.tar.* ] || { echo &#39; the absence of php-7.3.10.tar.xz&#39; ;exit 1;}[ -a /usr/lib/systemd/system/php-fpm.service ] &amp;&amp; { echo &#39; php-fpm is aready installed&#39; ;exit 2;}[ -a $DST/php* ] &amp;&amp; { echo &#39; php is aready installed&#39; ;exit 2;}mkdir -p $DST/phpyum install libxml2-devel bzip2-devel libmcrypt-devel -y || { echo &#39;Dependencies is not installed&#39;;exit 5;}#wget https://www.php.net/distributions/php-7.3.10.tar.xztar xvf php-*.tar.*cd php-7.3.10./configure \--prefix=$DST/php \--enable-mysqlnd \--with-mysqli=mysqlnd \--with-pdo-mysql=mysqlnd \--with-openssl \--with-freetype-dir \--with-jpeg-dir \--with-png-dir \--with-zlib \--with-libxml-dir=/usr \--with-config-file-path=/etc \--with-config-file-scan-dir=/etc/php.d \--enable-mbstring \--enable-xml \--enable-sockets \--enable-fpm \--enable-maintainer-zts \--disable-fileinfo make -j 4 &amp;&amp; make installcp php.ini-production  /etc/php.inicp sapi/fpm/php-fpm.service /usr/lib/systemd/system/cp $DST/php/etc/php-fpm.conf.default  $DST/php/etc/php-fpm.confcp $DST/php/etc/php-fpm.d/www.conf.default $DST/php/etc/php-fpm.d/www.confsed -i &#39;s@nobody@nginx@g&#39; $DST/php/etc/php-fpm.d/www.confsed -i &#39;/#/!s@index  index.html index.htm@index index.php index.html index.htm@&#39; $DST/nginx/conf/nginx.confsed -i &#39;s@/scripts$fastcgi_script_name@$document_root$fastcgi_script_name@g&#39; $DST/nginx/conf/nginx.conf$DST/nginx/sbin/nginx -s reloadsystemctl enable --now php-fpm</code></pre>]]></content>
    
    <summary type="html">
    
      KVM 是Kernel-based Virtual Machine的简称，是一个开源的系统虚拟化模块，自Linux 2.6.20之后集成在Linux的各个主要发行版本中，KVM目前已成为学术界的主流 VMM (virtual machine monitor，虚拟机监视器，也称为hypervisor)之一。
    
    </summary>
    
    
      <category term="cloud" scheme="https://hewanyue.com/categories/cloud/"/>
    
    
      <category term="一键安装" scheme="https://hewanyue.com/tags/%E4%B8%80%E9%94%AE%E5%AE%89%E8%A3%85/"/>
    
      <category term="负载均衡" scheme="https://hewanyue.com/tags/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/"/>
    
      <category term="虚拟化" scheme="https://hewanyue.com/tags/%E8%99%9A%E6%8B%9F%E5%8C%96/"/>
    
      <category term="KVM" scheme="https://hewanyue.com/tags/KVM/"/>
    
      <category term="ubuntu" scheme="https://hewanyue.com/tags/ubuntu/"/>
    
  </entry>
  
  <entry>
    <title>OpenVPN的搭建</title>
    <link href="https://hewanyue.com/blog/2c6b894f.html"/>
    <id>https://hewanyue.com/blog/2c6b894f.html</id>
    <published>2019-11-25T13:05:48.000Z</published>
    <updated>2020-01-20T07:29:36.345Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;企业中，必不可少的应用就是VPN了，它可以帮助员工在外网中访问公司内网，常见开源实现方案有OpenVPN和jumpserver。<br>&emsp;&emsp;OpenVPN是采用了端口转发的原理实现，是基于IP+端口的4层代理机制，一般是用于出差员工访问公司内部ERP系统等使用，而jumpserver是7层代理，所以功能更加强大却也更加复杂，一般适合运维人员管理维护企业内部服务器。对于中小公司日常使用，OpenVPN就已经完全足够了，当然，也可用于科学，你懂得。本文将对linux环境下（CentOS），OpenVPN的安装配置做一个详细的介绍。</p><a id="more"></a><h1 id="部署OpenVPN"><a href="#部署OpenVPN" class="headerlink" title="部署OpenVPN"></a>部署OpenVPN</h1><h2 id="部署OpenVPN服务器"><a href="#部署OpenVPN服务器" class="headerlink" title="部署OpenVPN服务器"></a>部署OpenVPN服务器</h2><h3 id="下载安装OpenVPN"><a href="#下载安装OpenVPN" class="headerlink" title="下载安装OpenVPN"></a>下载安装OpenVPN</h3><p>&emsp;&emsp;OpenVPN的rpm包可以在epel源中直接下载安装，所以我们需要先配置epel源，推荐选用阿里的epel源，<code>CentOS67</code>命令如下：</p><pre><code>cat &gt; /etc/yum.repos.d/aliyunepel.repo &lt;&lt; &quot;END&quot;[aliyun-epel]name=aliyun-epelbaseurl=https://mirrors.aliyun.com/epel/$releasever/$basearch/gpgcheck=1gpgkey=https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-$releaseverenabled=1END</code></pre><p>&emsp;&emsp;<code>CentOS8</code>路径有所变化，命令如下：</p><pre><code>cat &gt; /etc/yum.repos.d/aliyunepel.repo &lt;&lt; &quot;END&quot;[aliyun-epel]name=aliyun-epelbaseurl=https://mirrors.aliyun.com/epel/$releasever/Everything/$basearch/gpgcheck=1gpgkey=https://mirrors.aliyun.com/epel/RPM-GPG-KEY-EPEL-$releaseverenabled=1END</code></pre><p>&emsp;&emsp;或者直接安装官方的epel源(6、7、8通用）。</p><pre><code>yum install epel-release -y</code></pre><p>&emsp;&emsp;配置好epel源之后，直接yum安装服务器端软件和证书管理工具就可以了。</p><pre><code>yum install openvpn -y #openvpn服务端yum install easy-rsa -y #证书管理工具</code></pre><h3 id="OpenVPN配置"><a href="#OpenVPN配置" class="headerlink" title="OpenVPN配置"></a>OpenVPN配置</h3><p>&emsp;&emsp;将模版配置文件及证书管理工具复制到指定目录</p><pre><code>cp /usr/share/doc/openvpn-2.4.8/sample/sample-config-files/server.conf /etc/openvpn/  #openvpn server 配置文件cp -r /usr/share/easy-rsa/ /etc/openvpn/easyrsa-server #证书管理工具cp /usr/share/doc/easy-rsa-3.0.6/vars.example /etc/openvpn/easyrsa-server/3/vars</code></pre><p>&emsp;&emsp;配置文件模版如果没有，那可能是在路径为<code>/usr/share/doc/openvpn/sample/sample-config-files/server.conf</code>，easy-rsa如果CentOS8yum安装不上，只能去github上<code>https://github.com/OpenVPN/easy-rsa</code>下载，将里面的easyrsa3目录复制出来就是证书管理工具,并且不需要复制<code>vars.example</code>文件，里面已经有了,改个名字就可以。</p><pre><code>cp -r easyrsa3 /etc/openvpn/easyrsa-server</code></pre><p>&emsp;&emsp;先进入证书管理工具目录（若github上下载的则是<code>cd /etc/openvpn/easyrsa-server/</code>，之后则都没有3的子目录，或者也创建一个3的子目录保持一致性）</p><pre><code>cd /etc/openvpn/easyrsa-server/3</code></pre><p>&emsp;&emsp;若easyrsa为github下载的话，目录结果如下</p><pre><code>[root@aws-host easyrsa-server]#tree.├── easyrsa├── openssl-easyrsa.cnf├── vars.example└── x509-types    ├── ca    ├── client    ├── code-signing    ├── COMMON    ├── email    ├── server    └── serverClient1 directory, 10 files[root@aws-host easyrsa-server]#</code></pre><p>&emsp;&emsp;OpenVPN的server端配置文件如下：</p><pre><code>vim /etc/openvpn/server.conflocal 172.18.200.101 #本机监听IP,写公网IPport 1194 #端口# TCP or UDP server?proto tcp #协议，指定OpenVPN创建的通信隧道类型#proto udp#dev tap：创建一个以太网隧道，以太网使用tapdev tun：创建一个路由IP隧道，互联网使用tun一个TUN设备大多时候，被用于基于IP协议的通讯。一个TAP设备允许完整的以太网帧通过Openvpn隧道，因此提供非ip协议的支持，比如IPX协议和AppleTalk协议#dev-node MyTap #TAP-Win32适配器。非windows不需要#topology subnet #网络拓扑，不需要配置server 10.8.0.0 255.255.255.0 #客户端连接后分配IP的地址池，服务器默认会占用第一个IP 10.8.0.1#ifconfig-pool-persist ipp.txt #为客户端分配固定IP，不需要配置#server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100 #配置网桥模式，不需要push &quot;route 10.20.0.0 255.255.0.0&quot; #给客户端生成的静态路由表，下一跳为openvpn服务器的10.8.0.1,地址段为openvpn服务器后的公司内部网络，可以是多个网段push &quot;route 172.31.0.0 255.255.248.0&quot;;client-config-dir ccd #为指定的客户端添加路由，改路由通常是客户端后面的内网网段而不是服务端的，也不需要设置;route 192.168.40.128 255.255.255.248;client-config-dir ccd;route 10.9.0.0 255.255.255.252;learn-address ./script #运行外部脚本，创建不同组的iptables 规则，不配置;push &quot;redirect-gateway def1 bypass-dhcp&quot; #启用后，客户端所有流量都将通过VPN服务器，因此不需要配置#;push &quot;dhcp-option DNS 208.67.222.222&quot; #推送DNS服务器，不需要配置#;push &quot;dhcp-option DNS 208.67.220.220&quot;#client-to-client #允许不同的client通过openvpn server直接通信，不开启#;duplicate-cn #多个用户共用一个账户，一般用于测试环境，生产环境都是一个用户一个证书keepalive 10 120 #设置服务端检测的间隔和超时时间，默认为每 10 秒 ping一次，如果 120 秒没有回应则认为对方已经 down</code></pre><p>&emsp;&emsp;所以最终配置如下</p><pre><code>local 172.18.32.71    #写公网IP，测试环境无所谓port 1194proto tcpdev tunca /etc/openvpn/certs/ca.crtcert /etc/openvpn/certs/server.crtkey /etc/openvpn/certs/server.key  # This file should be kept secretdh /etc/openvpn/certs/dh.pemserver 10.8.0.0 255.255.255.0   #默认设置，不需要修改push &quot;route 10.0.0.0 255.255.255.0&quot;    #内网网段，不需要修改，科学上网则为 push &quot;redirect-gateway def1 bypass-dhcp&quot;client-to-clientkeepalive 10 120cipher AES-256-CBCmax-clients 100user nobodygroup nobodypersist-tunstatus openvpn-status.loglog-append  /var/log/openvpn/openvpn.logverb 9mute 20</code></pre><h3 id="搭建CA并签发证书"><a href="#搭建CA并签发证书" class="headerlink" title="搭建CA并签发证书"></a>搭建CA并签发证书</h3><p>&emsp;&emsp;初始化pki环境</p><pre><code>./easyrsa init-pki #生成pki目录用于保存证书</code></pre><p>&emsp;&emsp;创建CA签发机构，然后<code>直接回车</code></p><pre><code>./easyrsa build-ca nopass #创建ca并不使用密码</code></pre><p>&emsp;&emsp;创建服务端证书(私钥)，<code>直接回车</code></p><pre><code>./easyrsa gen-req server nopass #生成server证书且不使用密码</code></pre><p>&emsp;&emsp;然后使用自建ca签发服务器证书，即生成服务端crt公钥。crt公钥后期将用户发送给客户端，从而实现与openvpnserver端加密传输数据。</p><pre><code>./easyrsa sign server server #签发服务端证书，备注信息为server</code></pre><p>&emsp;&emsp;然后输入<code>yes</code>输入。</p><h3 id="创建非对称秘钥对"><a href="#创建非对称秘钥对" class="headerlink" title="创建非对称秘钥对"></a>创建非对称秘钥对</h3><p>&emsp;&emsp;还在easyrsa-server目录下，生成秘钥，这可能会花费一段时间。</p><pre><code>./easyrsa gen-dh</code></pre><p>&emsp;&emsp;到此服务端证书环境配置完成，下面是配置客户端证书配置</p><h3 id="创建客户端证书及配置文件"><a href="#创建客户端证书及配置文件" class="headerlink" title="创建客户端证书及配置文件"></a>创建客户端证书及配置文件</h3><p>&emsp;&emsp;证书还是使用easyrsa工具，（github下载的也还是那个目录<code>easyrsa3</code>，不需要复制<code>vars.example</code>文件,改个名字就可以）</p><pre><code>cp -r /usr/share/easy-rsa/ /etc/openvpn/easyrsa-client/cp /usr/share/doc/easy-rsa-3.0.6/vars.example /etc/openvpn/easyrsa-client/3/vars</code></pre><pre><code>cd /etc/openvpn/easyrsa-client/3./easyrsa init-pki</code></pre><p>&emsp;&emsp;例如员工为Mice，则创建名为Mice的证书</p><pre><code>./easyrsa gen-req Mice nopass #客户证书为Mice，没有设置密码</code></pre><p>&emsp;&emsp;然后<code>直接回车</code>，便生成了Mice的证书申请和私钥。<strong>进入easyrsa的server目录中</strong>，(注意，<code>一定要进入easyrsa-server目录</code>，否则会报错)，导入证书申请并给客户端的证书请求签发证书,记得输入<code>yes</code>确认。</p><pre><code>./easyrsa import-req /etc/openvpn/easyrsa-client/3/pki/reqs/Mice.req Mice./easyrsa sign client Mice</code></pre><p>&emsp;&emsp;复制证书到server目录：</p><pre><code> mkdir /etc/openvpn/certs cd /etc/openvpn/certs/ cp /etc/openvpn/easyrsa-server/3/pki/dh.pem . cp /etc/openvpn/easyrsa-server/3/pki/ca.crt . cp /etc/openvpn/easyrsa-server/3/pki/issued/server.crt . cp /etc/openvpn/easyrsa-server/3/pki/private/server.key .</code></pre><p>&emsp;&emsp;创建客户端配置文件</p><pre><code>mkdir /etc/openvpn/client/Mice/vim /etc/openvpn/client/Mice/client.ovpn</code></pre><pre><code>client #声明自己是个客户端dev tun #接口类型，必须和服务端保持一致proto tcp #使用的协议，必须和服务端保持一致remote 172.18.32.71 1194 #server端的ip和端口，可以写域名但是需要可以解析成IPresolv-retry infinite #如果是写的server端的域名，那么就始终解析，如果域名发生变化，会重新连接到新&gt;的域名对应的IPnobind #本机不绑定监听端口，客户端是随机打开端口连接到服务端的1194persist-key #persist-tunca ca.crtcert Mice.crtkey Mice.keyremote-cert-tls server #指定采用服务器校验方式#tls-auth ta.key 1cipher AES-256-CBCverb 3</code></pre><p>&emsp;&emsp;对签发的客户端证书及配置文件进行归档,并打包发送给客户端主机。</p><pre><code>cd /etc/openvpn/client/Mice/cp /etc/openvpn/easyrsa-server/3/pki/ca.crt .cp /etc/openvpn/easyrsa-server/3/pki/issued/Mice.crt .cp /etc/openvpn/easyrsa-client/3/pki/private/Mice.key .tar czvf  Mice.tar.gz *</code></pre><h3 id="配置防火墙转发规则"><a href="#配置防火墙转发规则" class="headerlink" title="配置防火墙转发规则"></a>配置防火墙转发规则</h3><p>&emsp;&emsp;开启路由转发功能：</p><pre><code># vim /etc/sysctl.conf# sysctl -pnet.ipv4.ip_forward = 1</code></pre><p>&emsp;&emsp;先清除现有防火墙策略以免发生干扰，如果CentOS7/8或Redhat7/8没有iptables命令就<code>yum install -y iptables-services</code>安装一个，之后设置IP伪装需要用到。<br>&emsp;&emsp;清空已有规则</p><pre><code>iptables -Fiptables -Xiptables -Ziptables -t nat -Fiptables -t nat -Xiptables -t nat -Z</code></pre><p>&emsp;&emsp;创建iptables 规则：</p><pre><code>iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -j MASQUERADE #此IP是server端默认ip，不要改iptables -A INPUT -p TCP --dport 1194 -j ACCEPTiptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT</code></pre><p>&emsp;&emsp;CentOS6/7的话可以用保存iptables规则（适用未安装iptables-services，俩方式选一个就好）:</p><pre><code>service iptables save</code></pre><p>&emsp;&emsp;显示OK说明保存成功</p><pre><code>iptables: Saving firewall rules to /etc/sysconfig/iptables:[ OK ]</code></pre><p>&emsp;&emsp;如果用的services，则<code>systemctl enable --now iptables</code>（会自动从/etc/sysconfig/iptables文件中读取防火墙策略）</p><p>&emsp;&emsp;创建日志目录并授权：</p><pre><code>mkdir /var/log/openvpnchown nobody.nobody /var/log/openvpn</code></pre><h3 id="启动OpenVPN服务"><a href="#启动OpenVPN服务" class="headerlink" title="启动OpenVPN服务"></a>启动OpenVPN服务</h3><p>&emsp;&emsp;启动openvpn服务</p><pre><code>systemctl enable --now openvpn@server</code></pre><p>&emsp;&emsp;如果没有<a href="mailto:openvpn@server.service" target="_blank" rel="external nofollow noopener noreferrer">openvpn@server.service</a>文件，那就自己编写一个吧</p><pre><code>vim /usr/lib/systemd/system/openvpn@.service </code></pre><pre><code>[Unit]Description=OpenVPN Robust And Highly Flexible Tunneling Application On %IAfter=network.target[Service]Type=notifyPrivateTmp=trueExecStart=/usr/sbin/openvpn --cd /etc/openvpn/ --config %i.conf[Install]WantedBy=multi-user.target</code></pre><p>&emsp;&emsp;验证日志：</p><pre><code>tail /var/log/openvpn/openvpn.logTue Nov 19 13:27:27 2019 Socket Buffers: R=[87380-&gt;87380] S=[16384-&gt;16384]Tue Nov 19 13:27:27 2019 Listening for incoming TCP connection on[AF_INET]172.18.32.71:1194Tue Nov 19 13:27:27 2019 TCPv4_SERVER link local (bound):[AF_INET]172.18.32.71:1194Tue Nov 19 13:27:27 2019 TCPv4_SERVER link remote: [AF_UNSPEC]Tue Nov 19 13:27:27 2019 GID set to rootTue Nov 19 13:27:27 2019 UID set to rootTue Nov 19 13:27:27 2019 MULTI: multi_init called, r=256 v=256Tue Nov 19 13:27:27 2019 IFCONFIG POOL: base=10.8.0.4 size=62, ipv6=0Tue Nov 19 13:27:27 2019 MULTI: TCP INIT maxclients=4096 maxevents=4100Tue Nov 19 13:27:27 2019 Initialization Sequence Completed</code></pre><h2 id="OpenVPN客户端"><a href="#OpenVPN客户端" class="headerlink" title="OpenVPN客户端"></a>OpenVPN客户端</h2><p>&emsp;&emsp;官方客户端下载地址： <a href="https://openvpn.net/community-downloads/" target="_blank" rel="external nofollow noopener noreferrer">https://openvpn.net/community-downloads/</a><br>&emsp;&emsp;非官方地址：<a href="https://sourceforge.net/projects/securepoint/files/" target="_blank" rel="external nofollow noopener noreferrer">https://sourceforge.net/projects/securepoint/files/</a><br>&emsp;&emsp;如果是默认安装路径的话，保存证书到openvpn 客户端安装目录：C:\Program Files\OpenVPN\config<br>&emsp;&emsp;之后就可以直接启动OpenVPN。此时就可以用浏览器直接访问内网IP或者直接ssh登陆内部服务器了。</p><hr><h1 id="关于科学上网"><a href="#关于科学上网" class="headerlink" title="关于科学上网"></a>关于科学上网</h1><p>&emsp;&emsp;经过本人尝试多次，使用OpenVPN做代理转发来实现科学上网，还是有点难度。理论上简单可行的东西，搞了好几次都没成功。<br>&emsp;&emsp;本人有亚马逊的海外云主机，在上面搭建了OpenVPN之后怎么也没法成功，甚至云主机都登陆不上了。开始以为是GW将数据包拦截了，导致云主机被封掉。后来使用其他海外主机直接连接均无法连接失联的云主机，使用端口检查工具发现，那个云主机的所有端口都被关闭了。猜测应该是被亚马逊给封掉了，无奈之下只能释放实例，重新搭建服务器。弄了数次发现，结局均以实例被封告终。猜测亚马逊应该是检查到异常流量，直接把实例的数据连接都断掉了（据说OpenVPN的收费版本不会被封掉），绝望之下，尝试了下UDP协议来代替TCP协议，将客户端设置和服务端设置都改为UPD，竟然发现可以访问外网了，而且服务器至今也平安无事~有需求的小伙伴可以尝试UDP协议。另外附上我云主机的server端配置,希望对大家有所帮助。</p><pre><code>grep ^[a-Z] /etc/openvpn/server.conflocal 0.0.0.0port 11094proto udpdev tunca  /etc/openvpn/certs/ca.crtcert /etc/openvpn/certs/server.crtkey /etc/openvpn/certs/server.keydh /etc/openvpn/certs/dh.pemserver 10.8.0.0 255.255.255.0push &quot;redirect-gateway def1 bypass-dhcp&quot;push &quot;dhcp-option DNS 8.8.8.8&quot;keepalive 10 120cipher AES-256-CBCmax-clients 10user openvpngroup openvpnpersist-keypersist-tunstatus openvpn-status.loglog-append  /var/log/openvpn/openvpn.logverb 3mute 20explicit-exit-notify 1</code></pre><hr><p>&emsp;&emsp;本文写得很简略，发现有小伙伴对相关问题很有兴趣，欢迎留言讨论。</p>]]></content>
    
    <summary type="html">
    
      企业中，必不可少的应用就是VPN了，它可以帮助员工在外网中访问公司内网，常见开源实现方案有OpenVPN和jumpserver。 OpenVPN是采用了端口转发的原理实现，是基于IP+端口的4层代理机制，一般是用于出差员工访问公司内部ERP系统等使用，而jumpserver是7层代理，所以功能更加强大却也更加复杂，一般适合运维人员管理维护企业内部服务器。对于中小公司日常使用，OpenVPN就已经完全足够了，当然，也可用于科学，你懂得。本文将对linux环境下（CentOS），OpenVPN的安装配置做一个详细的介绍。
    
    </summary>
    
    
      <category term="linux" scheme="https://hewanyue.com/categories/linux/"/>
    
    
      <category term="企业级应用" scheme="https://hewanyue.com/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BA%94%E7%94%A8/"/>
    
      <category term="经验分享" scheme="https://hewanyue.com/tags/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
    
      <category term="OpenVPN" scheme="https://hewanyue.com/tags/OpenVPN/"/>
    
      <category term="SSH" scheme="https://hewanyue.com/tags/SSH/"/>
    
      <category term="科学上网" scheme="https://hewanyue.com/tags/%E7%A7%91%E5%AD%A6%E4%B8%8A%E7%BD%91/"/>
    
  </entry>
  
  <entry>
    <title>tomcat集群的session共享</title>
    <link href="https://hewanyue.com/blog/6254cc16.html"/>
    <id>https://hewanyue.com/blog/6254cc16.html</id>
    <published>2019-11-22T13:50:11.000Z</published>
    <updated>2020-01-20T07:29:36.370Z</updated>
    
    <content type="html"><![CDATA[<p>&emsp;&emsp;tomcat作为一个应用服务器，单机性能上都是无法满足生产中需要的，而想要解决高并发场景，光靠提升单机性能，成本与效果肯定都是无法让人接受的，而此时我们一般都采用tomcat集群的方式，用多台tomcat服务器来共同支撑我们的业务。<br>&emsp;&emsp;但这时就出现了一个新的问题，那就是会话保持。因为每台tomcat服务器的session是独立的，当客户端被调度到一个新的tomcat服务器时，他无法识别之前一台的tomcat服务器分配的sessionID，于是对于此次访问，之前的会话信息就都没有了，这表现在用户的客户端就相当于，点开一个新的链接，就发现需要重新登陆，或者之前的购物车里的商品都不见了等等。这样的客户访问体验绝对不是我们想要的，所以我们需要实现会话保持功能！</p><a id="more"></a><h2 id="会话保持实现"><a href="#会话保持实现" class="headerlink" title="会话保持实现"></a>会话保持实现</h2><p>&emsp;&emsp;一般tomcat的会话保持有三种方案实现：</p><ul><li>nginx、httpd或者haproxy的调度实现session绑定,一般是源地址哈希方式实现<br>优点:简单易配置<br>缺点：<br>①如果目标服务器故障后,没有做持久化的话就会丢失session;<br>②即便做了持久化,当服务器故障后,nginx或者haproxy会不得不重新分配一个tomcat服务器,而这时因为新的tomcat服务器上没有原来的sessionID,所以无法找到相应会话信息,会重新分配一个sessionID给客户端,就算原来的tomcat服务器重新上线,又被分配到原来的tomcat服务器中,可此时客户端已经有了新的sessionID,也不会去读取最开始的session信息,那些会话信息就相当于永远丢失了。</li><li>session复制集群,官方给出的tomcat会话共享解决方案<br>&emsp;&emsp;tomcat自己提供的多播集群，通过多播将任何一台的session同步到其他节点。<br>缺点：<br>①tomcat的同步节点不宜过多，互相即时通信session需要太多带宽；<br>②每一台tomcat服务器都拥有全部session信息，内存占用太多。</li><li>session server<br>&emsp;&emsp;session 共享服务器，一般使用memcached、redis做共享的session服务器。<br>目前最理想的解决方案，不过会需要额外的机器来配置共享服务器。<h3 id="反向代理的session绑定"><a href="#反向代理的session绑定" class="headerlink" title="反向代理的session绑定"></a>反向代理的session绑定</h3>&emsp;&emsp;这种实现session保持的方案，一般是使用的不多，一般用于公司内部中的会话保持场景。只需在haproxy或者nginx中的调度算法中，加入基于源地址hash即可（调度算法参考<a href="https://hewanyue.com/blog/5aeb7732.html#haproxy调度算法">之前博客</a>）。于是，用户每次访问都会被调度到同一台tomcat服务器上，上面已有他的session信息，便实现了会话保持。<h4 id="配置实现"><a href="#配置实现" class="headerlink" title="配置实现"></a>配置实现</h4>&emsp;&emsp;我们用一台nginx服务器做反向代理，两台tomcat服务器来演示实现过程。</li></ul><p>&amp;gt nginx主机ip：192.168.32.207<br>&amp;gt tomcat主机1ip：192.168.32.231<br>&amp;gt tomcat主机2ip：192.168.32.232</p><p>&emsp;&emsp;将3台机子中的nginx或tomcat服务启动之后，分别检查80端口和8080端口是否都已监听，确保服务启动。</p><pre><code>iptables -Fgetenforce 0</code></pre><p>&emsp;&emsp;确保防火墙和SELinux设置不会干扰我们几台主机间的相互通信。<br>&emsp;&emsp;nginx主机反向代理的配置</p><pre><code>upstream tomcat {    #ip_hash; #先关闭原地址iphash，观察效果    server 192.168.32.231:8080 weight=1 fail_timeout=5s max_fails=3;    server 192.168.32.232:8080 weight=1 fail_timeout=5s max_fails=3;}server {    listen 80;    index index.jsp#    server_name www.example.net;#    location ~* \.(jsp|do)$ {    location / {        proxy_pass http://tomcat;    }}</code></pre><p>&emsp;&emsp;可启用主机名，也可不启用直接用端口访问。用域名访问还需要改DNS或者hosts设置，比较麻烦，我们就直接通过IP+端口访问就可以了。<br>&emsp;&emsp;tomcat服务器中可以新建一个host，也可使用原先的localhost默认主机。我们这次不用之前的localhost，而是自己新创建一个host标签，指定appBase在/data/myapp目录下。两个tomcat服务器都要执行如下操作：</p><pre><code>vim /apps/tomcat/conf/server.xml</code></pre><p>&emsp;&emsp;找到<code>Engine标签</code>，将默认主机修改为<code>myapp</code></p><pre><code>    &amp;ltEngine name=&quot;Catalina&quot; defaultHost=&quot;myapp&quot;&amp;gt</code></pre><p>&emsp;&emsp;找到<code>localhost</code>的<code>&amp;lt/Host&amp;gt</code>标签，在下面创建新的主机<code>myapp</code>,指定appBase为/data/myapp</p><pre><code>      &amp;ltHost name=&quot;myapp&quot;  appBase=&quot;/data/myapp&quot;            unpackWARs=&quot;true&quot; autoDeploy=&quot;true&quot;&amp;gt      &amp;lt/Host&amp;gt</code></pre><p>&emsp;&emsp;PS：Host name一般为主机域名，当一个tomcat中有多个host服务时，就是通过http报文请求头部的host信息来判断去访问哪个host服务的，当找不到对应的主机之后才访问<code>defaultHost</code>，我们这里因为只有启用了一个host，且为defaultHost，所以就无所谓，可以任意命名了，只需对应上就好。<br>&emsp;&emsp;之后我们要在/data/myapp目录下创建一个<code>ROOT</code>目录来作为tomcat访问的默认目录，注意<code>ROOT</code>是大写的。</p><pre><code>mkdir -p /data/myapp/ROOT</code></pre><p>&emsp;&emsp;为了方便我们看到效果，我们编写index.jsp时，调用一些函数，方便我们看到我们访问的tomcat主机的IP和端口、sessionID、访问时间。</p><pre><code>vim /data/myapp/ROOT/index.jsp</code></pre><pre><code>&amp;lt%@ page import=&quot;java.util.*&quot; %&amp;gt&amp;lt!DOCTYPE html&amp;gt&amp;lthtml lang=&quot;en&quot;&amp;gt&amp;lthead&amp;gt&amp;ltmeta charset=&quot;UTF-8&quot;&amp;gt&amp;lttitle&amp;gtlbjsptest&amp;lt/title&amp;gt&amp;lt/head&amp;gt&amp;ltbody&amp;gt&amp;ltdiv&amp;gtOn &amp;lt%=request.getServerName() %&amp;gt&amp;lt/div&amp;gt&amp;ltdiv&amp;gt&amp;lt%=request.getLocalAddr() + &quot;:&quot; + request.getLocalPort() %&amp;gt&amp;lt/div&amp;gt&amp;ltdiv&amp;gtSessionID = &amp;ltspan style=&quot;color:blue&quot;&amp;gt&amp;lt%=session.getId() %&amp;gt&amp;lt/span&amp;gt&amp;lt/div&amp;gt&amp;lt%=new Date()%&amp;gt&amp;lt/body&amp;gt&amp;lt/html&amp;gt</code></pre><p>&emsp;&emsp;此时，我们访问nginx的80端口，就可以被反向代理到后端的两个tomcat服务器上去。而这时，是轮询的调度方式，也就是一边一次，可以看到访问的主机和sessionID都是一直在变化的。<br>效果如下图所示：<br><img src="https://img-blog.csdnimg.cn/20191122152145219.gif" alt="nginx轮询tomcat"></p><p>&emsp;&emsp;每一次的sessionID都没有重复过，这肯定不满足我们的需要。所以我们将nginx配置中#ip_hash;的#注释去掉，重启nginx服务，再看效果，如下图所示：<br><img src="https://img-blog.csdnimg.cn/20191122152346465.gif" alt="在这里插入图片描述"><br>&emsp;&emsp;可以看到，每次刷新，主机IP和sessionID都不再变化，说明绑定session成功。</p><h3 id="session复制集群"><a href="#session复制集群" class="headerlink" title="session复制集群"></a>session复制集群</h3><p>&emsp;&emsp;这是tomcat官方提供的解决方案，所有tomcat上都有全量的session，不过同步session信息会消耗带宽，而且所有服务器保存所有session信息也比较占用资源，对于tomcat这种本身就处于效率瓶颈的服务来说，高并发场景下超过若五个tomcat服务器，就不再建议使用。<br>&emsp;&emsp;配置详细可以参见<a href="https://tomcat.apache.org/tomcat-8.5-doc/cluster-howto.html" target="_blank" rel="external nofollow noopener noreferrer">官网配置说明</a>。</p><pre><code>        &amp;ltCluster className=&quot;org.apache.catalina.ha.tcp.SimpleTcpCluster&quot;                 channelSendOptions=&quot;8&quot;&amp;gt          &amp;ltManager className=&quot;org.apache.catalina.ha.session.DeltaManager&quot;                   expireSessionsOnShutdown=&quot;false&quot;                   notifyListenersOnReplication=&quot;true&quot;/&amp;gt          &amp;ltChannel className=&quot;org.apache.catalina.tribes.group.GroupChannel&quot;&amp;gt            &amp;ltMembership className=&quot;org.apache.catalina.tribes.membership.McastService&quot;                        address=&quot;228.0.0.4&quot;                        port=&quot;45564&quot;                        frequency=&quot;500&quot;                        dropTime=&quot;3000&quot;/&amp;gt            &amp;ltReceiver className=&quot;org.apache.catalina.tribes.transport.nio.NioReceiver&quot;                      address=&quot;auto&quot;                      port=&quot;4000&quot;                      autoBind=&quot;100&quot;                      selectorTimeout=&quot;5000&quot;                      maxThreads=&quot;6&quot;/&amp;gt            &amp;ltSender className=&quot;org.apache.catalina.tribes.transport.ReplicationTransmitter&quot;&amp;gt              &amp;ltTransport className=&quot;org.apache.catalina.tribes.transport.nio.PooledParallelSender&quot;/&amp;gt            &amp;lt/Sender&amp;gt            &amp;ltInterceptor className=&quot;org.apache.catalina.tribes.group.interceptors.TcpFailureDetector&quot;/&amp;gt            &amp;ltInterceptor className=&quot;org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor&quot;/&amp;gt          &amp;lt/Channel&amp;gt          &amp;ltValve className=&quot;org.apache.catalina.ha.tcp.ReplicationValve&quot;                 filter=&quot;&quot;/&amp;gt          &amp;ltValve className=&quot;org.apache.catalina.ha.session.JvmRouteBinderValve&quot;/&amp;gt          &amp;ltDeployer className=&quot;org.apache.catalina.ha.deploy.FarmWarDeployer&quot;                    tempDir=&quot;/tmp/war-temp/&quot;                    deployDir=&quot;/tmp/war-deploy/&quot;                    watchDir=&quot;/tmp/war-listen/&quot;                    watchEnabled=&quot;false&quot;/&amp;gt          &amp;ltClusterListener className=&quot;org.apache.catalina.ha.session.ClusterSessionListener&quot;/&amp;gt        &amp;lt/Cluster&amp;gt</code></pre><p>&emsp;&emsp;将官网上的这段集群配置，插入到两个tomcat服务器中我们创建的host标签中（也可插入引擎标签中，就相当于所有主机都生效），即<code>&amp;ltHost name</code>标签与<code>&amp;lt/Host&amp;gt</code>标签的中间。<br>&emsp;&emsp;配置说明：</p><pre><code>Cluster 集群配置Manager 会话管理器配置Channel 信道配置        Membership 成员判定。使用什么多播地址、端口多少、间隔时长ms、超时时长ms。同一个多播地址和端口认为同属一个组。使用时修改这个多播地址，以防冲突        Receiver 接收器，多线程接收多个其他节点的心跳、会话信息。默认会从4000到4100依次尝试可用端口。        address=&quot;auto&quot;，auto可能绑定到127.0.0.1上，所以一定要改为可以用的IP上去        Sender 多线程发送器，内部使用了tcp连接池。        Interceptor 拦截器ReplicationValve 检测哪些请求需要检测Session，Session数据是否有了变化，需要启动复制过程ClusterSessionListener 集群session侦听器</code></pre><p>&emsp;&emsp;复制完官方文档里的配置，我们还需要修改接收器的ip为本机ip，不能使用<code>auto</code>，否则会无法同步session信息。<br>&emsp;&emsp;此外，还需要在应用的web.xml文件中最后一行<code>&amp;lt/web-app&amp;gt</code>标签的上面一行插入子标签<code>&amp;ltdistributable/&amp;gt</code>表示可分配。<br>&emsp;&emsp;操作如下：</p><pre><code>mkdir /data/myapp/ROOT/WEB-INFcp /apps/tomcat/conf/web.xml /data/myapp/ROOT/WEB-INF/sed -i &#39;/&amp;lt\/web-app&amp;gt/i&amp;ltdistributable/&amp;gt&#39; /data/myapp/ROOT/WEB-INF/web.xml</code></pre><p>&emsp;&emsp;此时，如果我们将前端Nginx或者其他反向代理服务器中的源地址hash去掉，我们就可以看到，虽然访问的tomcat服务器在变化（服务器ip变化），而sessionID却是不变的，因为每个tomcat服务器上都有全量的相同的session信息。<br><img src="https://img-blog.csdnimg.cn/2019112217482979.gif" alt="集群tomcat"></p><h3 id="session-共享服务器"><a href="#session-共享服务器" class="headerlink" title="session 共享服务器"></a>session 共享服务器</h3><p>&emsp;&emsp;这种实现方式其实才是本文标题中真正的session共享，毕竟共享经济炒的很热，我们都知道，所谓共享，共用才叫共享，前面提到的那种tomcat集群里会话信息人手一份可不能称作是共享。于是乎，我们想到用将session放到外部存储，所有的tomcat服务器都去外部存储中去查找sessionID。<br>&emsp;&emsp;储存session信息肯定不能存储在磁盘文件中，这样的读取写入性能都会很慢，放在mysql数据库中或者以硬盘文件的方式保存，高并发场景下的读取写入速度都将会大打折扣。所以我们要使用类似memcached或者redis这种Key/Value的非关系型数据库里，也被称作NoSQL。<br>&emsp;&emsp;memcached和redis这种键值对型数据库的数据信息都是存储在内存中的，读写效率都很高，而且由于没有复杂的表关系，采用的是哈希算法，他们对于信息的查找都是O(1)，而不是类似mysql等数据库的O(n)，意思就是说耗时/耗空间与总数据量大小无关，不会随着数据量的增大导致查找时间几何倍数的增长。<br>&emsp;&emsp;但也因为memcached和redis的数据都是储存在内存中的，而且memcached还不支持持久化，所以我们一定要做好高可用，一旦发生故障，会话信息将立即丢失，几乎没有恢复的可能。<br>&emsp;&emsp;要实现tomcat共享session服务器，首先，我们要让tomcat将session储存到memcached或者redis等外部存储上，这就需要我们对tomcat进行配置，其次我们要将session信息序列化为变为字节流以便能储存在session服务器中，还要能将session服务器中的数据反序列化为可以识别的session信息，最后，当然我们还需要一个客户端来跟后端的session服务器通信，才能将数据写入和读取。<br>&emsp;&emsp;这想实现确实也比较复杂，不过在github已有开源解决方案（网址是<a href="https://github.com/magro/memcached-session-manager" target="_blank" rel="external nofollow noopener noreferrer">https://github.com/magro/memcached-session-manager</a>），memcached-session-manager，简称msm，后端采用memcached或者redis都可以（之前只支持memcached，因而得名msm，后来支持redis后，人们还是习惯叫它msm），且已经完成了对tomcat的session共享的配置支持（支持tomcat6.X、7.X、8.X、9.X），我们直接去下载对应版本的去使用就可以了。<br>&emsp;&emsp;根据项目的介绍文档，我们知道，想实现tomcat的session共享，我们至少需要配套的工具有：</p><ul><li>tomcat的session管理工具 memcached-session-manager</li><li>与session服务器通信的客户端<br>如果是memcached，则建议使用spymemcached.jar<br>如果是redis，则建议使用jedis.jar</li><li>将session信息序列化的工具，作者推荐使用kryo。</li></ul><p>&emsp;&emsp;这些工具官网上也都提供了下载链接，我们直接下载下来即可。<br>&emsp;&emsp;kryo如下图所示<br><img src="https://img-blog.csdnimg.cn/20191122212611125.png" alt="kryo"><br>&emsp;&emsp;其他工具包如下图所示<br><img src="https://img-blog.csdnimg.cn/20191122212642803.png" alt="msm"><br>&emsp;&emsp;将这些jar包统统拷贝到tomcat服务器的lib目录下（改变lib目录下的jar包需重启tomcat服务才能生效）<br>&emsp;&emsp;使用msm搭建session共享服务器，如果后端为memcached，则有两种模式可以选，分别是sticky模式和non-sticky模式，后端为redis，则使用类似non-sticky模式。</p><h4 id="sticky模式"><a href="#sticky模式" class="headerlink" title="sticky模式"></a>sticky模式</h4><p>&emsp;&emsp;以两台服务器为例，将tomcat1和memcached1部署在一台服务器上（简称为t1、m1），tomcat2和memcached2部署在另一台服务器上（简称为t2、m2）为例，结构图如下图所示。</p><pre><code>&amp;ltt1&amp;gt   &amp;ltt2&amp;gt  . \ / .  .  X  .  . / \ .&amp;ltm1&amp;gt   &amp;ltm2&amp;gt</code></pre><p>&emsp;&emsp;实现原理：当请求结束时Tomcat的session会送给memcached备份。即Tomcat session为主session，memcached session为备session，使用memcached相当于备份了一份Session。查询Session时Tomcat会优先使用自己内存的Session，Tomcat通过jvmRoute发现不是自己的Session，便从memcached中找到该Session，更新本机Session，请求完成后更新memcached。<br>&emsp;&emsp;可能有的朋友看的一头雾水，这到底是个什么结构。其实很简单，sticky模式就是t1的session信息还是储存在t1上，不过以m2为备用数据库，t2的session信息也是放在t2中储存，以m1服务器为备用服务器。这就意味着，用户在访问t1时，是从t1获取session信息，当t1挂掉或者整个节点1服务器挂掉之后，用户会被调度到t2上，而t2本地中没有session信息时，就会去m2中上找相关sessionID，而m2因为是t1的备用存储，所以有跟t1完全相同的session信息，于是用户的sessionID就可以被t2识别；而当m2备用存储服务挂掉之后，t1服务会通过检测发现自己没有备用存储，就会自动将m1也指定为自己的备用存储，将备份信息也同步至m1中，于是用户若再从t2访问时，虽然因为m2挂掉，其中的数据都无法访问，但t2就可以从m1上读取到对应的sessionID并同步到t2本身的存储中，也可以保持之前的会话信息。<br>&emsp;&emsp;修改的配置也很简单，依照官网说明，将下面的代码标签插入<code>/conf/context.xml</code>文件中的<code>context标签</code>结尾就可以了</p><pre><code>&amp;ltManager className=&quot;de.javakaffee.web.msm.MemcachedBackupSessionManager&quot;memcachedNodes=&quot;n1:192.168.32.231:11211,n2:192.168.32.232:11211&quot;failoverNodes=&quot;n1&quot;requestUriIgnorePattern=&quot;.*\.(ico|png|gif|jpg|css|js)$&quot;transcoderFactoryClass=&quot;de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory&quot;/&amp;gt</code></pre><p>&emsp;&emsp;n1、n2只是memcached的节点别名，可以重新命名。<br>failoverNodes是指故障转移节点，也就是发生故障之后的备用节点，所以在n1节点上，n1是备用节点，n2是主存储节点。另一台Tomcat中配置将failoverNodes改为n2，意思是其主节点是n1，备用节点是n2。<br>若配置成功，在/apps/tomcat/log/catalina.out文件中看到如下信息。</p><pre><code>tail -n 20 /apps/tomcat/logs/catalina.out</code></pre><pre><code>23-Nov-2019 13:35:54.187 INFO [myapp-startStop-1] de.javakaffee.web.msm.MemcachedSessionService.startInternal ---------  finished initialization:- sticky: true- operation timeout: 1000- node ids: [n1]- failover node ids: [n2]- storage key prefix: null- locking mode: null (expiration: 5s)--------</code></pre><p>此时在访问我们的前端代理（取消ipHASH绑定）就会看到下面界面，将节点2 关机，可以看到访问ip固定为192.168.32.231，而使用的memcached变成了n1节点。<br><img src="https://img-blog.csdnimg.cn/20191123194433405.gif" alt="tomcatsticky"></p><h4 id="non-sticky模式"><a href="#non-sticky模式" class="headerlink" title="non-sticky模式"></a>non-sticky模式</h4><p>&emsp;&emsp;从msm 1.4.0之后开始支持non-sticky模式。<br>Tomcat session为中转Session，如果n1为主session，n2为备session，则产生的新的Session会发送给主、备memcached，并清除本地Session，也就是说tomcat本身不储存session信息，只负责产生session。<br>&emsp;&emsp;需要注意的是，如果n1下线，n2转换为主节点。n1再次上线，n2依然是主Session存储节点。<br>&emsp;&emsp;配置方法与sticky大致相同，不过在<code>/conf/context.xml</code>文件中的<code>context标签</code>结尾插入代码略有不同，具体代码如下</p><pre><code>&amp;ltManager className=&quot;de.javakaffee.web.msm.MemcachedBackupSessionManager&quot;memcachedNodes=&quot;n1:192.168.32.231:11211,n2:192.168.32.232:11211&quot;sticky=&quot;false&quot;sessionBackupAsync=&quot;false&quot;lockingMode=&quot;uriPattern:/path1|/path2&quot;requestUriIgnorePattern=&quot;.*\.(ico|png|gif|jpg|css|js)$&quot;transcoderFactoryClass=&quot;de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory&quot;/&amp;gt</code></pre><p>&emsp;&emsp;重启tomcat服务后生效。此时在/apps/tomcat/log/catalina.out文件中看到如下信息。</p><pre><code>tail -n 20 /apps/tomcat/logs/catalina.out</code></pre><pre><code>23-Nov-2019 13:43:05.863 INFO [myapp-startStop-1] de.javakaffee.web.msm.MemcachedSessionService.startInternal ---------  finished initialization:- sticky: false- operation timeout: 1000- node ids: [n1, n2]- failover node ids: []- storage key prefix: null- locking mode: uriPattern:/path1|/path2 (expiration: 5s)--------</code></pre><p>&emsp;&emsp;再次尝试访问负载代理服务器，发现同样实现了访问tomcatIP变化，sessionID不变，说明配置成功。<br>&emsp;&emsp;而后端使用redis作为session共享服务器时，仅支持non-stricky模式。建议用另外的服务器安装redis服务，并修改监听IP后启动，tomcat服务器中将<code>jedis.jar</code>jar包拷贝至tomcat安装路径下<code>lib目录</code>，同样在<code>/conf/context.xml</code>文件中的<code>context标签</code>结尾插入下面的代码即可（例如redis服务器IP端口为192.168.32.233:6379，可配置redis集群，可参考我之前博客<a href="https://hewanyue.com/blog/14b8983d.html">redis高可用配置</a>）。</p><pre><code>&amp;ltManager className=&quot;de.javakaffee.web.msm.MemcachedBackupSessionManager&quot;memcachedNodes=&quot;redis://192.168.32.233:6379&quot;sticky=&quot;false&quot;sessionBackupAsync=&quot;false&quot;lockingMode=&quot;uriPattern:/path1|/path2&quot;requestUriIgnorePattern=&quot;.*\.(ico|png|gif|jpg|css|js)$&quot;transcoderFactoryClass=&quot;de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory&quot;/&amp;gt</code></pre>]]></content>
    
    <summary type="html">
    
      tomcat作为一个应用服务器，单机性能上都是无法满足生产中需要的，而想要解决高并发场景，光靠提升单机性能，成本与效果肯定都是无法让人接受的，而此时我们一般都采用tomcat集群的方式，用多台tomcat服务器来共同支撑我们的业务。 但这时就出现了一个新的问题，那就是会话保持。因为每台tomcat服务器的session是独立的，当客户端被调度到一个新的tomcat服务器时，他无法识别之前一台的tomcat服务器分配的sessionID，于是对于此次访问，之前的会话信息就都没有了，这表现在用户的客户端就相当于，点开一个新的链接，就发现需要重新登陆，或者之前的购物车里的商品都不见了等等。这样的客户访问体验绝对不是我们想要的，所以我们需要实现会话保持功能！
    
    </summary>
    
    
      <category term="linux" scheme="https://hewanyue.com/categories/linux/"/>
    
    
      <category term="tomcat" scheme="https://hewanyue.com/tags/tomcat/"/>
    
      <category term="redis" scheme="https://hewanyue.com/tags/redis/"/>
    
      <category term="集群" scheme="https://hewanyue.com/tags/%E9%9B%86%E7%BE%A4/"/>
    
      <category term="MSM" scheme="https://hewanyue.com/tags/MSM/"/>
    
      <category term="session" scheme="https://hewanyue.com/tags/session/"/>
    
  </entry>
  
</feed>
