Fabric
Introduction to Fabric#
Official Documentation can be found here: https://docs.fabfile.org/en/stable/
Fabric is a python library that enables more sophisticated server and service automation using Python. It combines multiple Python libraries, including Invoke and Paramiko. This library uses SSH to connect to remote servers. It enables to execute commands on multiple remote servers, and also supports SFTP protocols.
Fabric also has unofficial forks, such as Fabric3. These versions however are not official and not maintained by the original creator. Development process for this library is also slow, as the last version has been released almost a year prior when making this documentation.
Fabric installation#
Install fabric to Ubuntu systems using command pipx install fabric
For this you also will need to install pipx sudo apt install pipx
Check the version with running fab -V, the current version for fabric is 3.2.x
Configuring Fabric#
Because Fabric is a python library, all of the code is handled via Python code snippets. How this works is very similar to what you would use in Paramiko for network configuration. This is granted, as Fabric incorporates Paramiko into the package.
You can start coding with Fabric as you would with any other Python library. First you need to import the necessary tools. These are imported with from Fabric. Although some modules might need to be imported from Invoke or Paramiko.
The most important module is probably the from fabric import Connection module, as it allows you to establish the connection to the remote servers and run commands on them.
Let's create a simple test Fabric file to test connection to Cloudfare DNS called fabtest.py:
from fabric import Connection
c = Connection('ubuntu@ip.here) # Connect to your host, there is also an option to have multiple hosts with Group() function.
c.run('ping 1.1.1.1 -c 4')
and when you run the file it should print the following:
python3 fabtest.py
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=54 time=9.85 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=54 time=9.53 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=54 time=9.64 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=54 time=9.34 ms
For example this is the file to build a new docker environment:
from fabric import Connection
from invoke import UnexpectedExit
c = Connection("ubuntu@x.x.x.x")
LOCAL_FILE = "docker-compose.yml"
REMOTE_PATH = "/home/ubuntu/"
PASS_FILE = "pass_file.txt" # Insert your password for the repository if needed
def copy_files(c):
try:
c.run("rm -f docker-compose.yml")
print(f"Copying {LOCAL_FILE} to {REMOTE_PATH}...")
c.put(LOCAL_FILE, REMOTE_PATH)
print("Docker Compose file copied successfully.")
except UnexpectedExit as e:
print(f"Error while copying files: {e}")
def compose_down(c):
try:
print("Composing down...")
c.run("docker compose down --remove-orphans")
print("Docker stack deployed successfully.")
except UnexpectedExit as e:
print(f"Error while composing down: {e}")
def docker_login(c):
try:
print("Login to repo")
c.put(PASS_FILE, REMOTE_PATH)
c.run("cat ~/pass_file.txt | docker login gitlab.labranet.jamk.fi:xx --username user --password-stdin")
except UnexpectedExit as e:
print(f"Error while logging into the repository: {e}")
def compose_up(c):
try:
print("Deploying Docker stack...")
c.run("docker compose up -d")
print("Docker stack deployed successfully.")
except UnexpectedExit as e:
print(f"Error while composing up: {e}")
def docker_logout(c):
try:
print("Logging out of repository")
c.run("docker logout")
c.run(f"rm -f {PASS_FILE}")
except UnexpectedExit as e:
print(f"Errro while logging out of the repository: {e}")
def main():
copy_files(c)
compose_down(c)
docker_login(c)
compose_up(c)
docker_logout(c)
if __name__ == "__main__":
main()
You can run this with python3 dockerbuild.py
Tasks#
Fabric also supports Tasks, which can be declared in a fabfile.py. Tasks can be imported with from fabric import task. When you declare the functions with @task, the function becomes available to be used with fab command. To list all the available tasks in the fabfile you can use the command fab --list
from fabric import task
from fabric import Connection
from invoke import UnexpectedExit
LOCAL_FILE = "/home/user/docker-compose.yml"
REMOTE_PATH = "/home/ubuntu/"
PASS_FILE = "pass_file.txt" # Insert your password for the repository if needed
@task
def copy_files(c):
try:
print(f"Copying {LOCAL_FILE} to {REMOTE_PATH}...")
c.put(LOCAL_FILE, REMOTE_PATH)
print("Docker Compose file copied successfully.")
except UnexpectedExit as e:
print(f"Error while copying files: {e}")
@task
def compose_up(c):
try:
print("Deploying Docker stack...")
c.run("docker compose up -d")
print("Docker stack deployed successfully.")
except UnexpectedExit as e:
print(f"Error while composing up: {e}")
@task
def compose_down(c):
try:
print("Composing down...")
c.run("docker compose down --remove-orphans")
print("Docker stack composed down successfully.")
except UnexpectedExit as e:
print(f"Error while composing down: {e}")
@task
def docker_login(c):
try:
print("Login to repo")
c.put(PASS_FILE, REMOTE_PATH)
c.run("cat ~/pass_file.txt | docker login gitlab.labranet.jamk.fi:xx --username user --password-stdin")
c.run("rm -f {}".format(PASS_FILE))
except UnexpectedExit as e:
print(f"Error while logging into the repository: {e}")
@task
def docker_logout(c):
try:
print("Log out from repo")
c.run("docker logout")
except UnexpectedExit as e:
print(f"Errro while logging out of the repository: {e}")
After this you can see these functions become available with the fab --list command.
fab --list
Available tasks:
compose-down
compose-up
copy-files
docker-login
docker-logout
Let's try to use the function compose-down:
fab compose-down -H ubuntu@x.x.x.x # You need to specify the host with -H . You can run this task against multiple hosts just by adding more hosts to the list, like -H host1,host2
Composing down...
Container ubuntu-caddy-1 Stopping
Container ubuntu-caddy-1 Stopped
Container ubuntu-caddy-1 Removing
Container ubuntu-caddy-1 Removed
Container prestashop Stopping
Container prestashop Stopped
Container prestashop Removing
Container prestashop Removed
Container some-mysql Stopping
Container some-mysql Stopped
Container some-mysql Removing
Container some-mysql Removed
Network ubuntu_prestashop_network Removing
Network ubuntu_web Removing
Network ubuntu_prestashop_network Removed
Network ubuntu_web Removed
Docker stack composed down successfully.
Hosts#
Fabric doesn't have their own hosts - file like in Puppet or Ansible, but you can add hosts to the ~/.ssh/config file:
Host dev
Hostname x.x.x.x
User ubuntu
Then you're able to just run commands just by referensing the hostname of the client, like fab compose-down -H dev