Who am I:
- email: [email protected]
- github: @sam-github
- twitter: @octetcloud
???
- Describe what I did at StrongLoop
- Wrote a Node.js app?
- In production?
- ... on prem?
- ... in a cloud?
linear increase in capacity with increase in resources
- What is capacity?
- What is resources?
I. Codebase II. Dependencies III. Config IV. Backing Services V. Build, release, run VI. Processes VII. Port binding VIII. Concurrency IX. Disposability X. Dev/prod parity XI. Logs XII. Admin processes
???
Describe where from, and why related to scale
I will use as framework to discuss
I. Codebase: One codebase tracked in revision control, many deploys II. Dependencies: Explicitly declare and isolate dependencies V. Build, release, run: Strictly separate build and run stages
III. Config: Store config in the environment IV. Backing Services: Treat backing services as attached resources VII. Port binding: Export services via port binding X. Dev/prod parity: Keep development, staging, and production as similar as possible XI. Logs: Treat logs as event streams
VI. Processes: Execute the app as one or more stateless processes VIII. Concurrency: Scale out via the process model IX. Disposability: Maximize robustness with fast startup and graceful shutdown XII. Admin processes: one-off processes
What are people using for version control?
- git
- mercurial
- perforce
- subversion
- CVS/RCS/SCCS?
- ...?
Note: We can git push to Heroku and OpenShift, but not to Bluemix
Node.js/npm makes it difficult to not do this:
- node will not require "globally" installed packages
- npm installs its dependencies into the current package
???
- Elaborate on the no-global-require, many people do not realize
- Use loose dependencies: XXX node-semver
"express": "^4.3"
???
Make the case for loose...
... mesh-models has 250 unique modules as deps, sometimes multiple versions
- Fix dependencies late, use
npm shrinkwrap
or strong-build/apic build
- Read my blog: https://strongloop.com/strongblog/node-js-deploy-production-best-practice/
???
- discuss shrinkwrap
- discuss git commit
- discuss npm packfiles
- Use npm packages for independent code, and try to build independent code
- Auto-publish your masters to a staging npm registry
- Resolve packages from the staging registry
- Use semver during development
???
ci and node.... talk about why its related
rmg:
for our dependant package builds, for a PR I basically fork off a new universe where that PR is published as the latest version of that package, and then see how the packages that depend on it work in that universe I created a docker image specifically for this usage of sinopia https://hub.docker.com/r/strongloop/ephemeral-npm/
The release stage takes the build produced by the build stage and combines it with the deploy’s current config. The resulting release contains both the build and the config and is ready for immediate execution in the execution environment.
Can be hard to do. At the least, pre-compile your assets at build time, not deploy time:
- npm install
- native addon build (unless its a cloud)
- template and css pre-compilation
all belong at build, not deploy.
???
Should strong-supervisor be injected? Even for BlueMix, this seems to be a good idea, restarts would be seconds, not 10s of seconds (or more) for a full container restart.
- Forces seperation of code and config
- All orchestrators support environment injection
- Pay attention to orthogonality
- You can still group ENV vars in files, or point to files (works well with puppet or chef)
12-factor anti-pattern, do it anyway:
NODE_ENV=production
???
discuss problems with grouping sets of behaviours into a single variable
defaults should be "dev friendly" - external for this example, more commonly localhost
immutability
Hard to pass X.509 certs or PFX files...
- terminate outside of Node.js (see later)
- use an environment variable to specify location of crypto materials
???
Talk about why:
- consistency, externality, maybe right for you, maybe not, arguably violates 12-factor... that is OK
-
Try to use URL-like variables, instead of env var groups:
MYSQL=mysql://user:pass@host:port/db
instead of
MYSQL_USER=user
MYSQL_PASS=pass
... etc.
Modify a standard LoopBack application to be configured via environment.
Static server/datasources.json:
exports = module.exports = {
mysqlDs: {
host: "demo.strongloop.com",
port: 3306,
database: "getting_started_intermediate",
username: "demo",
password: "L00pBack",
name: "mysqlDs",
connector: "mysql"
}
}
Dynamic server/datasources.local.js using URL:
exports = module.exports = {
// Not working consistently (yet?) for all loopback connectors:
mysqlDs: {
url: process.env.DB_MYSQLDS ||
'mysql://demo:[email protected]:3306/getting_started_intermediate'
name: 'mysqlDs',
}
};
Dynamic server/datasources.local.js using URL:
exports = module.exports = {};
set(exports, 'mysqlDs',
'mysql://demo:[email protected]:3306/getting_started_intermediate'
);
};
Dynamic server/datasources.local.js using URL:
function set(config, dsname, def) {
var env = process.env['DB_' + dsname.toUpperCase];
config[dsname] = parse(env || def);
config[dsname].name = dsname;
return config;
}
Dynamic server/datasources.local.js using URL:
function parse(uri) {
var url = require('url');
var parsed = url.parse(uri);
var auth = parsed.auth ? parsed.auth.split(':') : [];
return {
host: parsed.hostname,
port: parsed.port,
database: parsed.pathname ? parsed.pathname.replace(/^\//,'') : undefined,
username: auth[0],
password: auth[1],
connector: parsed.protocol.replace(/:$/, ''),
};
}
Natural for Node.js
Run linux on your laptop :-).
At least, use the same database types.
- Logs go to stdout, consumed by system (or displayed to console)
- Bluemix requires this
Event loop:
- node to OS: please connect to x.com
- node to OS: please connect to y.com
- node to OS: please connect to z.com
- node to OS: wake me when something happened ... blocks on OS, waiting for notifications, using no CPU...
- OS to node: got a connection to y.com
- node to OS: write this data to y.com
- node to OS: wake me when something happened
- OS to node: wrote that data
- ... etc.
Use node for things its good at, as a data switch, services with lots of concurrency, not a lot of CPU per request.
- Stateless refers to HTTP requests, each one can be routed to any instance
- Persisent state must be stored out-of-instance: SQL, NoSQL, ...
A pre-requisite for concurrency/horizontal scaling.
???
loopback models are by default
websockets might share state by default, make sure that session state is offloaded in production
discuss granularity: talk about how to build purpose built components, services that will scale similarly they will have similar request times for example
Node is good at this, as are clouds
Node cluster exists, but use production (auto-)scaling instead.
???
discuss node cluster problems:
- hides behaviour from load balancer
- hides scaling from auto-scaler
- uneven balancing on v0.10, complex and unstable in 0.12+
Fast means sub-second, not sub-minute.
Use strong-supervisor (or equivalent) even with BlueMix.
- Do not format what you will not use
log.debug('look at this: %s', JSON.serialize(someLargeObject));
- Use data sources that can update you on change: evented everywhere, not just the loop!
- Subscribe to things you are interested in only, if possible.
- If the data source cannot do this... consider writing a micro service (in node), that can poll as necessary, batch, and implement a subcribe/notify protocol.
Know the strengths of your tools. Similar looking things can have very different performance capabilities:
- Example: mongo vs couch: do you want consistency, or partition tolerance?
-
Do it at the perimeter if you can, YMMV, you can see 10x performance increase in TLS if you do it outside of Node.js
-
If you require TLS on the wire everywhere: Do it on the host with nginx, and proxy to Node.js on that host, bound to localhost.
-
Hardware acceleration is really useful, but only accelerates the handshake, so you need hardware for high concurrency of unique connections, but not for long-standing high rate connections. Benchmark.
Default limits on descriptors are wrong for high concurrency servers... set them higher.
How high: test what your app can handle, and set them higher than that.
???
Describe what it does, and why.
Missing LoopBack features: