Sunday, February 16, 2014

Dockerfile Tutorial | Running Apache http server inside docker container

This article will serve as a very basic hands on tutorial of Dockerfile. I will be assuming that you are already aware of docker. If not, you can read about docker in one of my other posts here.


What are we trying to do here?

Run Apache http server inside a docker container and access it from host machine.


What is the purpose?

Familiarize ourself with Dockerfile. I have done the same exercise using docker commandline here.


Where am i running everything?

I am running everything on my Mac, We will first be creating a CentOS VM using Vagrant. Within CentOS VM, we will be installing docker and then run apache http server inside a docker container. (You don't have to necessarily run docker in VM, you can directly run it on your machine. If you decide to run it directly on your machine, please ignore Step 1 below)


Pre-requisites

You should have Oracle Virtual Box and Vagrant installed on your machine (only if you decide to run docker inside VM).

Let's get started.


Step 1: Create a CentOS VM 

Inside a directory on your machine (~/vagarnt in my case), create a file named Vagrantfile with following contents
    # -*- mode: ruby -*-
    # vi: set ft=ruby :
    VAGRANTFILE_API_VERSION = "2"
    Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
      config.vm.box = "centos65"
      config.vm.box_url = "https://github.com/2creatives/vagrant-centos/releases/download/v6.5.1/centos65-x86_64-20131205.box"
      config.vm.network :forwarded_port, guest: 80, host: 8080
      config.vm.network :public_network
      config.ssh.forward_agent = true
    end
    I am not going to explain What Vagrant and Vagrantfile does, if you are not aware of them, please read about them. Also create a directory called htdocs parallel to Vagrantfile and inside htdocs directory create a file index.html with following contents.
       This file is being server by http server running inside docker container
      Once Vagrantfile and htdocs directory is ready. Execute following commands
      vagrant up # Will start VM, you may be asked to select network interface
      vagrant ssh # Once VM is started, ssh into the VM

      Step 2: Install docker 

      Run following commands to install docker
      sudo yum -y update # Update Installed packages
      sudo yum -y install docker-io # Install Docker 
      sudo service docker start # Start Docker
      

      Step 3: Create first custom container image

      Create a directory called docker (or any name of your choice) and add a file Dockerfile in it with following contents
      # use the centos base image
      FROM centos
      #RUN command is equivalent to the docker commands: docker run image command + docker commit container_id, Where image would be replaced automatically with the current image, and container_id would be the result of the previous RUN instruction.
      RUN yum -y update
      RUN yum -y install python-setuptools
      RUN easy_install supervisor
      RUN mkdir -p /var/log/supervisor
      RUN yum -y install which
      RUN yum -y install git
      Once Dockefile is ready, execute following command to build your first custom container image
      sudo docker build -t custom/base . #This will create an image called custom/base based on DockerFile in current directory 
      Once the process is finished, If you execute sudo docker images now, you will see a new container that above command committed and it can now be used as base to start a new container.
      
      REPOSITORY          TAG                 IMAGE ID            CREATED            
      custom/base         latest              05b6cecd370b        2 minutes ago      
      centos              6.4                 539c0211cd76        10 months ago      
      centos              latest              539c0211cd76        10 months ago...

      Step 4: Install apache

      Create a file supervisord.conf in current directory with following contents. You can read more about supervisor here.
      [supervisord]
      nodaemon=true
      
      [program:httpd]
      command=/bin/bash -c "exec /usr/sbin/apachectl start"
      Change the contents of Dockefile to 
      FROM custom/base
      RUN yum -y install httpd
      ADD supervisord.conf /etc/supervisord.conf
      EXPOSE 22 80 
      CMD ["/usr/bin/supervisord"]
      Run the docker build again
      sudo docker build -t custom/httpd . #This will create an image called custom/httpd

      I am sure you have already realized that we have just created a reusable container image with http server installed on it. This idea can be used for anything and everything. You can create container for each component of your technology stack and they can be used on developer machine as well as production environment.


      Step 5: Running http server

      sudo docker run -p 80:80 -v /vagrant/htdocs:/var/www/html -t -i custom/httpd
      # -v will Mount a volume from VM to the container which was also shared from host to Vagrant VM.
      # -p forward VM port 80 to container port 80; VM port 80 is mapped to host port 8080 in Vagrantfile  
      

      Step 8: Open a browser and test

      Open a browser and hit http://localhost:8080

      Docker commandline Tutorial | Running Apache http server inside docker container

      This article will serve as a very basic hands on tutorial of docker commandline. I will be assuming that you are already aware of docker. If not, you can read about docker in one of my other posts here.


      What are we trying to do here?

      Run Apache http server inside a docker container and access it from host machine.


      What is the purpose?

      Familiarize ourself with basic docker commands and feel its power.


      Where am i running everything?

      I am running everything on my Mac, We will first be creating a CentOS VM using Vagrant. Within CentOS VM, we will be installing docker and then run apache http server inside a docker container. (You don't have to necessarily run docker in VM, you can directly run it on your machine. If you decide to run it directly on your machine, please ignore Step 1 below)


      Pre-requisites

      You should have Oracle Virtual Box and Vagrant installed on your machine (only if you decide to run docker inside VM).

      Let's get started.


      Step 1: Create a CentOS VM 

      Inside a directory on your machine (~/vagarnt in my case), create a file named Vagrantfile with following contents
        # -*- mode: ruby -*-
        # vi: set ft=ruby :
        VAGRANTFILE_API_VERSION = "2"
        Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
          config.vm.box = "centos65"
          config.vm.box_url = "https://github.com/2creatives/vagrant-centos/releases/download/v6.5.1/centos65-x86_64-20131205.box"
          config.vm.network :forwarded_port, guest: 80, host: 8080
          config.vm.network :public_network
          config.ssh.forward_agent = true
        end
        I am not going to explain What Vagrant and Vagrantfile does, if you are not aware of them, please read about them. Also create a directory called htdocs parallel to Vagrantfile and inside htdocs directory create a file index.html with following contents.
           This file is being server by http server running inside docker container
          Once Vagrantfile and htdocs directory is ready. Execute following commands
          vagrant up # Will start VM, you may be asked to select network interface
          vagrant ssh # Once VM is started, ssh into the VM

          Step 2: Install docker 

          Run following commands to install docker
          sudo yum -y update # Update Installed packages
          sudo yum install docker-io # Install Docker 
          sudo service docker start # Start Docker
          

          Step 3: Setup Docker

          Docker needs a base image to work. This is the image from which all our containers will be running (either directly or indirectly). To pull the base image
           sudo docker pull centos # Download base image

          Step 4: Create first custom container image

          sudo docker run -t -i centos /bin/bash # Start a new container using centos as base image and run /bin/bash command in the container
          Note: -t -i creates a pseudo-terminal. 
          You have successfully started your first docker container and you are inside the container. Execute following commands inside the container
          yum -y update # Update the packages
          yum install which # Install which
          yum install git # Install Git
          Press Ctrl + d to kill the container.
          Now if you execte sudo docker ps -a, you will see the container that we killed
          CONTAINER ID        IMAGE               COMMAND             CREATED......
          da9031d3568f        centos:6.4          /bin/bash           5 minutes ago.....
          Commit our changes to a new container
          sudo docker commit da90 custom/base # Idea here is to create a custom base container that has all the common tools installed for project. Container id will be different for you
          
          Once new container is committed
          If you execute sudo docker images, you will see container that we just committed and it can now be used as base to start a new container.
          
          REPOSITORY          TAG                 IMAGE ID            CREATED            
          custom/base         latest              05b6cecd370b        2 minutes ago      
          centos              6.4                 539c0211cd76        10 months ago      
          centos              latest              539c0211cd76        10 months ago...

          Step 5: Start a new container and install apache

          sudo docker run -t -i custom/base /bin/bash # Start a new container using custom/base as image.
          yum install httpd # Install httpd

          Step 6: Commit the changes again

          Press Ctrl + d to kill the container.
          sudo docker commit aa6e2fc0b94c custom/httpd This will commit our httpd related changes of step 5 and create a new base container image called custom/httpd. Container id will be different for you, execute sudo docker ps -a to find the container id. 
          I am sure you have already realized that we have just created a reusable container image with http server installed on it. This idea can be used for anything and everything. You can create container for each component of your technology stack and they can be used on developer machine as well as production environment.


          Step 7: Running http server

          sudo docker run -t -i -p 80:80 -v /vagrant/htdocs:/var/www/html custom/httpd /bin/bash
          # -v will Mount a volume from VM to the container which was also shared from host to Vagrant VM.
          # -p forward VM port 80 to container port 80; VM port 80 is mapped to host port 8080 in Vagrantfile  
          
          apachectl -k start # Start Apache

          Step 8: Open a browser and test

          Hit http://localhost:8080; you should see the index.html we created in step 1.


          Conclusion:

          I am sure you must have realized the power of docker by now. It create lightweight reusable images that fits perfectly in continuous delivery use case. We will be doing the exercise done in this tutorial again but using Docker files concept. Stayed tuned.

          Thursday, February 13, 2014

          Maven Release Plugin and Gitflow

          If you are a git user, i am sure you are aware of very famous git branching model (or git flow in other words) explained here. If you are not aware of it, please check it out, its very useful. We use it for all our projects.

          Let's say you are using maven and you need to do a release 1.0.0 of your application as per gitflow. Application version in your pom.xml of develop branch will be 1.0.0-SNAPSHOT before release and 1.1.0-SNAPSHOT after release. 

          When you use gitflow, high level release steps are:
          1. Create a release branch (release/1.0)
          2. Perform the release
          3. Tag the released code
          4. Merge the release branch in develop
          5. Merge the release branch in master
          6. Delete the release branch (release/1.0)
          We need to do some ground work before we execute any release steps.
          • Make sure you have correct git scm tag in pom.xml
             <scm>
                <connection>scm:git:<YOUR_REPO_URL></connection>
             </scm>
          • Make sure you have release plugin configure properly in pom.xml (Please pay attention to configuration)
          <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-release-plugin</artifactId>
             <version>2.4.2</version>
             <dependencies>
                <dependency>
                   <groupId>org.apache.maven.scm</groupId>
                   <artifactId>maven-scm-provider-gitexe</artifactId>
                   <version>1.9</version>
                </dependency>
             </dependencies>
             <configuration>
                <tagNameFormat>v@{project.version}</tagNameFormat> 
                <autoVersionSubmodules>true</autoVersionSubmodules>
                <pushChanges>false</pushChanges> <!--Release Plugin don't push to remote-->
                <localCheckout>true</localCheckout> <!--Release Plugin clone from local repo-->
             </configuration>
          </plugin>
          You can add following additional configuration parameter to release plugin when you are trying this out. This will make sure release plugin does install not deploy. Don't get to remove this once things are tested locally.
                <goals>install</goals>
          Let's start doing release

          Step 1: Create a release branch from develop

                git checkout -b release/1.0 develop
          Step 2: Do maven release

               mvn release:prepare 
          
          There will be 2 commits happenings in your local git repo as part of prepare process
          
          Commit 1 # Change the pom version to release version (1.0.0 in our case) and append tag name (v1.0.0 in our case) in scm connection details of pom.xml. Commit the changed pom.xml
          
          Commit 2 # Change the pom version to next snapshot version ( 1.1.0-SNAPSHOT in our case) and remove the tag name from scm connection details. Commit the changed pom.xml so that things are ready for next development version
          
          Note: Between commit 1 and commit 2, a tag v1.0.0 will be created in your local git repo as well so that code that we are releasing is tagged. 
              mvn release:perform 
          
          # Based on tag created in release:prepare step, 1.0.0 version of application is built and deployed to central repository. 
          Step 3: Merge Release branch in Develop branch

              git checkout develop             
              git merge --no-ff release/1.0 
          Step 4: Merge Released code in master (in other word commit 1 of step 2)

              git checkout master               
              git merge --no-ff release/1.0~1 #Merge 1 commit before HEAD of release/1.0
          Step 5: Delete local release branch

              git branch -D release/1.0
          Step 6: Push everything to remote (Tag/Master and Develop)

              git push --all && git push --tags

          Docker Tip - If you are running docker behind proxy

          I had centos running inside vagrant on my machine and i was trying to run docker on centos. I was not able to pull any images even thought http_proxy environment variable was set on centos.

          How did i install docker
          sudo yum -y install docker-io
          How did i start docker
          sudo service docker start
          When i was trying to pull ubuntu base machine
          sudo docker pull ubuntu
          I was getting connection timed out error even when I had http_proxy environment variable set on my centos vagrant instance.

          Solution

          Stop docker 
          sudo service docker stop
           Start it again using following command.
          sudo HTTP_PROXY=http://<PROXY_DETAILS>/ docker -d &

          Tuesday, February 4, 2014

          Tired of typing HEAD on git command line..Use @

          If you are tired of typing HEAD on git command line, please upgrade git to version 1.8.5

          After upgrade you will find, HEAD has a new alias, instead of typing four capital letters you can say "@", e.g. "git log @"

          Wednesday, January 22, 2014

          Maven 3 - Parallel Builds

          One of the maven 3 feature that most of the people are not using is parallel build. It is a pretty cool feature and speeds up the build process in a multi module project.

          Parallel build analyzes your project’s dependency graph and the dependencies between your modules, to figure out which modules can be built in parallel. 

          To run a parallel build, you can type

          1. mvn -T 4 clean install
          2. mvn -T 2C clean install


          The first command will run your build with 4 threads and second command will build your project with 2 threads per core. 

          How fast your build will become, depends a lot on your project/build structure.

          Needless to say, plugins that you use should be thread safe. If you are using any of your own plugin, please make sure it is thread safe.

          Hope this helps.

          Friday, December 13, 2013

          Singleton in Scala

          A simple way to enforce the Singleton pattern in Scala is to make the primary constructor private, then put a getInstance method in the companion object of the class

          Example

          class Person private (name: String) {
          }

          object Person {
            val person = new Person("Mohit")
            def getInstance = person
          }

          When you want to use Person class

          // this won't compile
          val p = new Person("Mohit")
            
          // this works
          val p = Person.getInstance