100-day Challenge #080: Running CommaFeed on Cloud Foundry

By: | April 19, 2016
Share

Translator’s note: This is the 17th article of the series “Cloud Foundry 100-day Challenge selection”. “#080” in the title means that it is 80th (published on October 16, 2015) in the original Japanese series.

Original Author: Noburou TANIGUCHI (GitHub) (BitBucket)


The 80th topic of “Cloud Foundry 100-Day Challenge” is the third (and most likely last) of the “Feed Readers that I am Personally Looking For” series, CommaFeed (1: Feedbin and 2: Tiny Tiny RSS (Translator’s note: Both are in Japanese)) . It is implemented with Java’s Dropwizard framework.

Basic Information

The summary of procedures is as follows:

  • 1) Retrieving Source Code
  • 2) Building Application
  • 3) Creating and Binding Service
  • 4) Creating Configuration File and Updating JAR File
  • 5) Setting Environment Variables (Creating manifest.yml)
  • 6) Deploying Application
  • 7) Checking Application Behavior

1. Retrieving Source Code

We clone the source code from GitHub, and move to its directory.

$ git clone https://github.com/Athou/commafeed.git
..
$ cd commafeed/

2. Building Application

With Cloud Foundry’s java-buildpack, we need to build the application prior to deploying.
Let’s build the application based on its README.

$ ./mvnw clean package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building CommaFeed 2.3.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
..
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:52 min
[INFO] Finished at: 2015-10-12T04:57:03+09:00
[INFO] Final Memory: 60M/569M
[INFO] ------------------------------------------------------------------------

target/commafeed.jar should have been generated.

$ ls target/commafeed.jar
target/commafeed.jar

3. Creating and Binding Service

CommaFeed uses DBMS for persistent data. It is configured to use H2 by default, but H2 will store data into local files, resulting in data loss upon restart on Cloud Foundry. Hence, we will use a PostgreSQL service in this post.

Uploading Application

Let’s push the application to Cloud Foundry without starting.

$ cf push commafeed -p target/commafeed.jar --no-start
Creating app commafeed in org nota-ja / space 100 as nota-ja...
OK
..

Creating Service

Next we create a PostgreSQL service.

$ cf create-service PostgreSQL "Basic PostgreSQL Plan" pg-commafeed
Creating service instance pg-commafeed in org nota-ja / space 100 as nota-ja...
OK
..

Binding Service

We then bind the application with the service.

$ cf bind-service commafeed pg-commafeed
Binding service pg-commafeed to app commafeed in org nota-ja / space 100 as nota-ja...
OK
..

4. Creating Configuration File and Updating JAR File

CommaFeed requires a configuration file to start, so we need to create one. Additionally, we store it in the JAR file so that this settings file can be uploaded.

Creating Configuration File

We copy config.yml.example, and make necessary modifications.

$ cp config.yml.example config.yml
$ emacs config.yml

The following are the three main modifications:

  • pulicUrl
    We need to set this to match the application URL.
  • allowRegistrations
    We need to enable user registrations from the application screen.
    This application has an administrator screen where users can be registered, but since Cloud Foundry does not currently allow assigning multiple ports to one application (development is under way), today we chose to enable registrations from the application screen.
  • database
    Lastly, we need to run cf env and set appropriate values in accordance with database connection information from VCAP_SERVICES.
$ cf env commafeed
Getting env variables for app commafeed in org nota-ja / space 100 as nota-ja...
OK

System-Provided:
{
 "VCAP_SERVICES": {
  "PostgreSQL": [
   {
    "credentials": {
     "uri": "postgres://ea4badfa-a871-4237-a534-9d69fb464200:v2qmkpa772n1r35gdqrko158nr@192.168.15.91:5432/ea4badfa-a871-4237-a534-9d69fb464200"
    },
    "label": "PostgreSQL",
    "name": "pg-commafeed",
    "plan": "Basic PostgreSQL Plan",
    "tags": [
     "PostgreSQL",
     "Database storage"
    ]
   }
  ]
 }
}
..

This is what it looked like after all modifications:

$ git diff --no-index -- config.yml.example config.ymldiff --git a/config.yml.example b/config.yml
index 5f6d167..2f7e219 100644
--- a/config.yml.example
+++ b/config.yml
@@ -2,13 +2,13 @@
 # ------------------
 app:
   # url used to access commafeed
-  publicUrl: http://localhost:8082/
+  publicUrl: http://commafeed.10.244.0.34.xip.io/^M

   # wether to allow user registrations
-  allowRegistrations: false
+  allowRegistrations: true^M

   # create a demo account the first time the app starts
-  createDemoAccount: false
+  createDemoAccount: true^M

   # put your google analytics tracking code here
   googleAnalyticsTrackingCode:
@@ -49,7 +49,7 @@ app:
   queryTimeout: 0

   # time to keep unread statuses (in days), 0 to disable
-  keepStatusDays: 0
+  keepStatusDays: 90^M

   # entries to keep per feed, old entries will be deleted, 0 to disable
   maxFeedCapacity: 500
@@ -75,10 +75,10 @@ app:
 # url is jdbc:jtds:sqlserver://localhost:1433/commafeed;instance=<instanceName, remove if not needed>

 database:
-  driverClass: org.h2.Driver
-  url: jdbc:h2:/home/commafeed/db;mv_store=false
-  user: sa
-  password: sa
+  driverClass: org.postgresql.Driver^M
+  url: jdbc:postgresql://192.168.15.91:5432/ea4badfa-a871-4237-a534-9d69fb464200^M
+  user: ea4badfa-a871-4237-a534-9d69fb464200^M
+  password: v2qmkpa772n1r35gdqrko158nr^M
   properties:
     charSet: UTF-8
   validationQuery: "/* CommaFeed Health Check */ SELECT 1"
@@ -95,9 +95,9 @@ server:
       port: 8084

 logging:
-  level: WARN
+  level: INFO^M
   loggers:
-    com.commafeed: INFO
+    com.commafeed: DEBUG^M
     liquibase: INFO
     io.dropwizard.server.ServerFactory: INFO
   appenders:

Aside from the major three points mentioned earlier, feel free to make additional adjustments to the configurations as you like.

Updating JAR File

In order to bring the created configuration file onto Cloud Foundry, we add it to target/commafeed.jar.

$ jar uvf target/commafeed.jar config.yml
adding: config.yml(in = 3411) (out= 1531)(deflated 55%)

5. Setting Environment Variables (Creating manifest.yml)

In order to run a Dropwizard JAR file on Cloud Foundry’s java-buildpack, we need to:

  • Specify a port to listen on with a java command argument
  • Specify command and the configuration file using the main class (com.commafeed.CommaFeedApplication) arguments

Today, we employed the following measures:

  • As a method to hand over arguments to the java command, we used JAVA_OPTS environment variable (Reference)
  • As a method to hand over arguments to classes specified by the Main-Class of executable JAR, we used JBP_CONFIG_JAVA_MAIN environment variable (Reference)
    [Extras]

    • Originally, this was done by forking the java-buildpack repository and rewriting config/java_main.yml.
    • From v3.0, we no longer needed to fork, as a method was implemented to inject the content of these configuration files through environment variables.
    • This “method using environment variables” is a bit tricky, but the overview is the following:
      When injecting content to a file called config/<settings name>.yml (e.g.: open_jdk_jre.yml) through environment variables:

      • We take an environment variable called JBP_CONFIG_<configuration name in ALL CAPS> (e.g.: JBP_CONFIG_OPEN_JDK_JRE),
      • And we script the configuration value with a flow-style YAML. (e.g.: '[jre: { version: 1.7.0_+ }]')

We include the information necessary for pushing the application, including these environment variables, in manifest.yml.

$ cat manifest.yml
---
applications:
- name: commafeed
  path: target/commafeed.jar
  memory: 1024M
  buildpack: https://github.com/cloudfoundry/java-buildpack.git#v3.3
  services:
  - pg-commafeed
  env:
    JAVA_OPTS: '-Djava.net.preferIPv4Stack=true -Ddw.server.applicationConnectors[0].port=\$PORT'
    JBP_CONFIG_JAVA_MAIN: '[arguments: "server ./config.yml"]'

We set the memory at 1GB to leave some margin. We also set it to retrieve the latest release (Translator’s note: At the time of the original post), v3.3, from the internet because the support for config/java_mail.yml began with java-buildpack v3.2, and was not available with java-buildpack v3.0 which is the standard for the bosh-lite based Cloud Foundry environment (cf-release v211) that we used today.

6. Deploying Application

Now that the preparations are complete, we can now deploy the application.

$ cf push
..
requested state: started
instances: 1/1
usage: 1G x 1 instances
urls: commafeed.10.244.0.34.xip.io
last uploaded: Mon Oct 12 06:56:07 UTC 2015
stack: cflinuxfs2
buildpack: https://github.com/cloudfoundry/java-buildpack.git#v3.3

     state     since                    cpu      memory         disk      details
#0   running   2015-10-12 04:35:59 PM   105.4%   485.1M of 1G   0 of 1G

.. and it is up and running.

7. Checking Application Behavior

When we access the application URL, we get redirected to the Welcome screen:

Let’s register a user:

Once the user registration is complete, we are automatically logged in:

We will register a feed using the [Subscribe] button to the upper left:

If the feed registration is successful, we get a list of titles of unread articles:

We can read articles when we click on their titles:

We can enter the settings screen if we click the gear-shaped button to the upper right, so we will try setting the language to Japanese:

Click on [Save], and we return to the standard screen.

Here, we see that the menu, etc. are now displayed in Japanese:

This concludes our check of the application behavior.

The impression we got was that, although it required quite a lot of memory, the action was quick and stable, and the functionality was simple but enough. We also valued the Japanese support because it seemed rare.

This application also has a function to send an email when a user forgets her/his password, and a function to use Redis as a cache, but we ran out of time before trying them out. We also omitted the administrator screen from this post, but we did confirm that we can access the administrator screen by changing the configuration of JAVA_OPTS, so we did think that we would like to be able to switch between the two.

Software Used in This Post