Simple MongoDb replica set with Docker

Here is the simplest way to create a 3-node MongoDb replica set using Docker. These instructions were created using Docker on Windows 10 and setup to use Linux containers.

Step 1: Create data volumes for each node

docker volume create --name mongodb_repl_data1 -d local
docker volume create --name mongodb_repl_data2 -d local
docker volume create --name mongodb_repl_data3 -d local

Step 2: Create a file called docker-compose.yml

version: "3"

services:
  mongo0:
    hostname: mongo0
    container_name: mongo0
    image: mongo:latest
    expose:
    - 30000
    ports:
      - 30000:30000
    volumes:
       - 'mongodb_repl_data1:/data/db:z'
    restart: always
    command: "--bind_ip_all --replSet rs0 --port 30000"
  mongo1:
    hostname: mongo1
    container_name: mongo1
    image: mongo:latest
    expose:
    - 30001
    ports:
      - 30001:30001
    volumes:
       - 'mongodb_repl_data2:/data/db:z'
    restart: always
    command: "--bind_ip_all --replSet rs0 --port 30001"
  mongo2:
    hostname: mongo2
    container_name: mongo2
    image: mongo:latest
    expose:
    - 30002
    ports:
      - 30002:30002
    volumes:
       - 'mongodb_repl_data3:/data/db:z'
    restart: always
    command: "--bind_ip_all --replSet rs0 --port 30002"

volumes:
  mongodb_repl_data1:
    external: true
  mongodb_repl_data2:
    external: true
  mongodb_repl_data3:
    external: true

Step 3: Start the Docker containers

docker-compose.exe up -d

Step 4: Start an interactive MongoDb shell

docker exec -it mongo0 mongo --port 30000

Step 5: Configure the replica set

From the MongoDb shell, type (or paste) the following

config={"_id":"rs0","members":[{"_id":0,"host":"mongo0:30000"},{"_id":1,"host":"mongo1:30001"},{"_id":2,"host":"mongo2:30002"}]}

If you use this, you probably have to update your hosts file, as well.

On Windows you can find it at:

C:\Windows\System32\drivers\etc\hosts

Add

127.0.0.1 mongo0 mongo1 mongo2 to the file and save it.

Step 6: Initiate the replica set

Still in the MongoDb shell, type (or paste) the following

rs.initiate(config);

End result

You should see the MongoDb shell switch to SECONDARY and if you hit Enter a few times it will switch to PRIMARY (it may start out as primary, as well).

You can exit the interactive MongoDb session with quit() or juct ctrl-c

Now you should be able to connect to the replica set using the following connection string

mongodb://localhost:30000,localhost:30001,localhost:30002/?replicaSet=rs0

Update Typescript version in Visual Studio Code

Typescript v2.2 was released today and it offers something that I was waiting for…generate members for interface.

Steps are pretty easy. Here we go.

  • Download and install the new version of Typescript
    npm install -g typescript
  • Open Visual Studio Code and update the setting typescript.tsdk with the following:
    "typescript.tsdk" : "{your_path_to_global_npm}\\typescript\\lib"
    Note: you can get your npm root path by typing npm root -g
  • open a Typescript file in Visual Studio Code and you should now see 2.2.1 in the status bar

You can find the release notes for Typescript v2.2 here

Enjoy.

Send email using Powershell

In a Powershell terminal, set the following:

$EmailFrom = "[email protected]"
$EmailTo = "[email protected]"
$Subject = "Test email"
$Body = "Testing to if email server works"
$SMTPServer = "mail.somecompany.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SMTPServer, 465)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
  • obviously, use your own settings…

Asp.Net 5, Ubuntu and Docker

UPDATE (2016-OCT-10): Everything has changed, for the better. This process is much easier now. I will follow-up soon with a new guide.

For my tests, I sparked up an Ubuntu 14.04 64-bit image in Parallels.

Following the instructions on the Asp.Net site, I was able to get Asp.Net 5 installed on my image.

Here are the steps broken down:

Install the .NET Version Manager (DNVM)

Use the .NET Version Manager (DNVM) to install different versions of the .NET Execution Environment (DNX) on Linux.

Install unzip and curl if you don’t already have them:

sudo apt-get install unzip curl

Download and install DNVM:

curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh

Once this step is complete you should be able to run dnvm and see some help text.

dnvm will output:

    ___  _  ___   ____  ___
   / _ \/ |/ / | / /  |/  /
  / // /    /| |/ / /|_/ / 
 /____/_/|_/ |___/_/  /_/  

.NET Version Manager - Version 1.0.0-rc2-15545
By Microsoft Open Technologies, Inc.

DNVM can be used to download versions of the .NET Execution Environment and manage which version you are using.
You can control the URL of the stable and unstable channel by setting the DNX_FEED and DNX_UNSTABLE_FEED variables.

Current feed settings:
Default Stable: https://www.nuget.org/api/v2
Default Unstable: https://www.myget.org/F/aspnetvnext/api/v2
Current Stable Override: <none>
Current Unstable Override: <none>

Use dnvm [help|-h|-help|--help]  to display help text.

Install the .NET Execution Environment (DNX)

The .NET Execution Environment (DNX) is used to build and run .NET projects. Use DNVM to install DNX for Mono or .NET Core.

To install DNX for .NET Core:

  • Install the DNX prerequisites:
sudo apt-get install libunwind8 gettext libssl-dev libcurl4-openssl-dev zlib1g libicu-dev uuid-dev
  • Use DNVM to install DNX for .NET Core:
dnvm upgrade -r coreclr

To install DNX for .NET Core:

  • Install Mono

Breaking down the instructions at http://www.mono-project.com/docs/getting-started/install/linux/#debian-ubuntu-and-derivatives:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --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

echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list

sudo apt-get install mono-devel

sudo apt-get install mono-complete

sudo apt-get install referenceassemblies-pcl

sudo apt-get install ca-certificates-mono
  • Use DNVM to install DNX for Mono:
dnvm upgrade -r mono

Install libuv

Libuv is a an IO library that is used by Kestrel.

To build libuv, run each of the following steps:

sudo apt-get install make 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

Install Node.js and npm

curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -

sudo apt-get install -y nodejs

sudo npm install -g npm

Install yeoman and the Asp.Net 5 generator

`sudo npm install -g yo bower grunt-cli gulp

`sudo npm install -g generator-aspnet

Install Docker

My suggestion would be to use the official instructions at http://docs.docker.com/engine/installation/ubuntulinux/, however, I followed the posting at https://blog.markrendle.net/fun-with-asp-net-5-linux-docker-part-3/.

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9

sudo sh -c "echo deb https://get.docker.com/ubuntu docker main > /etc/apt/sources.list.d/docker.list"

sudo apt-get update

sudo apt-get install lxc-docker

To verify that it is installed:

docker --version

It should return something like this:

Docker version 1.9.1, build a34a1d5

To get around having to use sudo on every Docker command, run the following four commands, replacing ${USER} with your own username:

sudo groupadd docker
sudo gpasswd -a ${USER} docker
sudo service docker restart
newgrp docker

###Create a new Asp.Net web application

yo aspnet

Follow the instructions to create a basic web application.

cd {name of folder (name of web application)}

dnu restore

dnx web

Open a web browser and point it at http://localhost:5000 and you should see a standard template site.

###Deploy a Docker container

Create a Docker container

Inside of the folder that houses your web application’s source code, you will find a Dockerfile file. The generator that I was using, created:

# Base of your container
FROM microsoft/aspnet:latest

# Copy the project into folder and then restore packages
COPY . /app
WORKDIR /app
RUN ["dnu","restore"]

# Open this port in the container
EXPOSE 5000
# Start application
ENTRYPOINT ["dnx","-p","project.json", "web"]

Run the Docker container

Swap out {yourapplication} for the actual name of your application

docker build -t {yourapplication} .

docker run -t -d -p 8080:5000 {yourapplication}

Now if you open your web browser and point it to http://localhost:5000 and you should see the same template site that you saw earlier.

EXCEPT THAT I DIDN’T!

Turns out that Docker forwards requests on 0.0.0.0, but Kestrel (the web server) listens on localhost. I got this information from http://stackoverflow.com/questions/33977474/cannot-run-asp-net-5-from-docker via answers by users @trm5073 and @armen.shimoon.

Find the container’s id:

docker ps

The response will look something like this:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
a957ec2ee445        helloworldweb       "dnx web --server.url"   15 hours ago        Up 15 hours         0.0.0.0:8080->5000/tcp   agitated_almeida

Use the Container ID to stop the container:

docker stop a957ec2ee445

Open the Dockerfile and change it to:

FROM microsoft/aspnet

COPY . /project        
WORKDIR /project       
RUN ["dnu", "restore"]

ENTRYPOINT ["dnx", "web", "--server.urls", "http://0.0.0.0:5000"]

Now run the container again:

docker run -t -d -p 8080:5000 {yourapplication}

Point your browser to http://localhost:8080 and tears of disbelief…

MiniProfiler with ASP.NET MVC

At work we were having trouble with the amount of time one of our pages was taking to move forward. To be fair, this page was doing a lot of work and posting a lot of data in one shot, but it really shouldn’t have been taking upwards of 15-20 seconds.

Using the normal means (Fiddler, Glimpse, Chrome tools, etc), we were still having trouble pinpointing the problem. We ruled out the jQuery validator, JSON serializing and the action method in our controller.

So along came MiniProfiler to the rescue.

To start, use Nuget to install MiniProfiler into the project. From the Project Management Console, type:

Install-Package MiniProfiler.MVC3

A new file called MiniProfiler.cs will be added to the App_Start folder. Looking in the Init() method in the MiniProfilerStartupModule class, code has already been added to limit MiniProfiler to run for local requests only. We wanted to take this one extra step and add configuration to shut it completely off when the application goes into the wild, but give us the ability to turn it on at a client site, just in case.

In the web.config file, we added:

<appSettings>
     ...
     <add key="ga:profiler" value="true" />
</appSettings>

In the MiniProfiler.cs file, we built a new method to access this configuration setting:

private static bool ProfilerConfiguredToRun()
{
    return System.Configuration.ConfigurationManager.AppSettings["ga:profiler"] == "false";
}

We updated the Init() method to look like:

public void Init(HttpApplication context)
{
    context.BeginRequest += (sender, e) =>
    {
        var request = ((HttpApplication)sender).Request;
        if (request.IsLocal && ProfilerConfiguredToRun()) 
            MiniProfiler.Start(); 
    };

    context.EndRequest += (sender, e) =>
    {
        MiniProfiler.Stop();
    };
}

To place a wrapper around every request, you could add something like:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    var profiler = MiniProfiler.Current;

    using (profiler.Step("Begin Request")) { }
}

protected void Application_EndRequest(object sender, EventArgs e)
{
    var profiler = MiniProfiler.Current;

    using (profiler.Step("End Request")) { }
}

To profile a method, perhaps a controller action,

public ActionResult SomeAction(ViewModel viewmodel)
{
    var profiler = MiniProfiler.Current;
    using (profiler.Step("Step 1: Saving"))
    {
        using (profiler.Step("Step 1: Saving"))
        {
            var application = _service.Load(viewmodel.Id);
        }
        
        var path = Url.Action("Finish", "Appl", new { area = "Cust" });
        return Json(new { success = true, appl = application, pathNext = myPath });
    }
}

When your application runs, you should now see your profiler results in the upper left corner of your browser window.

During this little adventure, one more problem cropped up regarding the sheer size of the data that we were trying to send through the profiler. It was actually maxing out what was allowed over the pipe.

To temporarily get around this, add the following to Application_Start() in the global.asax.cs file (comment or remove this when you are done with it):

MiniProfiler.Settings.MaxJsonResponseSize = int.MaxValue;

Hopefully this provides a good starting point for the next person that may need to jump in and use MiniProfiler.