What are we going to do?

In this article, I'm going to show you how you can configure your Centos server to get its yum updates from the ISO file that was used for installation. The advantage of using this method is that you don't need an Internet connection to download the packages you need. This should be useful in places where Internet access is restricted, has low bandwidth, or no Internet at all.

I'm going to first show you the manual way of doing it, and then how we can use Ansible to automate the process and propagate it to even thousands of machines at the same time. So, let's get started.

The traditional way

Step 1: mount your ISO file

Depending on how you installed Centos, this might be an ISO file or a DVD. In all cases, it should be mounted to a specific path. So, first let's create that path:

sudo mkdir -p /mnt/dvd

Now, the following step is going to be different depending on your installation media:

If you used a physical DVD

You might need to unmount it first if it is already mounted so that you have a well-known mount directory:

sudo umount /dev/sr0

Where /dev/sr0 is the device that corresponds to your DVD device.

Now, mount the device to the directory we created earlier:

sudo mount -t iso9660 -o ro /dev/sr0 /mnt/dvd

If you are using an ISO file

If this is a virtual machine, you're probably using an ISO file that was downloaded from Centos.org. In this case, you will need to first upload the file to your machine using a command like scp, rsync, sftp or whatever method you prefer. Assuming that the the ISO file is located in /opt/CentOS-7-x86_64-DVD-1804.iso, you can use the following command to mount it:

sudo mount -t iso9660 -o loop,ro /opt/CentOS-7-x86_64-DVD-1804.iso /mnt/dvd

Step 2: Create the repository

Creating a YUM repository is very simple. All what you need is make a new file in /etc/yum.repos.d, choose a name for your repo and add some instructions that yum will use to determine how it should use this repo to handle files.

Using your favorite editor (I'll use vim here), create the following file in /etc/yum.repos.d/local.repo and add the following lines:

[local]
name=Local DVD repository
baseurl=file:///mnt/dvd
gpgcheck=0
enabled=1

Save your file.

Step 3: Test your repo

Your repository is ready now. You can test it by running the following yum command:

yum install --disablerepo="*" --enablerepo="local" httpd

The above command will install Apache 2 (called httpd in Centos and Red Hat repositories). Notice how we used the --disablerepo="*"to disable all the configured repositories and --enablerepo="local" to selectively enable our local repository, the one that points to the ISO file or DVD.

The Ansible way

If you are not familiar with Ansible, you can have a look at the documentation, or you can enroll in my course: Beginning Ansible with Vagrant and Amazon AWS. The readers of this article can enjoy a 90% discount and have this course for only $10.99. Just use this code when you checkout: ZSAVE2018.

First things first, you need to ensure that Ansible is installed on your laptop (or the control machine). There is more than one way to install Ansible. I will use the OS package manager method here:

Centos/Red Hat client

sudo yum install -y ansible

Ubuntu/Debian

sudo apt install ansible -y

Test your installation by running ansible --version, it should give you the currently-installed version of Ansible. At the time of writing this, Ansible is released under version 2.5.1.

We also need to perform additional setup to ensure that Ansible can connect to the remote host(s) using public/private keys and use the sudo command without needing a password. This is covered in detail in my course.

Step 1: the inventory file

The inventory file is the one that contains a list of one or more hosts that Ansible is made aware of when being executed. They can be placed in groups for easier management. The inventory file is placed by default under /etc/ansible/inventory, however, it is possible to place your inventory file elsewhere as long as you point to it when executing the ansible-playbook command. The good thing about this approach is that you can have all your Ansible files in one location. This makes it portable and can be further added to version control, which is a highly recommended practice.

So, create a new file called inventory in the current directory and add the following:

[centos]
centos

We created a group called centos and added the machine that we wanted to configure. You can use the DNS name or the IP address depending on the method you prefer to connect to your remote host.

Step 2: the playbook

The playbook is where you write the instructions that Ansible will use to bring your machine to the desired state. We'll create a directory called playbooks and place our playbook file inside it. We'll also create a files subdirectory where the CentOS-7-x86_64-DVD-1804.iso file will be placed. Then, we can start editing our playbook (I called it local_repo.yaml). The steps are demonstrated below:

mkdir -p playbooks/files
cd playbooks
mv /opt/CentOS-7-x86_64-DVD-1804.iso files/
vim local_repo.yaml

Let's start adding our tasks:

Task 1: Uploading the ISO file

---
- hosts: centos
  become: yes
  vars:
    - source_iso: "files/CentOS-7-x86_64-DVD-1804.iso"
    - dest_iso: "/opt/CentOS-7-x86_64-DVD-1804.iso"
    - repo_mount: "/mnt/dvd"
  tasks:
    - name: Upload the ISO file to the remote host
      copy:
        src: "{{ source_iso }}"
        dest: "{{ dest_iso }}"
        force: no

The first task uses Ansible's copy module to upload the ISO file from the source directory on the control machine to the destination path on the remote host(s).

Notice that we needed to add some variables first to define the source and destination paths, and also the mount directory.

The force: no attribute is used to enforce idempotence. This will ensure that the file will not be re-uploaded if it already exists.

Task 2: Mounting the ISO file

Now that our file is there, we need to mount it so that it can be later referenced in our repository file. Ad the following to the playbook:

    - name: Mount the ISO file
      mount:
        path: "{{repo_mount}}"
        src: "{{dest_iso}}"
        fstype: iso9660
        opts: loop,ro
        state: mounted

The mount module is used to mount/unmount devices and configure the /etc/fstabfile accordingly. In our case, we are using it to mount our ISO file to the specified mount point.

Notice that the module will create the mount point directory if it does not already exist. So, this saved us an extra task where we would ensure that the mount point directory is created.

The fstype corresponds to the -t option in the mount command, where you specify the filesystem type of the device to be mounted.

The opts attribute allows you to add different mount options separated by commas, just like how you'd supply them to the mount command after the -o command line option.

Finally, the state attribute defines what are you intending to do with the mount point. In our case, we chose mounted, which will both mount the device and add the appropriate fstab entry to ensure that this mount point is activated on boot. It is worth mentioning that the present choice will only configure the fstab file without actually performing the mount operation.

Task 3: Creating the repository

Our final task will be to actually create the repository that uses our mounted ISO file as its source. We can create the repo file locally and use the copy module again to upload it to the appropriate location, but Ansible has a module designed specifically for managing YUM repositories so let's use it. Add the following to your playbook file:

    - name: Add repository
      yum_repository:
        name: local
        description: Local DVD repository
        baseurl: "file://{{repo_mount}}"
        enabled: yes
        gpgcheck: no

Executing the playbook and testing the results

Now, you should have an inventory file, and a directory called playbooks that contains a files directory where the Centos ISO file resides, and a local_repo.yaml file that represents your playbook. The tree shoul look as follows:

├── hosts
└── playbooks
    ├── files
    │   └── CentOS-7-x86_64-DVD-1804.iso
    └── local_repo.yaml

Your playbook should look like this:

---
- hosts: centos
  become: yes
  vars:
    - source_iso: "files/CentOS-7-x86_64-DVD-1804.iso"
    - dest_iso: "/opt/CentOS-7-x86_64-DVD-1804.iso"
    - repo_mount: "/mnt/dvd"
  tasks:
    - name: Upload the ISO file to the remote host
      copy:
        src: "{{ source_iso }}"
        dest: "{{ dest_iso }}"
        force: no

    - name: Mount the ISO file
      mount:
        path: "{{repo_mount}}"
        src: "{{dest_iso}}"
        fstype: iso9660
        opts: loop,ro
        state: mounted

    - name: Add repository
      yum_repository:
        name: local
        description: Local DVD repository
        baseurl: "file://{{repo_mount}}"
        enabled: yes
        gpgcheck: no

The playbook can be executed by running the following command:

ansible-playbook -i hosts playbooks/local_repo.yaml

The output should look as follows:

PLAY [centos] ************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************
ok: [server]

TASK [Upload the ISO file to the remote host] ************************************************************************************************
changed: [server]

TASK [Mount the ISO file] ************************************************************************************************
changed: [server]

TASK [Add repository] ************************************************************************************************
changed: [server]

PLAY RECAP ************************************************************************************************
server                     : ok=4    changed=3    unreachable=0    failed=0

You can now login to your remote server and ensure Ansible has did the job right by issuing the command that we mentioned earlier:

sudo yum install --disablerepo="*" --enablerepo="local" httpd

It should use the local repository to install the Apache 2 web server

Further thoughts

In this article I demonstrated how we can use Ansible to save time and effort by automating a repetitive task like building a local YUM repository for a Centos machine. This example could be further enhanced to create a central repository where the ISO file gets mounted and served to a whole environment over http or FTP. I may cover that in a separate article.

If you enjoyed this post and want to have hands-on learning labs on Ansible, I suggest you enroll in my course: Beginning Ansible with Vagrant and Amazon AWS. In this course, I take you from “What is Ansible?” to successfully deploying two real-world web application stacks: the LAMP and the MEAN stacks. I use Vagrant, which is another excellent DevOps tool to avail the infrastructure for the course labs. I also included a special section about using Ansible to create and manage Amazon EC2 instances. For a limited time, you can use the coupon code: ZSAVE2018 at checkout to enjoy 90% discount.