Git 实现自动化部署项目

  • 应用场景
    在本地开发、调试后想部署到服务器上,那么一般做法是先将本地的代码全部提交到 github 仓库,这时候要让服务器上的代码更换成最新提交的,那就得登录到服务器做一次 git pull origin master 的操作,前提是已经在服务器的项目目录下初始化好项目仓库。

此文探讨 Git 如何实现自动化部署项目,避免每次 git pull origin master 的繁琐操作。

  • Git 钩子
    官网解释:和其它版本控制系统一样,Git 能在特定的重要动作发生时触发自定义脚本。
    有两组这样的钩子:
    1、客户端的。客户端钩子由诸如提交和合并这样的操作所调用。
    2、服务器端。服务器端钩子作用于诸如接收被推送的提交这样的联网操作。你可以随心所欲地运用这些钩子。

简单来说就是一个在特定环境下触发的脚本。也就是说我们可以把服务器拉取更新的代码这一步设置为自动触发,自动拉取到最新

部署过程

以 root 用户为例,若不是 root 用户,可以看最底下的非 root 用户操作说明

  • 第一步:检查家目录下是否有.ssh 文件夹
    .ssh 中应有 id_rsaid_rsa.pubauthorized_keys 三个文件,若没有则用 touch 创建。

  • 第二步:配置 git 并获取公钥

    • 如果用了 --global 选项,那么以后该用户下所有的 git 项目都会使用这个全局配置的用户信息。
    #在本地配置用户名和邮箱
    git config --global user.name "your name"
    git config --global user.email "your email"
    
    • 如果要在某个特定的项目中使用特定名字或邮箱,只需在该项目目录下执行
    git config user.name "your name"
    git config user.email "your email"
    
    • 接下来获取公钥,请先查看你的用户下的.ssh 文件夹中是否之前就含有公钥和私钥,我们需要寻找一对以 id_dsa 或 id_rsa 命名的文件(第一步已说明),其中一个带有 .pub 扩展名。.pub 文件是你的公钥,另一个则是私钥。如果没有请运行 ssh-keygen。

      ssh-keygen -t rsa -C "your_email@example.com"
      
    • 注意:执行 ssh-keygen 命令后,会出现 Enter file in which to save the key (/root/.ssh/id_rsa): 的提示,直接回车即可,表明该用户想在 /root/.ssh/id_rsa 默认路径下创建。然后会提示 Enter passphrase (empty for no passphrase): ,输入密码,然后会提示 Enter same passphrase again: ,再次输入密码,最后会提示 Your identification has been saved in /root/.ssh/id_rsa. 说明公钥已经生成。

    • 查看公钥,执行命令:

      cat ~/.ssh/id_rsa.pub
      
    • 使用 cat ~/.ssh/id_rsa.pub 命令可以获取公钥,复制它,使用 vi 或者 vim 命令把它粘贴到我们创建的 authorized_keys 文件中,使用:wq 保存。

  • 第三步:初始化仓库 (这个仓库用作中转)

    • 中转的作用?是指在服务器上创建的裸仓库,本地项目仓库会克隆该裸仓库到本地,具有向远程裸仓库 push、pull 的能力,服务器上的项目仓库也会克隆一份裸仓库,那么在这三者中,本地仓库向远程裸仓库 git push 最新代码后,这个裸仓库会执行我们使用 git 钩子编写的自动化脚本,然后命令服务器上的项目仓库 git pull 裸仓库中最新的代码,从而实现自动化部署。

    • 进入项目文件目录 cd /your_work_dir,或者也可以创建一个存放 git 仓库的文件夹(此文示例只展示直接在项目目录中直接添加一个裸仓库且 your_work_dir 为 node 的情况):

      #创建一个存放 git 仓库的文件夹
      mkdir /your_work_dir/git
      cd /your_work_dir/git
      
    • 初始化仓库:

    #初始化一个裸仓库(强烈建议):
    # - 当只使用 git init 时,会将执行命令时所在的文件夹初始化为一个 git 仓库
    # - 当 git init 后加 gitResigterName 时,会在所在文件夹下创建一个新文件夹,此时该新文件夹为 git 仓库
    #如下命令是在 /node 目录下执行,即创建 /node/bare.git 仓库
    git init --bare bare.git
    

    • 关于裸仓库和普通仓库的区别简单来说就是裸仓库看不到项目文件,普通仓库和你的项目目录一样,只是多了一个 .git 文件夹。
  • 第四步:生成项目仓库

    • 在服务器上进行

      cd /your_work_dir
      #克隆仓库
      git clone /your_work_dir/bare.git
      
    • ==注意!一定要注意上面的路径!git 仓库是 /your_work_dir/bare.git, 项目仓库是 /your_work_dir/bare,即 bare 仓库目录下可存放自己的项目代码(此处存放的代码需要拉取中转仓库 bare.git 中的代码,自动化脚本就是执行这个自动拉取步骤)。==

  • 第五步:克隆到本地

    • 在你自己的电脑上进行

      # 通过ip地址从配置好的线上仓库拉取下来
      git clone git@your_server_ip:/your_work_dir/bare.git
      # 如果有配置域名的话也可以通过域名拉取
      git clone git@your_server_domain_name:/your_work_dir/bare.git
      
    • 因为公钥的原因,这里是不需要密码的,如果需要,那么就输入远程 SSH 连接需要的密码。

    • git clone 成功的话,你的电脑上会出现一个 bare 的文件夹,如果报错请检查后再进行下面的操作。

    • 上述两次 git clone 得到的 bare 文件夹是可以改名的。

  • 第六步:上传代码(git push)

    • 打开刚才克隆下来的本地仓库
      cd bare
      # 创建README.md文件
      touch README.md
      git add .
      git commit -m"创建README.md文件"
      git push
    
    • 不出意外已经正常上传了,如果报错请检查权限和 git 仓库情况。
  • 第七步:添加钩子

    • 回到线上的服务器,下面的命令是在线上服务器操作的:

      #切换到这个目录
      cd /your_work_dir/bare.git/hooks
      # 生成post-receive文件
      touch post-receive
      # 使用vim编辑
      vim post-receive
      
    • 在 post-receive 文件里面输入(下面所列的未注释命令除 echo 外,都需要添加进自己的脚本中):

      #!/bin/sh
      # 打印输出
      echo '======上传代码到服务器======'
      # 打开线上项目文件夹
      cd /your_work_dir/bare
      
      # 这个很重要,如果不取消的话将不能在cd的路径上进行git操作
      unset GIT_DIR
      git pull origin master
      
      # 在 /your_work_dir/code/bare 下需要执行的命令
      # 如npm run build、nodemon index.js
      
      # 输出 git 自动化脚本执行的日期日志到 /your_work_dir/logs/hook.log
      echo $(date) >> /your_work_dir/logs/hook.log
      
      # 以上仅为示例,有需要者可自行参考 git 钩子和根据需要扩展相关命令
      
    • 保存后给 post-receive 文件加上运行权限 chmod +x post-receive

  • 测试
    在本地修改部分内容,然后提交推送 git push,可以看到已经实现了自动化部署。

非 root 用户操作说明

  • 上面提到的部署过程仅为 root 用户示例,若为非 root 用户,大致步骤也一样,需要特别注意的是文件/文件夹的权限问题,比如在 /your_work_dir/git/bare.git 目录,当没有写入权限时,git pull 将会报错。

文章参考: 如何使用 Git 实现自动化部署你的项目

一些细枝末节的唠叨

  • 上述部署过程中使用到了三个仓库,bare 本地仓库、bare.git 中转仓库和 bare 线上项目仓库,为区分三者,先将本地仓库改为 dev,线上仓库改为 server 后,再做如下讲解:

  • 真正能 git push / pull 的仓库其实是 dev,它 push / pull 的对象是远程仓库 bare.git,而 server 仓库只是 git pull,一直自动拉取 bare.git 最新的更新情况

  • ==当手动修改了 server 仓库中的代码时,==,每次从 dev 本地仓库 push 到远程仓库后的自动更新可能会失败,其原因在于自动化脚本执行了 git pull origin master,它会自动执行一次合并,但是当合并失败时需要在 server 中手动解决合并。合并修改后的文件再重新添加到 server 的本地仓库,再次进行 git pull 从而得到 bare.git 中最新的一次提交