mirror of
https://gitea.com/mcereda/oam.git
synced 2026-02-09 05:44:23 +00:00
feat(ansible): long-running tasks + ansible setting to make aws ssm work for async
This commit is contained in:
@@ -208,24 +208,43 @@ Pitfalls:
|
|||||||
a folder the remote user can write to ([source][ansible temp dir change]):
|
a folder the remote user can write to ([source][ansible temp dir change]):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ANSIBLE_REMOTE_TMP='/tmp' ansible…
|
ANSIBLE_REMOTE_TMP="/tmp/.ansible-${USER}/tmp" ansible…
|
||||||
```
|
```
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# file: ansible.cfg
|
# file: ansible.cfg
|
||||||
remote_tmp=/tmp
|
remote_tmp=/tmp/.ansible-${USER}/tmp
|
||||||
```
|
```
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
- hosts: all
|
- hosts: all
|
||||||
+ vars:
|
+ vars:
|
||||||
+ ansible_remote_tmp: /tmp
|
+ ansible_remote_tmp: /tmp/.ansible-ssm-user/tmp
|
||||||
tasks: …
|
tasks: …
|
||||||
```
|
```
|
||||||
|
|
||||||
This, or use the shell profiles in [SSM's preferences][session manager preferences] to change the directory when
|
This, or use the shell profiles in [SSM's preferences][session manager preferences] to change the directory when
|
||||||
logged in.
|
logged in.
|
||||||
|
|
||||||
|
- In similar fashion to the point above, SSM might mess up the directory used by `async` tasks.<br/>
|
||||||
|
To avoid this, set it to a folder the remote user can write to:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ANSIBLE_ASYNC_DIR="/tmp/.ansible-${USER}/async" ansible…
|
||||||
|
```
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# file: ansible.cfg
|
||||||
|
async_dir=/tmp/.ansible-${USER}/async
|
||||||
|
```
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- hosts: all
|
||||||
|
+ vars:
|
||||||
|
+ ansible_async_dir: /tmp/.ansible-ssm-user/async
|
||||||
|
tasks: …
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
Refer [Troubleshooting managed node availability].
|
Refer [Troubleshooting managed node availability].
|
||||||
|
|||||||
@@ -70,12 +70,26 @@
|
|||||||
}}
|
}}
|
||||||
- name: Manipulate strings
|
- name: Manipulate strings
|
||||||
tags: string_manipulation
|
tags: string_manipulation
|
||||||
|
vars:
|
||||||
|
module_output: >-
|
||||||
|
u001b]0;@smth:/u0007{
|
||||||
|
"failed": 0, "started": 1, "finished": 0, "ansible_job_id": "j968817333249.114504",
|
||||||
|
"results_file": "/home/ssm-user/.ansible_async/j968817333249.114504", "_ansible_suppress_tmpdir_delete": true
|
||||||
|
}\r\r
|
||||||
|
pattern: >-
|
||||||
|
{{ '"failed": 0, "started": 1, "finished": 0' | regex_escape() }}
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
first_letter_to_uppercase: "{{ 'all_lowercase' | capitalize }}"
|
first_letter_to_uppercase: "{{ 'all_lowercase' | capitalize }}"
|
||||||
something_replaced: "{{ 'dots.to.dashes' | replace('.','-') }}"
|
something_replaced: "{{ 'dots.to.dashes' | replace('.','-') }}"
|
||||||
split_string: "{{ 'testMe@example.com' | split('@') | first }}"
|
split_string: "{{ 'testMe@example.com' | split('@') | first }}"
|
||||||
pattern_replaced: >-
|
pattern_replaced: >-
|
||||||
{{ '*.domain.com...' | regex_replace('*' | regex_escape, 'star') | regex_replace('\.+$', '') }}
|
{{ '*.domain.com...' | regex_replace('*' | regex_escape, 'star') | regex_replace('\.+$', '') }}
|
||||||
|
pattern_is_anywhere_in_module_output: "{{ module_output is search(pattern) }}"
|
||||||
|
pattern_is_at_the_beginning_of_string: "{{ 'sator arepo tenet opera rotas' is match('sator arepo') }}"
|
||||||
|
regex_is_anywhere_in_string: "{{ 'sator arepo tenet opera rotas' is regex('\\stenet\\s') }}"
|
||||||
|
first_substr_matching_regex: "{{ 'sator arepo tenet opera rotas' | regex_search('\\stenet\\s') }}"
|
||||||
|
value_from_json_string_in_module_output: >-
|
||||||
|
{{ 'ansible_job_id' | extract(module_output | regex_search('{.*}') | from_json) }}
|
||||||
- name: Manipulate lists
|
- name: Manipulate lists
|
||||||
tags: list_manipulation
|
tags: list_manipulation
|
||||||
block:
|
block:
|
||||||
@@ -307,6 +321,33 @@
|
|||||||
- name: This always executes
|
- name: This always executes
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg: I always execute
|
msg: I always execute
|
||||||
|
- name: Long-running tasks
|
||||||
|
tags: long-running
|
||||||
|
vars:
|
||||||
|
ansible_async_dir: /tmp/.ansible/async # defaults to '~/.ansible_async'
|
||||||
|
block:
|
||||||
|
- name: Long-running task with integrated poll
|
||||||
|
tags: async_with_self_poll
|
||||||
|
ansible.builtin.command: /bin/sleep 15
|
||||||
|
changed_when: false
|
||||||
|
async: 45 # run max 45s
|
||||||
|
poll: 5 # check once every 5s
|
||||||
|
- name: Long-running task with external poll
|
||||||
|
tags: async_with_external_poll
|
||||||
|
block:
|
||||||
|
- name: Long-running task with external poll
|
||||||
|
ansible.builtin.command: /bin/sleep 15
|
||||||
|
changed_when: false
|
||||||
|
async: 45 # run max 45s
|
||||||
|
poll: 0 # fire and forget
|
||||||
|
register: long_running_task_with_external_poll
|
||||||
|
- name: Check on long_running_task_with_external_poll
|
||||||
|
ansible.builtin.async_status:
|
||||||
|
jid: "{{ long_running_task_with_external_poll.ansible_job_id }}"
|
||||||
|
register: job_result
|
||||||
|
until: job_result.finished
|
||||||
|
retries: 9
|
||||||
|
delay: 5
|
||||||
|
|
||||||
- name: Debugging
|
- name: Debugging
|
||||||
tags:
|
tags:
|
||||||
@@ -489,12 +530,36 @@
|
|||||||
- '!all'
|
- '!all'
|
||||||
- min
|
- min
|
||||||
check_mode: true
|
check_mode: true
|
||||||
tasks:
|
tasks: # ordered alphabetically by name
|
||||||
|
- name: Add authorized keys
|
||||||
|
become: true
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: ansible
|
||||||
|
key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI0123456789abcdefghijkl/ABCDEFGHIJKL01234567 ansible@example.org
|
||||||
|
- name: Add repositories
|
||||||
|
block:
|
||||||
|
- name: To DNF/YUM
|
||||||
|
when: ansible_pkg_mgr | lower in [ 'dnf', 'yum' ]
|
||||||
|
ansible.builtin.yum_repository:
|
||||||
|
name: epel
|
||||||
|
description: EPEL YUM repo
|
||||||
|
baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
|
||||||
|
- name: Add users to the sudoers
|
||||||
|
become: true
|
||||||
|
community.general.sudoers:
|
||||||
|
name: ansible
|
||||||
|
user: ansible
|
||||||
|
nopassword: true
|
||||||
|
commands: ALL
|
||||||
- name: Create directories recursively
|
- name: Create directories recursively
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /tmp/path/to/final/dir
|
path: /tmp/path/to/final/dir
|
||||||
state: directory
|
state: directory
|
||||||
mode: '0775'
|
mode: '0775'
|
||||||
|
- name: Create users
|
||||||
|
become: true
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: ansible
|
||||||
- name: Define files content in tasks
|
- name: Define files content in tasks
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
dest: "{{ ansible_user_dir }}/.tmux.conf"
|
dest: "{{ ansible_user_dir }}/.tmux.conf"
|
||||||
@@ -511,14 +576,6 @@
|
|||||||
- name: Look for files
|
- name: Look for files
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
path_list_of_all_txt_files_in_dir: "{{ lookup('ansible.builtin.fileglob', '/my/path/*.txt') }}"
|
path_list_of_all_txt_files_in_dir: "{{ lookup('ansible.builtin.fileglob', '/my/path/*.txt') }}"
|
||||||
- name: Add repositories
|
|
||||||
block:
|
|
||||||
- name: To DNF/YUM
|
|
||||||
when: ansible_pkg_mgr | lower in [ 'dnf', 'yum' ]
|
|
||||||
ansible.builtin.yum_repository:
|
|
||||||
name: epel
|
|
||||||
description: EPEL YUM repo
|
|
||||||
baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/
|
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
block:
|
block:
|
||||||
- name: Via package manager on any supported system
|
- name: Via package manager on any supported system
|
||||||
@@ -580,6 +637,27 @@
|
|||||||
text:
|
text:
|
||||||
type: mrkdwn
|
type: mrkdwn
|
||||||
text: This is a *_fancy_* message
|
text: This is a *_fancy_* message
|
||||||
|
- name: Setup cronjobs
|
||||||
|
block:
|
||||||
|
- name: At specific times
|
||||||
|
# Mind this is based on the *hosts'* time.
|
||||||
|
become: true
|
||||||
|
ansible.builtin.cron:
|
||||||
|
name: Prometheus manual data backup
|
||||||
|
cron_file: prometheus-manual-data-backup
|
||||||
|
hour: 4
|
||||||
|
minute: 0
|
||||||
|
user: root
|
||||||
|
job:
|
||||||
|
# - Keep '%' characters escaped or they'll be treated as newlines.
|
||||||
|
# - Archive creation returns 1 if it detects changes to read files.
|
||||||
|
# Using ';' instead of '&&' to ignore.
|
||||||
|
>
|
||||||
|
FILENAME="/tmp/prometheus-data-$(date +'\%s-\%F-\%H-\%m-\%S').tar.gz"
|
||||||
|
&& tar -czf "$FILENAME" '/var/lib/prometheus/data'
|
||||||
|
; tar -tf "$FILENAME" > '/dev/null'
|
||||||
|
&& aws s3 cp "$FILENAME" 's3://backups/prometheus/'
|
||||||
|
&& rm "$FILENAME"
|
||||||
- name: Use the users' home directory for something
|
- name: Use the users' home directory for something
|
||||||
block:
|
block:
|
||||||
- name: Executing commands from specified users
|
- name: Executing commands from specified users
|
||||||
@@ -640,27 +718,6 @@
|
|||||||
state: touch
|
state: touch
|
||||||
mode: '0755'
|
mode: '0755'
|
||||||
with_dict: "{{ users_info }}"
|
with_dict: "{{ users_info }}"
|
||||||
- name: Cronjobs
|
|
||||||
block:
|
|
||||||
- name: At specific times
|
|
||||||
become: true
|
|
||||||
ansible.builtin.cron:
|
|
||||||
name: Prometheus manual data backup
|
|
||||||
cron_file: prometheus-manual-data-backup
|
|
||||||
# Mind this is based on the hosts' time.
|
|
||||||
hour: 4
|
|
||||||
minute: 0
|
|
||||||
user: root
|
|
||||||
job:
|
|
||||||
# - Keep '%' characters escaped or they'll be treated as newlines.
|
|
||||||
# - Archive creation returns 1 if it detects changes to read files.
|
|
||||||
# Using ';' instead of '&&' to ignore.
|
|
||||||
>
|
|
||||||
FILENAME="/tmp/prometheus-data-$(date +'\%s-\%F-\%H-\%m-\%S').tar.gz"
|
|
||||||
&& tar -czf "$FILENAME" '/var/lib/prometheus/data'
|
|
||||||
; tar -tf "$FILENAME" > '/dev/null'
|
|
||||||
&& aws s3 cp "$FILENAME" 's3://backups/prometheus/'
|
|
||||||
&& rm "$FILENAME"
|
|
||||||
|
|
||||||
- name: AWS-specific operations
|
- name: AWS-specific operations
|
||||||
tags: never
|
tags: never
|
||||||
@@ -671,12 +728,13 @@
|
|||||||
tasks:
|
tasks:
|
||||||
- name: Apply roles on different targets than the current one
|
- name: Apply roles on different targets than the current one
|
||||||
block: []
|
block: []
|
||||||
# - name: Gather facts about the EC2 instance
|
# - name: Gather facts about the target EC2 instance
|
||||||
# when: instance_information.instance_ids | length > 0
|
# when: instance_information.instance_ids | length > 0
|
||||||
# delegate_to: "{{ instance_information.instance_ids | first }}"
|
# delegate_to: "{{ instance_information.instance_ids | first }}"
|
||||||
# vars:
|
# vars:
|
||||||
# ansible_connection: aws_ssm
|
# ansible_connection: aws_ssm
|
||||||
# ansible_python_interpreter: /usr/bin/python3
|
# ansible_python_interpreter: /usr/bin/python3
|
||||||
|
# ansible_remote_tmp: /tmp/.ansible-ssm-user/tmp
|
||||||
# ansible.builtin.gather_facts: {}
|
# ansible.builtin.gather_facts: {}
|
||||||
# register: fact_gathering
|
# register: fact_gathering
|
||||||
# - name: Apply the role to the EC2 instance
|
# - name: Apply the role to the EC2 instance
|
||||||
@@ -689,6 +747,8 @@
|
|||||||
# ansible_connection: aws_ssm
|
# ansible_connection: aws_ssm
|
||||||
# ansible_aws_ssm_timeout: 900
|
# ansible_aws_ssm_timeout: 900
|
||||||
# ansible_python_interpreter: /usr/bin/python3
|
# ansible_python_interpreter: /usr/bin/python3
|
||||||
|
# ansible_remote_tmp: /tmp/.ansible-ssm-user/tmp
|
||||||
|
# ansible_async_dir: /tmp/.ansible-ssm-user/async
|
||||||
# some_role_var: some value
|
# some_role_var: some value
|
||||||
# some_other_role_var: some value
|
# some_other_role_var: some value
|
||||||
# ansible.builtin.import_role:
|
# ansible.builtin.import_role:
|
||||||
@@ -722,7 +782,7 @@
|
|||||||
resource: i-xyzxyz01
|
resource: i-xyzxyz01
|
||||||
tags:
|
tags:
|
||||||
MyNewTag: value
|
MyNewTag: value
|
||||||
- name: EC2
|
- name: EC2-specific operations
|
||||||
block:
|
block:
|
||||||
- name: Get running instances with 'K8S' as the 'Application' tag
|
- name: Get running instances with 'K8S' as the 'Application' tag
|
||||||
amazon.aws.ec2_instance_info:
|
amazon.aws.ec2_instance_info:
|
||||||
@@ -754,7 +814,49 @@
|
|||||||
instance_type: "{{ source_instance_info.instances[0].instance_type }}"
|
instance_type: "{{ source_instance_info.instances[0].instance_type }}"
|
||||||
image:
|
image:
|
||||||
id: "{{ source_ami.image_id }}"
|
id: "{{ source_ami.image_id }}"
|
||||||
- name: RDS
|
- name: Long-running tasks via SSM
|
||||||
|
block:
|
||||||
|
- name: Dump a DB from an RDS instance to a temporary file
|
||||||
|
when: rds_instance.endpoint is defined
|
||||||
|
vars:
|
||||||
|
ansible_connection: community.aws.aws_ssm
|
||||||
|
ansible_remote_tmp: /tmp/.ansible-ssm-user/tmp
|
||||||
|
ansible_async_dir: /tmp/.ansible-ssm-user/async
|
||||||
|
wanted_pattern_in_module_output: >-
|
||||||
|
{{ '"failed": 0, "started": 1, "finished": 0' | regex_escape() }}
|
||||||
|
community.postgresql.postgresql_db:
|
||||||
|
login_host: "{{ rds_instance.endpoint.address }}"
|
||||||
|
login_port: "{{ rds_instance.endpoint.port }}"
|
||||||
|
login_user: "{{ rds_instance.master_username }}"
|
||||||
|
login_password: "{{ db_password }}"
|
||||||
|
name: sales
|
||||||
|
state: dump
|
||||||
|
target: "{{ temp_file_for_dump.path }}"
|
||||||
|
target_opts: >-
|
||||||
|
--exclude-table …
|
||||||
|
--exclude-schema archived
|
||||||
|
--no-publications
|
||||||
|
--format c
|
||||||
|
async: "{{ 60 * 60 * 2 }}" # wait up to 2 hours
|
||||||
|
poll: 0 # fire and forget, since it would not check anyways
|
||||||
|
register: dump
|
||||||
|
changed_when:
|
||||||
|
- dump.rc == 0
|
||||||
|
- dump.module_stderr == ''
|
||||||
|
- "'started' | extract(dump.module_stdout | regex_search('{.*}') | from_json) == 1"
|
||||||
|
- "'failed' | extract(dump.module_stdout | regex_search('{.*}') | from_json) == 0"
|
||||||
|
failed_when: dump.rc != 0
|
||||||
|
- name: Check on the dump task
|
||||||
|
vars:
|
||||||
|
dump_stdout_as_obj: "{{ dump.module_stdout | regex_search('{.*}') | from_json }}"
|
||||||
|
ansible_job_id: "{{ dump_stdout_as_obj.ansible_job_id }}"
|
||||||
|
ansible.builtin.async_status:
|
||||||
|
jid: "{{ ansible_job_id }}"
|
||||||
|
register: dump_result
|
||||||
|
until: dump_result.finished
|
||||||
|
retries: "{{ 60 * 2 }}"
|
||||||
|
delay: 60
|
||||||
|
- name: RDS-specific operations
|
||||||
block:
|
block:
|
||||||
- name: Create an instance's snapshot
|
- name: Create an instance's snapshot
|
||||||
block:
|
block:
|
||||||
@@ -830,6 +932,8 @@
|
|||||||
ansible_aws_ssm_bucket_name: company-ssm-logs
|
ansible_aws_ssm_bucket_name: company-ssm-logs
|
||||||
ansible_aws_ssm_region: eu-west-1
|
ansible_aws_ssm_region: eu-west-1
|
||||||
ansible_aws_ssm_timeout: 900
|
ansible_aws_ssm_timeout: 900
|
||||||
|
ansible_remote_tmp: /tmp/.ansible-ssm-user/tmp
|
||||||
|
ansible_async_dir: /tmp/.ansible-ssm-user/async
|
||||||
tasks:
|
tasks:
|
||||||
- name: Start the PG dumper instance
|
- name: Start the PG dumper instance
|
||||||
tags: dumper
|
tags: dumper
|
||||||
|
|||||||
Reference in New Issue
Block a user