.NET and Java
Containers let you deploy applications built on frameworks such as .NET and Java without requiring users to install the .NET Framework or Java runtime.
Containerizing also assures that your application will run reliably regardless of what runtimes may already be installed on the endpoint.
We will begin by downloading the sample applications from GitHub. A nice trick to do this without installing Git is to create a temporary container with turbo try that is only used to clone the repository. The --mount=%CD%\samples flag is used to poke a hole in the container to the samples directory on the native file system so that the containerized Git application can write to it. (The %CD% special path variable represents the current directory.)
# Create a samples folder > mkdir samples # Download the sample applications from GitHub > turbo try --mount=%CD%\samples git -- /c git clone https://github.com/turboapps/samples
This example shows how to make an image for a .NET Framework application using TurboScript, an automated way to build application images.
The script copies the application binaries from the cloned repository to a new container and commits it to an image. The
--mount=%CD%\samples flag is used to poke a hole in the container isolation so that the cloned repository can be accessed. The
--no-base flag just marks
microsoft/dotnet:4.5 as a dependency rather than copying it into the image. This greatly reduces the size of the final image.
# Build an image using the application binaries from the cloned repository > turbo build --mount=%CD%\samples --name=dotNetApp --no-base turbo.me
Below is the TurboScript used for this example project. You should copy and paste this code into an empty text file named turbo.me:
# Layer in the required version of the .NET Framework layer microsoft/dotnet:4.5 # Create a new directory for your application cmd mkdir C:\myApp # Copy the application from the mounted path to the application folder cmd copy %CD%\samples\dotnet-helloworld\DotNetHelloWorld.exe c:\myApp # Set the startup file for the image to the application executable startup file c:\myApp\DotNetHelloWorld.exe
Instead of using TurboScript automation, we will demonstrate a different and more interactive process for containerizing the Java sample application.
First, create a new container with the Java Development Kit, specified with the
turbo new jdk:7 command. We will do all of our work in this container, such as copying and running the application. When everything is ready, the container state will be committed to an image.
--mount=%CD%\samples flag is used to poke a hole in the container isolation so that the Java application can be copied from the host file system. The
--detach flag specifies that the turbo command should not wait for the container to finish.
# Start a new Java Development Kit container > turbo new jdk:7 --detach --mount=%CD%\samples # Create a folder for the Java application (3df234f3) > mkdir C:\javaApp # Copy the Java application from the host file system to the container (3df234f3) > xcopy /s samples\java-simple-webserver C:\javaApp # Set the working directory to C:\javaApp (3df234f3) > pushd C:\javaApp # Run the server from within the container (3df234f3) C:\javaApp> "%java_home%\bin\java.exe" -jar SimpleWebServer.jar
The Java application is now running on your local machine using port 80, which can be confirmed by opening a web browser to
http://localhost. You should see a listing of all the files in the
C:\javaApp folder of the container.
Now that the Java application has been copied to the container and is running correctly, we can proceed to the final step of committing the container state to an image. To do that, shutdown the container using the
turbo stop command with the container identifier.
# Stop the container > turbo stop 3df234ff3
Commit the container state to an image using the
turbo commit command.
# Commit the container > turbo commit 3df234f3 simple-java-webserver
By default, committing will generate a final image that also contains the base
oracle/jdk:7 dependency. This may be desirable for portability if you plan on exporting the image for use with Turbo Studio or Server, or if the base dependency may get deleted later on. However, it will also increase the size of the image. Instead, you may want to soft-link the
oracle/jdk:7 dependency by using the
--no-base flag. A soft-linked dependency will be pulled automatically and added to the container by our layering mechanism on startup.
# Commit with a soft-linked dependency > turbo commit --no-base 3df234f3 simple-java-webserver
Each time an image is pushed a new version is generated. But usually only certain versions are released to end users. These images can be marked as releases using the
turbo release command.
To illustrate this idea, we’ll create a
1.0 release from our newly created image.
# Create a new release version 1.0 for the image > turbo release simple-java-webserver 1.0
turbo images is executed, you should now see a new entry for simple-java-webserver:1.0.
> turbo images ID Name Release Created Size -- ---- ------- ------- ---- 7a85fe8f7ad1 oracle/jdk 7.65 8/22/2014 11:34:19 AM 74.3 MB 9iejrk2a34hd git/git 1.94 8/21/2014 11:32:00 AM 50.4 MB 3a24fade3ea4 simple-java-webserver 8/22/2014 11:52:32 AM 20.2 MB 3a24fade3ea4 simple-java-webserver 1.0 8/22/2014 11:59:59 AM 20.2 MB
Deploying to the Turbo.net Hub
Now that we have built our image, we can deploy it to end users by pushing it to the Turbo Hub.
> turbo push simple-java-webserver
This will create a new repository in your Turbo.net account called simple-java-webserver where the image will be placed.
Note that if you tagged your image in the previous section, use the command:
>turbo push simple-java-webserver:1.0.
Turbo can also be used to containerize web applications. Let’s see how to use Turbo with two popular web frameworks — ASP.NET and Node.
Creating an ASP.NET application container
# Create a new container with .NET, ASP.NET, and git > turbo run microsoft/dotnet,microsoft/aspnet --using=git/git Downloading dotnet from https://turbo.net/users/microsoft Downloading aspnet from https://turbo.net/users/microsoft Downloading git from https://turbo.net/users/git Running container 249c4f3e with visibility private
Inside the container session, use the git clone command to move the application into the container.
(249c4f3e) > cd c:\ (249c4f3e) > git clone https://github.com/turboapps/samples
We use IIS Express to launch the ASP.NET application:
# Start the ASP.NET application console (249c4f3e) > start "MiniBlog" "C:\Program Files (x86)\IIS Express\iisexpress.exe" /path:C:\samples\aspnet-MiniBlog\Website
We can see the ASP.NET application start up as the IIS output is logged in the new console window.
Copied template config file 'C:\Program Files (x86)\IIS Express\AppServer\applicationhost.config' to 'C:\Users\Administrator\appdata\local\temp\iisexpress\applicationhost2014112420457848.config' Updated configuration file 'C:\Users\Administrator\appdata\local\temp\iisexpress\applicationhost2014112420457848.config' with given cmd line info. Starting IIS Express ... Successfully registered URL "http://localhost:8080/" for site "Development Web Site" application "/" Registration completed IIS Express is running. Enter 'Q' to stop IIS Express
Let’s confirm the application is running by opening
http://localhost:8080 in a browser.
Login using username demo and password demo.
And finally create a new post!
Creating a Node.js container
In this example we’ll containerize aIRChat, an open source IRC client built on the popular Node.js framework.
Start a new container using the
turbo run command specifying the Node.js image to be included and applying as before a transient Git layer:
> turbo run node --using=git
In the container, first create a folder where we can clone the aIRChat project from GitHub.
> mkdir projects > cd projects
git clone command again to copy the application contents into the container.
> git clone https://github.com/redwire/airchat.git Cloning into 'airchat'... remote: Reusing existing pack: 2983, done. remote: Counting objects: 21, done. remote: Compressing objects: 100% (18/18), done. Receiving objects: 99% (remote: Total 3004 (delta 9), reused 0 (delta 0) Receiving objects: 100% (3004/3004), 8.84MiB | 948.00 KiB/s, done. Resolving deltas: 100% (1302/1302), done. Checking connectivity... done.
Once the application files are present, we can use the standard npm package manager to install the aIRChat application:
> cd .\airchat\Content > npm install
We are now ready to launch the application!
# Launch a Node.js server > node app.js connect.multipart() will be removed in connect 3.0 visit https://github.com/senchalabs/connect/wiki/Connect-3.0 for alternatives connect.limit() will be removed in connect 3.0 Express server listening on port 3000
aIRChat is now running on port 3000. You can confirm this by opening a browser to
Once you’ve verified that aIRChat is running, stop the Node.js server by entering
Ctrl+C. At the command prompt type
exit to shut down the container session.
When you’re finished configuring the container, committing it will create a new merged image.
# Specify a name for the new image > turbo commit 1be755fcfafc4cf0b8e1c0667f6d13f0 aIRChat
Turbo.net supports both desktop and server applications. For the most part, server application images behave in the same way as desktop application images. However, special configuration considerations often arise with server applications.
Multiple database server versions
Turbo can be used to run database servers such as SQL Server, MySQL, ElasticSearch, MongoDB, and others.
In this example, we will use Turbo to run multiple versions of the free SQL Server Express database and access them from the database management client.
> turbo run sqlserver/sqlserver2012-express -d --route-block=tcp,udp -n=sql1 > turbo run sqlserver/sqlserver2014-express -d --route-block=tcp,udp -n=sql2 > turbo run sqlserver/ssms2012 -d --link=sql1:sql1 --link=sql2:sql2
Note that we have used container linking and naming to connect the SSMS management client to the two database instances, and route blocking to prevent the database ports from being accessible on the host device’s network.
Login using the username
sa and password
password1 to the database servers
sql2. You can confirm the two databases are manageable through the client and are two distinct versions.
Multiple instances of a single server
In this example, we’ll use TCP port mapping, a special case of network routing, to run multiple instances of the Ghost blogging engine on the same host.
By default, Ghost uses port 2368 to listen to requests. Since only one server can listen on a given port at a time, we need to remap this port to allow multiple Ghost instances to run simultaneously:
# Create a container that maps port 8080 on the host to port 2368 on the container. C:\> turbo new -d --route-add=tcp://2368:8080 ghost # Create a container that maps port 8081 on the host to port 2368 on the container. C:\> turbo new -d --route-add=tcp://2368:8081 ghost # Create a container that maps port 8082 on the host to port 2368 on the container. C:\> turbo new -d --route-add=tcp://2368:8082 ghost
tcp:// prefix indicates that the routing rule should apply to TCP traffic. The rule
2368:8080 causes the container’s port 2368 to be mapped to the host device port 8080. We can then launch a second and third instance by mapping 2368 to host port 8081 and 8082.
You can check that three distinct Ghost instances are accessible by browsing to localhost:8080, localhost:8081, and localhost:8082. You can also test what happens if you try to launch instances without applying the routing rules!
Factoring a database into a layer
A useful practice when constructing container architectures is to use layering to factor data from the underlying server application.
For example, suppose that we have a Ghost blog that’s been populated with some data. We would like to move the data into a separate layer. This will allow us to layer in just the database contents on top of any compatible base Ghost image. Saving the database in a distinct layer can help you test different application versions against the same data, back up database contents, or quickly rollback a database to a fixed base state during development.
First, we need to create a blank container that will hold the database:
# Create a clean container > turbo new clean --no-run
We do not want to actually run the new container so we used the
Next, use the
turbo cp command to copy the database from the Ghost container (
bc53e584 in our example) to the blank container:
> turbo cp bc53e584:c:\ghost\content\data d65260ad:c:\ghost\content\data
d65260ad: prefix used for the target folder. This tells the copy command to place the contents into a particular folder. If no container prefix is used, the host device is assumed. The
c:\ghost\content\data folder is the location for the database contents in this example.
Now that the database container is ready, we commit it into a new image called
# Commit the container to a new image > turbo commit d65260ad ghost-db
Finally, we can layer
ghost-db into any base Ghost server container and to pre-populate it with our content:
# Create a ghost container with the ghost-db content layered on top of it > turbo run ghost,ghost-db