• 微信
    咨询
    微信在线咨询 服务时间:9:00-18:00
    纵横数据官方微信 使用微信扫一扫
    马上在线沟通
  • 业务
    咨询

    QQ在线咨询 服务时间:9:00-18:00

    选择下列产品马上在线沟通

    纵横售前-老古
    QQ:519082853 售前电话:18950029581
    纵横售前-江夏
    QQ:576791973 售前电话:19906048602
    纵横售前-小李
    QQ:3494196421 售前电话:19906048601
    纵横售前-小智
    QQ:2732502176 售前电话:17750597339
    纵横售前-燕子
    QQ:609863413 售前电话:17750597993
    纵横值班售后
    QQ:407474592 售后电话:18950029502
    纵横财务
    QQ:568149701 售后电话:18965139141

    售前咨询热线:

    400-188-6560

    业务姚经理:18950029581

  • 关注

    关于纵横数据 更多优惠活动等您来拿!
    纵横数据官方微信 扫一扫关注官方微信
  • 关闭
  • 顶部
  • 您所在的位置 : 首页 > 新闻公告 > 云服务器自动化部署报错如何修复?

    云服务器自动化部署报错如何修复?

    说句实在话,自动化部署这件事,做的时候信心满满,觉得从此以后可以告别手动登录服务器、敲命令、传文件的原始时代。可真用起来才发现,自动化部署报错的频率,比手动部署的时候还要高。只不过手动部署是把痛苦分散在每一次操作里,而自动化部署是把痛苦集中在了流水线变红的那一刻。

    这些年我在云服务器上跑自动化部署,见过的报错没有一百也有八十。有些报错看一眼就知道问题在哪,有些则要翻来覆去查好几个小时。这篇文章我想把这些经历过的报错和修复方法记录下来,不是那种网上抄来抄去的标准答案,而是我自己踩过的坑和填过的土。

    SSH连接失败:自动化部署的第一道坎

    自动化部署到云服务器,最常见的方式是通过SSH执行远程命令。所以第一个可能报错的地方就是SSH连接。

    我记得有一次,公司的部署流水线突然全线飘红,所有部署到某台云服务器的任务都失败了。报错信息是“ssh: connect to host 端口22:  Connection timed  out”。我第一反应是云服务器挂了,登录云厂商的控制台看了一眼,服务器状态是运行中,CPU和内存都正常。试着从我本地的电脑SSH连上去,也连不上。

    后来在云厂商的安全组日志里发现了端倪。那台云服务器的公网IP在凌晨的时候更换了,因为之前用的是弹性公网IP,被释放之后重新绑定了一个新的IP。而部署流水线里配置的还是旧IP地址,自然就连不上了。修复方法很简单,把部署配置里的IP地址更新成新的,流水线就恢复了。从那以后,我给所有需要自动化部署的云服务器都改成了用固定的内网IP或者域名来连接,不再依赖公网IP,这样即使IP变了也不会影响部署。

    还有一个案例是SSH密钥的问题。自动化部署通常使用SSH密钥认证,而不是密码。某天部署报错“Permission denied  (publickey)”,检查了云服务器上的authorized_keys文件,发现部署用的公钥不知道什么时候被删除了。可能是有人手动清理过这个文件,也可能是不小心覆盖了。重新把公钥添加进去,部署就正常了。

    这里有一个经验:自动化部署用的SSH密钥应该和人工运维用的密钥分开。专门为部署系统创建一个独立的密钥对,把公钥添加到云服务器的authorized_keys里。这样即使有人手动修改了运维密钥,也不会影响到自动化部署。

    执行权限不足:sudo的坑

    SSH连上去了,执行命令的时候报错说权限不足,这也是常见的问题。自动化部署经常需要执行一些需要管理员权限的操作,比如安装软件、修改系统配置、重启服务。

    我处理过一个案例。部署脚本里有一条命令是“systemctl restart  nginx”,这条命令在手动SSH登录后执行没有问题,因为登录用户有sudo权限。但是在自动化部署的SSH会话里执行同样的命令,却报错说没有权限。

    原因在于自动化部署系统执行命令的时候,通常不会分配一个完整的交互式shell,也不会自动加载sudo的配置。解决方案是在部署脚本里的每条需要权限的命令前面都加上sudo,并且在sudoers文件里配置部署用户不需要输入密码就能执行特定的命令。比如添加一行“deployer  ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart  nginx”,这样自动化部署执行这条命令的时候就不会被卡住了。

    还有一个容易被忽略的点是文件和目录的写权限。部署的时候经常需要往云服务器上的某些目录里写文件,比如上传配置文件、写入日志。如果部署用户对这些目录没有写权限,操作就会失败。我自己的习惯是在部署脚本里加上创建目录和设置权限的步骤,确保目标目录存在并且部署用户有足够的权限。

    环境变量缺失:同一个命令,不同的结果

    这个问题真的很让人抓狂。部署脚本在本地测试的时候一切正常,放到自动化部署系统里就报错。同样的脚本,同样的命令,结果却不一样。

    有一次我遇到一个Python项目的部署报错,说“command not found:  python3”。在云服务器上手动执行python3命令,明明是可以的。后来查了自动化部署系统的执行日志,发现它通过SSH执行命令的时候,使用的环境变量和手动登录时完全不一样。PATH环境变量里没有包含python3所在的目录/usr/local/bin。

    修复这个问题有两个办法。一是在部署脚本里显式设置PATH变量,把需要的目录都加进去。二是在执行Python命令的时候使用绝对路径,比如/usr/local/bin/python3而不是python3。我倾向于使用绝对路径,因为这样更可靠,不依赖于环境变量的配置。

    类似的问题还有LANG和LC_ALL这些语言环境变量。有些脚本的输出依赖这些变量,如果变量没设置,输出可能是空值或者乱码,导致后续的解析失败。在部署脚本开头加上“export.UTF-8”可以避免这类问题。

    端口冲突:新服务抢了旧服务的饭碗

    自动化部署的时候,通常会停止旧版本的容器或者服务,启动新版本。如果停止旧服务的步骤失败了,或者根本没有执行,新服务启动的时候就会遇到端口已经被占用的问题。

    有一个案例是这样的。部署脚本里写了“docker stop old-container”和“docker rm  old-container”,但是这两条命令放在了一个条件判断里,只有当旧容器存在的时候才执行。新部署的第一次没有问题,但第二次部署的时候,旧容器名字变了,条件判断没有命中,停止旧容器的步骤被跳过了。结果新容器尝试绑定同一个端口的时候,旧容器还在占用,启动失败。

    修复这个问题的方法是不管旧容器存不存在,都执行停止和删除的操作。即使容器不存在,docker  stop也会报错,但这个错误可以被忽略。更稳妥的做法是使用docker-compose或者容器编排工具,它们会处理好容器的替换过程,不会有端口冲突的问题。

    还有一个相关的问题是端口被其他非容器进程占用了。部署之前可以用netstat或者lsof检查一下目标端口是否已经被占用,如果是被其他必要的进程占用了,就需要修改部署配置,换一个端口。

    依赖服务未就绪:启动顺序的问题

    自动化部署一个应用,往往不是单独部署一个服务,而是一整套服务。比如数据库、缓存、消息队列、应用服务器等等。这些服务之间有依赖关系,如果依赖的服务没有就绪,依赖它的服务就会启动失败或者运行异常。

    我记得有一个案例,部署脚本里同时启动了数据库和应用服务器,两个命令几乎是同时执行的。数据库启动需要十几秒钟才能开始接受连接,而应用服务器启动后马上就尝试连接数据库,结果连接被拒绝,应用服务器就退出了。等到数据库就绪的时候,应用服务器已经挂了。

    解决这个问题需要在部署脚本里加入等待依赖服务就绪的机制。最简单的办法是用sleep命令硬等几秒钟,但这个办法不可靠,因为不同的环境启动速度不一样。更好的办法是用一个循环来检测依赖服务是否真的可用了,比如每隔一秒尝试连接一下数据库,连接成功了才继续往下走。用nc、telnet或者curl都可以做这种健康检查。

    配置文件覆盖错误:新配置没生效,旧配置被覆盖

    自动化部署通常会同时部署新的代码和新的配置文件。配置文件的管理是一个很容易出错的环节。

    有一个案例让我印象深刻。团队用Ansible做自动化部署,每次部署都会从模板生成配置文件,然后覆盖云服务器上的旧配置文件。有一次,开发人员在模板里写错了一个配置项的值,部署之后整个服务的行为就变了。更糟糕的是,之前的正确配置文件被覆盖了,想要回滚也找不到备份。

    从那以后,团队改了做法。部署的时候不直接覆盖配置文件,而是先备份旧配置文件,生成新配置文件之后和旧配置做对比,如果差异很大就暂停部署并发出告警。这个机制虽然增加了一些复杂度,但避免了因为配置文件错误导致的大面积故障。

    还有一个经验是将配置文件和代码分离。代码经常变,但配置文件相对稳定。不要把配置文件写在Docker镜像里或者打包在代码里,而是单独管理。部署的时候从配置中心或者对象存储里拉取配置文件,这样即使代码回滚了,配置还能保持正确。

    磁盘空间不足:不知不觉积累的问题

    自动化部署跑得多了,云服务器上的磁盘空间会被慢慢占满。旧版本的代码、旧的容器、旧的镜像、日志文件,这些东西如果不清理,迟早会出问题。

    有一次,一个自动化部署任务一直报错,说写文件失败。登录云服务器一看,磁盘使用率已经达到了百分之九十七。部署脚本要写一个锁文件到/tmp目录,因为空间不足写不进去,整个部署就失败了。

    修复这个问题需要清理磁盘空间。部署脚本里可以加入自动清理的步骤,比如删除多少天之前的日志、删除多少个版本之前的代码备份。还可以配置云服务器的监控告警,当磁盘使用率超过一定阈值的时候提前通知运维人员处理。

    对于Docker环境,定期执行“docker system  prune”可以清理未使用的镜像、容器和网络。对于日志文件,配置logrotate或者容器日志驱动的大小限制,可以防止日志无限增长。

    版本兼容性问题:新旧API不匹配

    自动化部署往往涉及多个组件协同工作。前端调用后端的API,后端调用数据库的存储过程,服务之间通过RPC通信。如果一个组件升级了,而另一个组件没有升级,就可能导致API不兼容的问题。

    我经历过一次比较严重的事故。后端服务升级到了新版本,修改了一个API接口的参数类型。前端服务还是旧版本,按照旧的参数格式调用后端API,结果后端接口报错了,整个功能不能用。自动化部署的时候没有做兼容性验证,前端和后端没有同步部署,导致了这个问题。

    事后复盘,团队增加了一个部署策略。部署之前先检查各个组件之间的版本兼容性,如果不兼容就阻止部署。部署的时候遵循一定的顺序,先部署依赖方,再部署被依赖方。对于API的修改,采用增量式变更,先增加新的API,保留旧的API一段时间,等所有调用方都升级了再移除旧的API。

    总结

    写到这里,我想说一个很多人都知道但很少做到的道理:自动化部署的报错,大多数不是新问题,而是老问题的重现。

    SSH连接失败不是新问题,是老问题重复出现。环境变量缺失不是新问题,是每次部署都要面对的麻烦。端口冲突不是新问题,是容器管理不规范的必然结果。配置文件覆盖错误不是新问题,是配置管理缺失的老毛病。

    修复自动化部署报错,本质上不是解决一个个孤立的技术问题,而是建立一套完善的流程和规范。SSH连接经常失败,那就放弃IP改用域名,或者写一个自动发现IP的脚本。环境变量总是缺失,那就把所有依赖都容器化,不再依赖宿主机环境。端口总是冲突,那就用容器编排工具统一管理端口分配。配置文件经常出错,那就引入配置管理工具,把配置版本化、可审计、可回滚。

    这些年跑自动化部署,我越来越觉得,报错本身不可怕,可怕的是每次报错都从头开始查,每次查完都不把经验沉淀下来。如果你能把每次遇到的报错和修复方法记录下来,形成一份排障手册,那么下次遇到同样的问题,几分钟就能解决。



    最新推荐


    微信公众帐号
    关注我们的微信