How to expose a virtual file system for saving and serving media files in a Linux environment
Introduction
Prerequisites: basic understanding of Linux operating system, and command line interactions, including user maintenance; experience with Java/JEE web applications, deployed into an application server;basic understanding of a web server like Apache.
We’ve encountered the same basic use case in multiple solutions: enable the upload of images via an admin web-app, to be used as images in an end-user web-site. Simple, right? Just let the user save the image to the web-server! This would be straight-forward in a single server scenario, where the app server is the web server, or at least resides on the same machine.
However, in order to ensure high availability and response times, we will often apply the single responsibility principle to servers, and create dedicated environments for the web and app servers. This means that images uploaded via one machine are potentially saved to another, and served to end-users by yet another.
The challenge, then, is to expose a remote file-system structure for the saving of images, which is accessible to an application server, and then to expose that same structure as a sub-set of a web-server’s HTTP resources. Enter FUSE and its evolved cousin, SSHFS.
Fuse
FUSE is essentially a mechanism available on Unix-type operating systems that enables a virtual file system, or a view into an existing file system, that resides within user space rather than the kernel space. SSHFS extends this concept by adding the ability to log into that virtual file system remotely.
Let’s get down to detail, then.
In the following steps, the scenario is that we have a Wildfly app server (from here on, the “local server”), from which we want to upload files to a remote Apache web server (the “remote server”), both running on Ubuntu 14.04.1. The user names and groups reflect this, but can be substituted with whatever values are appropriate to your actual environment.
Step One: Install Software
Install SSHFS, if it has not yet been installed.
To establish this, use a terminal to log into the machine from which you require access to the remote file system, and sudo su to root. Run:
If it has already been installed, you will be prompted to provide a host.
If not, run the following command:
Note that this process will also install fuse-utils and libfuse2, upon which SSHFS is dependent.
Step Two: Set Up User
Next, you will need to add the user that will be running the application as a FUSE user. In our example, as the app server is Wildfly, the user would by default be “wildfly”:
For ease of maintenance, add a local file directory structure that mirrors the target remote file system.
For the sake of this example, let’s make it so:
Make the local user the owner of that file system.
On the remote server, create a user with the same name as your local server (“wildfly”, in our current scenario), and add that user to a group that has read and write access to the target file system, such as “www-data”. For testing purposes, sudo to the new user, cd into the directory, and create a file called “test.txt”.
Step Three: Mount Virtual Directory
Now we will mount the local directory to the remote one.
On the local server, sudo to the target user (wildfly), then run the following:
You may be prompted to accept the remote server’s fingerprint, and you will be prompted for the remote user’s password. Enter “yes” and the password respectively.
Note: If you are using an environment that forces the use of keys instead of passwords, make sure you have a local copy of the remote server’s private key, and use the following syntax to include it in the above command (instead of -o idmap=user):
If you have done this successfully, listing files on the local server should reflect those in the remote directory, so the test.txt file we created above should appear if you perform a ls. You should be able to open the file using an editor like vi, insert some text, and successfully save the file. On the remote server, you can then open that file, and verify that the text appears.
Step Four: Add to Server Startup
Let’s take this a step further, though. We’d like this setup to survive a server restart, as it is most likely an integral part of our solution.
To achieve this, it is important to negate the requirement for the password to be entered. This can be effected by manually creating SSH keys, or if you are using a cloud-based environment like Amazon, by using the console to generate the private key. Once you are happy that you can SSH via the command line without being prompted for a password, add the following line to /etc/fstab as root (note that the identityfile value must be the fully qualified path to the appropriate key file):
To verify if this entry has worked, we’ll first need to unmount the virtual directory created through our previous efforts (tip: make sure you are not in the directory when you try to unmount it):
Perform a directory listing to verify that the test file we created is no longer available.
Next, force the execution of the mounting specified in the fstab file by entering the following as root:
Perform the listing once again, and verify that you can see the test file.
Uploading to the Virtual Directory
So, assuming all the above worked, we have successfully exposed a remote file system on an application server. To exploit this, we will need to configure the file upload functionality within the web app to save to the freshly mounted path. The front-end mechanism to upload a file is not part of this discussion, as the particular technology you elect to use for your web app will dictate that. If you are using AngularJS, you may make use of angular-file-upload, while if you work in a JSF world, you may use the PrimeFaces fileUpload component; in Wicket, you’d probably exploit the FileUploadField. Whatever your framework, it is when it hands off to a server-side component to persist the file that our little piece of magic comes into play.
Assuming a Java-based implementation, you will most likely use the java.io library to save your file. Simply use the local path as defined above as the destination for your file, and SSHFS will take care of the rest.
And there you have it! You can use multiple servers to ensure good response times and segregation of responsibilities, and still save and serve images, or other media files, at will! Next time, we will explore how to use Apache to reverse proxy the virtual directory we have created so that it appears to be part of the app server’s web resources.