Skip to content

IBM Cloud Developer (C1000-177) - Application Deployment & Integration

Table of Contents


CI/CD Pipelines

IBM Cloud Continuous Delivery

IBM Cloud Continuous Delivery provides integrated DevOps tools for building, testing, and deploying applications.

Key Components

  1. Delivery Pipeline
  2. Build stage
  3. Test stage
  4. Deploy stage
  5. Custom stages

  6. Pipeline Types

  7. Classic pipelines
  8. Tekton pipelines (preferred)
  9. Jenkins integration

  10. Pipeline Triggers

  11. Git commit
  12. Pull request
  13. Manual trigger
  14. Timer-based

Creating a Tekton Pipeline

# .tekton/pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: ibm-cloud-app-pipeline
spec:
  params:
    - name: repository
      description: The git repo URL
    - name: revision
      description: The git revision
      default: main
    - name: registry-namespace
      description: Container registry namespace
    - name: image-name
      description: Image name

  workspaces:
    - name: pipeline-workspace

  tasks:
    - name: git-clone
      taskRef:
        name: git-clone
      params:
        - name: url
          value: $(params.repository)
        - name: revision
          value: $(params.revision)
      workspaces:
        - name: output
          workspace: pipeline-workspace

    - name: unit-tests
      taskRef:
        name: npm-test
      runAfter:
        - git-clone
      workspaces:
        - name: source
          workspace: pipeline-workspace

    - name: build-image
      taskRef:
        name: icr-containerize
      runAfter:
        - unit-tests
      params:
        - name: registry-namespace
          value: $(params.registry-namespace)
        - name: image-name
          value: $(params.image-name)
        - name: path-to-dockerfile
          value: Dockerfile
      workspaces:
        - name: source
          workspace: pipeline-workspace

    - name: vulnerability-scan
      taskRef:
        name: icr-check-va-scan
      runAfter:
        - build-image
      params:
        - name: registry-namespace
          value: $(params.registry-namespace)
        - name: image-name
          value: $(params.image-name)

    - name: deploy-to-kubernetes
      taskRef:
        name: kubernetes-deploy
      runAfter:
        - vulnerability-scan
      params:
        - name: cluster-name
          value: my-iks-cluster
        - name: deployment-file
          value: deployment.yaml
      workspaces:
        - name: source
          workspace: pipeline-workspace

Tekton Task Definition

# .tekton/tasks/npm-test.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: npm-test
spec:
  workspaces:
    - name: source

  steps:
    - name: install-dependencies
      image: node:16
      workingDir: $(workspaces.source.path)
      script: |
        #!/bin/bash
        npm ci

    - name: run-tests
      image: node:16
      workingDir: $(workspaces.source.path)
      script: |
        #!/bin/bash
        npm test -- --coverage

    - name: run-linter
      image: node:16
      workingDir: $(workspaces.source.path)
      script: |
        #!/bin/bash
        npm run lint

Creating Pipeline via CLI

# Install IBM Cloud CLI plugins
ibmcloud plugin install doi
ibmcloud plugin install kubernetes-service

# Create a toolchain
ibmcloud dev toolchain-create --name my-toolchain

# Get toolchain ID
TOOLCHAIN_ID=$(ibmcloud dev toolchain-get my-toolchain --output json | jq -r '.id')

# Create a pipeline
ibmcloud dev pipeline-create \
  --toolchain-id $TOOLCHAIN_ID \
  --name my-pipeline \
  --type tekton

# Create pipeline run
ibmcloud dev pipeline-run \
  --pipeline-id <pipeline-id> \
  --trigger manual

Classic Pipeline Configuration

# .bluemix/pipeline.yml
stages:
  - name: BUILD
    inputs:
      - type: git
        branch: main
    triggers:
      - type: commit
    jobs:
      - name: Build
        type: builder
        build_type: shell
        script: |
          #!/bin/bash
          npm install
          npm run build

  - name: TEST
    inputs:
      - type: job
        stage: BUILD
        job: Build
    jobs:
      - name: Unit Tests
        type: tester
        script: |
          #!/bin/bash
          npm test
      - name: Security Scan
        type: tester
        script: |
          #!/bin/bash
          npm audit

  - name: DEPLOY
    inputs:
      - type: job
        stage: BUILD
        job: Build
    triggers:
      - type: stage
    jobs:
      - name: Deploy to Cloud Foundry
        type: deployer
        target:
          region_id: us-south
          organization: my-org
          space: production
        script: |
          #!/bin/bash
          cf push "${CF_APP_NAME}"

Pipeline Environment Variables

# Set environment variables for pipeline
ibmcloud dev pipeline-property-set \
  --pipeline-id <pipeline-id> \
  --name API_KEY \
  --value $IBM_CLOUD_API_KEY \
  --type secure

ibmcloud dev pipeline-property-set \
  --pipeline-id <pipeline-id> \
  --name REGION \
  --value us-south \
  --type text

IBM Cloud Toolchains

Toolchain Components

  1. Source Control
  2. GitHub
  3. GitLab
  4. Bitbucket
  5. IBM Cloud Git Repos

  6. Build & Deploy

  7. Delivery Pipeline
  8. Tekton
  9. Jenkins

  10. Testing

  11. Sauce Labs
  12. DevOps Insights
  13. Custom test tools

  14. Monitoring

  15. PagerDuty
  16. Slack
  17. IBM Cloud Monitoring

Creating a Toolchain

# Create toolchain from template
ibmcloud dev toolchain-create \
  --name my-complete-toolchain \
  --template "Cloud Native Toolchain"

# Add GitHub integration
ibmcloud dev toolchain-integration-add \
  --toolchain-id $TOOLCHAIN_ID \
  --type githubconsolidated \
  --name my-github \
  --parameters '{
    "repo_url": "https://github.com/myorg/myapp",
    "type": "existing",
    "api_token": "$GITHUB_TOKEN"
  }'

# Add Slack integration
ibmcloud dev toolchain-integration-add \
  --toolchain-id $TOOLCHAIN_ID \
  --type slack \
  --name my-slack \
  --parameters '{
    "api_token": "$SLACK_TOKEN",
    "channel_name": "deployments"
  }'

Toolchain Template (JSON)

{
  "name": "My Application Toolchain",
  "description": "Complete CI/CD toolchain for Node.js application",
  "required": [
    "repo",
    "pipeline"
  ],
  "services": {
    "repo": {
      "service_id": "githubconsolidated",
      "parameters": {
        "repo_name": "{{toolchain.name}}",
        "repo_url": "https://github.com/myorg/myapp",
        "type": "clone",
        "has_issues": true,
        "enable_traceability": true
      }
    },
    "pipeline": {
      "service_id": "pipeline",
      "parameters": {
        "services": [
          "repo"
        ],
        "name": "{{services.repo.parameters.repo_name}}",
        "type": "tekton",
        "configuration": {
          "content": {
            "$text": "pipeline.yml"
          },
          "env": {
            "REPO": "repo",
            "CF_APP_NAME": "{{form.pipeline.parameters.app-name}}",
            "REGISTRY_NAMESPACE": "{{form.pipeline.parameters.registry-namespace}}",
            "API_KEY": "{{form.pipeline.parameters.api-key}}"
          }
        }
      }
    },
    "devops-insights": {
      "service_id": "draservicebroker"
    },
    "slack": {
      "service_id": "slack",
      "parameters": {
        "api_token": "{{form.slack.parameters.api_token}}",
        "channel_name": "{{form.slack.parameters.channel_name}}"
      }
    }
  }
}

DevOps Insights Integration

# Install DevOps Insights CLI
npm install -g grunt-idra3

# Publish test results
grunt idra:publishTestResult \
  --file=mocha.json \
  --type=unittest

# Publish code coverage
grunt idra:publishTestResult \
  --file=coverage/coverage.json \
  --type=code

# Publish build record
grunt idra:publishBuildRecord \
  --status=pass \
  --logicalappname="MyApp" \
  --buildnumber=$BUILD_NUMBER

# Evaluate gate policy
grunt idra:evaluateGate \
  --policy="Unit Test Policy" \
  --ruletype=unittest

Container Orchestration

Kubernetes Deployments

Deployment Manifest

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
  namespace: production
  labels:
    app: myapp
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
        version: v1
    spec:
      containers:
      - name: myapp
        image: us.icr.io/mynamespace/myapp:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          protocol: TCP
        env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
      imagePullSecrets:
      - name: icr-secret
---
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
  namespace: production
spec:
  type: LoadBalancer
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  namespace: production
  annotations:
    kubernetes.io/ingress.class: "public-iks-k8s-nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - myapp.example.com
    secretName: myapp-tls-secret
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp-service
            port:
              number: 80

Kubernetes CLI Commands

# Configure kubectl for IKS
ibmcloud ks cluster config --cluster my-cluster

# Create namespace
kubectl create namespace production

# Create image pull secret for IBM Container Registry
kubectl create secret docker-registry icr-secret \
  --docker-server=us.icr.io \
  --docker-username=iamapikey \
  --docker-password=$IBM_CLOUD_API_KEY \
  --namespace=production

# Create secret for database credentials
kubectl create secret generic db-credentials \
  --from-literal=url="postgresql://user:pass@host:5432/db" \
  --namespace=production

# Apply deployment
kubectl apply -f deployment.yaml

# Check deployment status
kubectl rollout status deployment/myapp-deployment -n production

# Scale deployment
kubectl scale deployment/myapp-deployment --replicas=5 -n production

# Update image
kubectl set image deployment/myapp-deployment \
  myapp=us.icr.io/mynamespace/myapp:v2 \
  -n production

# Rollback deployment
kubectl rollout undo deployment/myapp-deployment -n production

# View logs
kubectl logs -f deployment/myapp-deployment -n production

# Execute command in pod
kubectl exec -it deployment/myapp-deployment -n production -- /bin/sh

OpenShift Deployments

DeploymentConfig

# openshift-deployment.yaml
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
  name: myapp
  namespace: myproject
spec:
  replicas: 3
  selector:
    app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: us.icr.io/mynamespace/myapp:latest
        ports:
        - containerPort: 8080
        env:
        - name: NODE_ENV
          value: production
  triggers:
  - type: ConfigChange
  - type: ImageChange
    imageChangeParams:
      automatic: true
      containerNames:
      - myapp
      from:
        kind: ImageStreamTag
        name: myapp:latest
---
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  ports:
  - port: 8080
    targetPort: 8080
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: myapp
spec:
  to:
    kind: Service
    name: myapp
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect

OpenShift CLI Commands

# Login to OpenShift cluster
oc login --token=$OC_TOKEN --server=https://api.myopenshift.com:6443

# Create new project
oc new-project myproject

# Create app from source code
oc new-app nodejs~https://github.com/myorg/myapp \
  --name=myapp \
  --env NODE_ENV=production

# Create app from Docker image
oc new-app us.icr.io/mynamespace/myapp:latest \
  --name=myapp

# Expose service
oc expose service myapp \
  --hostname=myapp.apps.myopenshift.com

# Start build
oc start-build myapp

# Follow build logs
oc logs -f bc/myapp

# Scale application
oc scale dc/myapp --replicas=5

# Set environment variable
oc set env dc/myapp DATABASE_URL="postgresql://..."

# Create secret
oc create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=secret

# Mount secret as environment
oc set env dc/myapp --from=secret/db-secret

# Rollout latest version
oc rollout latest dc/myapp

# Check rollout status
oc rollout status dc/myapp

# Rollback to previous version
oc rollback myapp

Helm Charts

# Chart.yaml
apiVersion: v2
name: myapp
description: My Application Helm Chart
type: application
version: 1.0.0
appVersion: "1.0"

---
# values.yaml
replicaCount: 3

image:
  repository: us.icr.io/mynamespace/myapp
  tag: latest
  pullPolicy: Always

service:
  type: LoadBalancer
  port: 80
  targetPort: 8080

ingress:
  enabled: true
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
          pathType: Prefix

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"

env:
  - name: NODE_ENV
    value: "production"

secrets:
  - name: DATABASE_URL
    key: url

---
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
  labels:
    {{- include "myapp.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "myapp.selectorLabels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: {{ .Values.service.targetPort }}
        env:
        {{- range .Values.env }}
        - name: {{ .name }}
          value: {{ .value | quote }}
        {{- end }}
        {{- range .Values.secrets }}
        - name: {{ .name }}
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: {{ .key }}
        {{- end }}
        resources:
          {{- toYaml .Values.resources | nindent 10 }}
# Install Helm chart
helm install myapp ./myapp-chart \
  --namespace production \
  --create-namespace

# Upgrade release
helm upgrade myapp ./myapp-chart \
  --namespace production \
  --set image.tag=v2

# Rollback release
helm rollback myapp 1 --namespace production

# Uninstall release
helm uninstall myapp --namespace production

Serverless Deployment

IBM Cloud Functions (OpenWhisk)

Action Deployment

// hello.js
function main(params) {
  const name = params.name || 'World';
  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: { message: `Hello, ${name}!` }
  };
}

exports.main = main;
# Create action
ibmcloud fn action create hello hello.js \
  --kind nodejs:16 \
  --web true

# Invoke action
ibmcloud fn action invoke hello \
  --param name "IBM Cloud" \
  --result

# Get action URL
ibmcloud fn action get hello --url

# Update action
ibmcloud fn action update hello hello.js

# Create action with dependencies
zip -r action.zip hello.js package.json node_modules
ibmcloud fn action create hello action.zip \
  --kind nodejs:16 \
  --web true

# Set default parameters
ibmcloud fn action update hello \
  --param apiKey $API_KEY

# Create Docker action
ibmcloud fn action create myaction --docker myimage:latest

Sequence Actions

# Create individual actions
ibmcloud fn action create auth auth.js
ibmcloud fn action create process process.js
ibmcloud fn action create respond respond.js

# Create sequence
ibmcloud fn action create processRequest \
  --sequence auth,process,respond

# Invoke sequence
ibmcloud fn action invoke processRequest \
  --param token "abc123" \
  --param data "mydata" \
  --result

Triggers and Rules

# Create trigger
ibmcloud fn trigger create myTrigger

# Fire trigger
ibmcloud fn trigger fire myTrigger \
  --param message "Hello"

# Create rule to connect trigger to action
ibmcloud fn rule create myRule myTrigger hello

# Create periodic trigger
ibmcloud fn trigger create everyMinute \
  --feed /whisk.system/alarms/alarm \
  --param cron "* * * * *"

# Create Cloudant trigger
ibmcloud fn trigger create dbTrigger \
  --feed /whisk.system/cloudant/changes \
  --param dbname mydb \
  --param username $USERNAME \
  --param password $PASSWORD

API Gateway

# Create API
ibmcloud fn api create /api /hello get hello \
  --response-type json

# List APIs
ibmcloud fn api list

# Get API details
ibmcloud fn api get /api

# Delete API
ibmcloud fn api delete /api

Code Engine

Application Deployment

# Create project
ibmcloud ce project create --name my-project

# Select project
ibmcloud ce project select --name my-project

# Deploy application from container image
ibmcloud ce application create \
  --name myapp \
  --image us.icr.io/mynamespace/myapp:latest \
  --port 8080 \
  --min-scale 0 \
  --max-scale 10 \
  --cpu 0.5 \
  --memory 1G \
  --env NODE_ENV=production \
  --registry-secret myregistry

# Deploy from source code
ibmcloud ce application create \
  --name myapp \
  --build-source . \
  --strategy buildpacks \
  --port 8080

# Update application
ibmcloud ce application update myapp \
  --image us.icr.io/mynamespace/myapp:v2 \
  --env-from-secret db-credentials

# Scale application
ibmcloud ce application update myapp \
  --min-scale 2 \
  --max-scale 20 \
  --concurrency 100

# Get application URL
ibmcloud ce application get --name myapp

# View logs
ibmcloud ce application logs --name myapp --follow

Batch Jobs

# Create job
ibmcloud ce job create \
  --name data-processing \
  --image us.icr.io/mynamespace/processor:latest \
  --cpu 2 \
  --memory 4G \
  --array-indices 0-9

# Run job
ibmcloud ce jobrun submit \
  --job data-processing \
  --env INPUT_FILE=data.csv

# Check job status
ibmcloud ce jobrun get --name data-processing-run-1

# View job logs
ibmcloud ce jobrun logs --name data-processing-run-1

Application Integration

App Connect

Integration Flow

{
  "name": "Salesforce to Slack Integration",
  "description": "Send Slack notification when new lead is created",
  "trigger": {
    "type": "salesforce",
    "event": "Lead Created",
    "connection": "salesforce-prod",
    "fields": [
      "Id",
      "FirstName",
      "LastName",
      "Email",
      "Company"
    ]
  },
  "actions": [
    {
      "type": "slack",
      "action": "Send Message",
      "connection": "slack-sales",
      "parameters": {
        "channel": "#new-leads",
        "message": "New lead: {{FirstName}} {{LastName}} from {{Company}}"
      }
    },
    {
      "type": "cloudant",
      "action": "Create Document",
      "connection": "cloudant-db",
      "parameters": {
        "database": "leads",
        "document": {
          "salesforceId": "{{Id}}",
          "name": "{{FirstName}} {{LastName}}",
          "email": "{{Email}}",
          "company": "{{Company}}",
          "timestamp": "{{$now}}"
        }
      }
    }
  ]
}

IBM MQ Integration

// mq-producer.js
const mq = require('ibmmq');

const connectionOptions = {
  queueManager: 'QM1',
  host: 'mq.example.com',
  port: 1414,
  channel: 'DEV.ADMIN.SVRCONN',
  userId: 'admin',
  password: process.env.MQ_PASSWORD
};

async function sendMessage(message) {
  const mqConn = new mq.MQQueueManager();

  try {
    await mqConn.connect(connectionOptions);

    const queue = await mqConn.openQueue(
      'DEV.QUEUE.1',
      mq.MQC.MQOO_OUTPUT
    );

    const msgObject = {
      type: mq.MQC.MQMT_DATAGRAM,
      format: mq.MQC.MQFMT_STRING,
      messageData: message
    };

    await queue.put(msgObject);
    await queue.close();
    await mqConn.disconnect();

    console.log('Message sent successfully');
  } catch (err) {
    console.error('Error:', err);
  }
}

sendMessage('Hello from IBM Cloud!');

Messaging Services

Event Streams (Kafka)

Producer

// kafka-producer.js
const { Kafka } = require('kafkajs');

const kafka = new Kafka({
  clientId: 'my-app',
  brokers: process.env.KAFKA_BROKERS.split(','),
  ssl: true,
  sasl: {
    mechanism: 'plain',
    username: 'token',
    password: process.env.KAFKA_API_KEY
  }
});

const producer = kafka.producer();

async function sendMessage(topic, message) {
  await producer.connect();

  await producer.send({
    topic: topic,
    messages: [
      {
        key: message.key,
        value: JSON.stringify(message.value),
        headers: {
          'correlation-id': message.correlationId
        }
      }
    ]
  });

  await producer.disconnect();
}

// Usage
sendMessage('orders', {
  key: 'order-123',
  value: {
    orderId: '123',
    customer: 'John Doe',
    amount: 99.99,
    timestamp: new Date().toISOString()
  },
  correlationId: 'abc-123'
});

Consumer

// kafka-consumer.js
const { Kafka } = require('kafkajs');

const kafka = new Kafka({
  clientId: 'my-app',
  brokers: process.env.KAFKA_BROKERS.split(','),
  ssl: true,
  sasl: {
    mechanism: 'plain',
    username: 'token',
    password: process.env.KAFKA_API_KEY
  }
});

const consumer = kafka.consumer({
  groupId: 'order-processor',
  sessionTimeout: 30000
});

async function consumeMessages() {
  await consumer.connect();
  await consumer.subscribe({
    topic: 'orders',
    fromBeginning: false
  });

  await consumer.run({
    eachMessage: async ({ topic, partition, message }) => {
      const order = JSON.parse(message.value.toString());

      console.log({
        topic,
        partition,
        offset: message.offset,
        key: message.key?.toString(),
        order
      });

      // Process order
      await processOrder(order);
    }
  });
}

async function processOrder(order) {
  // Business logic here
  console.log(`Processing order ${order.orderId}`);
}

consumeMessages().catch(console.error);

Managing Topics

# Create topic
ibmcloud es topic-create orders \
  --partitions 3 \
  --replication-factor 3

# List topics
ibmcloud es topics

# Update topic configuration
ibmcloud es topic-update orders \
  --config retention.ms=86400000

# Delete topic
ibmcloud es topic-delete orders

Event-Driven Architecture

Event Notifications

# Create Event Notifications instance
ibmcloud resource service-instance-create \
  my-event-notifications \
  event-notifications \
  standard \
  us-south

# Create topic
ibmcloud event-notifications topic-create \
  --instance-id $INSTANCE_ID \
  --name "order-events" \
  --description "Order processing events"

# Create destination (webhook)
ibmcloud event-notifications destination-create \
  --instance-id $INSTANCE_ID \
  --name "order-webhook" \
  --type webhook \
  --config '{
    "url": "https://myapp.com/webhook",
    "verb": "POST",
    "headers": {
      "Authorization": "Bearer token"
    }
  }'

# Create subscription
ibmcloud event-notifications subscription-create \
  --instance-id $INSTANCE_ID \
  --name "order-subscription" \
  --destination-id $DEST_ID \
  --topic-id $TOPIC_ID

Event Notifications SDK

// event-notifications.js
const EventNotificationsV1 = require('@ibm-cloud/event-notifications-node-admin-sdk');
const { IamAuthenticator } = require('ibm-cloud-sdk-core');

const authenticator = new IamAuthenticator({
  apikey: process.env.EN_API_KEY
});

const eventNotifications = new EventNotificationsV1({
  authenticator,
  serviceUrl: 'https://us-south.event-notifications.cloud.ibm.com'
});

// Send notification
async function sendNotification(instanceId, event) {
  const params = {
    instanceId,
    body: {
      specversion: '1.0',
      id: event.id,
      source: 'myapp',
      type: 'order.created',
      time: new Date().toISOString(),
      data: event.data
    }
  };

  try {
    const response = await eventNotifications.sendNotifications(params);
    console.log('Notification sent:', response.result);
  } catch (err) {
    console.error('Error sending notification:', err);
  }
}

// Usage
sendNotification(process.env.EN_INSTANCE_ID, {
  id: 'order-123',
  data: {
    orderId: '123',
    customer: 'John Doe',
    amount: 99.99,
    status: 'created'
  }
});

API Management

API Connect

API Definition (OpenAPI)

# api-definition.yaml
openapi: 3.0.0
info:
  title: Orders API
  version: 1.0.0
  description: API for managing orders

servers:
  - url: https://api.example.com/v1

security:
  - ApiKeyAuth: []

paths:
  /orders:
    get:
      summary: List all orders
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  orders:
                    type: array
                    items:
                      $ref: '#/components/schemas/Order'
                  total:
                    type: integer

    post:
      summary: Create a new order
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderInput'
      responses:
        '201':
          description: Order created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'

  /orders/{orderId}:
    get:
      summary: Get order by ID
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '404':
          description: Order not found

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key

  schemas:
    Order:
      type: object
      properties:
        id:
          type: string
        customerId:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'
        totalAmount:
          type: number
        status:
          type: string
          enum: [pending, processing, completed, cancelled]
        createdAt:
          type: string
          format: date-time

    OrderInput:
      type: object
      required:
        - customerId
        - items
      properties:
        customerId:
          type: string
        items:
          type: array
          items:
            $ref: '#/components/schemas/OrderItem'

    OrderItem:
      type: object
      properties:
        productId:
          type: string
        quantity:
          type: integer
        price:
          type: number

API Policies

# policies.yaml
assembly:
  execute:
    - set-variable:
        title: Set CORS Headers
        actions:
          - set: message.headers.Access-Control-Allow-Origin
            value: '*'
          - set: message.headers.Access-Control-Allow-Methods
            value: 'GET, POST, PUT, DELETE, OPTIONS'

    - rate-limit:
        title: Rate Limiting
        rate-limit:
          value: 100
          interval: 60
          time-unit: second
          key: $(request.headers.X-API-Key)

    - validate:
        title: Validate Request
        source: request
        definition: '#/definitions/OrderInput'

    - invoke:
        title: Call Backend
        target-url: $(backend-url)/orders$(request.path)
        verb: $(request.verb)
        cache-response: time-to-live
        cache-ttl: 300

    - map:
        title: Transform Response
        inputs:
          input:
            schema: '#/definitions/BackendResponse'
            variable: message.body
        outputs:
          output:
            schema: '#/definitions/Order'
            variable: message.body
        actions:
          - set: output.id
            from: input.orderId
          - set: output.status
            from: input.orderStatus

    - log:
        title: Log Transaction
        content: 'auto'
        error-content: 'payload'

DevOps Best Practices

Blue-Green Deployment

# Deploy blue version
kubectl apply -f blue-deployment.yaml

# Test blue version
curl https://blue.example.com/health

# Deploy green version
kubectl apply -f green-deployment.yaml

# Switch traffic to green
kubectl patch service myapp \
  -p '{"spec":{"selector":{"version":"green"}}}'

# Verify green version
kubectl get pods -l version=green

# Remove blue deployment if successful
kubectl delete deployment myapp-blue

Canary Deployment

# canary-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp-canary
            port:
              number: 80

GitOps with Tekton

# gitops-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: gitops-deploy
spec:
  params:
    - name: git-url
    - name: git-revision
    - name: image-tag

  workspaces:
    - name: source
    - name: gitops-repo

  tasks:
    - name: clone-app-repo
      taskRef:
        name: git-clone
      params:
        - name: url
          value: $(params.git-url)
        - name: revision
          value: $(params.git-revision)
      workspaces:
        - name: output
          workspace: source

    - name: build-and-push
      taskRef:
        name: kaniko
      runAfter:
        - clone-app-repo
      params:
        - name: IMAGE
          value: us.icr.io/namespace/app:$(params.image-tag)
      workspaces:
        - name: source
          workspace: source

    - name: clone-gitops-repo
      taskRef:
        name: git-clone
      params:
        - name: url
          value: https://github.com/myorg/gitops-repo
        - name: revision
          value: main
      workspaces:
        - name: output
          workspace: gitops-repo

    - name: update-manifest
      taskRef:
        name: yq
      runAfter:
        - build-and-push
        - clone-gitops-repo
      params:
        - name: file
          value: deployment.yaml
        - name: expression
          value: .spec.template.spec.containers[0].image = "us.icr.io/namespace/app:$(params.image-tag)"
      workspaces:
        - name: source
          workspace: gitops-repo

    - name: commit-and-push
      taskRef:
        name: git-cli
      runAfter:
        - update-manifest
      params:
        - name: GIT_USER_NAME
          value: Tekton Pipeline
        - name: GIT_USER_EMAIL
          value: tekton@example.com
        - name: GIT_SCRIPT
          value: |
            git add .
            git commit -m "Update image to $(params.image-tag)"
            git push origin main
      workspaces:
        - name: source
          workspace: gitops-repo

Exam Tips

  1. CI/CD
  2. Know Tekton vs Classic pipelines
  3. Understand toolchain components
  4. Practice creating pipelines via CLI and YAML
  5. Study DevOps Insights integration

  6. Container Orchestration

  7. Master kubectl and oc commands
  8. Understand Kubernetes objects (Deployment, Service, Ingress)
  9. Know OpenShift-specific resources (Route, DeploymentConfig)
  10. Practice Helm chart creation

  11. Serverless

  12. Know Cloud Functions action types
  13. Understand triggers and rules
  14. Practice Code Engine deployments
  15. Study scaling configurations

  16. Integration

  17. Understand Event Streams (Kafka) patterns
  18. Know App Connect use cases
  19. Study Event Notifications
  20. Practice API Connect policies

  21. Deployment Strategies

  22. Know blue-green vs canary deployments
  23. Understand GitOps principles
  24. Study rollback procedures
  25. Practice zero-downtime deployments

Common Scenarios

Scenario 1: Deploy containerized Node.js app to IKS with auto-scaling

# Solution approach:
# 1. Build and push image to ICR
# 2. Create Deployment with HorizontalPodAutoscaler
# 3. Configure ingress for external access
# 4. Set up monitoring

Scenario 2: Implement event-driven architecture with Kafka

# Solution approach:
# 1. Create Event Streams instance
# 2. Create topics for different event types
# 3. Implement producers and consumers
# 4. Handle failures with dead letter queues

Scenario 3: Set up CI/CD pipeline with automatic deployments

# Solution approach:
# 1. Create toolchain with GitHub integration
# 2. Configure Tekton pipeline
# 3. Add testing and security scanning stages
# 4. Implement GitOps for deployment