OCI Service Mesh with OKE: mTLS, Traffic Splitting, and Ingress Gateway

When you have more than a handful of microservices communicating with each other, you start adding the same things to every service: TLS for encryption, retry logic, circuit breaking, and latency metrics. These are infrastructure concerns, not application concerns. OCI Service Mesh is Oracle’s managed Envoy-based service mesh that provides mutual TLS between services without code changes, traffic management for canary deployments, and observability through metrics and distributed tracing.

Step 1: Mesh and Certificate Authority

resource "oci_identity_policy" "service_mesh_policy" {
  compartment_id = var.compartment_id
  name           = "service-mesh-policy"
  statements = [
    "Allow any-user to manage service-mesh-family in compartment id ${var.compartment_id} where request.principal.type = 'cluster'",
    "Allow any-user to manage certificate-authority-family in compartment id ${var.compartment_id} where request.principal.type = 'cluster'",
    "Allow group ${var.platform_group} to manage service-mesh-family in compartment id ${var.compartment_id}"
  ]
}

resource "oci_certificates_management_certificate_authority" "mesh_ca" {
  compartment_id = var.compartment_id
  name           = "service-mesh-ca"
  kms_key_id     = var.vault_key_id
  certificate_authority_config {
    config_type = "ROOT_CA_GENERATED_INTERNALLY"
    subject {
      common_name  = "production-mesh-ca"
      organization = "OsamaOracle"
    }
    signing_algorithm = "SHA512_WITH_RSA"
  }
}

resource "oci_service_mesh_mesh" "production" {
  compartment_id = var.compartment_id
  display_name   = "production-mesh"

  certificate_authorities {
    id = oci_certificates_management_certificate_authority.mesh_ca.id
  }

  mtls {
    minimum = "PERMISSIVE"  # Start permissive, move to STRICT after all services are enrolled
  }

  defined_tags = {
    "Operations.Environment" = "production"
    "Operations.ManagedBy"   = "terraform"
  }
}

output "mesh_id" { value = oci_service_mesh_mesh.production.id }

Step 2: Virtual Services and Deployments

resource "oci_service_mesh_virtual_service" "orders_api" {
  compartment_id = var.compartment_id
  mesh_id        = oci_service_mesh_mesh.production.id
  name           = "orders-api"
  hosts          = ["orders-api"]

  default_routing_policy { type = "UNIFORM" }
  mtls { mode = "STRICT" }
}

# Stable version
resource "oci_service_mesh_virtual_deployment" "orders_api_v1" {
  compartment_id     = var.compartment_id
  virtual_service_id = oci_service_mesh_virtual_service.orders_api.id
  name               = "orders-api-v1"

  service_discovery {
    type     = "DNS"
    hostname = "orders-api-v1.orders.svc.cluster.local"
  }
  listeners { protocol = "HTTP"; port = 8080 }
}

# Canary version
resource "oci_service_mesh_virtual_deployment" "orders_api_v2" {
  compartment_id     = var.compartment_id
  virtual_service_id = oci_service_mesh_virtual_service.orders_api.id
  name               = "orders-api-v2"

  service_discovery {
    type     = "DNS"
    hostname = "orders-api-v2.orders.svc.cluster.local"
  }
  listeners { protocol = "HTTP"; port = 8080 }
}

Step 3: Canary Traffic Splitting

resource "oci_service_mesh_virtual_service_route_table" "orders_routes" {
  compartment_id     = var.compartment_id
  virtual_service_id = oci_service_mesh_virtual_service.orders_api.id
  name               = "orders-api-canary-split"
  priority           = 1

  route_rules {
    type      = "HTTP"
    is_grpc   = false
    path      = "/"
    path_type = "PREFIX"

    destinations {
      virtual_deployment_id = oci_service_mesh_virtual_deployment.orders_api_v1.id
      port   = 8080
      weight = 90  # 90% to stable
    }

    destinations {
      virtual_deployment_id = oci_service_mesh_virtual_deployment.orders_api_v2.id
      port   = 8080
      weight = 10  # 10% canary
    }
  }
}

Step 4: Ingress Gateway

resource "oci_service_mesh_ingress_gateway" "production" {
  compartment_id = var.compartment_id
  mesh_id        = oci_service_mesh_mesh.production.id
  name           = "production-ingress-gateway"

  hosts {
    name      = "api.yourdomain.com"
    hostnames = ["api.yourdomain.com"]

    listeners {
      protocol = "HTTPS"
      port     = 443
      tls {
        mode = "TLS"
        server_certificate {
          type           = "OCI_CERTIFICATES"
          certificate_id = var.tls_certificate_id
        }
      }
    }
  }

  access_logging { is_enabled = true }
}

resource "oci_service_mesh_ingress_gateway_route_table" "orders" {
  compartment_id     = var.compartment_id
  ingress_gateway_id = oci_service_mesh_ingress_gateway.production.id
  name               = "orders-api-routes"
  priority           = 1

  route_rules {
    type = "HTTP"
    ingress_gateway_host { name = "api.yourdomain.com"; port = 443 }
    destinations {
      virtual_service_id = oci_service_mesh_virtual_service.orders_api.id
      port               = 8080
    }
  }
}

Step 5: Enable Sidecar Injection on OKE

apiVersion: v1
kind: Namespace
metadata:
  name: orders
  labels:
    oci.oraclecloud.com/injection: enabled
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders-api-v1
  namespace: orders
spec:
  template:
    metadata:
      annotations:
        servicemesh.oci.oraclecloud.com/virtual-deployment-ocid: "ocid1.meshvirtualdeployment.oc1..your-vd-ocid"
    spec:
      containers:
        - name: orders-api
          image: me-jeddah-1.ocir.io/namespace/orders-api:1.2.3
          ports:
            - containerPort: 8080

Step 6: Access Policy for Service-to-Service mTLS

resource "oci_service_mesh_access_policy" "orders_to_inventory" {
  compartment_id = var.compartment_id
  mesh_id        = oci_service_mesh_mesh.production.id
  name           = "orders-to-inventory"

  rules {
    action = "ALLOW"
    source {
      type               = "VIRTUAL_SERVICE"
      virtual_service_id = oci_service_mesh_virtual_service.orders_api.id
    }
    destination {
      type               = "VIRTUAL_SERVICE"
      virtual_service_id = oci_service_mesh_virtual_service.inventory_api.id
    }
  }
}

# All other service-to-service communication is denied by default
# Only explicitly declared access policies are permitted

Operational Notes

Start the mesh in PERMISSIVE mTLS mode. Permissive mode accepts both mTLS and plain text traffic, letting you validate all services are correctly enrolled before enforcing encryption. Switch to STRICT only after every service-to-service call is confirmed to go through the mesh proxy. Switching to STRICT prematurely breaks any service still communicating over plain text.

Traffic weight changes take effect within seconds of a Terraform apply. For a production canary rollout, start at 5 percent, monitor error rate and latency for 30 minutes, increase to 10 then 25 then 50 then 100. If any step shows an error rate increase, set canary weight to 0 and apply. The rollback is as fast as the promotion.

Regards,
Osama

#OCI #OracleCloud #ServiceMesh #OKE #Kubernetes #mTLS #CloudNative #Terraform #TechBlog #Oracle #CanaryDeployment #TrafficManagement #Envoy #Microservices #OracleCloudInfrastructure #IaC #PlatformEngineering #DevSecOps #Observability #ZeroTrust

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.