我正在设置一个带有 PXE 启动的自动安装环境。要以这种方式安装 Linux,基本上需要下载linux
和initrd.gz
文件,它们是深埋于地下在存储库结构中,并提供将引用这些文件的 pxelinux 和/或 pxegrub 配置。这是针对 Debian 的;其他发行版应该类似。我之前手动做过这个。
我编写了以下基本剧本来从服务器下载文件并将其放入 tftp 服务器:
- name: test download
hosts: test-netinstsrv
gather_facts: false
vars:
netboot:
base: https://deb.debian.org/debian/dists/bullseye/main/installer-amd64/current/images/
files_base: netboot/debian-installer/amd64/
files: [ linux, initrd.gz ]
tasks:
- name: Create a temporary directory
ansible.builtin.tempfile:
state: directory
suffix: netboot
register: tmpdir
delegate_to: localhost
run_once: true
changed_when: false
- name: Set permissions
ansible.builtin.file:
path: "{{ tmpdir.path }}"
mode: 0755
delegate_to: localhost
run_once: true
changed_when: false
- name: Download files
ansible.builtin.uri:
url: "{{ netboot.base }}{{ netboot.files_base }}{{ item }}"
dest: "{{ tmpdir.path }}/{{ item }}"
method: "get"
loop: "{{ netboot.files }}"
delegate_to: localhost
run_once: true
register: downloaded_file
changed_when: false
- name: Copy files to the remote
ansible.builtin.copy:
src: "{{ tmpdir.path }}/{{ item }}"
dest: "/tmp/"
loop: "{{ netboot.files }}"
- name: Remove temporary directory
ansible.builtin.file:
path: "{{ tmpdir.path }}"
state: absent
delegate_to: localhost
run_once: true
changed_when: false
(实际剧本要庞大得多,这只是摘录。)
这很有效,但效率低下的是,每次播放时它总是下载两个文件,即使没有任何变化。我不得不把changed_when
几乎所有地方都放进去,以免在播放重演中看到虚假的变化;唯一真正的变化可能是一个copy
模块可以做的。由于我计划将其扩展到 Debian 的几个版本(目前支持所有版本)并添加其他发行版,因此下载量将变得过多。
我使用一个uri
模块来下载文件,但我没有找到在文档中如何使此下载有条件。我想要实现的是,让下载模块发出类似于 If-Modified-Since 请求,存储库 Web 服务器可以返回 304 Not Modified,这意味着跳过目标服务器上的启动映像更新,从而节省下载时间。
由于目标服务器无法访问互联网,因此这有点复杂。因此,所有下载都必须由控制机器完成,所以我将大多数任务委托给本地主机。
一个想法是首先从我的目标服务器之一获取这些文件到控制器,然后希望 uri 模块能够检测到文件已经存在,并且除非文件发生变化,否则不会重新下载它们。这样可行吗?还有其他方法可以实现吗?
更新
我尝试使用get_url
模块(甚至在答案出现之前)。剧本的更改基本上是:
- name: Create required subdirectories
ansible.builtin.file:
path: "{{ tmpdir.path }}/{{ netboot.files_base }}"
state: directory
delegate_to: localhost
run_once: true
- name: Download files
ansible.builtin.get_url:
url: "{{ netboot.base }}{{ netboot.files_base }}{{ item }}"
dest: "{{ tmpdir.path }}/{{ netboot.files_base }}{{ item }}"
checksum: "sha256:{{ netboot.base }}SHA256SUMS"
loop: "{{ netboot.files }}"
delegate_to: localhost
run_once: true
register: downloaded_file
失败了:
failed: [test-netinstsrv -> localhost] (item=linux) => {"ansible_loop_var": "item", "changed": false, "item": "linux", "msg": "Unable to find a checksum for file 'linux' in 'https://deb.debian.org/debian/dists/bullseye/main/installer-amd64/current/images/SHA256SUMS'"}
failed: [test-netinstsrv -> localhost] (item=initrd.gz) => {"ansible_loop_var": "item", "changed": false, "item": "initrd.gz", "msg": "Unable to find a checksum for file 'initrd.gz' in 'https://deb.debian.org/debian/dists/bullseye/main/installer-amd64/current/images/SHA256SUMS'"}
可能,因为SHA256SUMS
具有以下结构:
...
52eb21964231223563a59656708270c5708c8dcf5b3a1c5cccb1924af9964332 ./netboot/debian-installer/amd64/initrd.gz
b00b339f8b1aada1841d86650377dd8e7299eaa7f34d0bbf21deb561467015cd ./netboot/debian-installer/amd64/linux
...
可能,如果我下载该文件并重新格式化它,它会有所帮助......
答案1
ansible.builtin.get_url 是一个通用的文件下载模块,它的作用就是在满足某些条件的情况下跳过下载。
根据目标文件修改时间,自标头以来已修改。如果响应标头显示未修改,则跳过下载(并返回未更改的模块)。
此外,提供校验和参数。如果目标文件存在且匹配,则不会下载。此外,如果下载的(临时)文件与提供的校验和不匹配,则模块失败。后一种用例可能有助于固定已知版本,这样下载文件的任何更改都是显而易见的。
答案2
get_url
无法识别 SHA256SUMS 文件的格式,这很令人失望,因为这种格式在校验和文件中很常见。此外,它要求文件存在于本地,这也不太有效。
通过以下任务集我能够实现我想要的目标:
- name: Download SHA256SUMS file to compare against
ansible.builtin.get_url:
url: "{{ netboot.base }}SHA256SUMS"
dest: "{{ tmpdir.path }}/"
delegate_to: localhost
run_once: true
become: false
changed_when: false
- name: Find checksums of files on the server
ansible.builtin.stat:
path: "{{ netboot.remote_location }}{{ item }}"
checksum_algorithm: sha256
get_checksum: yes
loop: "{{ netboot.files }}"
register: remote_stat
changed_when: false
- name: Compare server checksums against SHA256SUMS file
command: "grep -E '{{ item.stat.checksum }} ./{{ netboot.files_base }}{{ item.item }}' {{ tmpdir.path }}/SHA256SUMS"
loop: "{{ remote_stat.results }}"
when: item.stat.exists
delegate_to: localhost
become: false
ignore_errors: true
changed_when: false
register: comparison
- name: Download missing or different files
ansible.builtin.get_url:
url: "{{ netboot.base }}{{ netboot.files_base }}{{ item.item.item }}"
dest: "{{ tmpdir.path }}/{{ item.item.item }}"
loop: "{{ comparison.results }}"
when: (item.skipped is defined) or item.failed
delegate_to: localhost
run_once: true
become: false
- name: Copy files to the remote
ansible.builtin.copy:
src: "{{ tmpdir.path }}/{{ item.item.item }}"
dest: "{{ netboot.remote_location }}"
loop: "{{ comparison.results }}"
when: (item.skipped is defined) or item.failed
它会下载 SHA256SUMS,对照它检查服务器上的文件,然后只下载缺失或不同的文件。理想情况下,它只会下载这个小文件,只有在出现问题时才会移动其他文件;甚至不需要将文件提取到控制器机器上。
它还可以通过修复 SHA256SUMS 文件本身的校验和来“固定”某些内容。
我还不确定当游戏中有多台服务器时它是否会工作(我只在一台服务器上测试过)。可能应该run_once
从上面的倒数第二个任务中删除。此外,我希望可以让它足够通用,以便它能够接受其他发行版使用的其他目录结构。