# SkillPatch skill: gke-app-onboarding

This skill guides agents through the full GKE application onboarding process, including app assessment, containerization with Dockerfiles or Cloud Native Buildpacks, image management with Artifact Registry, and Kubernetes manifest generation. It provides step-by-step workflows, best practices, and code templates for deploying a new application to Google Kubernetes Engine for the first time.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/gke-app-onboarding
curl -sSL https://skillpatch.dev/install_skill/gke-app-onboarding | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (5)

- `SKILL.md`
- `assets/deployment.yaml`
- `assets/Dockerfile`
- `assets/index.js`
- `assets/package.json`


### `SKILL.md`

````markdown
---
name: gke-app-onboarding
description: >-
  Manages GKE application onboarding, covering containerization, deployment
  manifests, and migration. Use when onboarding or deploying an application to
  GKE for the first time, or containerizing an app for GKE. Don't use for
  general GKE cluster administration or upgrades (use gke-basics or
  gke-upgrades instead).
metadata:
  category: Containers
---

# GKE App Onboarding

This reference provides workflows for containerizing and deploying applications
to GKE for the first time.

> **MCP Tools:** `apply_k8s_manifest`, `get_k8s_resource`,
> `get_k8s_rollout_status`, `get_k8s_logs`, `describe_k8s_resource`

## Workflow

### 1. App Assessment

Before containerizing, assess the application:

-   **Language & Framework**: Identify the tech stack
-   **Dependencies**: List required libraries and external services
-   **Configuration**: How is the app configured? (env vars, config files,
    secrets)
-   **Statefulness**: Does it need persistent storage? (databases, file storage)
-   **Networking**: Port mapping and protocol (HTTP, gRPC, TCP)
-   **Health endpoints**: Does the app expose health check endpoints?

### 2. Containerization

Create a container image:

**Dockerfile (recommended for most apps):**

```dockerfile
# Multi-stage build for smaller, more secure images
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server .

FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
```

**Best practices:**

-   Use multi-stage builds to keep production images small
-   Use distroless or minimal base images to reduce attack surface
-   Run as non-root user
-   Log to `stdout` and `stderr` for Cloud Logging collection

For applications where writing a Dockerfile is not preferred, you can use
[**Cloud Native Buildpacks**](https://buildpacks.io/) to automatically detect
the language and build a container image:

```bash
pack build <image> --builder gcr.io/buildpacks/builder:latest
```

### 3. Image Management

Build and store the container image:

```bash
# Configure Docker for Artifact Registry
gcloud auth configure-docker <REGION>-docker.pkg.dev --quiet

# Build and push
docker build -t <REGION>-docker.pkg.dev/<PROJECT>/<REPO>/<IMAGE>:<TAG> .
docker push <REGION>-docker.pkg.dev/<PROJECT>/<REPO>/<IMAGE>:<TAG>
```

**Vulnerability scanning**: Enable automatic scanning in Artifact Registry to
detect issues in base images and dependencies.

```bash
# Check scan results
gcloud artifacts docker images describe \
  <REGION>-docker.pkg.dev/<PROJECT>/<REPO>/<IMAGE>:<TAG> \
  --show-package-vulnerability \
  --quiet
```

### 4. Manifest Generation

Generate Kubernetes manifests for the application:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: <REGION>-docker.pkg.dev/<PROJECT>/<REPO>/<IMAGE>:<TAG>
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "250m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 10
        readinessProbe:
          httpGet:
            path: /readyz
            port: 8080
          initialDelaySeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP
```

**Checklist for manifests:**

-   Resource requests and limits set
-   Liveness and readiness probes configured
-   At least 2 replicas for production
-   Service type appropriate (ClusterIP for internal, use Gateway API for
    external)

### 5. Deploy

```
# MCP (preferred)
apply_k8s_manifest(parent="projects/<PROJECT>/locations/<REGION>/clusters/<CLUSTER>", yamlManifest="<manifest>")

# Verify
get_k8s_rollout_status(parent="...", resourceType="deployment", name="my-app")
get_k8s_resource(parent="...", resourceType="pod", labelSelector="app=my-app")
```

**kubectl fallback:**

```bash
kubectl apply -f manifests/
kubectl rollout status deployment/my-app
kubectl get pods -l app=my-app
```

## Next Steps

Once the application is running on GKE:

-   Configure autoscaling — see the `gke-scaling` skill
-   Set up observability — see the `gke-observability` skill
-   Harden security — see the `gke-security` skill
-   Configure reliability (PDBs, topology spread) — see the `gke-reliability`
    skill

````


### `assets/deployment.yaml`

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-app-deployment
  labels:
    app: node-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: node-app
  template:
    metadata:
      labels:
        app: node-app
    spec:
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: node-app
        # Replace <IMAGE_PATH> with your actual container image path
        # e.g., us-docker.pkg.dev/my-project/my-repo/node-app@sha256:0123456789abcdef...
        image: gcr.io/my-project/node-app@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
        ports:
        - containerPort: 8080
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            cpu: "200m"
            memory: "256Mi"
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: node-app-service
spec:
  selector:
    app: node-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer

```


### `assets/Dockerfile`

```
# Use official lightweight Node.js image
FROM node:18-slim

# Set working directory
WORKDIR /app

# Copy package files and install dependencies (none in this case, but good practice)
COPY package*.json ./
RUN npm install --production

# Copy the application code
COPY index.js .

# Expose the application port
EXPOSE 8080

# Run the application as a non-root user for security
USER node

# Start the application
CMD [ "node", "index.js" ]

```


### `assets/index.js`

```
const http = require('http');

const port = process.env.PORT || 8080;

const server = http.createServer((req, res) => {
  if (req.url === '/healthz') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('OK');
  } else {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello, World!\n');
  }
});

server.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

```


### `assets/package.json`

```
{
  "name": "simple-node-app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  }
}

```
