diff --git a/README.md b/README.md index 777cf9a..135dfb3 100644 --- a/README.md +++ b/README.md @@ -36,5 +36,6 @@ | 07 | Create free VPN server on AWS | [terraform-free-vpn-on-aws](./projects/terraform-free-vpn-on-aws/) | ✔️ Done | | 08 | Provision, deploy, monitor application on AKS | [terraform-aks-cluster](./projects/terraform-aks-cluster/) | ✔️ Done | | 09 | Deploy application on AKS with Istio service mesh | [aks-istio-application](./projects/aks-istio-application/) | ✔️ Done | +| 10 | Nginx ingress with Istio service mesh on AKS | [nginx-with-istio-on-aks](./projects/nginx-with-istio-on-aks/) | ✔️ Done | ### Explore our upcoming projects by visiting [this link](https://github.com/tungbq/devops-project/issues?q=is%3Aissue+is%3Aopen+label%3Aproject) ⏩ diff --git a/docs/troubleshooting/common_issue.md b/docs/troubleshooting/common_issue.md new file mode 100644 index 0000000..ecf6684 --- /dev/null +++ b/docs/troubleshooting/common_issue.md @@ -0,0 +1,3 @@ +# Common issues + +- nginx-returns-426: https://stackoverflow.com/questions/69231743/nginx-returns-426 diff --git a/docs/troubleshooting/k8s_issue.md b/docs/troubleshooting/k8s_issue.md new file mode 100644 index 0000000..13d924c --- /dev/null +++ b/docs/troubleshooting/k8s_issue.md @@ -0,0 +1,3 @@ +# How to Debug CrashLoopBackOff in a Container or Pod? +Step 1: Using kubectl describe pod to get Pod details +Step 2: Using kubectl logs to get Pod logs diff --git a/projects/aks-istio-application/README.md b/projects/aks-istio-application/README.md index 5711e65..66b4c45 100644 --- a/projects/aks-istio-application/README.md +++ b/projects/aks-istio-application/README.md @@ -36,9 +36,9 @@ variable "vm_size" { ```bash # Change to yours -export CLUSTER="cluster-boss-moth" +export CLUSTER="cluster-golden-sturgeon" # Change to yours -export RESOURCE_GROUP="rg-just-gar" +export RESOURCE_GROUP="rg-genuine-longhorn" export DEVOPS_PROJECT_PATH="/mnt/d/CODING/GITHUB/OPEN-SOURCE/my-project/devops-project" # replace by yours export KUBECONFIG=$DEVOPS_PROJECT_PATH/projects/terraform-aks-cluster/private_k8s_config/azurek8s kubectl get nodes @@ -67,7 +67,7 @@ kubectl label namespace aks-istio-system istio.io/rev=asm-1-20 --overwrite **NOTE:** -- It will take few minutes to enable istio adon to your cluster +- It will take few minutes (~ 4m) to enable istio adon to your cluster - The istio will be installed in `aks-istio-system` namespace ## 4-Deploy microservices sample app diff --git a/projects/aks-istio-application/scripts/frontend_load_testing.sh b/projects/aks-istio-application/scripts/frontend_load_testing.sh index d3f7c23..8a9b17f 100755 --- a/projects/aks-istio-application/scripts/frontend_load_testing.sh +++ b/projects/aks-istio-application/scripts/frontend_load_testing.sh @@ -1,13 +1,43 @@ #!/bin/bash -PAGE_URL=$1 -# To get page, run: `kubectl get svc aks-istio-ingressgateway-external -n aks-istio-ingress` -## Usage: ./frontend_load_testing.sh 1.2.3.4 - -echo "Testing frontend page workload..." -for i in $(seq 1 1000); do - echo "Try number $i..." - curl -s -o /dev/null "$PAGE_URL" - sleep 1 -done -echo "Testing frontend page completed!" +# Set 'set -e' to exit immediately if any command fails +set -e + +# Function to display script usage +usage() { + echo "Usage: $0 [sleep_time]" + echo "Example: $0 http://example.com 1" + exit 1 +} + +# Check for required arguments +if [ $# -lt 1 ]; then + usage +fi + +# Assign command-line arguments to variables +PAGE_URL="$1" +SLEEP_TIME="${2:-1}" # Default sleep time is 1 second + +# Main function to perform load testing +perform_load_testing() { + echo "Testing frontend page workload..." + echo "Sending curl command to $PAGE_URL..." + for ((i = 1; i <= 1000; i++)); do + echo "Try number $i..." + if ! curl -s -o /dev/null "$PAGE_URL"; then + echo "Failed to fetch page: $PAGE_URL" + exit 1 + fi + sleep "$SLEEP_TIME" + done + echo "Testing frontend page completed!" +} + +# Prompt for sleep time if not provided +if [ -z "$2" ]; then + read -rp "Enter sleep time (default is 1 second): " SLEEP_TIME +fi + +# Call the main function +perform_load_testing diff --git a/projects/nginx-with-istio-on-aks/README.md b/projects/nginx-with-istio-on-aks/README.md new file mode 100644 index 0000000..a3e54a7 --- /dev/null +++ b/projects/nginx-with-istio-on-aks/README.md @@ -0,0 +1,103 @@ +# Nginx Ingress with Istio service mesh + +## Architecture + +- Refer: https://docs.nginx.com/nginx-ingress-controller/tutorials/nginx-ingress-istio/ +- Nginx Ingress is implemented as an entry point to an Istio service mesh + +## Steps + +### 0-Prepare environment + +```bash +export DEVOPS_PROJECT_PATH="/mnt/d/CODING/GITHUB/OPEN-SOURCE/my-project/devops-project" +``` + +### 1-Deploy fresh cluster + +- Check [terraform-aks-cluster](../terraform-aks-cluster/) + +### 2-Enable istio + +- Istio (aks-istio-system ns) and deploy app (default ns) +- Check [aks-istio-application](../aks-istio-application/) - Only step #1->#4 (skip step #5 and later) +- Enable side car enjection for nginx + +```bash +kubectl create ns nginx-ingress +# Check current istio version on AKS +az aks show --resource-group ${RESOURCE_GROUP} --name ${CLUSTER} | grep asm +kubectl label namespace nginx-ingress istio.io/rev=asm-1-20 --overwrite + +# Check labels +kubectl get namespaces -A --show-labels +``` + +### 3-Nginx deploy + +- Deploy + +```bash +kubectl apply -f $DEVOPS_PROJECT_PATH/projects/nginx-with-istio-on-aks/k8s_manifest/nginx_deployment.yaml +``` + +- Get the external nginx IP + +```bash +kubectl get svc | grep nginx +# Check the EXTERNAL-IP +``` + +- Access + - Open browser then check the webpage `http://` + +### 4-Monitor the application with Prometheus & Grafana + +- Deploy Prometheus/Grafana, see: [aks-istio-application](../aks-istio-application/) - step #6 + - Expose the port with port-forward then visit Grafana to get application insights + - Import the Istio dashboards to Grafana +- Perform load testing to the frontend main page, by running: + +```bash +# EXTERNAL_IP get from nginx service +$DEVOPS_PROJECT_PATH/projects/aks-istio-application/scripts/frontend_load_testing.sh "$EXTERNAL_IP" "0.2" + +# 0.2 is the sleep time between the curl commands, to do more load, reduce that value like 0.1 +``` + +## 5-Result + +Once completed, you could archive the results like below: + +- Frontend webpage of the application + ![front-end-nginx-based](./assets/front-end-nginx-based.png) + +- Grafana Istio Service Dashboard for application insights + ![grafana-istio-nginx-frontend-monitor](./assets/grafana-istio-nginx-frontend-monitor.png) + +If you could reach to this point, congratulations for your effort! Happy coding! + +## 6-Cleanup + +- Once done, destroy the cluster to free up resource and avoid upcomming Azure cost ($$$) +- Follow the `Delete AKS resources` section of project [terraform-aks-cluster](../terraform-aks-cluster/) + +## Summary + +To use Nginx ingress to serve the external request and then forward to the application inside Istio service mesh, we could: + +- Enable the service mesh as usual +- Deploy the application as previous project +- (\*)Enable Istio side-car injection feature to the namespace that we deploy the `nginx` service/ingress +- (\*)Create `nginx.conf` file to route the traffic to frontend service +- Deploy nginx as usual, then get the `External-IP` from nginx service +- Visit the `External-IP` page then enjoy the result + +The (\*) is the most difference steps in this project + +## Document + +- https://docs.nginx.com/nginx-ingress-controller/tutorials/nginx-ingress-istio/ +- https://platform9.com/learn/v1.0/tutorials/nginix-controller-via-yaml +- https://gist.github.com/petitviolet/d36f33d145d0bbf4b54eb187b79d0244 +- https://cloud.google.com/endpoints/docs/openapi/custom-nginx diff --git a/projects/nginx-with-istio-on-aks/assets/front-end-nginx-based.png b/projects/nginx-with-istio-on-aks/assets/front-end-nginx-based.png new file mode 100755 index 0000000..f2ea454 Binary files /dev/null and b/projects/nginx-with-istio-on-aks/assets/front-end-nginx-based.png differ diff --git a/projects/nginx-with-istio-on-aks/assets/grafana-istio-nginx-frontend-monitor.png b/projects/nginx-with-istio-on-aks/assets/grafana-istio-nginx-frontend-monitor.png new file mode 100755 index 0000000..43ba50e Binary files /dev/null and b/projects/nginx-with-istio-on-aks/assets/grafana-istio-nginx-frontend-monitor.png differ diff --git a/projects/nginx-with-istio-on-aks/k8s_manifest/nginx-deployment.yaml b/projects/nginx-with-istio-on-aks/k8s_manifest/nginx-deployment.yaml new file mode 100644 index 0000000..680e07d --- /dev/null +++ b/projects/nginx-with-istio-on-aks/k8s_manifest/nginx-deployment.yaml @@ -0,0 +1,70 @@ +# full-setup.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-config +data: + nginx.conf: | + events {} + + http { + server { + listen 80; + + location / { + proxy_pass http://frontend.default.svc.cluster.local; # Added http:// protocol prefix + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + } + } + } +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-ingress +spec: + selector: + app: nginx-ingress + ports: + - protocol: TCP + port: 80 + targetPort: 80 + type: LoadBalancer # Expose the service using a LoadBalancer +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-ingress +spec: + replicas: 1 + selector: + matchLabels: + app: nginx-ingress + template: + metadata: + labels: + app: nginx-ingress + annotations: + traffic.sidecar.istio.io/includeInboundPorts: "" + traffic.sidecar.istio.io/excludeInboundPorts: "80,443" + traffic.sidecar.istio.io/excludeOutboundIPRanges: "10.0.0.1/32" + sidecar.istio.io/inject: "true" + spec: + containers: + - name: nginx + image: nginx:1.25.4 + ports: + - containerPort: 80 + volumeMounts: + - name: nginx-config + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + readOnly: true + volumes: + - name: nginx-config + configMap: + name: nginx-config