docs: add 100 Days of DevOps challenge notes
This commit is contained in:
994
100 - days of devops/devops-61-70.md
Normal file
994
100 - days of devops/devops-61-70.md
Normal file
@@ -0,0 +1,994 @@
|
||||
## Task 61
|
||||
|
||||
There are some applications that need to be deployed on Kubernetes cluster and these apps have some pre-requisites where some configurations need to be changed before deploying the app container. Some of these changes cannot be made inside the images so the DevOps team has come up with a solution to use init containers to perform these tasks during deployment. Below is a sample scenario that the team is going to test first.
|
||||
|
||||
Create a Deployment named as ic-deploy-xfusion.
|
||||
|
||||
Configure spec as replicas should be 1, labels app should be ic-xfusion, template's metadata lables app should be the same ic-xfusion.
|
||||
|
||||
The initContainers should be named as ic-msg-xfusion, use image fedora with latest tag and use command '/bin/bash', '-c' and 'echo Init Done - Welcome to xFusionCorp Industries > /ic/blog'. The volume mount should be named as ic-volume-xfusion and mount path should be /ic.
|
||||
|
||||
Main container should be named as ic-main-xfusion, use image fedora with latest tag and use command '/bin/bash', '-c' and 'while true; do cat /ic/blog; sleep 5; done'. The volume mount should be named as ic-volume-xfusion and mount path should be /ic.
|
||||
|
||||
Volume to be named as ic-volume-xfusion and it should be an emptyDir type.
|
||||
|
||||
Note: The kubectl utility on the jump-host has been configured to work with the Kubernetes cluster.
|
||||
|
||||
### Solution
|
||||
|
||||
# Create ic-deploy-xfusion deployment
|
||||
|
||||
```bash
|
||||
kubectl apply -f - <<'EOF'
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ic-deploy-xfusion
|
||||
labels:
|
||||
app: ic-xfusion
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ic-xfusion
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ic-xfusion
|
||||
spec:
|
||||
initContainers:
|
||||
- name: ic-msg-xfusion
|
||||
image: fedora:latest
|
||||
command: ["/bin/bash", "-c", "echo Init Done - Welcome to xFusionCorp Industries > /ic/blog"]
|
||||
volumeMounts:
|
||||
- name: ic-volume-xfusion
|
||||
mountPath: /ic
|
||||
containers:
|
||||
- name: ic-main-xfusion
|
||||
image: fedora:latest
|
||||
command: ["/bin/bash", "-c", "while true; do cat /ic/blog; sleep 5; done"]
|
||||
volumeMounts:
|
||||
- name: ic-volume-xfusion
|
||||
mountPath: /ic
|
||||
volumes:
|
||||
- name: ic-volume-xfusion
|
||||
emptyDir: {}
|
||||
EOF
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
kubectl get deployment ic-deploy-xfusion
|
||||
kubectl get pods -l app=ic-xfusion
|
||||
kubectl logs -l app=ic-xfusion -c ic-main-xfusion
|
||||
```
|
||||
|
||||
Want: deployment `1/1` ready, pod Running, and the main container's logs printing `Init Done - Welcome to xFusionCorp Industries` every 5s.
|
||||
|
||||
## Task 62
|
||||
|
||||
The Nautilus DevOps team is working to deploy some tools in Kubernetes cluster. Some of the tools are licence based so that licence information needs to be stored securely within Kubernetes cluster. Therefore, the team wants to utilize Kubernetes secrets to store those secrets. Below you can find more details about the requirements:
|
||||
|
||||
We already have a secret key file ecommerce.txt under the /opt/ directory. Create a generic secret named ecommerce, it should contain the password/license-number present in ecommerce.txt file.
|
||||
|
||||
Also create a pod named secret-nautilus.
|
||||
|
||||
Configure pod's spec as container name should be secret-container-nautilus, image should be debian with latest tag (remember to mention the tag with image). Use sleep command for container so that it remains in running state. Consume the created secret and mount it under /opt/games within the container.
|
||||
|
||||
To verify you can exec into the container secret-container-nautilus, to check the secret key under the mounted path /opt/games. Before hitting the Check button please make sure pod/pods are in running state, also validation can take some time to complete so keep patience.
|
||||
|
||||
Note: The kubectl utility on the jump-host has been configured to work with the Kubernetes cluster.
|
||||
|
||||
### Solution
|
||||
|
||||
# Create secret + pod that mounts it
|
||||
|
||||
## Step 1 — create the generic secret from the file
|
||||
|
||||
```bash
|
||||
kubectl create secret generic ecommerce --from-file=/opt/ecommerce.txt
|
||||
```
|
||||
|
||||
## Step 2 — create the pod mounting the secret at /opt/games
|
||||
|
||||
```bash
|
||||
kubectl apply -f - <<'EOF'
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: secret-nautilus
|
||||
spec:
|
||||
containers:
|
||||
- name: secret-container-nautilus
|
||||
image: debian:latest
|
||||
command: ["sleep", "infinity"]
|
||||
volumeMounts:
|
||||
- name: secret-volume
|
||||
mountPath: /opt/games
|
||||
volumes:
|
||||
- name: secret-volume
|
||||
secret:
|
||||
secretName: ecommerce
|
||||
EOF
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
kubectl get secret ecommerce
|
||||
kubectl get pod secret-nautilus
|
||||
kubectl exec secret-nautilus -- ls -l /opt/games
|
||||
kubectl exec secret-nautilus -- cat /opt/games/ecommerce.txt
|
||||
```
|
||||
|
||||
Want: secret `ecommerce` exists, pod `Running`, and `/opt/games/ecommerce.txt` present with the license content inside the container.
|
||||
|
||||
## Task 63
|
||||
|
||||
There is an iron gallery app that the Nautilus DevOps team was developing. They have recently customized the app and are going to deploy the same on the Kubernetes cluster. Below you can find more details:
|
||||
|
||||
Create a namespace iron-namespace-nautilus
|
||||
|
||||
Create a deployment iron-gallery-deployment-nautilus for iron gallery under the same namespace you created.
|
||||
|
||||
:- Labels run should be iron-gallery.
|
||||
:- Replicas count should be 1.
|
||||
:- Selector's matchLabels run should be iron-gallery.
|
||||
:- Template labels run should be iron-gallery under metadata.
|
||||
:- The container should be named as iron-gallery-container-nautilus, use kodekloud/irongallery:2.0 image ( use exact image name / tag ).
|
||||
:- Resources limits for memory should be 100Mi and for CPU should be 50m.
|
||||
:- First volumeMount name should be config, its mountPath should be /usr/share/nginx/html/data.
|
||||
:- Second volumeMount name should be images, its mountPath should be /usr/share/nginx/html/uploads.
|
||||
:- First volume name should be config and give it emptyDir and second volume name should be images, also give it emptyDir.
|
||||
|
||||
Create a deployment iron-db-deployment-nautilus for iron db under the same namespace.
|
||||
:- Labels db should be mariadb.
|
||||
:- Replicas count should be 1.
|
||||
:- Selector's matchLabels db should be mariadb.
|
||||
:- Template labels db should be mariadb under metadata.
|
||||
:- The container name should be iron-db-container-nautilus, use kodekloud/irondb:2.0 image ( use exact image name / tag ).
|
||||
:- Define environment, set MYSQL_DATABASE its value should be database_apache, set MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD value should be with some complex passwords for DB connections, and MYSQL_USER value should be any custom user ( except root ).
|
||||
|
||||
:- Volume mount name should be db and its mountPath should be /var/lib/mysql. Volume name should be db and give it an emptyDir.
|
||||
|
||||
Create a service for iron db which should be named iron-db-service-nautilus under the same namespace. Configure spec as selector's db should be mariadb. Protocol should be TCP, port and targetPort should be 3306 and its type should be ClusterIP.
|
||||
|
||||
Create a service for iron gallery which should be named iron-gallery-service-nautilus under the same namespace. Configure spec as selector's run should be iron-gallery. Protocol should be TCP, port and targetPort should be 80, nodePort should be 32678 and its type should be NodePort.
|
||||
|
||||
Note:
|
||||
We don't need to make connection b/w database and front-end now, if the installation page is coming up it should be enough for now.
|
||||
|
||||
The kubectl utility on the jump-host has been configured to work with the Kubernetes cluster.
|
||||
|
||||
### Solution
|
||||
|
||||
# Iron Gallery app — namespace, deployments, services
|
||||
|
||||
```bash
|
||||
kubectl apply -f - <<'EOF'
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: iron-namespace-nautilus
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: iron-gallery-deployment-nautilus
|
||||
namespace: iron-namespace-nautilus
|
||||
labels:
|
||||
run: iron-gallery
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
run: iron-gallery
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
run: iron-gallery
|
||||
spec:
|
||||
containers:
|
||||
- name: iron-gallery-container-nautilus
|
||||
image: kodekloud/irongallery:2.0
|
||||
resources:
|
||||
limits:
|
||||
memory: "100Mi"
|
||||
cpu: "50m"
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /usr/share/nginx/html/data
|
||||
- name: images
|
||||
mountPath: /usr/share/nginx/html/uploads
|
||||
volumes:
|
||||
- name: config
|
||||
emptyDir: {}
|
||||
- name: images
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: iron-db-deployment-nautilus
|
||||
namespace: iron-namespace-nautilus
|
||||
labels:
|
||||
db: mariadb
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
db: mariadb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
db: mariadb
|
||||
spec:
|
||||
containers:
|
||||
- name: iron-db-container-nautilus
|
||||
image: kodekloud/irondb:2.0
|
||||
env:
|
||||
- name: MYSQL_DATABASE
|
||||
value: "database_apache"
|
||||
- name: MYSQL_ROOT_PASSWORD
|
||||
value: "R00t_C0mpl3x_P@ss"
|
||||
- name: MYSQL_PASSWORD
|
||||
value: "Us3r_C0mpl3x_P@ss"
|
||||
- name: MYSQL_USER
|
||||
value: "iron_user"
|
||||
volumeMounts:
|
||||
- name: db
|
||||
mountPath: /var/lib/mysql
|
||||
volumes:
|
||||
- name: db
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: iron-db-service-nautilus
|
||||
namespace: iron-namespace-nautilus
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
db: mariadb
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 3306
|
||||
targetPort: 3306
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: iron-gallery-service-nautilus
|
||||
namespace: iron-namespace-nautilus
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
run: iron-gallery
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
nodePort: 32678
|
||||
EOF
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
kubectl get all -n iron-namespace-nautilus
|
||||
kubectl get pods -n iron-namespace-nautilus
|
||||
kubectl get svc -n iron-namespace-nautilus
|
||||
```
|
||||
|
||||
Want: both deployments `1/1`, both pods Running, db service ClusterIP on 3306, gallery service NodePort on 32678. Then the iron gallery install page should load via the nodePort.
|
||||
|
||||
## Task 64
|
||||
|
||||
One of the DevOps engineers was trying to deploy a python app on Kubernetes cluster. Unfortunately, due to some mis-configuration, the application is not coming up. Please take a look into it and fix the issues. Application should be accessible on the specified nodePort.
|
||||
|
||||
The deployment name is python-deployment-devops, its using poroko/flask-demo-app image. The deployment and service of this app is already deployed.
|
||||
|
||||
nodePort should be 32345 and targetPort should be python flask app's default port.
|
||||
|
||||
Note: The kubectl utility on the jump-host has been configured to work with the Kubernetes cluster.
|
||||
|
||||
### Solution
|
||||
|
||||
```bash
|
||||
kubectl set image deployment/python-deployment-devops python-container-devops=poroko/flask-demo-app
|
||||
|
||||
kubectl patch svc python-service-devops --type=json -p='[
|
||||
{"op":"replace","path":"/spec/ports/0/targetPort","value":5000},
|
||||
{"op":"replace","path":"/spec/ports/0/nodePort","value":32345}
|
||||
]'
|
||||
```
|
||||
|
||||
## Task 65
|
||||
|
||||
The Nautilus application development team observed some performance issues with one of the application that is deployed in Kubernetes cluster. After looking into number of factors, the team has suggested to use some in-memory caching utility for DB service. After number of discussions, they have decided to use Redis. Initially they would like to deploy Redis on kubernetes cluster for testing and later they will move it to production. Please find below more details about the task:
|
||||
|
||||
Create a redis deployment with following parameters:
|
||||
|
||||
Create a config map called my-redis-config having maxmemory 2mb in redis-config.
|
||||
|
||||
Name of the deployment should be redis-deployment, it should use
|
||||
redis:alpine image and container name should be redis-container. Also make sure it has only 1 replica.
|
||||
|
||||
The container should request for 1 CPU.
|
||||
|
||||
Mount 2 volumes:
|
||||
a. An Empty directory volume called data at path /redis-master-data.
|
||||
b. A configmap volume called redis-config at path /redis-master.
|
||||
c. The container should expose the port 6379.
|
||||
|
||||
Finally, redis-deployment should be up and running.
|
||||
|
||||
### Solution
|
||||
|
||||
# Redis deployment with configmap
|
||||
|
||||
```bash
|
||||
kubectl apply -f - <<'EOF'
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-redis-config
|
||||
data:
|
||||
redis-config: |
|
||||
maxmemory 2mb
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis-deployment
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis-container
|
||||
image: redis:alpine
|
||||
resources:
|
||||
requests:
|
||||
cpu: "1"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /redis-master-data
|
||||
- name: redis-config
|
||||
mountPath: /redis-master
|
||||
volumes:
|
||||
- name: data
|
||||
emptyDir: {}
|
||||
- name: redis-config
|
||||
configMap:
|
||||
name: my-redis-config
|
||||
EOF
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
kubectl get configmap my-redis-config
|
||||
kubectl get deployment redis-deployment
|
||||
kubectl get pods -l app=redis
|
||||
kubectl describe pod -l app=redis | grep -A3 -i "requests\|mounts"
|
||||
```
|
||||
|
||||
Want: configmap exists, deployment `1/1` ready, pod Running, CPU request 1, both volumes mounted.
|
||||
|
||||
## Task 66
|
||||
|
||||
A new MySQL server needs to be deployed on Kubernetes cluster. The Nautilus DevOps team was working on to gather the requirements. Recently they were able to finalize the requirements and shared them with the team members to start working on it. Below you can find the details:
|
||||
|
||||
1.) Create a PersistentVolume mysql-pv, its capacity should be 250Mi, set other parameters as per your preference.
|
||||
|
||||
2.) Create a PersistentVolumeClaim to request this PersistentVolume storage. Name it as mysql-pv-claim and request a 250Mi of storage. Set other parameters as per your preference.
|
||||
|
||||
3.) Create a deployment named mysql-deployment, use any mysql image as per your preference. Mount the PersistentVolume at mount path /var/lib/mysql.
|
||||
|
||||
4.) Create a NodePort type service named mysql and set nodePort to 30007.
|
||||
|
||||
5.) Create a secret named mysql-root-pass having a key pair value, where key is password and its value is YUIidhb667, create another secret named mysql-user-pass having some key pair values, where first key is username and its value is kodekloud_pop, second key is password and value is BruCStnMT5, create one more secret named mysql-db-url, key name is database and value is kodekloud_db10
|
||||
|
||||
6.) Define some environment variables within the container:
|
||||
a.) name: MYSQL_ROOT_PASSWORD, should pick value from secretKeyRef name: mysql-root-pass and key: password
|
||||
b.) name: MYSQL_DATABASE, should pick value from secretKeyRef name: mysql-db-url and key: database
|
||||
c.) name: MYSQL_USER, should pick value from secretKeyRef name: mysql-user-pass key key: username
|
||||
d.) name: MYSQL_PASSWORD, should pick value from secretKeyRef name: mysql-user-pass and key: password
|
||||
|
||||
### Solution
|
||||
|
||||
# MySQL deployment with secrets, PV/PVC, service
|
||||
|
||||
```bash
|
||||
kubectl apply -f - <<'EOF'
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mysql-root-pass
|
||||
type: Opaque
|
||||
stringData:
|
||||
password: YUIidhb667
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mysql-user-pass
|
||||
type: Opaque
|
||||
stringData:
|
||||
username: kodekloud_pop
|
||||
password: BruCStnMT5
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mysql-db-url
|
||||
type: Opaque
|
||||
stringData:
|
||||
database: kodekloud_db10
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: mysql-pv
|
||||
spec:
|
||||
storageClassName: manual
|
||||
capacity:
|
||||
storage: 250Mi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
hostPath:
|
||||
path: /mnt/mysql-data
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: mysql-pv-claim
|
||||
spec:
|
||||
storageClassName: manual
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 250Mi
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mysql-deployment
|
||||
labels:
|
||||
app: mysql
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mysql
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql
|
||||
spec:
|
||||
containers:
|
||||
- name: mysql
|
||||
image: mysql:8.0
|
||||
ports:
|
||||
- containerPort: 3306
|
||||
env:
|
||||
- name: MYSQL_ROOT_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mysql-root-pass
|
||||
key: password
|
||||
- name: MYSQL_DATABASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mysql-db-url
|
||||
key: database
|
||||
- name: MYSQL_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mysql-user-pass
|
||||
key: username
|
||||
- name: MYSQL_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mysql-user-pass
|
||||
key: password
|
||||
volumeMounts:
|
||||
- name: mysql-storage
|
||||
mountPath: /var/lib/mysql
|
||||
volumes:
|
||||
- name: mysql-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: mysql-pv-claim
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mysql
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: mysql
|
||||
ports:
|
||||
- port: 3306
|
||||
targetPort: 3306
|
||||
nodePort: 30007
|
||||
EOF
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
kubectl get secrets
|
||||
kubectl get pv mysql-pv
|
||||
kubectl get pvc mysql-pv-claim
|
||||
kubectl get deployment mysql-deployment
|
||||
kubectl get pods -l app=mysql
|
||||
kubectl get svc mysql
|
||||
```
|
||||
|
||||
Want: 3 secrets, PV+PVC `Bound`, deployment `1/1`, pod Running, service NodePort 30007.
|
||||
|
||||
## Task 67
|
||||
|
||||
The Nautilus Application development team has finished development of one of the applications and it is ready for deployment. It is a guestbook application that will be used to manage entries for guests/visitors. As per discussion with the DevOps team, they have finalized the infrastructure that will be deployed on Kubernetes cluster. Below you can find more details about it.
|
||||
|
||||
BACK-END TIER
|
||||
|
||||
Create a deployment named redis-master for Redis master.
|
||||
|
||||
a.) Replicas count should be 1.
|
||||
b.) Container name should be master-redis-devops and it should use image redis.
|
||||
c.) Request resources as CPU should be 100m and Memory should be 100Mi.
|
||||
d.) Container port should be redis default port i.e 6379.
|
||||
|
||||
Create a service named redis-master for Redis master. Port and targetPort should be Redis default port i.e 6379.
|
||||
|
||||
Create another deployment named redis-slave for Redis slave.
|
||||
|
||||
a.) Replicas count should be 2.
|
||||
b.) Container name should be slave-redis-devops and it should use gcr.io/google_samples/gb-redisslave:v3 image.
|
||||
c.) Requests resources as CPU should be 100m and Memory should be 100Mi.
|
||||
d.) Define an environment variable named GET_HOSTS_FROM and its value should be dns.
|
||||
e.) Container port should be Redis default port i.e 6379.
|
||||
|
||||
Create another service named redis-slave. It should use Redis default port i.e 6379.
|
||||
|
||||
Create another service named redis-follower. Port and targetPort should be Redis default port i.e 6379. Its selector app should be redis-slave.
|
||||
|
||||
FRONT END TIER
|
||||
|
||||
Create a deployment named frontend.
|
||||
|
||||
a.) Replicas count should be 3.
|
||||
b.) Container name should be php-redis-devops and it should use gcr.io/google-samples/gb-frontend@sha256:a908df8486ff66f2c4daa0d3d8a2fa09846a1fc8efd65649c0109695c7c5cbff image.
|
||||
c.) Request resources as CPU should be 100m and Memory should be 100Mi.
|
||||
d.) Define an environment variable named as GET_HOSTS_FROM and its value should be dns.
|
||||
e.) Container port should be 80.
|
||||
|
||||
Create a service named frontend. Its type should be NodePort, port should be 80 and its nodePort should be 30009.
|
||||
|
||||
Finally, you can check the guestbook app by clicking on App button.
|
||||
|
||||
You can use any labels as per your choice.
|
||||
|
||||
### Solution
|
||||
|
||||
# Guestbook app — full stack
|
||||
|
||||
```bash
|
||||
kubectl apply -f - <<'EOF'
|
||||
# ===== BACK-END: redis-master =====
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis-master
|
||||
labels:
|
||||
app: redis-master
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis-master
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis-master
|
||||
spec:
|
||||
containers:
|
||||
- name: master-redis-devops
|
||||
image: redis
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "100Mi"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-master
|
||||
spec:
|
||||
selector:
|
||||
app: redis-master
|
||||
ports:
|
||||
- port: 6379
|
||||
targetPort: 6379
|
||||
---
|
||||
# ===== BACK-END: redis-slave =====
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis-slave
|
||||
labels:
|
||||
app: redis-slave
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis-slave
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis-slave
|
||||
spec:
|
||||
containers:
|
||||
- name: slave-redis-devops
|
||||
image: gcr.io/google_samples/gb-redisslave:v3
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "100Mi"
|
||||
env:
|
||||
- name: GET_HOSTS_FROM
|
||||
value: dns
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-slave
|
||||
spec:
|
||||
selector:
|
||||
app: redis-slave
|
||||
ports:
|
||||
- port: 6379
|
||||
targetPort: 6379
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-follower
|
||||
spec:
|
||||
selector:
|
||||
app: redis-slave
|
||||
ports:
|
||||
- port: 6379
|
||||
targetPort: 6379
|
||||
---
|
||||
# ===== FRONT-END =====
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app: frontend
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: php-redis-devops
|
||||
image: gcr.io/google-samples/gb-frontend@sha256:a908df8486ff66f2c4daa0d3d8a2fa09846a1fc8efd65649c0109695c7c5cbff
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "100Mi"
|
||||
env:
|
||||
- name: GET_HOSTS_FROM
|
||||
value: dns
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: frontend
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: frontend
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
nodePort: 30009
|
||||
EOF
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
kubectl get deployments
|
||||
kubectl get pods
|
||||
kubectl get svc
|
||||
kubectl get pods -o wide | grep -E "redis|frontend"
|
||||
```
|
||||
|
||||
Want: redis-master `1/1`, redis-slave `2/2`, frontend `3/3`, all four services present, frontend NodePort 30009. Then the App button loads the guestbook.
|
||||
|
||||
## Task 68
|
||||
|
||||
The DevOps team at xFusionCorp Industries is initiating the setup of CI/CD pipelines and has decided to utilize Jenkins as their server. Execute the task according to the provided requirements:
|
||||
|
||||
1. Install Jenkins on the jenkins server using the apt utility only, and start it using the service command.
|
||||
|
||||
If you face a timeout issue while starting the Jenkins service, first check the service status with service jenkins status
|
||||
Then review the logs in /var/log/jenkins/jenkins.log to identify the cause.
|
||||
2. Jenkin's admin user name should be theadmin, password should be Adm!n321, full name should be Mariyam and email should be mariyam@jenkins.stratos.xfusioncorp.com.
|
||||
|
||||
|
||||
Note:
|
||||
1. To access the jenkins server, connect from the jump host using the root user with the password S3curePass.
|
||||
|
||||
2. After Jenkins server installation, click the Jenkins button on the top bar to access the Jenkins UI and follow on-screen instructions to create an admin user.
|
||||
|
||||
### Solution
|
||||
|
||||
# Install Jenkins on jenkins server + create admin user
|
||||
|
||||
## Access the server
|
||||
|
||||
From the jump host, SSH in as root:
|
||||
|
||||
```bash
|
||||
ssh root@jenkins
|
||||
# password: S3curePass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 1 — Install Jenkins (CLI)
|
||||
|
||||
### Step 1: Install Java (Jenkins prerequisite)
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt install -y fontconfig openjdk-17-jre
|
||||
java -version
|
||||
|
||||
apt update
|
||||
apt install -y openjdk-21-jre
|
||||
|
||||
# make Java 21 the default (in case 17 is still selected)
|
||||
update-alternatives --config java
|
||||
```
|
||||
|
||||
### Step 2: Add the Jenkins apt repository + signing key
|
||||
|
||||
```bash
|
||||
wget -O /usr/share/keyrings/jenkins-keyring.asc \
|
||||
https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
|
||||
|
||||
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc]" \
|
||||
"https://pkg.jenkins.io/debian-stable binary/" | \
|
||||
tee /etc/apt/sources.list.d/jenkins.list > /dev/null
|
||||
```
|
||||
|
||||
### Step 3: Install Jenkins
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt install -y jenkins
|
||||
```
|
||||
|
||||
### Step 4: Start Jenkins using the service command
|
||||
|
||||
```bash
|
||||
service jenkins start
|
||||
service jenkins status
|
||||
```
|
||||
|
||||
### If you hit a startup timeout
|
||||
|
||||
```bash
|
||||
service jenkins status
|
||||
cat /var/log/jenkins/jenkins.log
|
||||
```
|
||||
|
||||
The log identifies the cause — most commonly a missing/wrong Java version. Ensure `openjdk-17-jre` is installed and set as default (`update-alternatives --config java`), then restart:
|
||||
|
||||
```bash
|
||||
service jenkins restart
|
||||
```
|
||||
|
||||
Jenkins can take 60–90s on first start; a first-attempt timeout doesn't always mean failure — re-check status after waiting.
|
||||
|
||||
---
|
||||
|
||||
## Part 2 — Create the admin user (Web UI)
|
||||
|
||||
### Step 1: Get the initial unlock password
|
||||
|
||||
```bash
|
||||
cat /var/lib/jenkins/secrets/initialAdminPassword
|
||||
```
|
||||
|
||||
Copy the output.
|
||||
|
||||
### Step 2: Open the Jenkins UI
|
||||
|
||||
Click the **Jenkins** button on the top bar.
|
||||
|
||||
### Step 3: Unlock Jenkins
|
||||
|
||||
Paste the initial admin password from Step 1 → **Continue**.
|
||||
|
||||
### Step 4: Install plugins
|
||||
|
||||
Choose **"Install suggested plugins"** and wait for completion.
|
||||
|
||||
### Step 5: Create the First Admin User
|
||||
|
||||
Fill in exactly:
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Username | `theadmin` |
|
||||
| Password | `Adm!n321` |
|
||||
| Full name | `Mariyam` |
|
||||
| Email | `mariyam@jenkins.stratos.xfusioncorp.com` |
|
||||
|
||||
Click **Save and Continue** → **Save and Finish** → **Start using Jenkins**.
|
||||
|
||||
---
|
||||
|
||||
## Verify
|
||||
|
||||
- `service jenkins status` shows Jenkins active/running.
|
||||
- You can log into the Jenkins UI as `theadmin` / `Adm!n321`.
|
||||
- The dashboard loads with `theadmin` as the logged-in user.
|
||||
|
||||
---
|
||||
|
||||
## Key points
|
||||
|
||||
- **Java first** — Jenkins is a Java app; without a JRE it won't start (the exact timeout the task warns about). Java 17 is the safe LTS-compatible choice.
|
||||
- **Repo + keyring required** — Jenkins isn't in Ubuntu base repos; add `pkg.jenkins.io` with its signing key before `apt install jenkins`.
|
||||
- **`debian-stable`** = Jenkins LTS line (more stable than weekly).
|
||||
- **`service jenkins start`** per the task (not `systemctl`).
|
||||
- **`initialAdminPassword`** unlocks the first UI screen before you can create `theadmin`.
|
||||
- **Admin user is created via the web wizard**, not CLI — the four field values must be exact for the grader.
|
||||
- Take a screenshot of the finished admin-user setup / dashboard for review.
|
||||
|
||||
## Task 69
|
||||
|
||||
The Nautilus DevOps team has recently setup a Jenkins server, which they want to use for some CI/CD jobs. Before that they want to install some plugins which will be used in most of the jobs. Please find below more details about the task
|
||||
|
||||
1. Click on the Jenkins button on the top bar to access the Jenkins UI. Login using username admin and password Adm!n321.
|
||||
2. Once logged in, install the Git and GitLab plugins. You may need to restart Jenkins to complete the plugin installation; if required, opt to Restart Jenkins when installation is complete and no jobs are running on the plugin installation/update page (Update Centre).
|
||||
|
||||
Note:
|
||||
|
||||
1. After restarting Jenkins, wait for the login page to reappear before proceeding.
|
||||
2. For tasks involving web UI changes, capture screenshots to share for review or consider using screen recording software like loom.com for documentation and sharing.
|
||||
|
||||
### Solution
|
||||
|
||||
# Install Git + GitLab plugins in Jenkins
|
||||
|
||||
## Step 1 — Log in
|
||||
|
||||
Click the **Jenkins** button on the top bar. Log in with:
|
||||
- Username: `admin`
|
||||
- Password: `Adm!n321`
|
||||
|
||||
## Step 2 — Navigate to Plugin Manager
|
||||
|
||||
**Manage Jenkins** → **Plugins** (under System Configuration; older versions: **Manage Plugins**).
|
||||
|
||||
## Step 3 — Find and select the plugins
|
||||
|
||||
Go to the **Available plugins** tab.
|
||||
|
||||
In the search box, search and check each:
|
||||
- Search `Git` → check the **Git** plugin
|
||||
- Search `GitLab` → check the **GitLab** plugin
|
||||
|
||||
## Step 4 — Install
|
||||
|
||||
Click **Install** (or **Download now and install after restart**).
|
||||
|
||||
## Step 5 — Restart Jenkins
|
||||
|
||||
On the installation progress page, check the box:
|
||||
**"Restart Jenkins when installation is complete and no jobs are running"**
|
||||
|
||||
Jenkins restarts. **Wait for the login page to reappear** before doing anything else.
|
||||
|
||||
## Step 6 — Verify
|
||||
|
||||
After restart, log back in and check:
|
||||
**Manage Jenkins** → **Plugins** → **Installed plugins** tab → search `Git` and `GitLab` — both should be listed.
|
||||
|
||||
## Task 70
|
||||
|
||||
The Nautilus team is integrating Jenkins into their CI/CD pipelines. After setting up a new Jenkins server, they're now configuring user access for the development team, Follow these steps:
|
||||
|
||||
1. Click on the Jenkins button on the top bar to access the Jenkins UI. Login with username admin and password Adm!n321.
|
||||
2. Create a jenkins user named rose with the password TmPcZjtRQx. Their full name should match Rose.
|
||||
3. Utilize the Project-based Matrix Authorization Strategy to assign overall read permission to the rose user.
|
||||
4. Remove all permissions for Anonymous users (if any) ensuring that the admin user retains overall Administer permissions.
|
||||
5. For the existing job, grant rose user only read permissions, disregarding other permissions such as Agent, SCM etc.
|
||||
|
||||
|
||||
Note:
|
||||
1. You may need to install plugins and restart Jenkins service. After plugins installation, select Restart Jenkins when installation is complete and no jobs are running on plugin installation/update page.
|
||||
2. After restarting the Jenkins service, wait for the Jenkins login page to reappear before proceeding. Avoid clicking Finish immediately after restarting the service.
|
||||
3. Capture screenshots of your configuration for review purposes. Consider using screen recording software like loom.com for documentation and sharing.
|
||||
|
||||
### Solution:
|
||||
|
||||
# Configure Jenkins user access for rose
|
||||
|
||||
## Step 1 — Log in
|
||||
Click **Jenkins** button → login `admin` / `Adm!n321`.
|
||||
|
||||
## Step 2 — Install Matrix Authorization Strategy plugin (if not present)
|
||||
**Manage Jenkins → Plugins → Available plugins** → search **"Matrix Authorization Strategy"** → check it → **Download now and install after restart**.
|
||||
|
||||
Wait for Jenkins to fully restart, wait for the **login page** to reappear, log back in.
|
||||
|
||||
## Step 3 — Create user rose
|
||||
**Manage Jenkins → Users → Create User**:
|
||||
- Username: `rose`
|
||||
- Password: `TmPcZjtRQx`
|
||||
- Full name: `Rose`
|
||||
- (email can be left blank or any value)
|
||||
|
||||
Click **Create User**.
|
||||
|
||||
## Step 4 — Switch to Project-based Matrix Authorization Strategy
|
||||
**Manage Jenkins → Security** (or **Configure Global Security**):
|
||||
- Under **Authorization**, select **"Project-based Matrix Authorization Strategy"**
|
||||
- A permission matrix appears.
|
||||
|
||||
## Step 5 — Set global permissions
|
||||
In the matrix:
|
||||
- **Add user** `admin` → grant **Overall / Administer** (full row, or at minimum Administer). **Do this FIRST — critical.**
|
||||
- **Add user** `rose` → grant **Overall / Read** only.
|
||||
- **Anonymous** row → **uncheck ALL permissions** (remove everything).
|
||||
|
||||
Click **Save**.
|
||||
|
||||
## Step 6 — Grant rose read-only on the existing job
|
||||
Go to the existing job → **Configure** → enable **"Enable project-based security"** (checkbox in the job config):
|
||||
- **Add user** `rose`
|
||||
- Grant **only Read** (under Job → Read). Leave Agent, SCM, Build, etc. **unchecked**.
|
||||
|
||||
Click **Save**.
|
||||
|
||||
## Verify
|
||||
- `admin` retains Administer (you can still access everything).
|
||||
- `rose` can log in, sees the job (read), can't configure/build.
|
||||
- Anonymous has no permissions.
|
||||
Reference in New Issue
Block a user