Docker and MEAN Note


Developing

Mount host directory into the image

Due to trying to be as portable as possible you cannot map a host directory to a docker container directory within a dockerfile, because the host directory can change depending on which machine you are running on. To map a host directory to a docker container directory you need to use the -v flag when using docker run like so:

docker run -v /host/directory:/container/directory -other -options image_name command_to_run

https://stackoverflow.com/questions/23439126/how-to-mount-a-host-directory-in-a-docker-container

Angular CLI

# install angular CLI locally.
$ npm install @angular/cli --save-dev

# run ng locally by finding the binary under node_modules folder
# execute npm binaries. https://www.npmjs.com/package/npx
$ npx ng <your_command>

ng serve – starts a local webpack-dev-server and monitor the file changes. When file is changed, the server is restarted. The server and compiled binary is in memory for fast iterating.

https://stackoverflow.com/questions/37137521/what-happens-when-you-run-ng-serve

ng build – build the binary for deployment. The output is in dist folder by default.

yarn run <script> – run the <script> defined in the package.json file.

CSS for bootstrap website: https://bootswatch.com/

MongoDB

MongoDB uses JSON as its data storage format. And JSON is JavaScript Object Notation. It is a great partner for Express and Node.js.

The official document shows the basic operations for the MongoDB

# pull the latest mongo docker image
$ docker pull mongo:latest

# create a file named init-mongo.js for user initialization
# init-mongo.js file
# Note: The /data/db folder on the host machine need to be removed otherwise
# the init script won't be executed.
db.createUser(
{
	user: "admin",
	pwd: "123456",
	roles: [{
	  role: "readWrite",
	  db: "shortlink",
	}]
})

# start a docker composer by checking the docker-compose.yml file in current folder
$ docker-compose up

# list running containers 
$ docker container ls

# execute a command in the container
# -i interactive ; -t open a tty
$ docker exec -ti <container> bash

# In the docker image terminal, enter the mongoDB
$ mongo -u <your username> -p <your password> --authenticationDatabase <your database name>

Insert a document

Mongoose

Mongoose is a mongoDB wrapper that models mongoDB documents for Node.JS.

Docker Image

Docker image should be treated as readonly. All data changes should be mounted on the host machine.

# restart container 
# the container filesystem won't be rebuilt. data is kept
# referece: https://stackoverflow.com/questions/28574433/do-docker-containers-retain-file-changes
$ docker restart <container_id>

Docker container networking

  1. build the network manually

We can build a network bridge and let the containers connect to the same bridge. Each container could have a hostname assigned and the hostname could be resolved to its IP address by the network bridge.

# create network bridge 
$ docker network create <network_name>

# start a mongo image, connect to the network and assign a host name to the container 
$ docker run -d `
>> --network <network_name> --network-alias <hostname> `
>> -w /app -v ${PWD}/mongo-volume:/data/db `
>> -e MONGO_INITDB_DATABASE=<db_name> `
>> -e MONGO_INITDB_ROOT_USERNAME=root `
>> -e MONGO_INITDB_ROOT_PASSWORD=secret `
>> mongo:latest

# start node image, connect to the network
$ docker run -dp 3000:3000 `
>> -w /app -v ${PWD}:/app `
>> --network <hostname> `
>> node:latest `
>> sh -c "yarn install && yarn run dev"

# Note: you need to specify the mongo host name as <hostname> in your code.
# sth like: mongoose.connect('mongodb://<hostname>:27017/<db_name>')

2. use docker-compose to build network

# docker-compose.yml file
version: "3.7"
services:
  app:
    image: "node:latest"
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MONGO_HOST: mongo
      MONGO_USERNAME: bo # This should keep the same as that in the init-mongo.js file
      MONGO_PASSWORD: 123456
      MONGO_DB: shortlink
    depends_on:
      - mongo # only starts the app server after all its dependencies are ready.

  mongo: # This is the service name as well as the network alias/hostname
    image: "mongo"
    container_name: "my-mongo-container"
    environment:
      - MONGO_INITDB_DATABASE=test
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=secret
    volumes:
      - ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro
      - ./mongo-volume:/data/db
    ports:
      - "27017-27019:27017-27019"

NodeJS

NodeJS has two types of threads. One eventloop thread, and manyworker threads. eventloop thread responding to incoming client requests by executing the appropriate callback; worker threads work on expensive tasks like I/O, or some specific CPU intensive tasks. (reference)

Typescript for NodeJS

NodeJS only accepts javascript. This and this articles introduces how to enable typescript in the project and compile it to javascript to feed into the NodeJS server.

And this article introduces how to use ts-node and nodemon to monitor the ts file change and restart the server.

The @types packages (example) need to be installed to declare the type information for NodeJS among other js libraries so that the typescript compiler could do the type checking at the compile time.

Express

# initialize a package.json file under current directory
$ npm init

# instal
$ npm install express --save

Best practices for Docker development

For example, use bind mount during development but use volume for production environment. The volume should be used for database for example.

https://docs.docker.com/develop/dev-best-practices/

Error handling in Typescript

Do not throw Errors but return a another type, sth like (Number|null). Thus the caller could know what to handle from the function signature. (reference)

Data migration

Step 1. Export data from MySQL (the old DB) to CSV (reference)

Step 2. Import data from CSV to mongoDB (reference)

Cons: server need to shutdown during the migration otherwise there will be data loss issue.

Google Analytics Integration

Tracking: ga

Set up a google analytics account. Go to the admin-> property page to get the tracking code.

paste the tracking code to the index.html file in the angular app, then you are done.

For some reason, pasting the <script> tag in the component html file does not work. http://blog.davidjs.com/2018/09/insert-script-tags-in-angular-components/

Embed report: gapi

Overview:

  1. create a service account in google developer console.
  1. enable google analytics API (api library)
  2. add that service account email to your google analytics view as read role.
  3. Server use service account secret to get access_token from google authentication server (use google-auth-library Node.JS version), and return it to client.
  4. Client carry that access_token to send requests to analytics report API. (use gapi)(server side authorization)

Deploying your service in docker

Use a single docker-compose file for prod but use two docker-compose files for development.

https://medium.com/softonic-eng/docker-compose-from-development-to-production-88000124a57c

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.