使用ansible添加交换空间

41

我正在处理一个项目,我的服务器需要交换空间以避免一些Python长时间运行的进程耗尽内存。我意识到我的Ubuntu Vagrant虚拟机和AWS Ubuntu实例都没有设置交换空间。

https://github.com/ansible/ansible/issues/5241中讨论了可能的内置解决方案,但从未实现,因此我想这应该是一个相当常见的任务要实现自动化。

您将如何使用Ansible以可重复方式设置基于文件的交换空间?Ansible提供哪些模块或变量来帮助此设置(例如ansible_swaptotal_mb变量)?


我无法回复Greg Dubicki的答案,但我认为在“从fstab中删除交换条目”的任务中,状态应该设置为“absent”而不是“present”,否则该任务将无法完成其宣传的功能。 - Josep M Homs
8个回答

51

这是我的目前解决方案:

- name: Create swap file
  command: dd if=/dev/zero of={{ swap_file_path }} bs=1024 count={{ swap_file_size_mb }}k
           creates="{{ swap_file_path }}"
  tags:
    - swap.file.create


- name: Change swap file permissions
  file: path="{{ swap_file_path }}"
        owner=root
        group=root
        mode=0600
  tags:
    - swap.file.permissions


- name: "Check swap file type"
  command: file {{ swap_file_path }}
  register: swapfile
  tags:
    - swap.file.mkswap


- name: Make swap file
  command: "sudo mkswap {{ swap_file_path }}"
  when: swapfile.stdout.find('swap file') == -1
  tags:
    - swap.file.mkswap


- name: Write swap entry in fstab
  mount: name=none
         src={{ swap_file_path }}
         fstype=swap
         opts=sw
         passno=0
         dump=0
         state=present
  tags:
    - swap.fstab


- name: Mount swap
  command: "swapon {{ swap_file_path }}"
  when: ansible_swaptotal_mb < 1
  tags:
    - swap.file.swapon

1
我会担心幂等性——特别是,如果在已经存在交换文件的情况下运行部署,这可能会将其清零,我想这会导致所有具有任何页面交换的进程崩溃。 - offby1
6
如果文件存在,第一个任务中的creates="{{ swap_file_path }}"属性将处理这个问题,dd命令不会运行。我仍在生产环境中使用此功能,并且当前任务条件(when/creates)似乎正在发挥作用,即使这是幂等性的......你能详细说明一下可能会将文件清零的情况吗? 请注意,我将原文中“idempotent”翻译为了“幂等性”。 - gonz
5
你说得对,"creates" 应该会解决这个问题。我最开始没有注意到这点。 - offby1
4
我用上述方法,并注意到交换文件没有停在我指定的文件大小,它继续增长并填满整个驱动器(在我的情况下是20GB)。原来是计数旁边的 k 导致的。将其删除后,它就像魔术般地运作了。这是在使用Centos 7.5系统时发生的。 - Richard Bale
1
就像Richard Bale所说的那样,由于块大小bs和末尾的kswap_file_size_kb实际上是以兆字节为单位而不是千字节。 - James D
显示剩余3条评论

37

我尝试了上面的答案,但是总是返回"Check swap file type"已经被更改,因此不符合幂等性。当编写Ansible任务时,这是一个最佳实践。

下面的角色已在Ubuntu 14.04 Trusty上进行了测试,并且不需要启用gather_facts

- name: Set swap_file variable
  set_fact:
    swap_file: "{{swap_file_path}}"
  tags:
    - swap.set.file.path

- name: Check if swap file exists
  stat:
    path: "{{swap_file}}"
  register: swap_file_check
  tags:
    - swap.file.check

- name: Create swap file
  command: fallocate -l {{swap_file_size}} {{swap_file}}
  when: not swap_file_check.stat.exists
  tags:
    - swap.file.create

- name: Change swap file permissions
  file: path="{{swap_file}}"
        owner=root
        group=root
        mode=0600
  tags:
    - swap.file.permissions

- name: Format swap file
  sudo: yes
  command: "mkswap {{swap_file}}"
  when: not swap_file_check.stat.exists
  tags:
    - swap.file.mkswap

- name: Write swap entry in fstab
  mount: name=none
         src={{swap_file}}
         fstype=swap
         opts=sw
         passno=0
         dump=0
         state=present
  tags:
    - swap.fstab

- name: Turn on swap
  sudo: yes
  command: swapon -a
  when: not swap_file_check.stat.exists
  tags:
    - swap.turn.on

- name: Set swappiness
  sudo: yes
  sysctl:
    name: vm.swappiness
    value: "{{swappiness}}"
  tags:
    - swap.set.swappiness

从Ansible 2.9版本开始,sudo声明应该改为become:

- name: Set swap_file variable
  set_fact:
    swap_file: "{{swap_file_path}}"
  tags:
    - swap.set.file.path

- name: Check if swap file exists
  stat:
    path: "{{swap_file}}"
  register: swap_file_check
  tags:
    - swap.file.check

- name: Create swap file
  command: fallocate -l {{swap_file_size}} {{swap_file}}
  when: not swap_file_check.stat.exists
  tags:
    - swap.file.create

- name: Change swap file permissions
  file: path="{{swap_file}}"
        owner=root
        group=root
        mode=0600
  tags:
    - swap.file.permissions

- name: Format swap file
  become: yes
  command: "mkswap {{swap_file}}"
  when: not swap_file_check.stat.exists
  tags:
    - swap.file.mkswap

- name: Write swap entry in fstab
  mount: name=none
         src={{swap_file}}
         fstype=swap
         opts=sw
         passno=0
         dump=0
         state=present
  tags:
    - swap.fstab

- name: Turn on swap
  become: yes
  command: swapon -a
  when: not swap_file_check.stat.exists
  tags:
    - swap.turn.on

- name: Set swappiness
  become: yes
  sysctl:
    name: vm.swappiness
    value: "{{swappiness}}"
  tags:
    - swap.set.swappiness

所需变量:

swap_file_path: /swapfile
# Use any of the following suffixes
# c=1
# w=2
# b=512
# kB=1000
# K=1024
# MB=1000*1000
# M=1024*1024
# xM=M
# GB=1000*1000*1000
# G=1024*1024*1024
swap_file_size: 4G
swappiness: 1

1
可以在“ubuntu/trusty” Vagrant 箱上运行。谢谢! - atripes
1
也适用于 CentOS 7.3 - chrlaura
1
也适用于Ubuntu 16.04 LTS(Xenial Xerus)。谢谢。 - Joe Eifert
1
也适用于Ubuntu 18.04 LTS。谢谢。 - Levon
2
从Ansible 2.9版本开始,“sudo”不再是有效的关键字类型。因此,“sudo:yes”应更改为“become:yes”。 - Bless
1
在游戏部分,我会将“已完成”设置为true,而不是任务部分,但这取决于你正在做什么。 - Alex R

18

我基于伟大的Ashley的答案(请点赞!)并添加了以下额外功能:

  • 全局标志来管理交换或不交换,
  • 允许更改交换大小,
  • 允许禁用交换,

...加上这些技术改进:

  • 完全幂等性(仅在未更改任何内容时使用okskipping,否则使用一些changed),
  • 使用dd而不是fallocate以与XFS文件系统兼容(有关更多信息,请参见此答案),

限制:

  • 仅当您将其路径提供为swap_file_path时,更改现有交换文件才能正常工作。

在Centos 7.7上使用Ansible 2.9和Rocky 8上测试过与Ansible 4.8.0。

需要使用特权升级,因为许多命令需要以root身份运行。 (最简单的方法是将--become添加到ansible-playbook参数或在ansible.cfg设置适当的值)。

参数:

swap_configure: true # or false
swap_enable: true # or false
swap_file_path: /swapfile
swap_file_size_mb: 4096
swappiness: 1

代码:


- name: Configure swap
  when: swap_configure | bool
  block:

    - name: Check if swap file exists
      stat:
        path: "{{swap_file_path}}"
        get_checksum: false
        get_md5: false
      register: swap_file_check
      changed_when: false

    - name: Set variable for existing swap file size
      set_fact:
        swap_file_existing_size_mb: "{{ (swap_file_check.stat.size / 1024 / 1024) | int }}"
      when: swap_file_check.stat.exists

    - name: Check if swap is on
      shell: swapon --show | grep {{swap_file_path}}
      register: swap_is_enabled
      changed_when: false
      failed_when: false

    - name: Disable swap
      command: swapoff {{swap_file_path}}
      register: swap_disabled
      when: >
        swap_file_check.stat.exists
        and 'rc' in swap_is_enabled and swap_is_enabled.rc == 0
        and (not swap_enable or (swap_enable and swap_file_existing_size_mb != swap_file_size_mb))

    - name: Delete the swap file
      file:
        path: "{{swap_file_path}}"
        state: absent
      when: not swap_enable

    - name: Remove swap entry from fstab
      mount:
        name: none
        src: "{{swap_file_path}}"
        fstype: swap
        opts: sw
        passno: '0'
        dump: '0'
        state: present
      when: not swap_enable

    - name: Configure swap
      when: swap_enable | bool
      block:

        - name: Create or change the size of swap file
          command: dd if=/dev/zero of={{swap_file_path}} count={{swap_file_size_mb}} bs=1MiB
          register: swap_file_created
          when: >
            not swap_file_check.stat.exists
            or swap_file_existing_size_mb != swap_file_size_mb

        - name: Change swap file permissions
          file:
            path: "{{swap_file_path}}"
            mode: 0600

        - name: Check if swap is formatted
          shell: file {{swap_file_path}} | grep 'swap file'
          register: swap_file_is_formatted
          changed_when: false
          failed_when: false

        - name: Format swap file if it's not formatted
          command: mkswap {{swap_file_path}}
          when: >
            ('rc' in swap_file_is_formatted and swap_file_is_formatted.rc > 0)
            or swap_file_created.changed

        - name: Add swap entry to fstab
          mount:
            name: none
            src: "{{swap_file_path}}"
            fstype: swap
            opts: sw
            passno: '0'
            dump: '0'
            state: present

        - name: Turn on swap
          shell: swapon -a
          # if swap was disabled from the start
          # or has been disabled to change its params
          when: >
            ('rc' in swap_is_enabled and swap_is_enabled.rc != 0)
            or swap_disabled.changed

        - name: Configure swappiness
          sysctl:
            name: vm.swappiness
            value: "{{ swappiness|string }}"
            state: present


谢谢!顺便提一下,当我没有交换空间时,“打开交换空间”被跳过了(因为“禁用交换空间”的更改也被跳过了)。 - vehovmar
3
有一个 bug,"Turn on swap" 需要检查 "swap_enabled.rc != 0",因为只有当交换已经打开时它才会为 0,在这种情况下就不需要再次启用它。 - vehovmar
1
已在Ubuntu 18.04上进行了测试和验证(使用Vehovmar建议的修复方法)。 - Cethy
1
谢谢!我认为任务“打开交换”需要更改为swap_emabled.rc != 0或swap_disabled.changed - lefterisnik
谢谢@vehovmar,我已经应用了你的修复! - Greg Dubicki
显示剩余2条评论

1
我无法回复 Greg Dubicki answer,所以在这里加上我的两分钱:
我认为在对swap_file_size_mb * 1024 * 1024进行计算时添加到int的转换,以将其转换为swap_file_size_mb | int * 1024 * 1024,将使代码更加智能化,以防您想要使用事实来提取基于安装的RAM量的最终交换大小,例如:
swap_file_size_mb: "{{ ansible_memory_mb['real']['total'] * 2 }}"

否则,即使大小相同,它也将始终调整交换文件的大小。

感谢您提供的建议,如何根据服务器RAM使交换大小动态变化!我已经采用了您关于强制转换为“int”的建议,以确保我的代码在最近的更新中仅在需要时才能正常工作。 :) - Greg Dubicki

0

0

这是我使用的ansible-swap playbook,用于在新服务器上安装4GB(或者我为group_vars中的dd_bs_size_mb* swap_count配置的任何值)交换空间:

https://github.com/tribou/ansible-swap

我还在我的~/.bash_profile中添加了一个函数来帮助完成这个任务:

# Path to where you clone the repo
ANSIBLE_SWAP_PLAYBOOK=$HOME/dev/ansible-swap

install-swap ()
{
    usage='Usage: install-swap HOST';
    if [ $# -ne 1 ]; then
        echo "$usage";
        return 1;
    fi;
    ansible-playbook $ANSIBLE_SWAP_PLAYBOOK/ansible-swap/site.yml --extra-vars "target=$1"
}

请确保首先将您的HOST添加到Ansible清单中。

然后只需执行install-swap HOST即可。


0
我创建了以下任务,尽管我对我们进行了一些“优化”。
首先,建议使用dd而不是fallocate(参考https://askubuntu.com/a/1017312)。我推荐使用general.community.filesize而不是内置的command,因为它包含适当的检查模式+使用dd,并以紧凑形式处理文件权限。
我根据(https://askubuntu.com/a/594402)设置了推荐的RAM大小。因此:swap_file_size_gb: "{{ ((ansible_memory_mb.real.total / 1000) ** 0.5) | round | int }}"
---
- name: add-swap-file | Check whether SWAP file exist
  ansible.builtin.command:
    cmd: swapon -s
  register: swap_exist
  no_log: true
  changed_when: false
  check_mode: false

- name: add-swap-file | Debug show server differences
  ansible.builtin.debug:
    msg: "Current swap file:\n{{ swap_exist.stdout }}\nRecommended swap file size: {{ swap_file_size_gb }}G"
    verbosity: 1

- name: add-swap-file | Create and configure swap file
  when: swap_exist.stdout | length == 0
  block:
    - name: add-swap-file | Create swap file
      community.general.filesize:
        path: "{{ swap_file_path }}"
        size: "{{ swap_file_size_gb }}G"
        blocksize: 1024
        mode: "600"
        owner: root
        group: root

    - name: add-swap-file | Check swap file type
      ansible.builtin.command:
        cmd: "file {{ swap_file_path }}"
      register: swapfile
      check_mode: false
      changed_when: false

    - name: add-swap-file | Make swap file
      ansible.builtin.command:
        cmd: "mkswap {{ swap_file_path }}"
      when: swapfile.stdout.find('swap file') == -1
      changed_when: true

    - name: add-swap-file | Write swap entry in fstab
      ansible.posix.mount:
        name: none
        src: "{{ swap_file_path }}"
        fstype: swap
        opts: sw
        passno: 0
        dump: 0
        state: present

    - name: add-swap-file | Mount swap
      ansible.builtin.command:
        cmd: "swapon {{ swap_file_path }}"
      when: ansible_swaptotal_mb < 1
      changed_when: true

-4
我的做法是通过Playbook添加4Gib交换空间。
请参阅以下内容:

this.


4
请用文字替换屏幕截图,@krishna-upadhyay。请参考https://meta.stackoverflow.com/questions/303812/discourage-screenshots-of-code-and-or-errors以获得理由。 - Greg Dubicki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接