-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
266 lines (128 loc) · 114 KB
/
search.xml
File metadata and controls
266 lines (128 loc) · 114 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>ITIL和DevOps</title>
<link href="/post/679a57d2/"/>
<url>/post/679a57d2/</url>
<content type="html"><![CDATA[<h2 id="ITIL"><a href="#ITIL" class="headerlink" title="ITIL"></a>ITIL</h2><p>ITIL即IT基础架构库(Information Technology Infrastructure Library, ITIL,信息技术基础架构库)由英国政府部门CCTA(Central Computing and Telecommunications Agency)在20世纪80年代末制订,现由英国商务部OGC(Office of Government Commerce)负责管理,主要适用于IT服务管理(ITSM)。ITIL为企业的IT服务管理实践提供了一个客观、严谨、可量化的标准和规范。</p><p><strong>1、事件管理(Incident Management)</strong></p><p>事故管理负责记录、归类和安排专家处理事故并监督整个处理过程直至事故得到解决和终止。事故管理的目的是在尽可能最小地影响客户和用户业务的情况下使IT系统恢复到服务级别协议所定义的服务级别。</p><p><strong>2、问题管理(Problem Management)</strong></p><p>问题管理是指通过调查和分析IT基础架构的薄弱环节、查明事故产生的潜在原因,并制定解决事故的方案和防止事故再次发生的措施,将由于问题和事故对业务产生的负面影响减小到最低的服务管理流程。与事故管理强调事故恢复的速度不同,问题管理强调的是找出事故产生的根源,从而制定恰当的解决方案或防止其再次发生的预防措施。</p><p><strong>3、配置管理(Configuration Management)</strong></p><p>配置管理是识别和确认系统的配置项,记录和报告配置项状态和变更请求,检验配置项的正确性和完整性等活动构成的过程,其目的是提供IT基础架构的逻辑模型,支持其它服务管理流程特别是变更管理和发布管理的运作。</p><p><strong>4、变更管理(Change Management)</strong></p><p>变更管理是指为在最短的中断时间内完成基础架构或服务的任一方面的变更而对其进行控制的服务管理流程。变更管理的目标是确保在变更实施过程中使用标准的方法和步骤,尽快地实施变更,以将由变更所导致的业务中断对业务的影响减小到最低。</p><p><strong>5、发布管理(Release Management)</strong></p><p>发布管理是指对经过测试后导入实际应用的新增或修改后的配置项进行分发和宣传的管理流程。发布管理以前又称为软件控制与分发</p><h3 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h3><p>事件管理的目标是在不影响业务的情况下,尽可能快速的恢复服务,从而保证最佳的效率和服务的可持续性。事件管理流程的建立包括事件分类,确定事件的优先级和建立事件的升级机制。</p><p>问题管理是调查基础设施和所有可用信息,包括事件数据库,来确定引起事件发生的真正潜在原因,一起提供的服务中可能存在的故障。</p><p>配置管理的目标是:定义和控制服务与基础设施的部件,并保持准确的配置信息。</p><p>变更管理的目标是:以受控的方式,确保所有变更得到评估、批准、实施和评审。</p><p>发布管理的目标是:在实际运行环境的发布中,交付、分发并跟踪一个或多个变更。</p><h2 id="DEVOPS"><a href="#DEVOPS" class="headerlink" title="DEVOPS"></a>DEVOPS</h2><h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><p>DevOps维基百科定义 DevOps(Development和Operations的组合词)是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。</p><p>DevOps 其实包含了三个部分:开发、测试和运维。换句话 DevOps 希望做到的是软件产品交付过程中IT工具链的打通,使得各个团队减少时间损耗,更加高效地协同工作。</p><p>简而言之,DevOps是一组最佳实践,是一个方法论,DevOps 强调整个组织的合作以及交付和基础设施变更的自动化、从而实现持续集成、持续交付和持续部署。</p><h3 id="相关概念"><a href="#相关概念" class="headerlink" title="相关概念"></a>相关概念</h3><p>互联网软件从开发到上线,后续迭代更新,已经有一套近乎标准的流程</p><p><strong>持续集成(Continuous integration)</strong><br>持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快地发现集成错误。许多团队发现这个过程可以大大减少集成的问题,让团队能够更快的开发内聚的软件。</p><p>简单说,持续集成就是指频繁自动将代码集成到主干和生产环境</p><p><strong>持续交付(Continuous delivery)</strong><br>持续交付(Continuous delivery)指的是,频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。 持续交付可以看作持续集成的下一步。它强调的是,不管怎么更新,软件是随时随地可以交付的。注意,持续交付在自动化测试和集成结束后,不一定会自动部署。如果有自动部署,则是持续部署的概念了</p><p><strong>持续部署 (continuous deployment)</strong><br>持续部署(continuous deployment)则是持续交付的下一步,代码通过评审,自动化部署到生产环境。其目的是可以随时部署,迅速投入生产阶段。持续部署这一步,意味着产品和观众见面,但是要通过重重考验,测试、构建、部署等步骤,而且每一步都是自动的。</p><p><a href="https://www.redhat.com/zh/topics/devops/what-is-ci-cd" title="CI/CD是什么?如何理解持续集成、持续交付和持续部署">CI/CD是什么?如何理解持续集成、持续交付和持续部署</a></p><h3 id="DevOps-工具链"><a href="#DevOps-工具链" class="headerlink" title="DevOps 工具链"></a>DevOps 工具链</h3><ul><li>项目管理(PM):Jira</li><li>代码管理:GitLab</li><li>持续集成(CI):GitLab CI、Jenkins</li><li>镜像仓库:VMware Harbor</li><li>容器:Docker</li><li>容器平台: Rancher</li><li>镜像扫描:Clairctl</li><li>编排:Kubernetes</li><li>服务注册与发现:etcd</li><li>脚本语言:python、go</li><li>日志管理:EFK</li><li>系统监控:prometheus</li><li>Web服务器:Nginx</li><li>数据库:MySQL Postgresql redis</li></ul>]]></content>
<categories>
<category> 分享 </category>
</categories>
<tags>
<tag> 运维 </tag>
</tags>
</entry>
<entry>
<title>Web后端开发知识点</title>
<link href="/post/c3df8a2e/"/>
<url>/post/c3df8a2e/</url>
<content type="html"><![CDATA[<h2 id="做Web后端开发需要学习什么"><a href="#做Web后端开发需要学习什么" class="headerlink" title="做Web后端开发需要学习什么"></a>做Web后端开发需要学习什么</h2><ol><li>学习一门面向对象的编程语言(java、python、go、nodejs、c#、php)</li><li>学习一个web框架(python:Flask/Django/Sanic go: Gin/Beego/GoFrame nodejs: egg.js koa express php: Larvel/ThinkPhp java: Spring全家桶)</li><li>学习关系型数据库(MySql/MariaDB/Postgresql)相关知识</li><li>学习缓存(Redis/Memcache)相关知识</li><li>学习消息队列(kafka/RabbitMQ)相关知识</li><li>Linux/网络/TCP IP协议…</li></ol><h2 id="Web后端框架知识点"><a href="#Web后端框架知识点" class="headerlink" title="Web后端框架知识点"></a>Web后端框架知识点</h2><h3 id="Request"><a href="#Request" class="headerlink" title="Request"></a>Request</h3><p>后端与前端交互的来源,Request需要掌握的知识点有</p><ul><li>如何获取前端请求过来的参数,GET、POST请求参数如何获取</li><li>如何获取JSON格式的请求体</li><li>如何获取请求上传过来的文件,并保存这些文件</li><li>如何获取动态路由的参数</li><li>如何校验请求过来的参数是否符合要求</li></ul><h3 id="Response"><a href="#Response" class="headerlink" title="Response"></a>Response</h3><p>得益于http协议的定义,有请求就必须有响应,Response需要掌握的知识点有</p><ul><li>如何响应文本类型数据</li><li>如何响应JSON类型数据</li><li>如何响应不同的状态码</li><li>如何流式响应(常用于文件下载)</li><li>如何响应模板文件</li></ul><h3 id="Routing"><a href="#Routing" class="headerlink" title="Routing"></a>Routing</h3><p>如何让Web框架知道一个Request用哪一个Response来响应,这就需要Routing,Routing主要解决Request和Response的对应关系,这里需要掌握的知识点有:</p><ul><li>路由参数是否支持正则匹配</li><li>动态路由如何配置,有几种配置方法</li><li>是否支持分组路由</li><li>路由规则对应的Response是函数还是类,或者两者都支持</li></ul><h3 id="Cookie-amp-amp-Session"><a href="#Cookie-amp-amp-Session" class="headerlink" title="Cookie && Session"></a>Cookie && Session</h3><p>会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份,这里需要掌握的知识点有</p><ul><li>如何获取Cookie</li><li>如何设置Session</li><li>如何从Session中获取值</li></ul><h3 id="MiddleWare"><a href="#MiddleWare" class="headerlink" title="MiddleWare"></a>MiddleWare</h3><p>中间件可以对请求进行拦截,实现各种插件,例如Cors/鉴权,这里需要掌握的知识点有</p><ul><li>如何编写中间件</li><li>中间件可以用在哪些地方,全局 or 路由</li></ul><h3 id="Signal-不全有"><a href="#Signal-不全有" class="headerlink" title="Signal(不全有)"></a>Signal(不全有)</h3><p>信号是一个观察者模式,它允许你配置一个事件,当观察到这个事件发生时,框架会将这个事件发送到某个或多个接收者,接收者接收到这些事件后即可自定义操作。这里需要掌握的知识点有:</p><ul><li>如何定义一个信号观察的事件</li><li>如何接收一个信号</li></ul><h3 id="ORM-微框架一般没有"><a href="#ORM-微框架一般没有" class="headerlink" title="ORM(微框架一般没有)"></a>ORM(微框架一般没有)</h3><p>对象关系映射(Object Relational Mapping,简称ORM),指的是面向对象的对象模型和关系型数据库的数据结构之间的相互转换,简单点理解就是通过代码层对数据库语句的封装,使用简单的代码调用即可调用底层SQL语句,而使用者不需要过多关注底层SQL语句怎么写,ORM需要掌握的知识点如下</p><ul><li>如何配置ORM</li><li>ORM的增删改查如何编写</li><li>ORM是否支持链式操作</li><li>ORM结果如何处理</li><li>ORM事务如何处理</li><li>ORM是否支持主从模式</li><li>ORM是否支持RAW模式</li><li>ORM是否支持查询缓存</li></ul><h3 id="Websocket-有些会自带,有些需要自己集成"><a href="#Websocket-有些会自带,有些需要自己集成" class="headerlink" title="Websocket(有些会自带,有些需要自己集成)"></a>Websocket(有些会自带,有些需要自己集成)</h3><p>长连接的支持是相对短连接而言的,长连接有不少优势,这里需要掌握的知识点有</p><ul><li>如何创建一个长连接服务端</li><li>外部如何操作这个服务端的长连接</li></ul><h3 id="Static-amp-amp-template"><a href="#Static-amp-amp-template" class="headerlink" title="Static && template"></a>Static && template</h3><p>静态文件的支持一般是web框架必备的需求,模板的渲染却不是每个Web框架必有的,现流行的请后端分离的web开发模式下,静态文件支持和模板渲染已经越来越边缘化,这里需要掌握的知识点有</p><ul><li>如何配置静态文件支持</li><li>如何渲染模板</li><li>模板语言的语法</li></ul><h3 id="Logging-有些会自带,有些需要自己集成"><a href="#Logging-有些会自带,有些需要自己集成" class="headerlink" title="Logging(有些会自带,有些需要自己集成)"></a>Logging(有些会自带,有些需要自己集成)</h3><p>日志打印的支持并不是web框架都有的,大部分框架都会集成,web开发打印日志是必须的,这方便我们查询问题,定位故障,这里需要掌握的知识点有</p><ul><li>日志如何配置</li><li>是否支持自定义日志文件</li><li>是否支持日志切割</li><li>是否支持日志级别输出</li></ul><h2 id="REST"><a href="#REST" class="headerlink" title="REST"></a>REST</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><p>网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备……)。</p><p>因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现”API First”的设计思想。<code>RESTful API</code>是目前比较成熟的一套互联网应用程序的API设计理论</p><p><code>REST</code>,即Representational State Transfer的缩写。翻译过来是”表现层状态转化”。</p><p>如果一个架构符合REST原则,就称它为RESTful架构。</p><h3 id="RESTful架构"><a href="#RESTful架构" class="headerlink" title="RESTful架构"></a>RESTful架构</h3><ul><li><p>每一个URI代表一种资源, 所以资源必须是名词;</p></li><li><p>客户端和服务器之间,传递这种资源的某种表现层;</p></li><li><p>客户端通过四个HTTP动词,对服务器端资源进行操作,实现”表现层状态转化”。</p></li></ul><h3 id="常见URI设计"><a href="#常见URI设计" class="headerlink" title="常见URI设计"></a>常见URI设计</h3><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">GET /zoos:列出所有动物园</span><br><span class="line">POST /zoos:新建一个动物园</span><br><span class="line">GET <span class="regexp">/zoos/I</span>D:获取某个指定动物园的信息</span><br><span class="line">PUT <span class="regexp">/zoos/I</span>D:更新某个指定动物园的信息(提供该动物园的全部信息)</span><br><span class="line">PATCH <span class="regexp">/zoos/I</span>D:更新某个指定动物园的信息(提供该动物园的部分信息)</span><br><span class="line"><span class="keyword">DELETE</span> <span class="regexp">/zoos/I</span>D:删除某个动物园</span><br><span class="line">GET <span class="regexp">/zoos/I</span>D/animals:列出某个指定动物园的所有动物</span><br><span class="line"><span class="keyword">DELETE</span> <span class="regexp">/zoos/I</span>D<span class="regexp">/animals/I</span>D:删除某个指定动物园的指定动物</span><br></pre></td></tr></table></figure><h3 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="推荐阅读"></a>推荐阅读</h3><p><a href="https://www.ruanyifeng.com/blog/2011/09/restful.html" title="理解RESTful架构">理解RESTful架构</a><br><a href="http://www.ruanyifeng.com/blog/2014/05/restful_api.html" title="RESTful API 设计指南">RESTful API 设计指南</a></p>]]></content>
<categories>
<category> 技术 </category>
<category> 分享 </category>
</categories>
<tags>
<tag> python </tag>
<tag> go </tag>
</tags>
</entry>
<entry>
<title>模拟 saltstack/ansible系列四(基于长连接实现salt主要功能)</title>
<link href="/post/1c8b7bb6/"/>
<url>/post/1c8b7bb6/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>前面讲了基于短连接的saltstack主要功能实现,今天我来讲讲基于长连接方式的saltstack实现。</p><h2 id="长连接"><a href="#长连接" class="headerlink" title="长连接"></a>长连接</h2><h3 id="长连接和短连接的区别"><a href="#长连接和短连接的区别" class="headerlink" title="长连接和短连接的区别"></a>长连接和短连接的区别</h3><p><a href="https://www.cnblogs.com/gotodsp/p/6366163.html">HTTP长连接、短连接究竟是什么?</a></p><h3 id="使用的长连接实现模块"><a href="#使用的长连接实现模块" class="headerlink" title="使用的长连接实现模块"></a>使用的长连接实现模块</h3><p><a href="https://websocket-client.readthedocs.io/en/latest/examples.html">websocket-client</a><br><a href="https://gitlab.com/noppo/gevent-websocket">gevent-websocket</a></p><p>这里没有使用<a href="https://websockets.readthedocs.io/en/stable/">websockets</a>这个库来做长连接的客户端和服务端,主要考虑的是上面2个库来做客户端和服务端的代码更容易理解</p><p>由于以上2个模块都属于第三方库,所以需要使用如下命令先行安装</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install websocket-client gevent-websocket</span><br></pre></td></tr></table></figure><h2 id="具体代码实现"><a href="#具体代码实现" class="headerlink" title="具体代码实现"></a>具体代码实现</h2><h3 id="长连接服务端"><a href="#长连接服务端" class="headerlink" title="长连接服务端"></a>长连接服务端</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> geventwebsocket <span class="keyword">import</span> WebSocketServer, WebSocketApplication, Resource</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> OrderedDict</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">EchoApplication</span>(<span class="params">WebSocketApplication</span>):</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">on_open</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Connection opened"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">on_message</span>(<span class="params">self, message</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(message)</span><br><span class="line"> self.ws.send(<span class="string">'recieve message'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">on_close</span>(<span class="params">self, reason</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(reason)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> WebSocketServer(</span><br><span class="line"> (<span class="string">'0.0.0.0'</span>, <span class="number">8761</span>),</span><br><span class="line"> Resource(OrderedDict([(<span class="string">'/ws'</span>, EchoApplication)]))</span><br><span class="line"> ).serve_forever()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>以上代码很容易理解,当程序启动时监听8761端口,当有客户端连入时,on_open方法将会被执行,收到消息时,on_message将会被执行,当长连接关闭时,on_close方法将会被执行。</p><h3 id="长连接客户端"><a href="#长连接客户端" class="headerlink" title="长连接客户端"></a>长连接客户端</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> websocket</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">on_message</span>(<span class="params">ws, message</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(message)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">on_error</span>(<span class="params">ws, error</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(ws)</span><br><span class="line"> <span class="built_in">print</span>(error)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">on_open</span>(<span class="params">ws</span>):</span></span><br><span class="line"> ws.send(<span class="string">"agent connect to master"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">on_close</span>(<span class="params">ws, close_status_code, close_msg</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"### closed ###"</span>)</span><br><span class="line"> <span class="built_in">print</span>(close_status_code, close_msg)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">keep_alive</span>():</span></span><br><span class="line"> websocket.enableTrace(<span class="literal">False</span>)</span><br><span class="line"> ws = websocket.WebSocketApp(</span><br><span class="line"> <span class="string">"ws://127.0.0.1:8761/ws"</span>,</span><br><span class="line"> on_message=on_message,</span><br><span class="line"> on_open=on_open,</span><br><span class="line"> on_error=on_error,</span><br><span class="line"> on_close=on_close,</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> ws.run_forever(suppress_origin=<span class="literal">True</span>, skip_utf8_validation=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> keep_alive()</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>可以看到长连接客户端和服务端代码非常相似,这也是选择这2个模块开发的原因。</p><p>客户端启动时连接服务端的接口,需要特别注意的是websocket的地址是以ws开头(SSL的长连接以wss开头)</p><p>此时,我们运行长连接服务端和客户端</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">root@demo:/data<span class="comment"># python3 master_socket.py</span></span><br><span class="line">Connection opened</span><br><span class="line">agent connect to master</span><br><span class="line"></span><br><span class="line">root@demo:/data<span class="comment"># python3 agent_socket.py</span></span><br><span class="line">recieve message</span><br></pre></td></tr></table></figure><p>如上,这就代表客户端和服务端长连接已经建立起来。</p><p>此时,我们需要考虑一个问题,假如我们把长连接服务端作为salt-master,客户端作为salt-minion,我们需要怎样做,才能通过salt-master发送指令给salt-minion去执行并且返回执行结果了?</p><p>我们可以看到不管是长连接服务端还是客户端都没有提供类似短连接的接口,也就不存在外部直接发个请求就可以返回数据回来</p><p>我们再看服务端,ws.send()方法即是发送消息给客户端,理论上来说只要我们能控制这个ws对象,就可以发送消息给客户端,那如何控制了?比如说初始化一个ws全局变量,然后on_open时,将on_open(ws)中的ws参数赋值给ws全局变量,然后我们外部只要操作这个ws就可以了?</p><p>但这个方法其实是行不通的,我们的salt-master是控制批量主机的,如此多的salt-minion通过长连接和master通信,ws这个全局变量到底属于哪个远程主机长连接的了?</p><h3 id="基于MQ实现长连接master和agent交互的纽带"><a href="#基于MQ实现长连接master和agent交互的纽带" class="headerlink" title="基于MQ实现长连接master和agent交互的纽带"></a>基于MQ实现长连接master和agent交互的纽带</h3><p>其实,我们可以新增一个消息提供者,用于外部操作salt-master</p><p>我们来看下代码实现:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> geventwebsocket <span class="keyword">import</span> WebSocketServer, WebSocketApplication, Resource</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> OrderedDict</span><br><span class="line"><span class="keyword">import</span> redis</span><br><span class="line"><span class="keyword">from</span> threading <span class="keyword">import</span> Thread</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">EchoApplication</span>(<span class="params">WebSocketApplication</span>):</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">subscribe_msg</span>(<span class="params">self</span>):</span></span><br><span class="line"> redis_conn = redis.Redis(host=<span class="string">'127.0.0.1'</span>, port=<span class="number">6379</span>, decode_responses=<span class="literal">True</span>) </span><br><span class="line"> ps = redis_conn.pubsub()</span><br><span class="line"> ps.subscribe(<span class="string">'ws'</span>)</span><br><span class="line"> <span class="keyword">for</span> item <span class="keyword">in</span> ps.listen():<span class="comment">#监听状态:有消息发布了就拿过来</span></span><br><span class="line"> <span class="keyword">if</span> item[<span class="string">'type'</span>] == <span class="string">'message'</span>:</span><br><span class="line"> msg = item[<span class="string">'data'</span>]</span><br><span class="line"> <span class="built_in">print</span>(msg)</span><br><span class="line"> self.ws.send(msg)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">on_open</span>(<span class="params">self</span>):</span></span><br><span class="line"> Thread(target=self.subscribe_msg).start()</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Connection opened"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">on_message</span>(<span class="params">self, message</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(message)</span><br><span class="line"> self.ws.send(<span class="string">'recieve message'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">on_close</span>(<span class="params">self, reason</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(reason)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> WebSocketServer(</span><br><span class="line"> (<span class="string">'0.0.0.0'</span>, <span class="number">8761</span>),</span><br><span class="line"> Resource(OrderedDict([(<span class="string">'/ws'</span>, EchoApplication)]))</span><br><span class="line"> ).serve_forever()</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><p>这里我们使用<code>Redis</code>作为MQ,主要是安装简单、Python操作也简单,也可以使用其他MQ(<code>RabbitMQ</code>、<code>Kafka</code>),或者使用saltstack使用的<code>zeromq</code>(这里没有看过saltstack的源代码实现,估摸着saltstack也是通过<code>zeromq</code>来让外部操作salt-master的)</p><p>以上代码很简单,我们在长连接服务端新加了个方法,用于操作外部Redis,监听外部Redis的发布订阅模块,如果ws这个key有消息生成,这边就会消费它,即通过ws.send()方法发送给客户端;然后我们在on_open方法中起一个线程去处理这个操作外部Redis的操作。此时,我们只要在外部操作这个Redis的pubsub中的ws key,新增消息,例如<code>{'ip': xxx, 'cmd': 'df -h'}</code>,我们就可以根据IP发送给具体的salt-minion去执行具体的命令</p><h3 id="长连接客户端如何执行服务端发过来的消息"><a href="#长连接客户端如何执行服务端发过来的消息" class="headerlink" title="长连接客户端如何执行服务端发过来的消息"></a>长连接客户端如何执行服务端发过来的消息</h3><p>其实我们前面讲了短连接的实现,就是为了长连接做铺垫,比如说执行命令,我们只要将短连接的执行命令代码稍作修改搬过来用即可</p><p>代码如下:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> websocket</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">exec_cmd</span>(<span class="params">cmd</span>):</span></span><br><span class="line"> <span class="comment"># 基于subprocess.Popen方法执行本地shell命令</span></span><br><span class="line"> proc = subprocess.Popen(cmd, shell=<span class="literal">True</span>, stdout=subprocess.PIPE)</span><br><span class="line"> <span class="keyword">if</span> proc:</span><br><span class="line"> result = proc.stdout.read()</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">on_message</span>(<span class="params">ws, message</span>):</span></span><br><span class="line"> <span class="comment"># 获取服务端发送过来的命令,然后执行返回给服务端</span></span><br><span class="line"> result = exec_cmd(message.get(<span class="string">'cmd'</span>))</span><br><span class="line"> ws.send(result)</span><br><span class="line"> <span class="built_in">print</span>(message)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">on_error</span>(<span class="params">ws, error</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(ws)</span><br><span class="line"> <span class="built_in">print</span>(error)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">on_open</span>(<span class="params">ws</span>):</span></span><br><span class="line"> ws.send(<span class="string">"agent connect to master"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">on_close</span>(<span class="params">ws, close_status_code, close_msg</span>):</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"### closed ###"</span>)</span><br><span class="line"> <span class="built_in">print</span>(close_status_code, close_msg)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">keep_alive</span>():</span></span><br><span class="line"> websocket.enableTrace(<span class="literal">False</span>)</span><br><span class="line"> ws = websocket.WebSocketApp(</span><br><span class="line"> <span class="string">"ws://127.0.0.1:8761/ws"</span>,</span><br><span class="line"> on_message=on_message,</span><br><span class="line"> on_open=on_open,</span><br><span class="line"> on_error=on_error,</span><br><span class="line"> on_close=on_close,</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> ws.run_forever(suppress_origin=<span class="literal">True</span>, skip_utf8_validation=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> keep_alive()</span><br></pre></td></tr></table></figure></p><p>这里只是将执行命令的方法搬过来,上传下载文件原理类似,这里不再赘述</p><blockquote><p>最后</p></blockquote><p>近几篇文章我们通过SSH实现了ansible主要功能、通过短连接和长连接实现saltstack主要功能,下一篇文章我们来讲讲如何模拟saltstack和ansible的命令行来操作远程主机</p>]]></content>
<categories>
<category> 技术 </category>
<category> 分享 </category>
</categories>
<tags>
<tag> python </tag>
<tag> devops </tag>
<tag> linux </tag>
<tag> 运维 </tag>
</tags>
</entry>
<entry>
<title>模拟 saltstack/ansible 系列三(基于短连接实现saltstack主要功能)</title>
<link href="/post/3de380ec/"/>
<url>/post/3de380ec/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>前面讲了基于SSH的ansible实现,今天我来讲讲基于短连接方式的saltstack实现。</p><blockquote><p>为什么要讲基于短连接的saltstack实现呢?</p></blockquote><p>前面不是说saltstack是基于长连接的吗?原因就在于当你掌握了短连接的实现后,长连接就水到渠成了,你也更能理解到短连接和长连接实现的一个区别</p><h2 id="Agent设计"><a href="#Agent设计" class="headerlink" title="Agent设计"></a>Agent设计</h2><p>不管是短连接实现还是长连接实现,都需要在远程主机起一个服务来承担Agent角色,就如同saltstack的<mark class="hl-label blue">salt-minion</mark> 一样。</p><p>下面我们就基于<code>flask</code>来简单实现一个短连接形式的agent(考虑性能的话可以使用<code>sanic</code>,这里用<code>flask</code>的原因是本人团队内部培训时,我主要培训的是<code>flask</code>)</p><p>由于<code>flask</code>属于第三方库,所以需要使用如下命令先行安装</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install flask</span><br></pre></td></tr></table></figure><p>如果想做成和salt-minion二进制模式启动的话,我们还需要安装一个额外的模块<code>pyinstaller</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install pyinstaller</span><br></pre></td></tr></table></figure><p>以下是flask作为一个服务端基本的代码: app.py</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/"</span></span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">hello_world</span>():</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"Hello, World!"</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> app.run(host=<span class="string">"0.0.0.0"</span>, port=<span class="number">8080</span>, debug=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p>当我们执行这个代码时,程序将会监听<code>8080</code>端口,请求<code>http://127.0.0.1:8080</code>将会返回Hello, World!</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">$ python3 app.py</span><br><span class="line"></span><br><span class="line"> * Serving Flask app <span class="string">'app'</span> (lazy loading)</span><br><span class="line"> * Environment: production</span><br><span class="line"> WARNING: This is a development server. Do not use it <span class="keyword">in</span> a production deployment.</span><br><span class="line"> Use a production WSGI server instead.</span><br><span class="line"> * Debug mode: on</span><br><span class="line"> * Running on all addresses.</span><br><span class="line"> WARNING: This is a development server. Do not use it <span class="keyword">in</span> a production deployment.</span><br><span class="line"> * Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)</span><br><span class="line"> * Restarting with <span class="built_in">stat</span></span><br><span class="line"> * Debugger is active!</span><br><span class="line"> * Debugger PIN: 142-934-530</span><br><span class="line"></span><br><span class="line">$ http http://127.0.0.1:8080</span><br><span class="line"></span><br><span class="line">HTTP/1.0 200 OK</span><br><span class="line">Content-Length: 13</span><br><span class="line">Content-Type: text/html; charset=utf-8</span><br><span class="line">Date: Wed, 24 Nov 2021 14:46:12 GMT</span><br><span class="line">Server: Werkzeug/2.0.2 Python/3.10.0</span><br><span class="line"></span><br><span class="line">Hello, World!</span><br></pre></td></tr></table></figure><p>此时,我们可以通过<code>pyinstaller</code>将该程序编译成一个二进制程序</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pyinstaller -F app.py</span><br></pre></td></tr></table></figure><h2 id="saltstack功能分析与代码实现"><a href="#saltstack功能分析与代码实现" class="headerlink" title="saltstack功能分析与代码实现"></a>saltstack功能分析与代码实现</h2><h3 id="连接远程主机"><a href="#连接远程主机" class="headerlink" title="连接远程主机"></a>连接远程主机</h3><p>基于短连接实现的agent,连接远程主机功能只需要判断接口是否能通即可,如上我们请求<code>http://127.0.0.1:8080</code>时返回200状态码,即可认为成功连接远程主机。</p><h3 id="test-ping"><a href="#test-ping" class="headerlink" title="test.ping"></a>test.ping</h3><p>ping功能的实现,我们可以写个ping接口,然后返回pong来实现,或者根据返回状态码是否为200来判断</p><p>我们把上边的app.py修改一下,加上ping接口</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/ping"</span>, methods=[<span class="string">"GET"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ping</span>():</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"pong"</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> app.run(host=<span class="string">"0.0.0.0"</span>, port=<span class="number">8080</span>, debug=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p>此时我们请求<code>http://127.0.0.1:8080/ping</code>则会返回pong,此时即可认为远程主机在线</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ http http://<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">8080</span>/ping</span><br><span class="line"></span><br><span class="line">HTTP/<span class="number">1.0</span> <span class="number">200</span> OK</span><br><span class="line">Content-Length: <span class="number">4</span></span><br><span class="line">Content-<span class="type">Type</span>: text/html; charset=utf-<span class="number">8</span></span><br><span class="line">Date: Wed, <span class="number">24</span> Nov <span class="number">2021</span> <span class="number">15</span>:04:<span class="number">58</span> GMT</span><br><span class="line">Server: Werkzeug/<span class="number">2.0</span><span class="number">.2</span> Python/<span class="number">3.10</span><span class="number">.0</span></span><br><span class="line"></span><br><span class="line">pong</span><br></pre></td></tr></table></figure><h3 id="cmd-run"><a href="#cmd-run" class="headerlink" title="cmd.run"></a>cmd.run</h3><p>执行命令我们可以再写一个接收POST请求的接口,接收一个参数,用于命令的传入,然后根据传入的命令在本地主机执行命令,获取命令返回的结果后响应给客户端。</p><p>我们把上边的app.py增加一个cmd接口</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request</span><br><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/ping"</span>, methods=[<span class="string">"GET"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ping</span>():</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"pong"</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/cmd"</span>, methods=[<span class="string">"post"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cmd</span>():</span></span><br><span class="line"> body = request.json</span><br><span class="line"> cmd = body.get(<span class="string">"cmd"</span>)</span><br><span class="line"> <span class="comment"># 基于subprocess.Popen方法执行本地shell命令</span></span><br><span class="line"> proc = subprocess.Popen(cmd, shell=<span class="literal">True</span>, stdout=subprocess.PIPE)</span><br><span class="line"> <span class="keyword">if</span> proc:</span><br><span class="line"> result = proc.stdout.read()</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> app.run(host=<span class="string">"0.0.0.0"</span>, port=<span class="number">8080</span>, debug=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p>此时我们请求<code>http://127.0.0.1:8080/cmd</code>,传入命令,则会返回命令结果</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">$ http http://<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">8080</span>/cmd cmd=<span class="string">'df -h'</span></span><br><span class="line"></span><br><span class="line">HTTP/<span class="number">1.0</span> <span class="number">200</span> OK</span><br><span class="line">Content-Length: <span class="number">590</span></span><br><span class="line">Content-<span class="type">Type</span>: text/html; charset=utf-<span class="number">8</span></span><br><span class="line">Date: Wed, <span class="number">24</span> Nov <span class="number">2021</span> <span class="number">15</span>:<span class="number">25</span>:<span class="number">24</span> GMT</span><br><span class="line">Server: Werkzeug/<span class="number">2.0</span><span class="number">.2</span> Python/<span class="number">3.8</span><span class="number">.10</span></span><br><span class="line"></span><br><span class="line">Filesystem Size Used Avail Use% Mounted on</span><br><span class="line">udev 957M <span class="number">0</span> 957M <span class="number">0</span>% /dev</span><br><span class="line">tmpfs 198M <span class="number">1.1</span>M 197M <span class="number">1</span>% /run</span><br><span class="line">/dev/sda1 20G <span class="number">1.5</span>G 18G <span class="number">8</span>% /</span><br><span class="line">tmpfs 990M <span class="number">0</span> 990M <span class="number">0</span>% /dev/shm</span><br><span class="line">tmpfs <span class="number">5.0</span>M <span class="number">0</span> <span class="number">5.0</span>M <span class="number">0</span>% /run/lock</span><br><span class="line">tmpfs 990M <span class="number">0</span> 990M <span class="number">0</span>% /sys/fs/cgroup</span><br><span class="line">/dev/sda15 98M 290K 98M <span class="number">1</span>% /boot/efi</span><br><span class="line">/dev/loop0 61M 61M <span class="number">0</span> <span class="number">100</span>% /snap/lxd/<span class="number">21843</span></span><br><span class="line">/dev/loop2 29M 29M <span class="number">0</span> <span class="number">100</span>% /snap/snapd/<span class="number">13643</span></span><br><span class="line">/dev/loop1 58M 58M <span class="number">0</span> <span class="number">100</span>% /snap/core20/<span class="number">1244</span></span><br><span class="line">tmpfs 198M <span class="number">0</span> 198M <span class="number">0</span>% /run/user/<span class="number">1000</span></span><br></pre></td></tr></table></figure><h3 id="上传文件"><a href="#上传文件" class="headerlink" title="上传文件"></a>上传文件</h3><p>执行命令我们可以再写一个接收文件的接口,将客户端上传过来的文件保存到本地,可以接收一个目标目录的参数,用做存放文件的目录</p><p>我们把上边的app.py增加一个upload_file接口</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request</span><br><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/ping"</span>, methods=[<span class="string">"GET"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ping</span>():</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"pong"</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/cmd"</span>, methods=[<span class="string">"post"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cmd</span>():</span></span><br><span class="line"> body = request.json</span><br><span class="line"> cmd = body.get(<span class="string">"cmd"</span>)</span><br><span class="line"> <span class="comment"># 基于subprocess.Popen方法执行本地shell命令</span></span><br><span class="line"> proc = subprocess.Popen(cmd, shell=<span class="literal">True</span>, stdout=subprocess.PIPE)</span><br><span class="line"> <span class="keyword">if</span> proc:</span><br><span class="line"> result = proc.stdout.read()</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/upload_file"</span>, methods=[<span class="string">"post"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload_file</span>():</span></span><br><span class="line"> <span class="comment"># 从请求中获取文件,如果上传的不是以file为key的文件,则认为未上传文件</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">"file"</span> <span class="keyword">not</span> <span class="keyword">in</span> request.files:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"not find file in request.files"</span></span><br><span class="line"> file = request.files[<span class="string">"file"</span>]</span><br><span class="line"> <span class="comment"># 如果请求没有带dest参数,则dest默认为/opt</span></span><br><span class="line"> dest = request.args.get(<span class="string">"dest"</span>, <span class="string">"/opt"</span>)</span><br><span class="line"> <span class="keyword">if</span> file:</span><br><span class="line"> file.save(dest)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"file uploaded successfully"</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"upload error"</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> app.run(host=<span class="string">"0.0.0.0"</span>, port=<span class="number">8080</span>, debug=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p>客户端代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload_file</span>(<span class="params">src, dest</span>):</span></span><br><span class="line"> resp = requests.post(</span><br><span class="line"> <span class="string">"http://127.0.0.1:8080/upload_file?dest="</span> + dest,</span><br><span class="line"> files={<span class="string">"file"</span>: <span class="built_in">open</span>(src, <span class="string">"rb"</span>)},</span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">if</span> resp.status_code == <span class="number">200</span>:</span><br><span class="line"> <span class="keyword">return</span> resp.text</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br></pre></td></tr></table></figure><p>此时客户端请求<code>http://127.0.0.1:8080/upload_file</code>,传入参数,则上传文件成功</p><h3 id="下载文件"><a href="#下载文件" class="headerlink" title="下载文件"></a>下载文件</h3><p>下载文件我们可以写个接口,接收文件所在目录地址和文件名2个参数</p><p>我们把上边的app.py增加一个download_file接口</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request</span><br><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/ping"</span>, methods=[<span class="string">"GET"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ping</span>():</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"pong"</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/cmd"</span>, methods=[<span class="string">"post"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cmd</span>():</span></span><br><span class="line"> body = request.json</span><br><span class="line"> cmd = body.get(<span class="string">"cmd"</span>)</span><br><span class="line"> <span class="comment"># 基于subprocess.Popen方法执行本地shell命令</span></span><br><span class="line"> proc = subprocess.Popen(cmd, shell=<span class="literal">True</span>, stdout=subprocess.PIPE)</span><br><span class="line"> <span class="keyword">if</span> proc:</span><br><span class="line"> result = proc.stdout.read()</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/upload_file"</span>, methods=[<span class="string">"post"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload_file</span>():</span></span><br><span class="line"> <span class="comment"># 从请求中获取文件,如果上传的不是以file为key的文件,则认为未上传文件</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">"file"</span> <span class="keyword">not</span> <span class="keyword">in</span> request.files:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"not find file in request.files"</span></span><br><span class="line"> file = request.files[<span class="string">"file"</span>]</span><br><span class="line"> <span class="comment"># 如果请求没有带dest参数,则dest默认为/opt</span></span><br><span class="line"> dest = request.args.get(<span class="string">"dest"</span>, <span class="string">"/opt"</span>)</span><br><span class="line"> <span class="keyword">if</span> file:</span><br><span class="line"> file.save(dest)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"file uploaded successfully"</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"upload error"</span></span><br><span class="line"> </span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/download_file"</span>, methods=[<span class="string">"post"</span>]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">download_file</span>():</span></span><br><span class="line"> body = request.json</span><br><span class="line"> directory = body.get(<span class="string">"directory"</span>)</span><br><span class="line"> filename = body.get(<span class="string">"filename"</span>)</span><br><span class="line"> <span class="comment"># send_from_directory用于发送本地的文件</span></span><br><span class="line"> <span class="keyword">return</span> send_from_directory(</span><br><span class="line"> directory=directory, filename=filename, as_attachment=<span class="literal">True</span></span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> app.run(host=<span class="string">"0.0.0.0"</span>, port=<span class="number">8080</span>, debug=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p>客户端代码如下:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">download_file</span>(<span class="params">src, dest</span>):</span></span><br><span class="line"> filename = src.split(<span class="string">"/"</span>)[-<span class="number">1</span>]</span><br><span class="line"> directory = src.replace(filename, <span class="string">""</span>)</span><br><span class="line"> <span class="built_in">print</span>(directory, filename)</span><br><span class="line"> data = {<span class="string">"directory"</span>: directory, <span class="string">"filename"</span>: filename}</span><br><span class="line"> resp = requests.post(<span class="string">"http://127.0.0.1:8080/download_file"</span>, json=data)</span><br><span class="line"> <span class="keyword">if</span> resp.status_code == <span class="number">200</span>:</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(dest, <span class="string">"wb"</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(resp.content)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br></pre></td></tr></table></figure><p>以上接口基本上实现saltstack常用功能。下一文章我们来讲讲长连接实现saltstack基本功能</p>]]></content>
<categories>
<category> 技术 </category>
<category> 分享 </category>
</categories>
<tags>
<tag> python </tag>
<tag> devops </tag>
<tag> linux </tag>
<tag> 运维 </tag>
</tags>
</entry>
<entry>
<title>模拟 saltstack/ansible 系列二(实现ansible主要功能)</title>
<link href="/post/b95044a6/"/>
<url>/post/b95044a6/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本次主要分享是如何基于Python模拟实现ansible的主要功能</p><h2 id="ansible功能分析与代码实现"><a href="#ansible功能分析与代码实现" class="headerlink" title="ansible功能分析与代码实现"></a>ansible功能分析与代码实现</h2><h3 id="连接远程主机"><a href="#连接远程主机" class="headerlink" title="连接远程主机"></a>连接远程主机</h3><p>ansible连接远程主机是通过SSH实现的,因此我们可以通过Python的SSH模块来实现连接远程主机。</p><p>Python中SSH模块主要是三方的<mark class="hl-label pink">paramiko</mark> 模块,这个模块也是ansible内部实现SSH所用的模块。</p><p>由于paramiko属于第三方库,所以需要使用如下命令先行安装</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install paramiko</span><br></pre></td></tr></table></figure><p>下面是使用paramiko进行SSH连接和SFTP连接的相关代码</p><blockquote><p>获取一个SSH连接</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_ssh_conn</span>(<span class="params">hostname, port, username, password</span>):</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># 实例化SSHClient</span></span><br><span class="line"> ssh_client = paramiko.SSHClient()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 当远程服务器没有本地主机的密钥时自动添加到本地,这样不用在建立连接的时候输入yes或no进行确认</span></span><br><span class="line"> ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())</span><br><span class="line"> <span class="comment"># 连接SSH服务器,这里以账号密码的方式进行认证,也可以用key认证</span></span><br><span class="line"> ssh_client.connect(</span><br><span class="line"> hostname=hostname, port=port, username=username, password=password, timeout=<span class="number">5</span></span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">return</span> ssh_client</span><br><span class="line"> <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"> logger.info(e)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br></pre></td></tr></table></figure><blockquote><p>获取一个SFTP连接</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_sftp_conn</span>(<span class="params">hostname, port, username, password</span>):</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># paramiko sftp功能需要先初始化一个Transport,有点类似打开一个文件传输通道</span></span><br><span class="line"> t = paramiko.Transport((hostname, port))</span><br><span class="line"> t.banner_timeout = <span class="number">10</span></span><br><span class="line"> t.connect(username=username, password=password)</span><br><span class="line"> sftp = paramiko.SFTPClient.from_transport(t)</span><br><span class="line"> <span class="keyword">return</span> sftp</span><br><span class="line"> <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br></pre></td></tr></table></figure><h3 id="执行命令"><a href="#执行命令" class="headerlink" title="执行命令"></a>执行命令</h3><p>paramiko既然能连接主机,当然也能执行命令</p><blockquote><p>基于SSH连接执行命令</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">exec_ssh_command</span>(<span class="params">ssh_client, cmd</span>):</span></span><br><span class="line"> <span class="comment"># 这个类似Popen执行命令,返回三个byte类型值,需要解码</span></span><br><span class="line"> stdin, stdout, stderr = ssh_client.exec_command(cmd, timeout=<span class="number">30</span>)</span><br><span class="line"> <span class="keyword">return</span> stderr.read().decode(), stdout.read().decode()</span><br></pre></td></tr></table></figure><h3 id="上传下载文件"><a href="#上传下载文件" class="headerlink" title="上传下载文件"></a>上传下载文件</h3><p>当我们初始化好后SFTP连接后,就可以对远程服务器进行文件上传下载了</p><blockquote><p>基于SFTP连接进行文件上传下载</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># put上传文件</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload_file</span>(<span class="params">sftp, src, dest</span>):</span></span><br><span class="line"> sftp.put(src, dest)</span><br><span class="line"></span><br><span class="line"><span class="comment"># get下载文件</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">download_file</span>(<span class="params">sftp, src, dest</span>):</span></span><br><span class="line"> sftp.get(src, dest)</span><br></pre></td></tr></table></figure><h3 id="ansible的ping功能"><a href="#ansible的ping功能" class="headerlink" title="ansible的ping功能"></a>ansible的ping功能</h3><p>其实我们可以通过paramiko是否能ssh连接远程主机作为ping功能实现的原理,当ssh能连接成功时,会返回一个sshclient,否则返回None,参考<code>获取一个SSH连接</code>的代码</p>]]></content>
<categories>
<category> 技术 </category>
<category> 分享 </category>
</categories>
<tags>
<tag> python </tag>
<tag> devops </tag>
<tag> linux </tag>
<tag> 运维 </tag>
</tags>
</entry>
<entry>
<title>模拟saltstack/ansible系列一(序言)</title>
<link href="/post/e4dbd1fc/"/>
<url>/post/e4dbd1fc/</url>
<content type="html"><![CDATA[<h2 id="序言"><a href="#序言" class="headerlink" title="序言"></a>序言</h2><h3 id="项目介绍"><a href="#项目介绍" class="headerlink" title="项目介绍"></a>项目介绍</h3><ul><li>做过运维的同学应该都知道,<mark class="hl-label blue">saltstack</mark> 和<mark class="hl-label green">ansible</mark> 都是运维常用的自动化工具,可以用来做批量系统配置、批量程序部署、批量运行命令等功能</li><li>本教程将分析saltstack和ansible的核心功能实现,然后自己用<mark class="hl-label pink">Python</mark> 实现一个类似saltstack和ansible的项目</li></ul><h3 id="saltstack、ansible原理"><a href="#saltstack、ansible原理" class="headerlink" title="saltstack、ansible原理"></a>saltstack、ansible原理</h3><blockquote><p>ansible</p></blockquote><ul><li>无Agent架构,基于SSH</li><li>ansible playbook可以按任务对批量主机编排</li><li>Python语言开发</li></ul><blockquote><p>saltstack</p></blockquote><ul><li>Agent架构,基于长连接</li><li>可以通过state文件来对批量主机编排</li><li>Python语言开发</li></ul><h2 id="自己如何开发一个实现ansible和saltstack基本功能的程序?"><a href="#自己如何开发一个实现ansible和saltstack基本功能的程序?" class="headerlink" title="自己如何开发一个实现ansible和saltstack基本功能的程序?"></a>自己如何开发一个实现ansible和saltstack基本功能的程序?</h2><h3 id="具体实现功能"><a href="#具体实现功能" class="headerlink" title="具体实现功能"></a>具体实现功能</h3><div class='checkbox green checked'><input type="checkbox" checked="checked"/> <p>主机连接模式</p> </div><ul><li>SSH连接模式</li><li>短连接模式</li><li>websocket长连接模式</li></ul><div class='checkbox red checked'><input type="checkbox" checked="checked"/> <p>Ad-hoc</p> </div><ul><li>ping功能</li><li>执行命令功能</li><li>copy文件功能</li><li>fetch文件功能</li></ul><div class='checkbox blue checked'><input type="checkbox" checked="checked"/> <p>模拟playbook、state文件执行</p> </div><ul><li>YAML读取</li><li>模板文件读取与配置</li><li>根据配置执行方法</li></ul><div class='checkbox pink checked'><input type="checkbox" checked="checked"/> <p>模拟ansible和saltstack命令行参数功能</p> </div><h3 id="开发语言"><a href="#开发语言" class="headerlink" title="开发语言"></a>开发语言</h3><p>Python(也可以用Go,主要是本人部门主用Python)</p><h3 id="使用的Python模块"><a href="#使用的Python模块" class="headerlink" title="使用的Python模块"></a>使用的Python模块</h3><ul><li>paramiko: SSH连接、SFTP上传下载文件、执行命令</li><li>ruamel/PyYaml: 解析YAML配置文件</li><li>jinja2: 模板引擎</li><li>click: 命令行模块</li><li>flask/sanic: web框架,agent使用</li><li>geventwebsocket: websocket服务端</li><li>websocket: websocket客户端</li><li>redis: 作为MQ,也可以用其他MQ</li><li>colorama:终端输入颜色字体</li><li>requests/httpx: http请求框架</li></ul>]]></content>
<categories>
<category> 技术 </category>
<category> 分享 </category>
</categories>
<tags>
<tag> python </tag>
<tag> devops </tag>
<tag> linux </tag>
<tag> 运维 </tag>
</tags>
</entry>
<entry>
<title>博客撰写计划</title>
<link href="/post/50c5b9ad/"/>
<url>/post/50c5b9ad/</url>
<content type="html"><![CDATA[<div class="note danger modern"><p>缘起</p></div><p>这些年记忆力越来越差,趁我还没老,趁我还在从事运维开发的工作,在这里记录下运维以及运维开发过程中经验,分享下运维以及运维开发的技术实战,分享尽量以项目实战为主!</p><div class="note primary flat"><p>运维管理平台项目</p></div><div class='checkbox'><input type="checkbox" /> <p>CMDB</p> </div><div class='checkbox'><input type="checkbox" /> <p>WEBSSH</p> </div><div class='checkbox'><input type="checkbox" /> <p>任务管理</p> </div><div class='checkbox'><input type="checkbox" /> <p>发布部署</p> </div><div class='checkbox'><input type="checkbox" /> <p>操作日志</p> </div><div class='checkbox'><input type="checkbox" /> <p>权限管理</p> </div><div class='checkbox'><input type="checkbox" /> <p>用户管理</p> </div><div class="note info flat"><p>模拟K8s管理系统</p></div><div class='checkbox'><input type="checkbox" /> <p>docker管理</p> </div><div class='checkbox'><input type="checkbox" /> <p>容器调度</p> </div><div class='checkbox'><input type="checkbox" /> <p>容器编排</p> </div><div class="note success flat"><p>模拟salt/Ansible</p></div><div class='checkbox'><input type="checkbox" /> <p>SSH形式、短连接模式、websocket长连接模式</p> </div><div class='checkbox'><input type="checkbox" /> <p>Ad-hoc</p> </div><div class='checkbox'><input type="checkbox" /> <p>playbook</p> </div><div class="note warning flat"><p>分布式监控系统</p></div><div class='checkbox'><input type="checkbox" /> <p>模仿zabbix、open-falcon、promethus等</p> </div>]]></content>
<categories>
<category> 技术 </category>
<category> 分享 </category>
</categories>
<tags>
<tag> python </tag>
<tag> go </tag>
<tag> devops </tag>
<tag> linux </tag>
<tag> 运维 </tag>
</tags>
</entry>
<entry>
<title>M1 MAC安装ubuntu系统的另一个选择-Multipass</title>
<link href="/post/510972a/"/>
<url>/post/510972a/</url>
<content type="html"><![CDATA[<div class="note primary modern"><p>缘起</p></div><p>今年Apple发布了新款搭载M1 Pro和M1 Max芯片的Macbook Pro,看着那屏幕甚是喜欢,性能也是爆炸。由于自己用了快2年的豆子峡谷黑苹果,貌似是CPU硅脂没了,温度动不动100℃,导致系统自动死机或重启,于是果断换成了丐版标配的M1 Pro Macbook。别说,新版Mac真的香!</p><p>由于M1芯片基于ARM,所以一些软件或虚拟机都得搞成ARM的,试过Parallels Desktop,搞起来不是很方便,巧合的突然想起以前下载Ubuntu server镜像时看到Multipass工具,它可以快速的初始化一个Linux系统,于是搜索了下发现Multipass工具已支持ARM运行Linux。so,开干!</p><div class="note info modern"><p>Multipass官网介绍</p></div><p><a href="https://github.com/canonical/multipass">Multipass</a> is a lightweight VM manager for Linux, Windows and macOS. It’s designed for developers who want a fresh Ubuntu environment with a single command. It uses KVM on Linux, Hyper-V on Windows and HyperKit on macOS to run the VM with minimal overhead. It can also use VirtualBox on Windows and macOS. Multipass will fetch images for you and keep them up to date.</p><p>Since it supports metadata for cloud-init, you can simulate a small cloud deployment on your laptop or workstation.</p><p>简单点说,该工具是一个虚拟机管理器,它在 Linux 上使用 KVM,在 Windows 上使用 Hyper-V,在 macOS 上使用 HyperKit 以最小的开销运行 VM,有点类似Docker</p><div class="note success modern"><p>安装</p></div><p><strong>Linux</strong><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo snap install multipass</span><br></pre></td></tr></table></figure></p><p><strong>Mac</strong><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install --cask multipass</span><br></pre></td></tr></table></figure></p><div class="note default modern"><p>使用</p></div><blockquote><p>快速启动一个VM</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ multipass launch --name <span class="built_in">test</span></span><br></pre></td></tr></table></figure><blockquote><p>具体配置内存、CPU、磁盘、系统版本来启动VM</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ multipass launch -n <span class="built_in">test</span> -c 2 -m 2G -d 20G 20.04</span><br></pre></td></tr></table></figure><blockquote><p>查看multipass具体支持什么系统</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ multipass find</span><br><span class="line">Image Aliases Version Description</span><br><span class="line">18.04 bionic 20211122 Ubuntu 18.04 LTS</span><br><span class="line">20.04 focal,lts 20211118 Ubuntu 20.04 LTS</span><br><span class="line">21.04 hirsute 20211119 Ubuntu 21.04</span><br><span class="line">21.10 impish 20211103 Ubuntu 21.10</span><br><span class="line">anbox-cloud-appliance latest Anbox Cloud Appliance</span><br><span class="line">minikube latest minikube is <span class="built_in">local</span> Kubernetes</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这里可以看到multipass还能安装minikube,这样来说快速初始化一个K8s单机实例是如此简单</p><blockquote><p>查看启动的虚拟机列表<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ multipass list</span><br><span class="line">Name State IPv4 Image</span><br><span class="line"><span class="built_in">test</span> Running 192.168.64.2 Ubuntu 20.04 LTS</span><br></pre></td></tr></table></figure></p><p>查看某个虚拟机具体信息</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ multipass info <span class="built_in">test</span></span><br><span class="line">Name: <span class="built_in">test</span></span><br><span class="line">State: Running</span><br><span class="line">IPv4: 192.168.64.2</span><br><span class="line">Release: Ubuntu 20.04.3 LTS</span><br><span class="line">Image <span class="built_in">hash</span>: e9271036efcd (Ubuntu 20.04 LTS)</span><br><span class="line">Load: 0.00 0.00 0.00</span><br><span class="line">Disk usage: 1.2G out of 19.2G</span><br><span class="line">Memory usage: 154.8M out of 1.9G</span><br><span class="line">Mounts: --</span><br></pre></td></tr></table></figure><blockquote><p>外部执行虚拟机内部命令</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$ multipass <span class="built_in">exec</span> <span class="built_in">test</span> -- df -h</span><br><span class="line">Filesystem Size Used Avail Use% Mounted on</span><br><span class="line">udev 957M 0 957M 0% /dev</span><br><span class="line">tmpfs 198M 1.1M 197M 1% /run</span><br><span class="line">/dev/sda1 20G 1.3G 18G 7% /</span><br><span class="line">tmpfs 990M 0 990M 0% /dev/shm</span><br><span class="line">tmpfs 5.0M 0 5.0M 0% /run/lock</span><br><span class="line">tmpfs 990M 0 990M 0% /sys/fs/cgroup</span><br><span class="line">/dev/loop0 58M 58M 0 100% /snap/core20/1244</span><br><span class="line">/dev/sda15 98M 290K 98M 1% /boot/efi</span><br><span class="line">/dev/loop1 61M 61M 0 100% /snap/lxd/21843</span><br><span class="line">/dev/loop2 29M 29M 0 100% /snap/snapd/13643</span><br><span class="line">tmpfs 198M 0 198M 0% /run/user/1000</span><br></pre></td></tr></table></figure><p>看,这语法是不是很像k8s kubectl连接pod语法</p><blockquote><p>连接到正在运行的实例</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">$ multipass shell <span class="built_in">test</span></span><br><span class="line">Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-90-generic aarch64)</span><br><span class="line"></span><br><span class="line"> * Documentation: https://help.ubuntu.com</span><br><span class="line"> * Management: https://landscape.canonical.com</span><br><span class="line"> * Support: https://ubuntu.com/advantage</span><br><span class="line"></span><br><span class="line"> System information as of Tue Nov 23 15:32:09 CST 2021</span><br><span class="line"></span><br><span class="line"> System load: 0.0</span><br><span class="line"> Usage of /: 6.4% of 19.22GB</span><br><span class="line"> Memory usage: 10%</span><br><span class="line"> Swap usage: 0%</span><br><span class="line"> Processes: 112</span><br><span class="line"> Users logged <span class="keyword">in</span>: 0</span><br><span class="line"> IPv4 address <span class="keyword">for</span> enp0s1: 192.168.64.2</span><br><span class="line"> IPv6 address <span class="keyword">for</span> enp0s1: fd0a:c754:c0d:703c:5054:ff:febd:2caa</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">0 updates can be applied immediately.</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">Last login: Tue Nov 23 15:29:51 2021 from 192.168.64.1</span><br><span class="line">ubuntu@<span class="built_in">test</span>:~$</span><br></pre></td></tr></table></figure><blockquote><p>停止VM</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ multipass stop <span class="built_in">test</span></span><br></pre></td></tr></table></figure><blockquote><p>启动VM</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ multipass start <span class="built_in">test</span></span><br></pre></td></tr></table></figure><blockquote><p>删除VM(只是进入DELETED状态)</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ multipass delete <span class="built_in">test</span></span><br></pre></td></tr></table></figure><blockquote><p>完全删除VM</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ multipass purge</span><br></pre></td></tr></table></figure><blockquote><p>更多语法</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">$ multipass <span class="built_in">help</span></span><br><span class="line">Usage: multipass [options] <<span class="built_in">command</span>></span><br><span class="line">Create, control and connect to Ubuntu instances.</span><br><span class="line"></span><br><span class="line">This is a <span class="built_in">command</span> line utility <span class="keyword">for</span> multipass, a</span><br><span class="line">service that manages Ubuntu instances.</span><br><span class="line"></span><br><span class="line">Options:</span><br><span class="line"> -h, --<span class="built_in">help</span> Displays <span class="built_in">help</span> on commandline options.</span><br><span class="line"> --help-all Displays <span class="built_in">help</span> including Qt specific options.</span><br><span class="line"> -v, --verbose Increase logging verbosity. Repeat the <span class="string">'v'</span> <span class="keyword">in</span> the short option</span><br><span class="line"> <span class="keyword">for</span> more detail. Maximum verbosity is obtained with 4 (or more)</span><br><span class="line"> v<span class="string">'s, i.e. -vvvv.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Available commands:</span></span><br><span class="line"><span class="string"> alias Create an alias</span></span><br><span class="line"><span class="string"> aliases List available aliases</span></span><br><span class="line"><span class="string"> delete Delete instances</span></span><br><span class="line"><span class="string"> exec Run a command on an instance</span></span><br><span class="line"><span class="string"> find Display available images to create instances from</span></span><br><span class="line"><span class="string"> get Get a configuration setting</span></span><br><span class="line"><span class="string"> help Display help about a command</span></span><br><span class="line"><span class="string"> info Display information about instances</span></span><br><span class="line"><span class="string"> launch Create and start an Ubuntu instance</span></span><br><span class="line"><span class="string"> list List all available instances</span></span><br><span class="line"><span class="string"> mount Mount a local directory in the instance</span></span><br><span class="line"><span class="string"> networks List available network interfaces</span></span><br><span class="line"><span class="string"> purge Purge all deleted instances permanently</span></span><br><span class="line"><span class="string"> recover Recover deleted instances</span></span><br><span class="line"><span class="string"> restart Restart instances</span></span><br><span class="line"><span class="string"> set Set a configuration setting</span></span><br><span class="line"><span class="string"> shell Open a shell on a running instance</span></span><br><span class="line"><span class="string"> start Start instances</span></span><br><span class="line"><span class="string"> stop Stop running instances</span></span><br><span class="line"><span class="string"> suspend Suspend running instances</span></span><br><span class="line"><span class="string"> transfer Transfer files between the host and instances</span></span><br><span class="line"><span class="string"> umount Unmount a directory from an instance</span></span><br><span class="line"><span class="string"> unalias Remove an alias</span></span><br><span class="line"><span class="string"> version Show version details</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">$ multipass launch -h</span><br><span class="line">Usage: multipass launch [options] [[<remote:>]<image> | <url>]</span><br><span class="line">Create and start a new instance.</span><br><span class="line"></span><br><span class="line">Options:</span><br><span class="line"> -h, --<span class="built_in">help</span> Displays <span class="built_in">help</span> on commandline options.</span><br><span class="line"> --help-all Displays <span class="built_in">help</span> including Qt specific options.</span><br><span class="line"> -v, --verbose Increase logging verbosity. Repeat the <span class="string">'v'</span> <span class="keyword">in</span> the short</span><br><span class="line"> option <span class="keyword">for</span> more detail. Maximum verbosity is obtained</span><br><span class="line"> with 4 (or more) v<span class="string">'s, i.e. -vvvv.</span></span><br><span class="line"><span class="string"> -c, --cpus <cpus> Number of CPUs to allocate.</span></span><br><span class="line"><span class="string"> Minimum: 1, default: 1.</span></span><br><span class="line"><span class="string"> -d, --disk <disk> Disk space to allocate. Positive integers, in bytes, or</span></span><br><span class="line"><span class="string"> with K, M, G suffix.</span></span><br><span class="line"><span class="string"> Minimum: 512M, default: 5G.</span></span><br><span class="line"><span class="string"> -m, --mem <mem> Amount of memory to allocate. Positive integers, in</span></span><br><span class="line"><span class="string"> bytes, or with K, M, G suffix.</span></span><br><span class="line"><span class="string"> Minimum: 128M, default: 1G.</span></span><br><span class="line"><span class="string"> -n, --name <name> Name for the instance. If it is '</span>primary<span class="string">' (the</span></span><br><span class="line"><span class="string"> configured primary instance name), the user'</span>s home</span><br><span class="line"> directory is mounted inside the newly launched instance,</span><br><span class="line"> <span class="keyword">in</span> <span class="string">'Home'</span>.</span><br><span class="line"> --cloud-init <file> Path to a user-data cloud-init configuration, or <span class="string">'-'</span> <span class="keyword">for</span></span><br><span class="line"> stdin</span><br><span class="line"> --network <spec> Add a network interface to the instance, <span class="built_in">where</span> <spec> is</span><br><span class="line"> <span class="keyword">in</span> the <span class="string">"key=value,key=value"</span> format, with the following</span><br><span class="line"> keys available:</span><br><span class="line"> name: the network to connect to (required), use the</span><br><span class="line"> networks <span class="built_in">command</span> <span class="keyword">for</span> a list of possible values, or use</span><br><span class="line"> <span class="string">'bridged'</span> to use the interface configured via `multipass</span><br><span class="line"> <span class="built_in">set</span> local.bridged-network`.</span><br><span class="line"> mode: auto|manual (default: auto)</span><br><span class="line"> mac: hardware address (default: random).</span><br><span class="line"> You can also use a shortcut of <span class="string">"<name>"</span> to mean</span><br><span class="line"> <span class="string">"name=<name>"</span>.</span><br><span class="line"> --bridged Adds one `--network bridged` network.</span><br><span class="line"> --timeout <timeout> Maximum time, <span class="keyword">in</span> seconds, to <span class="built_in">wait</span> <span class="keyword">for</span> the <span class="built_in">command</span> to</span><br><span class="line"> complete. Note that some background operations may</span><br><span class="line"> <span class="built_in">continue</span> beyond that. By default, instance startup and</span><br><span class="line"> initialization is limited to 5 minutes each.</span><br><span class="line"></span><br><span class="line">Arguments:</span><br><span class="line"> image Optional image to launch. If omitted, <span class="keyword">then</span> the default</span><br><span class="line"> Ubuntu LTS will be used.</span><br><span class="line"> <remote> can be either ‘release’ or ‘daily‘. If <remote></span><br><span class="line"> is omitted, ‘release’ will be used.</span><br><span class="line"> <image> can be a partial image <span class="built_in">hash</span> or an Ubuntu release</span><br><span class="line"> version, codename or <span class="built_in">alias</span>.</span><br><span class="line"> <url> is a custom image URL that is <span class="keyword">in</span> http://, https://,</span><br><span class="line"> or file:// format.</span><br><span class="line"></span><br></pre></td></tr></table></figure><div class="note warning modern"><p>拓展</p></div><blockquote><p>SSH连接multipass启动的VM</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">multipass shell <span class="built_in">test</span></span><br><span class="line">sudo -i <span class="comment"># 切到root用户</span></span><br><span class="line">vim /etc/ssh/sshd_config</span><br><span class="line">将PasswordAuthentication no修改为</span><br><span class="line">PasswordAuthentication yes</span><br><span class="line">将PermitRootLogin prohibit-password修改为</span><br><span class="line">PermitRootLogin yes</span><br><span class="line">重启ssh</span><br><span class="line">service ssh restart</span><br><span class="line">现在就可以愉快的ssh登录了</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 分享 </category>
</categories>
<tags>
<tag> linux </tag>
</tags>
</entry>
<entry>
<title>Hello World</title>
<link href="/post/4a17b156/"/>
<url>/post/4a17b156/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post221"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
</entry>
</search>