Travis CI 自动化部署

前言

现在项目增加新功能后,需要先推送到 GitHub 上提 PR,再等 CI 运行项目单元测试通过后再合并到主分支,最后还需要登上服务器进行部署,感觉有点麻烦呢,就想试试自动化部署。

大致流程就是当项目主分支有新的提交记录后,Travis CI 会运行对应的deploy配置项,这个配置只需要通过ssh运行服务器上一个脚本就可以了。

创建 travis 用户

先为 CI 部署时ssh登录服务器新增一个专用用户,这样简单的限制一下用户权限,避免直接使用管理员。最开始我还想过去限制登录这个用户后,把这个用户限制到一个目录里,但是后来使用Chroot Jail做了尝试,虽然真的限制了用户访问的目录,但是很多基础操作和命令都被受限或缺失,还需要去配置,并不是想要的效果,如果想了解一下的话可以参考Restrict SSH User Access to Certain Directory Using Chrooted Jail这个文章去配置。

  1. 新增一个travis用户用来部署登录

    useradd travis
    
  2. 为这个用户设置一个登录密码

    passwd travis
    

加密 ssh 私钥文件

因为部署时需要ssh连接到服务器运行脚本,所以我们需要先使用 Travis CLI 命令行工具对私钥进行加密,之后 Travis 自动化部署时只需要这个加密后的密钥,这样才能确保服务器安全。

  1. 使用gem安装 Travis CLI

    sudo gem install travis
    

    如果提示没有gem就自己安装下哈。

  2. 先切换到项目的目录下,再登录 Travis

    travis login --pro g <github token>
    

    --pro是使用travis-ci.com,之前是.org的地址,但是现在都是.com

    -g  是使用 GitHub 的个人 Token 进行登录,如果你能接受输入用户名和密码登录的话就去掉这个参数,直接使用travis login --pro也行。怎么创建私人Token可以参考:Creating a personal access token

  3. 生成一个 SSH 密钥

    ssh-keygen -f ./key -t ecdsa -b 521 -C 'travis'
    

    -f创建后密钥文件名称,-t密钥算法,-b密钥大小,-C注释(方便服务器区分密钥),为什么使用ecdsa可以查看官方文档:ssh-keygen

    运行这个命令后会提示输入密钥加密口令,直接回车不设置。

  4. 将生成的 SSH 密钥的公钥拷贝到服务器travis用户的ssh授权主机文件上,之后就可以进行免密登录了。

    ssh-copy-id -i ./key travis@<ip>
    
  5. 使用 Travis CLI 加密 SSH 私钥

    travis encrypt-file --pro key --add
    

    -add参数会自动添加需要的配置到.travis.yml里。需要注意的是自动添加后缩减可以会不习惯,比如我的:

    language: node_js
    node_js:
      - lts/*
    before_install:
    - openssl aes-256-cbc -K $encrypted_a68f2225bf71_key -iv $encrypted_a68f2225bf71_iv
      -in key.enc -out key -d
    

    这里如果你想统一锁进,需要注意-in key.enc ...这一行是和上一行是一起的,不要调整错了,应该是这样:

    language: node_js
    node_js:
      - lts/*
    before_install:
      - openssl aes-256-cbc -K $encrypted_a68f2225bf71_key -iv $encrypted_a68f2225bf71_iv -in key.enc -out key -d
    

    加密后生成的key.enc文件之后提交时需要也需要推到 GitHub的。

  6. 删除之前生成的 SSH 密钥文件

    rm -f key key.pub
    

配置自动化部署

最开始我使用的是after_success这个 Travis 提供的工作周期,但是它会在构建成功之后运行,我们实际需求并不是这样,我们是需要限制是主分支才部署。如果使用branches去限制构建分支的话也不太对,这样会导致其他分支有新提交不进行 CI 构建了。

所以我们需要使用deploy去配置,具体解释可参考官方文档:Script deployment

  1. 将之前获得 SSH 私钥的命令的周期从before_install修改为before_deploy。因为这个私钥只在部署的时候才会用到。

  2. 配置deploy的脚本命令,deploy默认在项目主分支触发,你可以自己通过on进行设置,也可以区分开发环境和生存环境使用不同脚本。

    language: node_js
    node_js:
      - lts/*
    before_deploy:
      - openssl aes-256-cbc -K $encrypted_a68f2225bf71_key -iv $encrypted_a68f2225bf71_iv -in key.enc -out key -d
      - chmod 600 key
    deploy:
      provider: script
      script: ssh -i key -o StrictHostKeyChecking=no travis@<ip> echo 'hello'>> test
      skip_cleanup: true
    

before_deploy的周期里需要添加密钥访问权限的修改命令,因为ssh连接时会出现密钥不应该被其他人访问的提示,所以我们需要去掉组和其他用户的权限chmod 600 key

-o StrictHostKeyChecking=no因为ssh连接时会出现未知主机提示确认的yes | no选择,但是没法去交互,我们需要跳出跳过这个提示。

skip_cleanup因为 CI 构建完成后会清除之前构建操作形成的文件,  而before_deploy生成的密钥也是被清掉了,所以我们需要跳过这个清理步骤。

最后

deploy的脚本配置上面只是给test文件添加一行hello,每次主分支有新提交就会触发。我们可以设置触发服务器上的一个脚本。

ssh -i key -o StrictHostKeyChecking=no travis@<ip> ~/deploy.blog.sh

但是这样不太好维护,所以我们把部署脚本放到仓库里,然后使用<标准输入执行脚本配置:

ssh -i key -o StrictHostKeyChecking=no travis@<ip> < scripts/deploy.sh

例如我的Express项目部署脚本:

#!/bin/bash

# 项目名
readonly APP_NAME=blog
# Express 服务入口文件
readonly SERVER_FILE=build/index.js

# 这里使用了 pm2 对服务进行管理
if !(command -v pm2 >/dev/null 2>&1); then 
  yarn global add --silent pm2
  # pm2 install typescript
fi

cd ${APP_NAME}
# 先拉取仓库最新内容
git pull
# 安装依赖
yarn install --silent
# 构建项目(打包前端资源、编译Express服务的typescript)
yarn build

# 使用 pm2 启动项目,监听项目文件变化自动重启项目
# 已经启动了就不再进行启动
if [ "$(pm2 id ${APP_NAME})" = "[]" ]; then
  pm2 start ${SERVER_FILE} -n ${APP_NAME} --watch
  pm2 save
fi

其他复杂情况的部署需求,可以直接看Travis CI Doc进行额外的配置。


版权声明:

Anand's Blog文章皆为站长Anand Zhang原创内容,转载请注明出处。

包括商业转载在内,注明下方要求的文章出处信息即可,无需联系站长授权。

请尊重他人劳动成果,用爱发电十分不易,谢谢!

请注明出处:

本文出自:Anand's Blog

本文永久链接:https://anandzhang.com/posts/server/8