Community

Enhancing Ghost.js with Cloudant on IBM codename: BlueMix

Share this post:

As a followup to my last post for deploying Ghost.js on IBM codename:BlueMix, I would like to share a follow-up technique which utilizes a Cloudant CouchDB instance as a persistent image datastore.

TL;DR

Why would I want to do that?

My earlier post generated a healthy amount of comments and inquiry for overcoming the ephemeral nature of file storage that exists in most Platform As a Service technologies, which includes Cloud Foundry and BlueMix. Briefly, BlueMix applications are typically given an ephemeral file system and isolated from read/write access to any persistent disk. The Cloud Foundry community recommends that applications avoid writing to the local file system. [Reference] Some reasons for this include:

  • Local file system storage is short-lived. When apps crash, stop or are restarted, resources can be reclaimed by the platform.
  • Instances of the same app do not share a local file system. Each app runs in its own container. If you scale up instances, both instances will not have access to the same file system.

This creates an interesting quandry. Ghost.js rocks! BlueMix can rapidly deploy, host and scale up a Ghost.js instance, which is awesome! But, BlueMix uses an ephemeral file storage and most bloggers will not tolerate being constrained to using images defined by URI only nor being constantly hassled with having to go upload local image files to some other place. [As an interesting aside: Ghost.js provides a configuration option named filestorage to eliminate the local file upload scenario entirely and be more compatible with PaaS environments]

Enter Cloudant. Let’s exploit BlueMix’s composeable service model to quickly attach a Cloudant CouchDB instance to our deployed Ghost.js app. A couple of BlueMix UI clicks and a quick Cloudant account create should be the elixir to our ills 🙂 I considered MongoDB’s GridFS capability as well, but settled on Cloudant because of its stellar dashboard management and responsive performance as a proven commercial solution. After 5 mins of clicking – well, that also includes one quick email and an instant message, the next chore was to empower Ghost.js to leverage this storage resource. I copied the localfilesystem module and created a new one called cloudant.js. I dutifully tweaked the save and exists functions to interact with Cloudant using the simple, but solid, Nano Node.js package. This resulted in a user experience where I can upload local files into my BlueMix Ghost.js deployment, while having Ghost.js be smart enough to subsequently go and create a representative Cloudant document and attachment which it will then exploit as a linked image resource. Sweet! Ghost.js now always uses URI defined image sources and the user experience remains intact for uploading local files without additional end-user hassle.

If this has piqued your interest, give it a try. You’ll love the results. Here’s how …

  1. Download the latest https://ghost.org/download/
  2. Unzip the download (e.g. unzip Ghost-0.4.2.zip) and change to the base directory (e.g. ghost-0.6.4)
  3. Clone a copy of this git repository and copy the content within the files folder into the root of your ghost installation directory. You will replace two (2) new files (package.json and core/server/storage/index.js), delete one (1) file (npm-shrinkwrap.json), add four (4) new files (config.js, manifest.yml, manifest.cfg and core/server/storage/cloudant.js) and modify one (1) file (core/server/api/upload.js) to the default Ghost.js installation to enable its use.
  4. Open the core/server/api/upload.js and edit line #44
    from:  return Promise.promisify(fs.unlink)(filepath);<br />to: return Promise.promisify(function(){return true});

    Reason: While the local files are temporarily stored on the local filesystem, our new CloudantFileStore actually tells Ghost a url that is an HTTP URL which we’ve generated on-the-fly via a binary attachment into Cloudant. Unlinking is unnecessary and these tmp files will be destroyed during app future app re-staging/restart as a victim of the ephemeral storage paradigm.

  5. Update the name, host and services entries within the manifest.yml to match your deployment environment. The service entries for both the MySQL and Cloudant Service instance names are totally up to you.
  6. Using the BlueMix Web UI, login to BlueMix, and create two (2) back-end database service instances (MySQL and Cloudant) that matches your manifest.yml entries.
    IMPORTANT: When creating the Cloudant Service Instance, the url field should be in the format of <your_cloudant_username>.cloudant.com . Your database can be named anything and your username/password will be whatever was used when setting up your Cloudant account. Be sure to alter your image store database permissions so that reader access is granted to everyone. This is easily done by clicking on the DB within the Cloudant dashboard, then clicking on the Docs dropdown button and choosing permissions. Click the Reader checkbox for the Everybody Else category. A database will automatically be located or created with the format <app_name>-ghost-images for use by Ghost.js during application startup. Permissions are automatically set for Everyone on the database to have read access.
  7. Navigate to the root of the Ghost.js installation directory. Using the cloud foundry v6 cli, login to BlueMix and deploy Ghost.js.
    #Step 0: cf login -a 
    <b>$ cf login -a https://api.ng.bluemix.net</b>
    
    # Step 1: CF_STAGING_TIMEOUT=4 CF_STARTUP_TIMEOUT=4 cf push -s cflinuxfs2
    # CF_STAGING_TIME : Max wait time for app instance startup, in minutes
    # CF_STARTUP_TIMEOUT : Max wait time for app instance startup, in minutes
    # -s : Establishes the stack to use as Trusty Ubuntu 14.04<br /># ENV variables are suggested to accommodate longer staging and startup times.
    <b>$ CF_STAGING_TIMEOUT=4 CF_STARTUP_TIMEOUT=4 cf push -s cflinuxfs2</b>
    
    
  8. If all goes well, you should have a running Ghost.js blogging platform. Browse and setup your user. Then visit: <your_app_name>.mybluemix.net/ghost/ .

Testing

There are two (2) ways to embed an image and there are at least two places where images are used within Ghost.js.

  1. Place #1: Settings. You can click on the upload image buttons for a blog cover and blog logo. You will see a little chain-link icon in the bottom left. That toggles the UI from being a file upload to a direct url link to the image. Feel free to give both ways a try, but the Cloudant tweak affects only the local file upload flow. After uploading an image and saving, you can verify if the tweak is working by using the browsers “inspect element” context menu. This should show an image that has a source using a cloudant url pointing to your account. In theory, this should never go away if the account remains in good standing and you never delete the database or document.
  2. Place #2: Content. You can embed an image using the markdown syntax ![alt text](url to image). If you simply type ![an inspiring image of earth] … you will see a familiar image uploader dialog in the preview that can be clicked. Same options hold true, direct web url or local file upload. Same technique to verify if its using Cloudant.

Running Example can be seen @ http://blueghostrox.mybluemix.net/

Cool. Happy Blogging!

Add Comment
11 Comments

Leave a Reply

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


John W. Williams

Hey,
My name is John and I am a user experience here at IBM. I actually work on the newer reporting solutions working their way through Rational right now…anyhow, I am in need of a fresh online portfolio and I have been wanting to try Ghost. I would also like to get everything up and running on Bluemix, if at all possible. I found you cool example and explanation of how you got it up and running and it looks like a feasible solution.

Unfortunately, I am not a dev per se and the whole Bluemix/web application deployment via cloud services thing is something I am just getting my head wrapped around…and this experiment with Bluemix strikes me as an ideal means to get my iron hot.

That said, everything seems rather clear to me. I have successfully deployed simple “Hey World” apps on BlueMix in the past. My issue here concerns some confusion over Cloudant and which account to use. I have a unique Cloudant account (the most basic, free version). I cannot figure out how to use this unique account to create my Cloudant service when using BlueMix…am I missing something or do you recommend I simply use the account that is auto-generated when I attempt to create a new service instance using Bluemix.

Also, are there any plans to make an auto deploy version of Ghost (similar to that found on OpenShift) for Bluemix? This would strike me as a awesome move in the “coolness” direction as Ghost seems to have quite a bit of momentum as a simple, fast blogging platform and many hosts seem to be struggling to get up to speed, for various reasons.

Any help would be appreciated…
John

Reply

    John W. Williams

    It may help if I clarify that I cannot find any means–when creating my Cloudant service–to change the URI to match myCloudantusernamecloudantcom. The app field, which I presume is the URI field you mention, is not editable.

    Reply

Sanjay.Joshi

Hi John,
While I wrote the article to leverage the built-in generated Cloudant account provided by the Bluemix service, there is no hard requirement to point to that. Take a look at: https://github.com/ibmjstart/bluemix-ghost-js/blob/master/files/core/server/storage/cloudant.js#L24-35 . These lines within the cloudant.js file (and used with Step 3) are what define the url to use with Cloudant. I’m dynamically reading the environment variable to grab the Bluemix service details … but you could comment/delete these lines and provide your own details in the form of a JSON string. Something like:

cloudantCreds = {
      "url": "yourname.cloudant.com",
      "database": "ghostimages",
      "username": "yourcloudantuser",
      "password": "yourcloudantpassword"
    }

Hope this helps. Eager to hear how it goes.

Reply

JohnWiltonWilliams

I was playing around with this again today and I cannot get my app deployed. Here is what I see. Everything looks fine except the app will not start once the push has completed.

[quote]C:UsersIBM_ADMINDocumentsGitHubjohnyux.mybluemix.net>cf push
Using manifest file C:UsersIBM_ADMINDocumentsGitHubjohnyux.mybluemix.netma
nifest.yml

Updating app johnyux in org johnww@us.ibm.com / space dev as johnww@us.ibm.com..
.
OK

Using route johnyux.mybluemix.net
Uploading johnyux…
Uploading app files from: C:UsersIBM_ADMINDocumentsGitHubjohnyux.mybluemix.
net
Uploading 818K, 220 files
OK
Binding service ClearDB MySQL Database-di to app johnyux in org johnww@us.ibm.co
m / space dev as johnww@us.ibm.com
OK
Binding service Cloudant NoSQL DB-8c to app johnyux in org johnww@us.ibm.com / s
pace dev as johnww@us.ibm.com
OK

Stopping app johnyux in org johnww@us.ibm.com / space dev as johnww@us.ibm.com..
OK
Starting app johnyux in org johnww@us.ibm.com / space dev as johnww@us.ibm.com..
.
FAILED
Server error, status code: 500, error code: 0, message:
[/quote]

Reply

    spj

    Hi John,
    Checkout my comment here. Specifically, I discovered a bug in my approach for using the manifest.yml as a config data source. I suspect that your app_url isn’t being populated correctly which causes a startup failure and an error during startup of a missing manifest.yml file relevant lines of code. Try

    cf logs appname --recent

    to see the errors. To resolve this … you can either hardcode your appurl info into the defined config.js variable OR create a copy of your manifest.yml (e.g. manifest.yml.cfg) and then modify https://github.com/ibmjstart/bluemix-ghost-js/blob/master/files/config.js#L19 to reflect this new file name.

    If you’re curious about why this happens: I think that CloudFoundry includes the manifest.yml file as an “ignoreable” file during pushes since in theory it is only supposed to be used to tell CF how to deploy and not tell the app how to run. It was a flaw in my thinking to use it.

    Reply

Guy

I feared for long hours setting this up, but amazingly it took about 15 minutes.. thank you for that

Just one comment i had to remove ‘https://’ + from line 144 in your cloudant.js file .. as the service was already prepending this, so uploading images gave me http://http://xxx.cloudant.com
after removing this http:// is nicely worked

Reply

    Sanjay.Joshi

    @Guy – Awesome! Glad that it worked smoothly and more importantly – quickly. I’ve updated the git repo files to remove the prefixed ‘https://’ string for the benefit of others. Appreciate the feedback.

    Reply

JohnWiltonWilliams

Awesome Sanjay. I will try this out using the new info and process you have documented. I gave up on my original exploration as I got sidetracked and slammed with work-related things.

Reply

[…] projects onto Bluemix. In past posts, I’ve shared how to deploy Drupal, WordPress, Ghost.js and Piwik. I’ve now turned my focus to Eve, a Python-based open source web REST API framework […]

Reply

juan

Hi Sanjay,

Great job! I got my first blue mix up and running! But I got some erros;
Error: Cannot find module ‘intl’
Error: Cannot find module ‘intl-messageformat’
So added this 2 lines;
“intl”: “1.0.0”,
“intl-messageformat”: “1.1.0”,
to package.json file. Now it works!

Cheers,

Juan

Reply

    Sanjay.Joshi

    Hi Juan,
    Awesome! Glad you were able to get it deployed with minimal trouble. Thanks for the heads up. I’ve located the package.json commit that caused this here (https://github.com/TryGhost/Ghost/commit/d89abaa15edf5990887e1c6341ca547fc13c97d8#diff-b9cfc7f2cdf78a7f4b91a753d10865a2) . In general, this will be an ongoing challenge to our repo since we’re maintaining a fork of the TryGhost project’s package.json. Feel free to send us a PR on our repo for updates to the package.json which include your mentioned changes and I’ll merge it in. Otherwise, I’ll try to make the tweak later this week. Thanks again for the feedback 🙂

    Reply
More How-tos Stories

Latently, Bitfusion, and IBM Cloud enable democratized access to deep learning

Latently, Bitfusion, and IBM Cloud partnered to provide a deep learning development platform and best-in-class GPU infrastructure allowing you to publicly replicate all AI and ML scientific papers via the Latently Deep Learning Certificate (LDLC) program.

Continue reading

Securely Access MongoDB from a Spring Boot App Running on Bluemix Kubernetes Using Kubernetes Secrets

This example builds on my previous post where I showed how to access a Bluemix MongoDB service from a Spring data app running locally. In that simple example the MongoDB credentials were either hard coded in the application or specified manually on the command line.

Continue reading

Accessing a Bluemix MongoDB Service from a Java Spring Boot Application

In this blog post I'll show how to access a Compose for MongoDB database running on IBM Bluemix from a Spring boot application running locally. Spring is a popular open source framework and container for Java applications. MongoDB is a popular open source document oriented NoSQL database that uses JSON-like documents.

Continue reading