Wilson Tayar //@wilsontayar

How to link containers in docker

This time we are going to see how to setup a mongodb image in the same docker environment as your app and how to link them together, so your app can connect to it!


For education purposes, I've created a very simple app in node to test this kind of stuff. You can find it in here.

This app does not use any external packages beyond the mongodb native driver from npm. Also, the Dockerfile contains everything to set up your image, including downloading references.

If you are new to docker and want to know a little bit more about Dockerfiles, this post can help you.


So, first of all, let's start a new mongo container and name it "mongo".

docker run -d --name mongo mongo

If it's your first time running a mongo container, docker will automatically pull the image from the official image registry. Don't worry.

The "-d" param means we want it to run in the background. If you don't explicit tell docker to do that it'll just start the container on your current terminal session until you exit.

After everything is set up you can check if your container is running by typing:

docker ps

If you see a mongodb container running like in the image below, great!

mongo container running mongo container running

 

Now let's clone the app repository, normal git stuff:

git clone https://github.com/wilsontayar/node-mongo-sample.git

And build the image from the dockerfile:

cd node-mongo-sample
docker build -t wilsontayar/node-mongo-sample .

This will probably take some time.

Docker is now downloading the base image, applying the dockerfile commands, downloading dependencies from npm and everything...

If you don't like the git clone part, just type in:

docker pull wilsontayar/node-mongo-sample

This way you'll pull the image directly from the official registry without the need to download sources and build the dockerfile.

After that, you can finally run the container:

docker run -d -p 3000:3000 --link mongo:mongo --name sample wilsontayar/node-mongo-sample

The important part is that "--link mongo:mongo" right there.

When you use this, docker will either use some environment variables inside your container OR update your container's /etc/hosts file telling that "mongo = x.x.x.x", where x.x.x.x is the internal ip to your mongodb's container.

And now you're done!

To test the app just navigate to localhost:3000 (if you're using boot2docker, remember to use "boot2docker ip", not localhost), type in a message and press enter to submit the form.


 

The correct "link" syntax is as follows:

--link <name>:alias

Where name is the name of the container you wish to link (remember the "--name mongo" we used earlier?) and alias is what you will tell your application, so the connection can be successful.

To know more about linking containers, take a look at the docker user guide here.

If you take a look at our app.js, specifically at the getConfiguration function, you can see that I did this:

process.env.NODE_ENV == "production" ? "mongodb://mongo/node-mongo-sample" : "mongodb://localhost:27017/node-mongo-sample"

So, if the NODE_ENV variable is set to production, our mongodb native driver will connect to the "mongo" server. That's our alias from the linking magic.

We are setting the NODE_ENV variable in our Dockerfile, this way:

ENV NODE_ENV production

 

Well, that's it for now!

Bye,

Wilson Tayar

Using docker to ship your app

Hey, it's been a long time since the last post. Guess it's time to clear out the dust and start from scratch.


Well, in the past few months one of the things I've been studying is docker, just to see what the buzz was all about. And it's awesome. :)

In this post I'll try to show you guys how to "dockerize" your app. And by "dockerize" I mean to put it inside a container, where you can include all your app dependencies as well, and finally start hosting it in your favourite cloud provider.


The app we'll dockerize is just a hello world written in node.js (without any dependencies for now). Also, it's answering calls on http://127.0.0.1:8000, as shown in the gist below:

  var http = require('http');

  var server = http.createServer(function (req, res) {
    res.end("Hello World");
  });

  server.listen(8000);

Now we'll create another file named "Dockerfile". This is where you tell docker your "container recipe", so it knows what to do in the build process:

  FROM ubuntu

  RUN sudo apt-get update
  RUN sudo apt-get install curl -y
  RUN curl -sL https://deb.nodesource.com/setup | sudo bash -
  RUN sudo apt-get install nodejs -y

  ADD . /src

  EXPOSE 8000
  CMD ["node", "/src/app.js"]

Let's take a closer look at it:

FROM ubuntu

Docker will pull the ubuntu repository (or use the local copy if you have it) to use as the base image, meaning that every line below will be applied to it.

RUN sudo apt-get update
RUN sudo apt-get install curl -y
RUN curl -sL https://deb.nodesource.com/setup | sudo bash -
RUN sudo apt-get install nodejs -y

RUN is used to send shell commands to the base image. In here we're telling it to update the apt-get repos, install curl, use curl to register node's source and install node.

ADD . /src

Then we'll copy all the files from your current Dockerfile's path to the image's /src. In this case we are moving our app.js to /src/app.js.

EXPOSE 8000

Tell the container to listen to port 8000, note that this is the same port that our app.js is using.

CMD ["node", "/src/app.js"]

CMD will execute a command, but it can only be used once. It's purpose is to tell the "default" way to get things running. In this line we're telling node to start our /src/app.js.

If you wish to know what else can be done in the Dockerfile or the full reference of the commands shown above, the Dockerfile reference has it all.


K, so now it's time to tell docker you'd like to build a container. If you're using Windows or Mac OS you'll need boot2docker, which is the way I'm going to show in here. If you're a linux user just follow the steps after the boot2docker commands and you'll be fine.

After installing boot2docker (if you haven't installed it already, check out docker's website to see your OS installation method), open your terminal and type:

boot2docker init

This will install boot2docker's image in your vmware, and you'll only need to run this once. After everything gets set up and the terminal starts responding again, type:

boot2docker start

This will give you some instructions on how to access docker. If you're on Windows, it will probably tell you to ssh the vm directly. If you're on Mac OS, it will give you a shell variable that you'll need to set things up. The IP address in this command is your way of accessing anything that is running inside boot2docker's vm (aka your containers). After following the instructions, let's move on:

cd path/to/your/app.js
docker build -t username/container-name .

In here, after navigating to our app's directory we tell docker to build our container. The -t means we want to tag this container as "username/container-name". The dot at the end is needed, and it's telling docker to use the current directory's Dockerfile.

And that's it, you have just built your first container!

To check it out, you can type "docker images" to get a list of all your current images.

To run it, just type:

docker run -p 49491:8000 -d username/container-name

So, with "-p" we basically mapped everything that comes in the host's 49491 port to the container's 8000 exposed port. The "-d" was used to tell docker that we want it to run in the background.

To access our app, type "boot2docker ip" to get the vm's IP address, open your favourite browser and type "INSERT_IP_HERE:49491" to see the "Hello World" message.


 

Well, that was it. Hope you guys enjoyed this as much as I did. :)

Bye,

Wilson Tayar

Gerenciamento de rotas na URL em Single Page Apps (SPA)

Uma Single Page Application, também conhecida por SPA, possui algumas características bem distintas se a compararmos com outros web apps que estamos acostumados a desenvolver.
Sua principal característica é o fato de ser uma única página que carrega outros conteúdos dinamicamente com chamadas AJAX, enquanto que o mais comum é encontrarmos aplicativos que solicitam páginas inteiras ao servidor, ou seja, com <head>, <body> e os demais elementos que a compõem.

Uma prática que vem se tornando cada vez mais comum é o uso de URLs com hash ("#") para "marcar" o conteúdo que foi carregado na Single Page App. Ao utilizarmos esta prática facilitamos a navegação do usuário, pois possibilitamos que o botão voltar do navegador funcione, além de deixarmos a app funcionando como se fosse um site comum. Para quem quer um exemplo, vale gastar alguns minutos navegando no Grooveshark e prestar atenção nas URLs com "#!" (hashbang). Vale dizer também que apesar do uso da "#" ser uma prática comum ela não é obrigatória, ou seja, você também pode fazer sua SPA com rotas normais (/rota, por exemplo).

Neste post, pretendo mostrar duas libraries em javascript que já utilizei em alguns projetos para o gerenciamento e funcionamento das rotas de uma SPA. No final do post também listei alguns nomes muito bons para ajudar quem está procurando outras alternativas.

Sammy.js

O Sammy.js é, como a própria descrição de seu repo no github diz:

"um framework pequeno construído sobre o jQuery e inspirado no Sinatra do Ruby".

Ele se destaca por ser muito simples e ter diversas funcionalidades muito úteis. Aqui estão algumas delas:

  • Criar rotas utilizando regular expression
  • Rotas com parâmetros
  • Vincular funções específicas para certas rotas
  • Carregar e criar templates
  • Controle de sessão próprio

Para utilizá-lo é simples, como mostrado no código abaixo, retirado da página de introdução do framework:

// define a new Sammy.Application bound to the #main element selector
Sammy('#main', function() {

  // define a 'get' route that will be triggered at '#/path'
  this.get('#/path', function() {
    // this context is a Sammy.EventContext
    this.$element() // $('#main')
        .html('A new route!');
  });
}).run();

Basta indicarmos em qual container serão carregadas todas as rotas (#main no exemplo acima) e logo depois passarmos uma rota ("#/path") e uma função que será executada sempre que ele reconhecer a rota na URL.

Vocês podem conferir um tutorial muito bom sobre diversas funcionalidades do Sammy.js no site oficial: http://sammyjs.org/docs#tutorials.

Como dica, a inicialização das rotas do Sammy pode ficar um pouco "macarrônico" dependendo da estrutura e complexidade da sua app. Tente sempre manter o funcionamento das rotas o mais simples possível chamando initializers de outras funções para posicionar tudo em sua página.

Pagerjs

O Pagerjs é uma library baseada em jQuery e knockout. Com uma página de demonstração que deixa qualquer um com vontade de usá-la alguma vez na vida, ela possui diversas features que facilitam a vida do desenvolvedor na hora de fazer mapping entre suas view models. Acredito que o pagerjs seja a melhor escolha para quem já utiliza o knockout (confiram o repo do github aqui).

Vamos analisar juntos o código abaixo, retirado de alguns trechos da página de demonstração:

<html>
<body>
<div class="container" style="padding-top: 30px;">

    <!-- #!/first-page -->
    <div data-bind="page: {id: 'first-page'}">

        <!-- #!/first-page/details -->
        <div data-bind="page: {id: 'details'}">

        </div>

    </div>

    <!-- #!/another-page -->
    <div data-bind="page: {id: 'another-page'}">

    </div>
</div>
</body>
</html>
  // use #!/ instead of the default #
  pager.Href.hash = '#!/';

  // extend your view-model with pager.js specific data
  pager.extendWithPage(viewModel);

  // apply the view-model using KnockoutJS as normal
  ko.applyBindings(viewModel);

  // start pager.js
  pager.start();

Basicamente, o Pagerjs se junta à sua view model (através do extendWithPage) e cria algumas propriedades dele. Desta forma, você consegue fazer o bind via knockout das rotas para containers da sua página.

São tantas as funcionalidades do Pagerjs que fica difícil escrever um pouco sobre todas elas. Convido os interessados a explorar melhor a página de demonstração e sentir na pele a facilidade que os bindings trazem.

A diferença básica do pagerjs para o sammy.js é que o sammy mapeia a sua rota para funções que tem o trabalho de carregar as views, enquanto que com o pagerjs, suas rotas estão ligadas com bindings ao seu HTML. Não podemos esquecer que o Pagerjs foi criado para ser uma extensão do uso normal do knockout. Caso você estiver lidando com projetos pequenos, mantenha tudo simples e não crie dependências de frameworks inteiros apenas para ter a flexibilidade das rotas. Para isso existem outras libraries que não dependem de frameworks.

Outras opções

Para quem está procurando gerenciar suas rotas, aqui estão mais algumas boas alternativas:

  • Crossroads.js - criada pelo brasileiro Miller Medeiros é uma ótima escolha para qualquer projeto, já que não depende de nenhum framework. Sua única dependência é do JS-Signals, também desenvolvido por ele.
  • pathjs - extremamente pequena, também não depende de nenhum framework. Já não é atualizada no github há algum tempo.
  • Davis.js - muito parecida com o sammy.js, já que também é baseada no Sinatra. Depende do jQuery para que tudo funcione.

 

Abraços!

Wilson Tayar