You are browsing a read-only backup copy of Wikitech. The live site can be found at wikitech.wikimedia.org

Blubber/Tutorial/HelloNode

From Wikitech-static
Jump to navigation Jump to search

Many of the microservices used in Wikimedia production are written in Node.js. Many of the microservices used in Wikimedia production use the suffix -oid; which, according to Wiktionary is a suffix which modifies the root to imply that something is "Of similar form to, but not the same as."; e.g., humanoid, meaning similar-to, but not the same as a human.

This example will be similar to our previous examples (in that it will still output, "Hello, World!"), but it will be written in Node.js.

This example uses the HelloWorldOid repository.

Clone the Example Repo

To clone this repository and follow along:

dev@laptop:~$ git clone https://gerrit.wikimedia.org/r/blubber-doc/example/helloworldoid blubber-doc/example/helloworldoid
dev@laptop:~$ cd blubber-doc/example/helloworldoid

The directory structure of the application is fairly trivial (omitting any git-related files):

dev@laptop:helloworldoid$ tree -a --dirsfirst
.
├── lib
│   ├── helloworld.js
│   └── server.js
├── .pipeline
│   └── blubber.yaml
├── test
│   └── test.js
├── .dockerignore
├── index.js
├── LICENSE
├── package.json
└── README.md

3 directories, 9 files

There are a few files that are noteworthy and not typical in basic Node.js sample applications:

  1. . .dockerignore - The .dockerignore ( on https://docs.docker.com) file is used to keep files necessary for development, but unnecessary for production out of a container image. In this case I've removed the README.md and the .git directory in an attempt to keep the Docker image created by Blubber small.
  2. . .pipeline directory - this directory is where the Wikimedia Continuous Delivery Pipeline expects to find the blubber.yaml file; so that's where it is!

Blubberfile

Another thing to notice is that the Blubberfile is a bit longer than in previous examples, and has more variants:

version: v4
base: docker-registry.wikimedia.org/nodejs-slim
runs:
  environment:
    HELLO_WORLD: Hi, I’d like to add you to my professional network on LinkedIn.

variants:
  build:
    base: docker-registry.wikimedia.org/nodejs-devel
    copies: [local]
    node: {requirements: [package.json]}
  test:
    includes: [build]
    entrypoint: [npm, test]
  prep:
    includes: [build]
    node: { env: production }
  production:
    copies: [prep]
    entrypoint: [node, index.js]

Notice that a variant can specify a different base image than the global base image at the top of the file—in this case the build variant is using the docker-registry.wikimedia.org/nodejs-devel image so that it can use the npm tool already present in that image.

Also noteworthy is that each variant can specify a different entrypoint. In the case of the test variant we'd like to create a Docker image that runs the application's tests, but in the production variant we want to run the actual application.

Includes for inheritance

The includes keyword is specified in a few variants: test and prep. Variants can inherit from one another. That is, rather than specify that we want to use all the same options as the build variant for test variant we can include that variant. This way the test variant uses the nodejs-devel base image and will run npm install after copying package.json into the image (since we've used the node keyword and specified that we need the package.json file as a requirement).

The build variant here is really only used as a set of options common to both the test and prep variants. Notice that the production variant is not including any other variant and so uses the nodejs-slim image.

Copies for local files and artifacts

The build variant copies in all local project files by specifying copies: [local].

The production variant copies the prep variant. The copies keyword will generate a multistage Dockerfile. Docker images can easily become very large. One method to slim-down a Docker image is to copy any generated artifacts into an image rather than install the tools to generate artifacts inside the image. A multistage Dockerfile will first create an intermediary image (in this case based on the prep variant) that can be based on a different base image or include different packages that will be used to create artifacts. The artifacts generated by the intermediary Docker image can then be copied to a slimmer final Docker image (in this case based on the production variant) to allow for a smaller final image.

The prep variant varies from the build variant insofar as it uses the --production flag when calling npm install (as indicated by the node: {env:production} in the Blubberfile. In this way we do not include development dependencies like mocha in the final production image.

Test image

We can build an image based on the test variant to run the tests in much same way we've built previous images:

dev@laptop:helloworldoid$ blubber .pipeline/blubber.yaml test | docker build -t blubber-tutorial-helloworldoid-test -f - .

Then we can run the resulting image in a container to show the test results:

dev@laptop:helloworldoid$ docker run --rm -it blubber-tutorial-helloworldoid-test

> helloworldoid@0.0.1 test /srv/app
> mocha



  helloWorld
    ✓ obvs should be a string
    ✓ obvs should start with "hello"
    ✓ obvs should contain the word "world"


  3 passing (19ms)

Production image

Then we can run build and run the production image in a container, exposing port 8001:

dev@laptop:helloworldoid$ blubber .pipeline/blubber.yaml production | docker build -t blubber-tutorial-helloworldoid -f - .
dev@laptop:helloworldoid$ docker run -p8001:8001 --rm -it blubber-tutorial-helloworldoid

And now we should be able to access localhost:8001 using http to reveal the fruits of our labor:

developer@laptop:~$ curl localhost:8001
 __________________________________________________________________________________________________________________________
/  ('-. .-.   ('-.                                             (`\ .-') /`             _  .-')            _ .-') _  ,---.  \
| ( OO )  / _(  OO)                                             `.( OO ),'            ( \( -O )          ( (  OO) ) |   |  |
| ,--. ,--.(,------.,--.      ,--.      .-'),-----.          ,--./  .--.   .-'),-----. ,------.  ,--.     \     .'_ |   |  |
| |  | |  | |  .---'|  |.-')  |  |.-') ( OO'  .-.  '         |      |  |  ( OO'  .-.  '|   /`. ' |  |.-') ,`'--..._)|   |  |
| |   .|  | |  |    |  | OO ) |  | OO )/   |  | |  |         |  |   |  |, /   |  | |  ||  /  | | |  | OO )|  |  \  '|   |  |
| |       |(|  '--. |  |`-' | |  |`-' |\_) |  |\|  |         |  |.'.|  |_)\_) |  |\|  ||  |_.' | |  |`-' ||  |   ' ||  .'  |
| |  .-.  | |  .--'(|  '---.'(|  '---.'  \ |  | |  |         |         |    \ |  | |  ||  .  '.'(|  '---.'|  |   / :`--'   |
| |  | |  | |  `---.|      |  |      |    `'  '-'  '.-.      |   ,'.   |     `'  '-'  '|  |\  \  |      | |  '--'  /.--.   |
| `--' `--' `------'`------'  `------'      `-----' ',/      '--'   '--'       `-----' `--' '--' `------' `-------' '--'   |
\ Hi, I’d like to add you to my professional network on LinkedIn.                                                          /
 --------------------------------------------------------------------------------------------------------------------------
        \   ^__^
         \  (oO)\_______
            (__)\       )\/\
             U  ||--WWW |
                ||     ||~