7.3 Deploying Containerized Applications to GKE
Key Takeaways
- The practical object hierarchy is Deployment → ReplicaSet → Pod; Deployments provide rolling updates, rollback, and self-healing
- kubectl apply -f (declarative) is preferred over one-off kubectl create commands for reproducible, version-controlled deployments
- A Deployment is not automatically reachable — use kubectl expose with ClusterIP (internal), NodePort (30000–32767 on every node), or LoadBalancer (external GCP load balancer)
- kubectl scale changes replica count instantly; kubectl set image triggers a rolling update; kubectl rollout undo reverts a bad rollout
- ImagePullBackOff on GKE is most often a missing Artifact Registry Reader role on the node service account, not a network problem
Why This Is the Payoff Objective
Everything in the prior two sections — installing kubectl, choosing a cluster configuration — exists to support this exam bullet: "Deploying a containerized application to Google Kubernetes Engine." This is where ACE questions get concrete: reading a kubectl command or short YAML snippet and predicting what happens, or diagnosing why a Pod never becomes reachable. You need working fluency with the core Kubernetes objects and the commands that create, expose, scale, and update them.
The Core Object Hierarchy
| Object | Role |
|---|---|
| Pod | The smallest deployable unit — one or more tightly coupled containers sharing network/storage. You almost never create bare Pods directly in production. |
| Deployment | Declares the desired state for a set of identical Pods (image, replica count, update strategy). Manages a hidden ReplicaSet, which in turn manages the Pods. Gives you rolling updates, rollback, and self-healing (a crashed Pod is automatically replaced). |
| Service | A stable network endpoint that load-balances traffic across a changing set of Pods, decoupling clients from individual Pod IPs (which change every time a Pod restarts). |
| Ingress | Layer-7 HTTP(S) routing in front of one or more Services — used when you need host/path-based routing or a single external endpoint for multiple apps. |
Deploying an Application
Imperative (fast, exam-lab friendly):
kubectl create deployment web --image=us-docker.pkg.dev/my-project/my-repo/web:v1
Declarative (recommended for production — version-controlled, repeatable):
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: us-docker.pkg.dev/my-project/my-repo/web:v1
ports:
- containerPort: 8080
kubectl apply -f deployment.yaml
kubectl apply -f is idempotent — re-running it against a modified file updates the live objects to match, which is why it is favored over one-off kubectl create commands whenever the manifest is checked into source control.
Sourcing the Container Image
Images deployed to GKE are normally pulled from Artifact Registry, Google Cloud's current image and package repository (the successor to the older Container Registry / gcr.io hostnames). A typical image path looks like:
REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG
GKE node service accounts need the Artifact Registry Reader role (roles/artifactregistry.reader) to pull images from a repository in the same or a different project. This single IAM permission is the source of a very common failure mode described below.
Exposing the Application
Creating a Deployment does not automatically make it reachable — you must create a Service:
kubectl expose deployment web --type=LoadBalancer --port=80 --target-port=8080
| Service Type | Behavior |
|---|---|
ClusterIP (default) | Internal-only virtual IP, reachable only from inside the cluster |
NodePort | Opens the same static port (range 30000–32767) on every node's IP, in addition to a ClusterIP |
LoadBalancer | Provisions an external Google Cloud Network Load Balancer with a public IP, in addition to NodePort and ClusterIP behavior |
Note the distinction between --port (the port clients connect to on the Service) and --target-port (the port the container actually listens on) — mixing these up is a common source of "connection refused" scenarios on the exam.
A newly created LoadBalancer Service typically shows EXTERNAL-IP: <pending> in kubectl get services output for a minute or two while Google Cloud provisions the load balancer — this is expected behavior, not a failure.
Scaling and Updating
kubectl scale deployment web --replicas=5 # change replica count immediately
kubectl set image deployment/web web=us-docker.pkg.dev/my-project/my-repo/web:v2 # rolling update
kubectl rollout status deployment/web # watch the rollout progress
kubectl rollout undo deployment/web # roll back to the previous revision
kubectl set image triggers a rolling update by default: Kubernetes gradually replaces old Pods with new ones, keeping the application available throughout. kubectl rollout undo reverts to the prior ReplicaSet if the new version misbehaves — a much faster recovery path than manually redeploying the old image.
Verifying and Troubleshooting
kubectl get pods # list Pods and their status
kubectl get pods --all-namespaces # across every namespace
kubectl describe pod POD_NAME # detailed events — the first stop when a Pod won't start
kubectl logs POD_NAME # container stdout/stderr
Exam scenario: A Pod's status is stuck at ImagePullBackOff. The image is confirmed to exist in Artifact Registry in the same project, and the cluster's network configuration is unchanged. The most likely cause on the exam is not networking — it is that the node's service account lacks the Artifact Registry Reader role on the repository, so every pull attempt is rejected as unauthorized.
Exam scenario 2: After running kubectl create deployment web --image=..., a candidate immediately tries to reach the application from a browser and it fails. kubectl get services shows only a default ClusterIP Service was never created at all — because kubectl create deployment does not create a Service automatically. The fix is kubectl expose deployment web --type=LoadBalancer ....
Key Takeaways
- The practical hierarchy is Deployment → ReplicaSet → Pod; Deployments provide rolling updates, rollback, and self-healing that bare Pods do not.
kubectl apply -f(declarative) is preferred over one-offkubectl createcommands for anything meant to be reproducible or version-controlled.- Creating a Deployment does not expose it — you need
kubectl expose(or a Service manifest) with the right type:ClusterIP(internal only),NodePort(30000–32767 on every node), orLoadBalancer(external GCP load balancer). kubectl scalechanges replica count instantly;kubectl set imagetriggers a rolling update;kubectl rollout undoreverts a bad rollout.ImagePullBackOffon GKE is most often an Artifact Registry IAM permission problem (missingroles/artifactregistry.readeron the node service account), not a network issue.
Which Kubernetes object manages the ReplicaSet that in turn manages a set of identical Pods, providing rolling updates and self-healing?
Which kubectl command reverts a Deployment to its previous working revision after a bad rolling update?
A Pod on a GKE cluster is stuck in ImagePullBackOff. The image exists in Artifact Registry in the same project and networking is unchanged. What is the most likely cause?
Which command exposes an existing Deployment through a Google Cloud external Network Load Balancer?