Reference

CDK8S Constructs API


Overview

GitLab BDA uses 15 CDK8S constructs to generate Kubernetes manifests. This reference documents each construct’s interface, parameters, generated resources, and usage.

Source location: deployments/gitlabbda/charts/constructs/

For WHY we use CDK8S, see CDK8S Approach Explanation.


Construct Catalog

Construct

Purpose

Resources Generated

Sync Wave

NamespaceConstruct

Create namespace

Namespace

Wave 0

[RbacConstruct](#rbacc construct)

Service accounts, roles

ServiceAccount (3), Role, RoleBinding

Wave 1

S3BucketsConstruct

Provision S3 buckets

Bucket (8, Crossplane)

Wave 1

S3CredentialsConstruct

S3 credential secrets

ExternalSecret (2)

Wave 1

AppSecretsConstruct

Application secrets

ExternalSecret (3)

Wave 2

DatabaseConstruct

PostgreSQL cluster

Cluster, Pooler, ObjectStore (CNPG)

Waves 2-3

RedisConstruct

Redis cache

PVC, StatefulSet, Service

Wave 2

GitLabHelmChart

GitLab platform

~100 resources (via Helm)

Waves 3-4

HarborConfigConstruct

Harbor configuration

ConfigMap (3)

Wave 4

HarborConstruct

Harbor registry

Deployment (4), Service (4)

Wave 4

HarborIngressConstruct

Harbor ingress

Ingress, IngressRoute (TCP)

Wave 5

PagesIngressConstruct

GitLab Pages ingress

Ingress (wildcard)

Wave 5

GitLabSshRoutingConstruct

SSH routing via Traefik

IngressRouteTCP

Wave 5

MonitoringConstruct

Prometheus scraping

ServiceMonitor (2)

Wave 6


NamespaceConstruct

Purpose: Create Kubernetes namespace for all GitLab BDA resources.

Source: constructs/namespace.ts

Interface

export interface NamespaceProps {
  name: string;  // Namespace name
}

Parameters

Parameter

Type

Required

Description

name

string

Yes

Kubernetes namespace name (DNS label format)

Generated Resources

  • Namespace (1)

Example Usage

const namespace = new NamespaceConstruct(this, 'namespace', {
  name: 'gitlabbda',
});

Generated Manifest

apiVersion: v1
kind: Namespace
metadata:
  name: gitlabbda
  annotations:
    argocd.argoproj.io/sync-wave: "0"
  labels:
    app.kubernetes.io/part-of: gitlab

RbacConstruct

Purpose: Create ServiceAccounts and RBAC for GitLab, Harbor, and monitoring.

Source: constructs/rbac.ts

Interface

export interface RbacProps {
  namespace: string;  // Target namespace
}

Generated Resources

  • ServiceAccount (3): gitlab, harbor, monitoring

  • Role (1): gitlab-role (permissions for all ServiceAccounts)

  • RoleBinding (1): Binds ServiceAccounts to Role

ServiceAccounts Created

  1. gitlab - Used by GitLab pods (webservice, gitaly, sidekiq, etc.)

  2. harbor - Used by Harbor pods (core, registry, jobservice, portal)

  3. monitoring - Used by monitoring resources (future)

Permissions Granted

# Role: gitlab-role
rules:
  - apiGroups: [""]
    resources: [secrets, configmaps]
    verbs: [get, list, watch]
  - apiGroups: [""]
    resources: [pods, pods/log]
    verbs: [get, list, watch]

Example Usage

const rbac = new RbacConstruct(this, 'rbac', {
  namespace: 'gitlabbda',
});

// Reference ServiceAccount in other constructs
const gitlabSA = rbac.gitlabServiceAccount.name;  // "gitlab"

Public Members

public readonly gitlabServiceAccount: KubeServiceAccount;
public readonly harborServiceAccount: KubeServiceAccount;
public readonly monitoringServiceAccount: KubeServiceAccount;

S3BucketsConstruct

Purpose: Provision 8 S3 buckets via Crossplane.

Source: constructs/s3-buckets.ts

Interface

export interface S3BucketsProps {
  namespace: string;  // Not used (buckets in crossplane-system)
  buckets: {
    artifacts: string;
    uploads: string;
    lfs: string;
    pages: string;
    registry: string;
    backups: string;
    postgresbackups: string;
    cache: string;
  };
}

Generated Resources

  • Bucket (8, Crossplane CRD) in crossplane-system namespace

Bucket Configuration

All buckets use identical configuration:

apiVersion: s3.aws.upbound.io/v1beta2
kind: Bucket
metadata:
  name: {bucket-name}
  namespace: crossplane-system  # NOT deployment namespace
  annotations:
    argocd.argoproj.io/sync-wave: "1"
spec:
  forProvider:
    region: fsn1  # Hetzner region
  providerConfigRef:
    name: hetzner-s3  # Infrastructure-managed ProviderConfig
  managementPolicies: [Observe, Create, Delete]  # Skip Update (no tagging)
  deletionPolicy: Orphan  # Safety: keep bucket on CR deletion

Buckets Created

Bucket

Purpose

Approximate Size (2-5 users)

artifacts-gitlabbda-kup6s

CI/CD artifacts

5-20GB

uploads-gitlabbda-kup6s

User uploads

1-5GB

lfs-gitlabbda-kup6s

Git LFS objects

0-50GB (varies)

pages-gitlabbda-kup6s

GitLab Pages

0.1-5GB

registry-gitlabbda-kup6s

Harbor images

10-100GB

backups-gitlabbda-kup6s

GitLab backups

20-50GB

postgresbackups-gitlabbda-kup6s

CNPG backups

5-20GB

cache-gitlabbda-kup6s

Runner cache

5-20GB

Dependencies

  • Crossplane operator installed (cluster infrastructure)

  • provider-aws-s3 installed (Upbound provider)

  • ProviderConfig hetzner-s3 exists (cluster infrastructure)

Example Usage

const s3Buckets = new S3BucketsConstruct(this, 's3-buckets', {
  namespace: 'gitlabbda',
  buckets: {
    artifacts: 'artifacts-gitlabbda-kup6s',
    uploads: 'uploads-gitlabbda-kup6s',
    // ... (8 total)
  },
});

For complete S3 bucket details, see S3 Buckets Reference.


S3CredentialsConstruct

Purpose: Sync S3 credentials from application-secrets namespace via External Secrets Operator (ESO).

Source: constructs/s3-credentials.ts

Interface

export interface S3CredentialsProps {
  namespace: string;  // Target namespace for secrets
  s3: {
    endpoint: string;
    region: string;
    buckets: {
      artifacts: string;
      uploads: string;
      lfs: string;
      pages: string;
      backups: string;
      cache: string;
      registry: string;  // Harbor
      postgresbackups: string;  // CNPG
    };
  };
}

Generated Resources

  • ExternalSecret gitlab-s3-credentials - GitLab S3 connection details

  • ExternalSecret harbor-s3-credentials - Harbor registry S3 connection

Secrets Generated

gitlab-s3-credentials

Keys:

  • connection - GitLab S3 connection YAML (includes credentials, endpoint, buckets)

  • s3cfg - s3cmd configuration (for Toolbox backups)

  • AWS_ACCESS_KEY_ID - S3 access key

  • AWS_SECRET_ACCESS_KEY - S3 secret key

Used by: GitLab Webservice, Sidekiq, Toolbox, Pages

harbor-s3-credentials

Keys:

  • access-key-id - S3 access key

  • secret-access-key - S3 secret key

  • region - S3 region (fsn1)

  • bucket - Registry bucket name

  • endpoint - S3 endpoint URL

Used by: Harbor Registry

ClusterSecretStore Reference

Both ExternalSecrets reference application-secrets-store (cluster-wide):

spec:
  secretStoreRef:
    name: application-secrets-store
    kind: ClusterSecretStore
  data:
    - secretKey: AWS_ACCESS_KEY_ID
      remoteRef:
        key: hetzner-s3  # Secret in application-secrets namespace
        property: access-key-id

Example Usage

const s3Creds = new S3CredentialsConstruct(this, 's3-credentials', {
  namespace: 'gitlabbda',
  s3: config.s3,
});

For secrets architecture, see Secrets Reference.


AppSecretsConstruct

Purpose: Sync application secrets (SMTP, Harbor OAuth, Harbor registry) via ESO.

Source: constructs/app-secrets.ts

Interface

export interface AppSecretsProps {
  namespace: string;
  smtp: {
    host: string;
    port: number;
    domain: string;
    from: string;
    replyTo: string;
  };
}

Generated Resources

  • ExternalSecret gitlab-smtp-password - SMTP credentials

  • ExternalSecret harbor-secrets - Harbor OAuth and registry secret

  • ExternalSecret gitlab-initial-root-password - GitLab admin password

Secrets Generated

gitlab-smtp-password

Keys:

  • password - SMTP password (from application-secrets/smtp)

Used by: GitLab Webservice (email delivery)

harbor-secrets

Keys:

  • gitlab-oauth-client-id - GitLab OAuth app ID

  • gitlab-oauth-client-secret - GitLab OAuth app secret

  • registry-secret - Harbor internal secret (CORE_SECRET, JOBSERVICE_SECRET)

Used by: Harbor Core (OAuth authentication)

gitlab-initial-root-password

Keys:

  • password - GitLab root user password

Used by: GitLab initial setup (set root password)

Example Usage

const appSecrets = new AppSecretsConstruct(this, 'app-secrets', {
  namespace: 'gitlabbda',
  smtp: config.smtp,
});

DatabaseConstruct

Purpose: Deploy PostgreSQL cluster via CloudNativePG (CNPG).

Source: constructs/database.ts

Interface

export interface DatabaseProps {
  namespace: string;
  storageClass: string;  // Longhorn storage class
  storageSize: string;  // PVC size (e.g., "10Gi")
  s3BackupEndpoint: string;  // S3 endpoint for backups
  s3BackupRegion: string;  // S3 region
  s3BackupBucket: string;  // Backup bucket name
}

Generated Resources

  • ObjectStore (Barman Cloud Plugin) - Wave 2

  • Cluster (CNPG) - Wave 3

  • Pooler (PgBouncer) - Wave 3

Cluster Specification

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: gitlab-postgres
spec:
  instances: 2  # Primary + Standby
  primaryUpdateStrategy: unsupervised  # Auto-failover

  postgresql:
    parameters:
      max_connections: "200"
      shared_buffers: "256MB"
      effective_cache_size: "1GB"

  bootstrap:
    initdb:
      database: gitlab
      owner: gitlab
      postInitSQL:
        - CREATE DATABASE harbor OWNER gitlab;  # Harbor DB

  storage:
    storageClass: longhorn-redundant-app  # 1 replica
    size: 10Gi  # Per instance

  backup:
    target: primary
    retentionPolicy: 30d

  plugins:
    - name: barman-cloud.cloudnative-pg.io  # S3 backups

  resources:
    requests: {memory: 256Mi, cpu: 100m}
    limits: {memory: 512Mi, cpu: 500m}

  monitoring:
    enablePodMonitor: true  # Prometheus scraping

  affinity:
    enablePodAntiAffinity: true  # Spread across nodes
    topologyKey: kubernetes.io/hostname

Pooler Specification

apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
  name: gitlab-postgres-pooler
spec:
  cluster:
    name: gitlab-postgres
  instances: 2  # HA pooler
  type: rw  # Read-write pooler
  pgbouncer:
    poolMode: transaction  # Connection pooling mode
    parameters:
      max_client_conn: "1000"  # Max client connections
      default_pool_size: "25"  # Backend connections per user/db

Public Members

public readonly clusterName: string = 'gitlab-postgres';
public readonly objectStoreName: string = 'gitlab-postgres-backup';

Connection Details

Applications connect to: gitlab-postgres-pooler.gitlabbda.svc.cluster.local:5432

Databases created:

  • gitlab - GitLab data

  • harbor - Harbor data

User: gitlab (owner of both databases)

Password secret: gitlab-postgres-app (CNPG-generated, key: password)

Example Usage

const database = new DatabaseConstruct(this, 'database', {
  namespace: 'gitlabbda',
  storageClass: 'longhorn-redundant-app',
  storageSize: '10Gi',
  s3BackupEndpoint: 'https://fsn1.your-objectstorage.com',
  s3BackupRegion: 'fsn1',
  s3BackupBucket: 'postgresbackups-gitlabbda-kup6s',
});

// Use in GitLab configuration
const pgHost = `${database.clusterName}-pooler`;  // "gitlab-postgres-pooler"

For PostgreSQL architecture, see GitLab Components: PostgreSQL.


RedisConstruct

Purpose: Deploy Redis single instance for cache and job queue.

Source: constructs/redis.ts

Interface

export interface RedisProps {
  namespace: string;
  storageClass: string;  // Longhorn storage class
  storageSize: string;  // PVC size (e.g., "10Gi")
  replicas?: number;  // Default: 1
}

Generated Resources

  • PersistentVolumeClaim redis-data - Wave 2

  • StatefulSet redis - Wave 2

  • Service redis (ClusterIP) - Wave 2

StatefulSet Specification

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: redis
  replicas: 1
  template:
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          command:
            - redis-server
            - --appendonly
            - "yes"
            - --save
            - "60"
            - "1"
          ports:
            - containerPort: 6379
          volumeMounts:
            - name: data
              mountPath: /data
          resources:
            requests: {cpu: 100m, memory: 128Mi}
            limits: {cpu: 500m, memory: 512Mi}
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: redis-data

Persistence Configuration

  • AOF: --appendonly yes (every write logged)

  • Snapshot: --save 60 1 (if ≥1 key changed in 60 seconds)

Public Members

public readonly service: KubeService;

Connection Details

Applications connect to: redis.gitlabbda.svc.cluster.local:6379

No authentication: GitLab and Harbor access Redis without password (within namespace).

Databases:

  • DB 0 - GitLab (sessions, cache, job queue)

  • DB 1 - Harbor JobService (job queue)

  • DB 2 - Harbor Registry (blob descriptor cache)

Example Usage

const redis = new RedisConstruct(this, 'redis', {
  namespace: 'gitlabbda',
  storageClass: 'longhorn',  // 2 replicas
  storageSize: '10Gi',
  replicas: 1,
});

For Redis architecture, see GitLab Components: Redis.


GitLabHelmChart

Purpose: Render GitLab Helm chart to Kubernetes manifests using CDK8S.

Source: constructs/gitlab-helm.ts

Interface

export interface GitLabHelmProps {
  namespace: string;
  isFreshInstall: boolean;  // Remove upgrade check hook if true
  gitlabDomain: string;
  pagesDomain: string;
  registryDomain: string;  // Not used (Harbor instead)
  s3: {
    endpoint: string;
    region: string;
    buckets: {
      artifacts: string;
      uploads: string;
      lfs: string;
      pages: string;
      backups: string;
      cache: string;
    };
  };
  postgresql: {
    host: string;  # E.g., gitlab-postgres-pooler.gitlabbda.svc.cluster.local
    port: number;  # 5432
    database: string;  # gitlab
  };
  smtp: {
    host: string;
    port: number;
    domain: string;
    from: string;
    replyTo: string;
  };
}

Generated Resources

~100 resources including:

  • Deployment (webservice, gitaly, sidekiq, shell, pages, workhorse)

  • Service (webservice, gitaly, shell, pages)

  • Ingress (webservice, registry - disabled)

  • ConfigMap (20+)

  • Secret (10+)

  • ServiceAccount

  • Role, RoleBinding

  • HorizontalPodAutoscaler

  • PodDisruptionBudget

Helm Chart Rendering Process

// 1. Build Helm values object
const helmValues = {
  global: {
    edition: 'ce',
    hosts: { gitlab: { name: gitlabDomain }, ... },
    psql: { host, port, database, ... },
    redis: { host: 'redis.gitlabbda.svc', ... },
    minio: { enabled: false },  // Use S3 instead
  },
  postgresql: { install: false },  // Use CNPG instead
  redis: { install: false },  // Use dedicated Redis
  registry: { enabled: false },  // Use Harbor instead
  gitlab: {
    webservice: { minReplicas: 2, ... },
    gitaly: { persistence: { storageClass: 'hcloud-volumes', size: '20Gi' } },
    ...
  },
};

// 2. Write values to temp file
writeFileSync('/tmp/gitlab-helm-values.yaml', JSON.stringify(helmValues));

// 3. Render Helm chart to YAML
execSync(`helm template gitlab gitlab/gitlab --values /tmp/gitlab-helm-values.yaml > /tmp/gitlab-rendered.yaml`);

// 4. Post-process YAML (fix upgrade hook, add sync waves)
let yaml = readFileSync('/tmp/gitlab-rendered.yaml');
yaml = removeUpgradeCheckHook(yaml, isFreshInstall);
yaml = addSyncWaves(yaml);

// 5. Include rendered YAML in CDK8S
new Include(this, 'gitlab-manifests', {
  url: `/tmp/gitlab-rendered.yaml`,
});

Key Helm Values

Disabled built-ins:

postgresql: {install: false}  # Use CNPG
redis: {install: false}  # Use dedicated Redis
registry: {enabled: false}  # Use Harbor
minio: {enabled: false}  # Use Hetzner S3
prometheus: {install: false}  # Use cluster Prometheus

External dependencies:

global:
  psql:
    host: gitlab-postgres-pooler
    password: {secret: gitlab-postgres-app, key: password}
  redis:
    host: redis.gitlabbda.svc
  appConfig:
    lfs: {bucket: lfs-gitlabbda-kup6s, connection: {secret: gitlab-s3-credentials}}
    artifacts: {bucket: artifacts-gitlabbda-kup6s, ...}
    uploads: {bucket: uploads-gitlabbda-kup6s, ...}
    pages: {bucket: pages-gitlabbda-kup6s, ...}

Example Usage

const gitlabHelm = new GitLabHelmChart(this, 'gitlab-helm', {
  namespace: 'gitlabbda',
  isFreshInstall: false,
  gitlabDomain: 'gitlab.staging.bluedynamics.eu',
  pagesDomain: 'pages.staging.bluedynamics.eu',
  registryDomain: 'registry.staging.bluedynamics.eu',  # Not used
  s3: config.s3,
  postgresql: {
    host: 'gitlab-postgres-pooler.gitlabbda.svc.cluster.local',
    port: 5432,
    database: 'gitlab',
  },
  smtp: config.smtp,
});

For Helm integration details, see CDK8S Approach: Helm Integration.


Harbor Constructs

Harbor is deployed via 3 constructs: Config, Core, Ingress.

HarborConfigConstruct

Purpose: Generate ConfigMaps for Harbor components.

Source: constructs/harbor-config.ts

Interface

export interface HarborConfigProps {
  namespace: string;
  harborDomain: string;
}

Generated Resources

  • ConfigMap harbor-registry-config - Docker Registry v2 configuration

  • ConfigMap harbor-jobservice-config - JobService worker pool configuration

  • ConfigMap harbor-portal-config - Nginx reverse proxy configuration

Example Config

Registry ConfigMap:

# Docker Registry v2 config.yml
storage:
  s3:
    region: ${REGISTRY_STORAGE_S3_REGION}
    bucket: ${REGISTRY_STORAGE_S3_BUCKET}
    accesskey: ${REGISTRY_STORAGE_S3_ACCESSKEY}
    secretkey: ${REGISTRY_STORAGE_S3_SECRETKEY}
    regionendpoint: ${REGISTRY_STORAGE_S3_REGIONENDPOINT}
  cache:
    blobdescriptor: redis  # Cache in Redis DB 2
redis:
  addr: redis:6379
  db: 2

HarborConstruct

Purpose: Deploy Harbor microservices (Core, Registry, JobService, Portal).

Source: constructs/harbor.ts

Interface

export interface HarborProps {
  namespace: string;
  serviceAccountName: string;  # From RbacConstruct
  harborDomain: string;
  gitlabDomain: string;  # For OAuth
  harborVersion: string;  # E.g., v2.14.0
}

Generated Resources

  • Deployment harbor-core - API server

  • Deployment harbor-registry - OCI image storage

  • Deployment harbor-jobservice - Background jobs

  • Deployment harbor-portal - Web UI (nginx)

  • Service (4, ClusterIP) - One per deployment

Core Deployment

Key environment variables:

env:
  # Database
  - name: POSTGRESQL_HOST
    value: gitlab-postgres-pooler
  - name: POSTGRESQL_DATABASE
    value: harbor

  # Redis
  - name: REDIS_HOST
    value: redis

  # OAuth
  - name: AUTH_MODE
    value: oidc_auth
  - name: OIDC_ENDPOINT
    value: https://gitlab.staging.bluedynamics.eu
  - name: OIDC_CLIENT_ID
    valueFrom: {secretKeyRef: {name: harbor-secrets, key: gitlab-oauth-client-id}}

Node placement:

nodeSelector:
  kubernetes.io/arch: amd64  # Harbor images AMD64-only
tolerations:
  - key: kubernetes.io/arch
    operator: Equal
    value: amd64
    effect: NoSchedule

Public Members

public readonly coreService: KubeService;
public readonly portalService: KubeService;

HarborIngressConstruct

Purpose: Create Ingress for Harbor web UI and Docker API.

Source: constructs/harbor-ingress.ts

Interface

export interface HarborIngressProps {
  namespace: string;
  harborDomain: string;
}

Generated Resources

  • Ingress harbor-ingress - HTTPS traffic

  • IngressRouteTCP (via Traefik CRD) - Docker Registry v2 API

Ingress Specification

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: harbor-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-cluster-issuer
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
  ingressClassName: traefik
  tls:
    - hosts: [registry.staging.bluedynamics.eu]
      secretName: harbor-tls
  rules:
    - host: registry.staging.bluedynamics.eu
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: harbor-portal
                port: {number: 8080}

For Harbor architecture, see Harbor Integration.


PagesIngressConstruct

Purpose: Wildcard ingress for GitLab Pages static sites.

Source: constructs/pages-ingress.ts

Interface

export interface PagesIngressProps {
  namespace: string;
  pagesDomain: string;  # E.g., pages.staging.bluedynamics.eu
}

Generated Resources

  • Ingress gitlab-pages-ingress (wildcard)

Ingress Specification

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitlab-pages-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-cluster-issuer
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
  ingressClassName: traefik
  tls:
    - hosts: ['*.pages.staging.bluedynamics.eu']
      secretName: gitlab-pages-tls
  rules:
    - host: '*.pages.staging.bluedynamics.eu'
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: gitlab-gitlab-pages
                port: {number: 8090}

Wildcard DNS required: *.pages.staging.bluedynamics.eu must resolve to cluster LoadBalancer.

Example Usage

new PagesIngressConstruct(this, 'pages-ingress', {
  namespace: 'gitlabbda',
  pagesDomain: 'pages.staging.bluedynamics.eu',
});

GitLabSshRoutingConstruct

Purpose: Route SSH traffic (port 22) to GitLab Shell via Traefik.

Source: constructs/gitlab-ssh-routing.ts

Interface

export interface GitLabSshRoutingProps {
  namespace: string;
}

Generated Resources

  • IngressRouteTCP gitlab-ssh (Traefik CRD)

IngressRouteTCP Specification

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
  name: gitlab-ssh
spec:
  entryPoints: [ssh]  # Traefik SSH entrypoint (port 22)
  routes:
    - match: HostSNI(`*`)  # All SSH traffic
      services:
        - name: gitlab-gitlab-shell
          port: 22

Why Traefik TCP routing?

  • Cost savings - No separate LoadBalancer for SSH (shares Traefik LB)

  • Consistent ingress - All traffic (HTTP, HTTPS, SSH) through single entry point

Traefik entrypoint configuration (cluster infrastructure):

# Traefik Helm values
ports:
  ssh:
    port: 22
    expose: true
    exposedPort: 22
    protocol: TCP

Example Usage

new GitLabSshRoutingConstruct(this, 'ssh-routing', {
  namespace: 'gitlabbda',
});

For SSH access, see Endpoints & Ports Reference.


MonitoringConstruct

Purpose: Create ServiceMonitors for Prometheus scraping.

Source: constructs/monitoring.ts

Interface

export interface MonitoringProps {
  namespace: string;
}

Generated Resources

  • ServiceMonitor gitlab-postgres (Prometheus Operator CRD)

  • ServiceMonitor harbor-core (future)

ServiceMonitor Specification

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: gitlab-postgres
spec:
  selector:
    matchLabels:
      cnpg.io/cluster: gitlab-postgres
  endpoints:
    - port: metrics
      interval: 30s
      path: /metrics

Metrics collected:

  • PostgreSQL: Connections, queries, replication lag, disk usage

  • Harbor: (Future) API requests, image pulls/pushes, scan results

Example Usage

new MonitoringConstruct(this, 'monitoring', {
  namespace: 'gitlabbda',
});

For monitoring setup, see Monitoring & Observability (future).


Sync Wave Strategy

ArgoCD sync waves ensure resources are created in dependency order:

Wave

Resources

Purpose

0

Namespace

Namespace must exist first

1

RBAC, S3 Buckets, S3 Credentials

Infrastructure and credentials

2

App Secrets, Redis, ObjectStore (CNPG)

Dependencies for applications

3

PostgreSQL Cluster, Pooler

Database ready for applications

4

GitLab, Harbor

Applications (depend on DB, Redis, S3)

5

Ingresses

External access (after applications ready)

6

Monitoring

Observability (after everything deployed)

For sync wave details, see ArgoCD GitOps Workflow.


Type Safety

All constructs are type-checked at build time:

// TypeScript catches errors:
const database = new DatabaseConstruct(this, 'db', {
  storageSize: 10,  // ERROR: Type 'number' not assignable to 'string'
});

// Correct:
const database = new DatabaseConstruct(this, 'db', {
  storageSize: '10Gi',  // OK: string with Kubernetes quantity format
});

For type safety benefits, see CDK8S Approach: Type Safety.


Summary

15 CDK8S constructs generate ~150 Kubernetes resources:

  • Infrastructure (4): Namespace, RBAC, S3 Buckets, S3 Credentials

  • Secrets (1): App Secrets (SMTP, OAuth, passwords)

  • Databases (2): PostgreSQL (CNPG), Redis

  • Applications (2): GitLab (Helm), Harbor (custom)

  • Configuration (1): Harbor ConfigMaps

  • Ingress (3): Harbor, GitLab Pages, SSH routing

  • Monitoring (1): ServiceMonitors

Key patterns:

  • Separation of concerns - Each construct handles one responsibility

  • Dependency injection - Constructs receive dependencies via props

  • Type safety - TypeScript prevents configuration errors

  • Sync waves - Resources created in correct order via annotations

For usage examples, see: