140 lines
5.3 KiB
Markdown
140 lines
5.3 KiB
Markdown
# Assignment
|
||
|
||
The Nautilus DevOps team is planning to set up a Jenkins CI server to create/manage some deployment pipelines for some of the projects. They want to set up the Jenkins server on Kubernetes cluster. Below you can find more details about the task:
|
||
|
||
|
||
1) Create a namespace jenkins
|
||
|
||
2) Create a Service for jenkins deployment. Service name should be jenkins-service under jenkins namespace, type should be NodePort, nodePort should be 30008
|
||
|
||
3) Create a Jenkins Deployment under jenkins namespace, It should be name as jenkins-deployment , labels app should be jenkins , container name should be jenkins-container , use jenkins/jenkins image , containerPort should be 8080 and replicas count should be 1. It should also have the environment variable JAVA_OPTS with the value -Djenkins.install.runSetupWizard=false to skip the initial setup wizard.
|
||
|
||
Make sure to wait for the pods to be in running state and make sure you are able to access the Jenkins UI screen in the browser before hitting the Check button.
|
||
|
||
Note: The kubectl utility on the jump-host has been configured to work with the Kubernetes cluster.
|
||
|
||
# Solution
|
||
|
||
# Jenkins on Kubernetes — `jenkins-deployment` + `jenkins-service` in `jenkins`
|
||
|
||
A Jenkins Deployment exposed via a NodePort Service, with the setup wizard disabled. All objects
|
||
in one multi-document heredoc — no manifest file on disk.
|
||
|
||
## Apply (heredoc → kubectl)
|
||
|
||
```bash
|
||
kubectl apply -f - <<'EOF'
|
||
apiVersion: v1
|
||
kind: Namespace
|
||
metadata:
|
||
name: jenkins
|
||
---
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
name: jenkins-deployment
|
||
namespace: jenkins
|
||
labels:
|
||
app: jenkins
|
||
spec:
|
||
replicas: 1
|
||
selector:
|
||
matchLabels:
|
||
app: jenkins
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: jenkins
|
||
spec:
|
||
containers:
|
||
- name: jenkins-container
|
||
image: jenkins/jenkins
|
||
ports:
|
||
- containerPort: 8080
|
||
env:
|
||
- name: JAVA_OPTS
|
||
value: "-Djenkins.install.runSetupWizard=false"
|
||
---
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
name: jenkins-service
|
||
namespace: jenkins
|
||
spec:
|
||
type: NodePort
|
||
selector:
|
||
app: jenkins
|
||
ports:
|
||
- port: 8080
|
||
targetPort: 8080
|
||
nodePort: 30008
|
||
EOF
|
||
|
||
# Wait for Jenkins to come up (it takes a bit to initialize)
|
||
kubectl rollout status deployment/jenkins-deployment -n jenkins
|
||
```
|
||
|
||
## How it works
|
||
|
||
### The heredoc apply pattern
|
||
|
||
- **`kubectl apply -f -`** reads from **stdin**; `---` separates the three documents, applied in
|
||
order so the **Namespace** exists before the Deployment and Service land in it.
|
||
- **`<<'EOF'` (delimiter quoted)** keeps the manifest literal — important here so `JAVA_OPTS`'s
|
||
value (with its `-D...` flag) isn't mangled by the jump-host shell.
|
||
|
||
### The Deployment
|
||
|
||
- **`namespace: jenkins`, `name: jenkins-deployment`, `labels.app: jenkins`** — exactly as
|
||
required.
|
||
- **container `name: jenkins-container`, `image: jenkins/jenkins`** — the official Jenkins image.
|
||
- **`containerPort: 8080`** — Jenkins' web UI listens on 8080 inside the container.
|
||
- **`replicas: 1`** — a single Jenkins instance.
|
||
- **`env: JAVA_OPTS = -Djenkins.install.runSetupWizard=false`** — passed to Jenkins' JVM. This
|
||
system property **skips the initial setup wizard** (the "unlock Jenkins / install plugins"
|
||
screens), so the UI is immediately usable — which is what lets the Check succeed without manual
|
||
unlock steps.
|
||
- **`selector.matchLabels: app: jenkins`** equals the template labels — the Deployment↔pod link.
|
||
|
||
### The Service — port 8080 is the key detail
|
||
|
||
- **`type: NodePort`** exposes Jenkins outside the cluster.
|
||
- **`selector: app: jenkins`** targets the deployment's pod by its label.
|
||
- **Port mapping:**
|
||
- `port: 8080` — the Service's ClusterIP port.
|
||
- **`targetPort: 8080`** — the container port. This **must** be 8080 because that's where
|
||
Jenkins actually listens; pointing it elsewhere would give you a Service with endpoints but no
|
||
response.
|
||
- `nodePort: 30008` — the external port on each node (in the valid `30000–32767` range).
|
||
|
||
Path: `<node-ip>:30008` → Service `:8080` → Jenkins pod `:8080`.
|
||
|
||
### Why waiting matters
|
||
|
||
Jenkins takes noticeably longer than a typical app to become ready — the JVM boots, unpacks
|
||
plugins, and initializes before the UI responds. `kubectl rollout status` blocks until the pod is
|
||
Ready, but the **UI** may need a few extra seconds after that. Confirm the login/dashboard
|
||
actually loads in the browser before hitting Check, as the task instructs.
|
||
|
||
## Verify
|
||
|
||
```bash
|
||
# Pod running
|
||
kubectl get pods -n jenkins
|
||
|
||
# Service exposed on 30008 with an endpoint
|
||
kubectl get service jenkins-service -n jenkins
|
||
kubectl get endpoints jenkins-service -n jenkins
|
||
|
||
# Jenkins responding inside the cluster (should return HTTP 200/403, not connection refused)
|
||
kubectl exec -n jenkins deploy/jenkins-deployment -- \
|
||
sh -c 'curl -sI http://localhost:8080 | head -n1' 2>/dev/null || true
|
||
```
|
||
|
||
Expected — the Jenkins pod `Running` (`READY 1/1`), `jenkins-service` NodePort `8080:30008/TCP`
|
||
with one endpoint, and the Jenkins dashboard loading at `<node-ip>:30008` in the browser (no setup
|
||
wizard, thanks to `JAVA_OPTS`).
|
||
|
||
> If the browser shows "unlock Jenkins," the `JAVA_OPTS` env var didn't take — confirm with
|
||
> `kubectl get deploy jenkins-deployment -n jenkins -o jsonpath='{.spec.template.spec.containers[0].env}'`.
|
||
> Give the pod a minute; Jenkins' first boot is slow. |