
环境差异是最常见的坑源
本地开发环境一般是开发者自己整的,用什么版本、开什么权限,都是顺着自己的习惯来,很少会碰到限制。但云服务器的环境是默认的标准环境,有很多预设的规则,这些规则刚好就是坑的来源。
第一个说权限。很多人在本地开发的时候,习惯用管理员账号登录操作系统,所有文件读写都是放开的,所以不会注意权限问题。放到云服务器上,大部分运维规范都是用普通用户运行服务,避免过高权限带来的安全风险。很多人把程序文件上传之后,直接用自己登录的账号改了权限,忘了给运行服务的用户开放读写权限。比如我学弟那个问题,就是他把程序日志目录放在了自己的用户目录下,运行服务用的是系统默认的服务用户,根本没有进入目录的权限,程序一启动就要写日志,直接报错退出,报错信息又只说"无文件或目录",他还以为是程序路径配错了,绕了大弯。还有一种情况,就是很多人图方便,直接给整个程序目录开了777权限,看起来能跑了,但是留下了很大的安全隐患,这个后面再说。
第二个说端口访问。很多人部署完程序,在云服务器本地用localhost也能访问,就是从外部网络访问不通,这时候大部分人都会去改程序的监听配置,改半天没用。其实大部分情况是云服务器默认的网络访问控制规则没开对应端口。云服务器默认会只开放几个常用端口,比如22、80、443,其他端口都是默认拒绝外部访问的。很多开发者部署新服务,用了自定义的端口,比如8080、9000之类的,忘了在访问控制规则里添加允许规则,自然就访问不通。这个问题看起来简单,但是第一次碰到的人,至少要折腾一两个小时才能想到这个方向。我自己第一次做云服务器软件部署的时候,就在这个问题上卡了快三个小时,现在想起来还有印象。
第三个说依赖版本。这个问题比前两个更隐蔽,排查难度也更高。很多人开发的时候,要么是在本地编译代码生成二进制文件,要么是直接把本地安装好的依赖打包上传。本地的开发环境一般会更新到比较新的系统版本,依赖库版本也比较新,但是云服务器默认的系统镜像,一般会用比较稳定的旧版本,依赖库版本也低。最常见的就是编译后的C或者C++程序,依赖高版本的glibc库,上传到云服务器上运行,直接报"无法找到动态链接库"的错误。这个报错很多新手看不懂,就算看懂了,也不敢随便升级云服务器上的glibc,因为升级glibc很容易导致整个系统出问题。还有一种情况,就是用Python写的程序,很多人本地开发用的是较新版本,云服务器默认装的是旧版本,很多新的语法特性和第三方库不支持,直接跑就报错,这种情况其实也属于依赖版本的问题,解决方法要么是在云服务器上装对应版本,要么就是用虚拟环境把依赖打包,不要直接用系统默认的版本,这个也是很多Python开发者容易碰到的坑。我前前后后碰到过三次这个问题,每次都要花一个多小时调整参数。从经验来看,避免这个问题最好的方法,要么是在和云服务器相同系统版本的环境里编译程序,要么是用静态编译把依赖打包进二进制文件,尽量不要直接在本地编译完就上传。
不同场景的部署选择思路
很多刚接触的开发者会问,云服务器软件部署到底用什么方法才对?其实没有绝对正确的方法,只有适合场景的选择,很多人喜欢追新,上来就用最复杂的架构,反而给自己添麻烦。
比如如果你只是部署一个个人用的小工具,或者访问量很低的个人项目,其实直接裸装部署就够了。就是直接在系统上装依赖、放程序、配自启动,步骤少,结构简单,出了问题随便就能找到,不需要额外占用资源去跑编排或者管理工具。很多人觉得裸装不专业,其实对小项目来说,简单就是最大的优势,我自己跑的几个小工具,都是裸装部署,跑了三四年都没出过问题,维护成本很低。
如果你的项目是多个服务配合,或者需要在同一台云服务器上跑多个不同的项目,那用容器隔离就是比较好的选择。容器可以把每个项目的依赖和环境隔离开,不会出现A项目要用到版本a的依赖,B项目要用到版本b的依赖,两个起冲突的问题。而且容器部署迁移也比较方便,换服务器的时候,直接导出镜像就能用,比重新装环境省很多事。但是容器也有缺点,就是多了一层抽象,出问题的时候排查难度比裸装高,对新手不太友好,所以如果只有一个单服务项目,其实没必要硬上容器。
还有很多新手,明明只有一个不到一千访问量的小项目,上来就搭集群,其实完全没必要。集群本身就要占用不少的CPU和内存资源,大部分云服务器的配置不算高,一半以上的资源都被集群管理组件吃掉了,留给业务的资源反而不够,而且出了问题排查起来,要懂的东西比单部署多很多,很多人搭完集群,根本没能力维护,反而增加了很多不必要的麻烦。从实际使用来看,十个开发者里,有九个的项目,根本用不到集群,单节点或者简单的多容器部署就足够了。
容易被忽略的细节问题
云服务器软件部署很多时候,不是部署完能跑就算完成了,很多细节如果没处理好,后面出问题的概率很高,维护成本也会高很多。
第一个就是开机自启动。很多开发者部署完,测试能跑,就觉得完事了,结果服务器因为维护或者故障重启之后,服务就没起来,自己还不知道,等有人访问发现不能用了,才知道出问题。这个问题完全是可以避免的,就是部署完一定要配置开机自启动。现在大部分主流的Linux发行版都用systemd管理服务,写一个简单的配置文件,放到指定目录,启用就可以了,花不了十分钟,但是能避免大麻烦。很多人嫌麻烦,或者抄了不对版本的自启动配置,自己又不会检查,结果就是重启就掉服务,这个我见过太多次了。
第二个就是日志管理。很多人部署程序的时候,用默认的配置,日志直接输出到程序目录里,也不做轮转,也不清理,跑了几个月之后,日志文件能占掉好几十G甚至上百G的磁盘空间,最后磁盘被占满,整个服务都宕掉了。很多小项目的开发者,根本不会去看磁盘使用率,等磁盘满了服务宕机,还不知道是什么原因,还要对着报错搜半天。其实只要部署的时候,给程序配好日志轮转,定期清理旧日志,或者把日志输出到专门的日志目录,定时清理,就能避免这个问题,花不了多少时间,但是能解决一个很大的隐患。
第三个就是运行用户权限。很多人图方便,直接用root用户跑服务,看起来省事,不用改权限,不用配规则,但是安全隐患很大。如果程序本身有漏洞,被人拿到了权限,那对方直接就拿到了整个服务器的最高权限,整个服务器都能被控制。正确的做法是,给每个服务单独创建一个普通用户,只给这个用户开放它需要的目录权限,其他地方都不让访问,哪怕程序出了问题,影响范围也能控制住,不会连累整个服务器。这个操作其实也很简单,就一条命令创建用户,改一下文件权限,花不了两分钟,但是能极大提高安全性,很多人就是嫌麻烦不做,真出问题就后悔了。
第四个就是资源规划。很多人在同一台云服务器上跑很多服务,随便堆在一起,不做资源限制,结果高峰期某个服务占了太多资源,把其他服务都挤垮了。最常见的就是把应用服务和数据库部署在同一台配置不高的服务器上,数据库很吃内存,高峰期查询多的时候,会占掉大部分内存,应用服务就会因为内存不足被系统杀掉,导致整个服务不可用。还有的人,把很多没在用的服务都留在服务器上,开机就启动,占着资源不用,本来够用的资源,被这些闲置服务占了,导致正常业务资源不够,所以部署完之后,也要把不用的服务关掉,释放资源,这个也是很简单的操作,但是很多人忘了做。我之前帮一个开发者调过这个问题,他的网站高峰期总是随机卡顿甚至打不开,查了好长时间,最后发现就是数据库占了几乎所有内存,应用被系统杀掉了,把应用和数据库分开之后,问题就解决了。如果你的服务器资源不够,不要把两个都吃资源的服务放在一起,分开部署反而能获得更好的稳定性,这个是很多人一开始不会注意到的点。
还有一个容易忽略的点,就是部署完之后一定要做基本的观察测试,很多人部署完,测一次能访问就不管了,其实可以等一天半天,看看服务会不会自己异常退出,日志有没有输出奇怪的错误,磁盘使用率有没有不正常增长,这些简单的检查,能提前发现很多问题,避免后面出了问题手忙脚乱。我自己现在部署完服务,都会留一天观察,碰到过好几次潜在的问题,都是在观察阶段发现的,省了后面很多事。
我自己做了快十年的运维,碰过各种各样的云服务器软件部署问题,大部分问题都不是什么复杂的技术难题,都是对环境不熟悉,或者细节没注意到导致的。很多新手总想着一步到位,用最先进的架构,其实对大部分普通开发者来说,云服务器软件部署的核心,就是稳定、好维护,能满足当前的需求就够了,没必要为了没用的功能增加复杂度。简单的场景用简单的方法,复杂的场景再用对应的方案,出问题的时候排查起来也容易很多。