OCI Developer Associate - DevOps and Monitoring¶
Table of Contents¶
- DevOps Service
- Resource Manager
- Application Performance Monitoring
- Logging and Observability
- Exam Tips
DevOps Service¶
DevOps Project Setup¶
Creating DevOps Project:
resource "oci_devops_project" "main" {
compartment_id = var.compartment_id
name = "my-application"
description = "DevOps project for My Application"
notification_config {
topic_id = oci_ons_notification_topic.devops.id
}
}
Code Repository¶
Creating Repository:
# Create repository
oci devops repository create \
--project-id ocid1.devopsproject... \
--name "my-app-repo" \
--repository-type HOSTED
# Clone repository
git clone https://devops.scmservice.us-phoenix-1.oci.oraclecloud.com/namespaces/namespace/projects/project/repositories/my-app-repo
# Add code and push
git add .
git commit -m "Initial commit"
git push origin main
Build Pipeline¶
Build Spec (build_spec.yaml):
version: 0.1
component: build
timeoutInSeconds: 600
env:
variables:
APP_NAME: "my-app"
DOCKER_REGISTRY: "phx.ocir.io"
exportedVariables:
- IMAGE_TAG
steps:
- type: Command
name: "Install dependencies"
command: |
npm install
- type: Command
name: "Run tests"
command: |
npm test
- type: Command
name: "Build application"
command: |
npm run build
- type: Command
name: "Build Docker image"
command: |
export IMAGE_TAG=$(git rev-parse --short HEAD)
docker build -t ${DOCKER_REGISTRY}/${TENANCY_NAMESPACE}/${APP_NAME}:${IMAGE_TAG} .
- type: Command
name: "Push to OCIR"
command: |
docker push ${DOCKER_REGISTRY}/${TENANCY_NAMESPACE}/${APP_NAME}:${IMAGE_TAG}
outputArtifacts:
- name: app_image
type: DOCKER_IMAGE
location: ${DOCKER_REGISTRY}/${TENANCY_NAMESPACE}/${APP_NAME}:${IMAGE_TAG}
- name: kubernetes_manifest
type: KUBERNETES_MANIFEST
location: k8s/deployment.yaml
Build Pipeline Configuration:
resource "oci_devops_build_pipeline" "main" {
project_id = oci_devops_project.main.id
display_name = "build-pipeline"
description = "Build and test application"
build_pipeline_parameters {
items {
name = "ENVIRONMENT"
default_value = "production"
description = "Deployment environment"
}
}
}
# Build stage
resource "oci_devops_build_pipeline_stage" "build" {
build_pipeline_id = oci_devops_build_pipeline.main.id
display_name = "Build and Test"
build_pipeline_stage_type = "BUILD"
build_pipeline_stage_predecessor_collection {
items {
id = oci_devops_build_pipeline.main.id
}
}
build_spec_file = "build_spec.yaml"
image = "OL7_X86_64_STANDARD_10" # Build runner image
build_source_collection {
items {
connection_type = "DEVOPS_CODE_REPOSITORY"
repository_id = oci_devops_repository.main.id
branch = "main"
}
}
}
# Deliver artifact stage
resource "oci_devops_build_pipeline_stage" "deliver" {
build_pipeline_id = oci_devops_build_pipeline.main.id
display_name = "Deliver Artifacts"
build_pipeline_stage_type = "DELIVER_ARTIFACT"
build_pipeline_stage_predecessor_collection {
items {
id = oci_devops_build_pipeline_stage.build.id
}
}
deliver_artifact_collection {
items {
artifact_name = "app_image"
artifact_id = oci_devops_deploy_artifact.image.id
}
}
}
Deployment Pipeline¶
Deployment Artifact:
resource "oci_devops_deploy_artifact" "image" {
project_id = oci_devops_project.main.id
display_name = "app-image"
deploy_artifact_type = "DOCKER_IMAGE"
deploy_artifact_source {
deploy_artifact_source_type = "OCIR"
image_uri = "${var.region}.ocir.io/${var.namespace}/my-app"
image_digest = "${var.image_digest}" # From build pipeline
}
}
resource "oci_devops_deploy_artifact" "k8s_manifest" {
project_id = oci_devops_project.main.id
display_name = "k8s-deployment"
deploy_artifact_type = "KUBERNETES_MANIFEST"
deploy_artifact_source {
deploy_artifact_source_type = "INLINE"
base64encoded_content = base64encode(templatefile(
"k8s/deployment.yaml",
{
image = "${var.region}.ocir.io/${var.namespace}/my-app:$${IMAGE_TAG}"
}
))
}
}
Deployment Pipeline:
resource "oci_devops_deploy_pipeline" "main" {
project_id = oci_devops_project.main.id
display_name = "deploy-pipeline"
description = "Deploy to OKE"
deploy_pipeline_parameters {
items {
name = "NAMESPACE"
default_value = "production"
}
}
}
# Deploy to OKE stage
resource "oci_devops_deploy_stage" "oke_deploy" {
deploy_pipeline_id = oci_devops_deploy_pipeline.main.id
display_name = "Deploy to OKE"
deploy_stage_type = "OKE_DEPLOYMENT"
deploy_stage_predecessor_collection {
items {
id = oci_devops_deploy_pipeline.main.id
}
}
oke_cluster_deploy_environment_id = oci_devops_deploy_environment.oke.id
kubernetes_manifest_deploy_artifact_ids = [
oci_devops_deploy_artifact.k8s_manifest.id
]
namespace = "${var.namespace}"
rollback_policy {
policy_type = "AUTOMATED_STAGE_ROLLBACK_POLICY"
}
}
# Approval stage (optional)
resource "oci_devops_deploy_stage" "approval" {
deploy_pipeline_id = oci_devops_deploy_pipeline.main.id
display_name = "Production Approval"
deploy_stage_type = "MANUAL_APPROVAL"
deploy_stage_predecessor_collection {
items {
id = oci_devops_deploy_stage.oke_deploy.id
}
}
approval_policy {
approval_policy_type = "COUNT_BASED_APPROVAL"
number_of_approvals_required = 1
}
}
Triggers¶
Trigger on Push to Main:
resource "oci_devops_trigger" "main_push" {
project_id = oci_devops_project.main.id
display_name = "trigger-on-main-push"
trigger_source = "DEVOPS_CODE_REPOSITORY"
repository_id = oci_devops_repository.main.id
actions {
type = "TRIGGER_BUILD_PIPELINE"
build_pipeline_id = oci_devops_build_pipeline.main.id
filter {
trigger_source = "DEVOPS_CODE_REPOSITORY"
events = ["PUSH"]
include {
head_ref = "refs/heads/main"
}
}
}
}
Resource Manager¶
Terraform Configuration¶
Provider Configuration:
# provider.tf
terraform {
required_providers {
oci = {
source = "oracle/oci"
version = "~> 5.0"
}
}
}
provider "oci" {
region = var.region
# Authentication via instance principal, resource principal, or config file
}
Variables:
# variables.tf
variable "compartment_id" {
description = "Compartment OCID"
type = string
}
variable "region" {
description = "OCI region"
type = string
default = "us-phoenix-1"
}
variable "instance_shape" {
description = "Compute instance shape"
type = string
default = "VM.Standard.E4.Flex"
}
variable "instance_ocpus" {
description = "Number of OCPUs"
type = number
default = 2
}
Resources:
# main.tf
resource "oci_core_vcn" "main" {
compartment_id = var.compartment_id
display_name = "app-vcn"
cidr_blocks = ["10.0.0.0/16"]
dns_label = "appvcn"
}
resource "oci_core_subnet" "public" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.main.id
display_name = "public-subnet"
cidr_block = "10.0.1.0/24"
route_table_id = oci_core_route_table.public.id
dns_label = "public"
}
resource "oci_core_instance" "app" {
compartment_id = var.compartment_id
availability_domain = data.oci_identity_availability_domain.ad1.name
display_name = "app-server"
shape = var.instance_shape
shape_config {
ocpus = var.instance_ocpus
memory_in_gbs = var.instance_ocpus * 8
}
source_details {
source_type = "image"
source_id = data.oci_core_images.oracle_linux.images[0].id
}
create_vnic_details {
subnet_id = oci_core_subnet.public.id
assign_public_ip = true
}
metadata = {
ssh_authorized_keys = var.ssh_public_key
}
}
Outputs:
# outputs.tf
output "instance_public_ip" {
description = "Public IP of instance"
value = oci_core_instance.app.public_ip
}
output "vcn_id" {
description = "VCN OCID"
value = oci_core_vcn.main.id
}
Resource Manager Stack¶
Creating Stack:
# Create zip of Terraform files
zip -r terraform.zip *.tf
# Create stack
oci resource-manager stack create \
--compartment-id ocid1.compartment... \
--display-name "app-infrastructure" \
--config-source terraform.zip \
--variables file://variables.json
Stack Operations:
# Plan
oci resource-manager job create-plan-job \
--stack-id ocid1.ormstack... \
--display-name "plan-job"
# Apply
oci resource-manager job create-apply-job \
--stack-id ocid1.ormstack... \
--display-name "apply-job" \
--execution-plan-strategy AUTO_APPROVED
# Destroy
oci resource-manager job create-destroy-job \
--stack-id ocid1.ormstack... \
--display-name "destroy-job" \
--execution-plan-strategy AUTO_APPROVED
Drift Detection:
# Detect drift (changes outside Terraform)
oci resource-manager stack detect-drift \
--stack-id ocid1.ormstack... \
--display-name "drift-detection"
# View drift
oci resource-manager job get-job-detailed-log-content \
--job-id ocid1.ormjob...
Application Performance Monitoring¶
APM Setup¶
Creating APM Domain:
resource "oci_apm_apm_domain" "main" {
compartment_id = var.compartment_id
display_name = "app-apm"
description = "APM for application monitoring"
is_free_tier = false
}
# Data keys for different services
resource "oci_apm_data_key" "frontend" {
apm_domain_id = oci_apm_apm_domain.main.id
name = "frontend-key"
type = "PRIVATE"
}
resource "oci_apm_data_key" "backend" {
apm_domain_id = oci_apm_apm_domain.main.id
name = "backend-key"
type = "PRIVATE"
}
Application Instrumentation¶
Java Application:
# Add APM Java agent
java -javaagent:/path/to/apm-java-agent.jar \
-Dcom.oracle.apm.agent.service_name=backend-api \
-Dcom.oracle.apm.agent.data_upload_endpoint=https://apm-domain-endpoint \
-Dcom.oracle.apm.agent.private_data_key=PRIVATE_KEY \
-jar application.jar
Python Application:
# Install OpenTelemetry
# pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
# Setup APM tracing
trace.set_tracer_provider(TracerProvider())
otlp_exporter = OTLPSpanExporter(
endpoint="https://apm-domain-endpoint",
headers={"authorization": f"Bearer {private_data_key}"}
)
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(otlp_exporter)
)
# Instrument Flask
from opentelemetry.instrumentation.flask import FlaskInstrumentor
FlaskInstrumentor().instrument_app(app)
# Manual spans
tracer = trace.get_tracer(__name__)
@app.route('/api/users')
def get_users():
with tracer.start_as_current_span("fetch-users"):
with tracer.start_as_current_span("db-query"):
users = database.query("SELECT * FROM users")
with tracer.start_as_current_span("serialize"):
result = jsonify(users)
return result
Node.js Application:
// Install: npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: 'https://apm-domain-endpoint/v1/traces',
headers: {
'authorization': `Bearer ${process.env.APM_PRIVATE_KEY}`
}
}),
instrumentations: [getNodeAutoInstrumentations()],
serviceName: 'nodejs-api'
});
sdk.start();
// Application code
const express = require('express');
const app = express();
app.get('/api/data', async (req, res) => {
// Automatically traced
const data = await database.query('SELECT * FROM data');
res.json(data);
});
Synthetic Monitoring¶
Creating Synthetic Monitor:
resource "oci_apm_synthetics_monitor" "api_health" {
apm_domain_id = oci_apm_apm_domain.main.id
display_name = "API Health Check"
monitor_type = "REST"
repeat_interval_in_seconds = 300 # 5 minutes
configuration {
config_type = "REST_CONFIG"
is_redirection_enabled = true
is_certificate_validation_enabled = true
req_authentication_details {
auth_type = "NONE"
}
request_method = "GET"
request_headers = [
{
header_name = "Content-Type"
header_value = "application/json"
}
]
}
vantage_points {
name = "us-phoenix-1"
}
# Alert on failures
is_run_once = false
status = "ENABLED"
script_parameters {
param_name = "endpoint"
param_value = "https://api.example.com/health"
}
}
Logging and Observability¶
Custom Logs¶
Creating Log Group:
resource "oci_logging_log_group" "app_logs" {
compartment_id = var.compartment_id
display_name = "application-logs"
}
# Custom log
resource "oci_logging_log" "application" {
display_name = "app-logs"
log_group_id = oci_logging_log_group.app_logs.id
log_type = "CUSTOM"
configuration {
source {
category = "custom"
resource = oci_functions_application.main.id
service = "functions"
source_type = "OCISERVICE"
}
compartment_id = var.compartment_id
}
is_enabled = true
retention_duration = 30
}
Sending Logs from Application:
import oci
import json
import time
logging_client = oci.loggingingestion.LoggingClient(config)
def send_log(message, level="INFO"):
log_entry = oci.loggingingestion.models.LogEntry(
data=json.dumps({
"message": message,
"level": level,
"timestamp": time.time()
}),
id=str(uuid.uuid4()),
time=datetime.utcnow().isoformat() + 'Z'
)
put_logs_details = oci.loggingingestion.models.PutLogsDetails(
specversion="1.0",
log_entry_batches=[
oci.loggingingestion.models.LogEntryBatch(
entries=[log_entry],
source="my-application",
type="custom",
defaultlogentrytime=datetime.utcnow().isoformat() + 'Z'
)
]
)
logging_client.put_logs(
log_id="ocid1.log...",
put_logs_details=put_logs_details
)
Log Analytics¶
Log Analytics Queries:
# Find errors in last hour
'Log Source' = 'application-logs' |
where level = 'ERROR' |
where 'Log Time' > now() - 1h |
stats count() by message
# Response time analysis
'Log Source' = 'api-access-logs' |
where 'Response Time' > 1000 |
stats avg('Response Time'), max('Response Time'), count() by endpoint
# User activity tracking
'Log Source' = 'audit-logs' |
where eventType = 'user.login' |
stats count() by user, location
Dashboards¶
Creating Custom Dashboard:
resource "oci_management_dashboard" "app_dashboard" {
compartment_id = var.compartment_id
display_name = "Application Dashboard"
config = jsonencode({
"widgets": [
{
"id": "widget-1",
"type": "line-chart",
"title": "Request Rate",
"query": "CpuUtilization[1m].mean()",
"namespace": "oci_computeagent"
},
{
"id": "widget-2",
"type": "bar-chart",
"title": "Error Count",
"query": "'Log Source' = 'app' | where level = 'ERROR' | stats count() by type"
}
]
})
}
Exam Tips¶
Critical Concepts¶
DevOps: - Build pipeline: Compile, test, package - Deployment pipeline: Deploy to environments - Triggers: Automate on code push - Approval stages: Manual gates for production
Resource Manager: - Infrastructure as Code (Terraform) - Stack: Collection of TF files - Operations: Plan, Apply, Destroy - Drift detection: Find manual changes
APM: - Distributed tracing: Track requests across services - Spans: Individual operations - Instrumentation: Add to application code - Synthetic monitoring: Proactive health checks
Logging: - Service logs: Automatic (VCN flow, LB access) - Custom logs: Application-generated - Log Analytics: Query and analyze logs - Retention: Configure based on requirements
Common Scenarios¶
Q: Automate deployment on code push? A: DevOps trigger + build pipeline + deployment pipeline
Q: Track request across microservices? A: APM with distributed tracing
Q: Manage infrastructure as code? A: Resource Manager (Terraform)
Q: Find errors in application logs? A: Log Analytics with queries
Q: Detect manual infrastructure changes? A: Resource Manager drift detection
Summary¶
DevOps: CI/CD pipelines, automated deployments, triggers
Resource Manager: Infrastructure as Code, Terraform, drift detection
APM: Distributed tracing, performance monitoring, synthetic checks
Logging: Service logs, custom logs, Log Analytics, queries
Congratulations on completing OCI Developer Associate study notes!