abbshr.github.io icon indicating copy to clipboard operation
abbshr.github.io copied to clipboard

基于 Ansible 的项目自动化部署管理

Open abbshr opened this issue 7 years ago • 4 comments

正式走入工作岗位一个月, 接着实习的工作内容继续, 还是承担团队的 Develop & Operation & Research 任务, 恰好我个人也比较喜欢什么都接触一些. 目前在应用开发部门设计了一套包括日志处理, 监控报警, 持续集成, 版本发布与回滚, 自动化管理在内的基础平台的架构, 当然这套体系肯定还尚未成熟, 这里经过老大的许可先把项目的部署与回滚架构发上来, 做个记录也方便日后 tuning.

为什么是 ansible?

8月初刚过来时我压根没听过 ansible 只知道 puppet, 正好那时团队准备做项目自动化管理相关的工作, 也就把自动化配置管理工具搜了一遍, 列了个优缺点比较表, 发现 ansible 完胜. 至于为什么, 我就一句话: "没有 agent, 没有 server, 用 ssh 批量管理主机". 玩过 puppet 和 chef 的同学应该秒懂了.

另外, ansible 也是 Devops 工具集中蹿升速度最快的配置管理工具之一. 截止至本年度7月份, Devops 使用度&接受度排行榜中前四位分别是 Puppet, Chef, Docker, Ansible. 而后者预计很快将超越前两个工具.

以其配置上的简洁, 使用上的顺手, 功能上的强大, 好了继续下一节.

自动化管理

ansible 的细节这里略过, 单说这套架构, 目前包括如下功能:

  • 开发者机器上集中管理所有远程主机
  • 对主机选择性管理
  • 要求规范化的项目基本目录结构
  • 线上机发布版本与回滚
  • 代理机模拟成线上机便于测试
  • 管理线上机服务的启动/停止/重启

接下来从整套机制的架构到细节逐步介绍.

规范化项目目录结构

在未引入 Docker 情况下, 为了做到项目(应用程序, 配置信息, 状态数据, 依赖工具链等)之间的隔离, 这里定义了一种项目的目录规范.

线上项目一般都是位于某个目录(比如/opt/app)下面, 不同的子目录用以区分不同的项目, 每个项目目录的基本结构如下:

/uopt/app/example-project
├── current # 指向项目最新发布的版本的软链接 ** 重要 **
├── releases # 包含所有的历史发布版本(以及当前版本) ** 重要 **
├── tmp # rsync 临时目录
├── shared # 发布工具使用的目录 ** 重要 **
├── log # 日志存放
├── run # 运行时数据
├── util # 外部工具
└── node_modules # Node.js Package 形式的外部工具, 如 pm2 ** 重要 **

每个项目可以在这个目录结构上进行扩展, 比如添加其他目录, 但上述标注 ** 重要 ** 的目录不建议删除或修改.

其中 current 目录指向的就是该项目的原代码, 即开发阶段所熟悉的目录结构.

Ansible Playbook 目录结构

在使用 ansible 时, current 所指代的代码目录中, 应包含 ansible playbook 的配置目录, 举个例子:

/opt/app/my_app/current/deploy ❯❯❯ tree -a -L 4
# 以下结构参考 ansible 官方给出的最佳实践
.
├── group_vars
│   ├── all
│   ├── nodes
│   │   └── vars
│   └── proxy
│       └── vars
├── host_vars # 测试环境下只需增加对应主机的变量文件即可覆盖预设变量
│   ├── 10.0.5.130
│   ├── 192.168.1.17
│   │   ├── vars
│   │   └── vault # 加密变量
│   ├── 192.168.5.68
│   │   ├── vars
│   │   └── vault
│   └── 192.168.5.69
│       ├── vars
│       └── vault
├── production # 线上环境 inventory
├── roles
│   ├── config
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       ├── config.json.j2
│   │       ├── logstash.conf.j2
│   │       └── macros.j2
│   ├── node
│   │   └── tasks
│   │       └── main.yml
│   ├── proxy
│   │   └── tasks
│   │       └── main.yml
│   ├── schedule
│   │   └── tasks
│   │       └── main.yml
│   └── worker
│       └── tasks
│           └── main.yml
├── rollback.yml
├── .rsync_ignore # 从代理机到线上机同步时需要忽略的文件/目录列表
├── site.yml
└── staging # 测试环境 inventory

其余文件的解释和作用见 ansible 官方文档.

发布和回滚

在执行发布回滚操作上, 我在开源 roles ansistrano 的基础上做了不少改动以适配我们的应用场景, 并以新的 roles 发布在 ansible galaxy 上 (https://github.com/upyun-dev/ansistrano-deploy | https://github.com/upyun-dev/ansistrano-rollback), 具体的代码实现这里就不说了.

来看看怎么用吧:

# 安装依赖 roles
$ ansible-galaxy install upyun-dev.ansistrano-deploy
$ ansible-galaxy install upyun-dev.ansistrano-rollback
# 在所有机器上执行项目部署, 发布版本并重启服务任务
ansible-playbook -i <staging | production> site.yml

# 在所有机器上执行项目回滚并重启服务任务
ansible-playbook -i <staging | production> rollback.yml

但是同构化 proxy 和 nodes 往往不是我们想要的结果, 多数情况下我们可能只把 proxy 作为纯粹的代理机使用, 并不需要模拟线上机器执行部署/回滚. 这种情况可以通过指定 --tag 或者 --limit 来选择要执行的任务:

# tag: proxy, nodes, config

# 仅在 proxy 上执行部署发布
ansible-playbook -i hosts site.yml --tag="proxy"
# 仅在 proxy 上执行回滚
ansible-playbook -i hosts rollback.yml --tag="proxy"

# 仅在 nodes 上执行部署发布以及服务启动
ansible-playbook -i hosts site.yml --tag="nodes"

# 只在属于proxy group的主机上执行tasks
ansible-playbook -i <staging | production> site.yml --limit=proxy

本地测试/开发

  1. 在本地测试与开发上, ansible playbook 配置目录中的一些变量可能需要稍做修改, 因此你可以创建一个host_var文件,比如:

    # 文件: host_vars/localhost
    
    # 项目名
    app_name: my_app_test
    # 项目的父级目录
    dest_prefix: "/tmp"
    # 分支/版本
    version:  test
    # 代理机的地址
    sync_host: [email protected]
    
  2. 此外, inventory 内容也可能根据测试主机的地址及ssh的配置不同而需要修改.

  3. 确保以下正常

    • proxy和node上启动 sshd (确保nodes 可以通过 ssh 访问)
    • 保证本地 shell 没有多余 ouput (确保以 ssh 方式同步数据时没有误差产生)
    • 安装外部 roles

注意当你选择使用 rsync 的 ssh 模式同步数据时, 一定要保证代理机上的 登录 shell 没有多余的输出(当时被这个坑了...).

项目启动脚本

由于引入规范化项目结构, 导致了冗余目录的产生以及项目状态数据路径(如 pm2)的变化. 为了屏蔽本地以及线上部署差异, 透明化本地开发, 这里对项目的启动脚本做了修改, 使其根据不同的开发环境判断如何启动项目, 如:

PRJ_DIR=$(cd $APP_ROOT_DIR/../.. && pwd)
APP_DIR_PARENT=$(cd $APP_ROOT_DIR/.. && pwd)

if [[ -L $APP_DIR_PARENT/current ]]; then
  PRJ_DIR=$APP_DIR_PARENT
elif [[ ! -d $PRJ_DIR/releases || ! -L $PRJ_DIR/current ]]; then
  PRJ_DIR=${APP_ROOT_DIR}
fi

PM2_BIN="$PRJ_DIR/node_modules/pm2/bin/pm2" # 指定新的pm2可执行文件路径

最终结果体现为: 如果并没有遵循如上所述的规范化目录结构, 那么将之后的部署作为本地开发环境下的部署. 否则作为线上/测试环境下的部署.

[WIP] - 执行流程

image

abbshr avatar Sep 04 '16 15:09 abbshr

小哥,github怎么404了

zhuima avatar May 17 '17 03:05 zhuima

@zhuima oh, my fault.. 我把项目名字改了..

https://github.com/upyun-dev/deploy
https://github.com/upyun-dev/rollback

abbshr avatar May 17 '17 03:05 abbshr

谢谢,我去look look

zhuima avatar May 18 '17 01:05 zhuima

666

cuidan avatar Aug 31 '17 03:08 cuidan