## 安全的代码部署
![](http://cdn.aipin100.cn/18-4-10/98820547.jpg)
安全,优雅的线上代码部署。
非常非常重要却又极其容易被忽视的问题:**原子发布**
* * * * *
#### 如果不能完全避免错误,那就努力把出错的概率降到最低。
~~~
`switch` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT '应用开关:1-开,0-关。应用升级、安装、维护时需要暂时关闭,使其不能访问,否则会出现意外(即使这样也还是不能完全避免极端时候的并发问题,不过还是将故障降到最低概率了)',
来自家校 s_app表
~~~
* * * * *
### 安全的实现服务部署的思考
上面这个问题可引申到怎么实现安全,平滑的代码部署的问题上去了。
要想实现安全的代码迁移、热替换、部署,光从应用层,用文件锁,做开关的形式去做还是不行的,必须从内核的角度去做。
比如实现php服务的线上运行环境的代码部署,如果直接替换文件可能会出现不可预料的问题,以及错误,所以做一个开关,暂停网站,但是已有的代码还在内存中跑怎么办,那就做一个程序生命检测的机制,只要程序有活着的,那就锁文件,知道文件没锁了就说明整个系统停了,但是这样还不够,很难保证所有程序都遵循这个约定,活着时都去摸一摸这个问题。
那么就要使出最后的绝招了,从内核去控制程序的生命,首先使用传统的方式暂停所有程序,关闭队列服务,关闭常驻程序,关闭访问开关,此时也还不能够确定是不是都停止了,那我们就等(正常的服务正在运行的可能此时还在内存中运行着呢),只要常驻和队列服务关闭了,那么内核一会也会停止的,只要从php内核那里检测到彻底没有生命活动了,就表示当前没有任何活动程序了,那么此时就说明绝对安全了,此时就可以放心的开始部署工作了。
对于线上的服务部署,针对不同的平台。业界肯定早已经有成熟的技术方案了,以后有时间在研究。
>[danger] 如果直接采用替换文件的方式,那么是很危险的,可能会出现难以预料的错误,这在生产环境中是致命的,因为文件的替换可能不是原子性的同时都能够替换(这是由PHP的运行机制决定的),这样的话,如果旧文件引入新文件的,那么逻辑可能就会出现难以预料的问题。所以安全的替换必须是原子性的,即要么同时都替换,要么都不替换,不能出现交叉的情况。
> 这种部署方式需要停服,对于一个线上的使用量很大的生产系统来说,停服代价太大了。
>
> 应该使用平稳的部署方案,比如A服务是旧服务,最新的提交时B服务,那么开启一个B服务,让新的请求全部使用B服务,这样就平滑过渡过来了,对原有的A服务完全没有影响,两者可以同时运行。等A服务逐渐没有人访问时,再停止就可以。
* * * * *
#### 热迁移
热迁移,可以想象的例子,蜂巢迁移,想把A蜂巢的蜜蜂迁移到B蜂巢,但是当前A蜂巢已经在使用中了,要使得迁移过程中不影响采蜜的蜜蜂只能这样,做个和A一样性质的蜂巢B,将B放在A旁边,关闭A的入口,只保留出口,这样蜜蜂会慢慢到B蜂巢中去,而此时A蜂巢中还有工作的蜜蜂,所以不能直接关闭,但是我们关闭入口只保留出口,这样A里面的蜜蜂会逐渐减少,一段时间后完全没有蜜蜂了,此时我们已经完成了热迁移了,蜜蜂都转移到B中了,现在可以放心的停用A了。
----
[SSH隧道技术----端口转发,socket代理 - 登高行远 - 博客园](https://www.cnblogs.com/fbwfbi/p/3702896.html)
[字节和腾讯都在使用,DevOps工具Zadig究竟有何魔力 - 知乎](https://zhuanlan.zhihu.com/p/381238540)
[Zadig | Cloud native CI/CD for large scale software development. 面向大规模微服务软件开发的云原生 CI/CD | KodeRover](https://www.koderover.com/)
[又一篇 Deployer 的使用攻略 | Laravel China 社区](https://learnku.com/articles/13242/another-introduction-to-the-use-of-deployer)
[Deployer让部署变得更加的简单_The Hard Way To Code-CSDN博客_deployer](https://blog.csdn.net/seven_2016/article/details/83628778)
[deployer 实战经验分享 | Laravel China 社区](https://learnku.com/articles/10985/deployer-real-experience-sharing)
[又一篇 Deployer 的使用攻略 - overtrue](https://overtrue.me/articles/2018/06/deployer-guide.html)
[PHP如何正确发布 PHP 代码 - 李培刚的博客 - CSDN博客](https://blog.csdn.net/lipeigang1109/article/details/79495324)
> 先通过`ln`创建一个临时的软链接,再通过`mv`实现原子操作,此时如果使用`strace`监控,会发现`mv`的`「T」`选项实际上仅仅执行了一个`rename`操作,所以是原子的。
[如何正确发布PHP代码 | 火丁笔记](https://blog.huoding.com/2016/05/27/515#comment-398297)
[php + Laravel 实现部署自动化](http://mp.weixin.qq.com/s/qZGVdNtCJOycR_sye9az6w)
[Facebook 如何实现大规模快速发布?](http://mp.weixin.qq.com/s/0GHf5_zfsqPuFYa4k-Um0A)
[Facebook 是如何做大规模代码部署的?](http://mp.weixin.qq.com/s/BbfRQ3tHQqmAIzoQ7Sj_hA)
[容器技术演化史](http://mp.weixin.qq.com/s/wwwB8OHQmbwqmKKE8Lyv-w)
[少年,是时候换种更优雅的方式部署你的php代码了 - 码王信息 - 博客园](https://www.cnblogs.com/mawang/p/6749396.html)
[一般如何将开发测试服务器上的代码部署到生产环境? - 知乎](https://www.zhihu.com/question/33411671)
[利用WebHook实现PHP自动部署Git代码-萧晔离](https://m.aoh.cc/149.html)
> `git pull` 能实现原子性替换吗?
[php - 使用git在服务器上更新代码,经常导致权限变了,日志没有写进去,怎么解决? - SegmentFault 思否](https://segmentfault.com/q/1010000019110432)
[Deployer — Deployment Tool for PHP](https://deployer.org/)
[在 2016 年做 PHP 开发是一种什么样的体验?(一) - SegmentFault 思否](https://segmentfault.com/p/1210000007170414?from=timeline&isappinstalled=1)
[简单轻松部署你的项目 - Deployer - PHP 那些事 - SegmentFault 思否](https://segmentfault.com/a/1190000009000278)
[使用php部署工具deployer实现自动部署 - 二次元の技术宅](https://www.maoxuner.cn/2017/05/04/php-deployer.html)
[如何正确发布PHP代码 - CSDN博客](https://blog.csdn.net/ivan820819/article/details/51719230)
>[danger] **一个正确实现的发布系统至少应该支持原子发布。如果说每一个版本都表示一个独立的状态的话,那么在发布期间,任何一次请求只能在单一状态下被执行。如此称之为支持原子发布**;反之如果在发布期间,一次请求跨越不同的状态,那么就不能称之为原子发布。我们不妨举个例子来说明一下:**假设一次请求需要 include 两个 PHP 文件,分别是 a.php 和 b.php,当 include a.php 完成后,发布代码,接着 include b.php,如果处理不当的话,那么就可能会导致旧版本的 a.php 和新版本的 b.php 同时存在于同一个请求之中,换句话说就是没有实现原子发布。** **(重要:非常非常重要却又极其容易被忽视的问题)**
[PHP执行系统外部命令函数:exec()、passthru()、system()、shell_exec() - gaohj - 博客园](http://www.cnblogs.com/gaohj/p/3267692.html)
* * * * *
### 从nginx看 线上环境的热部署和热更新替换
[【开源组件】深入浅出Nginx](https://mp.weixin.qq.com/s/31DGDYpDHcYg-4qM4OczxA)
~~~
思考1:Nginx如何做到热部署?
所谓热部署,就是配置文件nginx.conf修改后,不需要stop Nginx,不需要中断请求,就能让配置文件生效!(nginx -s reload 重新加载/nginx -t检查配置/nginx -s stop)
通过上文我们已经知道worker进程负责处理具体的请求,那么如果想达到热部署的效果,可以想象:
方案一:修改配置文件nginx.conf后,主进程master负责推送给worker进程更新配置信息,worker进程收到信息后,更新进程内部的线程信息。
方案二:修改配置文件nginx.conf后,重新生成新的worker进程,当然会以新的配置进行处理,而且新的请求都必须交给新的worker进程,至于老worker进程,等把那些以前的请求处理完毕,kill掉即可。
Nginx采用的就是方案二来达到热部署的!
~~~
[深入NGINX:nginx高性能的实现原理 - panda521 - 博客园](https://www.cnblogs.com/chenjfblog/p/8715580.html)
> …… **通知旧的工作进程安静地退出。** 这些旧进程不会再接受新的连接了。只要它们处理的HTTP请求结束了,它们就会干净地关闭连接。一旦所有的连接都被关闭,工作进程也就退出了。
> (优雅的退出,优雅的在不中断服务的情况下更新配置,php-fpm也是这样的哦,修改php.ini后,只需要 `service php-fpm reload` 也不需要重启进程,直接就 再装 应用新配置和加载新扩展了,原理也同理,新启动的cli就更不用说了,但是注意已经运行的进程,已经在内存中的进程则无法获得新配置)
*****
[代码部署规范 - 个人文章 - SegmentFault 思否](https://segmentfault.com/a/1190000012042692#articleHeader1)
[为啥总在凌晨上线,如何无损发布](https://mp.weixin.qq.com/s/OirX0Q1ilXKqlQL8sI2utQ)
* * * * *
### 无服务器环境的安全部署
没有服务器的网站怎么安全部署呢(只能FTP上传文件的那种虚拟主机),网站需要增加一个站点开关,能够关闭入口(用户访问时就显示网站关闭升级中,多多返利程序升级时就是这样的,需要在后台关闭站点),并保证没有后台程序在运行时(开关关闭后等一会,检查没有队列等后台任务),再上传新文件覆盖就可以,等新文件上传完毕后在打开站点开关,这样才能确保安全。否则直接上传文件覆盖就不满足原子发布。
* * * * *
### 如何制作升级补丁包
老版本升级时,怎么制作老版本的升级包呢,拉出两个版本的commit,difference出所有变化/新增/删除文件,建立更新文件包(变化文件直接替换,新增文件直接新增),然后还要生成diff文件(处理旧文件的删除)。这就好了好?但这只是文件上的更新,如果涉及到数据库上的更新,那么需要一个update.sql作为一个数据库的升级文件。
所以升级补丁包至少包含:`更新文件包`,`update.diff`,`update.sql`
程序文件的升级,数据库的升级。完成了这两部分的处理,就可以在线拉取升级补丁包直接升级了。**不过升级时还是要以安全的原子升级为前提。**
>[danger] 有表结构变化的升级部署,升级时必须增加开关,停止服务才行。
* * * * *
原子发布,前端也有这个问题。
[大公司里怎样开发和部署前端代码? - 知乎](https://www.zhihu.com/question/20790576/answer/32602154)
[45分钟搞垮一家上市公司,只因一次失败的部署?](https://mp.weixin.qq.com/s/ylMi_ShSnmbE1DRJcGLiMg)
* * * * *
last update:2018-6-2 19:33:09
- 开始
- 公益
- 更好的使用看云
- 推荐书单
- 优秀资源整理
- 技术文章写作规范
- SublimeText - 编码利器
- PSR-0/PSR-4命名标准
- php的多进程实验分析
- 高级PHP
- 进程
- 信号
- 事件
- IO模型
- 同步、异步
- socket
- Swoole
- PHP扩展
- Composer
- easyswoole
- php多线程
- 守护程序
- 文件锁
- s-socket
- aphp
- 队列&并发
- 队列
- 讲个故事
- 如何最大效率的问题
- 访问式的web服务(一)
- 访问式的web服务(二)
- 请求
- 浏览器访问阻塞问题
- Swoole
- 你必须理解的计算机核心概念 - 码农翻身
- CPU阿甘 - 码农翻身
- 异步通知,那我要怎么通知你啊?
- 实时操作系统
- 深入实时 Linux
- Redis 实现队列
- redis与队列
- 定时-时钟-阻塞
- 计算机的生命
- 多进程/多线程
- 进程通信
- 拜占庭将军问题深入探讨
- JAVA CAS原理深度分析
- 队列的思考
- 走进并发的世界
- 锁
- 事务笔记
- 并发问题带来的后果
- 为什么说乐观锁是安全的
- 内存锁与内存事务 - 刘小兵2014
- 加锁还是不加锁,这是一个问题 - 码农翻身
- 编程世界的那把锁 - 码农翻身
- 如何保证万无一失
- 传统事务与柔性事务
- 大白话搞懂什么是同步/异步/阻塞/非阻塞
- redis实现锁
- 浅谈mysql事务
- PHP异常
- php错误
- 文件加载
- 路由与伪静态
- URL模式之分析
- 字符串处理
- 正则表达式
- 数组合并与+
- 文件上传
- 常用验证与过滤
- 记录
- 趣图
- foreach需要注意的问题
- Discuz!笔记
- 程序设计思维
- 抽象与具体
- 配置
- 关于如何学习的思考
- 编程思维
- 谈编程
- 如何安全的修改对象
- 临时
- 临时笔记
- 透过问题看本质
- 程序后门
- 边界检查
- session
- 安全
- 王垠
- 第三方数据接口
- 验证码问题
- 还是少不了虚拟机
- 程序员如何谈恋爱
- 程序员为什么要一直改BUG,为什么不能一次性把代码写好?
- 碎碎念
- 算法
- 实用代码
- 相对私密与绝对私密
- 学习目标
- 随记
- 编程小知识
- foo
- 落盘
- URL编码的思考
- 字符编码
- Elasticsearch
- TCP-IP协议
- 碎碎念2
- Grafana
- EFK、ELK
- RPC
- 依赖注入
- 开发笔记
- 经纬度格式转换
- php时区问题
- 解决本地开发时调用远程AIP跨域问题
- 后期静态绑定
- 谈tp的跳转提示页面
- 无限分类问题
- 生成微缩图
- MVC名词
- MVC架构
- 也许模块不是唯一的答案
- 哈希算法
- 开发后台
- 软件设计架构
- mysql表字段设计
- 上传表如何设计
- 二开心得
- awesomes-tables
- 安全的代码部署
- 微信开发笔记
- 账户授权相关
- 小程序获取是否关注其公众号
- 支付相关
- 提交订单
- 微信支付笔记
- 支付接口笔记
- 支付中心开发
- 下单与支付
- 支付流程设计
- 订单与支付设计
- 敏感操作验证
- 排序设计
- 代码的运行环境
- 搜索关键字的显示处理
- 接口异步更新ip信息
- 图片处理
- 项目搭建
- 阅读文档的新方式
- mysql_insert_id并发问题思考
- 行锁注意事项
- 细节注意
- 如何处理用户的输入
- 不可见的字符
- 抽奖
- 时间处理
- 应用开发实战
- python 学习记录
- Scrapy 教程
- Playwright 教程
- stealth.min.js
- Selenium 教程
- requests 教程
- pyautogui 教程
- Flask 教程
- PyInstaller 教程
- 蜘蛛
- python 文档相似度验证
- thinkphp5.0数据库与模型的研究
- workerman进程管理
- workerman网络分析
- java学习记录
- docker
- 笔记
- kubernetes
- Kubernetes
- PaddlePaddle
- composer
- oneinstack
- 人工智能 AI
- 京东
- pc_detailpage_wareBusiness
- doc
- 电商网站设计
- iwebshop
- 商品规格分析
- 商品属性分析
- tpshop
- 商品规格分析
- 商品属性分析
- 电商表设计
- 设计记录
- 优惠券
- 生成唯一订单号
- 购物车技术
- 分类与类型
- 微信登录与绑定
- 京东到家库存系统架构设计
- crmeb
- 命名规范
- Nginx https配置
- 关于人工智能
- 从人的思考方式到二叉树
- 架构
- 今日有感
- 文章保存
- 安全背后: 浏览器是如何校验证书的
- 避不开的分布式事务
- devops自动化运维、部署、测试的最后一公里 —— ApiFox 云时代的接口管理工具
- 找到自己今生要做的事
- 自动化生活
- 开源与浆果
- Apifox: API 接口自动化测试指南