VS Code on a Supercomputer
I mean no offense to Vim, I swear. But sometimes, you just want a full-fledged text editor to get the job done. I discovered this fact the other day as I was developing some CLI tools in Go for Washington State University’s high performance computing cluster, Aeolus. Since the tool was meant to be used on the cluster, writing locally and exporting was not an option. Even if I wrote locally for the CentOS 6 environment that the cluster runs on, I would have to go build
and scp
every time I wanted to test a change, which ended up being every few seconds.
Vim will never let you down in a pinch, but it can be tedious for developing large scale tools with several source files. And without heavy modification, it does not have the rich features that an editor like VS Code offers, such as intelli-sense, tab completion, and interactive debugging.
The big idea
After a few minutes of Vim-ing around the tool I was building, I had a thought which went like this: VS Code is written in React and runs on Chrome. Doesn’t that kind of make it a server? In fact, it does! It will not work as a server out of the box. But since it is based on Electron, which uses Chrome’s V8, VS Code is written like a website already. And, as you can see if you run ps aux | grep code
or similar, the threads are already split off into helpful groupings that could be interfaced with using a web API.
All that’s needed is the server and daemon to interface with those threads! Simply send all that beautiful React-y HTML to the browser, and have it call back to localhost for any thread-ly interaction (write this file, load this extension, debug that). Someone has to have thought of this before, right?
In fact, some wonderful people at CoderCom have:
Now, code-server
is already fantastic out of the box. In fact, if you have Docker on your server and Linux kernel v4
or higher, all you need to do is pull and run their image from Docker Hub. However, as you know if you work in HPC, Docker’s requirement of root permissions precludes its use in distributed multi-user setups.
To address this problem, Sylabs’ Singularity was created:
Singularity allows any unprivileged user to take full advantage of the wide world of containers by allowing those containers to run in the HPC environment without root permissions. Amazingly, this includes any image from Docker Hub! Good on you, Singularity team.
The synthesis of this is to spin up a code-server
with Singularity (it’ll listen on port 8443 by default) and then run this on your host (non-cluster) computer:
ssh -NL 8443:[hostname]:8443 $USER@[hostname]
If you don’t already know, this will bind your local port to a port on a foreign machine over an SSH tunnel. This means that you can access the cluster’s localhost
as if it’s your own, all over a secure SSH session. Just go to localhost:8443
in the browser and watch a full-fledged VS Code spring to life. Neat!
Getting it to work
However, it did not end there for me. Although we have do have Singularity on our cluster, we are running CentOS 6 with Linux kernel 2.32; too old to support Ubuntu 18. Trying to run an Ubuntu 18 image with Docker or Singularity on kernel version 2 will yield a Fatal: Kernel too old
error. To resolve this, I ended up forking the code-server
repository and building the Docker image off of Ubuntu 16 instead. As you may expect, this created a cascade of compatibility issues which I resolved with various modifications to the Dockerfile
. You can see my modifications on GitHub, and if you’d like to run it on CentOS 6 as I did, you can also grab my Docker Hub image:
Making it pretty
The backport to Ubuntu 16 did just what I needed! I was able to easily run the image via Singularity and spin up instances of VS Code that could be accessed from the browser. This is a truly useful tool for anyone who needs to test or debug live from the cluster itself, rather than having to constantly move code and/or binaries around. However, there’s still a bit more we can do to optimize the user experience so that anyone on the cluster can easily enjoy the benefits of VS Code.
First, I decided to write a shell script that mimics the code
command that Visual Studio optionally exports to your $PATH
on *nix systems. I use this all the time, and figured it would be nice to use on the cluster as well. Here’s what I did:
Let’s step through everything this script does.
Well, it cleans up nice
Lines 2–8 establish an exit trap, which sets up code that will run before the script exits (regardless of what signal caused the exit). I found that code-server
had a habit of leaving behind parentless child threads after the main process was killed. So this bit of code sends SIGINT
to the process group ($GID
) when the script is killed. This group id should be the same as the parent id, which in our case is the PID of the main process, our code
script. In bash
, you can get the current PID with $$
, which you can see on line 24. The end result is that the lifetime of the Singularity instance and the lifetime of the VS Code server are the same as the lifetime of the code
command.
Taking names and serving dirs
Lines 10–12 mimic the ability of VS Code’s code
verb to open to a specific directory as a command line argument, e.g. code tacos/
. The Singularity image will use pwd
to determine what to serve, so all we need to do is cd
to the location specified.
Getting down to business
Lines 16–22 is where the magic happens. First, I set up some useful variables. The port number ipnport
is randomized so that multiple people can run their own instances without stepping on each others’ sockets. Additionally, the IP address is collected for use in the ssh -NL
command, and a log file is initialized. Now the good part on line 22: spinning up the Singularity image. That may look complex, but the core of it is simply singularity run code-server_xenial-2.sif
. I preface it with nohup
so that we can return control of the thread to the script and print out some useful instructions after the code server has started. I also found it useful to export the PATH
environment variable to the Singularity instance using SINGULARITYENV_PATH=PATH
. Check the Singularity docs if you’re curious about that. -p $ipnport
tells the code server to listen to the port we just randomly selected. And finally, > $ipnlogname 2>&1
combines the standard input and output and directs it into the timestamped logfile we created on line 20, and the trailing &
moves the whole shebang into the background so that the script continues to execute.
The part everyone skips
Instructions might be the most under-read blocks of text in the world, possibly rivaled by forewards. However, mine (I hope) is useful and helpful. On line 33, we generate and print the command that the user will need to create their SSH tunnel. And since we plug in our variables, they will only need to copy and paste. Lines 40–49 are a bit more complicated. Remember how we set up logging in the previous section? That’s not only to catch errors. It’s also because code-server
outputs a per-instance hash password for you to enter in the browser after spinning up its shared threads. However, the time at which it prints this is not regular, so we poll and grep that file in a while loop each second until we have the password, printing another .
each time so the user knows we haven’t left them hanging. Once the password is acquired, we print it out in bold to make it easy to see and copy. And finally, on line 60, we listen for user input (and do nothing with it). This is so that the script can be exited cleanly with Ctrl-C as advised in the instructions. As you remember, Ctrl-C will trigger the exit trap, cleanly exiting your code instance.
The fruit of our labor
I’m very glad I decided to put in the time to get this working well. On the Aeolus cluster, we now have the entire capability set of VS Code at our fingertips, using only a single command: code
. This includes installing extensions, getting mouse-over man
entries, even interactive step-through debugging, all in the browser! Finally, writing and compiling code on the cluster is not just feasible, but fun. Here’s the final output of our tool (with private information redacted):
[[redacted]@[redacted]]$ code myproject/
----------------------CODE SERVER---------------------
Starting server so you can run VS Code in the browser.To access VS Code on Aeolus in the browser, please run
this command in a shell on your local machine:ssh -NL 8443:[redacted]:9259 [redacted]@[redacted]Then, you can access VS Code from your browser:
https://localhost:8443Getting your one time password........
Here is your one-time password to access the session:
Password: 97f054811019d72962ebfc1d
Please enter it when prompted.When you are finished using VS Code in your browser,
please kill this script using Ctrl-C.
----------------------CODE SERVER---------------------
Thanks so much to both the Singularity team and the code-server team, who responded to my various bug submissions faster than I could test their fixes (which worked great).
I hope this helped you get VS Code working in your HPC environment. Happy coding!