diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e43b0f98 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..02e97f8a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +# Use an official Python runtime as a parent image +# Using a specific image for M1/M2 chip Mac users +ARG ARCH + +FROM python:3.9 +FROM --platform=linux/amd64 public.ecr.aws/docker/library/python:3.9.10-slim-buster + +# Set the working directory in the container +WORKDIR /app + +# Copy the current directory contents into the container at /app +COPY . /app + +# Install system dependencies and ODBC driver +RUN apt-get update && apt-get install -y \ + unixodbc unixodbc-dev odbcinst odbcinst1debian2 libpq-dev gcc && \ + apt-get install -y gnupg && \ + apt-get install -y wget && \ + wget -qO- https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ + wget -qO- https://packages.microsoft.com/config/debian/10/prod.list > /etc/apt/sources.list.d/mssql-release.list && \ + apt-get update && \ + ACCEPT_EULA=Y apt-get install -y msodbcsql18 && \ + # apt-get purge -y --auto-remove wget && \ + apt-get clean + +# Install pip and setuptools +RUN pip install --upgrade pip setuptools + +# Install Python packages specified in requirements.txt +RUN pip install --trusted-host pypi.python.org -r requirements.txt + +# Expose port 5000 (change to your desired port) +EXPOSE 5000 + +# Run app.py when the container launches +CMD ["python", "app.py"] diff --git a/README.md b/README.md index 08407749..e8c3279a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Web-App-DevOps-Project -Welcome to the Web App DevOps Project repo! This application allows you to efficiently manage and track orders for a potential business. It provides an intuitive user interface for viewing existing orders and adding new ones. +Welcome to the Web App DevOps Project repo! This application allows you to efficiently manage and track orders for a potential business. It provides an intuitive user interface for viewing existing orders and adding new ones. This is the finally project for AI core students who are on the DevOps pathway.Although the repo is forked, files have been replaced with my personal project files. ## Table of Contents @@ -53,9 +53,288 @@ To run the application, you simply need to run the `app.py` script in this repos - **Database:** The application employs an Azure SQL Database as its database system to store order-related data. + + +## Terraform Project Documentation + +**Overview** + +Terraform by HashiCorp is an Infrastructure as Code (IaC) tool used for building, changing, and versioning infrastructure safely and efficiently. It supports various cloud providers like Azure, AWS, and Google Cloud. Terraform uses declarative configuration files that describe the desired state of your infrastructure. + +**Key Concepts** + +Infrastructure as Code (IaC): Manage infrastructure using configuration files rather than through manual processes. + +Providers: Plugins that interact with APIs of cloud providers, services, or other tools (e.g., Azure, AWS). +Resources: Components of your infrastructure such as virtual networks, compute instances, or higher-level components like DNS records. +Modules: Reusable, encapsulated Terraform configurations for creating sets of resources that are used together. +State: Terraform records information about what infrastructure is created in a state file, allowing for consistent management of resources. +Terraform Workflow +Initialize: + +Run terraform init in your project directory. +This command sets up Terraform's working directory, downloads providers, and prepares the backend for state storage. +Writing Configuration: + +Define your infrastructure in configuration files (.tf). +Resources, variables, outputs, and provider configurations are declared here. +Planning: + +Execute terraform plan. +Terraform reads configuration files and creates an execution plan, detailing what actions it will perform to reach the desired state. +Applying: + +Run terraform apply. +Terraform will execute the plan to create, update, or delete resources as per the configuration. +Maintaining State: + +Terraform maintains a state file to map resources to the configuration and keep track of metadata. +The state file is critical for Terraform to function correctly and should be handled with care. +Modifying Infrastructure: + +Update configuration files as needed. +Rerun terraform plan and terraform apply to implement changes. +Destroying Infrastructure: + +To remove all resources managed by Terraform, use terraform destroy. + +**Best Practices** + +Version Control: Keep your Terraform configurations in a version control system (like Git). +Modularization: Use modules to organize and reuse code. +Secrets Management: Avoid hardcoding sensitive information. Use environment variables or secret management tools. +Review Plans: Carefully review execution plans before applying changes. + + +## AKS Cluster Provisioning with Terraform + +**Overview** + +This project automates the deployment of an Azure Kubernetes Service (AKS) cluster using Terraform, an Infrastructure as Code tool. The project is structured into modules, with specific focus on the aks-cluster-module for creating and managing the AKS cluster. + +**Prerequisites** + +Azure account and Azure CLI installed and configured +Terraform v0.12+ installed + +**Module Structure** + +aks-cluster-module: Contains Terraform configurations to set up the AKS cluster in Azure. +Usage + +**Step 1: Initialize the Module** + +Navigate to the aks-cluster-module directory and run the initialization command: + +cd modules/aks-cluster-module +terraform init + +**Step 2: Configuration** + +Edit the variables.tf file to configure the desired settings for your AKS cluster. + +**Step 3: Apply the Configuration** + +Run the following commands to apply the configuration: + +terraform plan +terraform apply + +### Inputs and Outputs + +**Input Variables** + +aks_cluster_name: Name of the AKS cluster. +cluster_location: Azure region where the AKS cluster will be deployed. +dns_prefix: DNS prefix for the AKS cluster. +kubernetes_version: Version of Kubernetes for the AKS cluster. +service_principal_client_id: Client ID for the service principal. +service_principal_secret: Client Secret for the service principal. +Additional networking-related variables. + +**Output Variables** + +aks_cluster_name: The name of the provisioned AKS cluster. +aks_cluster_id: The ID of the provisioned AKS cluster. +aks_kubeconfig: Kubernetes configuration file for the AKS cluster. + + +# Infrastructure Provisioning with Terraform + +**Overview** + +This project utilizes Terraform for the automated provisioning of infrastructure in Azure, specifically focusing on setting up an Azure Kubernetes Service (AKS) cluster and associated networking resources. + +**Infrastructure Components** + +Azure Resource Group: Serves as a logical container for grouping related resources. +Virtual Network (VNet): Provides networking for AKS, including control plane and worker node subnets. +Subnets: Two subnets, one for the control plane and one for the worker nodes. +Network Security Group (NSG): Manages network security rules for secure access to the AKS cluster. +AKS Cluster: The central Kubernetes cluster managed by Azure. +Setup and Configuration +Terraform is used to define and manage the above resources. +The configuration is divided into modules for better organization and reusability. +Variables are used to ensure configurability and flexibility of the setup. +Troubleshooting Steps Undertaken +Throughout the setup, several issues were encountered and resolved: + +Service Principal Authentication: Initially faced issues with Azure Service Principal permissions. Resolved by assigning the appropriate roles to the Service Principal. + +Resource Provider Registration: Encountered a MissingSubscriptionRegistration error. This was fixed by manually registering the Microsoft.ContainerService provider with the Azure subscription. + +Permission Issues for Resource Group Creation: Faced AuthorizationFailed errors when attempting to create resource groups. This was resolved by ensuring the Service Principal had sufficient permissions. + +Namespace Registration: Addressed errors related to the subscription not being registered to use certain Azure services (namespaces). + +**Commands Used** + +terraform init: To initialize the Terraform environment. +terraform plan: To preview the changes before applying. +terraform apply: To apply the changes and provision the infrastructure. +Conclusion +The project demonstrates the power of Infrastructure as Code (IaC) using Terraform, showcasing how complex infrastructure can be provisioned, managed, and troubleshooted systematically. + +# Kubernetes Deployment Documentation + +### Deployment and Service Manifests + +This application is deployed on Azure Kubernetes Service (AKS) using Kubernetes manifests. These manifests define the desired state of our application's deployment and service in the cluster. + +### Key Components: +Deployment Manifest: The deployment manifest (deployment.yaml) specifies our application's deployment configuration. It includes the following key settings: + +**Pod Template**: Defines the container image to use (chyjuls/web-delivery:v1) and necessary environment variables. +**Replicas**: Sets the number of pod replicas for high availability. +**Resource Requests/Limits**: Configures CPU and memory resources for each pod. +**Readiness and Liveness Probes**: Ensures that the application is running correctly and is ready to receive traffic. +**Service Manifest**: The service manifest (service.yaml) defines how the application's pods are exposed within the cluster. It includes: +**Type**: Determines how the service is exposed. For internal use, we use ClusterIP; for external access, LoadBalancer can be used. +**Port Mapping**: Maps the port from the pod to the service. + +### Deployment Strategy + +For our application, we've chosen a rolling update deployment strategy. This approach ensures zero downtime during updates, gradually replacing instances of the older version of our application with the new version. + +**Benefits**: +Zero Downtime: Ensures that the application remains available to users during deployment. +Rollback Capabilities: Allows for easy rollback to the previous version if issues arise. +Testing and Validation +Post-deployment, we conducted several tests to ensure the application's functionality and reliability: + +**Connectivity Test**: Verified that the application's pods are accessible and running as expected using kubectl get pods. +**Functionality Test**: Used port forwarding (kubectl port-forward) to temporarily access the application and test its core functionalities, including the orders table and Add Order feature. + +### Internal and External Access + +**Internal Access**: +For internal users, the application can be accessed through an internal load balancer or ingress controller within the AKS cluster. This approach allows employees to access the application without exposing it to the public internet. + +We plan to set up an ingress controller that routes internal traffic to the application based on URL paths. + +**External Access**: + +To make the application accessible to external users, we can expose it through an external load balancer or ingress controller with proper security measures in place. + +**Key considerations for external access include**: +- TLS/SSL Certificates: For secure HTTPS access. +- Authentication and Authorization: To control access to the application. +- Monitoring and Logging: To track usage and potential security incidents. + + + +# CI/CD Pipeline Setup + +## Overview +This project includes a Continuous Integration/Continuous Deployment (CI/CD) pipeline, which automates the process of testing, building, and deploying the application. The pipeline is defined in the azure-pipeline.yaml file and utilizes Azure DevOps for execution. + +### azure-pipeline.yaml** + +The azure-pipeline.yaml file defines the pipeline's stages, jobs, and steps. It is structured as follows: + +**Trigger**: Specifies the branch(es) that will trigger the pipeline. +**Variables**: Defines the variables used across the pipeline. +**Stages**: Organizes the pipeline into distinct stages such as Build, Test, and Deploy. +**Build Stage**: Compiles the code, runs tests, and builds the Docker image. The image is then pushed to a Docker registry. +**Deploy Stage**: Handles the deployment of the built image to the Kubernetes cluster. + +### Kubernetes Manifest File + +The Kubernetes manifest file, located at [path-to-manifest-file], is crucial for the deployment process. It defines the desired state of the application in the Kubernetes cluster. Key components include: + +**Deployment**: Specifies the container image to use, the number of replicas, and configuration like environment variables and resource limits. +**Service**: Defines how the application is exposed within the Kubernetes cluster or to the outside world, like LoadBalancer or NodePort services. + +### CI/CD Pipeline Flow + +**Code Commit**: A commit to the specified branch triggers the pipeline. +**Build**: The application is built, and a Docker image is created. +**Test**: Automated tests are run to ensure code reliability. +**Docker Push**: The Docker image is pushed to the registry. +**Deployment**: The application is deployed to the AKS cluster using the Kubernetes manifest file. +**Post-Deployment**: The pipeline performs any post-deployment steps like health checks or notifications. + +### Conclusion +The CI/CD pipeline ensures that every code change is automatically tested and deployed, maintaining the reliability and stability of the application. This automation streamlines the development process, reduces manual errors, and ensures quicker delivery of features and fixes. + + + +# Monitoring Strategy for AKS Cluster + + +## Metrics Explorer Charts +The AKS cluster monitoring utilizes Azure Monitor's Metrics Explorer to visualize key performance indicators. Below are the specific charts utilized, their significance, and interpretation guidelines: + +1. Average Node CPU Usage + +**Significance**: This chart tracks the CPU usage across all nodes, providing insights into the computational load and identifying potential bottlenecks. +**Metrics Tracked**: CPU utilization percentage. +Interpretation: Values nearing 100% indicate high CPU load, suggesting the need for scaling or optimization. + +2. Average Node Memory Usage + +**Significance**: Monitors memory consumption, crucial for ensuring applications have sufficient resources and for detecting memory leaks. +**Metrics Tracked**: Memory utilization percentage. +Interpretation: High memory usage close to the node capacity may require scaling or investigating potential memory leaks. + +3. Pod Count by Phase + +**Significance**: Offers a snapshot of pod distribution by their lifecycle phase, useful for understanding cluster workload and deployment health. +**Metrics Tracked**: Count of pods in phases like Running, Pending, Failed, etc. +Interpretation: An unusual increase in Pending or Failed pods may indicate issues with scheduling or application errors. + +## Log Analytics + +Azure Log Analytics is used to parse and analyze logs from the AKS cluster. Key logs include: + +1. Node and Pod Logs +**Content**: Include metrics on operations, performance, and errors at both the node and pod levels. +**Relevance**: Helps in diagnosing system-level and application-level issues. + +2. Container Logs +**Content**: Capture stdout and stderr from containers, including application logs. +**Relevance**: Critical for troubleshooting application-specific issues. + + +## Alarm Configurations + +1. CPU Usage Alarm +**Condition**: Triggered when CPU usage exceeds 80% for over 5 minutes. +**Threshold**: >80% CPU utilization. +**Response Strategy**: Investigate running pods and services for optimization or scale up the cluster. + +2. Memory Usage Alarm +**Condition**: Fires when memory usage surpasses 80% for a continuous 5-minute window. +**Threshold**: >80% memory utilization. +**Response Strategy**: Check for memory-intensive applications, consider scaling or optimizing pod configurations. + + + + ## Contributors - [Maya Iuga]([https://github.com/yourusername](https://github.com/maya-a-iuga)) +- C.Ugorji (AI core DevOps Student) ## License diff --git a/aks-terraform/.DS_Store b/aks-terraform/.DS_Store new file mode 100644 index 00000000..91830f70 Binary files /dev/null and b/aks-terraform/.DS_Store differ diff --git a/aks-terraform/.gitignore b/aks-terraform/.gitignore new file mode 100644 index 00000000..de6869f2 --- /dev/null +++ b/aks-terraform/.gitignore @@ -0,0 +1,3 @@ +*.tfstate +*.tfvars +.terraform/ diff --git a/aks-terraform/.terraform.lock.hcl b/aks-terraform/.terraform.lock.hcl new file mode 100644 index 00000000..3124ba0a --- /dev/null +++ b/aks-terraform/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "2.99.0" + constraints = "~> 2.0" + hashes = [ + "h1:/M8yLHqv0uOm9IbHRa4yZvQORr9ir1QyJyIyjGs4ryQ=", + "zh:08d81e72e97351538ab4d15548942217bf0c4d3b79ad3f4c95d8f07f902d2fa6", + "zh:11fdfa4f42d6b6f01371f336fea56f28a1db9e7b490c5ca0b352f6bbca5a27f1", + "zh:12376e2c4b56b76098d5d713d1a4e07e748a926c4d165f0bd6f52157b1f7a7e9", + "zh:31f1cb5b88ed1307625050e3ee7dd9948773f522a3f3bf179195d607de843ea3", + "zh:767971161405d38412662a73ea40a422125cdc214c72fbc569bcfbea6e66c366", + "zh:973c402c3728b68c980ea537319b703c009b902a981b0067fbc64e04a90e434c", + "zh:9ec62a4f82ec1e92bceeff80dd8783f61de0a94665c133f7c7a7a68bda9cdbd6", + "zh:bbb3b7e1229c531c4634338e4fc81b28bce58312eb843a931a4420abe42d5b7e", + "zh:cbbe02cd410d21476b3a081b5fa74b4f1b3d9d79b00214009028d60e859c19a3", + "zh:cc00ecc7617a55543b60a0da1196ea92df48c399bcadbedf04c783e3d47c6e08", + "zh:eecb9fd0e7509c7fd4763e546ef0933f125770cbab2b46152416e23d5ec9dd53", + ] +} diff --git a/aks-terraform/main.tf b/aks-terraform/main.tf new file mode 100644 index 00000000..46e5e087 --- /dev/null +++ b/aks-terraform/main.tf @@ -0,0 +1,46 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 2.0" + } + } +} + + + + +provider "azurerm" { + features {} + + client_id = var.client_id + client_secret = var.client_secret + subscription_id = var.subscription_id + tenant_id = var.tenant_id +} + +module "networking" { + source = "./modules/networking-module" + + resource_group_name = "networking-resource-group" + location = "UK South" + vnet_address_space = ["10.0.0.0/16"] +} + +module "aks_cluster" { + source = "./modules/aks-cluster-module" # Update this path to your AKS cluster module + + cluster_name = "terraform-aks-cluster" + location = "UK South" + dns_prefix = "myaks-project" + kubernetes_version = "1.26.6" + service_principal_client_id = var.client_id + service_principal_secret = var.client_secret + + resource_group_name = module.networking.resource_group_name + vnet_id = module.networking.vnet_id + control_plane_subnet_id = module.networking.control_plane_subnet_id + worker_node_subnet_id = module.networking.worker_node_subnet_id + +} + diff --git a/aks-terraform/modules/.DS_Store b/aks-terraform/modules/.DS_Store new file mode 100644 index 00000000..f66d02c9 Binary files /dev/null and b/aks-terraform/modules/.DS_Store differ diff --git a/aks-terraform/modules/aks-cluster-module/.terraform.lock.hcl b/aks-terraform/modules/aks-cluster-module/.terraform.lock.hcl new file mode 100644 index 00000000..39f75cb6 --- /dev/null +++ b/aks-terraform/modules/aks-cluster-module/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.87.0" + hashes = [ + "h1:PfnDrSQo5bwN6KV1nVj+5MHnNxeD3bglvFahgJctQHY=", + "zh:1547ed020fa6ca25d940b28601442c7e4495fdea9fb1ead7affb867383f5f40b", + "zh:325e6d636b5ab09a24837194647617c9fabd42f0fb2c7e18ae8d2a8b2d890a55", + "zh:3abb0074de1dc3b723f8c209354ba93e717ba52003847484b19369e8f54735f4", + "zh:52d2b1700108d5093113a986aa10757834e48083c1358a2c7d8d0360e2689390", + "zh:5fe377d5cc80e26766ff411dbcb227728709fe34b14ad106c9e374df653086a4", + "zh:747fe80de4fb88b17cac93ff05d62909d3563325c8ed5a461641b48579c328f8", + "zh:b40142e4041b7f000ab2dda58309755395a4018d5d00218f6a601f737389865a", + "zh:bca622818c221cec81d636879e376c15696a8d091703298195d728b3c1eae7db", + "zh:bfaecd203137ff9eb3228b1cbd3191e1d84d7c019855eb5f3071bbf6eb060a51", + "zh:d197f04b54f2be07f827ced220954d723039c84793a4ce91894b622982c25811", + "zh:e831601ea1f67c5e745946ed3ac0cac772ed8e95ca7d7314d3f0ed631e6eefb1", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/aks-terraform/modules/aks-cluster-module/main.tf b/aks-terraform/modules/aks-cluster-module/main.tf new file mode 100644 index 00000000..f04363ed --- /dev/null +++ b/aks-terraform/modules/aks-cluster-module/main.tf @@ -0,0 +1,31 @@ +resource "azurerm_kubernetes_cluster" "aks_cluster" { + name = var.cluster_name + location = var.location + resource_group_name = var.resource_group_name # Reference the variable here + dns_prefix = var.dns_prefix + kubernetes_version = var.kubernetes_version + + default_node_pool { + name = "default" + vm_size = "Standard_DS2_v2" + enable_auto_scaling = true + min_count = 1 + max_count = 5 +} + + + service_principal { + client_id = var.service_principal_client_id + client_secret = var.service_principal_secret + } + + network_profile { + network_plugin = "azure" + network_policy = "calico" + } + + tags = { + Environment = "Development" + } +} + diff --git a/aks-terraform/modules/aks-cluster-module/outputs.tf b/aks-terraform/modules/aks-cluster-module/outputs.tf new file mode 100644 index 00000000..7b01afff --- /dev/null +++ b/aks-terraform/modules/aks-cluster-module/outputs.tf @@ -0,0 +1,15 @@ +output "cluster_name" { + value = azurerm_kubernetes_cluster.aks_cluster.name + description = "The name of the provisioned AKS cluster." +} + +output "aks_cluster_id" { + value = azurerm_kubernetes_cluster.aks_cluster.id + description = "The ID of the provisioned AKS cluster." +} + +output "aks_kubeconfig" { + value = azurerm_kubernetes_cluster.aks_cluster.kube_config_raw + description = "The Kubernetes configuration file for managing the AKS cluster with kubectl." +} + diff --git a/aks-terraform/modules/aks-cluster-module/variables.tf b/aks-terraform/modules/aks-cluster-module/variables.tf new file mode 100644 index 00000000..5497e311 --- /dev/null +++ b/aks-terraform/modules/aks-cluster-module/variables.tf @@ -0,0 +1,54 @@ +variable "cluster_name" { + type = string + description = "The name of the AKS cluster." +} + +variable "location" { + type = string + description = "The Azure region where the AKS cluster will be deployed." +} + +variable "dns_prefix" { + type = string + description = "The DNS prefix for the AKS cluster." +} + +variable "kubernetes_version" { + type = string + description = "The specific version of Kubernetes to use for the AKS cluster." +} + +variable "service_principal_client_id" { + type = string + description = "The Client ID for the service principal associated with the AKS cluster." +} + +variable "service_principal_secret" { + type = string + description = "The Client Secret for the service principal associated with the AKS cluster." +} + +# Variables from networking module +variable "resource_group_name" { + type = string + description = "The name of the Azure Resource Group for networking resources." +} + +variable "vnet_id" { + type = string + description = "The ID of the Virtual Network where the AKS cluster is deployed." +} + +variable "control_plane_subnet_id" { + type = string + description = "The ID of the subnet for the AKS cluster control plane." +} + +variable "worker_node_subnet_id" { + type = string + description = "The ID of the subnet for the AKS cluster worker nodes." +} + + + + diff --git a/aks-terraform/modules/networking-module/.terraform.lock.hcl b/aks-terraform/modules/networking-module/.terraform.lock.hcl new file mode 100644 index 00000000..39f75cb6 --- /dev/null +++ b/aks-terraform/modules/networking-module/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.87.0" + hashes = [ + "h1:PfnDrSQo5bwN6KV1nVj+5MHnNxeD3bglvFahgJctQHY=", + "zh:1547ed020fa6ca25d940b28601442c7e4495fdea9fb1ead7affb867383f5f40b", + "zh:325e6d636b5ab09a24837194647617c9fabd42f0fb2c7e18ae8d2a8b2d890a55", + "zh:3abb0074de1dc3b723f8c209354ba93e717ba52003847484b19369e8f54735f4", + "zh:52d2b1700108d5093113a986aa10757834e48083c1358a2c7d8d0360e2689390", + "zh:5fe377d5cc80e26766ff411dbcb227728709fe34b14ad106c9e374df653086a4", + "zh:747fe80de4fb88b17cac93ff05d62909d3563325c8ed5a461641b48579c328f8", + "zh:b40142e4041b7f000ab2dda58309755395a4018d5d00218f6a601f737389865a", + "zh:bca622818c221cec81d636879e376c15696a8d091703298195d728b3c1eae7db", + "zh:bfaecd203137ff9eb3228b1cbd3191e1d84d7c019855eb5f3071bbf6eb060a51", + "zh:d197f04b54f2be07f827ced220954d723039c84793a4ce91894b622982c25811", + "zh:e831601ea1f67c5e745946ed3ac0cac772ed8e95ca7d7314d3f0ed631e6eefb1", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/aks-terraform/modules/networking-module/main.tf b/aks-terraform/modules/networking-module/main.tf new file mode 100644 index 00000000..f7a78e13 --- /dev/null +++ b/aks-terraform/modules/networking-module/main.tf @@ -0,0 +1,63 @@ +resource "azurerm_resource_group" "aks_rg" { + name = var.resource_group_name + location = var.location +} + +resource "azurerm_virtual_network" "aks_vnet" { + name = "aks-vnet" + address_space = var.vnet_address_space + location = var.location + resource_group_name = azurerm_resource_group.aks_rg.name +} + +resource "azurerm_subnet" "control_plane_subnet" { + name = "control-plane-subnet" + resource_group_name = azurerm_resource_group.aks_rg.name + virtual_network_name = azurerm_virtual_network.aks_vnet.name + address_prefixes = ["10.0.1.0/24"] +} +resource "azurerm_subnet" "worker_node_subnet" { + name = "worker-node-subnet" + resource_group_name = azurerm_resource_group.aks_rg.name + virtual_network_name = azurerm_virtual_network.aks_vnet.name + address_prefixes = ["10.0.2.0/24"] +} + +resource "azurerm_network_security_group" "aks_nsg" { + name = "aks-nsg" + location = var.location + resource_group_name = azurerm_resource_group.aks_rg.name +} + +# Inbound rules for kube_apiserver + +resource "azurerm_network_security_rule" "kube_apiserver_rule" { + name = "kube-apiserver-rule" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "443" # Standard port for kube-apiserver + source_address_prefix = "0.0.0.0/0" # WARNING allows all traffic ...use only for testing! + destination_address_prefix = "*" + resource_group_name = azurerm_resource_group.aks_rg.name + network_security_group_name = azurerm_network_security_group.aks_nsg.name +} + +# inbound rules SSH + +resource "azurerm_network_security_rule" "ssh_rule" { + name = "ssh-rule" + priority = 101 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "22" # Standard port for SSH + source_address_prefix = "0.0.0.0/0" # WARNING: Allows all traffic, please use only for testing! + destination_address_prefix = "*" + resource_group_name = azurerm_resource_group.aks_rg.name + network_security_group_name = azurerm_network_security_group.aks_nsg.name +} + diff --git a/aks-terraform/modules/networking-module/outputs.tf b/aks-terraform/modules/networking-module/outputs.tf new file mode 100644 index 00000000..1d9fd79c --- /dev/null +++ b/aks-terraform/modules/networking-module/outputs.tf @@ -0,0 +1,24 @@ +output "vnet_id" { + value = azurerm_virtual_network.aks_vnet.id + description = "The ID of the Virtual Network." +} + +output "control_plane_subnet_id" { + value = azurerm_subnet.control_plane_subnet.id + description = "The ID of the control plane subnet within the VNet." +} + +output "worker_node_subnet_id" { + value = azurerm_subnet.worker_node_subnet.id + description = "The ID of the worker node subnet within the VNet." +} + +output "resource_group_name" { + value = azurerm_resource_group.aks_rg.name + description = "The name of the Azure Resource Group where the networking resources were provisioned." +} +output "aks_nsg_id" { + value = azurerm_network_security_group.aks_nsg.id + description = "The ID of the Network Security Group." +} + diff --git a/aks-terraform/modules/networking-module/variables.tf b/aks-terraform/modules/networking-module/variables.tf new file mode 100644 index 00000000..e81d1a70 --- /dev/null +++ b/aks-terraform/modules/networking-module/variables.tf @@ -0,0 +1,18 @@ +variable "resource_group_name" { + type = string + description = "The name of the Azure Resource Group for networking resources." + default = "default-rg" +} + +variable "location" { + type = string + description = "The Azure region where the networking resources will be deployed." + default = "UK South" +} + +variable "vnet_address_space" { + type = list(string) + description = "The address space for the Virtual Network." + default = ["10.0.0.0/16"] +} + diff --git a/aks-terraform/terraform.tfstate.backup b/aks-terraform/terraform.tfstate.backup new file mode 100644 index 00000000..a68a407b --- /dev/null +++ b/aks-terraform/terraform.tfstate.backup @@ -0,0 +1,9 @@ +{ + "version": 4, + "terraform_version": "1.6.6", + "serial": 49, + "lineage": "9fa196a3-ac0b-6f73-b86d-6aea533f1be0", + "outputs": {}, + "resources": [], + "check_results": null +} diff --git a/aks-terraform/variables.tf b/aks-terraform/variables.tf new file mode 100644 index 00000000..36054580 --- /dev/null +++ b/aks-terraform/variables.tf @@ -0,0 +1,18 @@ +variable "client_id" { + description = "The Client ID for the Azure Service Principal" +} + +variable "client_secret" { + description = "The Client Secret for the Azure Service Principal" +} + + + +variable "subscription_id" { + description = "The Azure Subscription ID" + +} +variable "tenant_id" { + description = " The tenant ID for the Azure Service Principal" + +} diff --git a/app.py b/app.py index 50f4e29d..1364626b 100644 --- a/app.py +++ b/app.py @@ -5,19 +5,32 @@ from sqlalchemy import create_engine import pyodbc import os +from azure.identity import DefaultAzureCredential +from azure.keyvault.secrets import SecretClient + # Initialise Flask App app = Flask(__name__) # database connection -server = 'devops-project-server.database.windows.net' -database = 'orders-db' -username = 'maya' -password = 'AiCore1237' -driver= '{ODBC Driver 18 for SQL Server}' + +# Azure Key Vault settings + + +kv_uri = "https://chy01245vault.vault.azure.net/" + +credential = DefaultAzureCredential() +client = SecretClient(vault_url=kv_uri, credential=credential) + +# Retrieve secrets from Azure Key Vault +server = client.get_secret("server-name").value +database = client.get_secret("database-name").value +username = client.get_secret("username").value +password = client.get_secret("database-password").value +driver = '{ODBC Driver 18 for SQL Server}' # Create the connection string -connection_string=f'Driver={driver};\ +connection_string = f'Driver={driver};\ Server=tcp:{server},1433;\ Database={database};\ Uid={username};\ @@ -36,6 +49,7 @@ # Define the Order data model Base = declarative_base() + class Order(Base): __tablename__ = 'orders' date_uuid = Column('date_uuid', String, primary_key=True) @@ -47,11 +61,11 @@ class Order(Base): order_date = Column('Order Date', DateTime) shipping_date = Column('Shipping Date', DateTime) + # define routes -# route to display orders +# to display orders @app.route('/') def display_orders(): - page = int(request.args.get('page', 1)) rows_per_page = 25 @@ -63,7 +77,8 @@ def display_orders(): session = Session() # Fetch a subset of data for the current page - current_page_orders = session.query(Order).order_by(Order.user_id, Order.date_uuid).slice(start_index, end_index).all() + current_page_orders = session.query(Order).order_by(Order.user_id, Order.date_uuid).slice(start_index, + end_index).all() # Calculate the total number of pages total_rows = session.query(Order).count() @@ -74,6 +89,7 @@ def display_orders(): return render_template('orders.html', orders=current_page_orders, page=page, total_pages=total_pages) + # route to add orders @app.route('/add_order', methods=['POST']) def add_order(): @@ -85,7 +101,7 @@ def add_order(): product_quantity = request.form.get('product_quantity') order_date = request.form.get('order_date') shipping_date = request.form.get('shipping_date') - + # Create a session to interact with the database session = Session() @@ -107,6 +123,7 @@ def add_order(): return redirect(url_for('display_orders')) + # run the app if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/application-manifest.yaml b/application-manifest.yaml new file mode 100644 index 00000000..2e7be5ab --- /dev/null +++ b/application-manifest.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: flask-app-deployment +spec: + replicas: 2 + selector: + matchLabels: + app: flask-app + template: + metadata: + labels: + app: flask-app + spec: + containers: + - name: flask-app + image: chyjuls/web-delivery:latest + ports: + - containerPort: 5000 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: flask-app-service +spec: + selector: + app: flask-app + ports: + - protocol: TCP + port: 80 + targetPort: 5000 + type: ClusterIP diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000..adb8736b --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,31 @@ +# Starter pipeline +# Start with a minimal pipeline that you can customize to build and deploy your code. +# Add steps that build, run tests, deploy, and more: +# https://aka.ms/yaml + +trigger: +- main + +pool: + vmImage: ubuntu-latest + +steps: +- task: Docker@2 + displayName: 'Build and Push Docker Image' + inputs: + containerRegistry: 'Docker Hub' + repository: 'chyjuls/web-delivery' + command: 'buildAndPush' + Dockerfile: '**/Dockerfile' + tags: 'latest' + + +- task: KubernetesManifest@1 + inputs: + action: 'deploy' + connectionType: 'azureResourceManager' + azureSubscriptionConnection: 'Chinyere Ugorji DevOps(9b6d204f-381b-4e09-8ce3-b10cfc7a5a4f)' + azureResourceGroup: 'networking-resource-group' + kubernetesCluster: 'terraform-aks-cluster' + manifests: 'application-manifest.yaml' + diff --git a/deployment.yaml b/deployment.yaml new file mode 100644 index 00000000..7e9549f2 --- /dev/null +++ b/deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: flask-app-deployment +spec: + replicas: 2 # Adjust the number of replicas as needed + selector: + matchLabels: + app: flask-app + template: + metadata: + labels: + app: flask-app + spec: + containers: + - name: flask-app-container + image: chyjuls/web-delivery:latest + ports: + - containerPort: 5000 # Expose the same port specified in your Dockerfile + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + +--- +apiVersion: v1 +kind: Service +metadata: + name: flask-app-services +spec: + selector: + app: flask-app + ports: + - protocol: TCP + port: 80 # Port for internal communication within the cluster + targetPort: 5000 # Port exposed by your container + type: ClusterIP # Internal service + + diff --git a/requirements.txt b/requirements.txt index 47ec762e..067f6f64 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +azure-identity +azure-keyvault-secrets flask==2.2.2 pyodbc==4.0.39 SQLAlchemy==2.0.21