community.general.deploy_helper 模块 – 管理部署项目中常见的步骤

注意

此模块是 community.general 集合(版本 10.1.0)的一部分。

如果您正在使用 ansible 包,则可能已经安装了此集合。它不包含在 ansible-core 中。要检查它是否已安装,请运行 ansible-galaxy collection list

要安装它,请使用:ansible-galaxy collection install community.general

要在 playbook 中使用它,请指定:community.general.deploy_helper

概要

  • Deploy Helper 管理部署软件时的一些常见步骤。它创建文件夹结构,管理当前版本的符号链接,并清理旧版本。

  • 使用 state=querystate=present 运行它将返回 deploy_helper 事实。project_path,无论您在 path 参数中设置什么,current_path,指向活动版本的符号链接的路径,releases_path,用于保存版本的文件夹的路径,shared_path,用于保存共享资源的文件夹的路径,unfinished_filename,要检查的用于识别未完成构建的文件,previous_release,’current’ 符号链接指向的版本,previous_release_path,’current’ 符号链接目标的完整路径,new_release,’release’ 参数或生成的时间戳,new_release_path,新版本文件夹的路径(不是由模块创建的)。

参数

参数

注释

attributes

别名:attr

字符串

结果文件系统对象应具有的属性。

要获取支持的标志,请查看目标系统上的 chattr 的手册页。

此字符串应包含与 lsattr 显示的顺序相同的属性。

默认情况下假定使用 = 运算符,否则需要在字符串中包含 +- 运算符。

clean

布尔值

state=finalize 的情况下是否运行清理过程。

选择

  • false

  • true ← (默认)

current_path

路径

在部署完成时创建的符号链接的名称。在 state=finalizestate=clean 中使用。在 deploy_helper.current_path 事实中返回。

默认值: "current"

group

字符串

应拥有文件系统对象的所有者的组的名称,就像传递给 chown 一样。

如果未指定,它将使用当前用户的当前组,除非您是 root 用户,在这种情况下,它可以保留之前的拥有权。

keep_releases

整数

清理时保留的旧版本数量。用于 state=finalizestate=clean。任何未完成的构建都将首先被删除,因此只有正确的发布版本才会被计数。当前版本不会被计数。

默认值: 5

mode

any

结果文件系统对象应具有的权限。

对于那些习惯使用 /usr/bin/chmod 的人来说,请记住模式实际上是八进制数。您必须为 Ansible 提供足够的信息才能正确解析它们。为了获得一致的结果,请引用八进制数(例如,'644''1777'),以便 Ansible 接收到一个字符串,并且可以自己将字符串转换为数字。添加前导零(例如,0755)有时会起作用,但在循环和其他一些情况下可能会失败。

如果未遵循这些规则中的任何一条,则向 Ansible 提供数字将最终得到一个十进制数,这将产生意外的结果。

从 Ansible 1.8 开始,可以将模式指定为符号模式(例如,u+rwxu=rw,g=r,o=r)。

如果未指定 mode,并且目标文件系统对象不存在,则在设置新创建的文件系统对象的模式时,将使用系统上的默认 umask

如果未指定 mode,并且目标文件系统对象确实存在,则将使用现有文件系统对象的模式。

指定 mode 是确保使用正确的权限创建文件系统对象的最佳方法。有关更多详细信息,请参见 CVE-2020-1736。

owner

字符串

应该拥有该文件系统对象的用户的名称,就像提供给 chown 的一样。

如果未指定,则它将使用当前用户,除非您是 root 用户,在这种情况下,它可以保留以前的所有权。

指定数字用户名将被视为用户 ID,而不是用户名。避免使用数字用户名以避免混淆。

path

别名:dest

路径 / 必需

项目的根路径。在 deploy_helper.project_path 事实中返回。

release

字符串

正在部署的发布版本。默认为时间戳格式 %Y%m%d%H%M%S(例如 20141119223359)。此参数在 state=present 期间是可选的,但对于 state=finalize 需要显式设置。您可以使用生成的事实 release={{ deploy_helper.new_release }}

releases_path

字符串

将保存版本的文件夹的名称。这可以是相对于 path 或绝对路径。在 deploy_helper.releases_path 事实中返回。

默认值: "releases"

selevel

字符串

SELinux 文件系统对象上下文的级别部分。

这是 MLS/MCS 属性,有时称为 range

当设置为 _default 时,如果可用,它将使用策略的 level 部分。

serole

字符串

SELinux 文件系统对象上下文的角色部分。

当设置为 _default 时,如果可用,它将使用策略的 role 部分。

setype

字符串

SELinux 文件系统对象上下文的类型部分。

当设置为 _default 时,如果可用,它将使用策略的 type 部分。

seuser

字符串

SELinux 文件系统对象上下文的用户部分。

默认情况下,它使用 system 策略(如果适用)。

当设置为 _default 时,如果可用,它将使用策略的 user 部分。

shared_path

路径

将保存共享资源的文件夹的名称。这可以是相对于 path 或绝对路径。如果将其设置为空字符串,则不会创建共享文件夹。在 deploy_helper.shared_path 事实中返回。

默认值: "shared"

state

字符串

项目的状态。

query 将仅收集事实。

present 将创建项目 root 文件夹,并在其中创建 releasesshared 文件夹。

finalize 将删除 unfinished_filename 文件,创建指向新部署版本的符号链接,并可选择清理旧版本。

clean 将删除失败和旧版本。

absent 将删除项目文件夹(等同于 ansible.builtin.file 模块,其中 state=absent)。

选择

  • "present" ←(默认)

  • "finalize"

  • "absent"

  • "clean"

  • "query"

unfinished_filename

字符串

指示部署未完成的文件的名称。在 releases_path 中包含此文件的所有文件夹都将在 state=finalize 时,如果 clean=truestate=clean 时被删除。此文件将在 state=finalize 期间自动从 new_release_path 中删除。

默认值: "DEPLOY_UNFINISHED"

unsafe_writes

布尔值

影响何时使用原子操作来防止数据损坏或从目标文件系统对象读取不一致的数据。

默认情况下,此模块使用原子操作来防止数据损坏或从目标文件系统对象读取不一致的数据,但有时系统配置或只是以阻止这种方式的方式损坏。一个例子是 docker 挂载的文件系统对象,它无法从容器内部原子更新,只能以不安全的方式写入。

当原子操作失败时,此选项允许 Ansible 回退到更新文件系统对象的不安全方法(但是,它不会强制 Ansible 执行不安全写入)。

重要!不安全的写入会受到竞争条件的影响,并可能导致数据损坏。

选择

  • false ←(默认)

  • true

属性

属性

支持

描述

check_mode

支持: 完全

可以在 check_mode 中运行,并返回已更改的状态预测,而无需修改目标。

diff_mode

支持:

当处于差异模式时,将返回有关已更改的内容(或可能需要在 check_mode 中进行更改的内容)的详细信息。

备注

注意

  • 仅为 state=querystate=present 返回事实。如果两者都使用,则应将任何被覆盖的参数传递给两个调用,否则第二个调用将覆盖第一个调用的事实。

  • 当使用 state=clean 时,版本会按创建日期排序。您应该可以毫无问题地切换到新的命名策略。

  • 由于生成 new_release 事实的默认行为,除非您使用 release 传递您自己的版本名称,否则此模块将不是幂等的。由于部署软件的特性,这应该不是什么大问题。

示例

# General explanation, starting with an example folder structure for a project:

# root:
#     releases:
#         - 20140415234508
#         - 20140415235146
#         - 20140416082818
#
#     shared:
#         - sessions
#         - uploads
#
#     current: releases/20140416082818


# The 'releases' folder holds all the available releases. A release is a complete build of the application being
# deployed. This can be a clone of a repository for example, or a sync of a local folder on your filesystem.
# Having timestamped folders is one way of having distinct releases, but you could choose your own strategy like
# git tags or commit hashes.
#
# During a deploy, a new folder should be created in the releases folder and any build steps required should be
# performed. Once the new build is ready, the deploy procedure is 'finalized' by replacing the 'current' symlink
# with a link to this build.
#
# The 'shared' folder holds any resource that is shared between releases. Examples of this are web-server
# session files, or files uploaded by users of your application. It's quite common to have symlinks from a release
# folder pointing to a shared/subfolder, and creating these links would be automated as part of the build steps.
#
# The 'current' symlink points to one of the releases. Probably the latest one, unless a deploy is in progress.
# The web-server's root for the project will go through this symlink, so the 'downtime' when switching to a new
# release is reduced to the time it takes to switch the link.
#
# To distinguish between successful builds and unfinished ones, a file can be placed in the folder of the release
# that is currently in progress. The existence of this file will mark it as unfinished, and allow an automated
# procedure to remove it during cleanup.


# Typical usage
- name: Initialize the deploy root and gather facts
  community.general.deploy_helper:
    path: /path/to/root
- name: Clone the project to the new release folder
  ansible.builtin.git:
    repo: ansible.builtin.git://foosball.example.org/path/to/repo.git
    dest: '{{ deploy_helper.new_release_path }}'
    version: v1.1.1
- name: Add an unfinished file, to allow cleanup on successful finalize
  ansible.builtin.file:
    path: '{{ deploy_helper.new_release_path }}/{{ deploy_helper.unfinished_filename }}'
    state: touch
- name: Perform some build steps, like running your dependency manager for example
  composer:
    command: install
    working_dir: '{{ deploy_helper.new_release_path }}'
- name: Create some folders in the shared folder
  ansible.builtin.file:
    path: '{{ deploy_helper.shared_path }}/{{ item }}'
    state: directory
  with_items:
    - sessions
    - uploads
- name: Add symlinks from the new release to the shared folder
  ansible.builtin.file:
    path: '{{ deploy_helper.new_release_path }}/{{ item.path }}'
    src: '{{ deploy_helper.shared_path }}/{{ item.src }}'
    state: link
  with_items:
      - path: app/sessions
        src: sessions
      - path: web/uploads
        src: uploads
- name: Finalize the deploy, removing the unfinished file and switching the symlink
  community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: finalize

# Retrieving facts before running a deploy
- name: Run 'state=query' to gather facts without changing anything
  community.general.deploy_helper:
    path: /path/to/root
    state: query
# Remember to set the 'release' parameter when you actually call 'state=present' later
- name: Initialize the deploy root
  community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: present

# all paths can be absolute or relative (to the 'path' parameter)
- community.general.deploy_helper:
    path: /path/to/root
    releases_path: /var/www/project/releases
    shared_path: /var/www/shared
    current_path: /var/www/active

# Using your own naming strategy for releases (a version tag in this case):
- community.general.deploy_helper:
    path: /path/to/root
    release: v1.1.1
    state: present
- community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: finalize

# Using a different unfinished_filename:
- community.general.deploy_helper:
    path: /path/to/root
    unfinished_filename: README.md
    release: '{{ deploy_helper.new_release }}'
    state: finalize

# Postponing the cleanup of older builds:
- community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: finalize
    clean: false
- community.general.deploy_helper:
    path: /path/to/root
    state: clean
# Or running the cleanup ahead of the new deploy
- community.general.deploy_helper:
    path: /path/to/root
    state: clean
- community.general.deploy_helper:
    path: /path/to/root
    state: present

# Keeping more old releases:
- community.general.deploy_helper:
    path: /path/to/root
    release: '{{ deploy_helper.new_release }}'
    state: finalize
    keep_releases: 10
# Or, if you use 'clean=false' on finalize:
- community.general.deploy_helper:
    path: /path/to/root
    state: clean
    keep_releases: 10

# Removing the entire project root folder
- community.general.deploy_helper:
    path: /path/to/root
    state: absent

# Debugging the facts returned by the module
- community.general.deploy_helper:
    path: /path/to/root
- ansible.builtin.debug:
    var: deploy_helper

作者

  • Ramon de la Fuente (@ramondelafuente)