Tag Archive for: Docker

At Jama we have migrated lots of functionally from Chef to Docker. Maybe that sounds a little odd? At first glance Chef and Docker don’t overlap much in functionality. But, as we develop our system with Docker, we constantly find ourselves referring to our Chef code for reference and as we implement new work in Docker, we find ourselves with functionality that allows us to deprecate our Chef based systems. What we’re looking at is two very different approaches to solving the same problems. We still have a lot of Chef kicking around so the rough edges of both Docker and Chef are constantly on my mind. These tools provide interesting points of comparison to each other.

Idempotency vs. Immutable servers

The configuration management tool domain specific languages (DSL’s) themselves are the most notable difference between Puppet and Chef. At times Puppet can feel painfully inflexible, while Chef makes sure to give you more than enough rope to hang yourself. Both try to push you toward good practices. Both encourage you to produce a catalog of idempotently applied declarations. At first glance, Docker seems to encourage you to throw out the best practices of configuration management tool languages; dockerfiles look suspiciously like shell scripts. Then you realize that every line of a dockerfile is creating a file-system snapshot and its clear that Docker is trying to solve some of the same configuration management challenges in a really different way. I should point out that nothing stops me from running Chef from within the Docker file but in practice I don’t know of anyone doing that.
Docker encourages you to wrap up most of your system as a reproducible and immutable image while configuration management tools encourage you to express your desired system state as a set of declarations. Both of these approaches help you avoid creating unnecessary complexity and both have weak points. With Docker I have concerns about how I will handle orchestration tasks in an emergency scenario. With configuration management tools I often find that what looked like declarative, idempotent code doesn’t actually handle edge cases like first boot or the existence or non-existence of a directory.
It feels like a step backwards to be doing configuration management using shell scripts and that’s what Docker has me doing. Things that I had solved using Chef’s declarative language start to turn into a procedural list of steps to create a desired configuration. On the other hand, it often feels like configuration management tools are taking on the impossible task of reimplementing and unifying the configuration languages of every piece of server software out there. At least when we fall back to shell scripts we naturally get back to handling configuration natively.
In the end, Docker or various configuration management tools all give us a way to approach defining our systems with code, code that gets checked into revision control systems.

The Image is the Cache

Docker effectively caches assets for us. As our Chef code gets more complicated we rely on more external artifacts. Although those artifacts are typically served by highly reliable services, as the number of them grows the odds that any one of them breaks on a given Chef run starts to be significant. We could eliminate that class of failure by hosting all of our artifacts locally, a strategy which requires complex artifact maintenance. Alternatively, we could use a caching proxy to access these artifacts. That reduces the risk that an artifact download fails but still would require us to maintain yet another highly available service. By using Docker, we are effectively caching the results of those downloads. We also move the impact of any failed downloads from deploy time to build time.
Failed downloads aren’t the only problem that we’d rather encounter at build time than at run time but tools like Chef don’t have a separate build time to take advantage of.
Running configuration management tools at deploy time is not only more fragile but also more time consuming. CM based deployments that I’ve worked on often take more than 10 minutes. AWS will usually get our AMI’s (host system images) into a running state in less than two minutes. We pay for that time and reliability advantage at deploy time with time spent and risk of fragility during the build.

Orchestration

Immutability itself has its disadvantages. Sometimes you can make a really small change on a system to solve a really big problem. The OpenSSL library on my Ubuntu system takes about 500K of disk space and installs in seconds with apt. To deploy the same with Docker requires a costly build step. And when our industry has wanted to deploy OpenSSL in the last couple of years, we’ve wanted to deploy it quickly.
Once, I worked in a place where Puppet controlled entries for the database servers through the /etc/hosts file on every host that would need to connect to them. One day we had a need to urgently update those host entries. Running puppet across our infrastructure could take a few minutes, but puppet was the system that had the information necessary to build the /etc/hosts file. On this occasion, the puppet server became overloaded by the level of parallelism that we were asking of it and stopped serving requests half way through the update. Rather than spending time recovering the puppet server we calculated new host entries with a shell script and pushed out the updates with parallel-scp. We would have liked a tool that could operate only on the /etc/hosts files but with all the information that the Puppet server had. In Docker small changes are even more expensive, as I described earlier in relation to OpenSSL.

Docker and Configuration Data

Chef’s databags leave us with key management pain. Docker’s golden path is very convenient when you’re working with Free Software but when you need to access artifacts that are restricted (when you need to use a password, for example as part of the download of an artifact referenced in your Dockerfile) it is very hard to keep that secret out of the Docker metadata for your image.
Docker encourages us to push as much of our configuration out of the image as possible. The natural place to move this configuration is environment variables; and Docker provides explicit support for this approach. But something has to set all those environment variables and Docker remains impartial about what. In our Chef managed environments, the same data comes from a global data store managed by the Chef server. Other configuration management tools provide similar features.
Docker doesn’t help us to manage such a global data store so we’ve been driven to start migrating our global configuration data into Consul, our discovery service of choice. Perhaps that’s a good thing; CM tools are a little awkward at the task while discovery tools can be very nimble.
Chef’s data bags at least address the issue of handling secret distribution, though awkwardly. Docker leaves us with the need to implement our own methods of safely distributing secrets needed at runtime.

Other Docker Advantages

Because we use Docker, we have a way to understand what produced our images. We can create a modified version of an image quickly. Once built, Docker images can be pushed out with less processing than configuration management code. So, despite their size, they don’t take longer to deploy.
Docker pushes us in the direction of immutable servers, servers that you never fiddle with by hand but only replace wholesale. Immutable servers set us up for really elegant zero-downtime deployments using the blue-green model. Blue-green deployments are the Holy Grail of DevOps, but there are many technical barriers to getting in a position to be able to use them. Without the constraints that Docker encourages, it would be a lot harder to lead an organization in the direction of blue-green deployments.

Docker in Production

Docker’s sweet spot is the development experience. People often express excitement when working with Docker for the first time; it is relatively easy to go from just starting out to having something that works. Things are a little less rosy for Docker in production. We’ve ended up building quite a bit of tooling around Docker to bridge the gap for our purposes.
We have not yet investigated Kubernetes or Docker Swarm to determine how we could use them with our system. One barrier to adapting these tools is the need to allow on-prem customers to deploy our containers in a simple way. That’s not a problem that everyone has to deal with but it affects how we can deploy Docker.
We have built a tool to share logic between the Docker builds of our different services, track dependencies and releases. It also helps work around the challenge of dealing with secrets during the build process. Another tool, that predated our use of Docker handles deploy-time configuration. It looks up context specific information in our AWS account and makes sure that the right values eventually get into place on the host system where a given Docker container will run. We also have tooling that collects boot-time information, from discovery services for example. Finally, our tooling handles clustering in AWS independent of any Docker-specific software.

End of Rumination …

Docker is a viable alternative to incumbent configuration management tools with its own advantages and disadvantages. Docker, like configuration management tools more generally, encourages certain good practices for integration and deployment of complex computing systems. The specific good practices that Docker encourages are different and perhaps a bit more radical than those encouraged by the more established configuration management tools.

Monitoring Java ApplicationsJava developers need to monitor and trouble-shoot Java applications from time to time. It’s usually straightforward to do that if an applications is running as a Java processes on your local machine. But when the Java application runs inside a Docker container on a Docker host, it becomes challenging to monitor them using tools running locally, even when the Docker host is just a virtual machine running on your desktop. In this blog post, I will describe a couple of ways to monitor such Java applications.

Connect VisualVM to an Application through JMX agents

VisualVM is a GUI tool that monitors and profiles a JVM. It comes with JDK and can be start by running “jvisualvm” command. You can also install it as a separate application. The tool connects to local Java processes directly, but in order to connect to a Java application running inside a Docker container, it needs to connect through a JMX port.

To enable remote JMX connection, you need to run your Java application in the Docker container with JVM options like these:

-Dcom.sun.management.jmxremote.rmi.port=9090

-Dcom.sun.management.jmxremote=true

-Dcom.sun.management.jmxremote.port=9090 

-Dcom.sun.management.jmxremote.ssl=false

-Dcom.sun.management.jmxremote.authenticate=false

-Dcom.sun.management.jmxremote.local.only=false

-Djava.rmi.server.hostname=192.168.99.100

Where java.rmi.server.host.name specifies the host name or IP address that your JMX client uses to connect to the target Java application.

Make sure to publish container’s port 9090 as the Docker host port 9090 when starting the Docker container:
$ docker run -p 9090:9090 {your_docker_image}

If your Docker host runs behind a firewall, you could use a SSH tunnel to connect to port 9090 of your Docker host. This time, you may need to set your RMI server host name to be localhost:
-Djava.rmi.server.hostname=localhost

Then ssh to Docker host using an SSH tunnel:
$ ssh -L 9090:localhost:9090 {your_docker_host}

This will forward the connection from your local port 9090 to localhostport 9090 on your Docker host server.

As a best practice, these JVM arguments should be passed as an environment variable that could be used by an entry script to start the Java application. For example:
$ docker run -p 9090:9090 -e MY_JAVA_OPTS=”…”  {your_docker_image}

Once your application is running with the the right options, you can connect your VisualVM to the application with the following steps:

1. Run VisualVM
$ jvisualvm

2. Add a remote host

Sean 1

3. Add a JMX connection
2 sean

3. Click on your JMS connection to connect to your application
sean 3

Running Java Mission Control and Java Flight Recorder

Java Mission Control (JMC) is a monitoring and performance tool offered by Oracle as a commercial feature of JDK 7 and 8. A key feature of JMC is Java Flight Recorder (JFR) that can be used to record event history for performance diagnosis and tuning. JMC is free to use for development.

To enable Java Mission Control, specify the following JVM options when starting your Java application in the Docker container, in addition to the same JMX-related options described above:

-XX:+UnlockCommercialFeatures

-XX:+FlightRecorder

Here are the steps to connect your JMC GUI client to a Java application that that has JMC/JFR feature enabled:

1. Run the Java Mission Control GUI that comes with Java JDK 7 or 8:
$ jmc

2. Set up a remote connection
sean 4 sean 5 sean 6

 

4. Click on 192.168.99.100:9090, then “MBean Server”, to monitor the application, or click on “192.168.99.100:9090” to start a flight recording session.
sean 7


RELATED POST: Checklist: Selecting a Requirements Management Tool


Setting up New Relic

New Relic is a hosted solution for application monitoring. To enable New Relic for your Java application, you need to have a New Relic account and install New Relic Java agent with your application. Here are the key steps to set it up. For more details, see the New Relic Documentation.

1. Download New Relic Agent ZIP file from https://docs.newrelic.com/docs/agents/java-agent/installation/install-java-agent.

2. Unzip the downloaded file. The unzipped file folder should contain files newrelic.jar and newrelic.yml

3. Edit newrelic.ymlfile to configure your license key and application name

license_key: ‘{your_newrelic_license_key}’

app_name: ‘{your_app_name}’

4. Include the unzipped folder in your Docker image

5. Start your Java application inside the container with the following JVM option:
-javaagent:/path/to/newrelic.jar

6. Log in to your New Relic account and your should see your application shows up in the application list.

Debug Java Application Remotely

To debug your Java application remotely using your favorite IDE, use the following JVM option to start your application assuming the debugging agent is running at port 5005:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

Make sure your publish port 5005 to your Docker host when starting your Docker container.

As an example, if you use IntelliJ as your Java IDE to debug, you can start by opening the project that contains the source code of your Java application.

Then, use “Run/Debug Configuration” Dialog to create a remote run/debug configuration.
sean 8

Now you can run “MyRemoteApp” application configuration to start remote debugging.

Summary

In this blog post, I have described how to configure VisualVM, JMC, New Relic, and Java remote debugging to monitor and profile your Java applications that runs inside Docker containers. I hope you find this information helpful!


Learn more about how Jama Connect streamlines tracking and tracing requirements.

SEE THE SOLUTION