One place for hosting & domains

      Nodejs

      How To Build a Node.js Application with Docker


      Introduction

      The Docker platform allows developers to package and run applications as containers. A container is an isolated process that runs on a shared operating system, offering a lighter weight alternative to virtual machines. Though containers are not new, they offer benefits — including process isolation and environment standardization — that are growing in importance as more developers use distributed application architectures.

      When building and scaling an application with Docker, the starting point is typically creating an image for your application, which you can then run in a container. The image includes your application code, libraries, configuration files, environment variables, and runtime. Using an image ensures that the environment in your container is standardized and contains only what is necessary to build and run your application.

      In this tutorial, you will create an application image for a static website that uses the Express framework and Bootstrap. You will then build a container using that image and push it to Docker Hub for future use. Finally, you will pull the stored image from your Docker Hub repository and build another container, demonstrating how you can recreate and scale your application.

      Prerequisites

      To follow this tutorial, you will need:

      Step 1 — Installing Your Application Dependencies

      To create your image, you will first need to make your application files, which you can then copy to your container. These files will include your application’s static content, code, and dependencies.

      First, create a directory for your project in your non-root user’s home directory. We will call ours node_project, but you should feel free to replace this with something else:

      Navigate to this directory:

      This will be the root directory of the project.

      Next, create a package.json file with your project's dependencies and other identifying information. Open the file with nano or your favorite editor:

      Add the following information about the project, including its name, author, license, entrypoint, and dependencies. Be sure to replace the author information with your own name and contact details:

      ~/node_project/package.json

      {
        "name": "nodejs-image-demo",
        "version": "1.0.0",
        "description": "nodejs image demo",
        "author": "Sammy the Shark <sammy@example.com>",
        "license": "MIT",
        "main": "app.js",
        "scripts": {
          "start": "node app.js",
          "test": "echo "Error: no test specified" && exit 1"
        },
        "keywords": [
          "nodejs",
          "bootstrap",
          "express"
        ],
        "dependencies": {
          "express": "^4.16.4"
        }
      }
      

      This file includes the project name, author, and license under which it is being shared. Npm recommends making your project name short and descriptive, and avoiding duplicates in the npm registry. We've listed the MIT license in the license field, permitting the free use and distribution of the application code.

      Additionally, the file specifies:

      • "main": The entrypoint for the application, app.js. You will create this file next.
      • "scripts": The commands that will run when you use npm start to start your application.
      • "dependencies": The project dependencies — in this case, Express 4.16.4 or above.

      Though this file does not list a repository, you can add one by following these guidelines on adding a repository to your package.json file. This is a good addition if you are versioning your application.

      Save and close the file when you've finished making changes.

      To install your project's dependencies, run the following command:

      This will install the packages you've listed in your package.json file in your project directory.

      We can now move on to building the application files.

      Step 2 — Creating the Application Files

      We will create a website that offers users information about sharks. Our application will have a main entrypoint, app.js, and a views directory that will include the project's static assets. The landing page, index.html, will offer users some preliminary information and a link to a page with more detailed shark information, sharks.html. In the views directory, we will create both the landing page and sharks.html.

      First, open app.js in the main project directory to define the project's routes:

      The first part of the file will create the Express application and Router objects, and define the base directory, port, and host as variables:

      ~/node_project/app.js

      var express = require("express");
      var app = express();
      var router = express.Router();
      
      var path = __dirname + '/views/';
      const PORT = 8080;
      const HOST = '0.0.0.0';
      

      The require function loads the express module, which we then use to create the app and router objects. The router object will perform the routing function of the application, and as we define HTTP method routes we will add them to this object to define how our application will handle requests.

      This section of the file also sets a few variables, path, PORT, and HOST:

      • path: Defines the base directory, which will be the views subdirectory within the current project directory.
      • HOST: Defines the address that the application will bind to and listen on. Setting this to 0.0.0.0 or all IPv4 addresses corresponds with Docker's default behavior of exposing containers to 0.0.0.0 unless otherwise instructed.
      • PORT: Tells the app to listen on and bind to port 8080.

      Next, set the routes for the application using the router object:

      ~/node_project/app.js

      ...
      
      router.use(function (req,res,next) {
        console.log("/" + req.method);
        next();
      });
      
      router.get("/",function(req,res){
        res.sendFile(path + "index.html");
      });
      
      router.get("/sharks",function(req,res){
        res.sendFile(path + "sharks.html");
      });
      

      The router.use function loads a middleware function that will log the router's requests and pass them on to the application's routes. These are defined in the subsequent functions, which specify that a GET request to the base project URL should return the index.html page, while a GET request to the /sharks route should return sharks.html.

      Finally, mount the router middleware and the application's static assets and tell the app to listen on port 8080:

      ~/node_project/app.js

      ...
      
      app.use(express.static(path));
      app.use("/", router);
      
      app.listen(8080, function () {
        console.log('Example app listening on port 8080!')
      })
      

      The finished app.js file will look like this:

      ~/node_project/app.js

      var express = require("express");
      var app = express();
      var router = express.Router();
      
      var path = __dirname + '/views/';
      const PORT = 8080;
      const HOST = '0.0.0.0';
      
      router.use(function (req,res,next) {
        console.log("/" + req.method);
        next();
      });
      
      router.get("/",function(req,res){
        res.sendFile(path + "index.html");
      });
      
      router.get("/sharks",function(req,res){
        res.sendFile(path + "sharks.html");
      });
      
      app.use(express.static(path));
      app.use("/", router);
      
      app.listen(8080, function () {
        console.log('Example app listening on port 8080!')
      })
      

      Save and close the file when you are finished.

      Next, let's add some static content to the application. Start by creating the views directory:

      Open the landing page file, index.html:

      Add the following code to the file, which will import Boostrap and create a jumbotron component with a link to the more detailed sharks.html info page:

      ~/node_project/views/index.html

      <!DOCTYPE html>
      <html lang="en">
         <head>
            <title>About Sharks</title>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
            <link href="css/styles.css" rel="stylesheet">
            <link href='https://fonts.googleapis.com/css?family=Merriweather:400,700' rel='stylesheet' type='text/css'>
            <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
            <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
         </head>
         <body>
            <nav class="navbar navbar-inverse navbar-static-top">
               <div class="container">
                  <div class="navbar-header">
                     <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                     <span class="sr-only">Toggle navigation</span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                     </button>
                     <a class="navbar-brand" href="#">Everything Sharks</a>
                  </div>
                  <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                     <ul class="nav navbar-nav mr-auto">
                        <li class="active"><a href="/">Home</a></li>
                        <li><a href="http://www.digitalocean.com/sharks">Sharks</a></li>
                     </ul>
                  </div>
               </div>
            </nav>
            <div class="jumbotron">
               <div class="container">
                  <h1>Want to Learn About Sharks?</h1>
                  <p>Are you ready to learn about sharks?</p>
                  <br>
                  <p><a class="btn btn-primary btn-lg" href="http://www.digitalocean.com/sharks" role="button">Get Shark Info</a></p>
               </div>
            </div>
            <div class="container">
               <div class="row">
                  <div class="col-md-6">
                     <h3>Not all sharks are alike</h3>
                     <p>Though some are dangerous, sharks generally do not attack humans. Out of the 500 species known to researchers, only 30 have been known to attack humans.</p>
                  </div>
                  <div class="col-md-6">
                     <h3>Sharks are ancient</h3>
                     <p>There is evidence to suggest that sharks lived up to 400 million years ago.</p>
                  </div>
               </div>
            </div>
         </body>
      </html>
      

      The top-level navbar here allows users to toggle between the Home and Sharks pages. In the navbar-nav subcomponent, we are using Bootstrap's active class to indicate the current page to the user. We've also specified the routes to our static pages, which match the routes we defined in app.js:

      ~/node_project/views/index.html

      ...
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav mr-auto">
              <li class="active"><a href="/">Home</a></li>
              <li><a href="http://www.digitalocean.com/sharks">Sharks</a></li>
          </ul>
      </div>
      ...
      

      Additionally, we've created a link to our shark information page in our jumbotron's button:

      ~/node_project/views/index.html

      ...
      <div class="jumbotron">
          <div class="container">
            <h1>Want to Learn About Sharks?</h1>
            <p>Are you ready to learn about sharks?</p>
            <br>
            <p><a class="btn btn-primary btn-lg" href="http://www.digitalocean.com/sharks" role="button">Get Shark Info</a></p>
          </div>
      </div>
      ...
      

      There is also a link to a custom style sheet in the header:

      ~/node_project/views/index.html

      ...
      <link href="css/styles.css" rel="stylesheet">
      ...
      

      We will create this style sheet at the end of this step.

      Save and close the file when you are finished.

      With the application landing page in place, we can create our shark information page, sharks.html, which will offer interested users more information about sharks.

      Open the file:

      Add the following code, which imports Bootstrap and the custom style sheet and offers users detailed information about certain sharks:

      ~/node_project/views/sharks.html

      <!DOCTYPE html>
      <html lang="en">
         <head>
            <title>About Sharks</title>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
            <link href="css/styles.css" rel="stylesheet">
            <link href='https://fonts.googleapis.com/css?family=Merriweather:400,700' rel='stylesheet' type='text/css'>
            <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
            <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
         </head>
         <nav class="navbar navbar-inverse navbar-static-top">
            <div class="container">
               <div class="navbar-header">
                  <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                  <span class="sr-only">Toggle navigation</span>
                  <span class="icon-bar"></span>
                  <span class="icon-bar"></span>
                  <span class="icon-bar"></span>
                  </button>
                  <a class="navbar-brand" href="/">Everything Sharks</a>
               </div>
               <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                  <ul class="nav navbar-nav mr-auto">
                     <li><a href="/">Home</a></li>
                     <li class="active"><a href="http://www.digitalocean.com/sharks">Sharks</a></li>
                  </ul>
               </div>
            </div>
         </nav>
         <div class="jumbotron text-center">
            <h1>Shark Info</h1>
         </div>
         <div class="container">
            <div class="row">
               <div class="col-md-6">
                  <p>
                  <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.</div>
                  <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
                  </p>
               </div>
               <div class="col-md-6">
                  <p>
                  <div class="caption">Other sharks are known to be friendly and welcoming!</div>
                  <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
                  </p>
               </div>
            </div>
          </div>
         </body>
      </html>
      

      Note that in this file, we again use the active class to indicate the current page.

      Save and close the file when you are finished.

      Finally, create the custom CSS style sheet that you've linked to in index.html and sharks.html by first creating a css folder in the views directory:

      Open the style sheet:

      • nano views/css/styles.css

      Add the following code, which will set the desired color and font for our pages:

      ~/node_project/views/css/styles.css

      .navbar {
          margin-bottom: 0;
      }
      
      body {
          background: #020A1B;
          color: #ffffff;
          font-family: 'Merriweather', sans-serif;
      }
      
      h1,
      h2 {
          font-weight: bold;
      }
      
      p {
          font-size: 16px;
          color: #ffffff;
      }
      
      .jumbotron {
          background: #0048CD;
          color: white;
          text-align: center;
      }
      
      .jumbotron p {
          color: white;
          font-size: 26px;
      }
      
      .btn-primary {
          color: #fff;
          text-color: #000000;
          border-color: white;
          margin-bottom: 5px;
      }
      
      img,
      video,
      audio {
          margin-top: 20px;
          max-width: 80%;
      }
      
      div.caption: {
          float: left;
          clear: both;
      }
      

      In addition to setting font and color, this file also limits the size of the images by specifying a max-width of 80%. This will prevent them from taking up more room than we would like on the page.

      Save and close the file when you are finished.

      With the application files in place and the project dependencies installed, you are ready to start the application.

      If you followed the initial server setup tutorial in the prerequisites, you will have an active firewall permitting only SSH traffic. To permit traffic to port 8080 run:

      To start the application, make sure that you are in your project's root directory:

      Start the application with npm start:

      Navigate your browser to http://your_server_ip:8080. You will see the following landing page:

      Application Landing Page

      Click on the Get Shark Info button. You will see the following information page:

      Shark Info Page

      You now have an application up and running. When you are ready, quit the server by typing CTRL+C. We can now move on to creating the Dockerfile that will allow us to recreate and scale this application as desired.

      Step 3 — Writing the Dockerfile

      Your Dockerfile specifies what will be included in your application container when it is executed. Using a Dockerfile allows you to define your container environment and avoid discrepancies with dependencies or runtime versions.

      Following these guidelines on building optimized containers, we will make our image as efficient as possible by minimizing the number of image layers and restricting the image's function to a single purpose — recreating our application files and static content.

      In your project's root directory, create the Dockerfile:

      Docker images are created using a succession of layered images that build on one another. Our first step will be to add the base image for our application that will form the starting point of the application build.

      Let's use the node:10 image, since, at the time of writing, this is the recommended LTS version of Node.js. Add the following FROM instruction to set the application's base image:

      ~/node_project/Dockerfile

      FROM node:10
      

      This image includes Node.js and npm. Each Dockerfile must begin with a FROM instruction.

      By default, the Docker Node image includes a non-root node user that you can use to avoid running your application container as root. It is a recommended security practice to avoid running containers as root and to restrict capabilities within the container to only those required to run its processes. We will therefore use the node user's home directory as the working directory for our application and set them as our user inside the container. For more information about best practices when working with the Docker Node image, see this best practices guide.

      To fine-tune the permissions on our application code in the container, let's create the node_modules subdirectory in /home/node along with the app directory. Creating these directories will ensure that they have the permissions we want, which will be important when we create local node modules in the container with npm install. In addition to creating these directories, we will set ownership on them to our node user:

      ~/node_project/Dockerfile

      ...
      RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
      

      For more information on the utility of consolidating RUN instructions, see this discussion of how to manage container layers.

      Next, set the working directory of the application to /home/node/app:

      ~/node_project/Dockerfile

      ...
      WORKDIR /home/node/app
      

      If a WORKDIR isn't set, Docker will create one by default, so it's a good idea to set it explicitly.

      Next, copy the package.json and package-lock.json (for npm 5+) files:

      ~/node_project/Dockerfile

      ...
      COPY package*.json ./
      

      Adding this COPY instruction before running npm install or copying the application code allows us to take advantage of Docker's caching mechanism. At each stage in the build, Docker will check to see if it has a layer cached for that particular instruction. If we change package.json, this layer will be rebuilt, but if we don't, this instruction will allow Docker to use the existing image layer and skip reinstalling our node modules.

      After copying the project dependencies, we can run npm install:

      ~/node_project/Dockerfile

      ...
      RUN npm install
      

      Copy your application code to the working application directory on the container:

      ~/node_project/Dockerfile

      ...
      COPY . .
      

      To ensure that the application files are owned by the non-root node user, copy the permissions from your application directory to the directory on the container:

      ~/node_project/Dockerfile

      ...
      COPY --chown=node:node . .
      

      Set the user to node:

      ~/node_project/Dockerfile

      ...
      USER node
      

      Expose port 8080 on the container and start the application:

      ~/node_project/Dockerfile

      ...
      EXPOSE 8080
      
      CMD [ "npm", "start" ]
      

      EXPOSE does not publish the port, but instead functions as a way of documenting which ports on the container will be published at runtime. CMD runs the command to start the application — in this case, npm start. Note that there should only be one CMD instruction in each Dockerfile. If you include more than one, only the last will take effect.

      There are many things you can do with the Dockerfile. For a complete list of instructions, please refer to Docker's Dockerfile reference documentation.

      The complete Dockerfile looks like this:

      ~/node_project/Dockerfile

      
      FROM node:10
      
      RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
      
      WORKDIR /home/node/app
      
      COPY package*.json ./
      
      RUN npm install
      
      COPY . .
      
      COPY --chown=node:node . .
      
      USER node
      
      EXPOSE 8080
      
      CMD [ "npm", "start" ]
      

      Save and close the file when you are finished editing.

      Before building the application image, let's add a .dockerignore file. Working in a similar way to a .gitignore file, .dockerignore specifies which files and directories in your project directory should not be copied over to your container.

      Open the .dockerignore file:

      Inside the file, add your local node modules, npm logs, Dockerfile, and .dockerignore file:

      ~/node_project/.dockerignore

      node_modules
      npm-debug.log
      Dockerfile
      .dockerignore
      

      If you are working with Git then you will also want to add your .git directory and .gitignore file.

      Save and close the file when you are finished.

      You are now ready to build the application image using the docker build command. Using the -t flag with docker build will allow you to tag the image with a memorable name. Because we are going to push the image to Docker Hub, let's include our Docker Hub username in the tag. We will tag the image as nodejs-image-demo, but feel free to replace this with a name of your own choosing. Remember to also replace your_dockerhub_username with your own Docker Hub username:

      • docker build -t your_dockerhub_username/nodejs-image-demo .

      The . specifies that the build context is the current directory.

      It will take a minute or two to build the image. Once it is complete, check your images:

      You will see the following output:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 8 seconds ago 895MB node 10 f09e7c96b6de 17 hours ago 893MB

      It is now possible to create a container with this image using docker run. We will include three flags with this command:

      • -p: This publishes the port on the container and maps it to a port on our host. We will use port 80 on the host, but you should feel free to modify this as necessary if you have another process running on that port. For more information about how this works, see this discussion in the Docker docs on port binding.
      • -d: This runs the container in the background.
      • --name: This allows us to give the container a memorable name.

      Run the following command to build the container:

      • docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo

      Once your container is up and running, you can inspect a list of your running containers with docker ps:

      You will see the following output:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "npm start" 8 seconds ago Up 7 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo

      With your container running, you can now visit your application by navigating your browser to http://your_server_ip. You will see your application landing page once again:

      Application Landing Page

      Now that you have created an image for your application, you can push it to Docker Hub for future use.

      Step 4 — Using a Repository to Work with Images

      By pushing your application image to a registry like Docker Hub, you make it available for subsequent use as you build and scale your containers. We will demonstrate how this works by pushing the application image to a repository and then using the image to recreate our container.

      The first step to pushing the image is to log in to the Docker Hub account you created in the prerequisites:

      • docker login -u your_dockerhub_username -p your_dockerhub_password

      Logging in this way will create a ~/.docker/config.json file in your user's home directory with your Docker Hub credentials.

      You can now push the application image to Docker Hub using the tag you created earlier, your_dockerhub_username/nodejs-image-demo:

      • docker push your_dockerhub_username/nodejs-image-demo

      Let's test the utility of the image registry by destroying our current application container and image and rebuilding them with the image in our repository.

      First, list your running containers:

      You will see the following output:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "npm start" 3 minutes ago Up 3 minutes 0.0.0.0:80->8080/tcp nodejs-image-demo

      Using the CONTAINER ID listed in your output, stop the running application container. Be sure to replace the highlighted ID below with your own CONTAINER ID:

      List your all of your images with the -a flag:

      You will see the following output with the name of your image, your_dockerhub_username/nodejs-image-demo, along with the node image and the other images from your build:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 7 minutes ago 895MB <none> <none> e039d1b9a6a0 7 minutes ago 895MB <none> <none> dfa98908c5d1 7 minutes ago 895MB <none> <none> b9a714435a86 7 minutes ago 895MB <none> <none> 51de3ed7e944 7 minutes ago 895MB <none> <none> 5228d6c3b480 7 minutes ago 895MB <none> <none> 833b622e5492 8 minutes ago 893MB <none> <none> 5c47cc4725f1 8 minutes ago 893MB <none> <none> 5386324d89fb 8 minutes ago 893MB <none> <none> 631661025e2d 8 minutes ago 893MB node 10 f09e7c96b6de 17 hours ago 893MB

      Remove the stopped container and all of the images, including unused or dangling images, with the following command:

      Type y when prompted in the output to confirm that you would like to remove the stopped container and images. Be advised that this will also remove your build cache.

      You have now removed both the container running your application image and the image itself. For more information on removing Docker containers, images, and volumes, please see How To Remove Docker Images, Containers, and Volumes.

      With all of your images and containers deleted, you can now pull the application image from Docker Hub:

      • docker pull your_dockerhub_username/nodejs-image-demo

      List your images once again:

      You will see your application image:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 11 minutes ago 895MB

      You can now rebuild your container using the command from Step 3:

      • docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo

      List your running containers:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f6bc2f50dff6 your_dockerhub_username/nodejs-image-demo "npm start" 4 seconds ago Up 3 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo

      Visit http://your_server_ip once again to view your running application.

      Conclusion

      In this tutorial you created a static web application with Express and Bootstrap, as well as a Docker image for this application. You used this image to create a container and pushed the image to Docker Hub. From there, you were able to destroy your image and container and recreate them using your Docker Hub repository.

      If you are interested in learning more about how to work with tools like Docker Compose and Docker Machine to create multi-container setups, you can look at the following guides:

      For general tips on working with container data, see:

      If you are interested in other Docker-related topics, please see our complete library of Docker tutorials.



      Source link

      How to Install Node.js and Create a Local Development Environment on macOS


      Introduction

      Node.js is an open source JavaScript runtime environment for easily building server-side applications. It’s also the runtime that powers many client-side development tools for modern JavaScript frameworks.

      In this tutorial, you’ll set up a Node.js programming environment on your local macOS machine using Homebrew, and you’ll test your environment out by writing a simple Node.js program.

      Prerequisites

      You will need a macOS computer running High Sierra or higher with administrative access and an internet connection.

      Step 1 — Using the macOS Terminal

      You’ll use the command line to install Node.js and run various commands related to developing Node.js applications. The command line is a non-graphical way to interact with your computer. Instead of clicking buttons with your mouse, you’ll type commands as text and receive text-based feedback. The command line, also known as a shell, lets you automate many tasks you do on your computer daily, and is an essential tool for software developers.

      To access the command line interface, you’ll use the Terminal application provided by macOS. Like any other application, you can find it by going into Finder, navigating to the Applications folder, and then into the Utilities folder. From here, double-click the Terminal application to open it up. Alternatively, you can use Spotlight by holding down the COMMAND key and pressing SPACE to find Terminal by typing it out in the box that appears.

      macOS Terminal

      If you’d like to get comfortable using the command line, take a look at An Introduction to the Linux Terminal. The command line interface on macOS is very similar, and the concepts in that tutorial are directly applicable.

      Now that you have the Terminal running, let’s install some prerequisites we’ll need for Node.js.

      Xcode is an integrated development environment (IDE) that is comprised of software development tools for macOS. You won’t need Xcode to write Node.js programs, but Node.js and some of its components will rely on Xcode’s Command Line Tools package.

      Execute this command in the Terminal to download and install these components:

      You'll be prompted to start the installation, and then prompted again to accept a software license. Then the tools will download and install automatically.

      We're now ready to install the package manager Homebrew, which will let us install the latest version of Node.js.

      Step 3 — Installing and Setting Up Homebrew

      While the command line interface on macOS has a lot of the functionality you'd find in Linux and other Unix systems, it does not ship with a good package manager. A package manager is a collection of software tools that work to automate software installations, configurations, and upgrades. They keep the software they install in a central location and can maintain all software packages on the system in formats that are commonly used. Homebrew is a free and open-source software package managing system that simplifies the installation of software on macOS. We'll use Homebrew to install the most recent version of Node.js.

      To install Homebrew, type this command into your Terminal window:

      • /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

      The command uses curl to download the Homebrew installation script from Homebrew's Git repository on GitHub.

      Let’s walk through the flags that are associated with the curl command:

      • The -f or --fail flag tells the Terminal window to give no HTML document output on server errors.
      • The -s or --silent flag mutes curl so that it does not show the progress meter, and combined with the -S or --show-error flag it will ensure that curl shows an error message if it fails.
      • The -L or --location flag will tell curl to handle redirects. If the server reports that the requested page has moved to a different location, it'll automatically execute the request again using the new location.

      Once curl downloads the script, it's then executed by the Ruby interpreter that ships with macOS, starting the Homebrew installation process.

      The installation script will explain what it will do and will prompt you to confirm that you want to do it. This lets you know exactly what Homebrew is going to do to your system before you let it proceed. It also ensures you have the prerequisites in place before it continues.

      You'll be prompted to enter your password during the process. However, when you type your password, your keystrokes will not display in the Terminal window. This is a security measure and is something you'll see often when prompted for passwords on the command line. Even though you don't see them, your keystrokes are being recorded by the system, so press the RETURN key once you’ve entered your password.

      Press the letter y for “yes” whenever you are prompted to confirm the installation.

      Now let's verify that Homebrew is set up correctly. Execute this command:

      If no updates are required at this time, you'll see this in your Terminal:

      Output

      Your system is ready to brew.

      Otherwise, you may get a warning to run another command such as brew update to ensure that your installation of Homebrew is up to date.

      Now that Homebrew is installed, you can install Node.js.

      Step 4 — Installing Node.js

      With Homebrew installed, you can install a wide range of software and developer tools. We'll use it to install Node.js and its dependencies.

      You can use Homebrew to search for everything you can install with the brew search command, but to provide us with a shorter list, let’s instead search for packages related to Node.js:

      You'll see a list of packages you can install, like this:

      Output

      ==> Formulae node.js nodejs

      Both of these packages install Node.js on your system. They both exist just in case you can't remember if you need to use nodejs or node.js.

      Execute this command to install the nodejs package:

      You'll see output similar to the following in your Terminal. Homebrew will install many dependencies, but will eventually download and install Node.js itself:

      Output

      ==> Installing dependencies for node: icu4c ==> Installing node dependency: icu4c ==> Installing node ==> Downloading https://homebrew.bintray.com/bottles/node-11.0.0.sierra.bottle.tar.gz ######################################################################## 100.0% ==> Pouring node-11.0.0.sierra.bottle.tar.gz ... ==> Summary 🍺 /usr/local/Cellar/node/11.0.0: 3,936 files, 50.1MB

      In addition to Node.js itself, Homebrew installs a few related tools, including npm, which makes it easy to install and update Node.js libraries and packages you might use in your own projects.

      To check the version of Node.js that you installed, type

      This will output the specific version of Node.js that is currently installed, which will by default be the most up-to-date stable version of Node.js that is available.

      Output

      v11.0.0

      Check the version of npm with

      You'll see the version displayed:

      Output

      6.4.1

      You'll use npm to install additional components, libraries, and frameworks.

      To update your version of Node.js, you can first update Homebrew to get the latest list of packages, and then upgrade Node.js itself:

      • brew update
      • brew upgrade nodejs

      Now that Node.js is installed, let's write a program to ensure everything works.

      Step 5 — Creating a Simple Program

      Let's create a simple "Hello, World" program. This will make sure that our environment is working and gets you comfortable creating and running a Node.js program.

      To do this, create a new file called hello.js using nano:

      Type the following code into the file:

      hello.js

      let message = "Hello, World!";
      console.log(message);
      

      Exit the editor by pressing CTRL+X. Then press y when prompted to save the file. You'll be returned to your prompt.

      Now run the program with the following command:

      The program executes and displays its output to the screen:

      Output

      Hello, World!

      This simple program proves that you have a working development environment. You can use this environment to continue exploring Node.js and build larger, more interesting projects.

      Conclusion

      You've successfully installed Node.js, npm, and tested out your setup by creating and running a simple program. You can now use this to develop client-side apps or server-side apps. Take a look at the following tutorials to learn more:



      Source link

      How To Develop a Node.js TCP Server Application using PM2 and Nginx on Ubuntu 16.04


      The author selected OSMI to receive a donation as part of the Write for DOnations program.

      Introduction

      Node.js is a popular open-source JavaScript runtime environment built on Chrome’s V8 Javascript engine. Node.js is used for building server-side and networking applications.TCP (Transmission Control Protocol) is a networking protocol that provides reliable, ordered and error-checked delivery of a stream of data between applications. A TCP server can accept a TCP connection request, and once the connection is established both sides can exchange data streams.

      In this tutorial, you’ll build a basic Node.js TCP server, along with a client to test the server. You’ll run your server as a background process using a powerful Node.js process manager called PM2. Then you’ll configure Nginx as a reverse proxy for the TCP application and test the client-server connection from your local machine.

      Prerequisites

      To complete this tutorial, you will need:

      Step 1 — Creating a Node.js TCP Application

      We will write a Node.js application using TCP Sockets. This is a sample application which will help you understand the Net library in Node.js which enables us to create raw TCP server and client applications.

      To begin, create a directory on your server in which you would like to place your Node.js application. For this tutorial, we will create our application in the ~/tcp-nodejs-app directory:

      Then switch to the new directory:

      Create a new file named package.json for your project. This file lists the packages that the application depends on. Creating this file will make the build reproducible as it will be easier to share this list of dependencies with other developers:

      You can also generate the package.json using the npm init command, which will prompt you for the details of the application, but we'll still have to manually alter the file to add additional pieces, including a startup command. Therefore, we'll manually create the file in this tutorial.

      Add the following JSON to the file, which specifies the application's name, version, the main file, the command to start the application, and the software license:

      package.json

      {
        "name": "tcp-nodejs-app",
        "version": "1.0.0",
        "main": "server.js",
        "scripts": {
          "start": "node server.js"
        },
        "license": "MIT"
      }
      

      The scripts field lets you define commands for your application. The setting you specified here lets you run the app by running npm start instead of running node server.js.

      The package.json file can also contain a list of runtime and development dependencies, but we won't have any third party dependencies for this application.

      Now that you have the project directory and package.json set up, let's create the server.

      In your application directory, create a server.js file:

      Node.js provides a module called net which enables TCP server and client communication. Load the net module with require(), then define variables to hold the port and host for the server:

      server.js

      const net = require('net');
      const port = 7070;
      const host = '127.0.0.1';
      

      We'll use port 7070 for this app, but you can use any available port you'd like. We're using 127.0.0.1 for the HOST which ensures that our server is only listening on our local network interface. Later we will place Nginx in front of this app as a reverse proxy. Nginx is well-versed at handling multiple connections and horizontal scaling.

      Then add this code to spawn a TCP server using the createServer() function from the net module. Then start listening for connections on the port and host you defined by using the listen() function of the net module:

      server.js

      ...
      const server = net.createServer();
      server.listen(port, host, () => {
          console.log('TCP Server is running on port ' + port +'.');
      });
      
      

      Save server.js and start the server:

      You'll see this output:

      Output

      TCP Server is running on port 7070

      The TCP server is running on port 7070. Press CTRL+C to stop the server.

      Now that we know the server is listening, let's write the code to handle client connections.

      When a client connects to the server, the server triggers a connection event, which we'll observe. We'll define an array of connected clients, which we'll call sockets, and add each client instance to this array when the client connects.

      We'll use the data event to process the data stream from the connected clients, using the sockets array to broadcast data to all the connected clients.

      Add this code to the server.js file to implement these features:

      server.js

      
      ...
      
      let sockets = [];
      
      server.on('connection', function(sock) {
          console.log('CONNECTED: ' + sock.remoteAddress + ':' + sock.remotePort);
          sockets.push(sock);
      
          sock.on('data', function(data) {
              console.log('DATA ' + sock.remoteAddress + ': ' + data);
              // Write the data back to all the connected, the client will receive it as data from the server
              sockets.forEach(function(sock, index, array) {
                  sock.write(sock.remoteAddress + ':' + sock.remotePort + " said " + data + 'n');
              });
          });
      });
      

      This tells the server to listen to data events sent by connected clients. When the connected clients send any data to the server, we echo it back to all the connected clients by iterating through the sockets array.

      Then add a handler for close events which will be trigerred when a connected client terminates the connection. Whenever a client disconnects, we want to remove the client from the sockets array so we no longer broadcast to it. Add this code at the end of the connection block:

      server.js

      
      let sockets = [];
      server.on('connection', function(sock) {
      
          ...
      
          // Add a 'close' event handler to this instance of socket
          sock.on('close', function(data) {
              let index = sockets.findIndex(function(o) {
                  return o.remoteAddress === sock.remoteAddress && o.remotePort === sock.remotePort;
              })
              if (index !== -1) sockets.splice(index, 1);
              console.log('CLOSED: ' + sock.remoteAddress + ' ' + sock.remotePort);
          });
      });
      

      Here is the complete code for server.js:

      server.js

      const net = require('net');
      const port = 7070;
      const host = '127.0.0.1';
      
      const server = net.createServer();
      server.listen(port, host, () => {
          console.log('TCP Server is running on port ' + port + '.');
      });
      
      let sockets = [];
      
      server.on('connection', function(sock) {
          console.log('CONNECTED: ' + sock.remoteAddress + ':' + sock.remotePort);
          sockets.push(sock);
      
          sock.on('data', function(data) {
              console.log('DATA ' + sock.remoteAddress + ': ' + data);
              // Write the data back to all the connected, the client will receive it as data from the server
              sockets.forEach(function(sock, index, array) {
                  sock.write(sock.remoteAddress + ':' + sock.remotePort + " said " + data + 'n');
              });
          });
      
          // Add a 'close' event handler to this instance of socket
          sock.on('close', function(data) {
              let index = sockets.findIndex(function(o) {
                  return o.remoteAddress === sock.remoteAddress && o.remotePort === sock.remotePort;
              })
              if (index !== -1) sockets.splice(index, 1);
              console.log('CLOSED: ' + sock.remoteAddress + ' ' + sock.remotePort);
          });
      });
      

      Save the file and then start the server again:

      We have a fully functional TCP Server running on our machine. Next we'll write a client to connect to our server.

      Step 2 — Creating a Node.js TCP Client

      Our Node.js TCP Server is running, so let's create a TCP Client to connect to the server and test the server out.

      The Node.js server you just wrote is still running, blocking your current terminal session. We want to keep that running as we develop the client, so open a new Terminal window or tab. Then connect into the server again from the new tab.

      Once connected, navigate to the tcp-nodejs-app directory:

      In the same directory, create a new file called client.js:

      The client will use the same net library used in the server.js file to connect to the TCP server. Add this code to the file to connect to the server using the IP address 127.0.0.1 on port 7070:

      client.js

      const net = require('net');
      const client = new net.Socket();
      const port = 7070;
      const host = '127.0.0.1';
      
      client.connect(port, host, function() {
          console.log('Connected');
          client.write("Hello From Client " + client.address().address);
      });
      

      This code will first try to connect to the TCP server to ensure that the server we created is running. Once the connection is established, the client will send "Hello From Client " + client.address().address to the server using the client.write function. Our server will receive this data and echo it back to the client.

      Once the client receives the data back from the server, we want it to print the server's response. Add this code to catch the data event and print the server's response to the command line:

      client.js

      client.on('data', function(data) {
          console.log('Server Says : ' + data);
      });
      

      Finally, handle disconnections from the server gracefully by adding this code:

      client.js

      client.on('close', function() {
          console.log('Connection closed');
      });
      
      

      Save the client.js file.

      Run the following command to start the client:

      The connection will establish and the server will recieve the data, echoing it back to the client:

      client.js Output

      Connected Server Says : 127.0.0.1:34548 said Hello From Client 127.0.0.1

      Switch back to the terminal where the server is running, and you'll see the following output:

      server.js Output

      CONNECTED: 127.0.0.1:34550 DATA 127.0.0.1: Hello From Client 127.0.0.1

      You have verified that you can establish a TCP connection between your server and client apps.

      Press CTRL+C to stop the server. Then switch to the other terminal session and press CTRL+C to stop the client. You can now disconnect this terminal session from your server and return to your original terminal session.

      In the next step we'll launch the server with PM2 and run it in the background.

      Step 3 — Running the Server with PM2

      You have a working server that accepts client connections, but it runs in the foreground. Let's run the server using PM2 so it runs in the backgrand and can restart gracefully.

      First, install PM2 on your server globally using npm:

      Once PM2 is installed, use it to run your server. Instead of running npm start to start the server, you'll use the pm2 command. Start the server:

      You'll see output like this:

      [secondary_label Output
      [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2
      [PM2] PM2 Successfully daemonized
      [PM2] Starting /home/sammy/tcp-nodejs-app/server.js in fork_mode (1 instance)
      [PM2] Done.
      ┌────────┬──────┬────────┬───┬─────┬───────────┐
      │ Name   │ mode │ status │ ↺ │ cpu │ memory    │
      ├────────┼──────┼────────┼───┼─────┼───────────┤
      │ server │ fork │ online │ 0 │ 5%  │ 24.8 MB   │
      └────────┴──────┴────────┴───┴─────┴───────────┘
       Use `pm2 show <id|name>` to get more details about an app
      
      

      The server is now running in the background. However, if we reboot the machine, it won't be running anymore, so let's create a systemd service for it.

      Run the following command to generate and install PM2's systemd startup scripts. Be sure to run this with sudo so the systemd files install automatically.

      You'll see this output:

      Output

      [PM2] Init System found: systemd Platform systemd ... [PM2] Writing init configuration in /etc/systemd/system/pm2-root.service [PM2] Making script booting at startup... [PM2] [-] Executing: systemctl enable pm2-root... Created symlink from /etc/systemd/system/multi-user.target.wants/pm2-root.service to /etc/systemd/system/pm2-root.service. [PM2] [v] Command successfully executed. +---------------------------------------+ [PM2] Freeze a process list on reboot via: $ pm2 save [PM2] Remove init script via: $ pm2 unstartup systemd

      PM2 is now running as a systemd service.

      You can list all the processes PM2 is managing with the pm2 list command:

      You'll see your application in the list, with the ID of 0:

      Output

      ┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬───────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │ ├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼───────┼──────────┤ │ server │ 0 │ fork │ 9075 │ online │ 0 │ 4m │ 0% │ 30.5 MB │ sammy │ disabled │ └──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴───────┴──────────┘

      In the preceding output, you'll notice that watching is disabled. This is a feature that reloads the server when you make a change to any of the application files. It's useful in development, but we don't need that feature in production.

      To get more info about any of the running processes, use the pm2 show command, followed by its ID. In this case, the ID is 0:

      This output shows the uptime, status, log file paths, and other info about the running application:

      Output

      Describing process with id 0 - name server ┌───────────────────┬──────────────────────────────────────────┐ │ status │ online │ │ name │ server │ │ restarts │ 0 │ │ uptime │ 7m │ │ script path │ /home/sammy/tcp-nodejs-app/server.js │ │ script args │ N/A │ │ error log path │ /home/sammy/.pm2/logs/server-error-0.log │ │ out log path │ /home/sammy/.pm2/logs/server-out-0.log │ │ pid path │ /home/sammy/.pm2/pids/server-0.pid │ │ interpreter │ node │ │ interpreter args │ N/A │ │ script id │ 0 │ │ exec cwd │ /home/sammy/tcp-nodejs-app │ │ exec mode │ fork_mode │ │ node.js version │ 8.11.2 │ │ watch & reload │ ✘ │ │ unstable restarts │ 0 │ │ created at │ 2018-05-30T19:29:45.765Z │ └───────────────────┴──────────────────────────────────────────┘ Code metrics value ┌─────────────────┬────────┐ │ Loop delay │ 1.12ms │ │ Active requests │ 0 │ │ Active handles │ 3 │ └─────────────────┴────────┘ Add your own code metrics: http://bit.ly/code-metrics Use `pm2 logs server [--lines 1000]` to display logs Use `pm2 monit` to monitor CPU and Memory usage server

      If the application status shows an error, you can use the error log path to open and review the error log to debug the error:

      • cat /home/tcp/.pm2/logs/server-error-0.log

      If you make changes to the server code, you'll need to restart the application's process to apply the changes, like this:

      PM2 is now managing the application. Now we'll use Nginx to proxy requests to the server.

      Step 4 — Set Up Nginx as a Reverse Proxy Server

      Your application is running and listening on 127.0.0.1, which means it will only accept connections from the local machine. We will set up Nginx as a reverse proxy which will handle incoming traffic and direct it to our server.

      To do this, we'll modify the Nginx configuration to use the stream {} and stream_proxy features of Nginx to forward TCP connections to our Node.js server.

      We have to edit the main Nginx configuration file as the stream block that configures TCP connection forwarding only works as a top-level block. The default Nginx configuration on Ubuntu loads server blocks within the http block of the file, and the stream block can't be placed within that block.

      Open the file /etc/nginx/nginx.conf in your editor:

      • sudo nano /etc/nginx/nginx.conf

      Add the following lines at the end of your configuration file:

      /etc/nginx/nginx.conf

      
      ...
      
      stream {
          server {
            listen 3000;
            proxy_pass 127.0.0.1:7070;        
            proxy_protocol on;
          }
      }
      

      This listens for TCP connections on port 3000 and proxies the requests to your Node.js server running on port 7070. If your application is set to listen on a different port, update the proxy pass URL port to the correct port number. The proxy_protocol directive tells Nginx to use the PROXY protocol to send client information to backend servers, which can then process that information as needed.

      Save the file and exit the editor.

      Check your Nginx configuration to ensure you didn't introduce any syntax errors:

      Next, restart Nginx to enable the TCP and UDP proxy functionality:

      • sudo systemctl restart nginx

      Next, allow TCP connections to our server on that port. Use ufw to allow connections on port 3000:

      Assuming that your Node.js application is running, and your application and Nginx configurations are correct, you should now be able to access your application via the Nginx reverse proxy.

      Step 5 — Testing the Client-Server Connection

      Let's test the server out by connecting to the TCP server from our local machine using the client.js script. To do so, you'll need to download the client.js file you developed to your local machine and change the port and IP address in the script.

      First, on your local machine, download the client.js file using scp:

      • [environment local
      • scp sammy@your_server_ip:~/tcp-nodejs-app/client.js client.js

      Open the client.js file in your editor:

      • [environment local
      • nano client.js

      Change the port to 3000 and change the host to your server's IP address:

      client.js

      // A Client Example to connect to the Node.js TCP Server
      const net = require('net');
      const client = new net.Socket();
      const port = 3000;
      const host = 'your_server_ip';
      ...
      
      

      Save the file, exit the editor, and test things out by running the client:

      You'll see the same output you saw when you ran it before, indicating that your client machine has connected through Nginx and reached your server:

      client.js Output

      Connected Server Says : 127.0.0.1:34584 said PROXY TCP4 your_local_ip_address your_server_ip 52920 3000 Hello From Client your_local_ip_address

      Since Nginx is proxying client connections to your server, your Node.js server won't see the real IP addresses of the clients; it will only see Nginx's IP address. Nginx doesn't support sending the real IP address to the backend directly without making some changes to your system that could impact security, but since we enabled the PROXY protocol in Nginx, the Node.js server is now receiving an additional PROXY message that contains the real IP. If you need that IP address, you can adapt your server to process PROXY requests and parse out the data you need.

      You now have your Node.js TCP application running behind an Nginx reverse proxy and can continue to develop your server further.

      Conclusion

      In this tutorial you created a TCP application with Node.js, ran it with PM2, and served it behind Nginx. You also created a client application to connect to it from other machines. You can use this application to handle large chunks of data streams or to build real-time messaging applications.



      Source link