I have a need to run an application that requires a GUI interface to start and configure. I also need to be able to run this application on Amazon's EC2 service and EMR service. The EMR requirement means it has to run on Amazon's Linux AMI.
After extensive searching I've been unable to find any ready made solutions, in particular the requirement to run on Amazon's AMI. The closest match and most often referenced solution is here. Unfortunately it was developed on a RHEL6 instance which differs enough from Amazon's AMI that the solution does not work.
I'm posting my solution below. Hopefully it will save some others from the many hours of experimentation it took to come up with the right recipe.
@Tim Ryan Unfortunately, I'm unable to comment, but this line in installGui.sh for Step 1 seems to cause an issue:
When you get to
There's an error, but I'm wondering if I'm interpreting the line correctly as well. I'm guessing $RSRC_DIR will usually be /etc but the first of the two lines seems to make $YUM_RSRC_DIR something akin to /etc/yum. When you get to the second posted line $YUM_RSRC_DIR/yum.repos.d/centos.repo it looks like it is attempting to copy /etc/yum/yum.repos.d/centos.repo which does not exist (granted, whatever file/directory it is, I get a 'file not found' error). And yes, that's after I placed the centos.repo file you referred to below installGUI.sh in multiple locations.
I'm still experimenting with it, but I think that should be addressed
Here is my solution to get a GUI running on Amazon's AMI. I used this post as a starting point, but had to make many changes to get it working on Amazon's AMI. I also added additional info to make this work in a reasonably automated way so an individual who needs to bring up this environment more than once could do it without too much hassle.
Note: I include a lot of commentary in this post. I apologize in advance, but I thought it might be helpful to someone needing to make modfications if they could understand why made the various choices along the way.
The scripts included below install some files along the way. See section 4 for a list of the files and the directory structure used by these scripts.
Step 1. Install the Desktop
After performing a 'yum update', most solutions include a line like
This deceivingly simple step requires significantly more effort on the Amazon AMI. This group is not configured in the Amazon AMI (AAMI from here on out). The AAMI has Amazon's own repositories installed and enabled by default. Also installed is the epel repo, but it is disabled by default. After enabling epel I found the Desktop group but it was not populated with packages. I also found Xfce (another desktop alternative) which was populated. Eventually I decided to install Xfce rather than Desktop. Still, that was not straight forward, but it eventually led to the solution.
Here it's worth noting that the first thing I tried was to install the centos repository and install the Desktop group from there. Initially this seemed promising. The group was fully populated with packages. However, after some effort I eventually decided there were simply too many version conflicts between the dependencies and packages that were already installed on the AAMI.
This led me choose Xfce from the epel repo. Since the epel repo was already installed on AAMI I figured there would be better dependency version coordination with the Amazon repos. This was generally true. Many dependencies were found either in the epel repo or the Amazon repos. For the ones that weren't, I was able to find them in the centos repo, and in most cases those were leaf dependencies. So most of the trouble came from the few dependencies in the centos repo that had sub-dependencies which conflicted with the amazon or epel repo. In the end a few hacks were required to bypass the dependency conflicts. I tried to minimize those as much as possible. Here is the script for installing Xfce
installGui.sh
Here are the contents for the centos repository config file:
centos.repo
If all you needed was a recipe to get a desktop package installed on the Amazon AMI, then you're done. The rest of this post covers how to configure VNC to access the desktop via an SSH tunnel, and how to package all of this so that the instance can be easily started from a script.
Step 2. Install and Configure VNC
Below is my top level script for installing the GUI. After configuring a few variables the first thing it does is call the script from step 1 above. This script has some extra baggage since I've built it to work on a regular ec2 instance, or emr and as root or as ec2-user. The essential steps are
A few key points to note:
This assumes you will access the VNC server through an SSH tunnel. In the end this really seemed like the easiest and most reliably secure way to go. Since you probably have a port for SSH open in your security group specification, you won't have to make any changes to it. Also, the encryption config for VNC clients/servers is not straight forward. It seemed easy to make a mistake and leave your communications unencrypted. The settings for this are in the vncservers file. The -localhost switch tells vnc only to accept local connections. The '-nolisten tcp' tells associated xserver modules to also not accept connections from the network. Finally the '-SecurityTypes None' switch allows you to open your VNC session without typing a passwd, since the only way into the machine is through ssh, the additional password check seems redundant.
The xstartup file determines what will start when your VNC session is initiated the first time. I've noticed many posts on this subject skip this point. If you don't tell it to start the Xfce desktop, you will just get a blank window when you start VNC. The config I have here is very simple.
Even though I mentioned above that the VNC server is configured to not prompt for a password, it nevertheless requires a passwd file in the .vnc directory in order for the server to start. The first time you run the script it will fail when it tries to start the server. Login to the machine via ssh and run 'vncpasswd'. It will create a passwd file in the .vnc directory that you can save to use as part of these scripts during install. Note, I've read that VNC does not do anything sophisticated to protect the passwd file. So I would not recommend using a passwd that you use for other, more important accounts.
installGui.sh
vncservers
xstartup
Step 3. Connect to Your Instance
Once you've got the VNC server running on EC2 you can try connecting to it. First open an SSH tunnel to your instance. 5901 is the port where the VNC server listens for display 1 from the vncservers file. It will listen for display 2 on port 5902, etc. This command creates a tunnel from port 5901 on your local machine to port 5901 on the instance.
Now open your preferred VNC client. Where it prompts for the IP address of the server enter:
If nothing happens at all, then either there was a problem starting the vnc server, or there is a connectivity problem preventing the client from reaching the server, or possibly a problem in vncservers config file
If a window comes up, but it is just blank then check that the Xfce install completed successfully and that the xstartup file is installed.
Step 4. Simplify
If you just need to do this once then sftp'ing the scripts over to your instance and running manually is fine. Otherwise you're going to want to automate this as much as possible to make it faster and less error prone when you do need to fire up an instance with a GUI.
The first step to automating is to create an EFS volume containing the scripts and config files that can be mounted when the instance is started. Amazon has plenty of info on creating a network file system. A couple points to pay attention to when creating the volume. If you don't want your volume to be open to the world you may want to create a custom security group to use for your EFS volume. I created security group for my EFS volume (call it NFS_Mount) that only allows inbound TCP traffic on port 2049 coming from one of my other security groups, call it MasterVNC. Then when you create an instance, make sure to associate the MasterVNC security group with that instance. Otherwise the EFS volume won't allow your instance to connect with it.
Now mount the EFS volume:
Now populate /mnt/YOUR_MOUNT_POINT_DIR with the 6 files mentioned in steps 1 and 2 using the following directory structure. Recall that you must create the passwd file the first time using the command 'vncpasswd'. It will create the file at ~/.vnc/passwd.
At this point, setting up an instance with a GUI should be pretty easy. Create your instance as you normally would (make sure to include the MasterVNC security group), ssh to the instance, mount the EFS volume, and run the installGui.sh script.
Step 5. Automate
You can take things a step further and launch your instance in 1 step using the AWS CLI tools on your local machine. To do this you will need to mount the EFS volume and run the installGui.sh script using arguments to the AWS CLI commands. This just requires creating a top level script and passing it to the CLI command.
Of course there are a couple complications. EC2 and EMR use different switches and mechanisms to attach the script. And furthermore, on EMR I only want the GUI to be installed on the master node (not the core or task nodes).
Launching an EC2 instance requires embedding the script in the command with the --user-data switch. This is done easily by specifying the absolute path to the script file on your local machine.
The EMR launch does not support embedding scripts from a local file. Instead you can specify an S3 URI in the bootstrap actions.
Finally, you'll see in top.sh below most of the script is a function to determine if the machine is a basic EC2 instance or an EMR master. If not for that the script could be 3 lines. You may wonder why not just use the built in 'run-if' bootstrap action rather than writing my own function. The built in 'run-if' script has a bug and does not properly run scripts located in S3.
Debugging things once you put them in the init sequence can be a challenge. One thing that can help is the log file: /var/log/cloud-init-output.log. This captures all the console output from the scripts run during bootstrap initialization.
top.sh