Why Containerize?

Why Containerize?

Talk about the need for containerization and the tenets of micro-services architecture. Containerize an application consisting of three services and build/run it using docker-compose. Explain Dockerfile, docker-compose file and what happens behind the scenes in detail.

E40e0d06ed05f17385dee72a56cfda48?s=128

Rupak Ganguly

April 18, 2020
Tweet

Transcript

  1. WHY CONTAINERIZE? what & why of containerization of apps Rupak

    Ganguly April 18th, 2020
  2. AGENDA Micro-services Tenets of micro-services Store App Architecture Local app

    architecture Containerized app architecture (composing services) Containerization the Store App Building the App Docker Image (Dockerfile) Running the App in containers (compose file) Behind the scenes of a docker-compose Demo Commands
  3. MICRO-SERVICES breaking the monolith

  4. “ Microservices architecture essentially follows the Unix philosophy of "Do

    one thing and do it well"
  5. MONOLITHIC TO MICRO-SERVICES https://martinfowler.com/articles/microservices.html

  6. THE SERVICE MINDSET ➤ Independent unit of functionality (separation of

    concerns) ➤ Organized around business capabilities ➤ One process and communicate with lightweight mechanisms ➤ Autonomously developed (has its own SDLC) ➤ Polyglot in nature (various programming languages) ➤ Different data storage technologies ➤ Independently versioned ➤ Independently deployable ➤ Built/Deployed via automated processes (CI/CD) ➤ Easily scaled up/down based on criteria
  7. STORE APP containerizing the store app

  8. THE LOCAL STORE APP Frontend Web (react app) Products API

    (nodejs app) Database (mongo db) Local Development Environment Frontend Web Products API Database Port: 27017 Port: 5000 Port: 3000 Overlay Network
  9. COMPOSING SERVICES Frontend Web Products API Database 27017 8081:5000 8080:3000

    Host Port:Container Port Overlay Network docker-compose up Note: The database port is NOT exposed to the host since the database is only required to be accessed by the Products API service. That makes the database access secured. services: web: build: context: ./basic-react-project ports: - 8080:3000 depends_on: - mongo - api api: build: context: ./MyNodeJsProject ports: - 8081:5000 depends_on: - mongo healthcheck: mongo: image: mongo:3.6.3 ports: - "27017" volumes: - api-mongo-db:/data/db
  10. DOCKERFILE FROM node:12.16.1-alpine ARG BUILD_ENV=development ENV APP_SOURCE_DIR=/some/path RUN apk --no-cache

    add bash curl WORKDIR $APP_SOURCE_DIR COPY . $APP_SOURCE_DIR CMD ["npm", "start"] A Dockerfile is a declarative way to express how to build your service or app into a Docker image that can then be run as a container. A Docker image is immutable and can be tagged to represent a snapshot (like a version). FROM: specifies the base image for your application. In this case we are using the node:12.16.1 image that provides node. ARG: specifies the build arguments that can passed via a build command to customize the build. e.g. docker build -—build-args BUILD_ENV=production —t myimage:1.0 . ENV: specifies the runtime environment variable that can be passed to the docker run command. e.g. docker run -e APP_SOURCE_DIR=/another/path myapp RUN: specifies the commands that you need to execute to install the software that you want in your image WORKDIR: specifies the working directory and all commands thereafter will use that COPY: used to copy files from current folder to the container CMD: specifies the command that will be run when a container starts. See ENTRYPOINT as well. Building a Docker image: docker build -t myimage:1.0 . (the . at the end represents the path of the Dockerfile)
  11. DOCKER-COMPOSE.YML services: web: build: context: ./basic-react-project ports: - 8080:3000 depends_on:

    - mongo - api api: build: context: ./MyNodeJsProject ports: - 8081:5000 depends_on: - mongo healthcheck: mongo: image: mongo:3.6.3 ports: - "27017" volumes: - api-mongo-db:/data/db A docker compose file is a declarative way to express services that make up an application. It is an YAML file and in plain text. services: list all services that make up the app web: represents the service ‘web’ and its corresponding configuration build: specifies how to build the service using a path to the code image: instead of building the service, use an already built image ports: represents the ports for the service ‘host port: container port’ depends_on: represents the dependent services for the service healthcheck: configures a command used to validate that a service is ‘healthy’ volumes: is used for a stateful service to persist data after a container exits. It creates a bridge between the host and the container. The bridge is two-way i.e. data written on the host is available on the container and vice- versa.
  12. BEHIND THE SCENES services: web: build: context: ./basic-react-project ports: -

    8080:3000 depends_on: - mongo - api api: build: context: ./MyNodeJsProject ports: - 8081:5000 depends_on: - mongo healthcheck: mongo: image: mongo:3.6.3 ports: - "27017" volumes: - api-mongo-db:/data/db When you run docker-compose up, the following happens: 1.A network named storeapp_default is created by default 2.A container named storeapp_web_1 is created 3.A container named storeapp_api_1 is created 4.A container named storeapp_mongo_1 is created 5.All the services joins the network storeapp_default ➤App is named after the directory it lives in but can be overridden by the -p flag ➤The default network is named <app_name>_default i.e. storeapp_default ➤Each container for a service joins the default network, ➤and is both reachable by other containers on that network, ➤and discoverable by them at a hostname identical to the container name. ➤Networked service-to-service communication use the CONTAINER_PORT. ➤When HOST_PORT is defined, the service is accessible outside the swarm as well. ➤Within the storeapp_web_1 container, your connection string to Mongo database would look like mongodb://mongo:27017/ docker-compose -p storeapp up
  13. DEMO running the store app

  14. DOCKER COMPOSE COMMANDS Build images from Dockerfile and then run

    the app interactively: docker-compose up —build Run the app in background (daemon): docker-compose up -d List the containers: docker-compose ps Set number of containers for a service (scale up/down): docker-compose up --scale <service-name>=<number> Stop and remove containers, networks, images, and volumes: docker-compose down
  15. DEMO COMMANDS - UP WITH BUILD Build images from Dockerfile

    and then run the app interactively: docker-compose up —build Creating network "storeapp_default" with the default driver Building api Step 1/19 : FROM node:12.16.2-alpine Successfully tagged storeapp_api:latest Building web Step 1/19 : FROM node:12.16.2-alpine Successfully tagged storeapp_web:latest Creating storeapp_mongo_1 ... done Creating storeapp_api_1 ... done Creating storeapp_web_1 ... done Attaching to storeapp_mongo_1, storeapp_api_1, storeapp_web_1 mongo_1 | 2020-04-17T08:21:49.284+0000 I CONTROL [initandlisten] MongoDB starting mongo_1 | 2020-04-17T08:21:49.285+0000 I CONTROL [initandlisten] db version v3.6.3 api_1 | $ nodemon server.js api_1 | [nodemon] starting `node server.js` web_1 | $ react-scripts start api_1 | Server running on port 5000 web_1 | Starting the development server...
  16. DEMO COMMANDS - UP Run the app in background (daemon):

    docker-compose up -d Starting storeapp_mongo_1 ... done Starting storeapp_api_1 ... done Starting storeapp_web_1 ... done List the containers: docker-compose ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c0f3471a3ae6 storeapp_web "docker-entrypoint.s " 6 seconds ago Up 3 seconds (health: starting) 0.0.0.0:8080->3000/tcp storeapp_web_1 f8150d450359 storeapp_api "docker-entrypoint.s " 7 seconds ago Up 5 seconds (health: starting) 0.0.0.0:8081->5000/tcp storeapp_api_1 309a9f2cd5f7 mongo:3.6.3 "docker-entrypoint.s " 8 seconds ago Up 6 seconds (health: starting) 0.0.0.0:27017->27017/tcp storeapp_mongo_1
  17. DEMO COMMANDS - SCALING (PORT CONFLICTS) Set number of containers

    for a service (scale up/down): docker-compose up --scale api=3 WARNING: The "api" service specifies a port on the host. If multiple containers for this service are created on a single host, the port will clash. Starting storeapp_api_1 ... done Creating storeapp_api_2 ... Creating storeapp_api_3 ... Creating storeapp_api_2 ... error WARNING: Host is already in use by another container ERROR: for storeapp_api_3 Cannot start service api: driver failed programming external connectivity on endpoint storeapp_api_3 (664b85882427d28ed4a694d7b46042128c590328e3d2e9bc6057f281371c0428): Bind for 0.0.0.0:8081 failed: port is already allocated ERROR: for storeapp_api_2 Cannot start service api: driver failed programming external connectivity on endpoint storeapp_api_2 (70d6c8df937a93e476ef8adf001c1a3c42ee270648321182029811eaaceee72e): Bind for 0.0.0.0:8081 failed: port is already allocated ERROR: for api Cannot start service api: driver failed programming external connectivity on endpoint storeapp_api_3 (664b85882427d28ed4a694d7b46042128c590328e3d2e9bc6057f281371c0428): Bind for 0.0.0.0:8081 failed: port is already allocated api: ports: - 8081:5000
  18. DEMO COMMANDS - SCALING (DYNAMIC PORTS) Set number of containers

    for a service (scale up/down): docker-compose up --scale api=3 Creating network "storeapp_default" with the default driver Creating storeapp_mongo_1 ... done Creating storeapp_api_1 ... done Creating storeapp_api_2 ... done Creating storeapp_api_3 ... done Creating storeapp_web_1 ... done docker-compose ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ff68c5d9741d storeapp_web "docker-entrypoint.s " 7 seconds ago Up 5 seconds (health: starting) 0.0.0.0:8080->3000/tcp storeapp_web_1 f6da8ba063b1 storeapp_api "docker-entrypoint.s " 10 seconds ago Up 6 seconds (health: starting) 0.0.0.0:32774->5000/tcp storeapp_api_1 6f2d277985c9 storeapp_api "docker-entrypoint.s " 10 seconds ago Up 6 seconds (health: starting) 0.0.0.0:32773->5000/tcp storeapp_api_3 e395e272d360 storeapp_api "docker-entrypoint.s " 10 seconds ago Up 7 seconds (health: starting) 0.0.0.0:32772->5000/tcp storeapp_api_2 8a370985695a mongo:3.6.3 "docker-entrypoint.s " 11 seconds ago Up 9 seconds (health: starting) 0.0.0.0:27017->27017/tcp storeapp_mongo_1 api: ports: - 5000
  19. DEMO COMMANDS - DOWN Stop and remove containers, networks, images,

    and volumes: docker-compose down Stopping storeapp_web_1 ... done Stopping storeapp_api_1 ... done Stopping storeapp_mongo_1 ... done Removing storeapp_web_1 ... done Removing storeapp_api_1 ... done Removing storeapp_mongo_1 ... done Removing network storeapp_default
  20. RESOURCES What is a container: https://www.docker.com/resources/what-container Getting started with Docker:

    https://docs.docker.com/get-started/overview/ Install Docker: https://www.docker.com/products/docker-desktop Docker Developer Tools: https://www.docker.com/products/developer-tools Docker Engine: https://docs.docker.com/engine/ Docker Compose: https://docs.docker.com/compose/ Docker Hub: https://docs.docker.com/docker-hub/ Docker Enterprise Platform: https://docs.docker.com/ee/ Docker Kubernetes Service: https://docs.docker.com/ee/#docker-kubernetes-service
  21. QUESTIONS? @rupakg rupakganguly.com Slides: https://speakerdeck.com/rupakg/why-containerize