首页 文章 精选 留言 我的

精选列表

搜索[自动装配],共10000篇文章
优秀的个人博客,低调大师

Docker 实战案例解析,实现自动化数据库迁移

出品丨Docker公司(ID:docker-cn)编译丨小东每周一、三、五晚6点10分 与您不见不散! 在 Phorest Salon Software 公司,我们是在 AWS 上运行我们的平台。该平台由 VPC 内的许多 AWS 资源(实例,数据库)组成。我们的大部分服务都运行在由亚马逊 ECS 管理的 Docker 容器上。 迁移的挑战 我们有三个专用环境:开发、分段构建和生产。每个环境都有自己的专用 VPC,因此 VPC 中的资源不能直接从外部访问,通常来说这是很好的。 问题是我们有时需要修改某些组件的模式结构,例如:添加一个新的表或列。在 Java-ish 中,我们使用 Liquibase 来管理 DDL / DML 的更改。同样的,通常来说这是很好的。但是我们要如何才能使这些更改通过持续交付的方式应用到各个不同的环境中呢? 我们针对上述问题,提出了几种解决方法: 1、在启动时执行 您可以让您的组件执行此操作。例如,您可以将应用程序配置为在启动时应用它。对于 Spring Boot 应用程序来说,这非常简单。您只需将 liquibase jar 添加到环境变量并配置一些配置属性即可实现此目的。 这种方法对于在本地的开发或开发环境来说可能是很好的,但是对于分段构建或生产呢?问题在于,您的数据库迁移脚本可能很慢,执行它们可能需要几分钟、几个小时甚至几天的时间。 对于上述情况,使用 ECS 的效果就不太理想了,因为在那里部署服务时,您定义了 ECS 将用于验证组件的运行状态,在执行数据库迁移脚本的情况下启动它,会导致整个迁移过程被认为是不健康的。而且 ECS 可能会在迁移过程中杀死您的容器。您可以为 ECS 服务配置一个宽限期,但这可能很难预测到它的值,因为每个迁移都可能不同。 2、手动应用 另一种选择是 SSH 连接到与服务在同一个 VPC 中运行的EC2实例,并从那里手动运行迁移脚本(你可能知道这种做法有多糟糕)。 3、使用 docker 容器 另一个选择是创建一个专用的 docker 镜像来运行迁移脚本。我们选择了这个选项: 定义 docker 镜像: 我们使用了一个开源的基础镜像,它为我们提供了一种执行 Liquibase 更改日志的方法。我们所要做的就是定义一些环境变量并将 Liquibase 更改日志复制过去。空变量是故意放进去的,以便大家在运行容器时记得替换掉它们。 我们将镜像定义文件与我们的代码库一起存储,因此在发布时会对其进行版本控制和标记。有了这一点,我们更新了 Jenkins 的工作,作为工作流程的一部分来构建和推动新的 docker 镜像。 下一步是更新 Jenkins 的工作来触发使用我们的新镜像迁移数据库的 ECS 任务。为此,我们使用了自己的框架,但您可以使用 Terraform 或K8s,或者您甚至可以使用以下命令手动运行它(请不要在生产中执行!) docker run your-image-tag liquibase update 使用新步骤构建管道 这种方法的好处 由于我们不在容器启动时运行迁移,所以我们不再有与长时间运行的迁移相关的问题,从而导致 ECS 运行状况检查失败。但好处是,我们的迁移现在已经与我们的部署分离了(这是一篇很好的文章,解释了从应用程序部署中分离数据库迁移的好处)。 使用这种新方法,我们必须确保我们的迁移始终与当前在生产中运行的代码兼容。它需要一些额外的工作,但现在意味着我们可以毫不费力地回滚我们的版本。 解决这个问题可能还有许多其他(也许更好)的方法,但是这种方法对我们更有效。如果你对这个话题有任何想法或想法可以在文尾处留言与我们探讨。

优秀的个人博客,低调大师

Python实现ECS自动镜像创建&镜像复制至其他地域

一、背景 1.1 问题: 同事反馈有可以鉴于目前几次大的公有云事故,腾讯云/阿里云两大公有云厂商尚且存在这样令人触目惊心的时刻,更何况其他厂商和我们的日常操作,有人的地方就有误操作,百分之一的风险但如果一旦发生就是100%的问题,虽SLA但或多或少存在影响,客户反馈如果阿里的A地域发生故障,例如地质灾害或不可控因素引发的ecs无法访问,用户数据都在云上无法操作情况下该如何,但考虑到不同地域灾备的高额度IT成本,想采用每天ecs的冷备。 1.2 思路: 在最大程度的降低IT成本,又想在不可控大规模地域性灾难面前做些什么,每天凌晨业务低峰期对ECS制作镜像,同时复制到其他的不同地域,如北京的镜像复制到上海,当北京整个region异常情况下,可利用复制在目标地域的ECS创建出来,在此抛砖引玉,后续可以将ecs在目标地域开出来并关机,归档删除之前的镜像,等等。同样可以将RDS备份也同样备份到异地OSS内,目前阿里已经有EBS非常方便的灾难情况下恢复RDS。利用此思路同意的适用于其他场景下。 二、代码 2.1 结构 如果多个实例可同时写入配置文件,用,进行分割。 2.2 核心代码 配置文件 # 阿里云ak配置,建议采用子账户只授权ecs镜像操作 [common] # 阿里云acccesskeyid accessKeyId = LTAIhfXlcjyln6tW # 阿里云accesssecret accessSecret = GwfAMvR4K2ELmt76184oqLTVgRfAso # log目录名称 logdir_name = logdir # log文件名称 logfile_name = ecsoperlog.log # ecs源地域配置信息段 #支持在华北 1、华北 2、华北 3、华北 5、华东 1、华东 2 和华南 1 地域之间复制镜像。涉及其他国家和地区地域时,可以 提交工单 申请 [source] # 源地域实例regionid,可以参考:https://help.aliyun.com/document_detail/40654.html?spm=a2c1g.8271268.10000.5.5f98df25B98bhJ s_RegionId = cn-shanghai # 源实例id,可指定多个用,进行分隔 s_InstanceId = i-uf661wb708uvqc9jyhem,i-uf661wb708uvqc9jyhel # 源端制作镜像name s_ImageName = api-source-image # 源镜像描述信息 s_Description = api-source-image源镜像描述信息 # 镜像复制目的地域配置信息段 [destination] # 目的地域实例regionid, d_DestinationRegionId = cn-qingdao # 复制过来的镜像名称 d_DestinationImageName = api-destination-image # 复制过来的镜像描述信息 d_DestinationDescription = api-destination-image目的镜像描述信息 image操作(制作镜像->查看镜像制作状态->复制镜像) # 创建实例生成器 def _get_Instance(self): for Instance in self.s_InstanceId_list.split(','): yield Instance def _create_image(self): """ 创建镜像 :return:返回镜像id """ s_timer = time.strftime("%Y-%m-%d-%H:%M", time.localtime(time.time())) request = CreateImageRequest.CreateImageRequest() request.set_accept_format('json') request.add_query_param('RegionId', self.s_RegionId) request.add_query_param('InstanceId', self.s_InstanceId) request.add_query_param('ImageName', self.s_ImageName + s_timer) request.add_query_param('Description', self.s_Description + s_timer) response = self.ecshelper.do_action_with_exception(request) self.logoper.info('创建镜像任务已提交,镜像id:%s' % json.loads(response)["ImageId"]) print('创建镜像任务已提交,镜像id:%s' % json.loads(response)["ImageId"]) return json.loads(response)["ImageId"] def _describe_image(self,imageid): """ 查询image状态 :param imageid: :return: """ request = DescribeImagesRequest.DescribeImagesRequest() request.set_accept_format('json') request.add_query_param('RegionId', self.s_RegionId) request.add_query_param('ImageId', imageid) response = self.ecshelper.do_action_with_exception(request) # 进度 json.loads(response)['Images']['Image'][0]['Progress'] self.logoper.info('镜像创建进度:%s' %json.loads(response)['Images']['Image'][0]['Progress']) # 镜像状态 return json.loads(response)['Images']['Image'][0]['Status'] #镜像复制 def _copy_image(self,imageid): """ 镜像复制 :param imageid:源镜像id :return: 复制成功后的镜像id """ flag = True while flag: try: if self._describe_image(imageid) == 'Available': flag = False else: time.sleep(300) except Exception as e: pass print('镜像已经创建完成') d_timer = time.strftime("%Y-%m-%d-%H:%M", time.localtime(time.time())) request = CopyImageRequest.CopyImageRequest() request.set_accept_format('json') request.add_query_param('RegionId', self.s_RegionId) request.add_query_param('DestinationRegionId', self.d_DestinationRegionId) request.add_query_param('DestinationImageName', self.d_DestinationImageName + d_timer) request.add_query_param('DestinationDescription', self.d_DestinationDescription + d_timer) request.add_query_param('ImageId', imageid) response = self.ecshelper.do_action_with_exception(request) self.logoper.info('复制镜像任务已提交,镜像id:%s' % json.loads(response)['ImageId']) print('复制镜像任务已提交,镜像id:%s' % json.loads(response)['ImageId']) return json.loads(response)['ImageId'] 三、测试 3.1 查看运行结果 3.2 查看web控制台 源镜像 添加了时间戳,方便查看 目的地域镜像 3.3 查看日志 四、优化 可以后续增加对制定天数的镜像进行归档删除

优秀的个人博客,低调大师

超级简单:共享两个自动生成存储过程的工具

开发一个项目或者开发一个应用系统初期大多数都是从数据库,类,UI界面开始的。其中最令人厌烦的是写些简单的CRUD的存储过程,以及调用这些存储过程的方法的类。 我花了很多时间在网上寻找,去找一个能根据能数据库中一个存在的数据表,为我们产生大部分存储过程和调用这些存储过程C#代码的实用的程序。这里共享两个我觉得还不错的程序给大家。 第一个是SQLAutoGen,如下图: 程序地址:http://www.codeproject.com/KB/database/SQLAutoGen.aspx 这是通过迭代我们选择数据表的列,来产生我们需要的代码。通过使用选择一些不同列(将出现在where子句中),使生产脚本变得相当智能。 (见下面的示例代码示例) 看上面图片,能抵得说很多话,它来表明这个应用程序的操作。 下面是这些代码,通过程序以不同的方式迭代本身,这里一个结合了SqlClient和SqlSMO访问数据库,得到table和columns等信息: 代码 代码 下面是一个示例表画面,以及随后产生的脚本。 代码 第二个是codeplex上面的程序:Stored Procedure Generator (for SQL Server 2000/2005) ,界面如下图所示: 解决方案的结构如下: 项目地址:http://spgen.codeplex.com/ 代码不是特别的难,有兴趣可以自己下载研究。这个程序产生的存储过程中的where子句中的列是固定的,注定了没有第一个程序那么灵活。 本文转自麒麟博客园博客,原文链接:http://www.cnblogs.com/zhuqil/archive/2009/12/31/1636460.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

CrazyWing:Python自动化运维开发实战 十七、Python异常

导语: 在写代码的时候,经常会遇到异常。python提供了两个功能来处理程序在运行中出现的异常和错误,可以使用该功能来调试python程序。 异常处理 断言(Assertions) 常用异常: Exception 它可以捕获任意(绝大部分)异常。 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类),代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的 python标准异常: 什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。 一般情况下,在Python无法正常处理程序时就会发生一个异常,异常是Python对象,表示一个错误。 当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。 #!/usr/bin/env python try: print "%d" % (5 / 0) except ZeroDivisionError: print "除数不能为零" else: print "没有报错" print "这是异常之后的代码" #如果没有上面的异常处理,下面的代码是不会执行的 for i in range(10): print i 捕捉异常: try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。 如果你不想在异常发生时结束你的程序,只需在try里捕获它。 语法: try: <语句> #运行别的代码 except <名字>: <语句> #如果在try部份引发了'name'异常 except <名字>,<数据>: <语句> #如果引发了'name'异常,获得附加的数据 else: <语句> #如果没有异常发生 try的工作原理 当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。 1. 如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的 except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异 常)。 2. 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的 try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。 3. 如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的 话),然后控制流通过整个try语句。 例 打开一个文件,在该文件中的写入内容,且并未发生异常: try: fh = open("testfile", "w") fh.write("这是一个测试文件,用于测试异常!!") except IOError: print "Error: 没有找到文件或读取文件失败" else: print "内容写入文件成功" fh.close() 结果: # python test.py 内容写入文件成功 # cat testfile # 查看写入的内容 这是一个测试文件,用于测试异常!! 例 打开一个文件,在该文件中的内容写入内容,但文件没有写入权限,发生了异常: try: fh = open("testfile", "w") fh.write("这是一个测试文件,用于测试异常!!") except IOError: print "Error: 没有找到文件或读取文件失败" else: print "内容写入文件成功" fh.close() 在执行代码前为了测试方便,先去掉 testfile 文件的写权限 再执行以上代码: $ python test.py #注意这里用的是普通用户 Error: 没有找到文件或读取文件失败 使用except不带任何异常类型 你可以不带任何异常类型使用except,如下实例: try: 正常的操作 ...................... except: 发生异常,执行这块代码 ...................... else: 如果没有异常执行这块代码 以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常。 使用except带多种异常类型 也可以使用相同的except语句来处理多个异常信息: try: 正常的操作 ................ except(Exception1[, Exception2[,...ExceptionN]]]): 发生以上多个异常中的一个,执行这块代码 ...................... else: 如果没有异常执行这块代码 try-finally 语句 try-finally 语句无论是否发生异常都将执行最后的代码。 try: <语句> finally: <语句> #退出try时总会执行 例1: try: fh = open("testfile", "w") fh.write("这是一个测试文件,用于测试异常!!") finally: print "Error: 没有找到文件或读取文件失败" 例2: import time try: f=file("文件.py") while True: line = f.read() if len(line)==0: break time.sleep(2) print line, finally: f.close() print "hello" 例3: try: fh = open("testfile", "w") try: fh.write("这是一个测试文件,用于测试异常!!") finally: print "关闭文件" fh.close() except IOError: print "Error: 没有找到文件或读取文件失败" 异常的参数: 一个异常可以带上参数,可作为输出的异常信息参数。你可以通过except语句来捕获异常的参数,如下所示: try: 正常的操作 ...................... except ExceptionType, Argument: 你可以在这输出 Argument 的值... 变量接收的异常值通常包含在异常的语句中。在元组的表单中变量可以接收一个或者多个值。元组通常包含错误字符串,错误数字,错误位置。 例以下为单个异常的实例: #!/usr/bin/python def temp_convert(var): try: return int(var) except ValueError, Argument: print "参数没 有包含数字\n", Argument # 调用函数 temp_convert("xyz") 以上程序执行结果如下: $ python test.py 参数没有包含数字 invalid literal for int() with base 10: 'xyz' 触发异常 可以使用raise语句自己触发异常raise语法格式: raise [Exception [, args [, traceback]]] 语句中Exception是异常的类型(例如,NameError)参数是一个异常参数值。该参数是可选的,如果不提供,异常的参数是"None"。最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象。 例一个异常可以是一个字符串,类或对象。 Python的内核提供的异常,大多数都是实例化的类,这是一个类的实例的参数。定义一个异常: def functionName( level ): if level < 1: raise Exception("Invalid level!", level) # 触发异常后,后面的代码就不会再执行 注意:为了能够捕获异常,"except"语句必须有用相同的异常来抛出类对象或者字符串。例如我们捕获以上异常,"except"语句如下: try: 正常逻辑 except "Invalid level!": 触发自定义异常 else: 其余代码 例 #!/usr/bin/python def mye( level ): if level < 1: raise Exception("Invalid level!", level) # 触发异常后,后面的代码就不会再执行 try: mye(0) # 触发异常 except "Invalid level!": print 1 else: print 2 输出结果: $ python test.py Traceback (most recent call last): File "test.py", line 11, in <module> mye(0) File "test.py", line 7, in mye raise Exception("Invalid level!", level) Exception: ('Invalid level!', 0) 用户自定义异常: 通过创建一个新的异常类,程序可以命名它们自己的异常。异常应该是典型的继承自Exception类,通过直接或间接的方式。以下为与RuntimeError相关的实例,实例中创建了一个类,基类为RuntimeError,用于在异常触发时输出更多的信息。在try语句块中,用户自定义的异常后执行except块语句,变量 e 是用于创建Networkerror类的实例。 class Networkerror(RuntimeError): def __init__(self, arg): self.args = arg 在你定义以上类后,你可以触发该异常,如下所示: try: raise Networkerror("Bad hostname") except Networkerror,e: print e.args 万能异常 在python的异常中,有一个万能异常:Exception,它可以捕获任意异常。例: #cat aa.py s1 = 'hello' try: int(s1) except Exception,e: print e 执行结果: #python aa.py invalid literal for int() with base 10: 'hello' 既然有这个万能异常,其他异常是不是就可以忽略了?当然不是,对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。例: s1 = 'hello' try: int(s1) except KeyError,e: print '键错误' except IndexError,e: print '索引错误' except Exception, e: print '错误' ====================================== assert断言 使用assert断言是学习python一个非常好的习惯,assert断言句语格式及用法很简单。在没完善一个程序之前,我们不知道程序在哪里会出错,与其让它在运行最崩溃,不如在出现错误条件时就崩溃,这时候就需要assert断言的帮助。 assert断言的作用 assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常。assert断言语句的语法格式assert expression 一些assert用法的语句供参考: assert 1==1 assert 2+2==2*2 assert len(['my boy',12])<10 assert range(4)==[0,1,2,3] 如何为assert断言语句添加异常参数 assert的异常参数,其实就是在断言表达式后添加字符串信息,用来解释断言并更好的知道是哪里出了问题。格式如下: assert expression [, arguments] 何时使用断言 Python的assert是用来检查一个条件,如果它为真,就不做任何事。如果它为假,则会抛出AssertError并且包含错误信息。例如: py> x = 23 py> assert x > 0, "x is not zero or negative" py> assert x%2 == 0, "x is not an even number" Traceback (most recent call last): File "", line 1, in .... AssertionError: x is not an even number 很多人用assert作为一个很快和容易的方法来在参数错误的时候抛出异常。但这样做是错的,非常错误,有两个原因。首先AssertError不是在测试参数时应该抛出的错误。你不应该像这样写代码: if not isinstance(x, int): raise AssertionError("not an int") 你应该抛出TypeError的错误,assert会抛出错误的异常。 但是,更危险的是,有一个关于assert的困扰:它可以被编译好然后从来不执行,如果你用 –O 或 –oo 选项运行Python,结果不保证assert表达式会运行到。当适当的使用assert时,这是未来,但是当assert不恰当的使用时,它会让代码用-O执行时出错。 那什么时候应该使用assert?没有特定的规则,断言应该用于: 防御型的编程 运行时检查程序逻辑 检查约定 程序常量 检查文档 (在测试代码的时候使用断言也是可接受的,是一种很方便的单元测试方法,你接受这些测试在用-O标志运行时不会做任何事。我有时在代码里使用assert False来标记没有写完的代码分支,我希望这些代码运行失败。尽管抛出NotImplementedError可能会更好。) 关于断言的意见有很多,因为它能确保代码的正确性。如果你确定代码是正确的,那么就没有用断言的必要了,因为他们从来不会运行失败,你可以直接移除这些断言。如果你确定检查会失败,那么如果你不用断言,代码就会通过编译并忽略你的检查。 在以上两种情况下会很有意思,当你比较肯定代码但是不是绝对肯定时。可能你会错过一些非常古怪的情况。在这个情况下,额外的运行时检查能帮你确保任何错误都会尽早地被捕捉到。 另一个好的使用断言的方式是检查程序的不变量。一个不变量是一些你需要依赖它为真的情况,除非一个bug导致它为假。如果有bug,最好能够尽早发现,所以我们为它进行一个测试,但是又不想减慢代码运行速度。所以就用断言,因为它能在开发时打开,在产品阶段关闭。 一个非变量的例子可能是,如果你的函数希望在它开始时有数据库的连接,并且承诺在它返回的时候仍然保持连接,这就是函数的不变量: def some_function(arg): assert not DB.closed() ... # code goes here assert not DB.closed() return result 断言本身就是很好的注释,胜过你直接写注释: # when we reach here, we know that n > 2 你可以通过添加断言来确保它: assert n > 2 断言也是一种防御型编程。你不是让你的代码防御现在的错误,而是防止在代码修改后引发的错误。理想情况下,单元测试可以完成这样的工作,可是需要面对的现实是,它们通常是没有完成的。人们可能在提交代码前会忘了运行测试代码。有一个内部检查是另一个阻挡错误的防线,尤其是那些不明显的错误,却导致了代码出问题并且返回错误的结果。 加入你有一些if…elif 的语句块,你知道在这之前一些需要有一些值: # target is expected to be one of x, y, or z, and nothing else. if target == x: run_x_code() elif target == y: run_y_code() else: run_z_code() 假设代码现在是完全正确的。但它会一直是正确的吗?依赖的修改,代码的修改。如果依赖修改成 target = w 会发生什么,会关系到run_w_code函数吗?如果我们改变了代码,但没有修改这里的代码,可能会导致错误的调用 run_z_code 函数并引发错误。用防御型的方法来写代码会很好,它能让代码运行正确,或者立马执行错误,即使你在未来对它进行了修改。 在代码开头的注释很好的一步,但是人们经常懒得读或者更新注释。一旦发生这种情况,注释会变得没用。但有了断言,我可以同时对代码块的假设书写文档,并且在它们违反的时候触发一个干净的错误 assert target in (x, y, z) if target == x: run_x_code() elif target == y: run_y_code() else: assert target == z run_z_code() 这样,断言是一种防御型编程,同时也是一种文档。我想到一个更好的方案: if target == x: run_x_code() elif target == y: run_y_code() elif target == z: run_z_code() else: # This can never happen. But just in case it does... raise RuntimeError("an unexpected error occurred") 按约定进行设计是断言的另一个好的用途。我们想象函数与调用者之间有个约定,比如下面的: “如果你传给我一个非空字符串,我保证传会字符串的第一个字母并将其大写。” 如果约定被函数或调用这破坏,代码就会出问题。我们说函数有一些前置条件和后置条件,所以函数就会这么写: def first_upper(astring): assert isinstance(astring, str) and len(astring) > 0 result = astring[0].upper() assert isinstance(result, str) and len(result) == 1 assert result == result.upper() return result 按约定设计的目标是为了正确的编程,前置条件和后置条件是需要保持的。这是断言的典型应用场景,因为一旦我们发布了没有问题的代码到产品中,程序会是正确的,并且我们能安全的移除检查。 建议不要用断言的场景: 不要用它测试用户提供的数据 不要用断言来检查你觉得在你的程序的常规使用时会出错的地方。断言是用来检查非常罕见的问题。你的用户不应该看到任何断言错误,如果他们看到了,这是一个bug,修复它。 有的情况下,不用断言是因为它比精确的检查要短,它不应该是懒码农的偷懒方式。 不要用它来检查对公共库的输入参数,因为它不能控制调用者,所以不能保证调用者会不会打破双方的约定。 不要为你觉得可以恢复的错误用断言。换句话说,不用改在产品代码里捕捉到断言错误。 不要用太多断言以至于让代码很晦涩。

优秀的个人博客,低调大师

用tarball实现liferay自动安装部署5-shell 脚本概述

做了前面所有步骤的概述,现在就开始讲解tarball中最核心,也是最重要的部分了--shell script,否则怎么能成为tarball呢。 从总体上,基于公司的需求,我们将脚本分化在5个子脚本中,以下是这些脚本的介绍,我直接从架构文档上复制过来了: Script Name Description configure-helper.sh This script is used for setting the constants,variables and do fundamental calculation or string parsing. configure.sh It will read the constants,variables defined in configure-helper.sh and actually do the fresh install work on a new machine. start.sh It will read configure-helper.sh and start the liferay server. stop.sh It will read configure-helper.sh and stop the liferay server. unconfigure.sh Environment cleanup (Now we haven’t defined the responsibility clearly ,so we leave it blank) 本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/979803,如需转载请自行联系原作者

优秀的个人博客,低调大师

运维自动化之ansible playbook结合docker安装smokeping

本次介绍ansible的paly book结合docker进行虚拟机里安装2.6.8版本smokeping(apache版本是2.4.7)。 docker版本 1 2 3 4 5 6 7 8 9 10 09:26:53 #dockerversion Clientversion:0.11.1 ClientAPIversion:1.11 Goversion(client):go1.2.1 Gitcommit(client):fb99f99 /0 .11.1 Serverversion:0.11.1 ServerAPIversion:1.11 Gitcommit(server):fb99f99 /0 .11.1 Goversion(server):go1.2.1 Laststableversion:1.1.2,pleaseupdatedocker ansible版本 1 2 09:28:13 #ansible--version ansible1.4.3 1、查看docker已有镜像 1 2 3 4 5 09:25:55 #dockerimages REPOSITORYTAGIMAGEIDCREATEDVIRTUALSIZE ubuntu3.06cee552765289weeksago369.8MB centos53.0e08d23b091899weeksago840.9MB centos63.0e94a3b24a19b9weeksago415.9MB 可以看到有3个镜像,1个是ubuntu,1个centos5,1个centos6.我这里打算使用centos6来弄。 2、加载新容器 1 2 3 4 5 6 09:31:01 #timedockerinspect$(dockerrun-d-p22-p80:80--name="smokeping"centos6:3.0/usr/sbin/sshd-D)|grep-iaddress|awk-F'"''{print$4}' 172.17.0.5 real 0m4.737s user 0m0.038s sys 0m0.054s 可以看到4秒就加载新容器完成,并且镜像为centos6系统,容器名为smokeping,开启了ssh服务,并且暴漏了22与80端口。 下面在从后端看看本机都运行了哪些容器 1 2 3 4 5 6 09:31:29 #dockerps-a CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES 56b70c31a07ecentos6:3.0 /usr/sbin/sshd -DAboutaminuteagoUpAboutaminute0.0.0.0:80->80 /tcp ,0.0.0.0:49156->22 /tcp smokeping 846efb9e4d7aubuntu:3.0 /usr/sbin/sshd -D12daysagoUp4days0.0.0.0:49167->22 /tcp ubuntu-test1 b9a9e6f2caedcentos6:3.0 /usr/sbin/sshd -D3weeksagoUp4days0.0.0.0:49166->22 /tcp zabbix-server 978fff134b18centos6:3.0 /usr/sbin/sshd -D4weeksagoUp4days0.0.0.0:49165->22 /tcp centos6-test5 下面是ansible安装smokeping的部分 3、ansible安装smokeping的信息 1 2 3 4 5 09:34:06 #catsmokeping_install/vars/main.yml smokeping_dir: /usr/local/smokeping smokeping_version:2.6.8 smokeping_user:admin smokeping_passwd: '()TF%penst*&MedHU' 可以看到smokeping安装目录是/usr/local/smokeping,安装版本是2.6.8,登陆的账户是admin,登陆密码是()TF%penst*&MedHU 4、下面是安装smokeping的playbook结构 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 09:36:25 #treesmokeping_* smokeping_delete ├──files │└──delete_smokeping.sh ├──handlers ├──meta │└──main.yml ├──tasks │├──copy.yml │├──delete.yml │└──main.yml ├──templates └──vars ├──main.xml ├──main.yml └──vars └──main.yml smokeping_install ├──files │├──rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm │└──smokeping. tar .gz ├──handlers ├──meta │└──main.yml ├──tasks │├──copy.yml │├──delete.yml │├── install .yml │└──main.yml ├──templates │├──config │└──smokeping.conf └──vars └──main.yml 13directories,18files 5、playbook安装smokeping的内容是 1 2 3 4 5 6 7 8 9 10 09:37:27 #catsmokeping_install.yml --- -hosts: "`host`" remote_user: "`user`" gather_facts:True roles: -common -pcre_install -apache_install -smokeping_install 6、playbook删除smokeping的内容是 1 2 3 4 5 6 7 8 9 09:38:01 #catsmokeping_delete.yml --- -hosts: "`host`" remote_user: "`user`" gather_facts:True roles: -apache_delete -pcre_delete -smokeping_delete 下面是安装与测试过程 7、把docker新容器smokeping的ip加入到ansible的hosts里 1 echo "172.17.0.5" >> /etc/ansible/hosts 8、安装smokeping 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 09:39:10 #timeansible-playbooksmokeping_install.yml--extra-vars"host=172.17.0.5user=root"-k SSHpassword: PLAY[172.17.0.5]************************************************************* GATHERINGFACTS*************************************************************** ok:[172.17.0.5] TASK:[common|Installinitializtionrequiresoftware]*********************** changed:[172.17.0.5] TASK:[pcre_install|CopyPcreSoftwareToRedhatClient]******************** changed:[172.17.0.5] TASK:[pcre_install|UncompressionPcreSoftwareInRedhatClient]*********** changed:[172.17.0.5] TASK:[pcre_install|DeletePcreSoftwareInRedhatClient]****************** changed:[172.17.0.5] TASK:[apache_install|CopyApacheSoftwareToRedhatClient]**************** changed:[172.17.0.5]=>(item=httpd-2.4.7. tar .gz) changed:[172.17.0.5]=>(item=libiconv. tar .gz) TASK:[apache_install|CreateApacheUserInRedhatClient]****************** changed:[172.17.0.5] TASK:[apache_install|UncompressionApacheSoftwareToRedhatClient]******* changed:[172.17.0.5] TASK:[apache_install|CopyApacheConfigToRedhatClient]****************** changed:[172.17.0.5] TASK:[apache_install|CopyApacheVhostConfigToRedhatClient]************ changed:[172.17.0.5] TASK:[apache_install|CopyApacheStartServiceScriptToRedhatClient]*** changed:[172.17.0.5] TASK:[apache_install|CreateLibInstallDir]******************************* ok:[172.17.0.5] TASK:[apache_install|CheckApacheIconvInRedhatClient]****************** changed:[172.17.0.5] TASK:[apache_install|InstallApacheIconvInRedhatClient]**************** changed:[172.17.0.5] TASK:[apache_install|CheckLibInConfigInRedhatClient]***************** failed:[172.17.0.5]=>{ "changed" : true , "cmd" : "grep-c/usr/local/lib//etc/ld.so.conf" , "delta" : "0:00:00.006524" , "end" : "2014-08-1109:40:48.822372" , "item" : "" , "rc" :1, "start" : "2014-08-1109:40:48.815848" } stdout:0 ...ignoring TASK:[apache_install|InstallApacheIconvInRedhatClient]**************** changed:[172.17.0.5] TASK:[apache_install|CreateApacheDir]************************************ changed:[172.17.0.5]=>(item= /data/webroot/apache ) changed:[172.17.0.5]=>(item= /data/webroot/apache/logs ) changed:[172.17.0.5]=>(item= /data/webroot/apache/vhost ) TASK:[apache_install|InstallCheckScriptInRedhatClient]**************** changed:[172.17.0.5] TASK:[apache_install|CreateIndexHtmlToRedhatClient]******************* changed:[172.17.0.5] TASK:[apache_install|StartApacheServiceInRedhatClient]**************** changed:[172.17.0.5] TASK:[apache_install|AddBootStartApacheServiceInRedhatClient]******* changed:[172.17.0.5] TASK:[apache_install|DeleteApachecompressionSoftwareInRedhatClient]*** changed:[172.17.0.5] TASK:[smokeping_install|CreateSmokepingInstallDir]********************** changed:[172.17.0.5] TASK:[smokeping_install|CopySmokepingSoftwareToRedhatClient]********** changed:[172.17.0.5]=>(item=rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm) changed:[172.17.0.5]=>(item=smokeping. tar .gz) TASK:[smokeping_install|InstallEpelYumRepo]***************************** changed:[172.17.0.5] TASK:[smokeping_install|InstallRequireSoftware]************************** changed:[172.17.0.5] TASK:[smokeping_install|UncompressionSmokeping]************************** changed:[172.17.0.5] TASK:[smokeping_install|CopySmokepingConfigToRedhatClient]************ changed:[172.17.0.5] TASK:[smokeping_install|CopySmokepingHttpdConfigToRedhatClient]****** changed:[172.17.0.5] TASK:[smokeping_install|CheckBootStartInRedhatClient]***************** failed:[172.17.0.5]=>{ "changed" : true , "cmd" : "grep-c'/usr/local/smokeping/bin/smokeping--logfile=/var/log/smokeping.log2>&1&'/etc/rc.local" , "delta" : "0:00:00.007325" , "end" : "2014-08-1109:46:03.045378" , "item" : "" , "rc" :1, "start" : "2014-08-1109:46:03.038053" } stdout:0 ...ignoring TASK:[smokeping_install|AddBootStartInRedhatClient]******************* changed:[172.17.0.5] TASK:[smokeping_install|ModifySmokepingDirPermissionInRedhatClient]*** changed:[172.17.0.5] TASK:[smokeping_install|StartSmokepingServiceInRedhatClient]********** changed:[172.17.0.5] TASK:[smokeping_install|RestartApacheServiceInRedhatClient]*********** changed:[172.17.0.5] TASK:[smokeping_install|DeleteInstalledFile]***************************** changed:[172.17.0.5] PLAYRECAP******************************************************************** 172.17.0.5:ok=35changed=33unreachable=0failed=0 real 6m52.934s user 0m15.104s sys 0m1.632s 花费6分52秒完成,花费时间多的主要是yum安装基础库与使用epel安装smokeping依赖库。 9、安装后测试 查看smokeping 由于我做了安全认证,需要输入账户与密码,这个账户与密码就上文第3步的账户与密码 然后默认的其他网络监控(包括1、2、3线电信、移动、联通、铁通与教育网的节点监控已经做好) 如果有其他的监控需要自己来添加即可。 10、删除 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 09:46:08 #timeansible-playbooksmokeping_delete.yml--extra-vars"host=172.17.0.5user=root"-k SSHpassword: PLAY[172.17.0.5]************************************************************* GATHERINGFACTS*************************************************************** ok:[172.17.0.5] TASK:[apache_delete|StopHttpdServiceInRedHatClient]******************* changed:[172.17.0.5] TASK:[apache_delete|DeleteBootStartInRedHatClient]******************** changed:[172.17.0.5] TASK:[apache_delete|DeleteApacheDirInRedHatClient]******************** changed:[172.17.0.5] TASK:[apache_delete|DeleteApacheServiceScriptInRedHatClient]********* changed:[172.17.0.5] TASK:[apache_delete|DeleteApacheUser]************************************ changed:[172.17.0.5] TASK:[pcre_delete|DeletePcre]********************************************* changed:[172.17.0.5] TASK:[smokeping_delete|StopSmokepingServiceInRedhatClient]************ changed:[172.17.0.5] TASK:[smokeping_delete|DeleteSmokepingDirInRedhatClient]************** changed:[172.17.0.5] TASK:[smokeping_delete|DeleteBootStartInRedhatClient]***************** changed:[172.17.0.5] PLAYRECAP******************************************************************** 172.17.0.5:ok=10changed=9unreachable=0failed=0 real 0m9.326s user 0m2.914s sys 0m0.446s 11、删除后测试 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 09:59:45 #ssh172.17.0.5 root@172.17.0.5'spassword: root@56b70c31a07e:~ 09:59:57 #ifconfig eth0Linkencap:EthernetHWaddrA6:80:57:2D:D3:F1 inetaddr:172.17.0.5Bcast:0.0.0.0Mask:255.255.0.0 inet6addr:fe80::a480:57ff:fe2d:d3f1 /64 Scope:Link UPBROADCASTRUNNINGMTU:1500Metric:1 RXpackets:84520errors:0dropped:0overruns:0frame:0 TXpackets:86385errors:0dropped:0overruns:0carrier:0 collisions:0txqueuelen:1000 RXbytes:69075188(65.8MiB)TXbytes:9400662(8.9MiB) loLinkencap:LocalLoopback inetaddr:127.0.0.1Mask:255.0.0.0 inet6addr:::1 /128 Scope:Host UPLOOPBACKRUNNINGMTU:16436Metric:1 RXpackets:2errors:0dropped:0overruns:0frame:0 TXpackets:2errors:0dropped:0overruns:0carrier:0 collisions:0txqueuelen:0 RXbytes:168(168.0b)TXbytes:168(168.0b) root@56b70c31a07e:~ 09:59:59 #ps-ef|grephttpd root10171002010:00pts /0 00:00:00 grep httpd root@56b70c31a07e:~ 10:00:04 #ps-ef|grepsmokeping root10191002010:00pts /0 00:00:00 grep smokeping root@56b70c31a07e:~ 10:00:07 #ll/usr/local/ total40 drwxr-xr-x2rootroot4096Sep232011bin drwxr-xr-x2rootroot4096Sep232011etc drwxr-xr-x2rootroot4096Sep232011games drwxr-xr-x2rootroot4096Sep232011include drwxr-xr-x2rootroot4096Aug1109:40lib drwxr-xr-x2rootroot4096Sep232011lib64 drwxr-xr-x2rootroot4096Sep232011libexec drwxr-xr-x2rootroot4096Sep232011sbin drwxr-xr-x5rootroot4096Sep232011share drwxr-xr-x2rootroot4096Sep232011src root@56b70c31a07e:~ 10:00:11 #cat/etc/rc.local #!/bin/sh # #Thisscriptwillbeexecuted*after*alltheotherinitscripts. #Youcanputyourowninitializationstuffinhereifyoudon't #wanttodothefullSysVstyleinitstuff. touch /var/lock/subsys/local 如果大家想使用我的例子,可以从github里下载(地址是https://github.com/dl528888/ansible-examples/tree/master/smokeping_install),然后放到/etc/ansible目录里,下面是内容 本文转自 reinxu 51CTO博客,原文链接:http://blog.51cto.com/dl528888/1538444,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册