Slide 1

Slide 1 text

The Twelve-Factor App for Node.js Developers by Nikita Galkin Feb 27, 2019

Slide 2

Slide 2 text

Nikita Galkin Love and Know: ▰ How to make developers and business happy ▰ Technical and process debt elimination Believe that: ▰ Any problem must be solved at the right level ▰ Software is easy. People Are Complicated ▰ A problem should be highlighted, an idea should be "sold", a solution should be demonstrated Links: Site GitHub Twitter Facebook 2

Slide 3

Slide 3 text

Takeaway Driven Development 3 TAKEAWAY ADOPT TDD SHARE

Slide 4

Slide 4 text

12factor.net

Slide 5

Slide 5 text

What is Quality? 5 Motivation ▰ Reduce the onboarding time ▰ A common language with DevOps Engineers ▰ Scale/Cloud/Production/etc -ready

Slide 6

Slide 6 text

What is Quality? 6 Talk Structure ▰ Factor ▰ RTFM ▰ how to implement using Node.js ▰ exclusion cases or hacks

Slide 7

Slide 7 text

7 I. Codebase

Slide 8

Slide 8 text

What is Quality? 8 One codebase tracked in revision control, many deploys ▰ App vs Distributed System ▰ Code sharing by dependency manager.

Slide 9

Slide 9 text

What is Quality? 9 How to match codebase and application at env? ▰ semver ▰ version in package.json ▰ git tag ▰ git describe --tag Example 2.2.0-44-g9f84663

Slide 10

Slide 10 text

What is Quality? 10 I. Codebase ▰ BE & FE aka Full-stack repo ▰ App & lambda from one src ▰ Monorepo

Slide 11

Slide 11 text

11 II. Dependencies

Slide 12

Slide 12 text

What is Quality? 12 Explicitly declare and isolate dependencies ▰ declaration VS isolation ▰ never relies on implicit existence ▰ system tool can be a dependency

Slide 13

Slide 13 text

What is Quality? 13 Package.json ▰ avoid npm install -g ... ▰ package-lock.json should published in git ▰ node & npm versions fixed at Tech Stack or you will have package-lock diff war ▰ post-install script is a good place for hacking node_modules

Slide 14

Slide 14 text

14 Scoped packages

Slide 15

Slide 15 text

What is Quality? 15 Shared code ▰ Use scoped packages ▰ @project/code-style is good first chose Check Code Style and Violence by Anton Nemtsev ▰ On self-hosted repo npm config set @myco:registry http://reg.example.com ▰ With a self-hosted repo, use add .npmrc to git

Slide 16

Slide 16 text

What is Quality? 16

Slide 17

Slide 17 text

What is Quality? 17 node_modules hacks ▰ Embedded or lambda: webpack ▰ Docker ▻ npm install --production ▻ npm prune --production ▻ node-prune ▻ docker in docker

Slide 18

Slide 18 text

FROM node:10.15-alpine ENV APP_WORKDIR=/usr/src/app RUN apk update && apk upgrade && \ apk add --virtual build-deps git openssh-client py-pip make COPY package.json yarn.lock $APP_WORKDIR WORKDIR $APP_WORKDIR RUN yarn install COPY . $APP_WORKDIR RUN yarn build RUN rm -rf tsconfig.json src RUN apk del build-deps RUN yarn install --production RUN yarn cache clean EXPOSE 3000 CMD ["node", "dist/index.js"]

Slide 19

Slide 19 text

19 III. Config

Slide 20

Slide 20 text

What is Quality? 20 Store config in the environment ▰ Config is not part of the artifact ▰ could be made open source at any moment, without compromising any credentials.

Slide 21

Slide 21 text

What is Quality? 21 .env ▰ Use .env file for local development ▰ Use .env.example file for description ▰ Use environment variables for any other environment ▰ Don’t use default config values in your code ▰ Break your start process if any value missed

Slide 22

Slide 22 text

# .env.example # Default log level LOG_LEVEL=info # Graceful shutdown timeout in milliseconds SHUTDOWN_TIMEOUT=1000 # HTTP Boundary port HTTP_PORT=8000 # Redis connection string REDIS_URI=redis://127.0.0.1:6379

Slide 23

Slide 23 text

require('dotenv-safe').load({ allowEmptyValues: true, path: join(__dirname, '..', '.env'), sample: join(__dirname, '..', '.env.example') }); const logger = require('./logger'); logger.levels('stdout', process.env.LOG_LEVEL); const settings = { shutdownTimeout: process.env.SHUTDOWN_TIMEOUT, http: { port: process.env.HTTP_PORT, }, redis: { uri: process.env.REDIS_URI, }, }; module.exports = settings;

Slide 24

Slide 24 text

What is Quality? 24 Dima: process.env knows every env var. This is risky! Nikita: Then your project uses only 11 factors.

Slide 25

Slide 25 text

25 IV. Backing services

Slide 26

Slide 26 text

What is Quality? 26 Treat backing services as attached resources ▰ local and third party parity ▰ Attach URL or other locator/credentials stored in the config

Slide 27

Slide 27 text

What is Quality? 27 Nice to have ▰ Tech stack for project or Readme.md with strict versions ▰ For local development docker-compose file or minikube config file

Slide 28

Slide 28 text

28 V. Build, release, run

Slide 29

Slide 29 text

What is Quality? 29 Strictly separate build and run stages ▰ Artifact ▰ Build ▰ Release ▰ Run time ▰ Dry run

Slide 30

Slide 30 text

What is Quality? 30 Node.js artifact ▰ N/A ▰ Docker ▰ Lambda zip ▰ Application Zip ▻ Big size ▻ node-gyp compiled code can be broken on another computer

Slide 31

Slide 31 text

31 VI. Processes (stateless)

Slide 32

Slide 32 text

What is Quality? 32 Execute the app as one or more stateless processes ▰ stateless ▰ share-nothing ▰ state stored at backing-service

Slide 33

Slide 33 text

Awesome Pair! Docker and Node.js has a common thing – single process

Slide 34

Slide 34 text

34 6 Done, 6 Left Let’s move faster

Slide 35

Slide 35 text

35 VII. Port binding Export services via port binding

Slide 36

Slide 36 text

What is Quality? 36 ▰ Don’t hardcode port at the code, use env vars ▰ Bootstrap should fail, if the port is not available ▰ Dockerfile EXPOSE

Slide 37

Slide 37 text

37 VIII. Concurrency Параллелизм

Slide 38

Slide 38 text

What is Quality? 38 Scale out via the process model ▰ Unix ▰ process ▰ should never daemonize ▰ Use process manager like systemd

Slide 39

Slide 39 text

39 IX. Disposability Maximize robustness with fast startup and graceful shutdown

Slide 40

Slide 40 text

const server = app.listen(settings.port, () => { logger.info(`Server started on port ${settings.port}`); }); function stopHandler(){ logger.info(`Stopping server on port ${settings.port}`); const timeout = setTimeout(() => { logger.info(`Server on port ${settings.port} stop forcefully`); process.exit(1); }, settings.stopTimeout); server.close(() => { logger.info(`Server on port ${settings.port} stopped`); clearTimeout(timeout); }); }; process.on('exit', (code) => logger.info(`Exit with code: ${code}`)); ['SIGTERM','SIGINT','SIGHUP'] .forEach(signal => process.on(signal, stopHandler);

Slide 41

Slide 41 text

41 X. Dev/prod parity Keep development, staging, and production as similar as possible

Slide 42

Slide 42 text

What is Quality? 42 ▰ Same backing services ▰ Same lib versions ▰ Same bla-bla for Reproducibility

Slide 43

Slide 43 text

43 XI. Logs Treat logs as event streams

Slide 44

Slide 44 text

What is Quality? 44 ▰ A twelve-factor app never concerns itself with routing or storage of its output stream ▰ Writes event stream, unbuffered, to stdout/stderr ▰ JSON format for simplification log parsing ▰ bunyan has this format from the box ▰ for local development npm start | bunyan -o short

Slide 45

Slide 45 text

stdout stderr

Slide 46

Slide 46 text

46 XII. Admin processes Run admin/management tasks as one-off processes

Slide 47

Slide 47 text

What is Quality? 47 ▰ Node.js has REPL ▰ Check node_modules/.bin every script starts with #!/usr/bin/env node ▰ Write your own script or reuse packages ▰ Use npx, for example npx https://gist.github.com/galkin/9b54683ec251d77b26fa4c7feec46df2

Slide 48

Slide 48 text

12factor.net

Slide 49

Slide 49 text

49 THANKS! Happy coding with 12 factors! You can find me on Twitter as @galk_in Slides are available at speakerdeck.com/galkin or at my site galk.in