A virtual machine is a simulated computing device. Let’s start with a simple example. You can play classic NES and Super NES games using the Switch. How is this done? Does the Switch contain the same hardware as the original NES or SNES? No. It uses a NVIDA Tegra processor, which utilizes the 64-bit ARM architecture. The NES used an 8-bit CPU, and the SNES used a 16-bit CPU. These are very different architectures. Of course, you may note the service I reference is an online service. So do they hook up thousands of NES and SNES units to a web server processing input and output? Also no, but also yes.
What they actually do is emulate that hardware with software. Thus, they have a program that pretends to be the NES hardware, and the load into that program the actual NES game (the software) you wish to play. This emulator is a part of a web server that streams the resulting game images to the Switch, and the Switch likewise streams the player’s input back to the server. This is essentially the same approach used by any video game console emulator (sans the use of a web server).
In the same way, we can use a virtual machine to emulate another kind of computer within our own computers. For example, the VMWare platform you have access to as a CS student allows you to install VMWare on your computer. Once installed, you can create virtual Linux or Windows PCs that run within the program. Essentially, VMWare pretends to be a separate computer and shares your computer’s real resources through interfaces that make it seem to be the hardware the program emulates. Another approach to running a Linux VM within a Windows environment is the Windows Subsystem for Linux (WSL). This is essentailly a virtual machine that runs a Linux kernel (of your choice) within Windows. It offers better performance than VMWare as the VM plugs directly into Windows OS procedures.
Most Internet hosting services offer Virtual Machines that you can rent. Typically you specify the amount of RAM and storage, as well as the number of CPUs you wish to utilize. These represent a portion of the real machine’s resources that are assigned to your specific VM. You then are granted root access to the VM, and can install whatever software you need; for example, installing Linux, Dotnet Core, and a relational database to run your web application. Once you have your application set up, you launch it as a constantly running service, and it begins to listen for HTTP and HTTPS requests on port 80 and 443. The service usually provides you with a static IP address, which you can then hook up to your domain name.
The computing device being emulated does not have to equate to hardware analogues. In CIS 200 you worked with a VM extensively that does not correspond to physical hardware - the Java Virtual Machine (JVM). The JVM is a virtual machine that does not correspond to any real-world hardware, but provides a similar computing environment that can process Java Byte Code (which is similar to the assembly code run by hardware-based processors). A different JVM exists for every hardware platform that Java can be run on, mapping the virtual system procedures to the specific platform’s hardware and operating system.
You may be asking yourself “is .NET also a virtual machine?” In one sense, yes, as it works from a similar concept as Java, but the reality is a bit more complex. The DOTNET languages are compiled into Intermedate Langauge, a format similar to Java’s Bytecode. And the DOTNET runtime has specific builds for different operating systems and hardware platforms, much like the JVM. But rather than the DOTNET runtime running the IL in a virutal environment, when you execute a DOTNET program, it is further compiled by the DOTNET runtime into assembly for the specific platform using a Just-in-Time compiler. So ultimately, DOTNET code is run in actual machine, not a virtual machine.
In recent years, containerized applications have become increasingly popular, especially for web-based deployments. This is a variant on the Virtual Machine idea, where the application and the execution environment are separated into two parts - the container (an image file containing the binary code and resources of an application) and an execution environment (a virtual machine, often emulating a Linux or Windows machine, but possibly more specialized). The containerized application is then run within this execution environment. The chief benefits of containerized approaches are portability and scaleability. As the execution environment can be installed on a variety of platforms, much like the JVM, the container can be run on different hardware - a development PC, a development Mac, a production server, or someting more esoteric like a cloud server.
As the emulated execution environment is the same across each of these platforms, the same containerized application should run identially on each. Moreover, to create additional instances of a containerized application, you just launch another copy of the container - a process that takes milliseconds. Compare this to adding another traditonal server or VM - you have to intialize the VM, install the necessary supporting environment, libraries, and other dependencies, and then install your application and launch it. This process can take minutes to hours, and often requires an IT professional to work through the steps. Cloud services like Amazon Web Services, Microsoft’s Azure, and Google Cloud all utilize container-based approaches to allow rapid upscaling and downscaling of web servers to meet the actual demand for a web application. These platforms typically handle load balancing (directing traffic evenly amongst the containers running instances of your web server) automatically, making the process of hosting large-volume web traffic much easier. And for small, limited-demand web applications, many of these cloud services offer a free tier with a limited amount of bandwith a month. The speed at which a containerized application can be spun up means that these free tier applications can be shut down most of the time, and only run when requests are coming in.