Deploy the application

You’ve come a long way, but you’re not quite done yet. Once you’ve created a great application, you need to share it with the world!

Because ASP.NET Core applications can run on Windows, Mac, or Linux, there are a number of different ways you can deploy your application. In this chapter, I’ll show you the most common (and easiest) ways to go live.

Deployment options

ASP.NET Core applications are typically deployed to one of these environments:

  • A Docker host. Any machine capable of hosting Docker containers can be used to host an ASP.NET Core application. Creating a Docker image is a very quick way to get your application deployed, especially if you’re familiar with Docker. (If you’re not, don’t worry! I’ll cover the steps later.)

  • Azure. Microsoft Azure has native support for ASP.NET Core applications. If you have an Azure subscription, you just need to create a Web App and upload your project files. I’ll cover how to do this with the Azure CLI in the next section.

  • Linux (with Nginx). If you don’t want to go the Docker route, you can still host your application on any Linux server (this includes Amazon EC2 and DigitalOcean virtual machines). It’s typical to pair ASP.NET Core with the Nginx reverse proxy. (More about Nginx below.)

  • Windows. You can use the IIS web server on Windows to host ASP.NET Core applications. It’s usually easier (and cheaper) to just deploy to Azure, but if you prefer managing Windows servers yourself, it’ll work just fine.

Kestrel and reverse proxies

If you don’t care about the guts of hosting ASP.NET Core applications and just want the step-by-step instructions, feel free to skip to one of the next two sections.

ASP.NET Core includes a fast, lightweight web server called Kestrel. It’s the server you’ve been using every time you ran dotnet run and browsed to http://localhost:5000. When you deploy your application to a production environment, it’ll still use Kestrel behind the scenes. However, it’s recommended that you put a reverse proxy in front of Kestrel, because Kestrel doesn’t yet have load balancing and other features that more mature web servers have.

On Linux (and in Docker containers), you can use Nginx or the Apache web server to receive incoming requests from the internet and route them to your application hosted with Kestrel. If you’re on Windows, IIS does the same thing.

If you’re using Azure to host your application, this is all done for you automatically. I’ll cover setting up Nginx as a reverse proxy in the Docker section.

Deploy to Azure

Deploying your ASP.NET Core application to Azure only takes a few steps. You can do it through the Azure web portal, or on the command line using the Azure CLI. I’ll cover the latter.

What you’ll need

  • Git (use git --version to make sure it’s installed)
  • The Azure CLI (follow the install instructions at https://github.com/Azure/azure-cli)
  • An Azure subscription (the free subscription is fine)
  • A deployment configuration file in your project root

Create a deployment configuration file

Since there are multiple projects in your directory structure (the web application, and two test projects), Azure won’t know which one to publish. To fix this, create a file called .deployment at the very top of your directory structure:

.deployment

[config]
project = AspNetCoreTodo/AspNetCoreTodo.csproj

Make sure you save the file as .deployment with no other parts to the name. (On Windows, you may need to put quotes around the filename, like ".deployment", to prevent a .txt extension from being added.)

If you ls or dir in your top-level directory, you should see these items:

.deployment
AspNetCoreTodo
AspNetCoreTodo.IntegrationTests
AspNetCoreTodo.UnitTests

Set up the Azure resources

If you just installed the Azure CLI for the first time, run

az login

and follow the prompts to log in on your machine. Then, create a new Resource Group for this application:

az group create -l westus -n AspNetCoreTodoGroup

This creates a Resource Group in the West US region. If you’re located far away from the western US, use az account list-locations to get a list of locations and find one closer to you.

Next, create an App Service plan in the group you just created:

az appservice plan create -g AspNetCoreTodoGroup -n AspNetCoreTodoPlan --sku F1

F1 is the free app plan. If you want to use a custom domain name with your app, use the D1 ($10/month) plan or higher.

Now create a Web App in the App Service plan:

az webapp create -g AspNetCoreTodoGroup -p AspNetCoreTodoPlan -n MyTodoApp

The name of the app (MyTodoApp above) must be globally unique in Azure. Once the app is created, it will have a default URL in the format: http://mytodoapp.azurewebsites.net

Deploy your project files to Azure

You can use Git to push your application files up to the Azure Web App. If your local directory isn’t already tracked as a Git repo, run these commands to set it up:

git init
git add .
git commit -m "First commit!"

Next, create an Azure username and password for deployment:

az webapp deployment user set --user-name nate

Follow the instructions to create a password. Then use config-local-git to spit out a Git URL:

az webapp deployment source config-local-git -g AspNetCoreTodoGroup -n MyTodoApp --out tsv

https://[email protected]/MyTodoApp.git

Copy the URL to the clipboard, and use it to add a Git remote to your local repository:

git remote add azure <paste>

You only need to do these steps once. Now, whenever you want to push your application files to Azure, check them in with Git and run

git push azure master

You’ll see a stream of log messages as the application is deployed to Azure.

When it’s complete, browse to http://yourappname.azurewebsites.net to check out the app!

Deploy with Docker

If you aren’t using a platform like Azure, containerization technologies like Docker can make it easy to deploy web applications to your own servers. Instead of spending time configuring a server with the dependencies it needs to run your app, copying files, and restarting processes, you can simply create a Docker image that describes everything your app needs to run, and spin it up as a container on any Docker host.

Docker can make scaling your app across multiple servers easier, too. Once you have an image, using it to create 1 container is the same process as creating 100 containers.

Before you start, you need the Docker CLI installed on your development machine. Search for “get docker for (mac/windows/linux)” and follow the instructions on the official Docker website. You can verify that it’s installed correctly with

docker version

Add a Dockerfile

The first thing you’ll need is a Dockerfile, which is like a recipe that tells Docker what your application needs to build and run.

Create a file called Dockerfile (no extension) in the root, top-level AspNetCoreTodo folder. Open it in your favorite editor. Write the following line:

FROM microsoft/dotnet:2.0-sdk AS build

This tells Docker to use the microsoft/dotnet:2.0-sdk image as a starting point. This image is published by Microsoft and contains the tools and dependencies you need to execute dotnet build and compile your application. By using this pre-built image as a starting point, Docker can optimize the image produced for your app and keep it small.

Next, add this line:

COPY AspNetCoreTodo/*.csproj ./app/AspNetCoreTodo/

The COPY command copies the .csproj project file into the image at the path /app/AspNetCoreTodo/. Note that none of the actual code (.cs files) have been copied into the image yet. You’ll see why in a minute.

WORKDIR /app/AspNetCoreTodo
RUN dotnet restore

WORKDIR is the Docker equivalent of cd. This means any commands executed next will run from inside the /app/AspNetCoreTodo directory that the COPY command created in the last step.

Running the dotnet restore command restores the NuGet packages that the application needs, defined in the .csproj file. By restoring packages inside the image before adding the rest of the code, Docker is able to cache the restored packages. Then, if you make code changes (but don’t change the packages defined in the project file), rebuilding the Docker image will be super fast.

Now it’s time to copy the rest of the code and compile the application:

COPY AspNetCoreTodo/. ./AspNetCoreTodo/
RUN dotnet publish -o out /p:PublishWithAspNetCoreTargetManifest="false"

The dotnet publish command compiles the project, and the -o out flag puts the compiled files in a directory called out.

These compiled files will be used to run the application with the final few commands:

FROM microsoft/dotnet:2.0-runtime AS runtime
ENV ASPNETCORE_URLS http://+:80
WORKDIR /app
COPY --from=build /app/AspNetCoreTodo/out ./
ENTRYPOINT ["dotnet", "AspNetCoreTodo.dll"]

The FROM command is used again to select a smaller image that only has the dependencies needed to run the application. The ENV command is used to set environment variables in the container, and the ASPNETCORE_URLS environment variable tells ASP.NET Core which network interface and port it should bind to (in this case, port 80).

The ENTRYPOINT command lets Docker know that the container should be started as an executable by running dotnet AspNetCoreTodo.dll. This tells dotnet to start up your application from the compiled file created by dotnet publish earlier. (When you do dotnet run during development, you’re accomplishing the same thing in one step.)

The full Dockerfile looks like this:

Dockerfile

FROM microsoft/dotnet:2.0-sdk AS build
COPY AspNetCoreTodo/*.csproj ./app/AspNetCoreTodo/
WORKDIR /app/AspNetCoreTodo
RUN dotnet restore

COPY AspNetCoreTodo/. ./
RUN dotnet publish -o out /p:PublishWithAspNetCoreTargetManifest="false"

FROM microsoft/dotnet:2.0-runtime AS runtime
ENV ASPNETCORE_URLS http://+:80
WORKDIR /app
COPY --from=build /app/AspNetCoreTodo/out ./
ENTRYPOINT ["dotnet", "AspNetCoreTodo.dll"]

Create an image

Make sure the Dockerfile is saved, and then use docker build to create an image:

docker build -t aspnetcoretodo .

Don’t miss the trailing period! That tells Docker to look for a Dockerfile in the current directory.

Once the image is created, you can run docker images to to list all the images available on your local machine. To test it out in a container, run

docker run --name aspnetcoretodo_sample --rm -it -p 8080:80 aspnetcoretodo

The -it flag tells Docker to run the container in interactive mode (outputting to the terminal, as opposed to running in the background). When you want to stop the container, press Control-C.

Remember the ASPNETCORE_URLS variable that told ASP.NET Core to listen on port 80? The -p 8080:80 option tells Docker to map port 8080 on your machine to the container’s port 80. Open up your browser and navigate to http://localhost:8080 to see the application running in the container!

Set up Nginx

At the beginning of this chapter, I mentioned that you should use a reverse proxy like Nginx to proxy requests to Kestrel. You can use Docker for this, too.

The overall architecture will consist of two containers: an Nginx container listening on port 80, forwarding requests to the container you just built that hosts your application with Kestrel.

The Nginx container needs its own Dockerfile. To keep it from conflicting with the Dockerfile you just created, make a new directory in the web application root:

mkdir nginx

Create a new Dockerfile and add these lines:

nginx/Dockerfile

FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf

Next, create an nginx.conf file:

nginx/nginx.conf

events { worker_connections 1024; }

http {
    server {
        listen 80;
        location / {
          proxy_pass http://kestrel:80;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection 'keep-alive';
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
    }
}

This configuration file tells Nginx to proxy incoming requests to http://kestrel:80. (You’ll see why kestrel works as a hostname in a moment.)

When you make deploy your application to a production environment, you should add the server_name directive and validate and restrict the host header to known good values. For more information, see:

https://github.com/aspnet/Announcements/issues/295

Set up Docker Compose

There’s one more file to create. Up in the root directory, create docker-compose.yml:

docker-compose.yml

nginx:
    build: ./nginx
    links:
        - kestrel:kestrel
    ports:
        - "80:80"
kestrel:
    build: .
    ports:
        - "80"

Docker Compose is a tool that helps you create and run multi-container applications. This configuration file defines two containers: nginx from the ./nginx/Dockerfile recipe, and kestrel from the ./Dockerfile recipe. The containers are explicitly linked together so they can communicate.

You can try spinning up the entire multi-container application by running:

docker-compose up

Try opening a browser and navigating to http://localhost (port 80, not 8080!). Nginx is listening on port 80 (the default HTTP port) and proxying requests to your ASP.NET Core application hosted by Kestrel.

Set up a Docker server

Specific setup instructions are outside the scope of this book, but any modern flavor of Linux (like Ubuntu) can be used to set up a Docker host. For example, you could create a virtual machine with Amazon EC2, and install the Docker service. You can search for “amazon ec2 set up docker” (for example) for instructions.

I like using DigitalOcean because they’ve made it really easy to get started. DigitalOcean has both a pre-built Docker virtual machine, and in-depth tutorials for getting Docker up and running (search for “digitalocean docker”).


Licenses and Attributions


Speak Your Mind