In 05-02 we deployed orders-service to Kubernetes by typing kubectl apply by hand. That is exactly what TechCorp cannot afford: with six services and a gateway, each deploying several times a week, the human hand is slow, inconsistent and the source of the "Thursday-night deployments with rollbacks" of Module 1. Continuous integration (CI) makes every change get tested automatically in minutes; continuous deployment (CD) takes whatever passes the tests all the way to production without intervention. This lesson builds the orders-service pipeline with GitHub Actions: unit tests, integration tests with Testcontainers and contract tests with Pact (04-05), building and publishing the image of 05-01, can-i-deploy as a gate, updating the Kustomize manifests of 05-02 and deploying with GitOps. At the end we apply Luis's rule so the other five services do not copy 150 lines of YAML, and we measure the outcome with the DORA metrics. How to change version without interrupting the service belongs to 05-04.
Contents
- What CI/CD changes with microservices
- The stages of TechCorp's pipeline
.github/workflows/ci.ymloforders-service, line by line- Contract tests in the pipeline: Pact Broker and
can-i-deploy - Continuous deployment:
.github/workflows/cd.yml - GitOps with Argo CD:
techcorp/platformas the source of truth - Versions, tags and changelogs per service
- Environments and promotion: dev → staging → prod
- Secrets in CI
- The
@techcorp/common-httppipeline - Luis's rule: one reusable workflow for the six services
- DORA metrics: measuring the pipeline
- What CI/CD changes with microservices
The techcorp-shop monolith had one pipeline: a 40-minute suite, one artifact, one coordinated deployment. With microservices:
| Aspect | Monolith | Microservices (TechCorp) |
|---|---|---|
| Number of pipelines | One big one | One per repository: six services + gateway + library + platform |
| Duration | 40 min (everything is always tested) | 5-8 min per service (only what changes is tested) |
| Artifact | One package | One image per service, with its version |
| Deployment | All or nothing, coordinated | Independent: Orders deploys without waiting for Catalog |
| Versioning | One global version | Semver per service; compatibility is guaranteed by contracts (03-06) and Pact (04-05) |
| Risk per deployment | High (a lot of change at once) | Low (little change, easy to revert) |
| Hidden cost | Few | Many pipelines to maintain: they must be standardized (section 11) |
Deployment independence is the central promise of the architecture (01-02, 02-01) and it only holds if each service's pipeline can deploy on its own, without a "release train" that groups everybody. The precondition for daring to do so is the confidence that contract tests provide: it is Pact, not a whole-system E2E test, that answers "will I break someone?".
- The stages of TechCorp's pipeline
flowchart LR
A[lint] --> B[unit + component<br/>npm test] --> C[integration<br/>Testcontainers] --> D[contract<br/>Pact + publish pacts]
D --> E[build image<br/>ghcr.io] --> F[scan<br/>Trivy] --> G[can-i-deploy<br/>staging]
G --> H[deploy staging<br/>Job + rollout] --> I[minimal E2E<br/>gateway 8080] --> J[can-i-deploy prod] --> K[promotion to prod<br/>GitOps]
style E fill:#dfe,stroke:#393
style G fill:#ffd,stroke:#a90
style J fill:#ffd,stroke:#a90
| Stage | What it does | When | Fails if... |
|---|---|---|---|
| Lint | eslint with the template's configuration |
Every push and PR | Style or static errors |
| Unit + component | npm test (Jest, Supertest, no Docker; 04-05) |
Every push and PR | Domain logic or routes |
| Integration | npm run test:integration with Testcontainers (PostgreSQL 16, RabbitMQ) |
Every push and PR | Outbox, saga consumer, SQL |
| Contract | npm run test:contract generates pacts (consumer) or verifies (provider); publication to the Broker |
Every push and PR | A broken contract |
| Build + push | The image of 05-01 with sha-<commit> and semver tags |
Only on main and on tags |
Dockerfile or npm ci |
| Scan | Trivy over the image | After the build | Critical CVE (policy in 07-04) |
can-i-deploy |
Is it compatible with what is in the target environment? | Before every deployment | Some pact not verified |
| Deploy to staging | Migrations Job + kubectl apply -k + rollout status |
Every push to main |
Migration or startup |
| Minimal E2E | The single E2E of 04-05 against staging | After deploying to staging | Wiring |
| Promotion to prod | Image change in the prod overlay (GitOps) |
Manual or automatic depending on the service | — |
Everything before the image is CI: fast, on every change. Everything after is CD.
.github/workflows/ci.yml of orders-service, line by line
.github/workflows/ci.yml of orders-service, line by line# techcorp/orders-service/.github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
tags: ["v*"] # v1.0.1 → image 1.0.1 (section 7)
pull_request:
permissions:
contents: read
packages: write # required to push to ghcr.io with GITHUB_TOKEN
env:
IMAGE: ghcr.io/techcorp/orders-service
PACT_BROKER_BASE_URL: https://pact.techcorp.example
jobs:
tests:
runs-on: ubuntu-latest # comes with Docker installed: Testcontainers works with no extra setup
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # caches ~/.npm between runs based on package-lock.json
registry-url: https://npm.pkg.github.com
scope: "@techcorp" # @techcorp/common-http resolves against GitHub Packages (04-01)
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # read access to the organization's packages
- run: npm run lint
- run: npm test # unit + component + events: seconds, no Docker
- run: npm run test:integration # Testcontainers starts postgres:16 and rabbitmq on the runner
- run: npm run test:contract # generates pacts/orders-service-catalog-service.json
- name: Publish pacts to the Broker
run: |
npx pact-broker publish pacts \
--consumer-app-version ${{ github.sha }} \
--branch ${{ github.head_ref || github.ref_name }} \
--broker-token ${{ secrets.PACT_BROKER_TOKEN }}
image:
needs: tests
if: github.event_name == 'push' # no image is published on a PR
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3 # BuildKit: required for --mount=type=secret and remote cache
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v5 # computes the tags according to the event
with:
images: ${{ env.IMAGE }}
tags: |
type=sha,prefix=sha-,format=short # always: sha-9f3c2ab
type=semver,pattern={{version}} # only on tags v1.0.1 → 1.0.1
- name: Prepare .npmrc for the build (secret, not a layer)
run: echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" > /tmp/npmrc
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
secrets: npmrc=/tmp/npmrc # the --secret id=npmrc of the 05-01 Dockerfile
cache-from: type=gha # layer cache between runs (the npm ci of 05-01 §4)
cache-to: type=gha,mode=max
- name: Vulnerability scan (details in 07-04)
uses: aquasecurity/[email protected]
with:
image-ref: ${{ env.IMAGE }}:sha-${{ github.sha }}
severity: CRITICAL
exit-code: "1"Reading notes:
- Two
jobs(tests,image) withneeds: the image is only built if everything passes; on a PR (pull_request) onlytestsruns. ubuntu-latestcomes with Docker, sotest:integrationworks as is: Testcontainers pullspostgres:16andrabbitmq:3-managementon the runner. That is the reason we separatedtest(no Docker) fromtest:integrationin 04-05.docker/metadata-actionproduces the two tags of 05-01 without hand-written scripts:sha-<commit>always, and the semver when the event is av1.0.1tag.secrets: npmrc=inbuild-push-actionfeeds the--mount=type=secret,id=npmrcof theDockerfile: the token never ends up in any layer.cache-from/to: type=ghastores the layers in the GitHub Actions cache: a change insrc/does not repeatnpm ciin CI, just as on the laptop.- Minimal
permissions:contents: readto clone,packages: writeto publish. No personal tokens.
- Contract tests in the pipeline: Pact Broker and
can-i-deploy
can-i-deployIn 04-05 the pact file traveled by hand from orders-service to catalog-service. In CI it travels through the Pact Broker (self-hosted or PactFlow), which stores each pact with the consumer's version (github.sha) and branch, and each verification result with the provider's version:
sequenceDiagram
participant P as CI orders-service (consumer)
participant B as Pact Broker
participant C as CI catalog-service (provider)
P->>B: publish pacts (version sha-9f3c2ab, branch main)
B-->>C: webhook: there is a new pact to verify
C->>C: npm run test:contract (Verifier with stateHandlers, 04-05)
C->>B: verification result (catalog sha-1a2b3c: OK)
P->>B: can-i-deploy --pacticipant orders-service --version sha-9f3c2ab --to-environment staging
B-->>P: yes: the catalog deployed in staging verified this pact
In the provider's CI (catalog-service), the test:contract of 04-05 runs with pactBrokerUrl instead of pactUrls, with publishVerificationResult: true and providerVersion: process.env.GITHUB_SHA, and after deploying it records which environment each version is in (pact-broker record-deployment --environment staging). With that, the gate before deploying is one line:
staging-gate:
needs: image
runs-on: ubuntu-latest
steps:
- run: |
npx pact-broker can-i-deploy \
--pacticipant orders-service --version ${{ github.sha }} \
--to-environment staging \
--broker-base-url ${{ env.PACT_BROKER_BASE_URL }} --broker-token ${{ secrets.PACT_BROKER_TOKEN }}can-i-deploy answers "yes" only if all the provider versions currently deployed in staging have successfully verified the pacts of this version of Orders (and, if Orders were a provider to someone, if its deployed consumers are still satisfied). It is the integration guarantee at the cost of one HTTP query: the reason TechCorp's single E2E test can remain a single one.
- Continuous deployment:
.github/workflows/cd.yml
.github/workflows/cd.ymlThe deployment to staging is done directly from the workflow: it updates the image tag in the Kustomize overlay (05-02), launches the migrations Job, applies and waits for the rollout, and runs the E2E test.
# techcorp/orders-service/.github/workflows/cd.yml
name: CD staging
on:
workflow_run:
workflows: [CI]
branches: [main]
types: [completed]
permissions: { contents: read }
jobs:
staging:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
environment: staging # GitHub environment: its own secrets and protection rules
env:
TAG: sha-${{ github.event.workflow_run.head_sha }}
steps:
- uses: actions/checkout@v4
with:
repository: techcorp/platform # the manifests live in the platform repo (05-02)
token: ${{ secrets.PLATFORM_TOKEN }}
- uses: azure/setup-kubectl@v4
- name: Staging cluster credentials
run: echo "${{ secrets.KUBECONFIG_STAGING }}" | base64 -d > $HOME/.kube/config
- name: Pin the image in the staging overlay
working-directory: k8s/orders-service/overlays/staging
run: kustomize edit set image ghcr.io/techcorp/orders-service=ghcr.io/techcorp/orders-service:${{ env.TAG }}
- name: Migrations (05-02 Job): delete the previous one, apply, wait
run: |
kubectl -n techcorp delete job orders-service-migrations --ignore-not-found
kubectl kustomize k8s/orders-service/overlays/staging | kubectl apply -f -
kubectl -n techcorp wait --for=condition=complete job/orders-service-migrations --timeout=180s
- name: Wait for the rollout
run: kubectl -n techcorp rollout status deploy/orders-service --timeout=180s
- name: Minimal E2E against staging (04-05)
run: GATEWAY_URL=https://api-staging.techcorp.example npm run test:e2e
- name: Record the deployment in the Pact Broker
run: npx pact-broker record-deployment --pacticipant orders-service --version ${{ github.event.workflow_run.head_sha }} --environment staging --broker-token ${{ secrets.PACT_BROKER_TOKEN }}Two details compared with 05-02: the migrations Job is now called plainly orders-service-migrations and the workflow deletes the previous one before applying (a Job is immutable); it is the alternative to the versioned name, and it works because migrate.js is idempotent. And kustomize edit set image modifies the images: line we left ready in the overlay: the YAML remains the source of truth, the pipeline only changes a tag. With Helm the equivalent would be helm upgrade --set image.tag=$TAG.
rollout status is what turns "I applied it" into "it is deployed and ready": it waits for the new replicas to pass the readinessProbe and fails (and the job fails) if they do not manage it within 180 s. What Kubernetes does meanwhile with the old replicas is 05-04.
- GitOps with Argo CD:
techcorp/platform as the source of truth
techcorp/platform as the source of truthFor production, TechCorp does not want a workflow holding a kubeconfig in a secret to run kubectl apply against the production cluster. It adopts GitOps: the desired state of production is what is in git (techcorp/platform, main branch, overlays/prod), and an agent inside the cluster (Argo CD; Flux is equivalent) reads it and applies it continuously.
flowchart LR
CI[CI orders-service] -->|image sha-9f3c2ab| REG[(ghcr.io)]
CI -->|PR: overlays/prod images newTag=1.0.1| GIT[(techcorp/platform)]
REV[Review + merge<br/>Luis or the pipeline] --> GIT
ARGO[Argo CD<br/>in the prod cluster] -->|pull every 3 min / webhook| GIT
ARGO -->|kubectl apply| K8S[Production cluster]
ARGO -.->|detects drift| K8S
# techcorp/platform/argocd/orders-service-prod.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata: { name: orders-service-prod, namespace: argocd }
spec:
project: techcorp
source:
repoURL: https://github.com/techcorp/platform
targetRevision: main
path: k8s/orders-service/overlays/prod
destination: { server: https://kubernetes.default.svc, namespace: techcorp }
syncPolicy:
automated: { prune: true, selfHeal: true } # applies what is in git; reverts manual changes in the clusterWhy TechCorp adopts it: (1) audit: every production deployment is a reviewable commit in platform; (2) no cluster credentials outside the cluster: Argo CD pulls, nobody pushes; (3) drift detection: if someone runs kubectl scale by hand on a Friday, Argo brings it back to the git state (or warns); (4) rollback = revert the commit. The migrations Job is annotated with argocd.argoproj.io/hook: PreSync and hook-delete-policy: BeforeHookCreation, which is the GitOps way of "delete the previous one and run before the Deployment". Argo CD and Flux can also deploy to staging; TechCorp starts with the direct workflow in staging to learn and with GitOps in production, and will unify later on.
- Versions, tags and changelogs per service
- Semver per service:
orders-service1.0.1 has nothing to do withcatalog-service1.4.2. Major = incompatible contract change (which in 03-06 means/v2/), minor = compatible functionality, patch = fix. - The
sha-<commit>image is always built; the semver, when tagging:git tag v1.0.1 && git push --tagstriggersci.ymlwith the tag andmetadata-actionadds1.0.1to the same image (same digest) that already went through staging assha-.... Nothing is rebuilt for production. - Changelog:
CHANGELOG.mdin every repository, fed by hand or generated from commit messages following the conventional commits convention (feat:,fix:,feat!:), which tools such assemantic-releaseorrelease-pleaseuse to compute the version and publish the release automatically. TechCorp adopts the message convention; automating the version is left as the next step.
- Environments and promotion: dev → staging → prod
| Environment | Cluster | Who deploys | Which image | Data |
|---|---|---|---|---|
| dev | kind on the laptop or Compose (05-01) | The developer | :local or sha-... |
Seed |
| staging | Shared cluster, small-sized managed services | cd.yml on every push to main |
sha-<commit> |
Synthetic, anonymized |
| prod | Production cluster, Argo CD | Merge of the promotion PR (human review for Orders and Payments; automatic for Catalog and Notifications) | 1.0.1 (same image) |
Real |
The three environments apply the same Kustomize base with different overlays (05-02): the only things that change between staging and prod are the image tag, the replicas and two ConfigMap keys. Promoting means opening a PR in platform that changes newTag in overlays/prod (the workflow opens it on its own with peter-evans/create-pull-request); reviewing and merging is the approval.
- Secrets in CI
secrets.GITHUB_TOKEN: generated by GitHub for each run, with the declaredpermissions; it works forghcr.ioand GitHub Packages without creating anything.- Repository/organization secrets (
PACT_BROKER_TOKEN,PLATFORM_TOKEN) and environment secrets (KUBECONFIG_STAGING, tied toenvironment: staging, with required reviewers if desired). - OIDC instead of long-lived keys: to talk to the cloud (AWS/GCP/Azure) or to an external registry, the workflow obtains an ephemeral token through federated identity (
permissions: id-token: write+aws-actions/configure-aws-credentials) instead of storing an access key in a secret. It is the recommended practice and the one TechCorp will use for the production cluster if one day a workflow needs to touch it (with GitOps, it does not). - Never
echoa secret into the logs; GitHub masks them, but base64 or transformations expose them.
- The
@techcorp/common-http pipeline
@techcorp/common-http pipelineThe library (04-01) has its own, shorter workflow: npm ci, npm test, and on a v* tag, npm publish to GitHub Packages:
# techcorp/common-http/.github/workflows/publish.yml (excerpt)
on: { push: { tags: ["v*"] } }
permissions: { contents: read, packages: write }
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm, registry-url: https://npm.pkg.github.com, scope: "@techcorp" }
- run: npm ci && npm test
- run: npm publish
env: { NODE_AUTH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" }Each service pins in its package.json the version it uses ("@techcorp/common-http": "^2.3.0") and updates it whenever it wants, with Renovate/Dependabot opening the PR: the library does not force anyone to redeploy, which was the condition for it to exist (04-01).
- Luis's rule: one reusable workflow for the six services
"If it is done more than once per service, automate it before the second time." The ci.yml of section 3 has 80 lines and would be identical in Catalog, Inventory, Payments, Notifications and Customers except for the name. GitHub Actions allows a reusable workflow (workflow_call) that lives in techcorp/platform:
# techcorp/platform/.github/workflows/node-service-ci.yml
on:
workflow_call:
inputs:
service-name: { required: true, type: string }
with-integration: { type: boolean, default: true } # Notifications has no DB: it can disable it
secrets:
PACT_BROKER_TOKEN: { required: true }
jobs:
tests: ... # exactly the steps of section 3, with ${{ inputs.service-name }} where orders-service was
image: ...And in each service, ci.yml shrinks to:
# techcorp/catalog-service/.github/workflows/ci.yml
name: CI
on: { push: { branches: [main], tags: ["v*"] }, pull_request: }
permissions: { contents: read, packages: write }
jobs:
ci:
uses: techcorp/platform/.github/workflows/node-service-ci.yml@v1 # @v1: tag of the platform repo; updated on purpose
with: { service-name: catalog-service }
secrets: inheritThe reusable workflow is to the pipelines what @techcorp/common-http is to the services: it is versioned (@v1), each service adopts the new version when it wants, and the node-service-template template (04-01) already ships these nine lines. A change to the pipeline (adding Trivy, bumping an action's version) is made once.
- DORA metrics: measuring the pipeline
The four DORA metrics (DevOps Research and Assessment) measure whether all of the above is good for anything:
| Metric | What it measures | TechCorp before (monolith, 01-05) | Target with the pipeline |
|---|---|---|---|
| Deployment frequency | How often production is reached | Thursday night, biweekly | Several times a day, per service |
| Change lead time | From commit to production | 1-2 weeks (waiting for the train) | < 1 day (< 1 hour for hotfixes) |
| Change failure rate | % of deployments that cause an incident or a rollback | High: frequent rollbacks | < 15% |
| Time to restore (MTTR) | How long it takes to recover the service after a failure | 50 min without selling on Black Friday | Minutes: rollout undo or revert the commit (05-04) |
The first two are provided by the pipeline itself (commit date, date of the Argo sync); the other two need the incident management of 06-05. What matters is the relationship between them: deploying more often with smaller changes lowers the failure rate and the MTTR, which is the opposite of the Thursday-night intuition ("we deploy rarely to risk little").
Common Mistakes and Tips
- A "system" pipeline that tests and deploys all the services together. It is the monolith's release train with more YAML. One pipeline per service and contracts as the guarantee.
- Rebuilding the image for production. What was tested is not what gets deployed. Semver tag on the already-tested
sha-image. can-i-deployas an optional step orcontinue-on-error. It ends up being ignored. It is a gate: if it says no, there is no deployment.kubectl applywithoutrollout status. The pipeline is green and the new replicas are inCrashLoopBackOff.- Copying
ci.ymlbetween repositories. By the third version of an action, six identical PRs.workflow_call. - Long-lived secrets in variables when OIDC is available; a developer's personal tokens in organization secrets (they stop working when they leave).
- Tip: run the
workflowlocally withactto debug; and keep in each repository'sREADMEthe link to the reusable workflow and to the runbooks, so that "how is this deployed" has an answer.
Exercises
Exercise 1. The CI of catalog-service (provider) must verify the pacts its consumers publish. Write the steps of the contract job of its ci.yml (without repeating checkout/setup-node) and explain which environment variables the Verifier of 04-05 needs to work with the Broker instead of a local file, and why it must run record-deployment after deploying.
Exercise 2. A Payments developer opens a PR that changes the payload of the payment.confirmed event. At which stage of the Payments pipeline would an incompatibility with orders-service be detected, with the tools of 04-05, and what would need to be added so that can-i-deploy blocked it?
Exercise 3. Marta asks that Catalog and Notifications be promoted to production automatically after passing the E2E test in staging, but that Orders and Payments require human approval. Describe how this is implemented with what we have seen (GitHub environments, workflow_call, promotion PR, Argo CD) without duplicating the workflow.
Solutions
Solution 1. Steps: npm ci → npm run test:contract with PACT_BROKER_BASE_URL, PACT_BROKER_TOKEN, GIT_COMMIT=${{ github.sha }} and GIT_BRANCH=${{ github.ref_name }} in env; the Verifier of 04-05 uses pactBrokerUrl and pactBrokerToken instead of pactUrls, providerVersion: process.env.GIT_COMMIT, providerVersionBranch, publishVerificationResult: true (only in CI: process.env.CI === 'true') and consumerVersionSelectors: [{ mainBranch: true }, { deployedOrReleased: true }] to verify both the pacts of each consumer's main branch and those of the deployed versions. After deploying to an environment, pact-broker record-deployment --pacticipant catalog-service --version $GIT_COMMIT --environment staging tells the Broker which version of Catalog is in staging: without that record, the Orders can-i-deploy does not know which provider to check against and answers "no" for lack of information.
Solution 2. At the contract stage, but on the consumer's side: 04-05 fixed JSON Schemas for events with Ajv and a test that orders-service tolerates new fields in payment.confirmed; if Payments removes or renames a field (amount → quantity), it is caught by Payments' own schema test if the schema is shared in contracts/asyncapi.yaml (it fails in Payments' npm test), or, if not, by the Orders events test when it updates the schema. For can-i-deploy to block it, a message pact would be needed (Pact supports message pacts: Orders declares the message it expects; Payments verifies it with a MessageProviderPact that produces the real event) published to the same Broker: then the Payments→Orders relationship appears like any other and can-i-deploy checks it. It is the natural evolution of the JSON Schema of 04-05.
Solution 3. The reusable CD workflow (node-service-cd.yml) receives an auto-promotion: boolean input. Its last job opens the promotion PR in techcorp/platform (change of newTag in overlays/prod); if auto-promotion is true, the same job merges it (gh pr merge --auto), and Argo CD syncs; if it is false, the PR stays open and the CODEOWNERS of k8s/orders-service/ and k8s/payments-service/ requires the review of Luis or the Payments lead. In addition, the production job uses environment: production in GitHub with required reviewers for those two services. No workflow is duplicated: Catalog calls with auto-promotion: true, Orders with false.
Conclusion
TechCorp's pipeline turns every push into an automatic, per-service sequence: ci.yml with actions/setup-node@v4 (Node 20, npm cache, GitHub Packages), npm run lint, npm test, npm run test:integration with Testcontainers on the runner, npm run test:contract and publication of pacts to the Pact Broker, docker/build-push-action with sha-<commit> and semver tags towards ghcr.io/techcorp/orders-service (with permissions: packages: write and the .npmrc as a build secret), scanning with Trivy, and can-i-deploy as a gate; cd.yml that pins the image in the Kustomize overlay (kustomize edit set image), runs the migrations Job, does kubectl apply + rollout status and launches the E2E test against staging; GitOps with Argo CD and techcorp/platform as the source of truth for production; semver and sha- on the same image; environments with the same manifests; GitHub secrets and OIDC; the @techcorp/common-http pipeline; the reusable workflow node-service-ci.yml@v1 that applies Luis's rule; and the DORA metrics to check that Thursday nights are over. One question remains that rollout status hides: what exactly happens to the traffic while the 1.0.0 replicas give way to the 1.0.1 ones, and how is a version deployed to 10% of users before giving it to everyone? That is the next lesson: deployment strategies.
Microservices Course
Module 1: Introduction to Microservices
- Basic Concepts of Microservices
- Advantages and Disadvantages of Microservices
- Comparison with the Monolithic Architecture
- When to Adopt Microservices: Decision Criteria
- The Course Case Study: TechCorp's Online Store
Module 2: Microservice Design
- Microservice Design Principles
- Decomposing Monolithic Applications
- Defining Bounded Contexts
- Data Management: One Database per Service
- Distributed Consistency: Sagas, CQRS and Event Sourcing
Module 3: Communication between Microservices
- RESTful APIs
- Asynchronous Messaging
- Communication Protocols: gRPC, GraphQL
- API Gateway and Backend for Frontend
- Service Discovery and Load Balancing
- API Contracts and Versioning
Module 4: Implementing Microservices
- Choosing Technologies and Tools
- Building a Simple Microservice
- Configuration Management
- Hands-On Integration: Consuming APIs and Publishing Events
- Testing Microservices: Unit, Integration and Contract Tests
Module 5: Deployment and Orchestration
- Containers and Docker
- Orchestration with Kubernetes
- CI/CD for Microservices
- Deployment Strategies: Rolling, Blue-Green and Canary
- Service Mesh: Istio and Linkerd
Module 6: Monitoring and Maintenance
- Monitoring and Logging
- Distributed Tracing with OpenTelemetry
- Error Handling and Recovery
- Scalability and Performance
- SLOs, Alerts and Incident Management
Module 7: Security in Microservices
- Authentication and Authorization
- Communication Security
- Security Practices
- Container and Kubernetes Security
