samedi 28 juin 2014

Server provisioning with Vagrant, CoreOS and Docker

In this article, we'll see how easy it is to build a custum Linux image an to deploy it in cloud using modern DevOps tools like Vagrant, CoreOS and Docker and the Amazon Web Services cloud provider.

Setup Vagrant and CoreOS

To build a custom Linux image for Docker we need to run Docker on Linux... ;-) CoreOS is a minimalist Linux distribution that ships with Docker and a few other services. As we want to setup our custom Linux image for Docker locally, let use CoreOS with Vagrant. Vagrant is about provisionning virtual development environment and it's exactly what we need to launch CoreOS quickly on Windows, Mac OS X or Linux. To get all this stuff to work together just follow the Running CoreOS on Vagrant documentation on the CoreOs website.
git clone https://github.com/coreos/coreos-vagrant.git
cd coreos-vagrant
vagrant up
Bringing machine 'core-01' up with 'virtualbox' provider...
==> core-01: Checking if box 'coreos-beta' is up to date...
==> core-01: Clearing any previously set network interfaces...
==> core-01: Preparing network interfaces based on configuration...
    core-01: Adapter 1: nat
    core-01: Adapter 2: hostonly
==> core-01: Forwarding ports...
    core-01: 22 => 2222 (adapter 1)
==> core-01: Running 'pre-boot' VM customizations...
==> core-01: Booting VM...
==> core-01: Waiting for machine to boot. This may take a few minutes...
    core-01: SSH address: 127.0.0.1:2222
    core-01: SSH username: core
    core-01: SSH auth method: private key
    core-01: Warning: Connection timeout. Retrying...
    core-01: Warning: Connection timeout. Retrying...
    core-01: Warning: Connection timeout. Retrying...
    core-01: Warning: Connection timeout. Retrying...
==> core-01: Machine booted and ready!
==> core-01: Setting hostname...
==> core-01: Configuring and enabling network interfaces...
==> core-01: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> core-01: to force provisioning. Provisioners marked to run always will still run.
You can now SSH to your virtual environnement :
CoreOS (beta)
core@core-01 ~ $

Create a custom Debian image

Once logged into CoreOS, we can see that Docker is available and that no image is available yet :
core@core-01 ~ $ docker images
REPOSITORY          TAG                 IMAGE ID
Let start a Debian Linux with Docker container :
core@core-01 ~ $ docker run -t -i debian:7.4 cat /etc/*-release | grep NAME
PRETTY_NAME="Debian GNU/Linux 7 (wheezy)"
NAME="Debian GNU/Linux"
Now we have the Debian Docker image available localy :
core@core-01 ~ $ docker images
REPOSITORY          TAG                 IMAGE ID
debian              7.4                 e565fbbc6033
As we can see, java is unavailable :
core@core-01 ~ $ docker run -t -i debian:7.4 java -version
2014/06/27 14:01:58 exec: "java": executable file not found in $PATH
So, how can we build a custom debian:7.4 based image with java pre-installed ? Nothing simpler than creating a Dockerfile with the necessary command to install the jdk on a debian distribution :
core@core-01 ~ $ touch DockerFile
core@core-01 ~ $ vi DockerFile
FROM debian:7.4
MAINTAINER Antoine Vianey 

RUN apt-get update
RUN apt-get -f install
RUN export DEBIAN_FRONTEND=noninteractive
RUN apt-get -y install --no-install-recommends openjdk-7-jdk
The docker build command builds the image for us... The -t option allow us to specify the repository + tag that will be associated with the resulting image. Read the Dockerfile reference guide for a full description of what you can do with dockerfile.
core@core-01 ~ $ sudo docker build -t avianey/ojdk7-deb .
If every step runs fine the final image is available under the specified tag. If a step fails, interdmediate steps images are kept to speed up the rebuild after having corrected the failing Dockerfile step.
core@core-01 ~ $ docker images
REPOSITORY          TAG                 IMAGE ID
avianey/ojdk7-deb   latest              54e73307f07e
debian              7.4                 e565fbbc6033
Check that java is installed :
core@core-01 ~ $ docker run -t -i avianey/ojdk7-deb java -version
java version "1.7.0_55"
OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-1~deb7u1)
OpenJDK 64-Bit Server VM (build 24.51-b03, mixed mode)
Everything is fine, we can tag our custom image :
core@core-01 ~ $ sudo docker tag 54e73307f07e avianey/ojdk7-deb:v1
core@core-01 ~ $ docker images
REPOSITORY          TAG                 IMAGE ID
avianey/ojdk7-deb   latest              54e73307f07e
avianey/ojdk7-deb   v1                  54e73307f07e
debian              7.4                 e565fbbc6033

Use the image in the cloud

Now that we have our custom image setup, we need to push it in the Docker Hub or a private Docker repository to use it from the cloud :
core@core-01 ~ $ sudo docker push avianey/ojdk7-deb
The push refers to a repository [avianey/ojdk7-deb] (len: 2)
Sending image list
...
Pushing repository avianey/ojdk7-deb (2 tags)
...
Pushing tag for rev [54e73307f07e] on {https://registry-1.docker.io/v1/repositories/avianey/ojdk7-deb/tags/latest}
Pushing tag for rev [54e73307f07e] on {https://registry-1.docker.io/v1/repositories/avianey/ojdk7-deb/tags/v1}
Everyone can now start a Docker container running the avianey/ojdk7-deb:v1 image. That what we are going to do with a CoreOS Amazon Machine Image :
Using username "core".
Authenticating with public key "imported-openssh-key"
CoreOS (beta)
core@ip-172-31-25-84 ~ $ docker run -t -i avianey/ojdk7-deb:v1 java -version
Unable to find image 'avianey/ojdk7-deb:v1' locally
Pulling repository avianey/ojdk7-deb
54e73307f07e: Download complete
511136ea3c5a: Download complete
405cce5cd17d: Download complete
e565fbbc6033: Download complete
782079a3e1cf: Download complete
3be917c2a827: Download complete
11311a0d3731: Download complete
b4754115da81: Download complete
java version "1.7.0_55"
OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-1~deb7u1)
OpenJDK 64-Bit Server VM (build 24.51-b03, mixed mode)
That's it, enjoy !

mercredi 19 février 2014

JSP, JS, CSS hot-deployment with Maven in Eclipse WTP

Eclipse WTP use Adtaper to provide hot deploy fonctionnalities with JEE application servers or web containers. Depending on the configuration, the adapter may reload the whole .war or .ear and take several seconds when not minutes to do so.

While WTP adatpers hot deployment is a time-saver as it rely on the Eclipse incremental compiler and does not pass through all the maven build phase to package the final .war or .ear, this is not the good choice when you work with resources like .jsp, .js, .css, .html, .tag, ...

For those static resources that don't need the whole application context to be reloaded a faster way is to copy themn automatically to the application server work directory. Some benefits are :
  • save time : no .war or .ear undeploy/redeploy
  • use [F5] to reload your resource instantly
    • the session is kept
    • the context is alive (jBPM, Spring web flow, DROOLS)
  • no more OutOfMemory or PermGenSpace when redeploying...
Next lines will show you how to set it up under JonAS. This HowTo is based on JonAS 5.2.4, Sun JKD 1.7, Maven 3.1.1, Windows 7 and Eclipse Juno. This should work on other OS as long as you go with Maven 3 and might be ported to other application servers...

WTP Adapter configuration

First of all, you need to tell JonAS WTP adatper not to publish automatically. The option is available when you double-click on the application server icons in the Eclipse "Servers" view :


Choose "Never publish automatically"... You'll then need to undeploy/deploy manually (remove and publish from the Eclipse "servers" view) when you want a code change to be taken in account or setup Cargo and configure the cargo:deploy goal to publish changes for you.

JonAS configuration

By default, JonAS will create a new directory each time an EAR or WAR is deployed. What we want is JonAS to use the same directory each time it deploys the same file, so we need to set the jonas.development property to false in the jonas.properties configuration file :

#  Set to true in order to execute the JOnAS Server in development mode.
#
#  WAR archive deployment case in development mode (for single or EAR packaged WARs):
#  Each modified WAR archive will be unpacked in the working directory of the 
#  JOnAS Server in a different folder to avoid file locks. 
#  This is especially useful in a Windows environment.
jonas.development    false
JonAS will then use a directory named against Eclipse project name.

Maven settings.xml configuration

As of Maven 3.0, profiles in the POM can also be activated based on properties from active profiles from the settings.xml.
As we want this trick to be a per user configuration we are going to rely on a property defined in the user's settings.xml to activate the static resource hot deployment :
<profile>
    <id>project-static-resources-hot-deploy</id>
    <activation>
        <activeByDefault>true</activeByDefault>
        <file>
            <exists>D:\PROJECT\jonas-full-5.2.4</exists>
        </file>
    </activation>
    <properties>
        <project.static.resources.hot.deploy.path>D:\PROJECT\jonas-full-5.2.4\work\webapps\jonas\single\{directory}</project.static.resources.hot.deploy.path>
    </properties>
</profile>
The {directory} part of the property must point to the WTP adapter deploy path for the desired Eclipse project.

Project configuration

Then we just need to add a profile activated based on the property defined in the user settings.xml that will trigger the use of the maven-resources-plugin to copy selected static resources to the application deployment path under JonAS. The copy is executed each time a static resource is saved in Eclipse and rely on the Maven Integration (m2e) for Eclipse.
<profile>
    <id>static-resources-hot-deploy</id>
    <activation>
        <property>
            <name>project.static.resources.hot.deploy.path</name>
        </property>
    </activation>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>static-resources-hot-deploy</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.static.resources.hot.deploy.path}</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>src/main/webapp</directory>
                                    <!-- if you use filtering in your build use true -->
                                    <filtering>false</filtering>
                                    <!-- whatever extension you want to copy -->
                                    <includes>
                                        <include>**/*.css</include>
                                        <include>**/*.jsp</include>
                                        <include>**/*.js</include>
                                        <include>**/*.tag</include>
                                    </includes>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

Troubleshooting

Sometimes, Eclipse ignore user settings.xml and does not activate project profiles based on settings.xml properties. As a workaround, you can :
  1. verify that Eclipse points to the correct user settings.xml in the following Eclipse menu Settings -> Maven -> Installations (you may need to restart Eclipse after changing this setting).
  2. add the profile that set the property directly in the project pom. 
Fork me on GitHub