agentsclimarketplace

Bazel k8s expert

Skill kinhluan/rules-quarkus-skills/.agent-skills/bazel-k8s-expert

πŸ€– Complete AI expert ecosystem for Modern Java, Quarkus & Bazel development β˜•οΈβš‘οΈ Coverage for Vert.x, GraalVM, Maven/Gradle migration, and more πŸš€

Install
npx -y skills add kinhluan/rules-quarkus-skills --skill bazel-k8s-expert

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Expert knowledge for deploying Quarkus/Java applications to Kubernetes using Bazel. Covers rules_k8s, Helm, Kustomize, ConfigMaps, Secrets, and health probes.

SKILL.md

13.9 KB, as published. Nobody here has run it

bazel-k8s-expert

Keyword: k8s | Platforms: gemini,claude,codex

Bazel Kubernetes Expert Skill - Deploying, managing, and operating Quarkus and Java applications on Kubernetes using Bazel-native tooling.

Core Mandates

  • GitOps-Ready: All K8s manifests must be generated from Bazel targets, never hand-edited YAML in production.
  • Hermetic Deployments: Use rules_k8s or helm via Bazel to ensure deployment artifacts match built images exactly.
  • Health Probes: Every deployment must define @Liveness and @Readiness probes (see microprofile-expert).
  • Resource Limits: Always set CPU/memory requests and limits to prevent cluster starvation.
  • Config Externalization: Use ConfigMaps and Secrets for environment-specific values; never bake config into images.

Setup (Bzlmod)

MODULE.bazel

bazel_dep(name = "rules_k8s", version = "0.7")
bazel_dep(name = "rules_helm", version = "0.1")

# For Kustomize support
bazel_dep(name = "rules_kustomize", version = "0.1")

rules_k8s Deployment

Basic Deployment + Service

# services/user/k8s/BUILD.bazel
load("@rules_k8s//k8s:defs.bzl", "k8s_deploy")

k8s_deploy(
    name = "user-service_deploy",
    template = ":deployment.yaml",
    images = {
        "user-service": "//services/user:user-service_image",
    },
)
# services/user/k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  labels:
    app: user-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
        - name: user-service
          image: user-service  # Replaced by rules_k8s
          ports:
            - containerPort: 8080
          env:
            - name: QUARKUS_HTTP_PORT
              value: "8080"
            - name: JAVA_TOOL_OPTIONS
              value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /q/health/live
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /q/health/ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user-service
  ports:
    - port: 80
      targetPort: 8080
  type: ClusterIP

Deploy to Cluster

# Apply deployment
bazel run //services/user/k8s:user-service_deploy.apply

# Delete deployment
bazel run //services/user/k8s:user-service_deploy.delete

# Describe deployment
bazel run //services/user/k8s:user-service_deploy.describe

ConfigMaps & Secrets

ConfigMap from Properties File

# services/order/k8s/BUILD.bazel
load("@rules_k8s//k8s:object.bzl", "k8s_object")

genrule(
    name = "order-config",
    srcs = ["application.properties"],
    outs = ["order-config.properties"],
    cmd = "cp $(SRCS) $@",
)

k8s_object(
    name = "order-configmap",
    template = "configmap.yaml",
    substitutions = {
        "{CONFIG_DATA}": "$(location :order-config)",
    },
)
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: order-service-config
data:
  application.properties: |
    quarkus.datasource.db-kind=postgresql
    quarkus.datasource.jdbc.url=jdbc:postgresql://postgres:5432/orders
    quarkus.hibernate-orm.database.generation=update

Secret from Bazel Secrets

# k8s/BUILD.bazel
load("@rules_k8s//k8s:object.bzl", "k8s_object")

k8s_object(
    name = "db-secret",
    template = "secret.yaml",
    substitutions = {
        "{DB_PASSWORD}": "$(location //secrets:db_password)",
    },
)
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  password: "{DB_PASSWORD}"

Mounting in Deployment

# deployment.yaml
spec:
  template:
    spec:
      containers:
        - name: order-service
          volumeMounts:
            - name: config
              mountPath: /app/config
            - name: secrets
              mountPath: /app/secrets
      volumes:
        - name: config
          configMap:
            name: order-service-config
        - name: secrets
          secret:
            secretName: db-credentials

Helm Charts

Chart Structure

services/user/helm/
β”œβ”€β”€ BUILD.bazel
β”œβ”€β”€ Chart.yaml
β”œβ”€β”€ values.yaml
β”œβ”€β”€ values-dev.yaml
β”œβ”€β”€ values-prod.yaml
└── templates/
    β”œβ”€β”€ deployment.yaml
    β”œβ”€β”€ service.yaml
    β”œβ”€β”€ ingress.yaml
    β”œβ”€β”€ configmap.yaml
    └── _helpers.tpl

BUILD.bazel for Helm

# services/user/helm/BUILD.bazel
load("@rules_helm//helm:defs.bzl", "helm_chart", "helm_push", "helm_template")

helm_chart(
    name = "user-service-chart",
    srcs = glob(["templates/**"]),
    chart_yaml = "Chart.yaml",
    values_yaml = "values.yaml",
)

helm_template(
    name = "user-service-template",
    chart = ":user-service-chart",
    values = "values-dev.yaml",
    namespace = "dev",
)

helm_push(
    name = "push-chart",
    chart = ":user-service-chart",
    repository = "oci://ghcr.io/myorg/charts",
    version = "1.0.0",
)

Template with Image Reference

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "user-service.fullname" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.service.port }}
          env:
            - name: QUARKUS_PROFILE
              value: {{ .Values.quarkus.profile }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          livenessProbe:
            {{- toYaml .Values.livenessProbe | nindent 12 }}
          readinessProbe:
            {{- toYaml .Values.readinessProbe | nindent 12 }}

values.yaml

# values.yaml
replicaCount: 3

image:
  repository: ghcr.io/myorg/user-service
  pullPolicy: IfNotPresent
  tag: ""

quarkus:
  profile: prod

service:
  type: ClusterIP
  port: 8080

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

livenessProbe:
  httpGet:
    path: /q/health/live
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /q/health/ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

Deploy Helm Chart

# Template (dry run)
bazel run //services/user/helm:user-service-template

# Install/Upgrade
helm upgrade --install user-service \
  $(bazel cquery --output=files //services/user/helm:user-service-chart) \
  --values services/user/helm/values-prod.yaml \
  --namespace prod

# Push to OCI registry
bazel run //services/user/helm:push-chart

Kustomize

Base + Overlay Pattern

services/payment/k8s/
β”œβ”€β”€ base/
β”‚   β”œβ”€β”€ BUILD.bazel
β”‚   β”œβ”€β”€ kustomization.yaml
β”‚   β”œβ”€β”€ deployment.yaml
β”‚   └── service.yaml
└── overlays/
    β”œβ”€β”€ dev/
    β”‚   β”œβ”€β”€ BUILD.bazel
    β”‚   β”œβ”€β”€ kustomization.yaml
    β”‚   └── replica-patch.yaml
    └── prod/
        β”œβ”€β”€ BUILD.bazel
        β”œβ”€β”€ kustomization.yaml
        └── resource-patch.yaml

Base Configuration

# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

commonLabels:
  app: payment-service

images:
  - name: payment-service
    newTag: latest

Dev Overlay

# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: dev

resources:
  - ../../base

replicas:
  - name: payment-service
    count: 1

configMapGenerator:
  - name: payment-config
    literals:
      - QUARKUS_PROFILE=dev
      - LOG_LEVEL=DEBUG

Prod Overlay

# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: prod

resources:
  - ../../base

replicas:
  - name: payment-service
    count: 5

patches:
  - path: resource-patch.yaml

configMapGenerator:
  - name: payment-config
    literals:
      - QUARKUS_PROFILE=prod
      - LOG_LEVEL=INFO
# overlays/prod/resource-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  template:
    spec:
      containers:
        - name: payment-service
          resources:
            requests:
              memory: "512Mi"
              cpu: "500m"
            limits:
              memory: "1Gi"
              cpu: "1000m"

Bazel Kustomize Build

# overlays/dev/BUILD.bazel
load("@rules_kustomize//kustomize:defs.bzl", "kustomize_build")

kustomize_build(
    name = "dev-manifests",
    srcs = glob(["*.yaml"]),
    kustomization = "kustomization.yaml",
)

# Apply
# kubectl apply -k $(bazel cquery --output=files //services/payment/k8s/overlays/dev:dev-manifests)

Ingress

Basic Ingress

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: "letsencrypt"
spec:
  tls:
    - hosts:
        - api.example.com
      secretName: api-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /users
            pathType: Prefix
            backend:
              service:
                name: user-service
                port:
                  number: 80
          - path: /orders
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 80

Horizontal Pod Autoscaler (HPA)

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: user-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: user-service
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 100
          periodSeconds: 15
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60

Pod Disruption Budget

# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: user-service-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: user-service

Troubleshooting

Pod CrashLoopBackOff

# Check logs
kubectl logs -l app=user-service --tail=100

# Check events
kubectl describe pod user-service-xxx

# Common causes:
# 1. Missing ConfigMap/Secret β†’ Create missing resources
# 2. Wrong image tag β†’ Verify image was pushed
# 3. Port conflict β†’ Check containerPort matches app
# 4. Resource limits too low β†’ Increase memory/CPU limits
# 5. Liveness probe failing β†’ Check /q/health/live endpoint

Service Not Reachable

# Check service endpoints
kubectl get endpoints user-service

# Port-forward for testing
kubectl port-forward svc/user-service 8080:80

# Common causes:
# 1. Selector mismatch β†’ Check labels on pod and service
# 2. Wrong targetPort β†’ Must match containerPort
# 3. Network policies blocking β†’ Check NetworkPolicy rules

ImagePullBackOff

# Check image exists
kubectl describe pod user-service-xxx | grep -i "failed to pull"

# Common causes:
# 1. Image not pushed β†’ bazel run //services/user:push_user_service
# 2. Wrong imagePullSecrets β†’ Add registry credentials
# 3. Wrong image tag β†’ Check tag in deployment

Decision Trees

Choosing Deployment Strategy

Single service?
  β”œβ”€β”€ YES β†’ rules_k8s k8s_deploy (simplest)
  └── NO (multiple services)
        β”œβ”€β”€ Need templating? β†’ Helm charts
        β”œβ”€β”€ Need environment overlays? β†’ Kustomize
        └── Need both? β†’ Helm + Kustomize (Helm chart + Kustomize patches)

Config Management

Environment-specific values?
  β”œβ”€β”€ Simple key-value pairs β†’ ConfigMap literals
  β”œβ”€β”€ Complex config files β†’ ConfigMap from file
  β”œβ”€β”€ Sensitive data β†’ Secret (base64 encoded)
  β”œβ”€β”€ Many environments β†’ Kustomize configMapGenerator
  └── Dynamic values β†’ External Secrets Operator

References

Skill Interoperability

The bazel-k8s-expert ☸️ skill deploys containers built by:

  • bazel-oci-expert 🐳: OCI container images.
  • rules-quarkus πŸ”§: Quarkus applications built with Bazel.
  • microprofile-expert πŸ“‹: Health probes, metrics, and config.
  • bazel-expert πŸ—: Core Bazel build infrastructure.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.