Monthly Archives: August 2015

Developing a NodeJS app using AppCatalyst

If you own a Mac and are into *nix virtual machines for development, then VMware’s AppCatalyst is a must have because…

AppCatalyst is free!

AppCatalyst is targeting that hip Application/DevOps demographic that loves Macs, Linux, API/Scripting and hates GUIs.  You know the same demographic who also loves free stuff for development!   I resemble that comment…

Who wouldn’t like AppCatalyst?   People with out a Mac, or people running Windows VMs.  For that audience there is still VMware Workstation, Fusion and other virtualization alternatives.

If your still with me, then download the hotness from….

http://getappcatalyst.com/

VM’s Phssssshhhha!  How do I make it useful for development?

I’ll skip the usual unzip instructions as many other blogs have covered the basic install.  The remainder will focus on how to integrate AppCatalyst into your development environment using Vagrant and an IDE, in my case it’s IntelliJ, the result will be a simple NodeJS application.

Vagrants and Containers

I’m talking third platform apps, not your local alley bar.   AppCatalyst is packaged with project Photon, a container run time host.

Download vagrant to your desktop.  https://www.vagrantup.com/

I won’t go through the vagrant install process. It’s easy and well documented on the internet.

Install the AppCatalyst plugin for vagrant.

$ vagrant plugin install vagrant-vmware-appcatalyst

I’m also using project Photon as the container host.  There is a vagrant plugin for photon too!  Install the photon plugin for vagrant.

$  vagrant plugin install vagrant-guests-photon

Now you need a VagrantFile.  No, that’s not someone that has a fetish for vagrants. You know a VagrantFile, like this…


# Set our default provider for this Vagrantfile to 'vmware_appcatalyst'
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'vmware_appcatalyst'

nodes = [
{ hostname: 'nodejs', box: 'vmware/photon' },

]

$ssl_script = <<SCRIPT

echo Setting up SSL...

mkdir -p /tmp/SSLCerts
cd /tmp/SSLCerts
openssl genrsa -aes256 -out ca-key.pem -passout pass:foobar 2048

openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem -subj "/C=US/ST=GA/L=ATL/O=IT/CN=www.inkysea.com" -passin pass:foobar

openssl genrsa -out server-key.pem 2048
HOST=`hostname`
openssl req -subj "/CN=$HOST" -new -key server-key.pem -out server.csr
IP=`ifconfig eth0 | grep "inet\ addr" | cut -d: -f2 | cut -d" " -f1 `

echo "subjectAltName = IP:$IP,IP:127.0.0.1" > extfile.cnf

openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf -passin pass:foobar

openssl genrsa -out key.pem 2048
openssl req -subj '/CN=client' -new -key key.pem -out client.csr
echo extendedKeyUsage = clientAuth > extfile.cnf
openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf -passin pass:foobar

rm -v client.csr server.csr

mkdir -p /vagrant/DockerCerts/

# Purge old certs on client
chmod 755 /vagrant/DockerCerts/*
rm /vagrant/DockerCerts/*

# setup keys for IDE

sudo -u vagrant cp -v {ca,cert,ca-key,key}.pem /vagrant/DockerCerts/
chmod -v 0400 /vagrant/DockerCerts/*key.pem
chmod -v 0444 /vagrant/DockerCerts/ca.pem
chmod -v 0444 /vagrant/DockerCerts/cert.pem

# Setup keys on docker host
chmod -v 0400 ca-key.pem key.pem server-key.pem
chmod -v 0444 ca.pem server-cert.pem cert.pem

cp -v {ca,server-cert,server-key}.pem /etc/ssl/certs/
mkdir -pv /root/.docker
cp -v {ca,cert,key}.pem /root/.docker
mkdir -pv /home/vagrant/.docker
cp -v {ca,cert,key}.pem /home/vagrant/.docker
echo "export DOCKER_HOST=tcp://$IP:2376 DOCKER_TLS_VERIFY=1" >> /etc/profile

# Setup Docker.service and client
SED_ORIG="ExecStart\\=\\/bin\\/docker \\-d \\-s overlay"
SED_NEW="ExecStart\\=\\/bin\\/docker \\-d \\-s overlay \\-\\-tlsverify \\-\\-tlscacert\\=\\/etc\\/ssl\\/certs\\/ca\\.pem \\-\\-tlscert\\=\\/etc\\/ssl\\/certs\\/server\\-cert\\.pem \\-\\-tlskey\\=\\/etc\\/ssl\\/certs\\/server\\-key\\.pem \\--host 0\\.0\\.0\\.0\\:2376"
sed -i "s/${SED_ORIG}/${SED_NEW}/" "/lib/systemd/system/docker.service"

systemctl daemon-reload
systemctl restart docker

SCRIPT

Vagrant.configure('2') do |config|

# Configure our boxes with 1 CPU and 384MB of RAM
config.vm.provider 'vmware_appcatalyst' do |v|
v.vmx['numvcpus'] = '1'
v.vmx['memsize'] = '512'
end

# Go through nodes and configure each of them.j
nodes.each do |node|
config.vm.define node[:hostname] do |node_config|
node_config.vm.box = node[:box]
node_config.vm.hostname = node[:hostname]
node_config.vm.provision "shell", inline: $ssl_script
end
end
end

Dont’ worry about copying and pasting.  I’ve setup a github project for this work.  Feel free to clone the project to your desktop.  The project can be found at https://github.com/inkysea/node-appcatalyst.

The project as three key items for review.

  1. VagrantFile :    This is a configuration script for vagrant.  Vagrant will work with AppCatalyst to magically provision a photon instance, complete with docker daemon and SSL certs so you can  communicate remotely with docker.   Simply type “vagrant up” from the command line and watch the magic!
  2. DockerSettings directory :  Contains a configuration file for docker, container_settings.json.  The configuration file sets values such as listening port, volumes, etc.  The file is used at build time and is useful for times when you don’t use ‘docker run’, like with an IDE.
  3. DockerOut Directory :  Contains DockerFile, .dockerignore and sample nodeJS code.

A. The DockerFile instructs Docker on what to run and install.   The included docker file is installing NPM and some other dependencies for a  nodeJS app.

FROM node:0.10

EXPOSE 8081

# App
ADD ./package.json /tmp/package.json

RUN cd /tmp && \
npm install && \
npm install -g nodemon && \
npm install express

RUN mkdir -p /opt/app && cp -a /tmp/node_modules /opt/app/

ADD . /opt/app

WORKDIR /opt/app

# Execute nodemon on the /app/index.js file. nodemon will poll the file for updates.
CMD ["nodemon", "/app/index.js"]

B. The .dockerignore file is incredibly important if you don’t want docker to fall over after running a couple of containers.  The file works similar to .gitignore but for docker, essentially defining files and directories that should not be placed into the container.  In this example, the node_modules directory is ignored as it can get huge and has no business making it into your container.

.DS_Store

# Node.js
/node_modules

# Vagrant
/.vagrant

/DockerCerts

.git
.gitignore
/.idea
LICENSE
VERSION
README.md
Changelog.md
Makefile
docker-compose.yml

C. index.js is your “hello world” node JS application.

var express = require('express');

// Constants
var DEFAULT_PORT = 8081;
var PORT = process.env.PORT || DEFAULT_PORT;

// App
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!\n');
});

app.listen(PORT)
console.log('Running on http://localhost:' + PORT);

 Run Docker, Run…

Now you are ready to run docker on that fancy photon VM you provisioned earlier with Vagrant and AppCatalyst.     You can run your container using the command line or using an IDE like IntelliJ.

You can setup IntelliJ with the Docker SSL certs that were created by Vagrant earlier.

  • Configure IntelliJ for Docker’s API.  Set the API URL and the Certificates Folder.   The API URL will be the IP of your vagrant VM and the docker API listening on 2376.  The Certificates required for communicating docker are in your projects directory under DockerCerts, they were created by the VagrantFile.

dockerconnection

  • Configure IntelliJ to run docker.  Navigate to “run” -> “edit configurations” and add a “Docker Deployment”.
    • Server:  Set this to the docker provider you configured previously
    • Deployment :  Set this to the DockerFile in the project
    • Container Settings : Set this to your container_settings.json file in the project

dockerrun

  • Deploy your application into a container. 1.  navigating to the “Application Servers” tab on the bottom right of intelliJ.  2. Press the deploy button.

dockerdeploy

  • When docker is done, you will see the following message stating that the container has successfully deployed.

deployedYou can now browse to the IP and Port (http://vagrantIP:8081) of your container to see the hello world message.

browse

Note:   The ability to use code injection for nodeJS with nodemon is very desirable as you can simply update your code and see real time results as it is mapped into the container.  Unfortunately, I haven’t found a way to make this work with the container_settings.json.  In theory you should be able to map a volume in the container settings similar to ‘docker run -v’.    If you want to use code injection then you are stuck using docker run -v at this time.