Automating Ubuntu 22.04 file and Docker server with Ansible
Introduction
In the realm of IT automation, Ansible stands out as a powerful and user-friendly tool. It simplifies complex configuration tasks, manages server deployments, and orchestrates multi-tier application environments with ease. Ansible’s agentless nature, where no software needs to be installed on the managed nodes, makes it a preferred choice for system administrators and DevOps professionals.
The purpose of this guide is to introduce Ansible to macOS users looking to automate and streamline operations on remote Linux servers, specifically Ubuntu 22.04. Whether you’re managing a single server or an entire data center, Ansible can help you automate the mundane, repetitive tasks, allowing you to focus on more strategic initiatives.
By the end of this tutorial, you will have learned how to set up Ansible on a macOS machine, configure SSH access to a remote Ubuntu server, and run Ansible playbooks to automate tasks. This guide is designed to be a comprehensive starting point for macOS users new to Ansible, providing the necessary steps to get up and running with this powerful automation tool.
This introduction sets the stage for the tutorial, explaining what Ansible is, why it’s useful, and what the reader will achieve by following your guide. It’s aimed at macOS users who are beginners with Ansible, guiding them through the process of managing a remote Ubuntu server.
Configuring Ansible on macOS
To harness the power of Ansible for automating tasks on your Ubuntu server, you first need to set up Ansible on your macOS system. This section guides you through the installation and configuration process, ensuring a seamless connection between your macOS machine and the Ubuntu server.
Installation
- Install Homebrew: If you haven’t already installed Homebrew, the package manager for macOS, you can do so by executing the following command in your terminal:
1
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Install Ansible: With Homebrew installed, you can easily install Ansible by running the following command:
1
brew install ansible
This command downloads and installs the latest version of Ansible, along with its dependencies.
Configuration
- Create a Project Directory: Organize your Ansible projects by creating a dedicated directory:
1
mkdir ~/ansible-projects && cd ~/ansible-projects
- Configure SSH Access: Ansible uses SSH to communicate with remote servers. Ensure you have an SSH key pair on your macOS machine. If you don’t have one, generate it using
ssh-keygen
and then copy the public key to your Ubuntu server:
1
2
ssh-keygen -t ed25519 -C "your_email@example.com"
ssh-copy-id user@your-ubuntu-server-ip
Replace your_email@example.com
with your email and user@your-ubuntu-server-ip
with your actual server login and IP address.
- Create an Inventory File: Ansible needs to know about the servers it manages. Create an inventory file named
hosts
:
1
touch hosts
Edit this file using a text editor and add your server details under a group, for example:
1
2
[ubuntu_servers]
your-ubuntu-server-ip ansible_user=user ansible_ssh_private_key_file=~/.ssh/id_ed25519
Replace your-ubuntu-server-ip
and user
with your server’s IP address and username. The ansible_ssh_private_key_file
should point to your private key file.
- Test the Connection: Ensure Ansible can connect to your Ubuntu server by running the following command:
1
ansible -i hosts ubuntu_servers -m ping
If everything is set up correctly, you should receive a SUCCESS
message.
Configuring SSH for Ansible
To enable Ansible to manage your remote servers seamlessly, you must configure SSH properly. This ensures secure and efficient communication between your local machine and the Ubuntu server. Here’s how to set up SSH for Ansible use:
Generating an SSH Key Pair
- Open Terminal: Launch the Terminal application on your macOS.
- Generate SSH Key: Run the following command to create a new SSH key pair. If you already have an SSH key and want to use it, you can skip this step.
1
ssh-keygen -t ed25519 -C "your_email@example.com"
Replace "your_email@example.com"
with your email address. This command generates a new SSH key, using the provided email as a label.
- Save the Key: When prompted to “Enter a file in which to save the key,” press Enter to accept the default file location.
Copying the SSH Public Key to the Ubuntu Server
- Copy Public Key: Use the
ssh-copy-id
command to copy your public SSH key to the Ubuntu server. This facilitates password-less SSH login.
1
ssh-copy-id user@your-ubuntu-server-ip
Replace user
with your username on the Ubuntu server and your-ubuntu-server-ip
with the server’s IP address.
Testing the SSH Connection
- SSH to the Server: Try logging into your server via SSH to ensure the key-based authentication works:
1
ssh user@your-ubuntu-server-ip
If successful, you should log in without being prompted for a password.
Configuring SSH for Ansible
- Ansible SSH Settings: Ensure Ansible uses the correct SSH settings by editing the
ansible.cfg
file or defining necessary parameters in your inventory file, like so:
1
2
[ubuntu_servers]
your-ubuntu-server-ip ansible_user=user ansible_ssh_private_key_file=~/.ssh/id_ed25519
This configuration tells Ansible to use the specified user and SSH private key when connecting to the Ubuntu server.
- Parallelism and Performance: Adjust the SSH settings in
ansible.cfg
to improve performance, such as increasingforks
for parallelism and enablingControlPersist
to keep SSH connections open.
By configuring SSH correctly, you ensure that Ansible can securely and efficiently execute tasks on the remote server without manual password entry. This setup is essential for automating server management tasks with Ansible playbooks.
Setting Up Ansible Inventory
An Ansible inventory is a file that details the hosts and groups of hosts upon which commands, modules, and tasks in a playbook operate. For Ansible to automate a server, it must be defined in the inventory file. Here’s how to set it up:
- Create the Inventory File: An inventory file can be named anything, but by default, Ansible looks for
hosts
in the/etc/ansible/
directory. However, on macOS, it’s common to store this file in a project-specific directory or under~/ansible-projects/
. We’ve previously created this file, but in case you haven’t, here it is again.
1
2
cd ~/ansible-projects
touch hosts
- Define Your Servers: In the inventory file, you can define individual servers or groups of servers under a bracketed header. For example, to define a group named
ubuntu_servers
, you might add:
1
2
3
[ubuntu_servers]
ubuntu1 ansible_host=192.168.1.100
ubuntu2 ansible_host=192.168.1.101
Replace 192.168.1.100
and 192.168.1.101
with the IP addresses of your actual Ubuntu servers.
- Specify Connection Details: If your servers require specific user names or private keys to connect, you can specify these in the inventory file:
1
2
3
[ubuntu_servers]
ubuntu1 ansible_host=192.168.1.100 ansible_user=myuser ansible_ssh_private_key_file=/path/to/private/key
ubuntu2 ansible_host=192.168.1.101 ansible_user=myuser ansible_ssh_private_key_file=/path/to/private/key
Replace myuser
and /path/to/private/key
with the username and path to the SSH private key you use to connect to these servers.
- Organize with Groups: For more complex setups, you can organize hosts into groups and even have groups of groups. This organization can be beneficial for targeting specific subsets of servers with your Ansible playbooks.
1
2
3
4
5
6
7
8
9
10
[web_servers]
ubuntu1
ubuntu2
[db_servers]
ubuntu3
[all_servers:children]
web_servers
db_servers
- Use Variables: You can define variables within the inventory file that apply to individual hosts or groups:
1
2
[ubuntu_servers:vars]
ansible_python_interpreter=/usr/bin/python3
This sets the Python interpreter for all hosts in the ubuntu_servers
group.
- Testing Your Inventory: Validate your inventory setup by pinging the hosts using the
ansible
command:
1
ansible -i hosts ubuntu_servers -m ping
A successful response indicates that Ansible can connect to the specified hosts.
By setting up the inventory file correctly, you ensure Ansible knows where and how to execute the tasks defined in your playbooks, paving the way for seamless automation of your server management tasks.
Creating Your First Ansible Playbook
A playbook in Ansible is a YAML file containing a series of procedures that the automation tool will execute on the configured servers. To illustrate, we’ll create a playbook that automates the installation and configuration of MergerFS, SnapRAID, SSMTP, Docker, and Docker-Compose on an Ubuntu 22.04 server.
- Create the Playbook File: Begin by creating a new YAML file for the playbook. For instance:
1
2
cd ~/ansible-projects
touch server-setup.yml
- Define Playbook Structure: Open
server-setup.yml
in a text editor and start defining the playbook structure with hosts, become, and tasks sections:
1
2
3
4
5
---
- name: Setup Ubuntu server
hosts: ubuntu_servers
become: true
tasks:
name
: Descriptive name of the play.hosts
: Specifies the group of hosts from the inventory file.become
: Enables privilege escalation (sudo).
- Install and Configure MergerFS and SnapRAID:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- name: Install MergerFS and SnapRAID dependencies
apt:
name: [build-essential, git, automake, autoconf, lzop, libfuse-dev, libattr1-dev]
state: present
update_cache: true
- name: Clone and compile MergerFS
git:
repo: 'https://github.com/trapexit/mergerfs.git'
dest: '/tmp/mergerfs'
command: make install
args:
chdir: /tmp/mergerfs
- name: Clone and compile SnapRAID
git:
repo: 'https://github.com/amadvance/snapraid.git'
dest: '/tmp/snapraid'
command: |
./autogen.sh
./configure
make
make install
args:
chdir: /tmp/snapraid
- Install and Configure SSMTP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- name: Install SSMTP
apt:
name: ssmtp
state: latest
- name: Configure SSMTP
copy:
dest: /etc/ssmtp/ssmtp.conf
content: |
root=user@gmail.com
mailhub=smtp.gmail.com:587
AuthUser=user@gmail.com
AuthPass=password
UseTLS=YES
UseSTARTTLS=YES
- Install Docker and Docker-Compose:
The below will set up the repository for Docker and download the latest version of docker and docker-compose. It also creates two docker networks: proxy and socket_proxy for use with your containers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- name: Docker installation and configuration
block:
- name: Add Docker's official GPG key
ansible.builtin.shell:
cmd: curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
- name: Set up the Docker stable repository
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/docker.list
content: |
deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu jammy stable
owner: root
group: root
mode: '0644'
- name: Install Docker CE
apt:
name: docker-ce
state: present
update_cache: true
- name: Install Docker Compose
ansible.builtin.shell:
cmd: >
curl -s https://api.github.com/repos/docker/compose/releases/latest |
grep browser_download_url | grep docker-compose-linux-x86_64 |
cut -d '"' -f 4 | wget -qi -
&& chmod +x docker-compose-linux-x86_64
&& mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose
- name: Create Docker networks
block:
- name: Create proxy network
community.docker.docker_network:
name: proxy
state: present
- name: Create socket_proxy network
community.docker.docker_network:
name: socket_proxy
state: present
- Running the Playbook: Save the file and execute it with the
ansible-playbook
command:
1
ansible-playbook -i hosts server-setup.yml
This playbook serves as a foundational example. You can expand it with more tasks, roles, handlers, and variables to fit the complexities of your infrastructure. With Ansible, you’re empowered to automate the setup and configuration of your servers efficiently and consistently.
Understanding Playbook Output
When you run an Ansible playbook, the console output provides detailed information about what Ansible is doing. Here’s how to understand the key parts of that output.
- Play and Task Names: At the start of the output, Ansible lists the play and tasks that it will execute. Each task will show its name, providing clarity on what Ansible is currently working on.
- Task Status: Each task will end with a status indicator:
ok
: The task completed successfully, and no changes were made to the host.changed
: The task completed successfully, and some changes were made to the host.failed
: The task did not complete successfully. If a task fails, Ansible will stop executing the rest of the playbook on that host.
- Host Summary: At the end of the playbook run, Ansible provides a summary for each host, showing how many tasks were ok, changed, or failed.
- Play Recap: This section summarizes the entire playbook execution, showing the total number of tasks that were ok, changed, failed, or skipped across all hosts.
Here’s an example of what the output might look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PLAY [Setup Ubuntu server] *****************************************************
TASK [Gathering Facts] *********************************************************
ok: [ubuntu_server]
TASK [Install MergerFS and SnapRAID dependencies] ******************************
changed: [ubuntu_server]
TASK [Clone and compile MergerFS] **********************************************
changed: [ubuntu_server]
TASK [Clone and compile SnapRAID] **********************************************
changed: [ubuntu_server]
TASK [Install SSMTP] ***********************************************************
changed: [ubuntu_server]
TASK [Configure SSMTP] *********************************************************
changed: [ubuntu_server]
TASK [Install Docker] **********************************************************
changed: [ubuntu_server]
TASK [Install Docker-Compose] **************************************************
changed: [ubuntu_server]
PLAY RECAP *********************************************************************
ubuntu_server : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
- ok=8: Eight tasks ran successfully without needing to make any changes.
- changed=7: Seven tasks made changes to the system.
- unreachable=0: No hosts were unreachable.
- failed=0: No tasks failed.
- skipped=0: No tasks were skipped.
Understanding the playbook output is crucial for diagnosing issues and verifying that your configuration changes have been applied as expected.
Best Practices and Tips
When working with Ansible, there are several best practices and tips you can follow to ensure your automation is efficient, secure, and reliable.
Organizing Playbooks and Inventory
- Directory Structure: Maintain a clear directory structure where playbooks, roles, inventory files, and other components are organized in a logical manner. This makes managing your Ansible project easier and more intuitive.
- Use of Roles: Utilize roles to break down complex playbooks into reusable sections. Roles can be used to group related tasks, variables, files, and templates, making your playbooks more modular and manageable.
- Inventory Management: Keep your inventory file(s) organized. Use groups to categorize hosts logically, such as by environment (
prod
,dev
,test
) or function (web
,db
,cache
). This simplification aids in targeting the correct hosts during playbook runs.
Security Considerations
- Ansible Vault: Use Ansible Vault to encrypt sensitive data, such as passwords, secret keys, and other credentials. This ensures that sensitive data is not exposed in your playbook or inventory files.
- Minimum Privilege: Ensure that the user Ansible uses to connect to remote machines has the minimum required privileges for the tasks it needs to perform. Use
become
only when necessary. - Secure SSH: Use SSH keys instead of passwords for Ansible to authenticate to remote servers, and protect these keys with strong passphrases.
Testing and Validation of Playbooks
- Syntax Check: Use
ansible-playbook --syntax-check
to validate the syntax of your playbooks before running them. - Dry Run: Perform a “dry run” using the
--check
flag. This executes the playbook without making any actual changes, allowing you to validate logic and task order. - Incremental Deployment: Test playbooks in a controlled environment before deploying to production. Consider using a staging environment that mirrors production to catch potential issues.
- Version Control: Use a version control system like Git to manage your Ansible code. This allows you to track changes, collaborate with others, and revert to previous versions if something goes wrong.
By adhering to these best practices, you can create more effective and secure automation routines with Ansible, ensuring smoother deployments and operations.
My Playbook
Here is the current monolithic version of my playbook (not broken out into smaller playbooks) to give you an example of a automatically setting up and configuring many common aspects of a fileserver with Docker.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
---
- name: Setup Ubuntu server
hosts: loki_fileserver
become: true
vars:
user_name: zack
data_disks:
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk01', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk02', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk03', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk04', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk05', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk06', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk07', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk08', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk09', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk10', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk11', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk12', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk13', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk14', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk15', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk16', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk17', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk18', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk19', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk20', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/data/disk21', options: 'defaults,noatime,errors=remount-ro' }
parity_disks:
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/parity/parity1', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/parity/parity2', options: 'defaults,noatime,errors=remount-ro' }
- { id: 'scsi-hard_disk_id_here-part1', mount: '/disks/parity/parity3', options: 'defaults,noatime,errors=remount-ro' }
mergerfs_mount: '/storage'
tasks:
- name: Include secrets
include_vars:
file: secrets.yml
name: secrets
- name: Update and install dependencies
apt:
name:
- git
- tmux
- htop
- nmon
- build-essential
- samba
- powertop
- mutt
- ssmtp
- python3-pip
- libssl-dev
- libffi-dev
- python3-dev
- ca-certificates
- curl
- gnupg
- lsb-release
- fzf
- apt-transport-https
- software-properties-common
- qemu-guest-agent
- linux-image-generic-hwe-22.04
- ocl-icd-libopencl1
- intel-gpu-tools
state: latest
update_cache: true
- name: Update and upgrade system packages
become: true # Ensure you have root privileges
apt:
update_cache: yes # equivalent to running `apt update`
upgrade: dist # equivalent to running `apt upgrade`
- name: Reduce GRUB timeout
block:
- name: Set GRUB timeout to 5 seconds
ansible.builtin.lineinfile:
path: /etc/default/grub
regexp: '^GRUB_TIMEOUT='
line: 'GRUB_TIMEOUT=5'
backrefs: true
- name: Update GRUB
ansible.builtin.shell:
cmd: update-grub
- name: Install the GPG signing key for Kopia
ansible.builtin.shell:
cmd: curl -s https://kopia.io/signing-key | gpg --dearmor -o /etc/apt/keyrings/kopia-keyring.gpg
- name: Register APT source for Kopia
ansible.builtin.lineinfile:
path: /etc/apt/sources.list.d/kopia.list
line: 'deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main'
create: true
- name: Update APT package index
ansible.builtin.apt:
update_cache: true
- name: Install Kopia
ansible.builtin.apt:
name: kopia
state: latest
- name: Docker installation and configuration
block:
- name: Add Docker's official GPG key
ansible.builtin.shell:
cmd: curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
- name: Set up the Docker stable repository
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/docker.list
content: |
deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu jammy stable
owner: root
group: root
mode: '0644'
- name: Install Docker CE
apt:
name: docker-ce
state: present
update_cache: true
- name: Install Docker Compose
ansible.builtin.shell:
cmd: >
curl -s https://api.github.com/repos/docker/compose/releases/latest |
grep browser_download_url | grep docker-compose-linux-x86_64 |
cut -d '"' -f 4 | wget -qi -
&& chmod +x docker-compose-linux-x86_64
&& mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose
- name: Create Docker networks
block:
- name: Create proxy network
community.docker.docker_network:
name: proxy
state: present
- name: Create socket_proxy network
community.docker.docker_network:
name: socket_proxy
state: present
become: true
- name: Ensure /etc/apt/keyrings directory exists
file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Add the GPG key for eza
ansible.builtin.shell:
cmd: wget -qO- https://raw.githubusercontent.com/eza-community/eza/main/deb.asc | gpg --dearmor -o /etc/apt/keyrings/gierens.gpg
- name: Add eza repository
ansible.builtin.lineinfile:
path: /etc/apt/sources.list.d/gierens.list
line: 'deb [signed-by=/etc/apt/keyrings/gierens.gpg] http://deb.gierens.de stable main'
create: yes
- name: Set correct permissions for eza repository files
file:
path: "{{ item }}"
mode: '0644'
loop:
- /etc/apt/keyrings/gierens.gpg
- /etc/apt/sources.list.d/gierens.list
- name: Update apt cache
ansible.builtin.apt:
update_cache: yes
- name: Install eza
apt:
name: eza
state: present
- name: Install Intel Compute Runtime
block:
- name: Create temporary directory for downloading Intel packages
ansible.builtin.file:
path: /tmp/neo
state: directory
- name: Download Intel Compute Runtime packages
get_url:
url: "{{ item }}"
dest: /tmp/neo/
loop:
- https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.15985.7/intel-igc-core_1.0.15985.7_amd64.deb
- https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.15985.7/intel-igc-opencl_1.0.15985.7_amd64.deb
- https://github.com/intel/compute-runtime/releases/download/24.05.28454.6/intel-level-zero-gpu-dbgsym_1.3.28454.6_amd64.ddeb
- https://github.com/intel/compute-runtime/releases/download/24.05.28454.6/intel-level-zero-gpu_1.3.28454.6_amd64.deb
- https://github.com/intel/compute-runtime/releases/download/24.05.28454.6/intel-opencl-icd-dbgsym_24.05.28454.6_amd64.ddeb
- https://github.com/intel/compute-runtime/releases/download/24.05.28454.6/intel-opencl-icd_24.05.28454.6_amd64.deb
- https://github.com/intel/compute-runtime/releases/download/24.05.28454.6/libigdgmm12_22.3.11_amd64.deb
- name: Install downloaded Intel packages
ansible.builtin.shell:
cmd: dpkg -i /tmp/neo/*.deb /tmp/neo/*.ddeb
- name: Remove the temporary directory
ansible.builtin.file:
path: /tmp/neo
state: absent
- name: User and authentication setup
block:
- name: Set up passwordless SSH for root
blockinfile:
path: "/root/.ssh/authorized_keys"
block: "{{ secrets.ssh_public_key }}"
create: true
- name: User and authentication setup
block:
- name: Ensure Docker group exists
ansible.builtin.group:
name: docker
state: present
- name: Add user user_name
ansible.builtin.user:
name: "{{ user_name }}"
shell: /bin/bash
createhome: true
- name: Add user_name to the docker group
ansible.builtin.user:
name: "{{ user_name }}"
groups: docker
append: true
- name: SnapRAID and MergerFS setup
block:
- name: Download SnapRAID
get_url:
url: https://github.com/amadvance/snapraid/releases/download/v12.3/snapraid-12.3.tar.gz
dest: /tmp/snapraid-12.3.tar.gz
- name: Extract SnapRAID archive
unarchive:
src: /tmp/snapraid-12.3.tar.gz
dest: /tmp/
remote_src: true
- name: Compile and install SnapRAID
command: "{{ item }}"
loop:
- './configure'
- 'make'
- 'make install'
args:
chdir: "/tmp/snapraid-12.3/"
- name: Download MergerFS package
get_url:
url: https://github.com/trapexit/mergerfs/releases/download/2.40.2/mergerfs_2.40.2.ubuntu-jammy_amd64.deb
dest: "/tmp/mergerfs.deb"
mode: '0755'
- name: Install MergerFS
apt:
deb: "/tmp/mergerfs.deb"
- name: Filesystem and storage configuration
block:
- name: Create mount points for data and parity
file:
path: "{{ item.mount }}"
state: directory
mode: '0755'
loop: "{{ data_disks + parity_disks }}"
- name: Update /etc/fstab with data disks
ansible.builtin.lineinfile:
path: /etc/fstab
line: "#UUID={{ item.id }} {{ item.mount }} ext4 {{ item.options }} 0 2"
state: present
loop: "{{ data_disks }}"
- name: Update /etc/fstab with parity disks
ansible.builtin.lineinfile:
path: /etc/fstab
line: "#UUID={{ item.id }} {{ item.mount }} ext4 {{ item.options }} 0 2"
state: present
loop: "{{ parity_disks }}"
- name: Configure mergerfs in fstab
blockinfile:
path: /etc/fstab
marker: "# {mark} ANSIBLE MANAGED BLOCK mergerfs"
block: |
# /disks/data/* /storage fuse.mergerfs cache.files=partial,dropcacheonclose=true,category.create=mfs,moveonenospc=true,minfreespace=20G,fsname=mergerfsPool,nonempty 0 0
- name: Copy SnapRAID configuration
template:
src: snapraid.conf.j2
dest: /etc/snapraid.conf
owner: root
group: root
mode: '0644'
- name: Create mail spool file
file:
path: /var/mail/root
state: touch
owner: root
group: mail
mode: '0660'
- name: Create /root/Mail directory
become: true
file:
path: /root/Mail
state: directory
owner: root
group: root
mode: '0700'
- name: Ensure /etc/ssmtp directory exists
file:
path: /etc/ssmtp
state: directory
mode: '0755'
- name: Configure ssmtp
blockinfile:
path: /etc/ssmtp/ssmtp.conf
create: true
block: |
root={{ secrets.email_user }}@gmail.com
mailhub=smtp.gmail.com:587
rewriteDomain=gmail.com
hostname=fileserver.local
TLS_CA_FILE=/etc/ssl/certs/ca-certificates.crt
UseTLS=YES
UseSTARTTLS=YES
AuthUser={{ secrets.email_user }}
AuthPass={{ secrets.email_password }}
AuthMethod=LOGIN
FromLineOverride=YES
- name: Set up sSMTP aliases
copy:
dest: /etc/ssmtp/revaliases
content: |
# sSMTP aliases
# Format: local_account:outgoing_address:mailhub
#
# Example: root:your_login@your.domain:mailhub.your.domain[:port]
# where [:port] is an optional port number that defaults to 25.
root:{{ secrets.email_user }}@gmail.com:smtp.gmail.com:587
user_name:{{ secrets.email_user }}@gmail.com:smtp.gmail.com:587
force: yes
mode: '0644'
- name: Samba configuration
block:
- name: Set samba password for user user_name
shell: "(echo '{{ secrets.samba_password }}'; echo '{{ secrets.samba_password }}') | smbpasswd -a {{ user_name }}"
args:
executable: "/bin/bash"
- name: Deploy Samba configuration
template:
src: "smb.conf.j2"
dest: "/etc/samba/smb.conf"
owner: "root"
group: "root"
mode: "0644"
notify:
- restart samba
- name: Configure system utilities
block:
- name: Set up PowerTOP for auto-tuning
block:
- name: Enable PowerTOP auto-tune service
copy:
content: |
[Unit]
Description=PowerTOP tunings
[Service]
Type=oneshot
ExecStart=/usr/sbin/powertop --auto-tune
[Install]
WantedBy=multi-user.target
dest: "/etc/systemd/system/powertop.service"
- name: Start and enable PowerTOP service
systemd:
name: "powertop"
enabled: true
state: started
- name: Install and configure unattended-upgrades
become: true
tasks:
- name: Install unattended-upgrades package
apt:
name: unattended-upgrades
state: latest
update_cache: yes
- name: Enable automatic updates
copy:
dest: /etc/apt/apt.conf.d/20auto-upgrades
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
- name: Configure unattended-upgrades
copy:
dest: /etc/apt/apt.conf.d/50unattended-upgrades
content: |
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
};
Unattended-Upgrade::Package-Blacklist {
};
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
- name: Add cscli alias for CrowdSec to root's .bashrc
lineinfile:
path: "/root/.bashrc"
line: 'alias cscli="docker exec -t crowdsec cscli"'
create: true
- name: CrowdSec setup
block:
- name: Download and execute the installation script for CrowdSec
ansible.builtin.shell: |
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | bash
- name: Install CrowdSec Firewall Bouncer
apt:
name: "crowdsec-firewall-bouncer-iptables"
state: latest
update_cache: true
- name: Configure CrowdSec Firewall Bouncer
template:
src: "crowdsec-firewall-bouncer.yaml.j2"
dest: "/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml"
# - name: Ensure CrowdSec service is running
# systemd:
# name: "crowdsec-firewall-bouncer"
# state: started
# enabled: yes
- name: CrowdSec service management
block:
- name: Create CrowdSec Firewall Bouncer systemd service file
copy:
dest: /etc/systemd/system/crowdsec-firewall-bouncer.service
content: |
[Unit]
Description=The firewall bouncer for CrowdSec
After=syslog.target network.target remote-fs.target nss-lookup.target crowdsec.service docker.service
Before=netfilter-persistent.service
ReloadPropagatedFrom=docker.service
StartLimitIntervalSec=20
[Service]
Type=notify
RemainAfterExit=no
Restart=always
RestartSec=20
ExecStart=/usr/local/bin/crowdsec-firewall-bouncer -c /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
ExecStartPre=/usr/local/bin/crowdsec-firewall-bouncer -c /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml -t
ExecStartPost=/bin/sleep 0.1
[Install]
WantedBy=multi-user.target
- name: Reload systemd daemon to recognize new service
ansible.builtin.systemd:
daemon_reload: true
# - name: Start and enable Crowdsec Firewall Bouncer service
# ansible.builtin.systemd:
# name: crowdsec-firewall-bouncer.service
# state: started
# enabled: yes
- name: Configure iptables for CrowdSec
block:
- name: Ensure /etc/rc.local exists and is executable
file:
path: /etc/rc.local
state: touch
mode: '0755'
- name: Ensure rc.local exists and has correct content
block:
- name: Ensure rc.local exists and is executable
file:
path: /etc/rc.local
state: touch
mode: '0755'
- name: Set content in rc.local
copy:
dest: /etc/rc.local
content: |
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
iptables -I INPUT 1 -m set --match-set crowdsec-blacklists src -j DROP
ip6tables -I INPUT 1 -m set --match-set crowdsec6-blacklists src -j DROP
owner: root
group: root
mode: '0755'
- name: Copy SSH public key for Git
copy:
content: "{{ secrets.public_ssh_key }}" # Your public SSH key here
dest: "/root/.ssh/id_ed25519.pub"
mode: '0644'
owner: root
group: root
- name: Copy SSH private key for Git
copy:
content: "{{ secrets.private_ssh_key }}" # Your private SSH key here
dest: "/root/.ssh/id_ed25519"
mode: '0600'
owner: root
group: root
- name: Clone private repository
ansible.builtin.git:
repo: 'git@github.com:your_user_name/scripts.git'
dest: "/root/scripts"
version: main # or whatever branch you want
key_file: "/root/.ssh/id_ed25519"
accept_hostkey: true
- name: Setup Kopia repository
block:
- name: Connect to Kopia repository
shell: |
kopia repository connect b2 --bucket={{ secrets.kopia_bucket_name }} --key-id={{ secrets.kopia_key_id }} --key={{ secrets.kopia_key }} --password={{ secrets.kopia_password }}
environment:
KOPIA_PASSWORD: "{{ secrets.kopia_password }}"
args:
executable: /bin/bash
- name: Create Kopia snapshot policies
shell: kopia policy set --global --keep-latest=10 --compression=zstd
environment:
KOPIA_PASSWORD: "{{ secrets.kopia_password }}"
- name: Copy root's crontab template
become: true
template:
src: root_crontab.j2
dest: /tmp/root_crontab
owner: root
group: root
mode: '0600'
- name: Apply the new crontab file for root
become: true
command: crontab -u root /tmp/root_crontab
handlers:
- name: restart samba
ansible.builtin.service:
name: smbd
state: restarted