Deploy a web app to CoreOS

The main objective of this session is to implement a system where we will deploy a grails application inside CoreOS, configured to run tomcat and mongodb inside it in two different cores. This blog requires introductory knowledge about coreOS and it’s components, like systemd, etcd, fleet etc. I have created a repository here for your help. 

So we will build our system step by step by going through following points:

  1. Create a grails application which will use mongodb as database
  2. Install coreOS using Vagrant and configuring it to create two cores inside it
  3. Creating mongodb Docker image inside one of the cores and configuring the database
  4. Creating tomcat Docker image inside another core
  5. Finishing some scripts and configurations(covered in above two points)

STEP 1: Creating Grails Application

Here we are going to create a simple grails application which will just list the blog title and description in a page. For that lets create a Domain class named as Blog.groovy. I have created my project inside /media/Others/Repos, lets call this work-dir, as we are gonna need this for other configurations.

 

We need just two fields in the Blog domain

  1. title
  2. description

as our purpose is just to provide the list of blogs with title and description.

Next we need to configure datasource for connection to mongodb server. So in our Datasource.groovy file we write following configurations:

	grails {
	    mongo {
	        port = 27017
	        username = "demo"
	        host = "172.17.8.102"
	        databaseName = "blog"
	        password = "password"
	        options {
	            autoConnectRetry = true
	            connectTimeout = 3000
	            connectionsPerHost = 40
	            socketTimeout = 60000
	            threadsAllowedToBlockForConnectionMultiplier = 5
	            maxAutoConnectRetryTime=5
	            maxWaitTime=120000
	        }
	    }
	}

	environments {
	    development {
	    }
	    test {
	        grails {
	            mongo {
	                databaseName = "blog_t"
	            }
	        }
	    }
	    production {
	    }
	}

For mongo we need to declare compile time dependency:

compile ":mongodb:3.0.1"

We will insert data in mongodb using the Bootstrap.groovy file:

import basicblog.Blog

	class BootStrap {

	    def init = { servletContext ->
	        log.debug "Blog bootstrap started executing"
	        
	        Blog blogInstance
	        for(i in 0..9) {
	            blogInstance = new Blog()
	            blogInstance.title = "Interesting Blog $i"
	            blogInstance.description = "Interesting blog description."
	            blogInstance.save()
	        }
	    }
	    def destroy = {
	        log.debug "Blog bootstrap finished executing"
	    }
	}

Finally, we need to make some changes to index.gsp to show the list of blogs. Add following lines:

 

	<%@ page import = "basicblog.Blog" %>
	<g:each in="${Blog.list()}" var="blogInstance">
	        <h3>${blogInstance?.title}</h3>
	        <p>${blogInstance?.description}</p>
	</g:each>

Note: Whether you use help repo provided or create one by yourself, remember to create a folder named db inside your project. For now this will be used to save data of mongodb database.

STEP 2: Setting up CoreOS using Vagrant

To set up CoreOS inside Vagrant in a virtualbox we will take help from the repo created by CoreOS team to start off. Clone the repo with the following command:

git clone https://github.com/coreos/coreos-vagrant.git

To start the cluster, we need to set some config parameters in cloud-config format via user-data file and set the number of machines in the cluster in config.rb.

But before we move forward we need to set-up nfs server in our local machine (centos 7 in this case).

Setting up nfs server

install nfs service in centos using following command:

sudo yum install nfs-utils nfs-utils-lib sudo service nfs start

Then follow these links to setup nfs server in your machine:

http://www.tecmint.com/how-to-setup-nfs-server-in-linux/
http://www.cyberciti.biz/faq/find-out-if-nfs-service-running-on-linux-unix-server/

While creating the server when you make entries in exports file, share your home(~) directory and the work-dir, and provide following privileges: (rw,sync,no_root_squash).

Now coming back to CoreOS configurations:

  1. user-data file: set your Discovery token here inside cloud-config.
  2. config.rb file: uncomment $new_discovery_url, set the number of instances to 2 and now we will declare the directories to be mounted inside coreos:
    This is the reason why we had to set up nfs server locally. Here $shared_folder parameter should mount your work-dir as /Repos inside the CoreOS:

share-config.png

After completing these configuration we can create the CoreOS instance by launching following command:

$ vagrant up --provider=virtualbox

This is what you will get after issuing the command.

machine-up2.jpg

machine-up.jpg

Since we set up nfs it asks for local machine password for authentication.
Once the machines have booted successfully we can SSH into any of the two cores with following command:
$ vagrant ssh core-01 -- -A
This will get you inside the core in CoreOS:

ssh-core1.png

STEP 3: Creating Docker container for MongoDB

We will use official pre built MongoDB docker image hosted at Docker registry hub. Here are some important key things to note:

 

  1. Every time we run the container the default data storage(/data/db) of mongodb inside the container is lost, also if some error occurred ever and container shuts down we might lose the data.
  2. We would like to keep a record of logs statements in a file.

So for above reasons we will use a file to start mongod service. This mongodb.conf file will have custom configurations.

Solution to the first point is, keeping the database storage location in our local machine. This way our data will always be available to us whether CoreOS is running or not. Exit from the CoreOS and create mongodb.conf and Dockerfile inside home(~) or work-dir as these are the only directories that will be available to you inside core os. I have created it here: work-dir/basicBlog/dockerfiles/mongodb. While creating the container we need to be inside this directory in coreOS(i.e. /Repos).

mongodb.cong:

	storage:

	    dbPath: "/mnt/basicBlog/db"

	    directoryPerDB: true

	    journal:

	        enabled: false

	systemLog:

	    destination: file

	    path: "/mnt/basicBlog/db/mongodb.log"

	    logAppend: true

	    timeStampFormat: iso8601-utc


	net:

	    bindIp: 127.0.0.1

	    port: 27017

	    wireObjectCheck : false

	    unixDomainSocket:

	        enabled : true

Dockerfile:

	#Create environment for custom mongodb

	FROM mongo:2.6.8


	ADD mongodb.conf /etc/mongodb.conf

	RUN chmod 777 /etc/mongodb.conf

next we need to ssh into core2 where we will create the container. Move to the directory where you created the Dockerfile and run following command to build the container:

 

~$ docker build -t mongoapp:v1 .

This has created a new image by name mongoapp and tag v1. Now we have to run this image and configure the database for our grails application. So for this run the image with this command:

~$ docker run -i -t -p 27017:27017 -v /Repos/basicBlog/:/mnt/basicBlog --name mongo0 mongoapp:v1 /bin/bash

This will get you inside the Docker container where we will start mongod process and configure the database:

# mongod --fork --nojournal --dbpath /mnt/basicBlog/db --logpath mnt/basicBlog/db/mongodb.log

After mongod has started launch mongo shell and create database “blog” and inside it create user “demo” with password “password”. These is initial setup that is required to run the project on tomcat. Exit from the container.

We will create a systemd unit file so that it becomes easy to manage the services using fleet. Inside /etc/systemd/system directory create a unit file by name mongodb@.service and write following lines:

	[Unit]

	Description=Start mongodb server

	After=etcd.service

	After=docker.service

	Requires=docker.service


	[Service]

	TimeoutStartSec=0

	ExecStartPre=-/usr/bin/docker kill mongo0

	ExecStartPre=-/usr/bin/docker rm mongo0

	ExecStart=/usr/bin/docker run -p 27017:27017 -v /Repos/basicBlog/:/mnt/basicBlog --name mongo0  mongoapp:v1 /usr/local/bin/mongod --config /etc/mongodb.conf

	ExecStop=/usr/bin/docker stop mongo0


	[Install]

	WantedBy=multi-user.target

Right now we will neither submit this service to fleet nor enable it via systemd. We still have one more important task to do, set up tomcat container.

STEP 4: Setup Tomcat Container

SSH into core01 and create Dockerfile inside home(~) or work-dir just like we did for mongodb. I have crated it here: work-dir/basicBlog/dockerfiles/tomcat.

Dockerfile:

	FROM centos:centos6


	#Installing basic tools

	RUN yum install wget -y

	RUN yum install unzip -y


	#Installing java7

	RUN curl -LO 'http://download.oracle.com/otn-pub/java/jdk/7u51-b13/jdk-7u51-linux-x64.rpm' -H 'Cookie: oraclelicense=accept-securebackup-cookie'

	RUN yum install jdk-7u51-linux-x64.rpm -y

	 

	ADD app-startup.sh /app-startup.sh

	RUN chmod +x /*.sh



	#Installing tomcat 7.0.32

	RUN wget http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.32/bin/apache-tomcat-7.0.32.zip

	RUN unzip apache-tomcat-7.0.32.zip

	RUN mv apache-tomcat-7.0.32/ /opt/tomcat

	RUN echo -e '#!/bin/bashnCATALINA_HOME=/opt/tomcatnPATH=$CATALINA_HOME/bin:$PATHnexport PATH CATALINA_HOMEnexport CLASSPATH=.' >> /etc/profile.d/tomcat.sh

	RUN chmod +x /opt/tomcat/bin/startup.sh

	RUN chmod +x /opt/tomcat/bin/shutdown.sh

        RUN chmod +x /opt/tomcat/bin/catalina.sh



        EXPOSE 8080

If you notice the above Dockerfile content you will find these lines:

ADD app-startup.sh /app-startup.sh
RUN chmod +x /*.sh

in these lines it is copying the app-startup.sh file into root folder of the docker container, but we don’t have any such file and what is it’s purpose? The answer is pretty simple. This file contains script that will automate the deployment process and start the tomcat server. So create the file with following content:

app-startup.sh

 

#!/bin/bash

	rm -rf /opt/tomcat/webapps/ROOT*

	if [ -f /opt/tomcat/webapps/ROOT.war ]; then

	    rm /opt/tomcat/webapps/ROOT.war

        fi


	cp /mnt/basicBlog/target/basicBlog*.war /opt/tomcat/webapps/ROOT.war


	# This will run tomcat in foreground

	/opt/tomcat/bin/catalina.sh run

The catalina.sh run command will keep the server running in the foreground so that the container does not exit from execution.

Now we can log into the core01 and create the docker image from the above created Dockerfile:

$ docker build -t tomcatapp:v1 .

This is same as what we did for mongodb image previously except the name of this image is tomcatapp. We will create another systemd service(unit) for tomcat by name /etc/systems/system/tomcat@.service:

tomcat@.service:

 

[Unit]

     Description=Start tomcat server 
     After=etcd.service 
     After=docker.service 
     Requires=docker.service 
[Service] 
     TimeoutStartSec=0 
     ExecStartPre=-/usr/bin/docker kill tomcat0 
     ExecStartPre=-/usr/bin/docker rm tomcat0 
     ExecStart=/usr/bin/docker -p 8080:8080 -v /Repos/basicBlog:/mnt/basicBlog tomcatapp:v1 /app-startup.sh 
     ExecStop=/usr/bin/docker stop tomcat0 
[Install] 
     WantedBy=multi-user.target

enable the service using this command:

$ sudo systemctl enable /etc/systemd/system/tomcat@.service

Do the same for mongodb service we created earlier in core02:

$ sudo systemctl enable /etc/systemd/system/mongodb@.service

Submit the service and start the service using fleet:

$ sudo fleetctl submit /etc/systemd/system/mongodb@.service
$ sudo fleetctl start /etc/systemd/system/mongodb@.service

verify if the service has started using following command:

$ fleetctl list-units

Once you have verified submit the tomcat@.service unit in core01 to fleet and start that as well. But before starting the tomcat@.service you will have to build the war file for the blog application we created. I have created the war file for the development environment:

grails -Dgrails.env=dev war

After the war file is created, you can start the tomcat service in core01 as:

$ sudo fleetctl start /etc/systemd/system/tomcat@.service

get ip address of your core01 using command:

$ fleetctl list-machines

If somehow you are not able to get the ip by this command the problem can be with the discovery token, try getting a new discovery token from this link and replace it in cloud-config.

When you have ip of your cores browse through:

core01-ip:8080/

After all this set up, next time you will just have to do vagrant up and ssh into both the cores your system will be up automatically.

Note: you might have to change the host address in Datasource.groovy file of your grails application depending upon the ip address of your core02.

For your convenience I have created a repository at bitbucket, where all the required files have been created already. You can find it here: basicBlog.

TL;DR

Standing out

`

About CauseCode: We are a technology company specializing in Healthtech related Web and Mobile application development. We collaborate with passionate companies looking to change health and wellness tech for good. If you are a startup, enterprise or generally interested in digital health, we would love to hear from you! Let's connect at bootstrap@causecode.com

Leave a Reply

Your email address will not be published. Required fields are marked *

STAY UPDATED!

Do you want to get articles like these in your inbox?

Email *

Interested groups *
Healthtech
Business
Technical articles

Archives