Last active
August 29, 2015 14:02
-
-
Save ryanj/f0219a15407824bba14b to your computer and use it in GitHub Desktop.
Writing portable postgreSQL-backed applications for the Open Cloud
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<section> | |
<section> | |
<h1>PostGIS</h1> | |
<h2>in the Open Cloud</h2> | |
<p>writing portable open source mapping applications</p> | |
<p>with</p> | |
<h2>Leaflet</h2> | |
<p>and</p> | |
<h2>OpenShift</h2> | |
<br/> | |
<p><a href='http://bit.ly/1tpBqGA'>bit.ly/1tpBqGA</a></p> | |
<p><a href='http://ryanjarvinen.com/presentations/postgis-open-cloud'>http://ryanjarvinen.com/presentations/postgis-open-cloud</a></p> | |
</section> | |
<section> | |
<div>presented by</div> | |
<br/> | |
<div><a href='http://ryanjarvinen.com/'>ryan jarvinen</a> / <a href='http://twitter.com/ryanj/'>@ryanj</a></div> | |
</section> | |
<section data-state="blackout"> | |
<div> | |
<div>Open Source Evangelist</div> | |
<div>at</div> | |
<div>Red Hat</div> | |
<br/> | |
<div>[email protected]</div> | |
</div> | |
</section> | |
</section> | |
<section> | |
<section> | |
<h2>Agenda</h2> | |
<ol> | |
<li class='fragment'><a href='#/open-cloud'>Open Cloud Overview</a></li> | |
<li class='fragment'><a href='#/frameworks'>Select a language and a lightweight web framwork</a></li> | |
<li class='fragment'><a href='#/leaflet'>Add Leaflet for client-side map interactions</a></li> | |
<li class='fragment'><a href='#/api'>Build a simple API</a></li> | |
<li class='fragment'><a href='#/pg'>Install and configure PostgreSQL, PostGIS</a></li> | |
<li class='fragment'><a href='#/env-vars'>Use environment variables to keep your code clean</a></li> | |
<li class='fragment'><a href='#/action-hooks'>Bootstrap your DB</a></li> | |
<li class='fragment'><a href='#/launch'>Launch your instant mapping solution</a></li> | |
<li class='fragment'><a href='#/tune'>Learn about tuning PG on OpenShift</a></li> | |
</ol> | |
</section> | |
</section> | |
<section> | |
<section id='open-cloud'> | |
<p style='font-size:larger;'><b>the Cloud</b></p> | |
<p class='fragment roll-in'><i>"what is it made of?"</i></p> | |
</section> | |
<section> | |
<p style='font-size:larger;'>The cloud is:</p> | |
<ul> | |
<li class='fragment roll-in'>hot air?</li> | |
<li class='fragment roll-in'>a series of tubes?</li> | |
<li class='fragment roll-in'>mostly cat photos<span class="fragment roll-in">✓</span></li> | |
</ul> | |
</section> | |
<section> | |
<h1>Infrastructure</h1> | |
<h2 class='fragment'>as a service</h2> | |
<p class='fragment' style='text-align:left;'><i>inputs:</i></p> | |
<ul> | |
<li class='fragment roll-in'><b>Hardware:</b> racks, fiber, routers, storage, compute</li> | |
<li class='fragment roll-in'><b>Software:</b> OpenStack, Eucalyptus, CloudStack, SaltStack</li> | |
</ul> | |
<br/> | |
<br/> | |
<p class='fragment' style='text-align:left;'><i>Result:</i></p> | |
<ul> | |
<li class='fragment'>Easy on-demand Linux environments</li> | |
</ul> | |
</section> | |
<section> | |
<h1>Platform</h1> | |
<h2 class='fragment'>as a service</h2> | |
<p class='fragment' style='text-align:left;'><i>inputs:</i></p> | |
<ul> | |
<li class='fragment roll-in'><b>Hardware:</b> IaaS</li> | |
<li class='fragment roll-in'><b>Software:</b> SELinux, CGroups, Docker, HAProxy, ssh, git</li> | |
</ul> | |
<br/> | |
<br/> | |
<p class='fragment' style='text-align:left;'><i>Result:</i></p> | |
<ul> | |
<li class='fragment'>Horizontally scalable application architectures on-demand</li> | |
</ul> | |
</section> | |
<section> | |
<p>Providing standards-based, open source workflows that answer the question of:</p> | |
<br/> | |
<div class='fragment' style='font-style: italic;font-weight:bold;background:#666;padding:2%;'> | |
<p class='fragment grow' style='padding-bottom:4%'>"How do I</p> | |
<h1 style='display:inline-block;padding-bottom:6%;' class="fragment grow">Build,</h1> | |
<h1 style='display:inline-block;padding-left:7%;padding-bottom:6%;' class="fragment grow">Host,</h1> | |
<h1 style='display:inline-block;padding-left:7%;padding-bottom:6%;'>&</h1> | |
<h1 style='display:inline-block;padding-left:7%;padding-bottom:2%;' class='fragment grow'>Scale</h1> | |
<p class='fragment grow'>my solutions</p><p>on an</p> | |
<p class='fragment grow'>Open Cloud?"</p> | |
</div> | |
</section> | |
<section> | |
<p>Open platforms provide a peaceful environment for Developers AND Operations teams to work together in</h4> | |
<img width="300" height="303" src="../shared/img/DarthLuke.png"> | |
<ul> | |
<li class="fragment"><strong>Operations teams can help ensure system-wide stability and performance</strong></li> | |
<li class="fragment"><strong>Developers can quickly provision environments without waiting</strong></li> | |
<li class="fragment"><strong>The discussion shifts towards establishing great policies for scaling in response to demand</strong></li> | |
</ul> | |
</section> | |
<section data-state='alert'> | |
<h2>Terminology (Red Hat)</h2> | |
<ul> | |
<li class="fragment" style='padding-bottom:20px;'><strong>Broker – Management host, orchestration of Nodes</strong></li> | |
<li class="fragment" style='padding-bottom:20px;'><strong>Node – Compute host containing Gears</strong></li> | |
<li class="fragment" style='padding-bottom:20px;'><strong>Gear – Allocation of fixed memory, compute, and storage resources for running applications (SELinux, CGoups)</strong></li> | |
<li class="fragment"><strong>Cartridge – A technology, framework, or application: Python, Ruby, Javascript, PHP, Perl, Java/JEE, PG, MySQL, Jenkins, PHPMyAdmin, etc.</strong></li> | |
</ul> | |
</section> | |
<section> | |
<h3>An Open Cartridge format</h3> | |
<a href='http://openshift.github.io/documentation/oo_cartridge_developers_guide.html'><img style='width:50%' class='fragment roll-in' alt='OpenShift Cartridge' src='../shared/img/Openshift-Cartridge.png'/></a> | |
<p><a href='http://openshift.github.io/documentation/oo_cartridge_developers_guide.html'>cart developer's guide</a></p> | |
</section> | |
<section> | |
<img src='../shared/img/akbar_seems_legit.jpg'/><br/> | |
</section> | |
<section> | |
<h3>OpenShift Release Schedule</h3> | |
<img src="../shared/img/Three_products.png"> | |
</section> | |
</section> | |
<section> | |
<section id='frameworks'> | |
<h1>Frameworks</h1> | |
<ul> | |
<li class="fragment">NodeJS / Restify: <br/> | |
<span class='fragment'><a href='https://github.com/ryanj/restify-base'>https://github.com/ryanj/restify-base</a></span> | |
</li> | |
<li class="fragment">Python / Flask: <br/> | |
<span class='fragment'><a href='https://github.com/ryanj/flask-base'>https://github.com/ryanj/flask-base</a></span> | |
</li> | |
<li class="fragment">PHP / Silex: <br/> | |
<span class='fragment'><a href='https://github.com/ryanj/silex-base'>https://github.com/ryanj/silex-base</a></span> | |
</li> | |
<li class="fragment">Ruby / Sinatra: <br/> | |
<span class='fragment'><a href='https://github.com/ryanj/sinatra-base'>https://github.com/ryanj/sinatra-base</a></span> | |
</li> | |
</ul> | |
</section> | |
<section> | |
<h3>Language-specific Dependencies</h3> | |
<p>Automatic support for dependency resolution using standard packaging, native to each language:</p> | |
<p><a href='https://github.com/openshift-quickstart/adopt-a-hydrant-openshift-quickstart/blob/master/Gemfile'>gems</a> (ruby), <a href='https://github.com/shekhargulati/todo-flask-openshift-quickstart/blob/master/setup.py'>eggs</a> (python), and <a href='https://github.com/ryanj/darkslides/blob/master/package.json'>npm modules</a> (node.js)</p> | |
</section> | |
<section> | |
<h3>Language-specific DB bindings</h3> | |
<p>For nodejs:</p> | |
<pre><code contenteditable>npm install pg-query --save</code></pre> | |
<p><a href="https://npmjs.org/~brianc">brianc's</a> <a href="https://npmjs.org/package/pg-query"><code>pg-query</code> module</a> makes working with PG exceedingly simple</p> | |
<p>Just map your queries to their related callback functions.</p> | |
</section> | |
<section> | |
<h3>Local development</h3> | |
<p>Resolve dependencies:</p> | |
<pre><code contenteditable>npm install</code></pre> | |
<p>Fire up a local server:</p> | |
<pre><code contenteditable>npm start</code></pre> | |
</section> | |
</section> | |
<section> | |
<section id='leaflet'> | |
<h1>Leaflet</h1> | |
</section> | |
<section> | |
<p>Include a link to Leaflet's css stylesheet and javascript code in your index.html file:</p> | |
<pre><code contenteditable><link rel="stylesheet" href="//cdn.leafletjs.com/leaflet-0.5.1/leaflet.css" /> | |
<script src="//cdn.leafletjs.com/leaflet-0.5.1/leaflet.js"></script></code></pre> | |
</section> | |
<section> | |
<p>Initialize the map:</p> | |
<pre><code contenteditable>var map = L.map('map').setView([37.8, -122.3], 10); | |
var markerLayerGroup = L.layerGroup().addTo(map); | |
L.tileLayer('http://{s}.tile.stamen.com/terrain/{z}/{x}/{y}.png', { | |
maxZoom: 18, | |
minZoom: 5, | |
attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.' | |
}).addTo(map);</code></pre> | |
</section> | |
<section> | |
<p>Update the Map on load, drag, or zoom:</p> | |
<pre><code contenteditable>function getPins(e){ | |
bounds = map.getBounds(); | |
url = "parks/within?lat1=" + bounds.getSouthWest().lat + "&lon1=" + bounds.getSouthWest().lng + "&lat2=" + bounds.getNorthEast().lat + "&lon2=" + bounds.getNorthEast().lng; | |
$.get(url, pinTheMap, "json") | |
} | |
function pinTheMap(data){ | |
//clear the current pins | |
map.removeLayer(markerLayerGroup); | |
//add the new pins | |
var markerArray = new Array(data.length) | |
for (var i = 0; i < data.length; i++){ | |
park = data[i]; | |
markerArray[i] = L.marker([park.lat, park.lon]).bindPopup(park.name); | |
} | |
markerLayerGroup = L.layerGroup(markerArray).addTo(map); | |
} | |
map.on('dragend', getPins); | |
map.on('zoomend', getPins); | |
map.whenReady(getPins); | |
</code></pre> | |
</section> | |
</section> | |
<section> | |
<section id='api'> | |
<h1>Building an API</h1> | |
</section> | |
<section> | |
<pre><code contenteditable>var config = require('config'), | |
restify = require('restify'), | |
/bin/bash: indent: command not found | |
var app = restify.createServer() | |
app.use(restify.queryParser()) | |
app.use(restify.CORS()) | |
app.use(restify.fullResponse()) | |
// Routes | |
app.get('/parks/within', db.selectBox); | |
app.get('/parks', db.selectAll); | |
// Static assets | |
app.get(/\/(css|js|img)\/?.*/, restify.serveStatic({directory: './static/'})); | |
app.get('/', function (req, res, next) | |
{ | |
var data = fs.readFileSync(__dirname + '/index.html'); | |
res.status(200); | |
res.header('Content-Type', 'text/html'); | |
res.end(data.toString().replace(/host:port/g, req.header('Host'))); | |
}); | |
app.listen(config.port, config.ip, function () { | |
console.log( "Listening on " + config.ip + ", port " + config.port ) | |
}); | |
</code></pre> | |
</section> | |
</section> | |
<section> | |
<section id='pg'> | |
<h1>PG Setup</h1> | |
</section> | |
<section> | |
<h4>Adding Postgres to existing apps:</h4> | |
<pre><code contenteditable>rhc cartridge add postgres-8.4</code></pre> | |
<p>or</p> | |
<pre><code contenteditable>rhc cartridge add postgres-9.2</code></pre> | |
<br/> | |
<p class="fragment roll-in"><i>done!</i></p> | |
<br/> | |
<p class="fragment roll-in"><a href='https://www.openshift.com/blogs/postgresql-92-comes-to-openshift'>blog post: PostgreSQL 9.2 Comes to OpenShift</a></p> | |
</section> | |
<section> | |
<pre><code contenteditable>function select_box(req, res, next){ | |
//clean our input variables before forming our DB query: | |
var query = req.query; | |
var limit = (typeof(query.limit) !== "undefined") ? query.limit : 40; | |
if(!(Number(query.lat1) | |
&& Number(query.lon1) | |
&& Number(query.lat2) | |
&& Number(query.lon2) | |
&& Number(limit))) | |
{ | |
res.send(500, {http_status:400,error_msg: "this endpoint requires two pair of lat, long coordinates: lat1 lon1 lat2 lon2\na query 'limit' parameter can be optionally specified as well."}); | |
return console.error('could not connect to postgres', err); | |
} | |
pg('SELECT gid,name,ST_X(the_geom) as lon,ST_Y(the_geom) as lat FROM ' + table_name+ ' t WHERE ST_Intersects( ST_MakeEnvelope('+query.lon1+", "+query.lat1+", "+query.lon2+", "+query.lat2+", 4326), t.the_geom) LIMIT "+limit+';', function(err, rows, result){ | |
if(err) { | |
res.send(500, {http_status:500,error_msg: err}) | |
return console.error('error running query', err); | |
} | |
res.send(rows); | |
return rows; | |
}) | |
};</code></pre> | |
</section> | |
</section> | |
<section> | |
<section id='env-vars'> | |
<h1>Env Vars</h1> | |
<h3>For writing Clean and Portable Code<h3> | |
</section> | |
<section> | |
<pre><code contenteditable>rhc app show rss</code></pre> | |
<p>Or, while connected over ssh:</p> | |
<pre><code contenteditable>env | grep DB</code></pre> | |
<pre><code contenteditable>OPENSHIFT_POSTGRESQL_DB_PASSWORD=lXcFVx4hIZgR | |
OPENSHIFT_POSTGRESQL_DB_SOCKET=/var/lib/openshift/523672f7e0b8cd02d70003bc/postgresql/socket/ | |
OPENSHIFT_POSTGRESQL_DB_HOST=127.7.8.130 | |
OPENSHIFT_POSTGRESQL_DB_PID=/var/lib/openshift/523672f7e0b8cd02d70003bc/postgresql/pid/postgres.pid | |
OPENSHIFT_POSTGRESQL_DB_USERNAME=adminpahue6e | |
OPENSHIFT_POSTGRESQL_DB_URL=postgresql://adminpahue6e:[email protected]:5432 | |
OPENSHIFT_POSTGRESQL_DB_PORT=5432 | |
OPENSHIFT_POSTGRESQL_DB_LOG_DIR=/var/lib/openshift/523672f7e0b8cd02d70003bc/postgresql/log/</code></pre> | |
</section> | |
<section> | |
<p>Persist configuration details, <br/>while keeping your source clean:</p> | |
<pre><code contenteditable>module.exports = { | |
port: process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 3000, | |
ip: process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1', | |
pg_config: process.env.OPENSHIFT_POSTGRESQL_DB_URL || 'postgresql://127.0.0.1:5432', | |
table_name: process.env.OPENSHIFT_APP_NAME || process.env.PG_MAP_TABLE_NAME || 'parks' | |
}</code></pre> | |
</section> | |
<section> | |
<h3>Environment Variables</h3> | |
<p>Listing your custom env vars:</p> | |
<pre><code contenteditable>cd myapp | |
rhc env list</code></pre> | |
<p>Setting a variable:</p> | |
<pre><code contenteditable>rhc env set SECRET_TOKEN="a1fdacc3b1d14d6a92ed1219ed304d02529f535085262a90c39f072ef6de0ee9fe3a3d0194f02a2a8eb3"</code></pre> | |
<p>Help with configuration:</p> | |
<pre><code contenteditable>rhc help env</code></pre> | |
</section> | |
<section> | |
<p>Or, supply additional keys during the app creation process:</p> | |
<pre><code contenteditable>rhc app create hydrant ruby-1.9 postgresql-8.4 --from=code=http://github.com/ryanj/adopt-a-hydrant.git --env SECRET_TOKEN="YOUR_SECRET_TOKEN"</code></pre> | |
<p><a href='http://hydrant-shifter.rhcloud.com/'>http://hydrant-shifter.rhcloud.com/</a></p> | |
</section> | |
<section> | |
<h3>Advanced DB services mapping</h3> | |
<p><a href='https://www.openshift.com/blogs/set-up-local-access-to-openshift-hosted-services-with-port-forwarding'>Using port-forwarding for local dev</a></p> | |
<p><a href='https://www.openshift.com/blogs/cloud-connections-how-to-use-openshift-with-external-databases'>Or, connect to existing hosted PG services</a></p> | |
</section> | |
</section> | |
<section> | |
<section id='action-hooks'> | |
<h2>Automate your DB Setup</h2> | |
</section> | |
<section> | |
<h3>Action Hooks</h3> | |
<ol> | |
<li class="fragment roll-in">enable postgis</li> | |
<li class="fragment roll-in">create your table schema</li> | |
<li class="fragment roll-in">add a geospatial index</li> | |
<li class="fragment roll-in">bootstrap your db</li> | |
</ol> | |
<br/> | |
<br/> | |
<p class="fragment roll-in"><a href='https://github.com/ryanj/restify-postGIS/'>https://github.com/ryanj/restify-postGIS/tree/master/.openshift/action_hooks</a></p> | |
</section> | |
<section> | |
<pre><code contenteditable>var config = require('config'), | |
pg = require('pg-query') | |
var pg_config = config.pg_config, | |
table_name = config.table_name; | |
pg.connectionParameters = pg_config + '/' +table_name; | |
var points = require('../parkcoord.json'); | |
function initDB(){ | |
pg('CREATE EXTENSION postgis;', createDBSchema); | |
} | |
function createDBSchema(err, rows, result) { | |
if(err && err.code == "ECONNREFUSED"){ | |
return console.error("DB connection unavailable, see README notes for setup assistance\n", err); | |
} | |
var query = "CREATE TABLE "+table_name+ | |
" ( gid serial NOT NULL, name character varying(240), the_geom geometry, CONSTRAINT "+table_name+ "_pkey PRIMARY KEY (gid), CONSTRAINT enforce_dims_geom CHECK (st_ndims(the_geom) = 2), CONSTRAINT enforce_geotype_geom CHECK (geometrytype(the_geom) = 'POINT'::text OR the_geom IS NULL),CONSTRAINT enforce_srid_geom CHECK (st_srid(the_geom) = 4326) ) WITH ( OIDS=FALSE );"; | |
pg(query, addSpatialIndex); | |
};</code></pre> | |
</section> | |
<section> | |
<pre><code contenteditable>function addSpatialIndex(err, rows, result) { | |
pg("CREATE INDEX "+table_name+"_geom_gist ON "+table_name+" USING gist (the_geom);", importMapPoints); | |
} | |
function importMapPoints(err, rows, result) { | |
var query = "Insert into "+table_name+" (name, the_geom) VALUES " + points.map(mapPinSQL).join(",") + ';'; | |
pg(query, function(err, rows, result) { | |
var response = 'Data import completed!'; | |
return response; | |
}); | |
}; | |
function mapPinSQL(pin) { | |
var query = ''; | |
if(typeof(pin) == 'object'){ | |
query = "('" + pin.Name.replace(/'/g,"''") + "', ST_GeomFromText('POINT(" + pin.pos[0] +" "+ pin.pos[1] + " )', 4326))"; | |
} | |
return query; | |
};</code></pre> | |
</section> | |
</section> | |
<section> | |
<section> | |
<h1>Results!</h1> | |
<ul> | |
<li class="fragment">NodeJS / restify: <br/> | |
<span class='fragment'><a href='https://github.com/ryanj/restify-postGIS'>https://github.com/ryanj/restify-postGIS</a></span> | |
</li> | |
<li class="fragment">Python / Flask: <br/> | |
<span class='fragment'><a href='https://github.com/ryanj/flask-postGIS'>https://github.com/ryanj/flask-postGIS</a></span> | |
</li> | |
<li class="fragment">Java / Hibernate: <br/> | |
<span class='fragment'><a href='https://github.com/thesteve0/parkshibernatespatial'>https://github.com/thesteve0/parkshibernatespatial</a></span> | |
</li> | |
</ul> | |
</section> | |
</section> | |
<section> | |
<section id='launch'> | |
<h1>Launch Time!</h1> | |
<p class='fragment'>Spin up a fresh app from the command line:</p> | |
<pre class='fragment'><code contenteditable>rhc app create myapp cart1 cart2 --from-code=http://github.com/user/repo.git</code></pre> | |
<p class='fragment'>For our nodejs example:</p> | |
<pre class='fragment'><code contenteditable>rhc app create nodegis nodejs-0.10 postgres-9.2 --from-code=http://github.com/ryanj/restify-postGIS.git</code></pre> | |
</section> | |
<section> | |
<p style='font-style:italic;'>Live Result</p> | |
<p class='fragment'><a href='http://nodegis-shifter.rhcloud.com/'><img src='https://www.openshift.com/sites/default/files/Parks_preview.png' /><br/>nodegis-shifter.rhcloud.com/</a></p> | |
</section> | |
<section> | |
<p><i>~ related content ~</i></p> | |
<h3 class='fragment'>Web Workflows for Launching Apps</h3> | |
<p class='fragment'><a href='https://www.openshift.com/blogs/customizing-openshifts-web-based-app-creation-workflow'>Customizing OpenShift's Web-based App Creation Workflow</a></p> | |
<br/> | |
<h3 class='fragment'>Open Source Ribbons for Launching Apps</h3> | |
<p class='fragment'><a href='https://www.openshift.com/blogs/instant-hosting-of-open-source-projects-with-github-style-ribbons'>Instant Hosting of Open Source Projects with GitHub-style Ribbons</a></p> | |
<br/> | |
<h3 class='fragment'>Custom Domain Names and SSL</h3> | |
<p class='fragment'><a href='https://www.openshift.com/blogs/domain-names-and-ssl-in-the-openshift-web-console'>Domain Names and SSL in the OpenShift Web Console</a></p> | |
</section> | |
</section> | |
<section> | |
<section id='tune'> | |
<h1>Tuning PG</h1> | |
<p class='fragment'>aka, where is my pg_hba.conf, and postgresql.conf?</p> | |
</section> | |
<section> | |
<p>Some PG tuning notes were recently posted in the <a href='https://www.openshift.com/blogs/more-online-features-for-april-2014#pg-tuning'>OpenShift Online release announcement for April 2014</a></p> | |
<pre><code contenteditable>OPENSHIFT_POSTGRESQL_SHARED_BUFFERS</code></pre> | |
<pre><code contenteditable>OPENSHIFT_POSTGRESQL_MAX_CONNECTIONS</code></pre> | |
<br/> | |
<br/> | |
<p>More general tuning advice: <a href='https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server'>https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server</a></p> | |
</section> | |
<section> | |
<h3>Advanced configurations</h3> | |
<ul> | |
<li class='fragment'>Watch out for statistics collector issues in the pg-9.2 cart</li> | |
</ul> | |
<br/> | |
<br/> | |
<p class='fragment'>Take a look at the latest from CruchyData!</h3> | |
<ul> | |
<li class='fragment'><a href='https://github.com/crunchyds/openshift-postgres-cartridge'>9.3 cart is available</a></li> | |
<li class='fragment'><a href='https://github.com/crunchyds/openshift-postgres-rls-cartridge'>9.4 devel with RLS patch</a></li> | |
</ul> | |
</section> | |
<section> | |
<h2>HA for PG by CrunchyData</h2> | |
<p>See their release announcement for additional details: <a href='http://crunchydatasolutions.com/crunchy-latest/crunchy-announces-high-availability-postgresql-for-openshift-enterprise/'>crunchydatasolutions.com</a></p> | |
<br/> | |
<p>Including support for LB, geographic failover, Master-slave replication, and more!</p> | |
</section> | |
</section> | |
<section> | |
<section> | |
<h2>Join the Community</h2> | |
<ul> | |
<li class="fragment">We accept pull requests</li> | |
<li class="fragment">Help with anything from core, quickstarts, and cartridges, to small typo fixes in the command line tools </li> | |
<li class="fragment">PEPs for major feature enhancements</li> | |
<li class="fragment"><a href="https://github.com/openshift/origin-server/blob/master/CONTRIBUTING.md" class="roll">Contribution Guidelines</a></li> | |
<li class="fragment"><a href="https://trello.com/openshift" class="roll"><span data-title="Public Trello cards">Public Trello cards</span></a></li> | |
<li class="fragment"><a href="https://tcms-openshift.rhcloud.com/plan/2/openshift-origin" class="roll"><span data-title="Public Test plans">Public Test plans</span></a></li> | |
<li class="fragment"><a href="https://bugzilla.redhat.com/" class="roll"><span data-title="Public Bugzilla">Public Bugzilla</span></a></li> | |
<li class="fragment"><a href="https://www.openshift.com/ideas" class="roll"><span data-title="Voting on Features">Vote on Features</span></a></li> | |
</ul> | |
</section> | |
<section> | |
<h2>Origin.ly</h2> | |
<p><a href='http://origin.ly/search?query=postgres'>The Origin.ly Appication and Cartridge index</a></p> | |
</section> | |
<section> | |
<h2>OpenShift Core Roadmap:</h2> | |
<ul> | |
<li class="fragment"><a href='http://www.projectatomic.io/'>Project Atomic</a></li> | |
<li class="fragment"><a href='https://www.openshift.com/blogs/technical-thoughts-on-openshift-and-docker'>Docker</a></li> | |
<li class="fragment"><a href='https://www.openshift.com/blogs/geard-the-intersection-of-paas-docker-and-project-atomic'>GearD</a></li> | |
</ul> | |
</section> | |
<section> | |
<p>Check out the upstream source: <br/><a href='http://openshift.github.com/'>OpenShift Origin</a></p> | |
<br/> | |
<p>Try our hosted solution (3 apps free): <br/><a href='https://openshift.redhat.com/app/account/new?web_user[promo_code]=PGCon2014'>OpenShift Online</a></p> | |
<br/> | |
<p>Request an evaluation for: <br/><a href='https://www.openshift.com/products/enterprise'>OpenShift Enterprise</a></p> | |
</section> | |
<section data-state="blackout" id='bye'> | |
<h1>Thank You!</h1> | |
<p>See my post on this topic for more info: <a href='https://www.openshift.com/blogs/instant-mapping-applications-with-postgis-and-nodejs'>Instant Mapping Applications with PostGIS and Nodejs</a></p> | |
<br/> | |
<p>Link to these slides: <a href='http://bit.ly/1tpBqGA'>bit.ly/1tpBqGA</a></p> | |
<br/> | |
<p><b><i>See you next time!<br/> --ryanj</i></b></p> | |
</section> | |
</section> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment