URL: https://www.progressiverobot.com/microservices-deployment-using-nomad-on-ubuntu/

Introduction

The world of DevOps and container orchestration is constantly changing, and picking the right tool can make a big difference in how efficiently microservices are deployed. One tool that stands out is HashiCorp Nomad. In this guide, we'll dive into how you can deploy a simple microservice using Nomad on a cloud servers running Ubuntu server.

Nomad is a lightweight workload scheduler that manages application deployment and scaling across a cluster of machines. It supports multiple workload types, integrates with other HashiCorp tools, and is infrastructure agnostic. Nomad's simplicity, versatility, scalability, resource efficiency, and integration make it an excellent choice for deploying and managing microservices.

Check out Nomad’s official documentation and its Comparison guide for more details and features.

Prerequisites

nomad illustration for: Prerequisites

Before we begin, make sure you have the following installed:

  • Doctl installed and configured on your system, which will be used to deploy a cloud servers running Ubuntu server.

Or

Step 1: Deploy a cloud servers using ‘doctl’

First and foremost, you will need to get the SSH-key-id and Access Token, which will be used to run the “doctl“ command to create a Droplet.

Let's set things up:

  1. You can get your SSH-key-id using the command:
				
					doctl compute ssh-key list
				
			
  1. You can create an access token here: Create Access Token

Now, let's create an Ubuntu Droplet:

				
					doctl compute droplet create my-droplet --size s-2vcpu-4gb --region nyc1 --image ubuntu-22-04-x64 --ssh-keys <YOUR_SSH_KEY_ID> --access-token '<ACCESS_TOKEN>' --output json"
				
			

The above command creates a new cloud servers named "my-droplet" with a specified size, region, image,SSH key ID and access token.

You can use the following command to get the supported sizes for Droplets and their hourly and monthly prices:

				
					doctl compute size list
				
			

You can refer to the “doctl` commands reference to learn more and play around with `doctl“.

Now, to SSH into your Ubuntu Droplet, we will first need the Public IP of the Droplet, which can be found using the following “doctl“ command:

				
					doctl compute droplet get my-droplet
				
			

Note the Public IPv4 address from the above command and then SSH to the Droplet from your local server.

Once you know the Public IPv4 address of your Ubuntu Droplet, you will need to add SSH keys to connect to your Droplet remotely from your local system via SSH.

Now that you have added your SSH keys, you can log in to the Ubuntu Droplet remotely via SSH:

				
					ssh root@<PUBLIC_IP>
				
			

Step 2: Installing Nomad and Docker Engine on your Droplet

You can refer to the following guides to install Nomad and Docker engine on an Ubuntu server:

  • Nomad installed on your the cloud provider Ubuntu Droplet.

Step 3: Setting up Nomad

Once you have the Ubuntu Droplet deployed with Nomad and Docker installed, you can start Nomad in "dev mode" using the following command in the terminal:

				
					nomad agent -dev
				
			

You will observe an output like the one below:

				
					[secondary_label Output]
==> No configuration files loaded
==> Starting Nomad agent...
==> Nomad agent configuration:
 Advertise Addrs: HTTP: 127.0.0.1:4646; RPC: 127.0.0.1:4647; Serf: 127.0.0.1:4648
 Bind Addrs: HTTP: [127.0.0.1:4646]; RPC: 127.0.0.1:4647; Serf: 127.0.0.1:4648
 Client: true
 Log Level: DEBUG
 Node Id: 51b0f49b-564b-5d1c-8e99-a17336c2a597
 Region: Global (DC: dc1)
 Server: true
 Version: 1.6.2
==> Nomad agent started! Log data will stream in below:
 2023-12-05T18:43:58.414+0530 [INFO] nomad.raft: initial configuration: index=1 servers="[{Suffrage:Voter ID:e707dbd2-be06-7cfc-dff0-a11714eec76f Address:127.0.0.1:4647}]"
 2023-12-05T18:43:58.415+0530 [INFO] nomad.raft: entering follower state: follower="Node at 127.0.0.1:4647 [Follower]" leader-address= leader-id=
				
			

This will start the Nomad agent on your machine as the server and client components. In dev mode, Nomad runs on a single node and does not persist in any data, making it ideal for experimentation and development.

Once the Nomad agent starts running, you can access the Nomad web user interface by visiting http://localhost:4646 in your browser. In our case, we need some tunneling to access the Nomad UI from our Ubuntu Server. We can use Pinggy, a tool that provides Public URLs for Localhost. It generates a temporary URL through which one can access the Nomad UI.

				
					ssh -p 443 -R0:localhost:4646 -L4300:localhost:4300 qr@a.pinggy.io
				
			

Copy and run the above SSH command on your Ubuntu server to get Nomad UI URLs that can be accessed from your phone browser or any other device.

Copy the URLs displayed in the above screenshot.

Here is the Nomad UI. You can view the Job details, details of the deployment, etc, from this UI throughout the tutorial.

Step 4: Nomad Job Specification

Nomad jobs are defined by the Nomad job specification, also known as *"jobspec"*. The job spec schema is written in HCL.

The job specification is divided into smaller sections, which you can find in the navigation menu. Nomad HCL is parsed in the command line and then sent to Nomad in JSON format via the HTTP API.

Let’s talk about each in a bit more detail:

  • task – Nomad uses tasks as the smallest unit of work. These tasks are executed by task drivers such as docker or exec, which allow Nomad to be flexible in the tasks it supports. Tasks specify their required task driver, configuration for the driver, constraints, and resources required.
  • group -A group refers to a set of tasks that are executed on a single Nomad client."
  • Job – A job is the core unit of control for Nomad and defines the application and its configurations. It can contain one or many tasks.
  • job specification – A job specification, also known as a jobspec defines the schema for Nomad jobs. This describes the type of job, the tasks and resources necessary for the job to run, job information like which clients it can run on, and more.
  • allocation – An allocation is a mapping between a task group in a job and a client node. When a job is run, Nomad will choose a client capable of running it and allocate resources on the machine for the task(s) in the task group defined in the job.

The above constructs make up a Job in Nomad.

Now that Nomad is operational, we can schedule our first job. Our initial task will involve running the “http-echo“ Docker container. This straightforward application generates an HTML page displaying the arguments passed to the http-echo process, such as "Hello World". The process dynamically listens on a port specified by another argument.

Let’s create a job file that ends with the name “microservice.nomad”—all the job files in Nomad end with “.nomad“ suffix .

				
					[label microservice.nomad]
job "http-microservice" {
 datacenters = ["dc1"]
 group "echo" {
 count = 1
 task "server" {
 driver = "docker"
 config {
 image = "hashicorp/http-echo:latest"
 args = [
 "-listen", ":${NOMAD_PORT_http}",
 "-text", "Hello and welcome to ${NOMAD_IP_http} running on port ${NOMAD_PORT_http}. This is my first microservice deployment using Nomad.",
 ]
 }
 resources {
 network {
 mbits = 10
 port "http" {}
 }
 }
 }
 }
}
				
			

In this example, we defined a job called “http-microservice`, set the driver to use `docker“, and passed the necessary text and port arguments to the container. As we need network access to the container to display the resulting webpage, we define the resources section as requiring a network with a port that Nomad dynamically chooses from the host machine to the container during runtime.

Nomad supports using dynamic port assignment, i.e., you don’t need to specify a port.

Step 5: Running the Nomad Job

We can submit/run new or update existing jobs with the “nomad job run“ command, using job files that conform to the job specification format.

				
					nomad job run microservice.nomad
				
			

Output:

				
					[secondary_label Output]
==> 2023-12-06T13:58:25+05:30: Monitoring evaluation "bd6dd191"
 2023-12-06T13:58:25+05:30: Evaluation triggered by job "http-microservice"
 2023-12-06T13:58:25+05:30: Evaluation within deployment: "b8499a02"
 2023-12-06T13:58:25+05:30: Evaluation status changed: "pending" -> "complete"
==> 2023-12-06T13:58:25+05:30: Evaluation "bd6dd191" finished with status "complete"
==> 2023-12-06T13:58:25+05:30: Monitoring deployment "b8499a02"
 ✓ Deployment "b8499a02" successful
 2023-12-06T13:58:25+05:30
 ID = b8499a02
 Job ID = http-microservice
 Job Version = 0
 Status = successful
 Description = Deployment completed successfully
 Deployed
 Task Group Desired Placed Healthy Unhealthy Progress
 Deadline
 echo 1 1 1 0
 2023-12-06T14:08:23+05:30
				
			

We can check the status of a job using the command:

				
					nomad job status http-microservice
				
			
				
					[secondary_label Output]
ID = http-microservice
Name = http-microservice
Submit Date = 2023-12-13T13:44:55+05:30
Type = service
Priority = 50
Datacenters = dc1
Namespace = default
Node Pool = default
Status = running
Periodic = false
Parameterized = false
Summary
Task Group Queued Starting Running Failed Complete Lost Unknown
echo 0 0 1 0 0 0 0
Latest Deployment
ID = 9a72c817
Status = successful
Description = Deployment completed successfully
Deployed
Task Group Desired Placed Healthy Unhealthy Progress Deadline
echo 1 1 1 0 2023-12-13T14:00:33+05:30
Allocations
ID Node ID Task Group Version Desired Status Created Modified
84d70c8f aa3f6a1a echo 0 run running 5m19s ago 5m6s ago
				
			

Please make a note of the allocation ID in the above output:84d70c8f. Allocation ID is used to troubleshoot deployment issues in Nomad.

You can also access the Nomad UI using the below command to check the Jobs and other important details of your deployment on your local desktop:

				
					nomad ui <job_name>
				
			

In our case:

				
					nomad ui http-microservice
				
			

We can use the command “nomad alloc-status <allocation_id> “ to get the port on which the microservice is deployed. When deploying jobs with dynamic ports, finding the exact port on which a specific task has been deployed can be challenging because the port is dynamically allocated at runtime. This command is also great for troubleshooting any deployment issues in Nomad.

We can get the allocation_id using the command nomad job status http-microservice specified in the above section.

				
					nomad alloc-status 84d70c8f
				
			

Output:

				
					[secondary_label Output]
ID = 84d70c8f-0687-e1c7-27fe-172ae274580b
Eval ID = 404f0d71
Name = http-microservice.echo[0]
Node ID = aa3f6a1a
Node Name = XXXXXXXX
Job ID = http-microservice
Job Version = 0
Client Status = running
Client Description = Tasks are running
Desired Status = run
Desired Description = &lt;none&gt;
Created = 11m3s ago
Modified = 10m50s ago
Deployment ID = 9a72c817
Deployment Health = healthy
Task "server" is "running"
Task Resources:
CPU Memory Disk Addresses
0/100 MHz 9.0 MiB/300 MiB 300 MiB http: 127.0.0.1:20004
Task Events:
Started At = 2023-12-13T08:20:23Z
Finished At = N/A
Total Restarts = 0
Last Restart = N/A
Recent Events:
Time Type Description
2023-12-13T13:50:23+05:30 Started Task started by client
2023-12-13T13:50:20+05:30 Driver Downloading image
2023-12-13T13:50:19+05:30 Task Setup Building Task Directory
2023-12-13T13:50:19+05:30 Received Task received by client
				
			

From the above, we can make a note of the address on which the microservice is running, which is “127.0.0.1:20004“, and send a curl request to get the status of the microservice.

				
					curl -i 127.0.0.1:20004
				
			
				
					[secondary_label Output]
curl: (56) Recv failure: Connection reset by peer
anish@W2MK93VGYX nomad.d % curl -i http://127.0.0.1:20004
HTTP/1.1 200 OK
X-App-Name: http-echo
X-App-Version: 1.0.0
Date: Wed, 13 Dec 2023 08:24:59 GMT
Content-Length: 108
Content-Type: text/plain; charset=utf-8
Hello and welcome to 127.0.0.1 running on port 20004. This is my first microservice deployment using Nomad.
				
			

We can verify that the microservice has been successfully deployed from the above.

Scaling up our App

One of the key benefits of using Nomad is its ability to scale your microservices based on demand. To scale up your job, simply modify the job file to increase the “count` parameter or use the `nomad job scale <job> <count>“ command.

Let’s scale our hello-world microservice:

				
					nomad job scale http-microservice 3
				
			

Output:

				
					[secondary_label Output]
 2023-12-05T19:28:45+05:30: Evaluation status changed: "pending" -&gt; "complete
==&gt; 2023-12-05T19:28:45+05:30: Evaluation "84241aca" finished with status "complete"
==&gt; 2023-12-05T19:28:45+05:30: Monitoring deployment "a26236c7"
 ✓ Deployment "a26236c7" successful
 2023-12-05T19:28:58+05:30
 ID = a26236c7
 Job ID = http-microservice
 Job Version = 1
 Status = successful
 Description = Deployment completed successfully
 Deployed
 Task Group Desired Placed Healthy Unhealthy Progress
 Deadline
 echo 3 3 3 0
 2023-12-05T19:38:57+05:30**
				
			

On the Nomad UI(“http://localhost:4646/ui/jobs“), we can see the changes being reflected on the go:

Some additional features and concepts you can explore in Nomad

Nomad offers a range of advanced features and concepts that can further enhance your deployment strategy:

1. Task Drivers:

Nomad supports multiple task drivers, allowing you to choose the best runtime environment for your applications. Explore different task drivers for various workload types.

2. Task Groups:

Organize your tasks into groups for logical separation and resource allocation. Task groups enable you to define specific constraints and settings for different sets of tasks.

3. Update Policies:

Nomad provides flexible update policies for controlling how your jobs are updated. Explore strategies like rolling updates to ensure zero downtime during deployments.

4. Service Discovery:

Utilize Nomad's integration with Consul for seamless service discovery. This is crucial for dynamic microservices architectures where services need to locate and communicate with each other.

Conclusion

Congratulations! You've successfully deployed a simple microservice and explored some advanced features of Nomad. Nomad's simplicity, versatility, and integration capabilities make it a compelling choice for DevOps engineers managing microservices at scale. Consider exploring more features and integrating Nomad into your broader infrastructure to maximize efficiency and scalability.

You can check out Nomad’s tutorial library and its comprehensive and detailed introduction guide to gain deeper insights into using Nomad.