编程

使用Ansible和GitHub Actions 部署你的 PHP 代码库

1334 2022-02-16 10:31:15

本文将展示如何使用GitHub Actions及Ansible自动部署你的PHP代码库。

在生产服务器上部署PHP应用有很多不同方式。如今,大部分项目使用git 和像GitHub, GitLab或Bitbucket这样的平台。但是,怎样将你的更新同步到服务器呢?你可以在每次变更后用SSH手动登录服务器, 从GitHub上拉取最新代码;你也可以使用GitHub的webhooks自动拉取,或者你也可以使用外部软件帮你管理和部署代码。本文将像你展示怎么使用自动化运维工具Ansible和GitHub Actions自动部署。 Ansible是一款强大的开源自动化运维软件,它是使用python编写,简化了安装过程和可以自动化管理远程机器。

一般而言,Ansible 这样工作的:你需要先创建一个Ansible 清单包括你服务器信息,你需要为Ansible设置一种登录服务器的方法。理想状态下,你可以在服务器上创建一个带有SSH密钥的新用户,仅供Ansible登录使用。其他配置通过YAML文件进行设置。

你可以使用Ansible做很多事情:安装(网页)服务器 ,保持更新,安装软件,自动扩容等等 — 本教程将只关注如何使用Ansible自动部署最新版的代码。我们会准备服务器,配置一个可以在每次将代码推送到production分支时自动运行Ansible和部署代码的GitHub Action。
准备

为了让Ansilble可以连上并在服务器上工作,你需要做两件事:安装必要的软件(python和Git) 以及在服务器上为Ansible单独创建一个管理用户。可以通过SSH连接到服务器运行以下命令:

# 1. Install Python and Git 安装Python和Git
sudo apt install python3 git
# 2. Create a superuser named "ansible" 创建一个名为ansible的超级用户
sudo useradd -m -G sudo ansible
sudo passwd ansible

现在你的服务器上有了一个ansible用户,你应该为该账户创建一个SSH 密钥对,以替代密码登录,使得连接更加安全。同时要确保为这个新创建的ansible用户启用SSH密钥,而不是为默认root用户启用:

如果你有多个服务器,需要在每个服务器上都重复此步骤(理论上,在所有服务器上都让Ansible用户使用同一个的SSH公钥)。

Set up the Inventory

所谓的Ansible Inventory 拥有你服务器的所有信息及所有可能的额外变量。你可以将内容放在一个文件里;也可以放在GitHub私有库中,然后在GitHub Action中获取。建议使用第二种方式, 因为inventory中的一些内容(如服务器ip) 可能是机密,不应该在你的 git 版本控制中出现(特别是当你的服务器放在Couldflare防火墙后诸如此类)。

Inventory 被分成服务器分组,相当直接了当:

---
all:
  vars:
    # here you can put any global variables you might need in Ansible playbooks
webserver: # the name of our first server group, you can rename this group or add more groups as you like
  hosts:
    254.254.254.254: # replace this with the IP address of your server. If you have multiple servers, just add them in the following lines

创建剧本(Playbook)

要让Ansible知道部署代码的时候怎么该做,我们需要先创建所谓的Ansible剧本(playbook)。 它是一个YAML文件,将任务分割成不同的步骤。建议在你的PHP代码库中为你的Ansible剧本创建新的目录。

同时,你也可以为Ansible Playbooks单独新建一个代码库,当你的PHP代码库中推送到你的主分支时,用以触发在你的Ansible 库中的GitHub Action的执行。这样做的好处是,你可以分离解耦不同的任务,但会显得比较复杂。因此,我建议先用简单的方式,在你的主代码库中创建目录:

Make sure to configure your webserver to forbid accessing your Ansible folder publicly.

在此目录中,创建一个新的YAML文件deploy-playbook.yml. 在这个剧本中,让Ansible做如下一些事情:

  1. 将服务器上PHP代码库克隆到一个临时文件夹C
  2. 可选:做其他我们部署时可能需要做的额外步骤(比如安装composer包)
  3. 将代码复制到目标目录(通常可能web服务器的DocumentRoot目录)
  4. 更新文件权限 (以便web服务器可以访问)

我们可以在playbook中为每一步添加任务:

 ---
- hosts: webserver
  become: yes
  become_user: root
  vars:
    temp_repo_path: "/tmp_repo" # path to the temporary downloadfolder of the repo
  tasks:
    # Get the latest code from your GitHub code repository
    - name: Get latest Application Codebase
      git:
        repo: https://github.com/AnTheMaker/Pesto.git # place the HTTPS git URL of your codebase here
        version: main # replace this with the name of your "production" branch (in most cases "main" or "master")
        dest: "{{ temp_repo_path }}"
        single_branch: yes
        accept_hostkey: true
        depth: 1
      # if your git repository is private, you'll need to create a GitHub deploy key (https://docs.github.com/en/developers/overview/managing-deploy-keys#deploy-keys), upload the private key to your server and uncomment the following line:
      # key_file: ~/github_deploy_private_key.key
      register: code_upload # set a variable in Ansible if this task succeeds
#   do any additional tasks you may need to do. E.g. install composer packages: (uncomment/change as you like)
#   - name: Install Composer Packages
#     composer:
#       command: "install"
#       working_dir: "{{ temp_repo_path }}"
#     environment:
#       COMPOSER_NO_INTERACTION: "1"
#     when: code_upload.changed # only run this task when the code has changed
    - name: Replace live Codebase
      delegate_to: "{{ inventory_hostname }}"
      synchronize:
        src: "{{ temp_repo_path }}"
        dest: /var/www/
        recursive: yes
        delete: yes
      when: code_upload.changed # only run this task when the code has changed
    - name: Update Permissions
      file:
        path: /var/www/html
        state: directory
        recurse: yes
        # change the following two lines to the user of your webserver
        owner: www-data
        group: www-data

Set up GitHub Actions

现在我们有了一个Ansible Playbook 引导如何部署代码以及Inventory包含服务器IP。我们只需设置

GitHub Action使之在每次推送代码到production(或者其他自定义分支名)分支的时候自动运行Ansible。

幸运的是,GitHub上已经有了一个现成的GitHub Action, 叫Run Ansible Playbook 。我们只需配置一下就能使之与我们的安装配合工作。

为此,我们需要在代码块中创建一个GitHub Action的YAML文件,文件路径:.github/workflows/main.yml

 name: Deploy
on:
  # Triggers this Action on push or pull request events on the "main" branch and when manually requested from the "Actions" tab
  push:
    branches: [ main ]
  workflow_dispatch:
jobs:
  deploy_code:
    runs-on: ubuntu-latest
    steps:
      - name: Run Ansible playbook
        uses: dawidd6/action-ansible-playbook@v2.5.0
        with:
          playbook: ansible/deploy-playbook.yml # path to your Ansible playbook
          directory: ./
          key: ${{ secrets.ansible_ssh_private_key }} # the ssh private key for ansible to use to connect to the servers, stored as "ansible_ssh_private_key" in the GitHub secrets
          inventory: ${{ secrets.ansible_inventory }} # the ansible inventory to use, stored as "ansible_inventory" in the GitHub secrets

保存这个工作流文件,将其提交发布到GitHub将会自动启动此Action。以后,当你推送更新到production(或者从你的代码库Action标签页中手动请求部署)时,会执行该Action,运行Ansible, 连接到你的服务器,部署新代码。当运行这个action时,你可以点击Action标签页,查看当前运行的Action(带有所有Ansible运行时可能的错误和警告实时日志)。