Deploying is my business... And business is Good!
# BIO
# BRIEF HISTORY
# WHY
DISCO - Deploy Infrastructure Safely, Consistently, and Optimally
# Coverage
DEVELOPMENT
CONFIGURATION
SCALING
ADMINISTRATION
DEPLOYMENT
ERROR HANDLING
DEPENDENCIES MANAGEMENT
THE 12 FACTOR APP
# (I) CODEBASE
"One codebase tracked in revision control, many deploys"
# (I) CODEBASE
# (II) DEPENDENCIES
"Explicitly declare and isolate dependencies"
// .nvmrc
v18.12.0
// dockerfile
FROM node:18-alpine
# (II) DEPENDENCIES
Fix dependencies versions
# (II) DEPENDENCIES
# (III) CONFIG
"Store config in the environment"
# (III) CONFIG
# (III) CONFIG
const config = () => ({
dbConnectionString: process.env.DB_CONNECTION_STRING,
customConf: {
auth: process.env.CUSTOM_AUTH,
options: process.env.CUSTOM_OPTIONS,
}
})
const validate = (config) => {
const errors = validateConfig(config)
if (errors?.length) {
throw new Error('Invalid config error')
}
}
# (IV) BACKING SERVICES
"Treat backing services as attached resources"
# (IV) BACKING SERVICES
[DEV]
[STAGING]
[PROD]
URL
URL
URL
# (V) BUILD, RELEASE, RUN
"Strictly separate build and run stages"
# (V) BUILD, RELEASE, RUN
# (VI) PROCESSES
"Execute the app as one or more stateless processes"
# (VI) PROCESSES
LOAD BALANCER
PROXY
# (VI) PROCESSES
Oops...
# (VI) PROCESSES
# (VII) PORT BINDING
"Export services via port binding"
# (VII) PORT BINDING
app.listen(process.env.PORT, () => {
log(`What's up?`)
})
FROM nginx
EXPOSE 80 443
# (VIII) CONCURRENCY
"Scale out via the process model"
# (VIII) CONCURRENCY
# (IX) DISPOSABILITY
"Maximize robustness with fast startup and graceful shutdown"
# (IX) DISPOSABILITY
# (IX) DISPOSABILITY
/**
* finish operations
* release resources
* log
* re-route requests/handlers if needed
*/
const stopHandler = async () => {
logger.info('Shutting down server...')
setShutDownStatus() // respond with 503
await releaseResources()
await closeServer()
}
['SIGTERM', 'SIGINT', 'SIGHUP']
.forEach((signal) => process.on(signal, stopHandler))
# (IX) DISPOSABILITY
process.on('uncaughtException', async (err) => {
logger.error('Uncaught Exception', err)
await releaseResources()
process.exit(1)
})
process.on('unhandledRejection', async (err) => {
logger.error('Unhandled Rejection', err)
await releaseResources()
process.exit(1)
})
process.on('exit', (code) => {
logger.info(`Process exited with code ${code}`)
process.exit(code)
})
# (X) DEV/PROD PARITY
"Keep development, staging, and production as similar as possible"
# (X) DEV/PROD PARITY
Aim to
# (XI) LOGS
"Treat logs as event streams"
# (XI) LOGS
What
# (XI) LOGS
> STDOUT
LOGS AGENT
> STDOUT
# (XII) ADMIN PROCESSES
"Run admin/management tasks as one-off processes"
# (XII) ADMIN PROCESSES
What
# SUMMARY
DISCO - Deploy Infrastructure Safely, Consistently, and Optimally
The 12 Factor App
Slides
roman.sachenko@volvocars.com
roman.sachenko
Questions?