I am interested in rewriting some personal websites using ASP.NET MVC because that is the technology I’m most comfortable using at the moment. Those sites are written in PHP and are hosted on a shared Linux hosting.
After I heard about the open sourced ASP.NET 5 and various recent advancements done in the field of running .NET code on Linux, I felt like it’s a good time to do some research. Because I feel that using Azure or some other Windows hosting is too overkill for the websites I have in mind.
So, let’s see what we need for hosting an ASP.NET 5 MVC site on Linux:
- a server. Ok, so let’s pick something cheap and good enough to play with: the cheapest DigitalOcean droplet, at 5$ / month.
- a Linux distribution. Generally, when I use Linux, I choose Ubuntu because it’s very largely used and there’s lots of help available for it. The instructions below assume that Ubuntu version >=14 is used.
- access to the server’s command line. DigitalOcean provides a nice in-browser HTML5 command console for quick access, but you are not able to copy and paste text and it disconnected itself for many times while I was using it. So, it’s nice to quickly access your Linux VM, but for the long run, a remote console through SSH is the better choice.
- install a .NET runtime. This is where many paths can be taken. And that’s why I am going to go a bit into detail below.
- develop & deploy a web app to the server
- see it work (phew!)
Setting up an Ubuntu VPS
I like DigitalOcean very much for their clutter-free and minimalistic experience and good performance/price ratio. If you look around the web or ask friends who may work with something like it, you may find some discount vouchers. The VPS-es they create are called “droplets”. The cheapest one is 5$ / month and offers you (at the moment) 512 MB RAM and 20GB SSD disk. For a lightweight Linux server used for prototyping, this is very good and I was quite happy with the VPS’s responsiveness. Also, the Linux distributions on the droplets are pre-configured to use the best mirrors for downloading packages and indeed they are downloaded very quickly.
I chose the Ubuntu 14.04 distribution for creating a new droplet.
Setting SSH access
Remote access to your VPS is nice and all, but security is a very big issue. There are a lot of bots / people on the web probing servers for remote access and weak SSH setups can be easily broken.
There is good info about SSH access in this AskUbuntu question page: http://askubuntu.com/questions/51925/how-do-i-configure-a-new-ubuntu-installation-to-accept-ssh-connections
If you are using DigitalOcean, you need to run these commands for setting up SSH from the in-browser console.
After you have set up your SSH server, you can use the PuTTY application on Windows. Enter your VPS IP for the “Host name” and the appropriate port (if you changed it from the default), then click “Open”. Credentials for logging in will be later asked by the remote VPS inside the console.
Setting up a .NET runtime
“Classic” Mono way (for ASP.NET < 5)
A few times in the past I’ve tried Mono and Mono Develop on Ubuntu, but just on the “Hello, world” level. I never got too much in detail. So, for a time now, a good part of code written for .NET has been working on Ubuntu, on top of the Mono framework. Including ASP.NET websites. But again, I didn’t attempt to create a web app on Mono. I was looking for a workflow on which I could write and test the app using Visual Studio on Windows (while making sure I’m not using things not available on Mono) and somehow deploy it to the Ubuntu server. That seemed quite time consuming to set up and I didn’t do it.
Install AspNet 5 directly
The following instructions are based on the ones at https://github.com/aspnet/Home/blob/dev/GettingStartedDeb.md with improvements and fixes for issues I encountered. I used Ubuntu Server 14.04.
! When you have the choice, choose to install the same beta version for each component, to have a bigger chance of things going together smoothly. At the moment of my writing, the best choice was choosing “beta-6” where possible, and not “latest”.
Get Mono
Mono is still required. Let’s install it.
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list sudo apt-get update sudo apt-get install mono-complete
Get and build libuv
The new web server for ASP.NET 5 (kestrel) requires this library.
sudo apt-get install automake libtool curl curl -sSL https://github.com/libuv/libuv/archive/v1.4.2.tar.gz | sudo tar zxfv - -C /usr/local/src cd /usr/local/src/libuv-1.4.2 sudo sh autogen.sh sudo ./configure sudo make sudo make install sudo rm -rf /usr/local/src/libuv-1.4.2 && cd ~/ sudo ldconfig
! There’s a good chance that later on kestrel will not properly detect libuv. The command below made my kestrel detect libuv.
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
Get DNVM (.NET SDK version manager)
sudo apt-get install unzip curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh
At this point, you can verify that dnvm installed correctly. To do that, just run:
dnvm
Add package sources to NuGet.Config
ASP.NET 5 is more heavily based on NuGet. Everything has been made more granular and thus in order to have a working ASP.NET 5 runtime, required packages would need to be downloaded from some NuGet sources. These sources include the traditional nuget.org and also the AspNetVNext section on myget.org.
But first we need to feed some sources to NuGet. There’s a good chance that NuGet has some default sources already configured, but we may need to tweak them a bit.
nano ~/.config/NuGet/NuGet.Config
… add the following text and save…
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <!--<add key="AspNetVNext" value="https://www.myget.org/F/aspnetvnext/api/v2/" />--> <add key="AspNetVNext" value="https://www.myget.org/F/aspnetlitedev/api/v2/" /> <add key="nuget.org" value="https://www.nuget.org/api/v2/" /> </packageSources> </configuration>
! I’ve commented out the first source (that was officially recommended) because I had issues with it later (some packages depending on Roslyn were not found) Instead, I am using the “aspnetlitedev” source.
Still, you may have issues with package sources. Be aware that web apps can have their own NuGet.Config which can override the source for asp vnext packages. They can revert to aspnetvnext which currently doesn’t work!
Solution: update project-level NuGet.Config with aspnetlitedev or remove the file completely.
Also, DNVM has its own package source settings for Stable and Unstable which can be overriden. I am not sure that overriding them is required. But if you have issues with restoring packages later on, you can also try to override the Unstable package source for DNVM:
export DNX_UNSTABLE_FEED=https://www.myget.org/F/aspnetlitedev/api/v2/
Upgrade the DNVM
Do this because the more recent version of DNVM has some references that are required.
dnvm upgrade -u
Optional – view installed .NET SDK-s and which one is active. You may need to install other SDK versions and mark them as active if something doesn’t work for you. This is the main issue with trying ASP 5 now, after all. Software is changing rapidly during this beta stage and many components need to go together well. From my experience, at this moment you should choose version beta-6 for any component that has such choice. If you choose “latest” for any of them, you may run into an issue, even though many online guides point you to retrieve code/packages from a folder/branch named “latest”. That’s because this very recent code may have some issues working together with slightly older components.
So, to view installed .NET SDK-s, you can run the following command.
dnvm list
Fix for NuGet restore process
! I needed to set the variable below for Mono to properly run HTTP connections for NuGet package restore. Otherwise, I had numerous HTTP timeout messages and quite some HTTP failures, as well. More info about this issue:
http://stackoverflow.com/questions/31973803/dnu-restore-gives-lots-of-http-request-timed-out
export MONO_THREADS_PER_CPU=2000
Now you’re ready to deploy a web app to the server. But we’ll take care of that in a separate section. Next, we’ll go a bit through yet another method to get ASP.NET 5 running on your Ubuntu VPS.
Other useful / interesting resources
GitHub Asp.Net Home: https://github.com/aspnet/home
Instructions from .NET foundation to install DNVM with CoreCLR to run Console applications: http://dotnet.github.io/core/getting-started/
Running ASP.NET 5 inside Docker
One other nice choice is to use Docker. It helps you “modularize” your server a little bit and have the .NET related concerns separated from your OS. Also, the deployment process is more automated, as different plugins and inter-connectivity apps exist that help you remotely manage Docker.
But still, I had to get through some trial and error with Docker, even while following the MSDN guide: http://blogs.msdn.com/b/webdev/archive/2015/01/14/running-asp-net-5-applications-in-linux-containers-with-docker.aspx
Install Docker
DigitalOcean provides Ubuntu + Docker VPS images for creating your VPS/droplet, so you can directly start with that, or you can install Docker manually on an Ubuntu VPS:
https://docs.docker.com/installation/ubuntulinux/
Initialize image and container for asp.net
Initializing Docker for the sample aspnet-Home project will also make Docker install the official aspnet image, which is marked as a dependency and inherited from.
So, we start with the web application source files (which include a Dockerfile), then let Docker do its job.
! Make sure that you get the sample that corresponds to what DNVM runtime is active inside the Docker image / container. By now, it is beta6. (I tried using both latest sample and latest DNVM, but they kind of break in Docker, so let’s fallback to beta6, which works).
cd ~ git clone https://github.com/aspnet/Home.git aspnet-Home cd aspnet-Home/samples/1.0.0-beta6/HelloWeb
This location should already have a Dockerfile file. Docker looks for it when running the build command.
The “app-name” part must match [a-z0-9]+(?:[._-][a-z0-9]+)*
Seemingly no uppercase characters.
docker build -t app-name .
Now docker should do a lot of stuff that takes several minutes to complete. Mostly because it restores lots of packages from nuget.org, which is slow.
Troubles building the Docker image?
There may be some troubles along the way when building the Docker image. I went through each of them and fixed them when installing DNX directly. If issues appear with Docker, it’s more tricky to fix them, because it has its setting files and apps sandboxed inside the Docker images. Especially nasty is the problem with Mono failing some HTTP connections on NuGet package restore,
For troubleshooting, it is good to know that Docker runs Linux processes sandboxed in “containers”. These containers have a default shell that you can attach to, or you can even launch new shells that run inside the sandbox. That way, you have access to the container’s processes and files. But you should bear in mind that Docker containers are in fact instances of Docker images and when you spawn a new container from an installed image, it would not have the changes that you previously made in the previous container. Not by default, at least. However, you can commit your changes done to a container by running a Docker command (named commit).
Attach shell to Docker containers
Attach to default shell:
sudo docker attach containerId | containerName
or (to allow multiple instances of the shell):
sudo docker exec -i -t containerId | containerName bash
! After attaching to the Docker container’s shell for troubleshooting, you can try running some of the commands from the previous section, that apply to a direct ASP.NET 5 installation.
Run the Docker container
Note that the “80:5004” argument below means that TCP port 80 open inside the sandbox (guest) should map to a 5004 TCP port open on the host. So, the web app will listen to port 80 by configuration, which Docker then maps to port 5004 on the host machine. In the end, you can access your app at hostname:5004.
docker run -t -d -p 80:5004 myapp
Other resources
Asp.Net Docker image source on GitHub: https://github.com/aspnet/aspnet-docker
Deploying an application
On plain ASP.NET 5 (no Docker)
Well, this is not really deployment, but it’s a way to get a web app running:
Get a web application project
git clone git@github.com:aspnet/Home.git aspnet-home cd aspnet-home/samples/1.0.0-beta6/HelloWeb
Restore NuGet dependencies for the project
This command just fetches packages which are not already downloaded (cached locally).
dnu restore
or
Use –no-cache if you want to fetch anew all required packages from NuGet sources.
dnu restore --no-cache
Run the server
dnx kestrel
! Note: the syntax for this command varies from one beta version of DNX to another. Some DNX versions need a reference to the folder, like “dnx . kestrel”
! Second Note: This command runs the server directly and blocks the console.
The web application should now be accessible at http://machine:5004
To see the actual IP & port that are used, take a look at project.json inside the app’s folder:
cat project.json
Deploying with Docker
You first need to make your Docker accessible from the outside. But this can be very dangerous if not properly secured. Otherwise, anybody could send commands to your Docker images, just by using the Docker API.
Docker security setup
There is an article about securing your Docker deployment in Docker’s documentation: https://docs.docker.com/articles/https/
The following are excerpts from the article, with some additional comments. You can follow the excerpts for a quick setup. This commands need to be run on the host machine running Docker.
- Replace $HOST with the DNS name of the machine e.g. vm12.example.com (or its public IP). You would usually need to fill this value in wherever CN (Common Name) is required.
openssl genrsa -aes256 -out ca-key.pem 4096 openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem openssl genrsa -out server-key.pem 4096 openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
- Replace or add IPs which will be available for TLS connection to Docker. If you do not have a DNS name, you need to include the public IP address here as well in addition to any internal IPs.
echo subjectAltName = IP:10.10.10.20,IP:127.0.0.1 > extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \ -CAcreateserial -out server-cert.pem -extfile extfile.cnf openssl genrsa -out key.pem 4096 openssl req -subj '/CN=client' -new -key key.pem -out client.csr echo extendedKeyUsage = clientAuth > extfile.cnf openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \ -CAcreateserial -out cert.pem -extfile extfile.cnf
Clean files up and increase security
rm -v client.csr server.csr chmod -v 0400 ca-key.pem key.pem server-key.pem chmod -v 0444 ca.pem server-cert.pem cert.pem
Make Docker commands on the VPS always use the TLS connection.
Copy the above pem files to a “.docker” folder in the user profile and set some env variables.
mkdir -pv ~/.docker cp -v {ca,cert,key}.pem ~/.docker export DOCKER_HOST=tcp://$HOST:2376 DOCKER_TLS_VERIFY=1
Start the Docker daemon with TLS enabled
First make sure that no other Docker daemon is already running.
docker daemon --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \ -H=0.0.0.0:2376
Note: The default Docker daemon startup command is: /usr/bin/docker daemon -H fd:// This command uses the default Unix socket as reference for the “H” (host listening interface) parameter.
Test connection to the Docker daemon for remote management
Run this command to run a Docker client which checks the Docker server’s version. You can run this from the Docker host or from another host, remotely. The command syntax should be the same both on Linux and Windows. Be sure to replace the $HOST variable with your Docker server’s host.
docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \ -H=$HOST:2376 version
Set default Docker startup options on the server
These commands apply to Ubuntu < 15:
Edit the default Docker settings file
sudo nano /etc/default/docker
Add (or edit) this entry:
DOCKER_OPTS="--tlsverify --tlscacert=/userdir/.dockerd/ca.pem --tlscert=/userdir/.dockerd/server-cert.pem --tlskey=/userdir/.dockerd/server-key.pem -H 0.0.0.0:2376"
As you can see, references to pem files should use the full path to them. Replace userdir with the appropriate value.
If Docker daemon is running using the above initialization parameters, commands you send to Docker from inside the server itself need to know to which socket to be directed. So you will need to add the “-H” parameter to them. You will also need to use “–tlsVerify” to conform to the daemon’s use of TLS.
Instead of sending “–tlsVerify” and “-H” with each such command, which is very tedious, you can set the following environment variables:
export DOCKER_HOST=tcp://127.0.0.1:2376 DOCKER_TLS_VERIFY=1
Afterwards, Docker commands will automatically use these parameters by reading them from the environment.
You can persist them between system restarts, by putting them inside the “environment” file:
sudo nano /etc/environment DOCKER_HOST=tcp://127.0.0.1:2376 DOCKER_TLS_VERIFY=1
Docker client setup on Windows
Installation is very straightforward if you are using Chocolatey:
choco install docker
Note: For more information about installing Docker on Windows, see: https://docs.docker.com/installation/windows/
- Create a folder named “.docker” in your %userprofile% directory.
- Take ca.pem, cert.pem and key.pem from your server and put them there.
- Run command in the Windows console to test successful conection to the remote Docker daemon:
docker --tlsverify -H tcp://yourhost:2376 info
Install Docker tools for Visual Studio 2015
These tools are in the preview stage at the moment and it looks like they are only officially available for Visual Studio 2015, although I may be wrong. If you are interested in developing for ASP.NET 5, then maybe you are going to use VS 2015 anyway, which offers support for them out of the box.
Download the tools from: https://visualstudiogallery.msdn.microsoft.com/0f5b2caa-ea00-41c8-b8a2-058c7da0b3e4?SRC=Featured
Some more information: https://azure.microsoft.com/en-us/documentation/articles/vs-azure-tools-docker-hosting-web-apps-in-docker/
Troubleshooting errors with Docker: publishing: https://msdn.microsoft.com/library/azure/mt125442.aspx
After installing the Docker tools, you can get default Dockerfiles from: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Microsoft\Windows Azure Tools\Docker
Publish web app from Visual Studio 2015 to Docker
You can publish web app projects built with ASP.NET 5 from Visual Studio by choosing Docker as Publish target, then choose to fill in details for custom docker server.
Note that during publishing, built project is stored temporarily at: C:\Users\username\AppData\Local\Temp\PublishTemp
Epilogue
As you can see, getting things setup required a lot of mingling. I did a lot of trial and error and spent quite a few nights with this. Luckily, I managed to document my steps so the existence of this article was made possible.
Things are still quite shaky and I feel that I need to experiment a lot more to get more accustomed to them.
Hopefully, by the time ASP.NET 5 has a final release, setting up will have less hurdles and definitive official guides should make things easier and clearer.
My attempts to rewrite the before mentioned websites have come to a halt when getting to the point of choosing a DB and a DB client implementation. I was hoping for a lightweight DB and a nice API from within ASP.NET v5.
Sqlite with EF would have been ideal, but I needed to use EF 7 with ASP.NET v5, which did not have effective support for Sqlite save for some workarounds, at the time I tried those things.
Meanwhile I decided it’s best to wait for ASP.NET v5 and EF 7 to come to a stable release.
Happy coding and deploying with ASP.NET 5 and Linux!