<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Hamza Noweder's Blog]]></title><description><![CDATA[A Cloud Enthusiast]]></description><link>https://blog.noweder.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Apr 2026 11:18:54 GMT</lastBuildDate><atom:link href="https://blog.noweder.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Provisioning an Amazon ECS Cluster Using Terraform]]></title><description><![CDATA[Description
Terraform is an infrastructure-as-code tool that makes handling infrastructure more straightforward and manageable. A Terraform module is a collection of configuration files that encapsulate groups of resources dedicated to one task. Modu...]]></description><link>https://blog.noweder.com/provisioning-an-amazon-ecs-cluster-using-terraform</link><guid isPermaLink="true">https://blog.noweder.com/provisioning-an-amazon-ecs-cluster-using-terraform</guid><dc:creator><![CDATA[Hamza Noweder]]></dc:creator><pubDate>Thu, 20 Mar 2025 22:54:39 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-description"><strong>Description</strong></h1>
<p>Terraform is an infrastructure-as-code tool that makes handling infrastructure more straightforward and manageable. A Terraform module is a collection of configuration files that encapsulate groups of resources dedicated to one task. Modules can be used to package and reuse resource configurations, making it easier to manage larger application components like an Amazon ECS Cluster.</p>
<p>In this demo, you will provision an Amazon ECS Cluster into an existing Amazon VPC.</p>
<h2 id="heading-learning-objectives">Learning objectives</h2>
<ul>
<li><p>Define a Terraform module that deploys Amazon ECS resources</p>
</li>
<li><p>Apply an Auto Scaling Group Policy to respond to ECS metrics</p>
</li>
<li><p>Deploy an Amazon ECS Cluster into an existing Amazon VPC using Terraform</p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>IDE with configured IAM credentials</p>
</li>
<li><p>Installed Terraform with IDE</p>
</li>
<li><p>cloning repo: <a target="_blank" href="https://github.com/noweder/ECS-Terraform.git">https://github.com/noweder/ECS-Terraform.git</a></p>
</li>
<li><p>Below resources pre-created in your AWS account to be referenced in later steps:</p>
<ul>
<li><p>1 Virtual Private Cloud</p>
</li>
<li><p>2 Public Subnets</p>
</li>
<li><p>2 Private Subnets</p>
</li>
<li><p>Public-facing Application Load Balancer</p>
</li>
<li><p>Internal-facing Application Load Balancer</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-target-architecture">Target Architecture</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742508422259/3f633785-b072-4433-9311-5df5b4ed8e61.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-demo-steps">Demo Steps</h2>
<ul>
<li><p>Initiate Terraform by running the following command from the cloned directory path: <code>terrafrom init</code></p>
</li>
<li><p>Reviewing the Terraform Variables file</p>
<p>  The <a target="_blank" href="http://variables.tf"><strong>variables.tf</strong></a> file defines the name, description, and expected data type for each variable referenced in the <a target="_blank" href="http://main.tf"><strong>main.tf</strong></a> file. Feel free to review the data types of each variable.</p>
<p>  You will configure the <strong>terraform.tfvars</strong> file in the next step. This file will include the actual values for each variable. These values will be retrieved from the existing infrastructure.</p>
<p>  The <a target="_blank" href="http://outputs.tf"><strong>outputs.tf</strong></a> file defines the expected output values for the deployment. In this demo, the CloudWatch Log Group names and the ECS Cluster ARN will be output after a successful deployment.</p>
</li>
<li><p>Defining an Amazon ECS Cluster Using Terraform</p>
<p>  In this step, you will configure an Amazon ECS cluster, service, and task definition in the main Terraform configuration file. This lab step will also explore the <code>terraform.tfvars</code> file that provides configuration values to the ECS Cluster.</p>
<p>  The ECS Cluster will contain two ECS Services.</p>
<p>  A frontend service will deploy tasks (containers) behind the public-facing Application Load Balancer (ALB) and in each of the public subnets. These tasks are based on an existing ECR image that serves a simple webpage and data retrieved from the backend.</p>
<p>  The backend service tasks will be behind the internal-facing application load balancer, inside each private subnet. The ECR image used for the backend service generates sample data that is passed to the frontend service.</p>
<p>  Once the ECS Cluster is deployed, you will be able to access the entire application by navigating to the public ALB's URL.</p>
<p>  Double-click the terraform.tfvars file to open it in the editor, then paste in the following configuration values:</p>
<ul>
<li><pre><code class="lang-plaintext">  app_name = "lab"
  ecs_role_arn = "arn:aws:iam::574227279091:role/lab-ecs-task-execution-role"
  ecs_services = {
    frontend = {
      image          = "574227279091.dkr.ecr.us-west-2.amazonaws.com/frontend:1.0.0"
      cpu            = 256
      memory         = 512
      container_port = 8080
      host_port      = 8080
      desired_count  = 2
      is_public      = true
      protocol       = "HTTP"
      auto_scaling = {
        max_capacity    = 3
        min_capacity    = 2
        cpu_threshold    = 50
        memory_threshold = 50
      }
    }
    backend = {
      image          = "574227279091.dkr.ecr.us-west-2.amazonaws.com/backend:1.0.0"
      cpu            = 256
      memory         = 512
      container_port = 8080
      host_port      = 8080
      desired_count  = 2
      is_public      = false
      protocol       = "HTTP"
      auto_scaling = {
        max_capacity    = 3
        min_capacity    = 2
        cpu_threshold    = 75
        memory_threshold = 75
      }
    }
  }
  internal_alb_dns = "internal-lab-internal-400236659.us-west-2.elb.amazonaws.com"
  private_subnet_ids = [
    "subnet-0198a9e41d0bca224",
    "subnet-017d8d0f81ff2975e"
  ]
  public_subnet_ids = [
    "subnet-05f01cddf0795094f",
    "subnet-097cabee00a37f8b5"
  ]
  security_group_ids = [
    "sg-020dd20b13ae12807",
    "sg-0269de162c343c363"
  ]
  target_group_arns = {
    backend = {
      arn = "arn:aws:elasticloadbalancing:us-west-2:574227279091:targetgroup/backend-tg/8798f2ad7abee6f8"
    }
    frontend = {
      arn = "arn:aws:elasticloadbalancing:us-west-2:574227279091:targetgroup/frontend-tg/5d5b9e91c3b1ba49"
    }
  }
</code></pre>
</li>
</ul>
</li>
</ul>
<p>    The most important value defined in this file is the <code>ecs_services</code> map. This map of objects includes the <code>frontend</code> and <code>backend</code> service configurations. The use of maps, objects, and lists allows you to template an application effectively. Terraform provides meta-arguments such as <code>for_each</code>, that can be used to traverse a map or list to reduce the number of resources defined in your <a target="_blank" href="http://main.tf"><code>main.tf</code></a> file.</p>
<ul>
<li><p>Open the main.tf file and paste below code at the end:</p>
<pre><code class="lang-plaintext">  resource "aws_ecs_cluster" "ecs_cluster" {
    name = lower("${var.app_name}-cluster")
  }

  # ECS Services
  resource "aws_ecs_service" "service" {
    for_each = var.ecs_services
    name            = "${each.key}-service"
    cluster         = aws_ecs_cluster.ecs_cluster.id
    task_definition = aws_ecs_task_definition.ecs_task_definition[each.key].arn
    launch_type     = "FARGATE"
    desired_count   = each.value.desired_count
    network_configuration {
      subnets          = each.value.is_public == true ? var.public_subnet_ids : var.private_subnet_ids
      assign_public_ip = each.value.is_public
      security_groups  = var.security_group_ids
    }
    load_balancer {
      target_group_arn = var.target_group_arns[each.key].arn
      container_name   = each.key
      container_port   = each.value.container_port
    }
  }

  # ECS Task Definitions
  resource "aws_ecs_task_definition" "ecs_task_definition" {
    for_each = var.ecs_services
    family                   = "${lower(var.app_name)}-${each.key}"
    execution_role_arn       = var.ecs_role_arn
    requires_compatibilities = ["FARGATE"]
    network_mode             = "awsvpc"
    memory                   = each.value.memory
    cpu                      = each.value.cpu
    container_definitions = jsonencode([
      {
        name      = each.key
        image     = each.value.image
        cpu       = each.value.cpu
        memory    = each.value.memory
        essential = true
        environment = [
          { name = "INTERNAL_ALB", value = var.internal_alb_dns },
          { name = "SERVICE_HOST", value = var.internal_alb_dns },
          { name = "SERVER_SERVLET_CONTEXT_PATH", value = each.value.is_public == true ? "/" : "/${each.key}" },
          { name = "SERVICES", value = "backend" },
          { name = "SERVICE", value = each.key },
          { name = "SERVICE_NAME", value = each.key }
        ]
        portMappings = [
          {
            containerPort = each.value.container_port
          }
        ]
        logConfiguration = {
          logDriver = "awslogs"
          options = {
            awslogs-group         = "${lower(each.key)}-logs"
            awslogs-region        = data.aws_region.current.name
            awslogs-stream-prefix = var.app_name
          }
        }
      }
    ])
  }
</code></pre>
<p>  The first resource is an ECS Cluster. The cluster is named <code>lab-cluster</code> after retrieving the <code>app_name</code> variable from the <strong>terraform.tfvars</strong> file.</p>
<p>  The ECS Service definition uses the <code>for_each</code> meta-argument to create one service for each object defined in the <code>ecs_services</code> variable. The <code>each.key</code> will resolve to <code>frontend</code> and <code>backend</code>. Instances of <code>each.value</code> are followed by the corresponding attribute name of the object. The <code>desired_count</code> of tasks for each service uses <code>each.value.desired_count</code> and resolves to <code>2</code>. Surrounding these dynamic calls with <code>${ }</code> allows you to resolve and concatenate the value into a string, i.e. <code>name = frontend-service</code>.</p>
<p>  The <code>network_configuration</code> references the <code>is_public</code> attribute of each ECS service to determine which subnet to deploy the services into. For each service, if this attribute is set to <code>true</code>, the service is deployed into the public subnets, and each task is assigned a public IP address. If set to <code>false</code>, no public IP is assigned, and the service is deployed into the private subnets.</p>
<p>  The <code>target_group_arns</code> map determines which ALB each service is associated with. The <code>backend</code> target group is associated with the internal ALB, which is not publicly accessible. The <code>frontend</code> target group is associated with the public ALB since it will serve the application's webpage.</p>
<p>  The <code>ecs_task_definition</code> resource defines the tasks, or containers, within each ECS service. All ECS tasks are set to the Fargate launch type and assume the same task execution IAM Role. This role provides the task agent with permission to perform AWS API calls on your behalf.</p>
<p>  <code>container_definitions</code> include which ECR image to use, CPU and memory configurations, as well as environment variables to be passed into the container. The environment variables in this task definition will be referenced by both frontend and backend services and are specific to the ECR image used in this lab.</p>
<p>  The task <code>portMappings</code> allow the containers to access the container port to send or receive traffic. Data will travel between frontend and backend tasks using this port.</p>
<p>  Finally, the <code>logConfiguration</code> defines the <code>awslogs</code> log driver which is used to send logs from the containers to Amazon CloudWatch Logs. The <code>options</code> define which CloudWatch Logs Group and AWS Region to send logs to.</p>
</li>
<li><p>Applying CloudWatch Monitoring and Auto Scaling to an ECS Cluster With Terraform</p>
<ul>
<li><p>In this lab step, you will define CloudWatch Logs that store container logs for each of the ECS Services. You will also configure your ECS Services with Auto Scaling Group policies that scale the service task count in or out depending on certain ECS metrics.</p>
</li>
<li><p>Paste the following code to the end of the <a target="_blank" href="http://main.tf"><strong>main.tf</strong></a> file:</p>
<pre><code class="lang-plaintext">  # CloudWatch Log Groups
  resource "aws_cloudwatch_log_group" "ecs_cw_log_group" {
    for_each = toset(keys(var.ecs_services))
    name     = lower("${each.key}-logs")
  }

  # ECS Auto Scaling Configuration
  resource "aws_appautoscaling_target" "service_autoscaling" {
    for_each = var.ecs_services
    max_capacity       = each.value.auto_scaling.max_capacity
    min_capacity       = each.value.auto_scaling.min_capacity
    resource_id        = "service/${aws_ecs_cluster.ecs_cluster.name}/${aws_ecs_service.service[each.key].name}"
    scalable_dimension = "ecs:service:DesiredCount"
    service_namespace  = "ecs"
  }

  # Auto Scaling Policies
  resource "aws_appautoscaling_policy" "ecs_policy_memory" {
    for_each = var.ecs_services
    name               = "${var.app_name}-memory-autoscaling"
    policy_type        = "TargetTrackingScaling"
    resource_id        = aws_appautoscaling_target.service_autoscaling[each.key].resource_id
    scalable_dimension = aws_appautoscaling_target.service_autoscaling[each.key].scalable_dimension
    service_namespace  = aws_appautoscaling_target.service_autoscaling[each.key].service_namespace
    target_tracking_scaling_policy_configuration {
      predefined_metric_specification {
        predefined_metric_type = "ECSServiceAverageMemoryUtilization"
      }
      target_value = each.value.auto_scaling.memory_threshold
    }
  }
  resource "aws_appautoscaling_policy" "ecs_policy_cpu" {
    for_each = var.ecs_services
    name               = "${var.app_name}-cpu-autoscaling"
    policy_type        = "TargetTrackingScaling"
    resource_id        = aws_appautoscaling_target.service_autoscaling[each.key].resource_id
    scalable_dimension = aws_appautoscaling_target.service_autoscaling[each.key].scalable_dimension
    service_namespace  = aws_appautoscaling_target.service_autoscaling[each.key].service_namespace
    target_tracking_scaling_policy_configuration {
      predefined_metric_specification {
        predefined_metric_type = "ECSServiceAverageCPUUtilization"
      }
      target_value = each.value.auto_scaling.cpu_threshold
    }
  }
</code></pre>
</li>
</ul>
</li>
<li><ul>
<li><p>In this lab step, you will deploy and test your ECS application using the Terraform CLI.</p>
<ol>
<li><p>In the browser IDE terminal, enter the following command to validate and summarize your deployment:</p>
<p> <a target="_blank" href="https://platform.qa.com/lab/provisioning-an-amazon-ecs-cluster-using-terraform/session-page/#"><strong>Copy code</strong></a></p>
<pre><code class="lang-plaintext"> terraform plan -no-color &gt; plan.txt
</code></pre>
<p> The command will send the output to the <strong>plan.txt</strong> file rather than displaying it in the terminal. There will be a total of 13 resources outlined in this plan and sorted in alphabetical order.</p>
<p> This command also runs <code>terraform validate</code> before outputting the plan. Any misaligned variable types or misconfigured resources will be output if found.</p>
<p> Feel free to explore this file and the resolved resource values.</p>
</li>
<li><p>Enter the following command to deploy your ECS Cluster:</p>
<p> <a target="_blank" href="https://platform.qa.com/lab/provisioning-an-amazon-ecs-cluster-using-terraform/session-page/#"><strong>Copy code</strong></a></p>
<pre><code class="lang-plaintext"> terraform apply --auto-approve
</code></pre>
<p> <em>Note</em>: The <code>terraform apply</code> process will take a few seconds to apply the changes to AWS, but the ECS Cluster may take up to 3 minutes to become accessible.</p>
<p> The following message will display in the terminal, along with the predefined output variables:</p>
<ol>
<li><p>Open the ALB URL in a new browser tab to confirm the application is running</p>
<p> <img src="https://assets.platform.qa.com/labs/uploads/provisioning-ecs-cluster-terraform/steps/4_deploy/assets/final-webpage.png" alt /></p>
<p> The application is a simple <strong>API Results</strong> webpage. Each time you refresh the page, the <strong>frontend</strong> ECS tasks retrieve a new set of data from the <strong>backend</strong> tasks, then display them in a table organized by <strong>Record ID</strong></p>
</li>
</ol>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<h3 id="heading-summary"><strong>Summary</strong></h3>
<p>            By completing this lab, you have accomplished the following tasks:</p>
<ul>
<li><p>Defined a Terraform module that deploys Amazon ECS resources</p>
</li>
<li><p>Applied an Auto Scaling Group Policy to respond to ECS metrics</p>
</li>
<li><p>Deployed an Amazon ECS Cluster into an existing Amazon VPC using Terraform</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Create an EKS Cluster with Nginx Application on AWS]]></title><description><![CDATA[Introduction
Amazon Elastic Kubernetes Service (Amazon EKS) is a managed Kubernetes service that makes it easy for you to run Kubernetes on AWS and on-premises. Kubernetes is an open-source system for automating deployment, scaling, and management of...]]></description><link>https://blog.noweder.com/create-an-eks-cluster-with-nginx-application-on-aws</link><guid isPermaLink="true">https://blog.noweder.com/create-an-eks-cluster-with-nginx-application-on-aws</guid><dc:creator><![CDATA[Hamza Noweder]]></dc:creator><pubDate>Mon, 03 Feb 2025 17:57:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738605436547/ea9175d1-ba07-4585-9bfc-0b10a602a2b9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Amazon Elastic Kubernetes Service (Amazon EKS) is a managed Kubernetes service that makes it easy for you to run Kubernetes on AWS and on-premises. Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications. Amazon EKS is certified Kubernetes-conformant, so existing applications that run on upstream Kubernetes are compatible with Amazon EKS.</p>
<p>Amazon EKS automatically manages the availability and scalability of the Kubernetes control plane nodes responsible for scheduling containers, managing application availability, storing cluster data, and other key tasks.</p>
<p>In this blog, I will step you through the process of deploying an nginx web server on EKS cluster.</p>
<h2 id="heading-pre-requisites">Pre-requisites</h2>
<ul>
<li>AWS Account</li>
</ul>
<h2 id="heading-step-1-creating-an-iam-user-with-admin-permissions">Step 1: Creating an IAM User with Admin Permissions</h2>
<ul>
<li><p>Go to <code>IAM &gt; Users</code> and click on "Add users"</p>
</li>
<li><p>Specify the username for example <code>k8s-admin</code>, enable Programmatic access, and hit Next.</p>
</li>
<li><p>Click on <code>Attach existing policies directly</code> and select the <code>AdministratorAccess</code> policy.</p>
</li>
<li><p>Proceed till the end and click on <code>Create user</code></p>
</li>
<li><p>Copy both Access key ID and Secret access key and save them for later use.</p>
</li>
</ul>
<h2 id="heading-step-2-launching-an-ec2-instance-and-configuring-the-required-cli-tools">Step 2: Launching an EC2 Instance and Configuring the required CLI Tools</h2>
<p>In this step, we will start by creating an EC2 instance, updating the AWS CLI version, and configuring the AWS CLI using the credentials of the user created in the previous step. We will then install both <code>eksctl</code> and <code>kubectl</code> on that EC2 instance. This instance will act as an admin workstation to run all administrative commands from.</p>
<ul>
<li><p>Go to <code>EC2</code> service and click on <code>Launch instance</code>.</p>
</li>
<li><p>Follow below instructions:</p>
<ul>
<li><p>Set Name: <code>k8s-admin</code></p>
</li>
<li><p>Select AMI: <code>Amazon Linux 2 AMI</code></p>
</li>
<li><p>Keep the default Instance Type <code>t2.micro</code></p>
</li>
<li><p>Create new key pair with you preferred name and download it (Note: Select <code>.ppk</code> format if you are using Putty)</p>
</li>
<li><p>Edit <code>Network Settings</code> and enable <code>Auto-assign public IP</code></p>
</li>
<li><p>Keep other settings as is and click <code>Launch instance</code></p>
</li>
</ul>
</li>
<li><p>Wait till the Status check turns into <code>2/2 checks passed</code> and then click on <code>Connect</code>. Choose <code>EC2 Instance Connect</code> and press on <code>Connect</code> button to access the EC2 instance terminal.</p>
</li>
<li><p>Follow the instructions from the AWS documentation <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html">here</a> or follow the below steps to update the AWS CLI version:</p>
<ul>
<li>Download the latest AWS CLI version</li>
</ul>
</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">curl</span> <span class="hljs-string">"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"</span> <span class="hljs-string">-o</span> <span class="hljs-string">"awscliv2.zip"</span>
</code></pre>
<ul>
<li>Unzip the installer</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">unzip</span> <span class="hljs-string">awscliv2.zip</span>
</code></pre>
<ul>
<li>Run following to update the current version:</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">sudo</span> <span class="hljs-string">./aws/install</span> <span class="hljs-string">--bin-dir</span> <span class="hljs-string">/usr/bin</span> <span class="hljs-string">--install-dir</span> <span class="hljs-string">/usr/local/aws-cli</span> <span class="hljs-string">--update</span>
</code></pre>
<ul>
<li>Verify version is updated</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">aws</span> <span class="hljs-string">--version</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665239652272/XRT5oQv8r.png" alt="image.png" /></p>
<ul>
<li><p>Run <code>aws configure</code> and set below values:</p>
<ul>
<li><p>AWS Acsess Key ID and AWS Secret Access Key from previous step</p>
</li>
<li><p>Default Region name: <code>us-east-1</code></p>
</li>
<li><p>Default output format: <code>json</code></p>
</li>
</ul>
</li>
<li><p>Install <code>kubectl</code> with following commands:</p>
</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">curl</span> <span class="hljs-string">-o</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">https://amazon-eks.s3.us-west-2.amazonaws.com/1.16.8/2020-04-16/bin/linux/amd64/kubectl</span>
<span class="hljs-string">chmod</span> <span class="hljs-string">+x</span> <span class="hljs-string">./kubectl</span>
<span class="hljs-string">mkdir</span> <span class="hljs-string">-p</span> <span class="hljs-string">$HOME/bin</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">cp</span> <span class="hljs-string">./kubectl</span> <span class="hljs-string">$HOME/bin/kubectl</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">export</span> <span class="hljs-string">PATH=$PATH:$HOME/bin</span>
</code></pre>
<ul>
<li>Ensure <code>kubectl</code> is installed</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">version</span> <span class="hljs-string">--short</span> <span class="hljs-string">--client</span>
</code></pre>
<ul>
<li>Install and configure <code>eksctl</code></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">curl</span> <span class="hljs-string">--silent</span> <span class="hljs-string">--location</span> <span class="hljs-string">"https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz"</span> <span class="hljs-string">|</span> <span class="hljs-string">tar</span> <span class="hljs-string">xz</span> <span class="hljs-string">-C</span> <span class="hljs-string">/tmp</span>
<span class="hljs-string">sudo</span> <span class="hljs-string">mv</span> <span class="hljs-string">/tmp/eksctl</span> <span class="hljs-string">/usr/bin</span>
</code></pre>
<ul>
<li>Ensure <code>eksctl</code> is installed</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">eksctl</span> <span class="hljs-string">version</span>
</code></pre>
<h2 id="heading-step-3-provisioning-an-eks-cluster">Step 3: Provisioning an EKS Cluster</h2>
<ul>
<li>Provision an EKS cluster with three worker nodes in <code>us-east-1</code></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">eksctl</span> <span class="hljs-string">create</span> <span class="hljs-string">cluster</span> <span class="hljs-string">--name</span> <span class="hljs-string">dev</span> <span class="hljs-string">--region</span> <span class="hljs-string">us-east-1</span> <span class="hljs-string">--nodegroup-name</span> <span class="hljs-string">standard-workers</span> <span class="hljs-string">--node-type</span> <span class="hljs-string">t3.medium</span> <span class="hljs-string">--nodes</span> <span class="hljs-number">3</span> <span class="hljs-string">--nodes-min</span> <span class="hljs-number">1</span> <span class="hljs-string">--nodes-max</span> <span class="hljs-number">4</span> <span class="hljs-string">--managed</span>
</code></pre>
<p>It will take 10–15 minutes since it's provisioning the control plane and worker nodes, attaching the worker nodes to the control plane, and creating the VPC, security group, and Auto Scaling group.</p>
<ul>
<li>After the process is fully completed, check the cluster status by running the following command</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">eksctl</span> <span class="hljs-string">get</span> <span class="hljs-string">cluster</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665247108704/nnvKOVb6S.png" alt="image.png" /></p>
<ul>
<li>Enable connecting to the cluster</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">aws</span> <span class="hljs-string">eks</span> <span class="hljs-string">update-kubeconfig</span> <span class="hljs-string">--name</span> <span class="hljs-string">dev</span> <span class="hljs-string">--region</span> <span class="hljs-string">us-east-1</span>
</code></pre>
<h2 id="heading-step-4-creating-an-nginx-deployment-on-the-eks-cluster">Step 4: Creating an Nginx Deployment on the EKS Cluster</h2>
<ul>
<li>Install Git</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">sudo</span> <span class="hljs-string">yum</span> <span class="hljs-string">install</span> <span class="hljs-string">-y</span> <span class="hljs-string">git</span>
</code></pre>
<ul>
<li>Download the configuration files</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">git</span> <span class="hljs-string">clone</span> <span class="hljs-string">https://github.com/noweder/EKS-with-Nginx.git</span>
</code></pre>
<ul>
<li>Move to the directory with configuration files</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">cd</span> <span class="hljs-string">EKS-with-Nginx</span>
</code></pre>
<ul>
<li>Create the Load Balancer service</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">./nginx-svc.yaml</span>
</code></pre>
<ul>
<li>Check the Load Balancer service status and copy the external DNS hostname of the load balancer, and paste it into a text file for later use</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">get</span> <span class="hljs-string">service</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665247260114/GuVGLtSoY.png" alt="image.png" /></p>
<ul>
<li>Create the deployment of 3 pods (3 Nginx containers)</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">./nginx-deployment.yaml</span>
</code></pre>
<ul>
<li>Check the deployment status</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">get</span> <span class="hljs-string">deployment</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665248435232/f_VNptwIZ.png" alt="image.png" /></p>
<ul>
<li>View the created pods</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">get</span> <span class="hljs-string">pod</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665248485198/5MDxP-ivx.png" alt="image.png" /></p>
<ul>
<li>View the ReplicaSets</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">get</span> <span class="hljs-string">rs</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665248525780/X5yXCmdF9.png" alt="image.png" /></p>
<ul>
<li>View the EC2 worker nodes</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">get</span> <span class="hljs-string">node</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665248552849/QG-SPiTBo.png" alt="image.png" /></p>
<ul>
<li>Finally, you can access the Nginx application using the load balancer DNS address previously copied on a web browser, or by running the below command in CLI replacing <code>&lt;LOAD_BALANCER_DNS_HOSTNAME&gt;</code> with your specific Load Balancer DNS address</li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-string">curl</span> <span class="hljs-string">&lt;LOAD_BALANCER_DNS_HOSTNAME&gt;</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665247715181/TmuNujfyp.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665247771294/VX4o97j00.png" alt="image.png" /></p>
<p>If you get above results, it means that you have successfully accessed the Nginx application running in container which is deployed in the EKS cluster and exposed to the internet via the load balancer service.</p>
<p>Thank you for reading!</p>
]]></content:encoded></item><item><title><![CDATA[AWS DevOps Team Strategy]]></title><description><![CDATA[In this article, I will briefly define what is DevOps, then we will discuss the suggested strategy to manage and automate DevOps teamwork in the context of AWS Team.
What is DevOps?
DevOps is the combination of cultural philosophies, practices, and t...]]></description><link>https://blog.noweder.com/aws-devops-team-strategy</link><guid isPermaLink="true">https://blog.noweder.com/aws-devops-team-strategy</guid><dc:creator><![CDATA[Hamza Noweder]]></dc:creator><pubDate>Fri, 02 Feb 2024 20:15:11 GMT</pubDate><content:encoded><![CDATA[<p>In this article, I will briefly define what is DevOps, then we will discuss the suggested strategy to manage and automate DevOps teamwork in the context of AWS Team.</p>
<h1 id="heading-what-is-devops">What is DevOps?</h1>
<p>DevOps is the combination of cultural philosophies, practices, and tools that increases an organization’s ability to deliver applications and services at high velocity: evolving and improving products at a faster pace than organizations using traditional software development and infrastructure management processes. This speed enables organizations to better serve their customers and compete more effectively in the market. Below are the main benefits of DevOps:</p>
<ul>
<li><p>Collaboration</p>
</li>
<li><p>Improved delivery</p>
</li>
<li><p>Security</p>
</li>
<li><p>Speed</p>
</li>
<li><p>Scale</p>
</li>
<li><p>Reliability</p>
</li>
</ul>
<h1 id="heading-the-strategy">The Strategy</h1>
<p>In order to automate and manage the AWS DevOps teamwork, we would need to split our strategy into below main parts:</p>
<h2 id="heading-part-1-the-aws-technology-stack">Part 1: The AWS Technology Stack</h2>
<p>As an AWS Team, we need to focus on the below AWS native services:</p>
<ul>
<li><p><strong>Continuous Integration / Continuous Deployment (CI/CD):</strong></p>
<ul>
<li><p><strong>AWS CodeCommit</strong>: Fully managed SCM service</p>
</li>
<li><p><strong>AWS CodeBuild</strong>: To build and test the code</p>
</li>
<li><p><strong>AWS CodeDeploy</strong>: For application deployment within AWS infrastructure</p>
</li>
<li><p><strong>AWS CodePipeline</strong>: Is the glue which binds these services together. It's a orchestration tool which links all these products and automates the process end-to-end</p>
</li>
</ul>
</li>
<li><p><strong>Continuous Monitoring and Improvement:</strong></p>
<ul>
<li><strong>Amazon CloudWatch</strong>: Fully managed monitoring service for AWS applications and resources. It can be used to collect and track metrics and logs, create alarms and automatically react to any change in AWS resources.</li>
</ul>
</li>
<li><p><strong>AWS X-Ray</strong>: Is a tracing service for analyzing the performance of applications/services and helps identify any issues.</p>
</li>
<li><p><strong>AWS CloudTrail</strong>: Is a product which logs API calls and account events. It is used to diagnose security or performance issues, or to provide quality account level traceability.</p>
</li>
<li><p><strong>Infrastructure and Application Deployment:</strong></p>
</li>
<li><p><strong>AWS CloudFormation</strong>: To automate infrastructure provisioning. This can avoid human errors and can be used repeatedly and at scale.</p>
</li>
<li><p><strong>AWS Elastic Beanstalk</strong>: To deploy web applications at scale without managing the underlying infrastructure</p>
</li>
<li><p><strong>Configuration Management:</strong></p>
</li>
<li><p><strong>AWS OpsWorks</strong>: Provides managed implementations of Puppet and Chef in a product which integrates with other AWS Products and services.</p>
</li>
<li><p><strong>AWS Systems Manager</strong>: Allows you to manage applications and infrastructure running in AWS</p>
</li>
<li><p><strong>DevSecOps:</strong> Security checks should be embedded in every below step of the CI/CD pipeline</p>
</li>
<li><p>Code</p>
</li>
<li><p>Build</p>
</li>
<li><p>Test</p>
</li>
<li><p>Deploy</p>
</li>
<li><p>Provision</p>
</li>
<li><p>Monitor</p>
</li>
</ul>
<h2 id="heading-part-2-devops-team-skills-development">Part 2: DevOps Team Skills Development</h2>
<p>The DevOps landscape is constantly evolving and there are new and updated tools being developed all the time. Hence, the DevOps team should be always in a continuous learning mode.</p>
<p>Although the below is not a comprehensive list and is subject to change for the above mentioned reason, these are the main technologies that should be focused on by the DevOps team for continuous upskilling in my humble opinion:</p>
<ul>
<li><p>Programming and Scripting Languages:</p>
</li>
<li><p>Python</p>
</li>
<li><p>Go</p>
</li>
<li><p>Bash Scripting</p>
</li>
<li><p>Containerization - Docker / ECS</p>
</li>
<li><p>Container Orchestration - Kubernetes / EKS</p>
</li>
<li><p>Version Control - Git</p>
</li>
<li><p>IaC:</p>
</li>
<li><p>Terraform</p>
</li>
<li><p>Pulumi</p>
</li>
<li><p>AWS CDK</p>
</li>
<li><p>Configuration Management - Ansible</p>
</li>
<li><p>Monitoring and Observability:</p>
</li>
<li><p>Prometheus - Monitoring</p>
</li>
<li><p>Grafana - Visualization</p>
</li>
<li><p>ELK Stack - Log Management</p>
</li>
<li><p>AWS Serverless Products:</p>
</li>
<li><p>Lambda</p>
</li>
<li><p>API Gateway</p>
</li>
<li><p>SQS</p>
</li>
<li><p>SNS</p>
</li>
<li><p>DynamoDB</p>
</li>
<li><p>Fargate</p>
</li>
<li><p>Etc..</p>
</li>
<li><p>CI/CD Tools:</p>
</li>
<li><p>Jenkins</p>
</li>
<li><p>GitLab</p>
</li>
<li><p>GitHub Actions</p>
</li>
<li><p>CircleCI</p>
</li>
<li><p>TravisCI</p>
</li>
<li><p>GitOps - ArgoCD</p>
</li>
<li><p>System Design</p>
</li>
</ul>
<h2 id="heading-part-3-project-management">Part 3: Project Management</h2>
<p>For the project management aspect, the focus should be on Agile and Scrum methodologies. Below are one of the main tools that would be beneficial for managing DevOps team workflow:</p>
<ul>
<li><p>JIRA</p>
</li>
<li><p>Asana</p>
</li>
<li><p>Trello</p>
</li>
<li><p>Slack - Collaboration</p>
</li>
</ul>
<p>On the other hand, a good recommendation when hiring is to mix experienced people having a variety of backgrounds with new team members so that they can share their knowledge for the benefit of all.</p>
]]></content:encoded></item><item><title><![CDATA[Using AWS Lambda to Revert Unauthorized Security Group Changes]]></title><description><![CDATA[Introduction
In this blog, we will demonstrate how to proactively monitor a web server EC2 security group for any traffic rule changes using Lambda along with a few other services. If we detect any unauthorized changes, we will then undo them using L...]]></description><link>https://blog.noweder.com/using-aws-lambda-to-revert-unauthorized-security-group-changes</link><guid isPermaLink="true">https://blog.noweder.com/using-aws-lambda-to-revert-unauthorized-security-group-changes</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><dc:creator><![CDATA[Hamza Noweder]]></dc:creator><pubDate>Sat, 14 Jan 2023 21:00:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673729568518/3e1a7774-6048-44aa-8712-2954ea9abcce.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In this blog, we will demonstrate how to proactively monitor a web server EC2 security group for any traffic rule changes using Lambda along with a few other services. If we detect <em>any</em> unauthorized changes, we will then undo them using Lambda. There will also be an SNS notification sent out to the admins, which is us.</p>
<p>In this activity we will be working with the following AWS services:</p>
<ul>
<li><p>Lambda</p>
</li>
<li><p>SNS</p>
</li>
<li><p>CloudTrail</p>
</li>
<li><p>CloudWatch</p>
</li>
<li><p>CloudFormation</p>
</li>
<li><p>EC2</p>
</li>
<li><p>VPC</p>
</li>
<li><p>S3</p>
</li>
</ul>
<p>This scenario is a working example of how we can use Lambda to perform various operational tasks within our account, especially ones that maintain our security posture.</p>
<h1 id="heading-architecture">Architecture</h1>
<p>The below diagram illustrates the high-level architecture and the steps to be followed in sequence.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673727728636/efede545-97d3-4609-b7bd-feb4e54a02f3.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-prerequisites">Prerequisites</h1>
<ul>
<li><p>An AWS account</p>
</li>
<li><p>An existing VPC such as the default VPC</p>
</li>
</ul>
<h1 id="heading-steps">Steps</h1>
<h2 id="heading-create-your-cloudtrail-trail">Create Your CloudTrail Trail</h2>
<p>Navigate to the <code>CloudTrail</code> service. From the left-hand menu, access <code>Trails</code> and click on <code>Create trail</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673698475496/5f6bbe5b-5860-4e99-8b48-047c53d15da4.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>Specify your desired name for the trail</p>
</li>
<li><p>For <code>Storage location</code> section, choose <code>New S3 bucket</code> and keep the default generated name. This will create an S3 bucket to load API event trail logs.</p>
</li>
<li><p>Enable <code>CloudWatch Logs</code> to create a log group to which the trail will send events.</p>
</li>
<li><p>Choose <code>Event type</code> as <code>Management events</code></p>
</li>
<li><p>Choose <code>API activity</code> to log both <code>Read</code> and <code>Write</code> activities.</p>
</li>
<li><p>Leave other settings as is and finally click <code>Create trail</code>.</p>
</li>
</ol>
<h2 id="heading-deploy-the-cloudformation-template">Deploy the CloudFormation Template</h2>
<p>The CloudFormation template will create several resources:</p>
<ul>
<li><p>A web security group that is monitored for any changes</p>
</li>
<li><p>An IAM role attached to the Lambda function as an execution role with permissions to remove any changes to the security group inbound rules.</p>
</li>
<li><p>A Lambda Permission which allows CloudWatch Events to invoke the Lambda function</p>
</li>
<li><p>A Lambda function that revokes any changes to the web security group and publishes a message to an SNS topic</p>
</li>
<li><p>A CloudWatch Event Rule that captures security group change events and triggers the Lambda function (<strong>Note</strong>: CloudWatch Events is now Amazon EventBridge)</p>
</li>
<li><p>SNS topic with an email subscription to send an email notification when the Lambda function is invoked</p>
</li>
</ul>
<p>Follow the below steps to deploy these resources:</p>
<ol>
<li>Using your preferred code editor, create a <code>.yaml</code> file with the below CloudFormation template.</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">AWSTemplateFormatVersion:</span> <span class="hljs-number">2010-09-09</span>
<span class="hljs-attr">Description:</span> <span class="hljs-string">|
  Creates the resources necessary to create a rule to monitor and
  auto-mitigate security group change events
</span>
<span class="hljs-attr">Metadata:</span>
  <span class="hljs-attr">License:</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">|
      Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
</span>
      <span class="hljs-string">Licensed</span> <span class="hljs-string">under</span> <span class="hljs-string">the</span> <span class="hljs-string">Apache</span> <span class="hljs-string">License,</span> <span class="hljs-string">Version</span> <span class="hljs-number">2.0</span> <span class="hljs-string">(the</span> <span class="hljs-string">"License"</span><span class="hljs-string">).</span> <span class="hljs-string">You</span> <span class="hljs-string">may</span> <span class="hljs-string">not</span> <span class="hljs-string">use</span> <span class="hljs-string">this</span> <span class="hljs-string">file</span> <span class="hljs-string">except</span> <span class="hljs-string">in</span> <span class="hljs-string">compliance</span> <span class="hljs-string">with</span> <span class="hljs-string">the</span> <span class="hljs-string">License.</span> <span class="hljs-string">A</span> <span class="hljs-string">copy</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">License</span> <span class="hljs-string">is</span> <span class="hljs-string">located</span> <span class="hljs-string">at</span>

          <span class="hljs-string">http://aws.amazon.com/apache2.0/</span>

      <span class="hljs-string">or</span> <span class="hljs-string">in</span> <span class="hljs-string">the</span> <span class="hljs-string">"license"</span> <span class="hljs-string">file</span> <span class="hljs-string">accompanying</span> <span class="hljs-string">this</span> <span class="hljs-string">file.</span> <span class="hljs-string">This</span> <span class="hljs-string">file</span> <span class="hljs-string">is</span> <span class="hljs-string">distributed</span> <span class="hljs-string">on</span> <span class="hljs-string">an</span> <span class="hljs-string">"AS IS"</span> <span class="hljs-string">BASIS,</span> <span class="hljs-string">WITHOUT</span> <span class="hljs-string">WARRANTIES</span> <span class="hljs-string">OR</span> <span class="hljs-string">CONDITIONS</span> <span class="hljs-string">OF</span> <span class="hljs-string">ANY</span> <span class="hljs-string">KIND,</span> <span class="hljs-string">either</span> <span class="hljs-string">express</span> <span class="hljs-string">or</span> <span class="hljs-string">implied.</span> <span class="hljs-string">See</span> <span class="hljs-string">the</span> <span class="hljs-string">License</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">specific</span> <span class="hljs-string">language</span> <span class="hljs-string">governing</span> <span class="hljs-string">permissions</span> <span class="hljs-string">and</span> <span class="hljs-string">limitations</span> <span class="hljs-string">under</span> <span class="hljs-string">the</span> <span class="hljs-string">License.</span>

  <span class="hljs-attr">AWS::CloudFormation::Interface:</span>
    <span class="hljs-attr">ParameterGroups:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">Label:</span>
          <span class="hljs-attr">default:</span> <span class="hljs-string">Settings</span>
        <span class="hljs-attr">Parameters:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">NotificationEmailAddress</span>

    <span class="hljs-attr">ParameterLabels:</span>
      <span class="hljs-attr">NotificationEmailAddress:</span>
        <span class="hljs-attr">default:</span> <span class="hljs-string">Send</span> <span class="hljs-string">notifications</span> <span class="hljs-string">to</span>

<span class="hljs-attr">Parameters:</span>

  <span class="hljs-attr">NotificationEmailAddress:</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">|
      This is the email address that will receive change notifications. You will
      receive a confirmation email that you must accept before you will receive
      notifications.
</span>    <span class="hljs-attr">Type:</span> <span class="hljs-string">String</span>

<span class="hljs-attr">Conditions:</span>

  <span class="hljs-attr">AddEmailNotificationSubscription:</span>
    <span class="hljs-type">!Not</span> [<span class="hljs-type">!Equals</span> [<span class="hljs-type">!Ref</span> <span class="hljs-string">NotificationEmailAddress</span>, <span class="hljs-string">""</span>]]

<span class="hljs-attr">Resources:</span>

  <span class="hljs-attr">WebServerSecurityGroup:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::EC2::SecurityGroup</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">GroupDescription:</span> <span class="hljs-string">Allow</span> <span class="hljs-string">HTTPS</span> <span class="hljs-string">access</span> <span class="hljs-string">to</span> <span class="hljs-string">web</span> <span class="hljs-string">servers</span>
      <span class="hljs-attr">SecurityGroupIngress:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">IpProtocol:</span> <span class="hljs-string">tcp</span>
        <span class="hljs-attr">FromPort:</span> <span class="hljs-number">443</span>
        <span class="hljs-attr">ToPort:</span> <span class="hljs-number">443</span>
        <span class="hljs-attr">CidrIp:</span> <span class="hljs-number">0.0</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">/0</span>
      <span class="hljs-attr">Tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">Key:</span> <span class="hljs-string">Name</span>
          <span class="hljs-attr">Value:</span> <span class="hljs-string">Web</span> <span class="hljs-string">Server</span> <span class="hljs-string">Security</span> <span class="hljs-string">Group</span>

  <span class="hljs-attr">SecurityGroupChangeAutoResponseRole:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::IAM::Role</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">AssumeRolePolicyDocument:</span>
        <span class="hljs-attr">Version:</span> <span class="hljs-number">2012-10-17</span>
        <span class="hljs-attr">Statement:</span>
          <span class="hljs-bullet">-</span>
            <span class="hljs-attr">Effect:</span> <span class="hljs-string">Allow</span>
            <span class="hljs-attr">Principal:</span>
              <span class="hljs-attr">Service:</span> <span class="hljs-string">lambda.amazonaws.com</span>
            <span class="hljs-attr">Action:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">sts:AssumeRole</span>
      <span class="hljs-attr">ManagedPolicyArns:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole</span>
      <span class="hljs-attr">Policies:</span>
        <span class="hljs-bullet">-</span>
          <span class="hljs-attr">PolicyName:</span> <span class="hljs-string">SecurityGroupModification</span>
          <span class="hljs-attr">PolicyDocument:</span>
            <span class="hljs-attr">Version:</span> <span class="hljs-number">2012-10-17</span>
            <span class="hljs-attr">Statement:</span>
              <span class="hljs-bullet">-</span>
                <span class="hljs-attr">Sid:</span> <span class="hljs-string">AllowSecurityGroupActions</span>
                <span class="hljs-attr">Effect:</span> <span class="hljs-string">Allow</span>
                <span class="hljs-attr">Action:</span>
                  <span class="hljs-bullet">-</span> <span class="hljs-string">ec2:RevokeSecurityGroupIngress</span>
                <span class="hljs-attr">Resource:</span> <span class="hljs-type">!Sub</span> <span class="hljs-string">arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:security-group/${WebServerSecurityGroup.GroupId}</span>
              <span class="hljs-bullet">-</span>
                <span class="hljs-attr">Sid:</span> <span class="hljs-string">AllowSnsActions</span>
                <span class="hljs-attr">Effect:</span> <span class="hljs-string">Allow</span>
                <span class="hljs-attr">Action:</span>
                  <span class="hljs-bullet">-</span> <span class="hljs-string">sns:Publish</span>
                <span class="hljs-attr">Resource:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">SnsTopicForCloudWatchEvent</span>

  <span class="hljs-attr">SecurityGroupChangeAutoResponse:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::Lambda::Function</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">Description:</span> <span class="hljs-string">Responds</span> <span class="hljs-string">to</span> <span class="hljs-string">security</span> <span class="hljs-string">group</span> <span class="hljs-string">changes</span>
      <span class="hljs-attr">Handler:</span> <span class="hljs-string">index.lambda_handler</span>
      <span class="hljs-attr">MemorySize:</span> <span class="hljs-number">1024</span>
      <span class="hljs-attr">Role:</span> <span class="hljs-type">!GetAtt</span> <span class="hljs-string">SecurityGroupChangeAutoResponseRole.Arn</span>
      <span class="hljs-attr">Runtime:</span> <span class="hljs-string">python3.9</span>
      <span class="hljs-attr">Timeout:</span> <span class="hljs-number">60</span>
      <span class="hljs-attr">Environment:</span>
        <span class="hljs-attr">Variables:</span>
          <span class="hljs-attr">security_group_id:</span> <span class="hljs-type">!GetAtt</span> <span class="hljs-string">WebServerSecurityGroup.GroupId</span>
          <span class="hljs-attr">sns_topic_arn:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">SnsTopicForCloudWatchEvent</span>
      <span class="hljs-attr">Code:</span>
        <span class="hljs-attr">ZipFile:</span> <span class="hljs-string">|
          import os, json, boto3
</span>
          <span class="hljs-comment">#===============================================================================</span>
          <span class="hljs-string">def</span> <span class="hljs-string">lambda_handler(event,</span> <span class="hljs-string">context):</span>

              <span class="hljs-string">print(event)</span>

              <span class="hljs-comment"># Ensure that we have an event name to evaluate.</span>
              <span class="hljs-string">if</span> <span class="hljs-string">'detail'</span> <span class="hljs-string">not</span> <span class="hljs-string">in</span> <span class="hljs-string">event</span> <span class="hljs-string">or</span> <span class="hljs-string">('detail'</span> <span class="hljs-string">in</span> <span class="hljs-string">event</span> <span class="hljs-string">and</span> <span class="hljs-string">'eventName'</span> <span class="hljs-string">not</span> <span class="hljs-string">in</span> <span class="hljs-string">event['detail']):</span>
                  <span class="hljs-string">return</span> {<span class="hljs-attr">"Result":</span> <span class="hljs-string">"Failure"</span>, <span class="hljs-attr">"Message":</span> <span class="hljs-string">"Lambda not triggered by an event"</span>}

              <span class="hljs-comment"># Remove the rule only if the event was to authorize the ingress rule for the given</span>
              <span class="hljs-comment"># security group that was injected during CloudFormation execution.</span>
              <span class="hljs-string">if</span> <span class="hljs-string">(event['detail']['eventName']</span> <span class="hljs-string">==</span> <span class="hljs-string">'AuthorizeSecurityGroupIngress'</span> <span class="hljs-string">and</span>
                  <span class="hljs-string">event['detail']['requestParameters']['groupId']</span> <span class="hljs-string">==</span> <span class="hljs-string">os.environ['security_group_id']):</span>
                  <span class="hljs-string">result</span> <span class="hljs-string">=</span> <span class="hljs-string">revoke_security_group_ingress(event['detail'])</span>

                  <span class="hljs-string">message</span> <span class="hljs-string">=</span> <span class="hljs-string">"AUTO-MITIGATED: Ingress rule removed from security group: {} that was added by {}: {}"</span><span class="hljs-string">.format(</span>
                          <span class="hljs-string">result['group_id'],</span>
                          <span class="hljs-string">result['user_name'],</span>
                          <span class="hljs-string">json.dumps(result['ip_permissions'])</span>
                          <span class="hljs-string">)</span>

                  <span class="hljs-string">boto3.client('sns').publish(</span>
                    <span class="hljs-string">TargetArn</span> <span class="hljs-string">=</span> <span class="hljs-string">os.environ['sns_topic_arn'],</span>
                    <span class="hljs-string">Message</span> <span class="hljs-string">=</span> <span class="hljs-string">message,</span>
                    <span class="hljs-string">Subject</span> <span class="hljs-string">=</span> <span class="hljs-string">"Auto-mitigation successful"</span>
                    <span class="hljs-string">)</span>


          <span class="hljs-comment">#===============================================================================</span>
          <span class="hljs-string">def</span> <span class="hljs-string">revoke_security_group_ingress(event_detail):</span>

              <span class="hljs-string">request_parameters</span> <span class="hljs-string">=</span> <span class="hljs-string">event_detail['requestParameters']</span>

              <span class="hljs-comment"># Build the normalized IP permission JSON struture.</span>
              <span class="hljs-string">ip_permissions</span> <span class="hljs-string">=</span> <span class="hljs-string">normalize_paramter_names(request_parameters['ipPermissions']['items'])</span>

              <span class="hljs-string">response</span> <span class="hljs-string">=</span> <span class="hljs-string">boto3.client('ec2').revoke_security_group_ingress(</span>
                  <span class="hljs-string">GroupId</span> <span class="hljs-string">=</span> <span class="hljs-string">request_parameters['groupId'],</span>
                  <span class="hljs-string">IpPermissions</span> <span class="hljs-string">=</span> <span class="hljs-string">ip_permissions</span>
                  <span class="hljs-string">)</span>

              <span class="hljs-comment"># Build the result</span>
              <span class="hljs-string">result</span> <span class="hljs-string">=</span> {}
              <span class="hljs-string">result['group_id']</span> <span class="hljs-string">=</span> <span class="hljs-string">request_parameters['groupId']</span>
              <span class="hljs-string">result['user_name']</span> <span class="hljs-string">=</span> <span class="hljs-string">event_detail['userIdentity']['arn']</span>
              <span class="hljs-string">result['ip_permissions']</span> <span class="hljs-string">=</span> <span class="hljs-string">ip_permissions</span>

              <span class="hljs-string">return</span> <span class="hljs-string">result</span>


          <span class="hljs-comment">#===============================================================================</span>
          <span class="hljs-string">def</span> <span class="hljs-string">normalize_paramter_names(ip_items):</span>

              <span class="hljs-comment"># Start building the permissions items list.</span>
              <span class="hljs-string">new_ip_items</span> <span class="hljs-string">=</span> []

              <span class="hljs-comment"># First, build the basic parameter list.</span>
              <span class="hljs-attr">for ip_item in ip_items:</span>

                  <span class="hljs-string">new_ip_item</span> <span class="hljs-string">=</span> {
                      <span class="hljs-attr">"IpProtocol":</span> <span class="hljs-string">ip_item</span>[<span class="hljs-string">'ipProtocol'</span>],
                      <span class="hljs-attr">"FromPort":</span> <span class="hljs-string">ip_item</span>[<span class="hljs-string">'fromPort'</span>],
                      <span class="hljs-attr">"ToPort":</span> <span class="hljs-string">ip_item</span>[<span class="hljs-string">'toPort'</span>]
                  }

                  <span class="hljs-comment">#CidrIp or CidrIpv6 (IPv4 or IPv6)?</span>
                  <span class="hljs-string">if</span> <span class="hljs-string">'ipv6Ranges'</span> <span class="hljs-string">in</span> <span class="hljs-string">ip_item</span> <span class="hljs-string">and</span> <span class="hljs-string">ip_item['ipv6Ranges']:</span>
                      <span class="hljs-comment"># This is an IPv6 permission range, so change the key names.</span>
                      <span class="hljs-string">ipv_range_list_name</span> <span class="hljs-string">=</span> <span class="hljs-string">'ipv6Ranges'</span>
                      <span class="hljs-string">ipv_address_value</span> <span class="hljs-string">=</span> <span class="hljs-string">'cidrIpv6'</span>
                      <span class="hljs-string">ipv_range_list_name_capitalized</span> <span class="hljs-string">=</span> <span class="hljs-string">'Ipv6Ranges'</span>
                      <span class="hljs-string">ipv_address_value_capitalized</span> <span class="hljs-string">=</span> <span class="hljs-string">'CidrIpv6'</span>
                  <span class="hljs-attr">else:</span>
                      <span class="hljs-string">ipv_range_list_name</span> <span class="hljs-string">=</span> <span class="hljs-string">'ipRanges'</span>
                      <span class="hljs-string">ipv_address_value</span> <span class="hljs-string">=</span> <span class="hljs-string">'cidrIp'</span>
                      <span class="hljs-string">ipv_range_list_name_capitalized</span> <span class="hljs-string">=</span> <span class="hljs-string">'IpRanges'</span>
                      <span class="hljs-string">ipv_address_value_capitalized</span> <span class="hljs-string">=</span> <span class="hljs-string">'CidrIp'</span>

                  <span class="hljs-string">ip_ranges</span> <span class="hljs-string">=</span> []

                  <span class="hljs-comment"># Next, build the IP permission list.</span>
                  <span class="hljs-string">for</span> <span class="hljs-string">item</span> <span class="hljs-string">in</span> <span class="hljs-string">ip_item[ipv_range_list_name]['items']:</span>
                      <span class="hljs-string">ip_ranges.append(</span>
                          {<span class="hljs-attr">ipv_address_value_capitalized :</span> <span class="hljs-string">item</span>[<span class="hljs-string">ipv_address_value</span>]}
                          <span class="hljs-string">)</span>

                  <span class="hljs-string">new_ip_item[ipv_range_list_name_capitalized]</span> <span class="hljs-string">=</span> <span class="hljs-string">ip_ranges</span>

                  <span class="hljs-string">new_ip_items.append(new_ip_item)</span>

              <span class="hljs-string">return</span> <span class="hljs-string">new_ip_items</span>

  <span class="hljs-attr">SecurityGroupChangeAutoResponseLambdaPermission:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::Lambda::Permission</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">Action:</span> <span class="hljs-string">lambda:InvokeFunction</span>
      <span class="hljs-attr">Principal:</span> <span class="hljs-string">events.amazonaws.com</span>
      <span class="hljs-attr">FunctionName:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">SecurityGroupChangeAutoResponse</span>

  <span class="hljs-attr">TriggeredRuleForSecurityGroupChangeAutoResponse:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::Events::Rule</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-comment">#Name: SecurityGroupChangeAutoResponse</span>
      <span class="hljs-attr">Description:</span> <span class="hljs-string">Responds</span> <span class="hljs-string">to</span> <span class="hljs-string">security</span> <span class="hljs-string">group</span> <span class="hljs-string">change</span> <span class="hljs-string">events</span>
      <span class="hljs-attr">EventPattern:</span>
        <span class="hljs-attr">detail:</span>
          <span class="hljs-attr">eventSource:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">ec2.amazonaws.com</span>
          <span class="hljs-attr">eventName:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">AuthorizeSecurityGroupIngress</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">AuthorizeSecurityGroupEgress</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">RevokeSecurityGroupEgress</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">RevokeSecurityGroupIngress</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">CreateSecurityGroup</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">DeleteSecurityGroup</span>
      <span class="hljs-attr">State:</span> <span class="hljs-string">ENABLED</span>
      <span class="hljs-attr">Targets:</span>
        <span class="hljs-bullet">-</span>
          <span class="hljs-attr">Arn:</span> <span class="hljs-type">!GetAtt</span> <span class="hljs-string">SecurityGroupChangeAutoResponse.Arn</span>
          <span class="hljs-attr">Id:</span> <span class="hljs-string">TargetFunctionV1</span>

  <span class="hljs-attr">SnsTopicForCloudWatchEvent:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::SNS::Topic</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">DisplayName:</span> <span class="hljs-string">Broadcasts</span> <span class="hljs-string">message</span> <span class="hljs-string">to</span> <span class="hljs-string">subscribers</span>

  <span class="hljs-attr">SnsTopicSubscriptionForCloudWatchEvent:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::SNS::Subscription</span>
    <span class="hljs-attr">Condition:</span> <span class="hljs-string">AddEmailNotificationSubscription</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">TopicArn:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">SnsTopicForCloudWatchEvent</span>
      <span class="hljs-attr">Endpoint:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">NotificationEmailAddress</span>
      <span class="hljs-attr">Protocol:</span> <span class="hljs-string">email</span>
</code></pre>
<ol>
<li><p>Navigate to <code>CloudFormation</code> service and click on <code>Create stack</code>.</p>
</li>
<li><p>Click on <code>Upload a template file</code> from the <code>Specify template</code> section and choose the locally created <code>.yaml</code> file, then click <code>Next</code>.</p>
</li>
<li><p>Specify your desired name of the stack.</p>
</li>
<li><p>Enter your email address in the <code>Parameters</code> section under <code>Send notifications to</code> parameter.</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673711932477/a87af387-457c-45db-9a7a-e7b874fda702.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Leave everything else as is and click on <code>Submit</code> at the end.</p>
</li>
<li><p>Once the stack status changes to <code>CREATE_COMPLETE</code>, you should receive an email from AWS to confirm the subscription to the SNS topic. Click on <code>Confirm subscription</code> link within the email bod</p>
</li>
</ol>
<h2 id="heading-test-and-verify-the-solution">Test and Verify The Solution</h2>
<p>Head to <code>EC2</code> in the console and access <code>Security Groups</code> under <code>Network &amp; Security</code>. Select the created <code>Web Server Security Group</code> and click on <code>Edit inbound rules</code>.</p>
<p>Click on <code>Add rule</code>. Select <code>SSH</code> as Type and <code>Anywhere-IPv4</code> as the Source. Then click on <code>Save rules</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673720065238/35aa5272-1109-4288-b1bb-d020b72ac425.png" alt class="image--center mx-auto" /></p>
<p>Once this change is done, CloudTrail will catch this event and send it to CloudWatch, which will trigger the deployed event rule and pass the data to the Lambda function. The function will parse that data and do two things; it will remove the rule we added, and also publish to the SNS topic which will send an email to the defined email address.</p>
<p>Refresh the inbound rules and you will notice the newly added rule has been immediately removed!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673720831250/52dd1816-971e-4f2d-badb-3e33eaec8e60.png" alt class="image--center mx-auto" /></p>
<p>You will also receive a notification email mentioning that the added ingress rule has been removed as shown below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673720917733/3eec8b75-d552-463f-9985-e7b0437311f3.png" alt class="image--center mx-auto" /></p>
<p>We can also view the metrics of the successful lambda function invocation from the <code>Monitor</code> menu of the function as shown below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673721133050/b8a13197-0765-4ec3-b9d0-81535132d4f1.png" alt class="image--center mx-auto" /></p>
<p>Until next time...</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Automate Deploying a LAMP Stack Application using Ansible]]></title><description><![CDATA[What is Ansible?
Ansible is a software tool that provides simple but powerful automation for cross-platform computer support. It is primarily intended for IT professionals, who use it for application deployment, updates on workstations and servers, c...]]></description><link>https://blog.noweder.com/automate-deploying-a-lamp-stack-application-using-ansible</link><guid isPermaLink="true">https://blog.noweder.com/automate-deploying-a-lamp-stack-application-using-ansible</guid><category><![CDATA[ansible]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[configuration management]]></category><dc:creator><![CDATA[Hamza Noweder]]></dc:creator><pubDate>Wed, 09 Nov 2022 22:43:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1668034059128/oWvSert5H.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-what-is-ansible">What is Ansible?</h1>
<p>Ansible is a software tool that provides simple but powerful automation for cross-platform computer support. It is primarily intended for IT professionals, who use it for application deployment, updates on workstations and servers, cloud provisioning, configuration management, intra-service orchestration, and nearly anything a systems administrator does on a weekly or daily basis. Ansible doesn't depend on agent software and has no additional security infrastructure, so it's easy to deploy.</p>
<p>In this project, we will use Ansible to deploy a LAMP stack web application on multi-node setup.</p>
<h1 id="heading-pre-requisites">Pre-requisites</h1>
<ul>
<li>User with <code>root</code> privileges</li>
<li><p>3 CentOS hosts on the same network. One will act the Ansible controller, and the other two will acts as target hosts. The hostnames should be configured as below to follow along: </p>
<ul>
<li>ansible-controller</li>
<li>lamp-db</li>
<li>lamp-web</li>
</ul>
</li>
<li><p>Ansible installed on ansible-controller host</p>
</li>
<li>/etc/hosts/ on <code>ansible-controller</code> host is configured with the IP addresses of the targets as below example</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1666967198317/wyue6mFQd.png" alt="image.png" /></p>
<ul>
<li>SSH Password-less login enabled between the ansible-controller host and the two target hosts</li>
</ul>
<h1 id="heading-steps"><strong>Steps</strong></h1>
<h2 id="heading-step1-create-the-project-folder-and-access-it-by-running-below-commands">Step1: Create the project folder and access it by running below commands:</h2>
<pre><code>mkdir lamp-stack-playbooks
cd lamp-stack-playbooks
</code></pre><h2 id="heading-step-2-configure-the-inventory-file">Step 2: Configure the inventory file</h2>
<ul>
<li>Create an inventory file<pre><code>vi inventory
</code></pre></li>
<li>Copy below lines and paste them in the inventory file. Also, make sure to replace  and  with your proper DB and Web hosts' IP addresses. In addition you need to specify the user and SSH private key path for both DB and WEB.</li>
</ul>
<pre><code>[db_servers]
lamp-db ansible_host=&lt;YOUR-DB-HOST-IP&gt; ansible_user=&lt;DB_USER&gt; ansible_ssh_private_key_file=&lt;PATH_TO_DB_SSH_PRIVATE_KEY&gt; mysqlservice=mysqld mysql_port=3306 dbname=ecomdb dbuser=ecomuser dbpassword=ecompassword

[web_servers]
lamp-web ansible_host=&lt;YOUR-WEB-HOST-IP&gt; ansible_user=&lt;WEB_USER&gt; ansible_ssh_private_key_file=&lt;PATH_TO_WEB_SSH_PRIVATE_KEY&gt; httpd_port=80 repository=https://github.com/noweder/lamp-app-ecommerce.git
</code></pre><h2 id="heading-step-3-prepare-the-configuration-files">Step 3: Prepare the configuration files</h2>
<ul>
<li><p>Create <code>files</code> sub-folder and move into it:</p>
<pre><code>mkdir files
</code></pre></li>
<li><p>Create MySQL Configuration file <code>my.cnf</code> under <code>files</code> folder:</p>
<pre><code>vi files/my.cnf
</code></pre></li>
<li>Paste the below lines inside <code>my.cnf</code> and save it:<pre><code>[mysqld]
innodb-buffer-pool-size=<span class="hljs-number">5242880</span>
# datadir=<span class="hljs-regexp">/var/</span>lib/mysql
# socket=<span class="hljs-regexp">/var/</span>lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=<span class="hljs-number">0</span>
port=<span class="hljs-number">3306</span>
</code></pre></li>
<li>Create another file named <code>db-load-script.sql</code> on the controller host under <code>files</code> folder:<pre><code>vi files/db-load-script.sql
</code></pre></li>
<li>Paste below lines inside <code>db-load-script.sql</code> file and save it:</li>
</ul>
<pre><code>USE ecomdb;
CREATE TABLE products (id mediumint(<span class="hljs-number">8</span>) unsigned NOT NULL auto_increment,Name varchar(<span class="hljs-number">255</span>) <span class="hljs-keyword">default</span> NULL,Price varchar(<span class="hljs-number">255</span>) <span class="hljs-keyword">default</span> NULL, ImageUrl varchar(<span class="hljs-number">255</span>) <span class="hljs-keyword">default</span> NULL,PRIMARY KEY (id)) AUTO_INCREMENT=<span class="hljs-number">1</span>;

INSERT INTO products (Name,Price,ImageUrl) VALUES (<span class="hljs-string">"Laptop"</span>,<span class="hljs-string">"100"</span>,<span class="hljs-string">"c-1.png"</span>),(<span class="hljs-string">"Drone"</span>,<span class="hljs-string">"200"</span>,<span class="hljs-string">"c-2.png"</span>),(<span class="hljs-string">"VR"</span>,<span class="hljs-string">"300"</span>,<span class="hljs-string">"c-3.png"</span>),(<span class="hljs-string">"Tablet"</span>,<span class="hljs-string">"50"</span>,<span class="hljs-string">"c-5.png"</span>),(<span class="hljs-string">"Watch"</span>,<span class="hljs-string">"90"</span>,<span class="hljs-string">"c-6.png"</span>),(<span class="hljs-string">"Phone Covers"</span>,<span class="hljs-string">"20"</span>,<span class="hljs-string">"c-7.png"</span>),(<span class="hljs-string">"Phone"</span>,<span class="hljs-string">"80"</span>,<span class="hljs-string">"c-8.png"</span>),(<span class="hljs-string">"Laptop"</span>,<span class="hljs-string">"150"</span>,<span class="hljs-string">"c-4.png"</span>);
</code></pre><ul>
<li>Create <code>index.php</code> file on controller host under <code>files</code> folder<pre><code>vi files/index.php
</code></pre><ul>
<li>Paste below code inside <code>index.php</code> file and make sure to replace <code>&lt;YOUR-DB-HOST-IP&gt;</code> with your specific DB Host IP address before saving it:</li>
</ul>
</li>
</ul>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
    &lt;head&gt;
        &lt;meta charset="utf-8"&gt;
        &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
        &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;

        &lt;title&gt;E-Commerce Website&lt;/title&gt;

        &lt;!-- Favicon --&gt;
        &lt;link rel="icon" href="img/favicon.png" type="image/png" /&gt;
        &lt;!-- Bootstrap CSS --&gt;
        &lt;link href="css/bootstrap.min.css" rel="stylesheet"&gt;
        &lt;!-- Icon CSS--&gt;
        &lt;link rel="stylesheet" href="vendors/font-awesome/css/font-awesome.min.css"&gt;
        &lt;link rel="stylesheet" href="vendors/linearicons/linearicons-1.0.0.css"&gt;
        &lt;!-- Animations CSS--&gt;
        &lt;link rel="stylesheet" href="vendors/wow-js/animate.css"&gt;
        &lt;!-- owl_carousel--&gt;
        &lt;link rel="stylesheet" href="vendors/owl_carousel/owl.carousel.css"&gt;

        &lt;!-- Theme style CSS --&gt;
        &lt;link href="css/style.css" rel="stylesheet"&gt;
&lt;!--        &lt;link href="css/responsive.css" rel="stylesheet"&gt;  --&gt;

        &lt;!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --&gt;
        &lt;!-- WARNING: Respond.js doesn't work if you view the page via file:// --&gt;
        &lt;!--[if lt IE 9]&gt;
          &lt;script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"&gt;&lt;/script&gt;
          &lt;script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"&gt;&lt;/script&gt;
        &lt;![endif]--&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;!--==========Main Header==========--&gt;
        &lt;header class="main_header_area"&gt;
            &lt;nav class="navbar navbar-default navbar-fixed-top" id="main_navbar"&gt;
                &lt;div class="container-fluid searchForm"&gt;
                    &lt;form action="#" class="row"&gt;
                        &lt;div class="input-group"&gt;
                            &lt;span class="input-group-addon"&gt;&lt;i class="lnr lnr-magnifier"&gt;&lt;/i&gt;&lt;/span&gt;
                            &lt;input type="search" name="search" class="form-control" placeholder="Type &amp; Hit Enter"&gt;
                            &lt;span class="input-group-addon form_hide"&gt;&lt;i class="lnr lnr-cross"&gt;&lt;/i&gt;&lt;/span&gt;
                        &lt;/div&gt;
                    &lt;/form&gt;
                &lt;/div&gt;
                &lt;div class="container"&gt;
                    &lt;div class="row"&gt;
                    &lt;!-- Brand and toggle get grouped for better mobile display --&gt;
                    &lt;div class="col-md-2 p0"&gt;
                        &lt;div class="navbar-header"&gt;
                            &lt;button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"&gt;
                            &lt;span class="sr-only"&gt;Toggle navigation&lt;/span&gt;
                            &lt;span class="icon-bar"&gt;&lt;/span&gt;
                            &lt;span class="icon-bar"&gt;&lt;/span&gt;
                            &lt;span class="icon-bar"&gt;&lt;/span&gt;
                            &lt;/button&gt;
                            &lt;a class="navbar-brand" href="index.html"&gt;
                                &lt;img src="img/logo.png" alt=""&gt;
                                &lt;img src="img/logo-2.png" alt=""&gt;
                            &lt;/a&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;

                    &lt;!-- Collect the nav links, forms, and other content for toggling --&gt;
                    &lt;div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"&gt;
                        &lt;div class="col-md-9 p0"&gt;
                            &lt;ul class="nav navbar-nav main_nav"&gt;
                              &lt;li&gt;&lt;a href="#"&gt;Laptops&lt;/a&gt;&lt;/li&gt;
                              &lt;li&gt;&lt;a href="#"&gt;Drones&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href="#"&gt;Gadgets&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href="#"&gt;Phones&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href="#"&gt;VR&lt;/a&gt;&lt;/li&gt;
                                &lt;li&gt;&lt;a href="#"&gt;Contact us&lt;/a&gt;&lt;/li&gt;
                            &lt;/ul&gt;
                        &lt;/div&gt;
                        &lt;div class="col-md-1 p0"&gt;
                            &lt;ul class="nav navbar-nav navbar-right"&gt;
                                &lt;li&gt;&lt;a href="#" class="nav_searchFrom"&gt;&lt;i class="lnr lnr-magnifier"&gt;&lt;/i&gt;&lt;/a&gt;&lt;/li&gt;
                            &lt;/ul&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;&lt;!-- /.navbar-collapse --&gt;
                    &lt;/div&gt;
                &lt;/div&gt;&lt;!-- /.container-fluid --&gt;
            &lt;/nav&gt;
        &lt;/header&gt;
        &lt;!--==========Main Header==========--&gt;

        &lt;!--==========Slider area==========--&gt;
        &lt;section class="slider_area row m0"&gt;
            &lt;div class="slider_inner"&gt;
                &lt;div class="camera_caption"&gt;
                    &lt;h2 class="wow fadeInUp animated"&gt;Make Your Shopping Easy&lt;/h2&gt;
                    &lt;h5 class="wow fadeIn animated" data-wow-delay="0.3s"&gt;Find everything accordingly&lt;/h5&gt;
                    &lt;a class="learn_mor wow fadeInU" data-wow-delay="0.6s" href="#product-list"&gt;Show Now!&lt;/a&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/section&gt;
        &lt;!--==========End Slider area==========--&gt;

        &lt;section class="best_business_area row"&gt;
            &lt;div class="check_tittle wow fadeInUp" data-wow-delay="0.7s" id="product-list"&gt;
                &lt;h2&gt;Product List&lt;/h2&gt;
            &lt;/div&gt;
            &lt;div class="row it_works"&gt;
              &lt;?php

                        $link = mysqli_connect('&lt;YOUR_DB_HOST_IP&gt;', 'ecomuser', 'ecompassword', 'ecomdb');

                        if ($link) {
                        $res = mysqli_query($link, "select * from products;");
                        while ($row = mysqli_fetch_assoc($res)) { ?&gt;

                &lt;div class="col-md-3 col-sm-6 business_content"&gt;
                    &lt;?php echo '&lt;img src="img/' . $row['ImageUrl'] . '" alt=""&gt;' ?&gt;
                    &lt;div class="media"&gt;
                        &lt;div class="media-left"&gt;

                        &lt;/div&gt;
                        &lt;div class="media-body"&gt;
                            &lt;a href="#"&gt;&lt;?php echo $row['Name'] ?&gt;&lt;/a&gt;
                            &lt;p&gt;Purchase &lt;?php echo $row['Name'] ?&gt; at the lowest price &lt;span&gt;&lt;?php echo $row['Price'] ?&gt;$&lt;/span&gt;&lt;/p&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/div&gt;

                &lt;?php
                        }
                    }
                    else {
                ?&gt;
                &lt;div style="width: 100%"&gt;
                &lt;div class="error-content"&gt;

                    &lt;h1&gt;Database connection error&lt;/h1&gt;
                    &lt;p&gt;
                    &lt;?php
                          echo mysqli_connect_errno() . ":" . mysqli_connect_error();
                    ?&gt;
                    &lt;/p&gt;
                  &lt;/div&gt;
                  &lt;/div&gt;
                  &lt;?php
                    }
                  ?&gt;


            &lt;/div&gt;
        &lt;/section&gt;


        &lt;footer class="footer_area row"&gt;
            &lt;div class="container custom-container"&gt;



                &lt;div class="copy_right_area"&gt;
                    &lt;h4 class="copy_right"&gt;© Copyright 2019 Ecommerce Website | All Rights Reserved&lt;/h4&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/footer&gt;

        &lt;!-- jQuery --&gt;
        &lt;script src="js/jquery-1.12.4.min.js"&gt;&lt;/script&gt;
        &lt;!-- Bootstrap --&gt;
        &lt;script src="js/bootstrap.min.js"&gt;&lt;/script&gt;
        &lt;!-- Wow js --&gt;
        &lt;script src="vendors/wow-js/wow.min.js"&gt;&lt;/script&gt;
        &lt;!-- Wow js --&gt;
        &lt;script src="vendors/Counter-Up/waypoints.min.js"&gt;&lt;/script&gt;
        &lt;script src="vendors/Counter-Up/jquery.counterup.min.js"&gt;&lt;/script&gt;
        &lt;!-- Stellar js --&gt;
        &lt;script src="vendors/stellar/jquery.stellar.js"&gt;&lt;/script&gt;
        &lt;!-- owl_carousel js --&gt;
        &lt;script src="vendors/owl_carousel/owl.carousel.min.js"&gt;&lt;/script&gt;
        &lt;!-- Theme js --&gt;
        &lt;script src="js/theme.js"&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre><h2 id="heading-step-3-create-a-deploy-lamp-stackyml-playbook-file-under-lamp-stack-playbooks-and-paste-below-code">Step 3: Create a <code>deploy-lamp-stack.yml</code> playbook file under <code>lamp-stack-playbooks</code> and paste below code:</h2>
<pre><code>---

- name: Deploy lamp stack application
  <span class="hljs-attr">hosts</span>: all
  <span class="hljs-attr">become</span>: yes
  <span class="hljs-attr">tasks</span>:
    - name: Install common dependencies
      <span class="hljs-attr">yum</span>:
        name:
          - libselinux-python
          - libsemanage-python
          - firewalld
        <span class="hljs-attr">state</span>: installed

    # Install and Configure Database
- name: Deploy DB <span class="hljs-keyword">of</span> the application
  <span class="hljs-attr">hosts</span>: lamp-db
  <span class="hljs-attr">become</span>: yes
  <span class="hljs-attr">tasks</span>:
    - name: Install MariaDB package
      <span class="hljs-attr">yum</span>:
        name:
          - mariadb-server
          - MySQL-python
        <span class="hljs-attr">state</span>: installed

    - name: Create Mysql configuration file
      <span class="hljs-attr">copy</span>: src=files/my.cnf dest=<span class="hljs-regexp">/etc/my</span>.cnf

    - name: Start MariaDB Service
      <span class="hljs-attr">service</span>: name=mariadb state=started enabled=yes

    - name: Start firewalld
      <span class="hljs-attr">service</span>: name=firewalld state=started enabled=yes

    - name: insert firewalld rule
      <span class="hljs-attr">firewalld</span>: port={{ mysql_port }}/tcp permanent=<span class="hljs-literal">true</span> state=enabled immediate=yes

    - name: Create Application Database
      <span class="hljs-attr">mysql_db</span>: name={{ dbname }} state=present

    - name: Create Application DB User
      <span class="hljs-attr">mysql_user</span>: name={{ dbuser }} password={{ dbpassword }} priv=*.*:ALL host=<span class="hljs-string">'&lt;YOUR_WEB_HOST_IP&gt;'</span> state=present

    - name: Move db-load-script to db host
      <span class="hljs-attr">copy</span>:
        src: files/db-load-script.sql
        <span class="hljs-attr">dest</span>: <span class="hljs-regexp">/tmp/</span>db-load-script.sql

    - name: Load Inventory Data
      <span class="hljs-attr">shell</span>: mysql -f &lt; <span class="hljs-regexp">/tmp/</span>db-load-script.sql

- name: Deploy web application
  <span class="hljs-attr">hosts</span>: lamp-web
  <span class="hljs-attr">become</span>: yes
  <span class="hljs-attr">tasks</span>:
    - name: Install httpd and php
      <span class="hljs-attr">yum</span>:
        name:
          - httpd
          - php
          - php-mysql
        <span class="hljs-attr">state</span>: present

    - name: Install web role specific dependencies
      <span class="hljs-attr">yum</span>: name=git state=installed

    - name: Start firewalld
      <span class="hljs-attr">service</span>: name=firewalld state=started enabled=yes

    - name: insert firewalld rule <span class="hljs-keyword">for</span> httpd
      <span class="hljs-attr">firewalld</span>: port={{ httpd_port }}/tcp permanent=<span class="hljs-literal">true</span> state=enabled immediate=yes

    - name: <span class="hljs-built_in">Set</span> index.php <span class="hljs-keyword">as</span> the <span class="hljs-keyword">default</span> page
      <span class="hljs-attr">tags</span>: <span class="hljs-string">"Set index.php as the default page"</span>
      <span class="hljs-attr">replace</span>:
        path: <span class="hljs-regexp">/etc/</span>httpd/conf/httpd.conf
        <span class="hljs-attr">regexp</span>: <span class="hljs-string">'DirectoryIndex index.html'</span>
        <span class="hljs-attr">replace</span>: <span class="hljs-string">'DirectoryIndex index.php'</span>

    - name: http service state
      <span class="hljs-attr">service</span>: name=httpd state=started enabled=yes

    - name: Copy the code <span class="hljs-keyword">from</span> repository
      <span class="hljs-attr">git</span>: repo={{ repository }} dest=<span class="hljs-regexp">/var/</span>www/html/  force=yes

    - name: Create the index.php file
      <span class="hljs-attr">copy</span>: src=files/index.php dest=<span class="hljs-regexp">/var/</span>www/html/index.php
</code></pre><ul>
<li>Make sure to replace <code>&lt;YOUR_WEB_HOST_IP&gt;</code> with your specific Web Host IP address before saving the file.</li>
</ul>
<h2 id="heading-step-4-run-the-playbook-with-below-command">Step 4: Run the playbook with below command</h2>
<pre><code>ansible-playbook -i inventory deploy-lamp-stack.yml
</code></pre><ul>
<li>Make sure to replace the <code>host</code> value <code>&lt;YOUR-WEB-HOST-IP&gt;</code> in <code>Create Application DB User</code> task with your specific web host IP address.</li>
</ul>
<h2 id="heading-step-5-access-the-web-application">Step 5: Access the web application</h2>
<ul>
<li>Run the below command to access the web application from CLI:<pre><code>curl &lt;YOUR-WEB-HOST-IP&gt;
</code></pre></li>
<li>Or, copy <code>&lt;YOUR-WEB-HOST-IP&gt;</code> and paste it into a web browser.</li>
</ul>
<p>Thank you for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Monitoring Containers with Prometheus and cAdviser]]></title><description><![CDATA[Introduction
In this blog, we will walk through how to monitor containers using Prometheus and cAdviser.
cAdvisor (short for container Advisor) analyzes and exposes resource usage and performance data from running containers. cAdvisor exposes Prometh...]]></description><link>https://blog.noweder.com/monitoring-containers-with-prometheus-and-cadviser</link><guid isPermaLink="true">https://blog.noweder.com/monitoring-containers-with-prometheus-and-cadviser</guid><category><![CDATA[Docker]]></category><category><![CDATA[Docker compose]]></category><category><![CDATA[#prometheus]]></category><dc:creator><![CDATA[Hamza Noweder]]></dc:creator><pubDate>Fri, 01 Jul 2022 11:00:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1656673187639/zBfW6PWIu.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In this blog, we will walk through how to monitor containers using Prometheus and cAdviser.</p>
<p>cAdvisor (short for container Advisor) analyzes and exposes resource usage and performance data from running containers. cAdvisor exposes Prometheus metrics out of the box. The container metrics will be collected by cAdvisor and scraped by Prometheus.</p>
<h1 id="heading-pre-requisites">Pre-requisites</h1>
<ul>
<li>User with <code>sudo</code> privileges</li>
<li><a target="_blank" href="https://docs.docker.com/engine/install/">Docker</a> installed</li>
</ul>
<h1 id="heading-steps">Steps</h1>
<h2 id="heading-step-1">Step 1</h2>
<p>Configure Prometheus to scrape metrics from cAdvisor. Create a <code>prometheus.yml</code> file and paste below code:</p>
<pre><code><span class="hljs-attribute">scrape_configs</span>:
- <span class="hljs-attribute">job_name</span>: cadvisor
  <span class="hljs-attribute">scrape_interval</span>: <span class="hljs-number">5s</span>
  <span class="hljs-attribute">static_configs</span>:
  - <span class="hljs-attribute">targets</span>:
    - <span class="hljs-attribute">cadvisor</span>:<span class="hljs-number">8080</span>
</code></pre><h2 id="heading-step-2">Step 2</h2>
<p>Now, in the same directory where you have created <code>prometheus.yml</code>, create a<code>docker-compose.yml</code> file defining both prometheus and cAdviser services. Copy and paste below in the file:</p>
<pre><code>version: <span class="hljs-string">'3'</span>
services:
  prometheus:
    image: prom<span class="hljs-operator">/</span>prometheus:latest
    container_name: prometheus
    ports:
      <span class="hljs-operator">-</span> <span class="hljs-number">9090</span>:<span class="hljs-number">9090</span>
    command:
      <span class="hljs-operator">-</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>config.file=<span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>prometheus<span class="hljs-operator">/</span>prometheus.yml
    volumes:
      <span class="hljs-operator">-</span> ./prometheus.yml:<span class="hljs-operator">/</span>etc<span class="hljs-operator">/</span>prometheus<span class="hljs-operator">/</span>prometheus.yml
    depends_on:
      <span class="hljs-operator">-</span> cadvisor

  cadvisor:
    image: google<span class="hljs-operator">/</span>cadvisor:latest
    container_name: cadvisor
    ports:
      <span class="hljs-operator">-</span> <span class="hljs-number">8080</span>:<span class="hljs-number">8080</span>
    volumes:
      <span class="hljs-operator">-</span> <span class="hljs-operator">/</span>:<span class="hljs-operator">/</span>rootfs:ro
      <span class="hljs-operator">-</span> <span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>run:<span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>run:rw
      <span class="hljs-operator">-</span> <span class="hljs-operator">/</span>sys:<span class="hljs-operator">/</span>sys:ro
      <span class="hljs-operator">-</span> <span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>docker:<span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>docker:ro
</code></pre><h2 id="heading-step-3">Step 3</h2>
<p>Run <code>docker-compose up -d</code> to deploy the services and make it running in the background.</p>
<p>To verify the running containers, run <code>docker ps</code>. you can also access prometheus web dashboard on <code>[your-public-ip]:9090</code> and cAdviser on <code>[your-public-ip]:8080</code></p>
]]></content:encoded></item><item><title><![CDATA[Build a Ghost Blog with Docker Compose]]></title><description><![CDATA[In this blog, we will create a Ghost Blog using Docker Compose.

Pre-requisites:

Docker pre-installed

User with sudo access


Steps:
Create a Docker Compose file docker-compose.yml in the root directory. You will create two services: a Ghost Blog s...]]></description><link>https://blog.noweder.com/build-a-ghost-blog-with-docker-compose</link><guid isPermaLink="true">https://blog.noweder.com/build-a-ghost-blog-with-docker-compose</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Hamza Noweder]]></dc:creator><pubDate>Mon, 27 Jun 2022 18:47:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1656673718852/nLMly6nu2.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog, we will create a Ghost Blog using Docker Compose.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656355445943/wic5etJ8-.png" alt="Docker Quest - Challenge 5.png" /></p>
<p><strong>Pre-requisites</strong>:</p>
<ul>
<li><p><a target="_blank" href="https://docs.docker.com/engine/install/">Docker</a> pre-installed</p>
</li>
<li><p>User with <code>sudo</code> access</p>
</li>
</ul>
<p><strong>Steps</strong>:</p>
<p>Create a Docker Compose file <code>docker-compose.yml</code> in the root directory. You will create two services: a Ghost Blog service and a MySQL service. Below is the code:</p>
<pre><code>version: <span class="hljs-string">"3"</span>
services:
  ghost:
    container_name: ghost<span class="hljs-operator">-</span>blog
    image: ghost:<span class="hljs-number">1</span><span class="hljs-operator">-</span>alpine
    restart: always
    environment:
      <span class="hljs-operator">-</span> database__client<span class="hljs-operator">=</span>mysql
      <span class="hljs-operator">-</span> database__connection__host<span class="hljs-operator">=</span>mysql
      <span class="hljs-operator">-</span> database__connection__user<span class="hljs-operator">=</span>root
      <span class="hljs-operator">-</span> database__connection__password<span class="hljs-operator">=</span>P4sSw0rd0<span class="hljs-operator">!</span>
      <span class="hljs-operator">-</span> database__connection__database<span class="hljs-operator">=</span>ghost
    ports:
      <span class="hljs-operator">-</span> <span class="hljs-number">80</span>:<span class="hljs-number">2368</span>
    volumes:
      <span class="hljs-operator">-</span> ghost<span class="hljs-operator">-</span>volume:<span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>ghost
    depends_on:
      <span class="hljs-operator">-</span> mysql

  mysql:
    container_name: ghost<span class="hljs-operator">-</span>db
    image: mysql:<span class="hljs-number">5.7</span>
    restart: always
    environment:
      <span class="hljs-operator">-</span> MYSQL_ROOT_PASSWORD<span class="hljs-operator">=</span>P4sSw0rd0<span class="hljs-operator">!</span>
    volumes:
      <span class="hljs-operator">-</span> mysql<span class="hljs-operator">-</span>volume:<span class="hljs-operator">/</span><span class="hljs-keyword">var</span><span class="hljs-operator">/</span>lib<span class="hljs-operator">/</span>mysql


volumes:
  ghost<span class="hljs-operator">-</span>volume: {}
  mysql<span class="hljs-operator">-</span>volume: {}
</code></pre><p>Run <code>docker-compose up -d</code> to deploy the defined services.</p>
<p>Once completed, you can run <code>docker ps</code> to list the deployed containers as below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656354126496/6-C9WyT3p.png" alt="image.png" /></p>
<p>You can then access the ghost blog from your web browser using your public IP address!</p>
]]></content:encoded></item></channel></rss>