One place for hosting & domains

      April 2019

      How To Build and Deploy a GraphQL Server with Node.js and MongoDB on Ubuntu 18.04


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

      Introduction

      GraphQL was publicly released by Facebook in 2015 as a query language for APIs that makes it easy to query and mutate data from different data collections. From a single endpoint, you can query and mutate multiple data sources with a single POST request. GraphQL solves some of the common design flaws in REST API architectures, such as situations where the endpoint returns more information than you actually need. Also, it is possible when using REST APIs you would need to send requests to multiple REST endpoints to collect all the information you require—a situation that is called the n+1 problem. An example of this would be when you want to show a users’ information, but need to collect data such as personal details and addresses from different endpoints.

      These problems don’t apply to GraphQL as it has only one endpoint, which can return data from multiple collections. The data it returns depends on the query that you send to this endpoint. In this query you define the structure of the data you want to receive, including any nested data collections. In addition to a query, you can also use a mutation to change data on a GraphQL server, and a subscription to watch for changes in the data. For more information about GraphQL and its concepts, you can visit the documentation on the official website.

      As GraphQL is a query language with a lot of flexibility, it combines especially well with document-based databases like MongoDB. Both technologies are based on hierarchical, typed schemas and are popular within the JavaScript community. Also, MongoDB’s data is stored as JSON objects, so no additional parsing is necessary on the GraphQL server.

      In this tutorial, you’ll build and deploy a GraphQL server with Node.js that can query and mutate data from a MongoDB database that is running on Ubuntu 18.04. At the end of this tutorial, you’ll be able to access data in your database by using a single endpoint, both by sending requests to the server directly through the terminal and by using the pre-made GraphiQL playground interface. With this playground you can explore the contents of the GraphQL server by sending queries, mutations, and subscriptions. Also, you can find visual representations of the schemas that are defined for this server.

      At the end of this tutorial, you’ll use the GraphiQL playground to quickly interface with your GraphQL server:

      The GraphiQL playground in action

      Prerequisites

      Before you begin this guide you’ll need the following:

      Step 1 — Setting Up the MongoDB Database

      Before creating the GraphQL server, make sure your database is configured right, has authentication enabled, and is filled with sample data. For this you need to connect to the Ubuntu 18.04 server running the MongoDB database from your command prompt. All steps in this tutorial will take place on this server.

      After you’ve established the connection, run the following command to check if MongoDB is active and running on your server:

      • sudo systemctl status mongodb

      You’ll see the following output in your terminal, indicating the MongoDB database is actively running:

      Output

      ● mongodb.service - An object/document-oriented database Loaded: loaded (/lib/systemd/system/mongodb.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2019-02-23 12:23:03 UTC; 1 months 13 days ago Docs: man:mongod(1) Main PID: 2388 (mongod) Tasks: 25 (limit: 1152) CGroup: /system.slice/mongodb.service └─2388 /usr/bin/mongod --unixSocketPrefix=/run/mongodb --config /etc/mongodb.conf

      Before creating the database where you’ll store the sample data, you need to create an admin user first, since regular users are scoped to a specific database. You can do this by executing the following command that opens the MongoDB shell:

      With the MongoDB shell you'll get direct access to the MongoDB database and can create users or databases and query data. Inside this shell, execute the following command that will add a new admin user to MongoDB. You can replace the highlighted keywords with your own username and password combination, but don't forget to write them down somewhere.

      • use admin
      • db.createUser({
      • user: "admin_username",
      • pwd: "admin_password",
      • roles: [{ role: "root", db: "admin"}]
      • })

      The first line of the preceding command selects the database called admin, which is the database where all the admin roles are stored. With the method db.createUser() you can create the actual user and define its username, password, and roles.

      Executing this command will return:

      Output

      Successfully added user: { "user" : "admin_username", "roles" : [ { "role" : "root", "db" : "admin" } ] }

      You can now close the MongoDB shell by typing exit.

      Next, log in at the MongoDB shell again, but this time with the newly created admin user:

      • mongo -u "admin_username" -p "admin_password" --authenticationDatabase "admin"

      This command will open the MongoDB shell as a specific user, where the -u flag specifies the username and the -p flag the password of that user. The extra flag --authenticationDatabase specifies that you want to log in as an admin.

      Next, you'll switch to a new database and then use the db.createUser() method to create a new user with permissions to make changes to this database. Replace the highlighted sections with your own information, making sure to write these credentials down.

      Run the following command in the MongoDB shell:

      • use database_name
      • db.createUser({
      • user: "username",
      • pwd: "password",
      • roles: ["readWrite"]
      • })

      This will return the following:

      Output

      Successfully added user: { "user" : "username", "roles" : ["readWrite"] }

      After creating the database and user, fill this database with sample data that can be queried by the GraphQL server later on in this tutorial. For this, you can use the bios collection sample from the MongoDB website. By executing the commands in the following code snippet you'll insert a smaller version of this bios collection dataset into your database. You can replace the highlighted sections with your own information, but for the purposes of this tutorial, name the collection bios:

      • db.bios.insertMany([
      • {
      • "_id" : 1,
      • "name" : {
      • "first" : "John",
      • "last" : "Backus"
      • },
      • "birth" : ISODate("1924-12-03T05:00:00Z"),
      • "death" : ISODate("2007-03-17T04:00:00Z"),
      • "contribs" : [
      • "Fortran",
      • "ALGOL",
      • "Backus-Naur Form",
      • "FP"
      • ],
      • "awards" : [
      • {
      • "award" : "W.W. McDowell Award",
      • "year" : 1967,
      • "by" : "IEEE Computer Society"
      • },
      • {
      • "award" : "National Medal of Science",
      • "year" : 1975,
      • "by" : "National Science Foundation"
      • },
      • {
      • "award" : "Turing Award",
      • "year" : 1977,
      • "by" : "ACM"
      • },
      • {
      • "award" : "Draper Prize",
      • "year" : 1993,
      • "by" : "National Academy of Engineering"
      • }
      • ]
      • },
      • {
      • "_id" : ObjectId("51df07b094c6acd67e492f41"),
      • "name" : {
      • "first" : "John",
      • "last" : "McCarthy"
      • },
      • "birth" : ISODate("1927-09-04T04:00:00Z"),
      • "death" : ISODate("2011-12-24T05:00:00Z"),
      • "contribs" : [
      • "Lisp",
      • "Artificial Intelligence",
      • "ALGOL"
      • ],
      • "awards" : [
      • {
      • "award" : "Turing Award",
      • "year" : 1971,
      • "by" : "ACM"
      • },
      • {
      • "award" : "Kyoto Prize",
      • "year" : 1988,
      • "by" : "Inamori Foundation"
      • },
      • {
      • "award" : "National Medal of Science",
      • "year" : 1990,
      • "by" : "National Science Foundation"
      • }
      • ]
      • }
      • ]);

      This code block is an array consisting of multiple objects that contain information about successful scientists from the past. After running these commands to enter this collection into your database, you'll receive the following message indicating the data was added:

      Output

      { "acknowledged" : true, "insertedIds" : [ 1, ObjectId("51df07b094c6acd67e492f41") ] }

      After seeing the success message, you can close the MongoDB shell by typing exit. Next, configure the MongoDB installation to have authorization enabled so only authenticated users can access the data. To edit the configuration of the MongoDB installation, open the file containing the settings for this installation:

      • sudo nano /etc/mongodb.conf

      Uncomment the highlighted line in the following code to enable authorization:

      /etc/mongodb.conf

      ...
      # Turn on/off security.  Off is currently the default
      #noauth = true
      auth = true
      ...
      

      In order to make these changes active, restart MongoDB by running:

      • sudo systemctl restart mongodb

      Make sure the database is running again by executing the command:

      • sudo systemctl status mongodb

      This will yield output similar to the following:

      Output

      ● mongodb.service - An object/document-oriented database Loaded: loaded (/lib/systemd/system/mongodb.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2019-02-23 12:23:03 UTC; 1 months 13 days ago Docs: man:mongod(1) Main PID: 2388 (mongod) Tasks: 25 (limit: 1152) CGroup: /system.slice/mongodb.service └─2388 /usr/bin/mongod --unixSocketPrefix=/run/mongodb --config /etc/mongodb.conf

      To make sure that your user can connect to the database you just created, try opening the MongoDB shell as an authenticated user with the command:

      • mongo -u "username" -p "password" --authenticationDatabase "database_name"

      This uses the same flags as before, only this time the --authenticationDatabase is set to the database you've created and filled with the sample data.

      Now you've successfully added an admin user and another user that has read/write access to the database with the sample data. Also, the database has authorization enabled meaning you need a username and password to access it. In the next step you'll create the GraphQL server that will be connected to this database later in the tutorial.

      Step 2 — Creating the GraphQL Server

      With the database configured and filled with sample data, it's time to create a GraphQL server that can query and mutate this data. For this you'll use Express and express-graphql, which both run on Node.js. Express is a lightweight framework to quickly create Node.js HTTP servers, and express-graphql provides middleware to make it possible to quickly build GraphQL servers.

      The first step is to make sure your machine is up to date:

      Next, install Node.js on your server by running the following commands. Together with Node.js you'll also install npm, a package manager for JavaScript that runs on Node.js.

      • sudo apt install nodejs npm

      After following the installation process, check if the Node.js version you've just installed is v8.10.0 or higher:

      This will return the following:

      Output

      v8.10.0

      To initialize a new JavaScript project, run the following commands on the server as a sudo user, and replace the highlighted keywords with a name for your project.

      First move into the root directory of your server:

      Once there, create a new directory named after your project:

      Move into this directory:

      Finally, initialize a new npm package with the following command:

      After running npm init -y you'll receive a success message that the following package.json file was created:

      Output

      Wrote to /home/username/project_name/package.json: { "name": "project_name", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }

      Note: You can also execute npm init without the -y flag, after which you would answer multiple questions to set up the project name, author, etc. You can enter the details or just press enter to proceed.

      Now that you've initialized the project, install the packages you need to set up the GraphQL server:

      • sudo npm install --save express express-graphql graphql

      Create a new file called index.js and subsequently open this file by running:

      Next, add the following code block into the newly created file to set up the GraphQL server:

      index.js

      const express = require('express');
      const graphqlHTTP = require('express-graphql');
      const { buildSchema } = require('graphql');
      
      // Construct a schema, using GraphQL schema language
      const schema = buildSchema(`
        type Query {
          hello: String
        }
      `);
      
      // Provide resolver functions for your schema fields
      const resolvers = {
        hello: () => 'Hello world!'
      };
      
      const app = express();
      app.use('/graphql', graphqlHTTP({
        schema,
        rootValue: resolvers
      }));
      app.listen(4000);
      
      console.log(`🚀 Server ready at http://localhost:4000/graphql`);
      

      This code block consists of several parts that are all important. First you describe the schema of the data that is returned by the GraphQL API:

      index.js

      ...
      // Construct a schema, using GraphQL schema language
      const schema = buildSchema(`
        type Query {
          hello: String
        }
      `);
      ...
      

      The type Query defines what queries can be executed and in which format it will return the result. As you can see, the only query defined is hello that returns data in a String format.

      The next section establishes the resolvers, where data is matched to the schemas that you can query:

      index.js

      ...
      // Provide resolver functions for your schema fields
      const resolvers = {
        hello: () => 'Hello world!'
      };
      ...
      

      These resolvers are directly linked to schemas, and return the data that matches these schemas.

      The final part of this code block initializes the GraphQL server, creates the API endpoint with Express, and describes the port on which the GraphQL endpoint is running:

      index.js

      ...
      const app = express();
      app.use('/graphql', graphqlHTTP({
        schema,
        rootValue: resolvers
      }));
      app.listen(4000);
      
      console.log(`🚀 Server ready at http://localhost:4000/graphql`);
      

      After you have added these lines, save and exit from index.js.

      Next, to actually run the GraphQL server you need to run the file index.js with Node.js. This can be done manually from the command line, but it's common practice to set up the package.json file to do this for you.

      Open the package.json file:

      Add the following highlighted line to this file:

      package.json

      {
        "name": "project_name",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
          "start": "node index.js",
          "test": "echo "Error: no test specified" && exit 1"
        },
        "keywords": [],
        "author": "",
        "license": "ISC"
      }
      

      Save and exit the file.

      To start the GraphQL server, execute the following command in the terminal:

      Once you run this, the terminal prompt will disappear, and a message will appear to confirm the GraphQL server is running:

      Output

      🚀 Server ready at http://localhost:4000/graphql

      If you now open up another terminal session, you can test if the GraphQL server is running by executing the following command. This sends a curl POST request with a JSON body after the --data flag that contains your GraphQL query to the local endpoint:

      • curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ hello }" }' http://localhost:4000/graphql

      This will execute the query as it's described in the GraphQL schema in your code and return data in a predictable JSON format that is equal to the data as it's returned in the resolvers:

      Output

      { "data": { "hello": "Hello world!" } }

      Note: In case the Express server crashes or gets stuck, you need to manually kill the node process that is running on the server. To kill all such processes, you can execute the following:

      After which, you can restart the GraphQL server by running:

      In this step you've created the first version of the GraphQL server that is now running on a local endpoint that can be accessed on your server. Next, you'll connect your resolvers to the MongoDB database.

      Step 3 — Connecting to the MongoDB Database

      With the GraphQL server in order, you can now set up the connection with the MongoDB database that you configured and filled with data before and create a new schema that matches this data.

      To be able to connect to MongoDB from the GraphQL server, install the JavaScript package for MongoDB from npm:

      • sudo npm install --save mongodb

      Once this has been installed, open up index.js in your text editor:

      Next, add the following highlighted code to index.js just after the imported dependencies and fill the highlighted values with your own connection details to the local MongoDB database. The username, password, and database_name are those that you created in the first step of this tutorial.

      index.js

      const express = require('express');
      const graphqlHTTP = require('express-graphql');
      const { buildSchema } = require('graphql');
      const { MongoClient } = require('mongodb');
      
      const context = () => MongoClient.connect('mongodb://username:password@localhost:27017/database_name', { useNewUrlParser: true }).then(client => client.db('database_name'));
      ...
      

      These lines add the connection to the local MongoDB database to a function called context. This context function will be available to every resolver, which is why you use this to set up database connections.

      Next, in your index.js file, add the context function to the initialization of the GraphQL server by inserting the following highlighted lines:

      index.js

      ...
      const app = express();
      app.use('/graphql', graphqlHTTP({
        schema,
        rootValue: resolvers,
        context
      }));
      app.listen(4000);
      
      console.log(`🚀 Server ready at http://localhost:4000/graphql`);
      

      Now you can call this context function from your resolvers, and thereby read variables from the MongoDB database. If you look back to the first step of this tutorial, you can see which values are present in the database. From here, define a new GraphQL schema that matches this data structure. Overwrite the previous value for the constant schema with the following highlighted lines:

      index.js

      ...
      // Construct a schema, using GrahQL schema language
      const schema = buildSchema(`
        type Query {
          bios: [Bio]
        }
        type Bio {
          name: Name,
          title: String,
          birth: String,
          death: String,
          awards: [Award]
        }
        type Name {
          first: String,
          last: String
        },
        type Award {
          award: String,
          year: Float,
          by: String
        }
      `);
      ...
      

      The type Query has changed and now returns a collection of the new type Bio. This new type consists of several types including two other non-scalar types Name and Awards, meaning these types don't match a predefined format like String or Float. For more information on defining GraphQL schemas you can look at the documentation for GraphQL.

      Also, since the resolvers tie the data from the database to the schema, update the code for the resolvers when you make changes to the schema. Create a new resolver that is called bios, which is equal to the Query that can be found in the schema and the name of the collection in the database. Note that, in this case, the name of the collection in db.collection('bios') is bios, but that this would change if you had assigned a different name to your collection.

      Add the following highlighted line to index.js:

      index.js

      ...
      // Provide resolver functions for your schema fields
      const resolvers = {
        bios: (args, context) => context().then(db => db.collection('bios').find().toArray())
      };
      ...
      

      This function will use the context function, which you can use to retrieve variables from the MongoDB database. Once you have made these changes to the code, save and exit index.js.

      In order to make these changes active, you need to restart the GraphQL server. You can stop the current process by using the keyboard combination CTRL + C and start the GraphQL server by running:

      Now you're able to use the updated schema and query the data that is inside the database. If you look at the schema, you'll see that the Query for bios returns the type Bio; this type could also return the type Name.

      To return all the first and last names for all the bios in the database, send the following request to the GraphQL server in a new terminal window:

      • curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ bios { name { first, last } } }" }' http://localhost:4000/graphql

      This again will return a JSON object that matches the structure of the schema:

      Output

      {"data":{"bios":[{"name":{"first":"John","last":"Backus"}},{"name":{"first":"John","last":"McCarthy"}}]}}

      You can easily retrieve more variables from the bios by extending the query with any of the types that are described in the type for Bio.

      Also, you can retrieve a bio by specifying an id. In order to do this you need to add another type to the Query type and extend the resolvers. To do this, open index.js in your text editor:

      Add the following highlighted lines of code:

      index.js

      ...
      // Construct a schema, using GrahQL schema language
      const schema = buildSchema(`
        type Query {
          bios: [Bio]
          bio(id: Int): Bio
        }
      
        ...
      
        // Provide resolver functions for your schema fields
        const resolvers = {
          bios: (args, context) => context().then(db => db.collection('bios').find().toArray()),
          bio: (args, context) => context().then(db => db.collection('bios').findOne({ _id: args.id }))
        };
        ...
      

      Save and exit the file.

      In the terminal that is running your GraphQL server, press CTRL + C to stop it from running, then execute the following to restart it:

      In another terminal window, execute the following GraphQL request:

      • curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ bio(id: 1) { name { first, last } } }" }' http://localhost:4000/graphql

      This returns the entry for the bio that has an id equal to 1:

      Output

      { "data": { "bio": { "name": { "first": "John", "last": "Backus" } } } }

      Being able to query data from a database is not the only feature of GraphQL; you can also change the data in the database. To do this, open up index.js:

      Next to the type Query you can also use the type Mutation, which allows you to mutate the database. To use this type, add it to the schema and also create input types by inserting these highlighted lines:

      index.js

      ...
      // Construct a schema, using GraphQL schema language
      const schema = buildSchema(`
        type Query {
          bios: [Bio]
          bio(id: Int): Bio
        }
        type Mutation {
          addBio(input: BioInput) : Bio
        }
        input BioInput {
          name: NameInput
          title: String
          birth: String
          death: String
        }
        input NameInput {
          first: String
          last: String
        }
      ...
      

      These input types define which variables can be used as inputs, which you can access in the resolvers and use to insert a new document in the database. Do this by adding the following lines to index.js:

      index.js

      ...
      // Provide resolver functions for your schema fields
      const resolvers = {
        bios: (args, context) => context().then(db => db.collection('bios').find().toArray()),
        bio: (args, context) => context().then(db => db.collection('bios').findOne({ _id: args.id })),
        addBio: (args, context) => context().then(db => db.collection('bios').insertOne({ name: args.input.name, title: args.input.title, death: args.input.death, birth: args.input.birth})).then(response => response.ops[0])
      };
      ...
      

      Just as with the resolvers for regular queries, you need to return a value from the resolver in index.js. In the case of a Mutation where the type Bio is mutated, you would return the value of the mutated bio.

      At this point, your index.js file will contain the following lines:

      index.js

      iconst express = require('express');
      const graphqlHTTP = require('express-graphql');
      const { buildSchema } = require('graphql');
      const { MongoClient } = require('mongodb');
      
      const context = () => MongoClient.connect('mongodb://username:password@localhost:27017/database_name', { useNewUrlParser: true })
        .then(client => client.db('GraphQL_Test'));
      
      // Construct a schema, using GraphQL schema language
      const schema = buildSchema(`
        type Query {
          bios: [Bio]
          bio(id: Int): Bio
        }
        type Mutation {
          addBio(input: BioInput) : Bio
        }
        input BioInput {
          name: NameInput
          title: String
          birth: String
          death: String
        }
        input NameInput {
          first: String
          last: String
        }
        type Bio {
          name: Name,
          title: String,
          birth: String,
          death: String,
          awards: [Award]
        }
        type Name {
          first: String,
          last: String
        },
        type Award {
          award: String,
          year: Float,
          by: String
        }
      `);
      
      // Provide resolver functions for your schema fields
      const resolvers = {
        bios: (args, context) =>context().then(db => db.collection('Sample_Data').find().toArray()),
        bio: (args, context) =>context().then(db => db.collection('Sample_Data').findOne({ _id: args.id })),
        addBio: (args, context) => context().then(db => db.collection('Sample_Data').insertOne({ name: args.input.name, title: args.input.title, death: args.input.death, birth: args.input.birth})).then(response => response.ops[0])
      };
      
      const app = express();
      app.use('/graphql', graphqlHTTP({
        schema,
        rootValue: resolvers,
        context
      }));
      app.listen(4000);
      
      console.log(`🚀 Server ready at http://localhost:4000/graphql`);
      

      Save and exit index.js.

      To check if your new mutation is working, restart the GraphQL server by pressing CTRL + c and running npm start in the terminal that is running your GraphQL server, then open another terminal session to execute the following curl request. Just as with the curl request for queries, the body in the --data flag will be sent to the GraphQL server. The highlighted parts will be added to the database:

      • curl -X POST -H "Content-Type: application/json" --data '{ "query": "mutation { addBio(input: { name: { first: "test", last: "user" } }) { name { first, last } } }" }' http://localhost:4000/graphql

      This returns the following result, meaning you just inserted a new bio to the database:

      Output

      { "data": { "addBio": { "name": { "first": "test", "last": "user" } } } }

      In this step, you created the connection with MongoDB and the GraphQL server, allowing you to retrieve and mutate data from this database by executing GraphQL queries. Next, you'll expose this GraphQL server for remote access.

      Step 4 — Allowing Remote Access

      Having set up the database and the GraphQL server, you can now configure the GraphQL server to allow remote access. For this you'll use Nginx, which you set up in the prerequisite tutorial How to install Nginx on Ubuntu 18.04. This Nginx configuration can be found in the /etc/nginx/sites-available/example.com file, where example.com is the server name you added in the prerequisite tutorial.

      Open this file for editing, replacing your domain name with example.com:

      • sudo nano /etc/nginx/sites-available/example.com

      In this file you can find a server block that listens to port 80, where you've already set up a value for server_name in the prerequisite tutorial. Inside this server block, change the value for root to be the directory in which you created the code for the GraphQL server and add index.js as the index. Also, within the location block, set a proxy_pass so you can use your server's IP or a custom domain name to refer to the GraphQL server:

      /etc/nginx/sites-available/example.com

      server {
        listen 80;
        listen [::]:80;
      
        root /project_name;
        index index.js;
      
        server_name example.com;
      
        location / {
          proxy_pass http://localhost:4000/graphql;
        }
      }
      

      Make sure there are no Nginx syntax errors in this configuration file by running:

      You will receive the following output:

      Output

      nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful

      When there are no errors found for the configuration file, restart Nginx:

      • sudo systemctl restart nginx

      Now you will be able to access your GraphQL server from any terminal session tab by executing and replacing example.com by either your server's IP or your custom domain name:

      • curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ bios { name { first, last } } }" }' http://example.com

      This will return the same JSON object as the one of the previous step, including any additional data you might have added by using a mutation:

      Output

      {"data":{"bios":[{"name":{"first":"John","last":"Backus"}},{"name":{"first":"John","last":"McCarthy"}},{"name":{"first":"test","last":"user"}}]}}

      Now that you have made your GraphQL server accessible remotely, make sure your GraphQL server doesn't go down when you close the terminal or the server restarts. This way, your MongoDB database will be accessible via the GraphQL server whenever you want to make a request.

      To do this, use the npm package forever, a CLI tool that ensures that your command line scripts run continuously, or get restarted in case of any failure.

      Install forever with npm:

      • sudo npm install forever -g

      Once it is done installing, add it to the package.json file:

      package.json

      {
        "name": "project_name",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
          "start": "node index.js",
          "deploy": "forever start --minUptime 2000 --spinSleepTime 5 index.js",
          "test": "echo "Error: no test specified" && exit 1"
        },
        ...
      

      To start the GraphQL server with forever enabled, run the following command:

      This will start the index.js file containing the GraphQL server with forever, and ensure it will keep running with a minimum uptime of 2000 milliseconds and 5 milliseconds between every restart in case of a failure. The GraphQL server will now continuously run in the background, so you don't need to open a new tab any longer when you want to send a request to the server.

      You've now created a GraphQL server that is using MongoDB to store data and is set up to allow access from a remote server. In the next step you'll enable the GraphiQL playground, which will make it easier for you to inspect the GraphQL server.

      Step 5 — Enabling GraphiQL Playground

      Being able to send cURL requests to the GraphQL server is great, but it would be faster to have a user interface that can execute GraphQL requests immediately, especially during development. For this you can use GraphiQL, an interface supported by the package express-graphql.

      To enable GraphiQL, edit the file index.js:

      Add the following highlighted lines:

      index.js

      const app = express();
      app.use('/graphql', graphqlHTTP({
        schema,
        rootValue: resolvers,
        context,
        graphiql: true
      }));
      app.listen(4000);
      
      console.log(`🚀 Server ready at http://localhost:4000/graphql`);
      

      Save and exit the file.

      In order for these changes to become visible, make sure to stop forever by executing:

      Next, start forever again so the latest version of your GraphQL server is running:

      Open a browser at the URL http://example.com, replacing example.com with your domain name or your server IP. You will see the GraphiQL playground, where you can type GraphQL requests.

      The initial screen for the GraphiQL playground

      On the left side of this playground you can type the GraphQL queries and mutations, while the output will be shown on the right side of the playground. To test if this is working, type the following query on the left side:

      query {
        bios {
          name {
            first
            last
          }
        }
      }
      

      This will output the same result on the right side of the playground, again in JSON format:

      The GraphiQL playground in action

      Now you can send GraphQL requests using the terminal and the GraphiQL playground.

      Conclusion

      In this tutorial you've set up a MongoDB database and retrieved and mutated data from this database using GraphQL, Node.js, and Express for the server. Additionally, you configured Nginx to allow remote access to this server. Not only can you send requests to this GraphQL server directly, you can also use the GraphiQL as a visual, in-browser GraphQL interface.

      If you want to learn about GraphQL, you can watch a recording of my presentation on GraphQL at NDC {London} or visit the website howtographql.com for tutorials about GraphQL. To study how GraphQL interacts with other technologies, check out the tutorial on How to Manually Set Up a Prisma Server on Ubuntu 18.04, and for more information on building applications with MongoDB, see How To Build a Blog with Nest.js, MongoDB, and Vue.js.



      Source link

      Como Usar o Traefik como um Proxy Reverso para Containers do Docker no CentOS 7


      O autor selecionou o Girls Who Code para receber uma doação como parte do programa Write for DOnations.

      Introdução

      O Docker pode ser uma maneira eficiente de executar aplicativos web em produção, mas você pode querer executar vários aplicativos no mesmo host do Docker. Nesta situação, você precisará configurar um proxy reverso, já que você só deseja expor as portas 80 e 443 para o resto do mundo.

      O Traefik é um proxy reverso que reconhece o Docker e inclui seu próprio painel de monitoramento ou dashboard. Neste tutorial, você usará o Traefik para rotear solicitações para dois containers de aplicação web diferentes: um container WordPress e um container Adminer, cada um falando com um banco de dados MySQL. Você irá configurar o Traefik para servir tudo através de HTTPS utilizando o Let’s Encrypt.

      Pré-requisitos

      Para acompanhar este tutorial, você vai precisar do seguinte:

      Passo 1 — Configurando e Executando o Traefik

      O projeto do Traefik tem uma imagem Docker oficial, portanto vamos utilizá-la para executar o Traefik em um container Docker.

      Mas antes de colocarmos o nosso container Traefik em funcionamento, precisamos criar um arquivo de configuração e configurar uma senha criptografada para que possamos acessar o painel de monitoramento.

      Usaremos o utilitário htpasswd para criar essa senha criptografada. Primeiro, instale o utilitário, que está incluído no pacote httpd-tools:

      • sudo yum install -y httpd-tools

      Em seguida, gere a senha com o htpasswd. Substitua senha_segura pela senha que você gostaria de usar para o usuário admin do Traefik:

      • htpasswd -nb admin senha_segura

      A saída do programa ficará assim:

      Output

      admin:$apr1$kEG/8JKj$yEXj8vKO7HDvkUMI/SbOO.

      Você utilizará essa saída no arquivo de configuração do Traefik para configurar a Autenticação Básica de HTTP para a verificação de integridade do Traefik e para o painel de monitoramento. Copie toda a linha de saída para poder colá-la mais tarde.

      Para configurar o servidor Traefik, criaremos um novo arquivo de configuração chamado traefik.toml usando o formato TOML. O TOML é uma linguagem de configuração semelhante ao arquivos INI, mas padronizado. Esse arquivo nos permite configurar o servidor Traefik e várias integrações, ou providers, que queremos usar. Neste tutorial, usaremos três dos provedores disponíveis do Traefik: api,docker e acme, que é usado para suportar o TLS utilizando o Let’s Encrypt.

      Abra seu novo arquivo no vi ou no seu editor de textos favorito:

      Entre no modo de inserção pressionando i, adicione dois EntryPoints nomeados http ehttps, que todos os backends terão acesso por padrão:

      traefik.toml

      defaultEntryPoints = ["http", "https"]
      

      Vamos configurar os EntryPoints http e https posteriormente neste arquivo.

      Em seguida, configure o provider api, que lhe dá acesso a uma interface do painel. É aqui que você irá colar a saída do comando htpasswd:

      traefik.toml

      ...
      [entryPoints]
        [entryPoints.dashboard]
          address = ":8080"
          [entryPoints.dashboard.auth]
            [entryPoints.dashboard.auth.basic]
              users = ["admin:sua_senha_criptografada"]
      
      [api]
      entrypoint="dashboard"
      

      O painel é uma aplicação web separada que será executada no container do Traefik. Vamos definir o painel para executar na porta 8080.

      A seção entrypoints.dashboard configura como nos conectaremos com o provider da api, e a seção entrypoints.dashboard.auth.basic configura a Autenticação Básica HTTP para o painel. Use a saída do comando htpasswd que você acabou de executar para o valor da entrada users. Você poderia especificar logins adicionais, separando-os com vírgulas.

      Definimos nosso primeiro entryPoint, mas precisaremos definir outros para comunicação HTTP e HTTPS padrão que não seja direcionada para o provider da api. A seção entryPoints configura os endereços que o Traefik e os containers com proxy podem escutar. Adicione estas linhas ao arquivo logo abaixo do cabeçalho entryPoints:

      traefik.toml

      ...
        [entryPoints.http]
          address = ":80"
            [entryPoints.http.redirect]
              entryPoint = "https"
        [entryPoints.https]
          address = ":443"
            [entryPoints.https.tls]
      ...
      

      O entrypoint http manipula a porta 80, enquanto o entrypoint https usa a porta443 para o TLS/SSL. Redirecionamos automaticamente todo o tráfego na porta 80 para o entrypoint https para forçar conexões seguras para todas as solicitações.

      Em seguida, adicione esta seção para configurar o suporte ao certificado Let's Encrypt do Traefik:

      traefik.toml

      ...
      [acme]
      email = "seu_email@seu_domínio"
      storage = "acme.json"
      entryPoint = "https"
      onHostRule = true
        [acme.httpChallenge]
        entryPoint = "http"
      

      Esta seção é chamada acme porque ACME é o nome do protocolo usado para se comunicar com o Let's Encrypt para gerenciar certificados. O serviço Let's Encrypt requer o registro com um endereço de e-mail válido, portanto, para que o Traefik gere certificados para nossos hosts, defina a chave email como seu endereço de e-mail. Em seguida, vamos especificar que armazenaremos as informações que vamos receber do Let's Encrypt em um arquivo JSON chamado acme.json. A chave entryPoint precisa apontar para a porta de manipulação do entrypoint 443, que no nosso caso é o entrypoint https.

      A chave onHostRule determina como o Traefik deve gerar certificados. Queremos buscar nossos certificados assim que nossos containers com os nomes de host especificados forem criados, e é isso que a configuração onHostRule fará.

      A seção acme.httpChallenge nos permite especificar como o Let's Encrypt pode verificar se o certificado deve ser gerado. Estamos configurando-o para servir um arquivo como parte do desafio através do entrypoint http.

      Finalmente, vamos configurar o provider docker adicionando estas linhas ao arquivo:

      traefik.toml

      ...
      [docker]
      domain = "seu_domínio"
      watch = true
      network = "web"
      

      O provedor docker permite que o Traefik atue como um proxy na frente dos containers do Docker. Configuramos o provider para vigiar ou watch por novos containers na rede web (que criaremos em breve) e os expor como subdomínios de seu_domínio.

      Neste ponto, o traefik.toml deve ter o seguinte conteúdo:

      traefik.toml

      defaultEntryPoints = ["http", "https"]
      
      [entryPoints]
        [entryPoints.dashboard]
          address = ":8080"
          [entryPoints.dashboard.auth]
            [entryPoints.dashboard.auth.basic]
              users = ["admin:sua_senha_criptografada"]
        [entryPoints.http]
          address = ":80"
            [entryPoints.http.redirect]
              entryPoint = "https"
        [entryPoints.https]
          address = ":443"
            [entryPoints.https.tls]
      
      [api]
      entrypoint="dashboard"
      
      [acme]
      email = "seu_email@seu_domínio"
      storage = "acme.json"
      entryPoint = "https"
      onHostRule = true
        [acme.httpChallenge]
        entryPoint = "http"
      
      [docker]
      domain = "seu_domínio"
      watch = true
      network = "web"
      

      Depois de adicionar o conteúdo, pressione ESC para sair do modo de inserção. Digite :x e depois ENTER para salvar e sair do arquivo. Com toda essa configuração pronta, podemos ativar o Traefik.

      Passo 2 – Executando o Container Traefik

      Em seguida, crie uma rede do Docker para o proxy compartilhar com os containers. A rede do Docker é necessária para que possamos usá-la com aplicações que são executadas usando o Docker Compose. Vamos chamar essa rede de web.

      • docker network create web

      Quando o container Traefik iniciar, nós o adicionaremos a essa rede. Em seguida, podemos adicionar containers adicionais a essa rede posteriormente para o Traefik fazer proxy.

      Em seguida, crie um arquivo vazio que conterá as informações do Let's Encrypt. Compartilharemos isso no container para que o Traefik possa usá-lo:

      O Traefik só poderá usar esse arquivo se o usuário root dentro do container tiver acesso exclusivo de leitura e gravação a ele. Para fazer isso, bloqueie as permissões em acme.json para que somente o proprietário do arquivo tenha permissão de leitura e gravação.

      Depois que o arquivo for repassado para o Docker, o proprietário será automaticamente alterado para o usuário root dentro do container.

      Finalmente, crie o container Traefik com este comando:

      • docker run -d
      • -v /var/run/docker.sock:/var/run/docker.sock
      • -v $PWD/traefik.toml:/traefik.toml
      • -v $PWD/acme.json:/acme.json
      • -p 80:80
      • -p 443:443
      • -l traefik.frontend.rule=Host:monitor.seu_domínio
      • -l traefik.port=8080
      • --network web
      • --name traefik
      • traefik:1.7.6-alpine

      O comando é um pouco longo, então vamos dividi-lo.

      Usamos a flag -d para executar o container em segundo plano como um daemon. Em seguida, compartilhamos nosso arquivo docker.sock dentro do container para que o processo do Traefik possa escutar por alterações nos containers. Compartilhamos também o arquivo de configuração traefik.toml e o arquivoacme.json que criamos dentro do container.

      Em seguida, mapeamos as portas :80 e :443 do nosso host Docker para as mesmas portas no container Traefik, para que o Traefik receba todo o tráfego HTTP e HTTPS para o servidor.

      Em seguida, configuramos dois labels do Docker que informam ao Traefik para direcionar o tráfego para o monitor.seu_domínio para a porta :8080 dentro do container do Traefik, expondo o painel de monitoramento.

      Configuramos a rede do container para web, e nomeamos o container para traefik.

      Finalmente, usamos a imagem traefik:1.7.6-alpine para este container, porque é pequena.

      Um ENTRYPOINT da imagem do Docker é um comando que sempre é executado quando um container é criado a partir da imagem. Neste caso, o comando é o binário traefik dentro do container. Você pode passar argumentos adicionais para esse comando quando você inicia o container, mas definimos todas as nossas configurações no arquivo traefik.toml.

      Com o container iniciado, agora você tem um painel que você pode acessar para ver a integridade de seus containers. Você também pode usar este painel para visualizar os frontends e backends que o Traefik registrou. Acesse o painel de monitoramento apontando seu navegador para https://monitor.seu_domínio. Você será solicitado a fornecer seu nome de usuário e senha, que são admin e a senha que você configurou no Passo 1.

      Uma vez logado, você verá uma interface semelhante a esta:

      Empty Traefik dashboard

      Ainda não há muito o que ver, mas deixe essa janela aberta e você verá o conteúdo mudar à medida que você adiciona containers para o Traefik trabalhar.

      Agora temos nosso proxy Traefik em execução, configurado para funcionar com o Docker, e pronto para monitorar outros containers Docker. Vamos iniciar alguns containers para que o Traefik possa agir como proxy para eles.

      Com o container do Traefik em execução, você está pronto para executar aplicações por trás dele. Vamos lançar os seguintes containers por trás do Traefik:

      1. Um blog usando a imagem oficial do WordPress.

      2. Um servidor de gerenciamento de banco de dados usando a imagem oficial do Adminer.

      Vamos gerenciar essas duas aplicações com o Docker Compose usando um arquivo docker-compose.yml. Abra o arquivo docker-compose.yml em seu editor:

      Adicione as seguintes linhas ao arquivo para especificar a versão e as redes que usaremos:

      docker-compose.yml

      version: "3"
      
      networks:
        web:
          external: true
        internal:
          external: false
      

      Usamos a versão 3 do Docker Compose porque é a mais nova versão principal do formato de arquivo Compose.

      Para o Traefik reconhecer nossas aplicações, elas devem fazer parte da mesma rede e, uma vez que criamos a rede manualmente, nós a inserimos especificando o nome da rede web e configurandoexternal para true. Em seguida, definimos outra rede para que possamos conectar nossos containers expostos a um container de banco de dados que não vamos expor por meio do Traefik. Chamaremos essa rede de internal.

      Em seguida, definiremos cada um dos nossos serviços ou services, um de cada vez. Vamos começar com o container blog, que basearemos na imagem oficial do WordPress. Adicione esta configuração ao arquivo:

      docker-compose.yml

      version: "3"
      ...
      
      services:
        blog:
          image: wordpress:4.9.8-apache
          environment:
            WORDPRESS_DB_PASSWORD:
          labels:
            - traefik.backend=blog
            - traefik.frontend.rule=Host:blog.seu_domínio
            - traefik.docker.network=web
            - traefik.port=80
          networks:
            - internal
            - web
          depends_on:
            - mysql
      

      A chave environment permite que você especifique variáveis de ambiente que serão definidas dentro do container. Ao não definir um valor para WORDPRESS_DB_PASSWORD, estamos dizendo ao Docker Compose para obter o valor de nosso shell e repassá-lo quando criamos o container. Vamos definir essa variável de ambiente em nosso shell antes de iniciar os containers. Dessa forma, não codificamos senhas no arquivo de configuração.

      A seção labels é onde você especifica os valores de configuração do Traefik. As labels do Docker não fazem nada sozinhas, mas o Traefik as lê para saber como tratar os containers. Veja o que cada uma dessas labels faz:

      • traefik.backend especifica o nome do serviço de backend no Traefik (que aponta para o container real blog).
      • traefik.frontend.rule=Host:blog.seu_domínio diz ao Traefik para examinar o host solicitado e, se ele corresponde ao padrão de blog.seu_domínio, ele deve rotear o tráfego para o container blog.
      • traefik.docker.network=web especifica qual rede procurar sob o Traefik para encontrar o IP interno para esse container. Como o nosso container Traefik tem acesso a todas as informações do Docker, ele possivelmente levaria o IP para a rede internal se não especificássemos isso.
      • traefik.port especifica a porta exposta que o Traefik deve usar para rotear o tráfego para esse container.

      Com essa configuração, todo o tráfego enviado para a porta 80 do host do Docker será roteado para o container blog.

      Atribuímos este container a duas redes diferentes para que o Traefik possa encontrá-lo através da rede web e possa se comunicar com o container do banco de dados através da rede internal.

      Por fim, a chave depends_on informa ao Docker Compose que este container precisa ser iniciado após suas dependências estarem sendo executadas. Como o WordPress precisa de um banco de dados para ser executado, devemos executar nosso container mysql antes de iniciar nosso containerblog.

      Em seguida, configure o serviço MySQL adicionando esta configuração ao seu arquivo:

      docker-compose.yml

      services:
      ...
        mysql:
          image: mysql:5.7
          environment:
            MYSQL_ROOT_PASSWORD:
          networks:
            - internal
          labels:
            - traefik.enable=false
      

      Estamos usando a imagem oficial do MySQL 5.7 para este container. Você notará que estamos mais uma vez usando um item environment sem um valor. As variáveis MYSQL_ROOT_PASSWORD eWORDPRESS_DB_PASSWORD precisarão ser configuradas com o mesmo valor para garantir que nosso container WordPress possa se comunicar com o MySQL. Nós não queremos expor o container mysql para o Traefik ou para o mundo externo, então estamos atribuindo este container apenas à rede internal. Como o Traefik tem acesso ao soquete do Docker, o processo ainda irá expor um frontend para o container mysql por padrão, então adicionaremos a label traefik.enable=false para especificar que o Traefik não deve expor este container.

      Por fim, adicione essa configuração para definir o container do Adminer:

      docker-compose.yml

      services:
      ...
        adminer:
          image: adminer:4.6.3-standalone
          labels:
            - traefik.backend=adminer
            - traefik.frontend.rule=Host:db-admin.seu_domínio
            - traefik.docker.network=web
            - traefik.port=8080
          networks:
            - internal
            - web
          depends_on:
            - mysql
      

      Este container é baseado na imagem oficial do Adminer. A configuração network e depends_on para este container corresponde exatamente ao que estamos usando para o container blog.

      No entanto, como estamos redirecionando todo o tráfego para a porta 80 em nosso host Docker diretamente para o container blog, precisamos configurar esse container de forma diferente para que o tráfego chegue ao container adminer. A linha traefik.frontend.rule=Host:db-admin.seu_domínio diz ao Traefik para examinar o host solicitado. Se ele corresponder ao padrão do db-admin.seu_domínio, o Traefik irá rotear o tráfego para o container adminer.

      Neste ponto, docker-compose.yml deve ter o seguinte conteúdo:

      docker-compose.yml

      version: "3"
      
      networks:
        web:
          external: true
        internal:
          external: false
      
      services:
        blog:
          image: wordpress:4.9.8-apache
          environment:
            WORDPRESS_DB_PASSWORD:
          labels:
            - traefik.backend=blog
            - traefik.frontend.rule=Host:blog.seu_domínio
            - traefik.docker.network=web
            - traefik.port=80
          networks:
            - internal
            - web
          depends_on:
            - mysql
        mysql:
          image: mysql:5.7
          environment:
            MYSQL_ROOT_PASSWORD:
          networks:
            - internal
          labels:
            - traefik.enable=false
        adminer:
          image: adminer:4.6.3-standalone
          labels:
            - traefik.backend=adminer
            - traefik.frontend.rule=Host:db-admin.seu_domínio
            - traefik.docker.network=web
            - traefik.port=8080
          networks:
            - internal
            - web
          depends_on:
            - mysql
      

      Salve o arquivo e saia do editor de texto.

      Em seguida, defina valores em seu shell para as variáveis WORDPRESS_DB_PASSWORD e MYSQL_ROOT_PASSWORD antes de iniciar seus containers:

      • export WORDPRESS_DB_PASSWORD=senha_segura_do_banco_de_dados
      • export MYSQL_ROOT_PASSWORD=senha_segura_do_banco_de_dados

      Substitua senha_segura_do_banco_de_dados pela sua senha do banco de dados desejada. Lembre-se de usar a mesma senha tanto para WORDPRESS_DB_PASSWORD quanto para MYSQL_ROOT_PASSWORD.

      Com estas variáveis definidas, execute os containers usando o docker-compose:

      Agora, dê outra olhada no painel de administrador do Traefik. Você verá que agora existe um backend e um frontend para os dois servidores expostos:

      Populated Traefik dashboard

      Navegue até blog.seu_domínio, substituindo seu_domínio pelo seu domínio. Você será redirecionado para uma conexão TLS e poderá agora concluir a configuração do WordPress:

      WordPress setup screen

      Agora acesse o Adminer visitando db-admin.seu_domínio no seu navegador, novamente substituindo seu_domínio pelo seu domínio. O container mysql não está exposto ao mundo externo, mas o container adminer tem acesso a ele através da rede internal do Docker que eles compartilham usando o nome do container mysql como um nome de host.

      Na tela de login do Adminer, use o nome de usuário root, use mysql para o server, e use o valor que você definiu para MYSQL_ROOT_PASSWORD para a senha. Uma vez logado, você verá a interface de usuário do Adminer:

      Adminer connected to the MySQL database

      Ambos os sites agora estão funcionando, e você pode usar o painel em monitor.seu_domínio para ficar de olho em suas aplicações.

      Conclusão

      Neste tutorial, você configurou o Traefik para fazer proxy das solicitações para outras aplicações em containers Docker.

      A configuração declarativa do Traefik no nível do container da aplicação facilita a configuração de mais serviços, e não há necessidade de reiniciar o container traefik quando você adiciona novas aplicações para fazer proxy, uma vez que o Traefik percebe as alterações imediatamente através do arquivo de soquete do Docker que ele está monitorando.

      Para saber mais sobre o que você pode fazer com o Traefik, consulte a documentação oficial do Traefik. Se você quiser explorar mais os containers Docker, confira Como Configurar um Registro Privado do Docker no Ubuntu 18.04 ou How To Secure a Containerized Node.js Application with Nginx, Let's Encrypt, and Docker Compose. Embora esses tutoriais sejam escritos para o Ubuntu 18.04, muitos dos comandos específicos do Docker podem ser usados para o CentOS 7.



      Source link

      How To Install and Configure Zabbix to Securely Monitor Remote Servers on Ubuntu 18.04


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

      Introduction

      Zabbix is open-source monitoring software for networks and applications. It offers real-time monitoring of thousands of metrics collected from servers, virtual machines, network devices, and web applications. These metrics can help you determine the current health of your IT infrastructure and detect problems with hardware or software components before customers complain. Useful information is stored in a database so you can analyze data over time and improve the quality of provided services, or plan upgrades of your equipment.

      Zabbix uses several options for collecting metrics, including agentless monitoring of user services and client-server architecture. To collect server metrics, it uses a small agent on the monitored client to gather data and send it to the Zabbix server. Zabbix supports encrypted communication between the server and connected clients, so your data is protected while it travels over insecure networks.

      The Zabbix server stores its data in a relational database powered by MySQL, PostgreSQL, or Oracle. You can also store historical data in nosql databases like Elasticsearch and TimescaleDB. Zabbix provides a web interface so you can view data and configure system settings.

      In this tutorial, you will configure two machines. One will be configured as the server, and the other as a client that you’ll monitor. The server will use a MySQL database to record monitoring data and use Apache to serve the web interface.

      Prerequisites

      To follow this tutorial, you will need:

      • Two Ubuntu 18.04 servers set up by following the Initial Server Setup Guide for Ubuntu 18.04, including a non-root user with sudo privileges and a firewall configured with ufw. On one server, you will install Zabbix; this tutorial will refer to this as the Zabbix server. It will monitor your second server; this second server will be referred to as the second Ubuntu server.

      • The server that will run the Zabbix server needs Apache, MySQL, and PHP installed. Follow this guide to configure those on your Zabbix server.

      Additionally, because the Zabbix Server is used to access valuable information about your infrastructure that you would not want unauthorized users to access, it’s important that you keep your server secure by installing a TLS/SSL certificate. This is optional but strongly encouraged. You can follow the Let’s Encrypt on Ubuntu 18.04 guide to obtain the free TLS/SSL certificate.

      Step 1 — Installing the Zabbix Server

      First, you need to install Zabbix on the server where you installed MySQL, Apache, and PHP. Log into this machine as your non-root user:

      • ssh sammy@zabbix_server_ip_address

      Zabbix is available in Ubuntu’s package manager, but it’s outdated, so use the official Zabbix repository to install the latest stable version. Download and install the repository configuration package:

      • wget https://repo.zabbix.com/zabbix/4.2/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.2-1+bionic_all.deb
      • sudo dpkg -i zabbix-release_4.2-1+bionic_all.deb

      You will see the following output:

      Output

      Selecting previously unselected package zabbix-release. (Reading database ... 61483 files and directories currently installed.) Preparing to unpack zabbix-release_4.2-1+bionic_all.deb ... Unpacking zabbix-release (4.2-1+bionicc) ... Setting up zabbix-release (4.2-1+bionicc) ...

      Update the package index so the new repository is included:

      Then install the Zabbix server and web frontend with MySQL database support:

      • sudo apt install zabbix-server-mysql zabbix-frontend-php

      Also, install the Zabbix agent, which will let you collect data about the Zabbix server status itself.

      • sudo apt install zabbix-agent

      Before you can use Zabbix, you have to set up a database to hold the data that the Zabbix server will collect from its agents. You can do this in the next step.

      Step 2 — Configuring the MySQL Database for Zabbix

      You need to create a new MySQL database and populate it with some basic information in order to make it suitable for Zabbix. You'll also create a specific user for this database so Zabbix isn't logging into MySQL with the root account.

      Log into MySQL as the root user using the root password that you set up during the MySQL server installation:

      Create the Zabbix database with UTF-8 character support:

      • create database zabbix character set utf8 collate utf8_bin;

      Then create a user that the Zabbix server will use, give it access to the new database, and set the password for the user:

      • grant all privileges on zabbix.* to zabbix@localhost identified by 'your_zabbix_mysql_password';

      Then apply these new permissions:

      That takes care of the user and the database. Exit out of the database console.

      Next you have to import the initial schema and data. The Zabbix installation provided you with a file that sets this up.

      Run the following command to set up the schema and import the data into the zabbix database. Use zcat since the data in the file is compressed.

      • zcat /usr/share/doc/zabbix-server-mysql/create.sql.gz | mysql -uzabbix -p zabbix

      Enter the password for the zabbix MySQL user that you configured when prompted.

      This command will not output any errors if it was successful. If you see the error ERROR 1045 (28000): Access denied for userzabbix@'localhost' (using password: YES) then make sure you used the password for the zabbix user and not the root user.

      In order for the Zabbix server to use this database, you need to set the database password in the Zabbix server configuration file. Open the configuration file in your preferred text editor. This tutorial will use nano:

      • sudo nano /etc/zabbix/zabbix_server.conf

      Look for the following section of the file:

      /etc/zabbix/zabbix_server.conf

      ### Option: DBPassword                           
      #       Database password. Ignored for SQLite.   
      #       Comment this line if no password is used.
      #                                                
      # Mandatory: no                                  
      # Default:                                       
      # DBPassword=
      

      These comments in the file explain how to connect to the database. You need to set the DBPassword value in the file to the password for your database user. Add this line below those comments to configure the database:

      /etc/zabbix/zabbix_server.conf

      ...
      DBPassword=your_zabbix_mysql_password
      

      Save and close zabbix_server.conf by pressing CTRL+X, followed by Y and then ENTER if you're using nano.

      That takes care of the Zabbix server configuration. Next, you will make some modifications to your PHP setup in order for the Zabbix web interface to work properly.

      Step 3 — Configuring PHP for Zabbix

      The Zabbix web interface is written in PHP and requires some special PHP server settings. The Zabbix installation process created an Apache configuration file that contains these settings. It is located in the directory /etc/zabbix and is loaded automatically by Apache. You need to make a small change to this file, so open it up with the following:

      • sudo nano /etc/zabbix/apache.conf

      The file contains PHP settings that meet the necessary requirements for the Zabbix web interface. However, the timezone setting is commented out by default. To make sure that Zabbix uses the correct time, you need to set the appropriate timezone.

      /etc/zabbix/apache.conf

      ...
      <IfModule mod_php7.c>
          php_value max_execution_time 300
          php_value memory_limit 128M
          php_value post_max_size 16M
          php_value upload_max_filesize 2M
          php_value max_input_time 300
          php_value always_populate_raw_post_data -1
          # php_value date.timezone Europe/Riga
      </IfModule>
      

      Uncomment the timezone line, highlighted in the preceding code block, and change it to your timezone. You can use this list of supported time zones to find the right one for you. Then save and close the file.

      Now restart Apache to apply these new settings.

      • sudo systemctl restart apache2

      You can now start the Zabbix server.

      • sudo systemctl start zabbix-server

      Then check whether the Zabbix server is running properly:

      • sudo systemctl status zabbix-server

      You will see the following status:

      Output

      ● zabbix-server.service - Zabbix Server Loaded: loaded (/lib/systemd/system/zabbix-server.service; disabled; vendor preset: enabled) Active: active (running) since Fri 2019-04-05 08:50:54 UTC; 3s ago Process: 16497 ExecStart=/usr/sbin/zabbix_server -c $CONFFILE (code=exited, status=0/SUCCESS) ...

      Finally, enable the server to start at boot time:

      • sudo systemctl enable zabbix-server

      The server is set up and connected to the database. Next, set up the web frontend.

      Note: As mentioned in the Prerequisites section, it is recommended that you enable SSL/TLS on your server. You can follow this tutorial now to obtain a free SSL certificate for Apache on Ubuntu 18.04. After obtaining your SSL/TLS certificates, you can come back and complete this tutorial.

      Step 4 — Configuring Settings for the Zabbix Web Interface

      The web interface lets you see reports and add hosts that you want to monitor, but it needs some initial setup before you can use it. Launch your browser and go to the address http://zabbix_server_name/zabbix/. On the first screen, you will see a welcome message. Click Next step to continue.

      On the next screen, you will see the table that lists all of the prerequisites to run Zabbix.

      Prerequisites

      All of the values in this table must be OK, so verify that they are. Be sure to scroll down and look at all of the prerequisites. Once you've verified that everything is ready to go, click Next step to proceed.

      The next screen asks for database connection information.

      DB Connection

      You told the Zabbix server about your database, but the Zabbix web interface also needs access to the database to manage hosts and read data. Therefore enter the MySQL credentials you configured in Step 2 and click Next step to proceed.

      On the next screen, you can leave the options at their default values.

      Zabbix Server Details

      The Name is optional; it is used in the web interface to distinguish one server from another in case you have several monitoring servers. Click Next step to proceed.

      The next screen will show the pre-installation summary so you can confirm everything is correct.

      Summary

      Click Next step to proceed to the final screen.

      The web interface setup is complete! This process creates the configuration file /usr/share/zabbix/conf/zabbix.conf.php which you could back up and use in the future. Click Finish to proceed to the login screen. The default user is Admin and the password is zabbix.

      Before you log in, set up the Zabbix agent on your second Ubuntu server.

      Step 5 — Installing and Configuring the Zabbix Agent

      Now you need to configure the agent software that will send monitoring data to the Zabbix server.

      Log in to the second Ubuntu server:

      • ssh sammy@second_ubuntu_server_ip_address

      Then, just like on the Zabbix server, run the following commands to install the repository configuration package:

      • wget https://repo.zabbix.com/zabbix/4.2/ubuntu/pool/main/z/zabbix-release/zabbix-release_4.2-1+bionic_all.deb
      • sudo dpkg -i zabbix-release_4.2-1+bionic_all.deb

      Next, update the package index:

      Then install the Zabbix agent:

      • sudo apt install zabbix-agent

      While Zabbix supports certificate-based encryption, setting up a certificate authority is beyond the scope of this tutorial, but you can use pre-shared keys (PSK) to secure the connection between the server and agent.

      First, generate a PSK:

      • sudo sh -c "openssl rand -hex 32 > /etc/zabbix/zabbix_agentd.psk"

      Show the key so you can copy it somewhere. You will need it to configure the host.

      • cat /etc/zabbix/zabbix_agentd.psk

      The key will look something like this:

      Output

      12eb854dea38ac9ee7d1ded2d74cee6262b0a56710f6946f7913d674ab82cdd4

      Now edit the Zabbix agent settings to set up its secure connection to the Zabbix server. Open the agent configuration file in your text editor:

      • sudo nano /etc/zabbix/zabbix_agentd.conf

      Each setting within this file is documented via informative comments throughout the file, but you only need to edit some of them.

      First you have to edit the IP address of the Zabbix server. Find the following section:

      /etc/zabbix/zabbix_agentd.conf

      ...
      ### Option: Server
      #       List of comma delimited IP addresses (or hostnames) of Zabbix servers.
      #       Incoming connections will be accepted only from the hosts listed here.
      #       If IPv6 support is enabled then '127.0.0.1', '::127.0.0.1', '::ffff:127.0.0.1' are treated equally.
      #
      # Mandatory: no
      # Default:
      # Server=
      
      Server=127.0.0.1
      ...
      

      Change the default value to the IP of your Zabbix server:

      /etc/zabbix/zabbix_agentd.conf

      ...
      Server=zabbix_server_ip_address
      ...
      

      Next, find the section that configures the secure connection to the Zabbix server and enable pre-shared key support. Find the TLSConnect section, which looks like this:

      /etc/zabbix/zabbix_agentd.conf

      ...
      ### Option: TLSConnect
      #       How the agent should connect to server or proxy. Used for active checks.
      #       Only one value can be specified:
      #               unencrypted - connect without encryption
      #               psk         - connect using TLS and a pre-shared key
      #               cert        - connect using TLS and a certificate
      #
      # Mandatory: yes, if TLS certificate or PSK parameters are defined (even for 'unencrypted' connection)
      # Default:
      # TLSConnect=unencrypted
      ...
      

      Then add this line to configure pre-shared key support:

      /etc/zabbix/zabbix_agentd.conf

      ...
      TLSConnect=psk
      ...
      

      Next, locate the TLSAccept section, which looks like this:

      /etc/zabbix/zabbix_agentd.conf

      ...
      ### Option: TLSAccept
      #       What incoming connections to accept.
      #       Multiple values can be specified, separated by comma:
      #               unencrypted - accept connections without encryption
      #               psk         - accept connections secured with TLS and a pre-shared key
      #               cert        - accept connections secured with TLS and a certificate
      #
      # Mandatory: yes, if TLS certificate or PSK parameters are defined (even for 'unencrypted' connection)
      # Default:
      # TLSAccept=unencrypted
      ...
      

      Configure incoming connections to support pre-shared keys by adding this line:

      /etc/zabbix/zabbix_agentd.conf

      ...
      TLSAccept=psk
      ...
      

      Next, find the TLSPSKIdentity section, which looks like this:

      /etc/zabbix/zabbix_agentd.conf

      ...
      ### Option: TLSPSKIdentity
      #       Unique, case sensitive string used to identify the pre-shared key.
      #
      # Mandatory: no
      # Default:
      # TLSPSKIdentity=
      ...
      

      Choose a unique name to identify your pre-shared key by adding this line:

      /etc/zabbix/zabbix_agentd.conf

      ...
      TLSPSKIdentity=PSK 001
      ...
      

      You'll use this as the PSK ID when you add your host through the Zabbix web interface.

      Then set the option that points to your previously created pre-shared key. Locate the TLSPSKFile option:

      /etc/zabbix/zabbix_agentd.conf

      ...
      ### Option: TLSPSKFile
      #       Full pathname of a file containing the pre-shared key.
      #
      # Mandatory: no
      # Default:
      # TLSPSKFile=
      ...
      

      Add this line to point the Zabbix agent to your PSK file you created:

      /etc/zabbix/zabbix_agentd.conf

      ...
      TLSPSKFile=/etc/zabbix/zabbix_agentd.psk
      ...
      

      Save and close the file. Now you can restart the Zabbix agent and set it to start at boot time:

      • sudo systemctl restart zabbix-agent
      • sudo systemctl enable zabbix-agent

      For good measure, check that the Zabbix agent is running properly:

      • sudo systemctl status zabbix-agent

      You will see the following status, indicating the agent is running:

      Output

      ● zabbix-agent.service - Zabbix Agent Loaded: loaded (/lib/systemd/system/zabbix-agent.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2019-04-05 09:03:04 UTC; 1s ago ...

      The agent will listen on port 10050 for connections from the server. Configure UFW to allow connections to this port:

      You can learn more about UFW in How To Set Up a Firewall with UFW on Ubuntu 18.04.

      Your agent is now ready to send data to the Zabbix server. But in order to use it, you have to link to it from the server's web console. In the next step, you will complete the configuration.

      Step 6 — Adding the New Host to the Zabbix Server

      Installing an agent on a server you want to monitor is only half of the process. Each host you want to monitor needs to be registered on the Zabbix server, which you can do through the web interface.

      Log in to the Zabbix Server web interface by navigating to the address http://zabbix_server_name/zabbix/.

      The Zabbix login screen

      When you have logged in, click on Configuration, and then Hosts in the top navigation bar. Then click the Create host button in the top right corner of the screen. This will open the host configuration page.

      Creating a host

      Adjust the Host name and IP address to reflect the host name and IP address of your second Ubuntu server, then add the host to a group. You can select an existing group, for example Linux servers, or create your own group. The host can be in multiple groups. To do this, enter the name of an existing or new group in the Groups field and select the desired value from the proposed list.

      Once you've added the group, click the Templates tab.

      Adding a template to the host

      Type Template OS Linux in the Search field and then click Add to add this template to the host.

      Next, navigate to the Encryption tab. Select PSK for both Connections to host and Connections from host. Then set PSK identity to PSK 001, which is the value of the TLSPSKIdentity setting of the Zabbix agent you configured previously. Then set PSK value to the key you generated for the Zabbix agent. It's the one stored in the file /etc/zabbix/zabbix_agentd.psk on the agent machine.

      Setting up the encryption

      Finally, click the Add button at the bottom of the form to create the host.

      You will see your new host in the list. Wait for a minute and reload the page to see green labels indicating that everything is working fine and the connection is encrypted.

      Zabbix shows your new host

      If you have additional servers you need to monitor, log in to each host, install the Zabbix agent, generate a PSK, configure the agent, and add the host to the web interface following the same steps you followed to add your first host.

      The Zabbix server is now monitoring your second Ubuntu server. Now, set up email notifications to be notified about problems.

      Step 7 — Configuring Email Notifications

      Zabbix automatically supports several types of notifications: email, Jabber, SMS, etc. You can also use alternative notification methods, such as Telegram or Slack. You can see the full list of integrations here.

      The simplest communication method is email, and this tutorial will configure notifications for this media type.

      Click on Administration, and then Media types in the top navigation bar. You will see the list of all media types. Click on Email.

      Adjust the SMTP options according to the settings provided by your email service. This tutorial uses Gmail's SMTP capabilities to set up email notifications; if you would like more information about setting this up, see How To Use Google's SMTP Server.


      Note: If you use 2-Step Verification with Gmail, you need to generate an App Password for Zabbix. You don't need to remember it, you’ll only have to enter an App password once during setup. You will find instructions on how to generate this password in the Google Help Center.

      You can also choose the message format—html or plain text. Finally, click the Update button at the bottom of the form to update the email parameters.

      Setting up email

      Now, create a new user. Click on Administration, and then Users in the top navigation bar. You will see the list of users. Then click the Create user button in the top right corner of the screen. This will open the user configuration page.

      Creating a user

      Enter the new username in the Alias field and set up a new password. Next, add the user to the administrator's group. Type Zabbix administrators in the Groups field and select it from the proposed list.

      Once you've added the group, click the Media tab and click on the Add underlined link. You will see a pop-up window.

      Adding an email

      Enter your email address in the Send to field. You can leave the rest of the options at the default values. Click the Add button at the bottom to submit.

      Now navigate to the Permissions tab. Select Zabbix Super Admin from the User type drop-down menu.

      Finally, click the Add button at the bottom of the form to create the user.

      Now you need to enable notifications. Click on the Configuration tab, and then Actions in the top navigation bar. You will see a pre-configured action, which is responsible for sending notifications to all Zabbix administrators. You can review and change the settings by clicking on its name. For the purposes of this tutorial, use the default parameters. To enable the action, click on the red Disabled link in the Status column.

      Now you are ready to receive alerts. In the next step, you will generate one to test your notification setup.

      Step 8 — Generating a Test Alert

      In this step, you will generate a test alert to ensure everything is connected. By default, Zabbix keeps track of the amount of free disk space on your server. It automatically detects all disk mounts and adds the corresponding checks. This discovery is executed every hour, so you need to wait a while for the notification to be triggered.

      Create a temporary file that's large enough to trigger Zabbix's file system usage alert. To do this, log in to your second Ubuntu server if you're not already connected.

      • ssh sammy@second_ubuntu_server_ip_address

      Next, determine how much free space you have on the server. You can use the df command to find out:

      The command df will report the disk space usage of your file system, and the -h will make the output human-readable. You'll see output like the following:

      Output

      Filesystem Size Used Avail Use% Mounted on /dev/vda1 25G 1.2G 23G 5% /

      In this case, the free space is 23GB. Your free space may differ.

      Use the fallocate command, which allows you to pre-allocate or de-allocate space to a file, to create a file that takes up more than 80% of the available disk space. This will be enough to trigger the alert:

      • fallocate -l 20G /tmp/temp.img

      After around an hour, Zabbix will trigger an alert about the amount of free disk space and will run the action you configured, sending the notification message. You can check your inbox for the message from the Zabbix server. You will see a message like:

      Output

      Problem started at 10:37:54 on 2019.04.05 Problem name: Free disk space is less than 20% on volume / Host: Second Ubuntu server Severity: Warning Original problem ID: 34

      You can also navigate to the Monitoring tab, and then Dashboard to see the notification and its details.

      Main dashboard

      Now that you know the alerts are working, delete the temporary file you created so you can reclaim your disk space:

      After a minute Zabbix will send the recovery message and the alert will disappear from main dashboard.

      Conclusion

      In this tutorial, you learned how to set up a simple and secure monitoring solution which will help you monitor the state of your servers. It can now warn you of problems, and you have the opportunity to analyze the processes occurring in your IT infrastructure.

      To learn more about setting up monitoring infrastructure, check out How To Install Elasticsearch, Logstash, and Kibana (Elastic Stack) on Ubuntu 18.04 and How To Gather Infrastructure Metrics with Metricbeat on Ubuntu 18.04.



      Source link