├── .gitignore
├── .gitkeep
├── LICENSE
├── README.md
├── chapter02
├── Docker_Hello_World
│ ├── app.py
│ ├── dockerfile
│ ├── first-cd-pipeline-deployment.yaml
│ └── requirements.txt
└── hello-world-deployment.yaml
├── chapter03
├── dockerfile
└── requirements.txt
├── chapter04
├── argocd_gitops
│ ├── .github
│ │ └── workflows
│ │ │ └── build-and-push-image.yml
│ ├── argocd-deployment.yaml
│ ├── deployment
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── src
│ │ ├── data.csv
│ │ ├── data.csv.bak
│ │ ├── dockerfile
│ │ ├── index.html
│ │ ├── package.json
│ │ └── server.js
├── gitops-k8s-deployments-helm
│ ├── .helmignore
│ ├── Chart.yaml
│ ├── templates
│ │ ├── NOTES.txt
│ │ ├── _helpers.tpl
│ │ ├── deployment.yaml
│ │ ├── hpa.yaml
│ │ ├── ingress.yaml
│ │ ├── service.yaml
│ │ ├── serviceaccount.yaml
│ │ └── tests
│ │ │ └── test-connection.yaml
│ └── values.yaml
└── gitops-k8s-deployments-kustomize
│ ├── base
│ ├── deployment.yaml
│ ├── kustomization.yaml
│ └── service.yaml
│ └── overlays
│ ├── development
│ ├── kustomization.yaml
│ └── patches
│ │ └── deployment_patch.yaml
│ └── staging
│ ├── kustomization.yaml
│ └── patches
│ └── deployment_patch.yaml
├── chapter05
├── chapter-5-building-a-service-catalog-for-kubernetes
│ ├── applicationsets
│ │ ├── argocd-applicationset.yaml
│ │ ├── cert-mananger-applicationset.yaml
│ │ ├── external-dns-applicationset.yaml
│ │ └── nginx-ingress-applicationset.yaml
│ ├── bonus
│ │ ├── README.md
│ │ ├── check-helm-dep-updates.sh
│ │ ├── dependencies.yaml
│ │ └── examples
│ │ │ ├── dns
│ │ │ └── external-dns
│ │ │ │ └── Chart.yaml
│ │ │ ├── security
│ │ │ └── kyverno
│ │ │ │ └── Chart.yaml
│ │ │ └── storage
│ │ │ ├── minio-operator
│ │ │ └── Chart.yaml
│ │ │ └── minio-tenant
│ │ │ └── Chart.yaml
│ ├── cluster
│ │ ├── in-cluster
│ │ │ ├── dns
│ │ │ │ └── external-dns
│ │ │ │ │ └── values.yaml
│ │ │ ├── networking
│ │ │ │ └── ingress-nginx
│ │ │ │ │ └── values.yaml
│ │ │ └── security
│ │ │ │ └── cert-manager
│ │ │ │ └── values.yaml
│ │ └── vcluster-team-a
│ │ │ ├── networkpolicy-deny-ingress.yaml
│ │ │ ├── optimization
│ │ │ └── vcluster
│ │ │ │ └── values.yaml
│ │ │ └── rbac.yaml
│ ├── dns
│ │ └── external-dns
│ │ │ ├── Chart.yaml
│ │ │ └── values.yaml
│ ├── networking
│ │ └── ingress-nginx
│ │ │ ├── Chart.yaml
│ │ │ └── values.yaml
│ ├── optimization
│ │ └── vcluster
│ │ │ ├── Chart.yaml
│ │ │ └── values.yaml
│ ├── security
│ │ ├── cert-manager
│ │ │ ├── Chart.yaml
│ │ │ └── values.yaml
│ │ ├── external-secrets
│ │ │ ├── Chart.yaml
│ │ │ └── values.yaml
│ │ └── kyverno
│ │ │ ├── Chart.yaml
│ │ │ └── values.yaml
│ └── system
│ │ └── argocd
│ │ ├── .gitignore
│ │ ├── Chart.yaml
│ │ ├── templates
│ │ └── ingress.yaml
│ │ └── values.yaml
├── chapter-5-effective-git-repository-strategies
│ ├── environment-branches
│ │ └── simple-webapp
│ │ │ ├── prod-deployment.yaml
│ │ │ ├── qa-deployment.yaml
│ │ │ └── stage-deployment.yaml
│ └── folders-for-environments
│ │ ├── base
│ │ ├── deployment.yaml
│ │ └── kustomization.yaml
│ │ └── overlays
│ │ ├── prod
│ │ ├── kustomization.yaml
│ │ └── patch.yaml
│ │ ├── qa
│ │ ├── kustomization.yaml
│ │ └── patch.yaml
│ │ └── stage
│ │ ├── kustomization.yaml
│ │ └── patch.yaml
├── chapter-5-multitenancy-with-vcluster-and-argo-cd
│ ├── bonus
│ │ └── connect_vcluster_fleet.sh
│ └── devteam-a
│ │ └── vcluster-application.yaml
├── chapter-5-native-multitenancy-with-argo-cd
│ └── devteam-a
│ │ ├── application-initializer.yaml
│ │ ├── argocd-project-devteam-a.yaml
│ │ ├── gitrepository-sealed.yaml
│ │ ├── namespace.yaml
│ │ ├── networkpolicy-deny-ingress.yaml
│ │ ├── rbac.yaml
│ │ └── resource-quotas.yaml
├── chapter-5-scale-with-applicationset-generators
│ └── nginx-ingress-applicationset-example
│ │ └── nginx-ingress-applicationset.yaml
└── chapter-5-the-app-of-apps-approach
│ ├── app-of-app
│ ├── application.yaml
│ └── simple-webapp
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ ├── app-of-apps
│ ├── application.yaml
│ └── simple-webapps
│ │ ├── simple-webapp-1
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ │ ├── simple-webapp-2
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ │ └── simple-webapp-3
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── applicationsets
│ ├── simple-webapp-applicationset.yaml
│ └── simple-webapp
│ ├── deployment.yaml
│ ├── namespace.yaml
│ └── service.yaml
├── chapter06
└── chapter-6-centralized-kubernetes-cluster-creation
│ ├── README.md
│ └── capi-quickstart.yaml
├── chapter08
├── azure-pipelines.yml
├── deployment
│ ├── base
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── kustomization.yaml
├── iac
│ ├── aws
│ │ └── main.tf
│ └── azure
│ │ ├── main.tf
│ │ └── versions.tf
├── manifests
│ ├── deployment.yml
│ └── service.yml
└── src
│ ├── data.csv
│ ├── dockerfile
│ ├── index.html
│ ├── package.json
│ └── server.js
├── chapter09
├── azure-pipelines.yml
├── deployment
│ ├── base
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── kustomization.yaml
├── iac
│ ├── aws
│ │ └── main.tf
│ └── azure
│ │ ├── main.tf
│ │ └── versions.tf
├── manifests
│ ├── deployment.yml
│ └── service.yml
└── src
│ ├── data.csv
│ ├── dockerfile
│ ├── index.html
│ ├── package.json
│ └── server.js
├── chapter10
├── Docker
│ └── dockerfile
├── README.md
├── flux-gitops-definitions
│ ├── dev-iac-automation.yaml
│ ├── github-repository-definition.yaml
│ ├── github-repository-secret.yaml
│ ├── prod-iac-automation.yaml
│ └── staging-iac-automation.yaml
├── iac
│ └── azure
│ │ └── vnet
│ │ └── main.tf
└── multi-env
│ └── iac
│ └── azure
│ ├── base
│ ├── main.tf
│ ├── readme.md
│ └── variables.tf
│ ├── dev
│ ├── main.tf
│ └── variables.tf
│ ├── prod
│ ├── main.tf
│ └── variables.tf
│ └── staging
│ ├── main.tf
│ └── variables.tf
├── chapter11
├── Step-01
│ ├── ArgoCD-GitOps
│ │ └── argocd_deployment.yaml
│ ├── backend-api.py
│ ├── deployment
│ │ ├── backend-api-deployment.yaml
│ │ └── backend-api-secrets.yaml
│ ├── dockerfile
│ ├── requirements.txt
│ ├── scripts
│ │ ├── get-argo-cd-external-ip.sh
│ │ └── get-argo-cd-initial-password.sh
│ └── terraform
│ │ ├── main.tf
│ │ └── variables.tf
├── Step-02-ArgoCD-Deployment
│ └── gitops-for-real-ci-cd-pipeline.yml
├── Step-03-Scalability
│ ├── deployment
│ │ ├── backend-api-deployment.yaml
│ │ └── hpa.yaml
│ └── hpa-testing.sh
├── Step-04-Security
│ ├── weather-app-manager-role-binding.yaml
│ ├── weather-app-manager-role.yaml
│ ├── weather-app-operator-role-binding.yaml
│ └── weather-app-operator-role.yaml
└── github
│ └── workflows
│ └── gitops-for-real-ci-cd-pipeline.yml
├── chapter13
├── chapter-13-committing-everything-to-git-and-what-about-secrets
│ ├── applicationsets
│ │ └── security
│ │ │ ├── external-secrets-applicationset.yaml
│ │ │ └── sealed-secrets-applicationset.yaml
│ ├── kustomize
│ │ └── .gitexplodee
│ └── security
│ │ ├── external-secrets
│ │ ├── Chart.yaml
│ │ └── values.yaml
│ │ └── sealed-secrets
│ │ ├── Chart.yaml
│ │ └── values.yaml
├── chapter-13-hardening-declarative-gitops-cd-on-kubernetes
│ └── end_user_threat_model.pdf
└── chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices
│ ├── applicationsets
│ └── security
│ │ └── kyverno-applicationset.yaml
│ ├── kustomize
│ ├── .gitexplodee
│ └── security
│ │ └── kyverno
│ │ └── policies
│ │ └── base
│ │ ├── argocd-application-field-validation.yaml
│ │ ├── argocd-application-prevent-default-project.yaml
│ │ ├── argocd-application-prevent-updates-project.yaml
│ │ ├── disallow-container-sock-mounts.yaml
│ │ ├── disallow-empty-ingress-host.yaml
│ │ ├── disallow-latest-tag.yaml
│ │ ├── drop-all-capabilities.yaml
│ │ ├── drop-cap-net-raw.yaml
│ │ ├── kustomization.yaml
│ │ ├── require-labels.yaml
│ │ ├── require-pod-probes.yaml
│ │ ├── require-requests-limits.yaml
│ │ ├── require-ro-rootfs.yaml
│ │ ├── restrict-deprecated-registry.yaml
│ │ └── restrict-nodeport.yaml
│ └── security
│ └── kyverno
│ ├── Chart.yaml
│ └── values.yaml
├── chapter14
└── chapter-14-forecasting-and-monitoring-costs-with-gitops
│ ├── applicationsets
│ └── optimization
│ │ └── opencost-applicationset.yaml
│ ├── cluster
│ ├── in-cluster-austria
│ │ └── optimization
│ │ │ └── opencost
│ │ │ └── values.yaml
│ ├── in-cluster-germany
│ │ └── optimization
│ │ │ └── opencost
│ │ │ └── values.yaml
│ └── in-cluster-ireland
│ │ └── optimization
│ │ └── opencost
│ │ └── values.yaml
│ └── optimization
│ ├── kubecost
│ ├── Chart.yaml
│ ├── README.md
│ ├── images
│ │ ├── kubecost_ui_budget_setup_alert.gif
│ │ └── kubecost_ui_setup_alert.gif
│ └── values.yaml
│ └── opencost
│ ├── Chart.yaml
│ └── values.yaml
└── pod-gitops-terraform-automation-tf-runner.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Helm
2 | charts/
3 | Chart.lock
4 |
5 | # Docker
6 | .DS_Store
7 | .dockerignore
8 | docker-compose.yml
9 | Dockerfile
10 |
11 | # Kubernetes
12 | *.env
13 |
14 | # macOS
15 | .DS_Store
16 |
--------------------------------------------------------------------------------
/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Implementing-GitOps-with-Kubernetes/123557c4905e8677fc4a6807aee28e146dcc192e/.gitkeep
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Packt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Implementing GitOps with Kubernetes
2 |
3 |
4 |
5 | This is the code repository for [Implementing GitOps with Kubernetes](https://www.packtpub.com/en-in/product/implementing-gitops-with-kubernetes-9781835884225?utm_source=github&utm_medium=repository&utm_campaign=9781786461629), published by Packt.
6 |
7 | **Automate, manage, scale, and secure infrastructure and cloud-native applications on AWS and Azure**
8 |
9 | ## What is this book about?
10 | This book provides step-by-step tutorials and hands-on examples for effectively implementing GitOps practices in your Kubernetes deployments. You’ll learn how to automate, monitor, and secure your infrastructure for efficient application delivery.
11 |
12 |
13 | This book covers the following exciting features:
14 | * Delve into GitOps methods and best practices used for modern cloud-native environments
15 | * Explore GitOps tools such as GitHub, Argo CD, Flux CD, Helm, and Kustomize
16 | * Automate Kubernetes CI/CD workflows using GitOps and GitHub Actions
17 | * Deploy infrastructure as code using Terraform, OpenTofu, and GitOps
18 | * Automate AWS, Azure, and OpenShift platforms with GitOps
19 | * Understand multitenancy, rolling back deployments, and how to handle stateful applications using GitOps methods
20 | * Implement observability, security, cost optimization, and AI in GitOps practices
21 |
22 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1835884237) today!
23 |
24 |
26 |
27 | ## Instructions and Navigations
28 | All of the code is organized into folders. For example, Chapter02.
29 |
30 | The code will look like the following:
31 | ```
32 | helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
33 | helm install sealed-secrets sealed-secrets/sealed-secrets
34 | #Install e.g. CLI on MacOS
35 | brew install kubeseal
36 | ```
37 |
38 | **Following is what you need for this book:**
39 | This book is for DevOps engineers, platform engineers, SREs, and cloud engineers who want to get skilled at implementing GitOps practices effectively in cloud-native environments. A foundational understanding of cloud computing, containerization, infrastructure as code, DevOps, CI/CD principles, and Kubernetes will be helpful to get the most out of this book.
40 |
41 | With the following software and hardware list you can run all code files present in the book (Chapter 1-14).
42 | ### Software and Hardware List
43 | | Chapter | Software required | OS required |
44 | | -------- | ------------------------------------ | ----------------------------------- |
45 | | 1-14 | Kubernetes | Windows, Mac OS X, and Linux (Any) |
46 | | 1-14 | Git | Windows, Mac OS X, and Linux (Any) |
47 | | 1-14 | Docker | Windows, Mac OS X, and Linux (Any) |
48 | | 1-14 | Argo CD | Windows, Mac OS X, and Linux (Any) |
49 | | 1-14 | Flux CD | Windows, Mac OS X, and Linux (Any) |
50 | | 1-14 | Helm | Windows, Mac OS X, and Linux (Any) |
51 | | 1-14 | Kustomize | Windows, Mac OS X, and Linux (Any) |
52 | | 1-14 | Terraform | Windows, Mac OS X, and Linux (Any) |
53 | | 1-14 | Azure Kubernetes Service (AKS) | Windows, Mac OS X, and Linux (Any) |
54 | | 1-14 | AWS Elastic Kubernetes Service (EKS) | Windows, Mac OS X, and Linux (Any) |
55 | | 1-14 | OpenShift | Windows, Mac OS X, and Linux (Any) |
56 |
57 |
58 | ### Related products
59 | * Automating DevOps with GitLab CI/CD Pipelines [[Packt]](https://www.packtpub.com/en-in/product/automating-devops-with-gitlab-cicd-pipelines-9781803233000?utm_source=github&utm_medium=repository&utm_campaign=) [[Amazon]](https://www.amazon.com/dp/1803233001)
60 |
61 | * Modern DevOps Practices [[Packt]](https://www.packtpub.com/en-in/product/modern-devops-practices-9781805121824?utm_source=github&utm_medium=repository&utm_campaign=) [[Amazon]](https://www.amazon.com/dp/1805121820)
62 |
63 | ## Get to Know the Author
64 | **Pietro Libro**
65 | is a tech enthusiast with over two decades of experience in software development and software architecture. His pragmatic problem-solving skills have been honed through work in the public administration, finance, and automation industries. He holds a master’s degree in computer science from the University of Rome, La Sapienza. Over the years, Pietro has transitioned from software development to a solution and cloud architect role. He is currently awaiting the defense of his PhD in bioinformatics at the University of Tuscia. Pietro’s dedication to learning is evident through his numerous certifications and his role as a technical speaker. Specializing in various technologies, especially software and cloud architecture, he relocated from Italy to Switzerland. Currently serving as a cloud solution architect in Zürich, Pietro lives with his wife, Eleonora, his daughter, Giulia, and their cat, “Miau”. In his free time, Pietro enjoys biking, practicing taekwondo, watching science fiction movies and series, and spending time with his family.
66 |
67 |
68 | **Artem Lajko**
69 | is a passionate and driven platform engineer and Kubstronaut, boasting over eight years of IT experience, backed by a master’s degree in computer science. His track record showcases expertise in designing, developing, and deploying efficient and scalable cloud infrastructures. As a curious and continuous learner, Artem holds certifications in Azure, AWS, Kubernetes, and GitOps. Currently, he’s playing a pivotal role in enhancing innovation and application management at the Port of Hamburg. His technical acumen spans cloud infrastructures, cross-cloud solutions, and DevOps practices. He is also passionate about blogging and networking with manufacturers to craft top-notch solutions using market-available tools.
70 |
71 |
--------------------------------------------------------------------------------
/chapter02/Docker_Hello_World/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | app = Flask(__name__)
3 |
4 | @app.route('/')
5 | def hello_world():
6 | return 'Hello, World!\n'
7 |
8 | @app.route('/name/')
9 | def hello_name(name):
10 | return 'Hello, {}\n'.format(name)
11 |
12 | @app.route('/datetime')
13 | def datetime():
14 | import datetime
15 | now = datetime.datetime.now()
16 | return now.strftime("%Y-%m-%d %H:%M:%S\n")
17 |
18 | if __name__ == '__main__':
19 | app.run(debug=True, host='0.0.0.0', port=80)
20 |
21 |
--------------------------------------------------------------------------------
/chapter02/Docker_Hello_World/dockerfile:
--------------------------------------------------------------------------------
1 | # Use an official Python runtime as a parent image
2 | FROM python:3.8-slim
3 |
4 | # Set the working directory in the container
5 | WORKDIR /usr/src/app
6 |
7 | # Copy the current directory contents into the container at /usr/src/app
8 | COPY . .
9 |
10 | # Install any needed packages specified in requirements.txt
11 | RUN pip install --no-cache-dir -r requirements.txt
12 |
13 | # Make port 80 available to the world outside this container
14 | EXPOSE 80
15 |
16 | # Define environment variable
17 | ENV NAME World
18 |
19 | # Run app.py when the container launches
20 | CMD ["python", "app.py"]
21 |
--------------------------------------------------------------------------------
/chapter02/Docker_Hello_World/first-cd-pipeline-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: first-cd-pipeline-deployment
5 | namespace: gitops-kubernetes
6 | labels:
7 | app: first-cd-pipeline
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: first-cd-pipeline
13 | template:
14 | metadata:
15 | labels:
16 | app: first-cd-pipeline
17 | spec:
18 | containers:
19 | - name: first-cd-pipeline
20 | image: pietrolibro/hello-world-py-app:2.0
21 | ports:
22 | - containerPort: 80
23 | ---
24 | apiVersion: v1
25 | kind: Service
26 | metadata:
27 | name: first-cd-pipeline-service
28 | namespace: gitops-kubernetes
29 | spec:
30 | type: NodePort
31 | selector:
32 | app: first-cd-pipeline
33 | ports:
34 | - protocol: TCP
35 | port: 80
36 | nodePort: 30007
37 |
--------------------------------------------------------------------------------
/chapter02/Docker_Hello_World/requirements.txt:
--------------------------------------------------------------------------------
1 | blinker==1.6.3
2 | click==8.1.7
3 | colorama==0.4.6
4 | distlib==0.3.7
5 | filelock==3.12.3
6 | Flask==3.0.0
7 | itsdangerous==2.1.2
8 | Jinja2>=3.1.4
9 | MarkupSafe==2.1.3
10 | platformdirs==3.10.0
11 | virtualenv==20.24.5
12 | Werkzeug>=3.0.3
--------------------------------------------------------------------------------
/chapter02/hello-world-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: hello-world-deployment
5 | namespace: gitops-kubernetes
6 | labels:
7 | app: hello-world
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: hello-world
13 | template:
14 | metadata:
15 | labels:
16 | app: hello-world
17 | spec:
18 | containers:
19 | - name: hello-world
20 | image: nginxdemos/hello
21 | ports:
22 | - containerPort: 80
23 | ---
24 | apiVersion: v1
25 | kind: Service
26 | metadata:
27 | name: hello-world-service
28 | namespace: gitops-kubernetes
29 | spec:
30 | type: NodePort
31 | selector:
32 | app: hello-world
33 | ports:
34 | - protocol: TCP
35 | port: 80
36 | nodePort: 30007
37 |
--------------------------------------------------------------------------------
/chapter03/dockerfile:
--------------------------------------------------------------------------------
1 | # Use an official Python runtime as a parent image
2 | FROM python:3.8-slim
3 |
4 | # Set the working directory in the container
5 | WORKDIR /usr/src/app
6 |
7 | # Copy the current directory contents into the container at /usr/src/app
8 | COPY . .
9 |
10 | # Install any needed packages specified in requirements.txt
11 | RUN pip install --no-cache-dir -r requirements.txt
12 |
13 | # Make port 80 available to the world outside this container
14 | EXPOSE 80
15 |
16 | # Define environment variable
17 | ENV NAME World
18 |
19 | # Run app.py when the container launches
20 | CMD ["python", "app.py"]
21 |
--------------------------------------------------------------------------------
/chapter03/requirements.txt:
--------------------------------------------------------------------------------
1 | blinker==1.6.3
2 | click==8.1.7
3 | colorama==0.4.6
4 | distlib==0.3.7
5 | filelock==3.12.3
6 | Flask==3.0.0
7 | itsdangerous==2.1.2
8 | Jinja2>=3.1.4
9 | MarkupSafe==2.1.3
10 | platformdirs==3.10.0
11 | virtualenv==20.24.5
12 | Werkzeug>=3.0.3
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/.github/workflows/build-and-push-image.yml:
--------------------------------------------------------------------------------
1 | name: Publish Docker image
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | - 'release'
8 |
9 | jobs:
10 | push_to_registry:
11 | name: Push Docker image to Docker Hub
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Check out the repo
15 | uses: actions/checkout@v4
16 |
17 | - name: Log in to Docker Hub
18 | uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
19 | with:
20 | username: ${{ secrets.DOCKER_USERNAME }}
21 | password: ${{ secrets.DOCKER_PASSWORD }}
22 |
23 | - name: Extract metadata (tags, labels) for Docker
24 | id: meta
25 | uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
26 | with:
27 | images: pietrolibro/gitops-k8s-deployments
28 |
29 | - name: Build and push Docker image
30 | uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
31 | with:
32 | context: .
33 | file: ./src/dockerfile
34 | push: true
35 | tags: ${{ steps.meta.outputs.tags }}
36 | labels: ${{ steps.meta.outputs.labels }}
37 |
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/argocd-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: my-city-weahter-app
5 | namespace: argocd
6 | spec:
7 | destination:
8 | namespace: gitops-kubernetes
9 | server: https://kubernetes.default.svc
10 | project: default
11 | source:
12 | repoURL: https://github.com/pietrolibro/gitops-k8s-deployments-book-weather-app.git
13 | path: deployment
14 | targetRevision: main
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/deployment/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: zurich-weather-app
5 | labels:
6 | app: zurich-weather-app
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app: zurich-weather-app
12 | template:
13 | metadata:
14 | labels:
15 | app: zurich-weather-app
16 | spec:
17 | containers:
18 | - name: zurich-weather-app
19 | image: pietrolibro/zurich-weather-app:1.0
20 | ports:
21 | - containerPort: 8080
22 | # resources:
23 | # requests:
24 | # memory: "64Mi"
25 | # cpu: "250m"
26 | # limits:
27 | # memory: "128Mi"
28 | # cpu: "500m"
29 |
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/deployment/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: zurich-weather-app-service
5 | spec:
6 | type: LoadBalancer
7 | ports:
8 | - port: 80
9 | targetPort: 8080
10 | protocol: TCP
11 | selector:
12 | app: zurich-weather-app
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/src/data.csv:
--------------------------------------------------------------------------------
1 | Date,Temperature
2 | 2023-01-01,5
3 | 2023-01-02,6
4 | 2023-01-03,4
5 | 2023-01-04,4
6 | 2023-01-05,5
7 | 2023-01-06,5
8 | 2023-01-07,6
9 | 2023-01-08,6
10 | 2023-01-09,7
11 | 2023-01-10,6
12 | 2023-01-11,7
13 |
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/src/data.csv.bak:
--------------------------------------------------------------------------------
1 | Date,Temperature
2 | 2023-01-01,5
3 | 2023-01-02,6
4 | 2023-01-03,4
5 | 2023-01-04,3
6 | 2023-01-05,4
7 | 2023-01-06,5
8 | 2023-01-07,6
9 | 2023-01-08,7
10 | 2023-01-09,8
11 | 2023-01-10,6
12 | 2023-01-11,7
13 |
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/src/dockerfile:
--------------------------------------------------------------------------------
1 | # Use an official Node.js runtime as a parent image
2 | FROM node:14
3 |
4 | # Set the working directory
5 | WORKDIR /usr/src/app
6 |
7 | # Copy package.json and install dependencies
8 | COPY package*.json ./
9 | RUN npm install
10 |
11 | # Bundle app source
12 | COPY . .
13 |
14 | # Expose the port the app runs on
15 | EXPOSE 3000
16 |
17 | # Define the command to run the app
18 | CMD ["node", "server.js"]
19 |
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Temperature Chart
5 |
6 |
7 |
8 |
9 |
10 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "temperature-chart-app",
3 | "version": "1.0.0",
4 | "description": "A simple web app displaying temperature charts from CSV data",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "dependencies": {
10 | "express": "^4.17.1"
11 | },
12 | "author": "",
13 | "license": "ISC"
14 | }
15 |
--------------------------------------------------------------------------------
/chapter04/argocd_gitops/src/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const port = 3000;
4 |
5 | app.use(express.static('.')); // Serve static files from the current directory
6 |
7 | app.listen(port, () => {
8 | console.log(`Server running at http://localhost:${port}`);
9 | });
10 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *.orig
18 | *~
19 | # Various IDEs
20 | .project
21 | .idea/
22 | *.tmproj
23 | .vscode/
24 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: gitops-k8s-deployments-helm
3 | description: A Helm chart for Kubernetes
4 |
5 | # A chart can be either an 'application' or a 'library' chart.
6 | #
7 | # Application charts are a collection of templates that can be packaged into versioned archives
8 | # to be deployed.
9 | #
10 | # Library charts provide useful utilities or functions for the chart developer. They're included as
11 | # a dependency of application charts to inject those utilities and functions into the rendering
12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed.
13 | type: application
14 |
15 | # This is the chart version. This version number should be incremented each time you make changes
16 | # to the chart and its templates, including the app version.
17 | # Versions are expected to follow Semantic Versioning (https://semver.org/)
18 | version: 0.2.0
19 |
20 | # This is the version number of the application being deployed. This version number should be
21 | # incremented each time you make changes to the application. Versions are not expected to
22 | # follow Semantic Versioning. They should reflect the version the application is using.
23 | # It is recommended to use it with quotes.
24 | appVersion: "2.1.0"
25 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | 1. Get the application URL by running these commands:
2 | {{- if .Values.ingress.enabled }}
3 | {{- range $host := .Values.ingress.hosts }}
4 | {{- range .paths }}
5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
6 | {{- end }}
7 | {{- end }}
8 | {{- else if contains "NodePort" .Values.service.type }}
9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "gitops-k8s-deployments-helm.fullname" . }})
10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
11 | echo http://$NODE_IP:$NODE_PORT
12 | {{- else if contains "LoadBalancer" .Values.service.type }}
13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available.
14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "gitops-k8s-deployments-helm.fullname" . }}'
15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "gitops-k8s-deployments-helm.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
16 | echo http://$SERVICE_IP:{{ .Values.service.port }}
17 | {{- else if contains "ClusterIP" .Values.service.type }}
18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "gitops-k8s-deployments-helm.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19 | export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
20 | echo "Visit http://127.0.0.1:8080 to use your application"
21 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
22 | {{- end }}
23 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | Expand the name of the chart.
3 | */}}
4 | {{- define "gitops-k8s-deployments-helm.name" -}}
5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6 | {{- end }}
7 |
8 | {{/*
9 | Create a default fully qualified app name.
10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11 | If release name contains chart name it will be used as a full name.
12 | */}}
13 | {{- define "gitops-k8s-deployments-helm.fullname" -}}
14 | {{- if .Values.fullnameOverride }}
15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16 | {{- else }}
17 | {{- $name := default .Chart.Name .Values.nameOverride }}
18 | {{- if contains $name .Release.Name }}
19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }}
20 | {{- else }}
21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22 | {{- end }}
23 | {{- end }}
24 | {{- end }}
25 |
26 | {{/*
27 | Create chart name and version as used by the chart label.
28 | */}}
29 | {{- define "gitops-k8s-deployments-helm.chart" -}}
30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31 | {{- end }}
32 |
33 | {{/*
34 | Common labels
35 | */}}
36 | {{- define "gitops-k8s-deployments-helm.labels" -}}
37 | helm.sh/chart: {{ include "gitops-k8s-deployments-helm.chart" . }}
38 | {{ include "gitops-k8s-deployments-helm.selectorLabels" . }}
39 | {{- if .Chart.AppVersion }}
40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41 | {{- end }}
42 | app.kubernetes.io/managed-by: {{ .Release.Service }}
43 | {{- end }}
44 |
45 | {{/*
46 | Selector labels
47 | */}}
48 | {{- define "gitops-k8s-deployments-helm.selectorLabels" -}}
49 | app.kubernetes.io/name: {{ include "gitops-k8s-deployments-helm.name" . }}
50 | app.kubernetes.io/instance: {{ .Release.Name }}
51 | {{- end }}
52 |
53 | {{/*
54 | Create the name of the service account to use
55 | */}}
56 | {{- define "gitops-k8s-deployments-helm.serviceAccountName" -}}
57 | {{- if .Values.serviceAccount.create }}
58 | {{- default (include "gitops-k8s-deployments-helm.fullname" .) .Values.serviceAccount.name }}
59 | {{- else }}
60 | {{- default "default" .Values.serviceAccount.name }}
61 | {{- end }}
62 | {{- end }}
63 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/templates/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: {{ include "gitops-k8s-deployments-helm.fullname" . }}
5 | labels:
6 | {{- include "gitops-k8s-deployments-helm.labels" . | nindent 4 }}
7 | spec:
8 | {{- if not .Values.autoscaling.enabled }}
9 | replicas: {{ .Values.replicaCount }}
10 | {{- end }}
11 | selector:
12 | matchLabels:
13 | {{- include "gitops-k8s-deployments-helm.selectorLabels" . | nindent 6 }}
14 | template:
15 | metadata:
16 | {{- with .Values.podAnnotations }}
17 | annotations:
18 | {{- toYaml . | nindent 8 }}
19 | {{- end }}
20 | labels:
21 | {{- include "gitops-k8s-deployments-helm.labels" . | nindent 8 }}
22 | {{- with .Values.podLabels }}
23 | {{- toYaml . | nindent 8 }}
24 | {{- end }}
25 | spec:
26 | {{- with .Values.imagePullSecrets }}
27 | imagePullSecrets:
28 | {{- toYaml . | nindent 8 }}
29 | {{- end }}
30 | serviceAccountName: {{ include "gitops-k8s-deployments-helm.serviceAccountName" . }}
31 | securityContext:
32 | {{- toYaml .Values.podSecurityContext | nindent 8 }}
33 | containers:
34 | - name: {{ .Chart.Name }}
35 | securityContext:
36 | {{- toYaml .Values.securityContext | nindent 12 }}
37 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
38 | imagePullPolicy: {{ .Values.image.pullPolicy }}
39 | ports:
40 | - name: http
41 | containerPort: {{ .Values.service.port }}
42 | protocol: TCP
43 | livenessProbe:
44 | {{- toYaml .Values.livenessProbe | nindent 12 }}
45 | readinessProbe:
46 | {{- toYaml .Values.readinessProbe | nindent 12 }}
47 | resources:
48 | {{- toYaml .Values.resources | nindent 12 }}
49 | {{- with .Values.volumeMounts }}
50 | volumeMounts:
51 | {{- toYaml . | nindent 12 }}
52 | {{- end }}
53 | {{- with .Values.volumes }}
54 | volumes:
55 | {{- toYaml . | nindent 8 }}
56 | {{- end }}
57 | {{- with .Values.nodeSelector }}
58 | nodeSelector:
59 | {{- toYaml . | nindent 8 }}
60 | {{- end }}
61 | {{- with .Values.affinity }}
62 | affinity:
63 | {{- toYaml . | nindent 8 }}
64 | {{- end }}
65 | {{- with .Values.tolerations }}
66 | tolerations:
67 | {{- toYaml . | nindent 8 }}
68 | {{- end }}
69 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/templates/hpa.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.autoscaling.enabled }}
2 | apiVersion: autoscaling/v2
3 | kind: HorizontalPodAutoscaler
4 | metadata:
5 | name: {{ include "gitops-k8s-deployments-helm.fullname" . }}
6 | namespace: {{ .Values.namespace }}
7 | labels:
8 | {{- include "gitops-k8s-deployments-helm.labels" . | nindent 4 }}
9 | spec:
10 | scaleTargetRef:
11 | apiVersion: apps/v1
12 | kind: Deployment
13 | name: {{ include "gitops-k8s-deployments-helm.fullname" . }}
14 | minReplicas: {{ .Values.autoscaling.minReplicas }}
15 | maxReplicas: {{ .Values.autoscaling.maxReplicas }}
16 | metrics:
17 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
18 | - type: Resource
19 | resource:
20 | name: cpu
21 | target:
22 | type: Utilization
23 | averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
24 | {{- end }}
25 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
26 | - type: Resource
27 | resource:
28 | name: memory
29 | target:
30 | type: Utilization
31 | averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
32 | {{- end }}
33 | {{- end }}
34 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.ingress.enabled -}}
2 | {{- $fullName := include "gitops-k8s-deployments-helm.fullname" . -}}
3 | {{- $svcPort := .Values.service.port -}}
4 | {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
5 | {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
6 | {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
7 | {{- end }}
8 | {{- end }}
9 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
10 | apiVersion: networking.k8s.io/v1
11 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
12 | apiVersion: networking.k8s.io/v1beta1
13 | {{- else -}}
14 | apiVersion: extensions/v1beta1
15 | {{- end }}
16 | kind: Ingress
17 | metadata:
18 | name: {{ $fullName }}
19 | labels:
20 | {{- include "gitops-k8s-deployments-helm.labels" . | nindent 4 }}
21 | {{- with .Values.ingress.annotations }}
22 | annotations:
23 | {{- toYaml . | nindent 4 }}
24 | {{- end }}
25 | spec:
26 | {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
27 | ingressClassName: {{ .Values.ingress.className }}
28 | {{- end }}
29 | {{- if .Values.ingress.tls }}
30 | tls:
31 | {{- range .Values.ingress.tls }}
32 | - hosts:
33 | {{- range .hosts }}
34 | - {{ . | quote }}
35 | {{- end }}
36 | secretName: {{ .secretName }}
37 | {{- end }}
38 | {{- end }}
39 | rules:
40 | {{- range .Values.ingress.hosts }}
41 | - host: {{ .host | quote }}
42 | http:
43 | paths:
44 | {{- range .paths }}
45 | - path: {{ .path }}
46 | {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
47 | pathType: {{ .pathType }}
48 | {{- end }}
49 | backend:
50 | {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
51 | service:
52 | name: {{ $fullName }}
53 | port:
54 | number: {{ $svcPort }}
55 | {{- else }}
56 | serviceName: {{ $fullName }}
57 | servicePort: {{ $svcPort }}
58 | {{- end }}
59 | {{- end }}
60 | {{- end }}
61 | {{- end }}
62 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ include "gitops-k8s-deployments-helm.fullname" . }}
5 | labels:
6 | {{- include "gitops-k8s-deployments-helm.labels" . | nindent 4 }}
7 | spec:
8 | type: {{ .Values.service.type }}
9 | ports:
10 | - port: {{ .Values.service.port }}
11 | targetPort: http
12 | protocol: TCP
13 | name: http
14 | selector:
15 | {{- include "gitops-k8s-deployments-helm.selectorLabels" . | nindent 4 }}
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceAccount.create -}}
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: {{ include "gitops-k8s-deployments-helm.serviceAccountName" . }}
6 | labels:
7 | {{- include "gitops-k8s-deployments-helm.labels" . | nindent 4 }}
8 | {{- with .Values.serviceAccount.annotations }}
9 | annotations:
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
13 | {{- end }}
14 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/templates/tests/test-connection.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: "{{ include "gitops-k8s-deployments-helm.fullname" . }}-test-connection"
5 | labels:
6 | {{- include "gitops-k8s-deployments-helm.labels" . | nindent 4 }}
7 | annotations:
8 | "helm.sh/hook": test
9 | spec:
10 | containers:
11 | - name: wget
12 | image: busybox
13 | command: ['wget']
14 | args: ['{{ include "gitops-k8s-deployments-helm.fullname" . }}:{{ .Values.service.port }}']
15 | restartPolicy: Never
16 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-helm/values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for gitops-k8s-deployments-helm.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 |
5 | replicaCount: 1
6 |
7 | image:
8 | repository: pietrolibro/hello-world-py-app
9 | pullPolicy: IfNotPresent
10 | # Overrides the image tag whose default is the chart appVersion.
11 | tag: 2.0
12 |
13 | imagePullSecrets: []
14 | nameOverride: ""
15 | fullnameOverride: ""
16 |
17 | serviceAccount:
18 | # Specifies whether a service account should be created
19 | create: true
20 | # Automatically mount a ServiceAccount's API credentials?
21 | automount: true
22 | # Annotations to add to the service account
23 | annotations: {}
24 | # The name of the service account to use.
25 | # If not set and create is true, a name is generated using the fullname template
26 | name: ""
27 |
28 | podAnnotations: {}
29 | podLabels: {}
30 |
31 | podSecurityContext: {}
32 | # fsGroup: 2000
33 |
34 | securityContext: {}
35 | # capabilities:
36 | # drop:
37 | # - ALL
38 | # readOnlyRootFilesystem: true
39 | # runAsNonRoot: true
40 | # runAsUser: 1000
41 |
42 | service:
43 | type: ClusterIP
44 | port: 80
45 |
46 | ingress:
47 | enabled: false
48 | className: ""
49 | annotations: {}
50 | # kubernetes.io/ingress.class: nginx
51 | # kubernetes.io/tls-acme: "true"
52 | hosts:
53 | - host: chart-example.local
54 | paths:
55 | - path: /
56 | pathType: ImplementationSpecific
57 | tls: []
58 | # - secretName: chart-example-tls
59 | # hosts:
60 | # - chart-example.local
61 |
62 | resources: {}
63 | # We usually recommend not to specify default resources and to leave this as a conscious
64 | # choice for the user. This also increases chances charts run on environments with little
65 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
66 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
67 | # limits:
68 | # cpu: 100m
69 | # memory: 128Mi
70 | # requests:
71 | # cpu: 100m
72 | # memory: 128Mi
73 |
74 | livenessProbe:
75 | httpGet:
76 | path: /
77 | port: http
78 | readinessProbe:
79 | httpGet:
80 | path: /
81 | port: http
82 |
83 | autoscaling:
84 | enabled: false
85 | minReplicas: 1
86 | maxReplicas: 100
87 | targetCPUUtilizationPercentage: 80
88 | # targetMemoryUtilizationPercentage: 80
89 |
90 | # Additional volumes on the output Deployment definition.
91 | volumes: []
92 | # - name: foo
93 | # secret:
94 | # secretName: mysecret
95 | # optional: false
96 |
97 | # Additional volumeMounts on the output Deployment definition.
98 | volumeMounts: []
99 | # - name: foo
100 | # mountPath: "/etc/foo"
101 | # readOnly: true
102 |
103 | nodeSelector: {}
104 |
105 | tolerations: []
106 |
107 | affinity: {}
108 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-kustomize/base/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: gitops-k8s-kustomize
5 | labels:
6 | app: gitops-k8s-kustomize
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app: gitops-k8s-kustomize
12 | template:
13 | metadata:
14 | labels:
15 | app: gitops-k8s-kustomize
16 | spec:
17 | containers:
18 | - name: gitops-k8s-kustomize
19 | image: k8s.gcr.io/echoserver:1.10
20 | ports:
21 | - containerPort: 8080
22 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-kustomize/base/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - deployment.yaml
3 | - service.yaml
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-kustomize/base/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: gitops-k8s-kustomize
5 | spec:
6 | selector:
7 | app: gitops-k8s-kustomize
8 | ports:
9 | - protocol: TCP
10 | port: 80
11 | targetPort: 8080
12 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-kustomize/overlays/development/kustomization.yaml:
--------------------------------------------------------------------------------
1 | namespace: gitops-k8s-kustomize-dev
2 | resources:
3 | - ../../base
4 | patches:
5 | - path: ./patches/deployment_patch.yaml
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-kustomize/overlays/development/patches/deployment_patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: gitops-k8s-kustomize
5 | labels:
6 | environment: development
7 | spec:
8 | replicas: 2 # Increase the number of replicas in development.
9 |
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-kustomize/overlays/staging/kustomization.yaml:
--------------------------------------------------------------------------------
1 | namespace: gitops-k8s-kustomize-stg
2 | resources:
3 | - ../../base
4 | patches:
5 | - path: ./patches/deployment_patch.yaml
--------------------------------------------------------------------------------
/chapter04/gitops-k8s-deployments-kustomize/overlays/staging/patches/deployment_patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: gitops-k8s-kustomize
5 | labels:
6 | environment: staging
7 | spec:
8 | replicas: 3 # Increase the number of replicas in staging.
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/applicationsets/argocd-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: argocd
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev #cluster label
12 | core-basic: enabled
13 | values:
14 | branch: main #point to main branch
15 | - clusters:
16 | selector:
17 | matchLabels:
18 | env: prod #cluster label
19 | core-basic: enabled
20 | values:
21 | branch: main #point to main branch
22 | template:
23 | metadata:
24 | name: "{{name}}-argocd" #name of cluster + name of application
25 | annotations:
26 | argocd.argoproj.io/manifest-generate-paths: ".;.."
27 | spec:
28 | project: default
29 | sources:
30 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #custom values repo
31 | targetRevision: main #point to main branch of custom values repo
32 | ref: valuesRepo #use valuesRepo variable
33 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #umbrella repo
34 | targetRevision: "{{values.branch}}" #point to the branch depending on the cluster branch pointer
35 | path: "./chapter05/chapter-5-building-a-service-catalog-for-kubernetes/system/argocd" #path to the application in the umbrella repo
36 | helm:
37 | releaseName: "argocd" # Release name override (defaults to application name)
38 | valueFiles:
39 | - "values.yaml" #use values.yaml from umbrella repo
40 | - "$valuesRepo/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/{{name}}/system/argocd/values.yaml" #override umbrella values from custom values repo
41 | destination:
42 | name: "{{name}}"
43 | namespace: "argocd"
44 | syncPolicy:
45 | automated:
46 | prune: false
47 | selfHeal: true
48 | syncOptions:
49 | - CreateNamespace=true
50 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/applicationsets/cert-mananger-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: cert-manager
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev #cluster label
12 | core-basic: enabled
13 | security-basic: enabled
14 | values:
15 | branch: main #point to main branch
16 | - clusters:
17 | selector:
18 | matchLabels:
19 | env: prod #cluster label
20 | core-basic: enabled
21 | security-basic: enabled
22 | values:
23 | branch: main #point to main branch
24 | template:
25 | metadata:
26 | name: "{{name}}-cert-manager" #name of cluster + name of application
27 | annotations:
28 | argocd.argoproj.io/manifest-generate-paths: ".;.."
29 | spec:
30 | project: default
31 | sources:
32 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #custom values repo
33 | targetRevision: main #point to main branch of custom values repo
34 | ref: valuesRepo #use valuesRepo variable
35 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #umbrella repo
36 | targetRevision: "{{values.branch}}" #point to the branch depending on the cluster branch pointer
37 | path: "./chapter05/chapter-5-building-a-service-catalog-for-kubernetes/security/cert-manager" #path to the application in the umbrella repo
38 | helm:
39 | releaseName: "cert-manager" # Release name override (defaults to application name)
40 | valueFiles:
41 | - "values.yaml" #use values.yaml from umbrella repo
42 | - "$valuesRepo/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/{{name}}/security/cert-manager/values.yaml" #override umbrella values from custom values repo
43 | destination:
44 | name: "{{name}}"
45 | namespace: "cert-manager"
46 | syncPolicy:
47 | automated:
48 | prune: false
49 | selfHeal: true
50 | syncOptions:
51 | - CreateNamespace=true
52 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/applicationsets/external-dns-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: external-dns
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev #cluster label
12 | core-basic: enabled
13 | values:
14 | branch: main #point to main branch
15 | - clusters:
16 | selector:
17 | matchLabels:
18 | env: prod #cluster label
19 | core-basic: enabled
20 | values:
21 | branch: main #point to main branch
22 | template:
23 | metadata:
24 | name: "{{name}}-external-dns" #name of cluster + name of application
25 | annotations:
26 | argocd.argoproj.io/manifest-generate-paths: ".;.."
27 | spec:
28 | project: default
29 | sources:
30 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #custom values repo
31 | targetRevision: main #point to main branch of custom values repo
32 | ref: valuesRepo #use valuesRepo variable
33 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #umbrella repo
34 | targetRevision: "{{values.branch}}" #point to the branch depending on the cluster branch pointer
35 | path: "./chapter05/chapter-5-building-a-service-catalog-for-kubernetes/dns/external-dns" #path to the application in the umbrella repo
36 | helm:
37 | releaseName: "external-dns" # Release name override (defaults to application name)
38 | valueFiles:
39 | - "values.yaml" #use values.yaml from umbrella repo
40 | - "$valuesRepo/chapter-5-building-a-service-catalog-for-kubernetes/cluster/{{name}}/dns/external-dns/values.yaml" #override umbrella values from custom values repo
41 | destination:
42 | name: "{{name}}"
43 | namespace: "external-dns"
44 | syncPolicy:
45 | automated:
46 | prune: false
47 | selfHeal: true
48 | syncOptions:
49 | - CreateNamespace=true
50 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/applicationsets/nginx-ingress-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: ingress-nginx
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev #cluster label
12 | core-basic: enabled
13 | values:
14 | branch: main #point to main branch
15 | - clusters:
16 | selector:
17 | matchLabels:
18 | env: prod #cluster label
19 | core-basic: enabled
20 | values:
21 | branch: main #point to main branch
22 | template:
23 | metadata:
24 | name: "{{name}}-ingress-nginx" #name of cluster + name of application
25 | annotations:
26 | argocd.argoproj.io/manifest-generate-paths: ".;.."
27 | spec:
28 | project: default
29 | sources:
30 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #custom values repo
31 | targetRevision: main #point to main branch of custom values repo
32 | ref: valuesRepo #use valuesRepo variable
33 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #umbrella repo
34 | targetRevision: "{{values.branch}}" #point to the branch depending on the cluster branch pointer
35 | path: "./chapter05/chapter-5-building-a-service-catalog-for-kubernetes/networking/ingress-nginx" #path to the application in the umbrella repo
36 | helm:
37 | releaseName: "ingress-nginx" # Release name override (defaults to application name)
38 | valueFiles:
39 | - "values.yaml" #use values.yaml from umbrella repo
40 | - "$valuesRepo/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/{{name}}/networking/ingress-nginx/values.yaml" #override umbrella values from custom values repo
41 | destination:
42 | name: "{{name}}"
43 | namespace: "ingress-nginx"
44 | syncPolicy:
45 | automated:
46 | prune: false
47 | selfHeal: true
48 | syncOptions:
49 | - CreateNamespace=true
50 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/bonus/dependencies.yaml:
--------------------------------------------------------------------------------
1 | config:
2 | BRANCH: main
3 | DRY_RUN: true
4 | GITHUB: false
5 | AZURE_DEVOPS: false
6 | WITHOUT_PR: false
7 |
8 | dependencies:
9 | - name: "External DNS"
10 | arrayPosition: 0
11 | repositoryName: bitnami/external-dns
12 | sourcePath: examples/dns/external-dns
13 | - name: "Kyverno"
14 | arrayPosition: 0
15 | repositoryName: kyverno/kyverno
16 | sourcePath: examples/security/kyverno
17 | - name: "minIO Operator"
18 | arrayPosition: 0
19 | repositoryName: minio-operator/operator
20 | sourcePath: examples/storage/minio-operator
21 | - name: "minIO Tenant"
22 | arrayPosition: 0
23 | repositoryName: minio-tenant/tenant
24 | sourcePath: examples/storage/minio-tenant
25 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/bonus/examples/dns/external-dns/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: external-dns
3 | version: 1.0.0
4 | description: This Chart deploys external-dns.
5 | dependencies:
6 | - name: external-dns
7 | version: 6.20.4
8 | repository: https://charts.bitnami.com/bitnami
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/bonus/examples/security/kyverno/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: kyverno
3 | version: 1.0.0
4 | description: This Chart deploys kyverno.
5 | dependencies:
6 | - name: kyverno
7 | version: 3.0.0
8 | repository: https://kyverno.github.io/kyverno/
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/bonus/examples/storage/minio-operator/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: minio-operator
3 | version: 1.0.0
4 | description: This Chart deploys minio-operator.
5 | dependencies:
6 | - name: operator
7 | version: 5.0.1
8 | repository: https://operator.min.io/
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/bonus/examples/storage/minio-tenant/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: minio-tenant
3 | version: 1.0.0
4 | description: This Chart deploys minio tenant.
5 | dependencies:
6 | - name: tenant
7 | version: 5.0.1
8 | repository: https://operator.min.io/
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/in-cluster/dns/external-dns/values.yaml:
--------------------------------------------------------------------------------
1 | external-dns:
2 | domainFilters:
3 | - your-domain.com
4 | txtOwnerId: "in-cluster-dev"
5 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/in-cluster/networking/ingress-nginx/values.yaml:
--------------------------------------------------------------------------------
1 | ingress-nginx:
2 | controller:
3 | resources:
4 | requests:
5 | memory: 1000Mi
6 | cpu: 1000m
7 | limits:
8 | memory: 1500Mi
9 | cpu: 1500m
10 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/in-cluster/security/cert-manager/values.yaml:
--------------------------------------------------------------------------------
1 | cert-manager:
2 | resources:
3 | requests:
4 | cpu: 1000m
5 | memory: 1000Mi
6 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/vcluster-team-a/networkpolicy-deny-ingress.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: networking.k8s.io/v1
3 | kind: NetworkPolicy
4 | metadata:
5 | name: default-deny-ingress
6 | namespace: vcluster-team-a
7 | spec:
8 | podSelector: {}
9 | policyTypes:
10 | - Ingress
11 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/vcluster-team-a/optimization/vcluster/values.yaml:
--------------------------------------------------------------------------------
1 | vcluster:
2 | sync:
3 | ingresses:
4 | enabled: true
5 | syncer:
6 | extraArgs:
7 | - --tls-san=vcluster-a.example.com
8 | ingress:
9 | enabled: true
10 | host: vcluster-a.example.com
11 | ingressClassName: "nginx"
12 | annotations:
13 | nginx.ingress.kubernetes.io/backend-protocol: HTTPS
14 | nginx.ingress.kubernetes.io/ssl-passthrough: "true"
15 | nginx.ingress.kubernetes.io/ssl-redirect: "true"
16 | isolation:
17 | podSecurityStandard: baseline
18 | # If enabled will add node/proxy permission to the cluster role
19 | # in isolation mode
20 | resourceQuota:
21 | enabled: true
22 | quota:
23 | requests.cpu: 10
24 | requests.memory: 20Gi
25 | requests.storage: "100Gi"
26 | requests.ephemeral-storage: 50Gi
27 | limits.cpu: 20
28 | limits.memory: 40Gi
29 | limits.ephemeral-storage: 150Gi
30 | services.nodeports: 1
31 | services.loadbalancers: 1
32 | count/endpoints: 40
33 | count/pods: 30
34 | count/services: 30
35 | count/secrets: 100
36 | count/configmaps: 100
37 | count/persistentvolumeclaims: 20
38 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/vcluster-team-a/rbac.yaml:
--------------------------------------------------------------------------------
1 | kind: RoleBinding
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | metadata:
4 | name: rolebinding-admin
5 | namespace: vcluster-team-a
6 | roleRef:
7 | kind: ClusterRole
8 | name: admin
9 | apiGroup: rbac.authorization.k8s.io
10 | subjects:
11 | - kind: Group
12 | name: sso:DEV_Team_K8s_Dev@example.com
13 | apiGroup: rbac.authorization.k8s.io
14 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/dns/external-dns/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: external-dns
3 | version: 1.0.0
4 | description: This Chart deploys external-dns.
5 | dependencies:
6 | - name: external-dns
7 | version: 6.26.1
8 | repository: https://charts.bitnami.com/bitnami
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/dns/external-dns/values.yaml:
--------------------------------------------------------------------------------
1 | external-dns:
2 | txtOwnerId: "overlay_me"
3 |
4 | rbac:
5 | pspEnabled: true
6 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/networking/ingress-nginx/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: ingress-nginx
3 | version: 1.0.0
4 | description: This Chart deploys ingress-nginx.
5 | dependencies:
6 | - name: ingress-nginx
7 | version: 4.8.0
8 | repository: https://kubernetes.github.io/ingress-nginx
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/networking/ingress-nginx/values.yaml:
--------------------------------------------------------------------------------
1 | ingress-nginx:
2 | controller:
3 | allowSnippetAnnotations: true
4 | resources:
5 | requests:
6 | memory: 250Mi
7 | cpu: 250m
8 | limits:
9 | memory: 500Mi
10 | cpu: 500m
11 | metrics:
12 | enabled: true
13 | serviceMonitor:
14 | enabled: false
15 | setAsDefaultIngress: true
16 | extraArgs:
17 | enable-ssl-passthrough: true
18 | defaultBackend:
19 | enabled: true
20 | resources:
21 | requests:
22 | memory: 50Mi
23 | cpu: 100m
24 | limits:
25 | memory: 100Mi
26 | cpu: 150m
27 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/optimization/vcluster/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: vcluster
3 | version: 1.0.0
4 | description: This Chart deploys vcluster k3s by default.
5 | #vcluster use default k3s
6 | dependencies:
7 | - name: vcluster
8 | version: 0.18.1
9 | repository: https://charts.loft.sh
10 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/optimization/vcluster/values.yaml:
--------------------------------------------------------------------------------
1 | vcluster:
2 | # Resource syncers that should be enabled/disabled.
3 | # Enabling syncers will impact RBAC Role and ClusterRole permissions.
4 | # To disable a syncer set "enabled: false".
5 | # See docs for details - https://www.vcluster.com/docs/architecture/synced-resources
6 | # Configure SecurityContext of the containers in the VCluster pod
7 | securityContext:
8 | runAsUser: 12345
9 | runAsGroup: 12345
10 | runAsNonRoot: true
11 |
12 | # Configure fsGroup
13 | # The field is specified, all processes of the container are also part of the supplementary group ID 12345
14 | fsGroup: 12345
15 |
16 | # If enabled will deploy vcluster in an isolated mode with pod security
17 | # standards, limit ranges and resource quotas
18 | isolation:
19 | enabled: true
20 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/security/cert-manager/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: cert-manager
3 | version: 1.0.0
4 | description: This Chart deploys cert-manager.
5 | dependencies:
6 | - name: cert-manager
7 | version: v1.13.0
8 | repository: https://charts.jetstack.io
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/security/cert-manager/values.yaml:
--------------------------------------------------------------------------------
1 | cert-manager:
2 | installCRDs: true
3 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/security/external-secrets/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: external-secrets
3 | version: 1.0.0
4 | description: This Chart deploys external-secrets secrets.
5 | dependencies:
6 | - name: external-secrets
7 | version: "0.9.5"
8 | repository: https://charts.external-secrets.io
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/security/external-secrets/values.yaml:
--------------------------------------------------------------------------------
1 | external-secrets:
2 | serviceMonitor:
3 | # -- Specifies whether to create a ServiceMonitor resource for collecting Prometheus metrics
4 | enabled: true
5 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/security/kyverno/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: kyverno
3 | version: 1.0.0
4 | description: This Chart deploys kyverno.
5 | dependencies:
6 | - name: kyverno
7 | version: 3.0.5
8 | repository: https://kyverno.github.io/kyverno/
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/security/kyverno/values.yaml:
--------------------------------------------------------------------------------
1 | kyverno:
2 | # Admission controller configuration
3 | admissionController:
4 | replicas: 0
5 | # Reports controller configuration
6 | reportsController:
7 | replicas: 0
8 | # Cleanup controller configuration
9 | cleanupController:
10 | replicas: 0
11 | # Background controller configuration
12 | backgroundController:
13 | replicas: 0
14 |
15 | # CRDs configuration
16 | crds:
17 | # -- Whether to have Helm install the Kyverno CRDs, if the CRDs are not installed by Helm, they must be added before policies can be created
18 | install: true
19 | annotations:
20 | argocd.argoproj.io/sync-options: Replace=true
21 | strategy.spinnaker.io/replace: "true"
22 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/system/argocd/.gitignore:
--------------------------------------------------------------------------------
1 | charts/
2 | Chart.lock
3 | test/
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/system/argocd/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: argocd
3 | version: 1.0.0
4 | description: This Chart deploys argoc cd by default.
5 | dependencies:
6 | - name: argo-cd
7 | version: 5.51.6
8 | repository: https://argoproj.github.io/argo-helm
9 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/system/argocd/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.ingress -}}
2 | apiVersion: networking.k8s.io/v1
3 | kind: Ingress
4 | metadata:
5 | name: argocd-server-ingress
6 | annotations:
7 | kubernetes.io/ingress.class: {{ .Values.ingress.className }}
8 | ingress.kubernetes.io/force-ssl-redirect: "true"
9 | nginx.ingress.kubernetes.io/ssl-passthrough: {{ .Values.ingress.sslPassthrough | default "false" | quote }}
10 | cert-manager.io/cluster-issuer: {{ .Values.ingress.issuer }}
11 | cert-manager.io/renew-before: 360h #15 days
12 | cert-manager.io/common-name: {{ .Values.ingress.host }}
13 | kubernetes.io/tls-acme: "true"
14 | nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
15 | spec:
16 | rules:
17 | - host: {{ .Values.ingress.host }}
18 | http:
19 | paths:
20 | - path: /
21 | pathType: Prefix
22 | backend:
23 | service:
24 | name: argocd
25 | port:
26 | name: https
27 | tls:
28 | - hosts:
29 | - {{ .Values.ingress.host }}
30 | secretName: {{ .Values.ingress.secretName | default "argocd-secret" }} # do not change, this is provided by Argo CD
31 | {{- end -}}
--------------------------------------------------------------------------------
/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/system/argocd/values.yaml:
--------------------------------------------------------------------------------
1 | ingress:
2 | host: "argocd.yourdomain.com"
3 | issuer: "letsencrypt"
4 | className: "nginx"
5 |
6 | argo-cd:
7 | redis:
8 | create: true
9 |
10 | controller:
11 | replicas: 1
12 |
13 | server:
14 | replicas: 2
15 |
16 | repoServer:
17 | replicas: 2
18 |
19 | applicationSet:
20 | replicaCount: 2
21 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/environment-branches/simple-webapp/prod-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | spec:
6 | replicas: 3
7 | selector:
8 | matchLabels:
9 | app: simple-webapp
10 | template:
11 | metadata:
12 | labels:
13 | app: simple-webapp
14 | spec:
15 | containers:
16 | - image: "ghcr.io/la-cc/simple-webapp:1.0.1"
17 | name: simple-webapp
18 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/environment-branches/simple-webapp/qa-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: simple-webapp
10 | template:
11 | metadata:
12 | labels:
13 | app: simple-webapp
14 | spec:
15 | containers:
16 | - image: "ghcr.io/la-cc/simple-webapp:1.0.1"
17 | name: simple-webapp
18 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/environment-branches/simple-webapp/stage-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | spec:
6 | replicas: 2
7 | selector:
8 | matchLabels:
9 | app: simple-webapp
10 | template:
11 | metadata:
12 | labels:
13 | app: simple-webapp
14 | spec:
15 | containers:
16 | - image: "ghcr.io/la-cc/simple-webapp:1.0.1"
17 | name: simple-webapp
18 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/folders-for-environments/base/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | spec:
6 | replicas: 2
7 | selector:
8 | matchLabels:
9 | app: simple-webapp
10 | template:
11 | metadata:
12 | labels:
13 | app: simple-webapp
14 | spec:
15 | containers:
16 | - image: "ghcr.io/la-cc/simple-webapp:1.1.0-stable"
17 | name: simple-webapp
18 | env:
19 | - name: UI_X_COLOR
20 | value: darkblue
21 | - name: SUBSCRIPTION_TIER #business-related values
22 | value: silver
23 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/folders-for-environments/base/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - deployment.yaml
3 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/folders-for-environments/overlays/prod/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | resources:
4 | - ../../base
5 | patches:
6 | - path: patch.yaml
7 | namePrefix: prod-
8 | commonLabels:
9 | variant: prod
10 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/folders-for-environments/overlays/prod/patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | spec:
6 | replicas: 1
7 | template:
8 | spec:
9 | containers:
10 | - image: "ghcr.io/la-cc/simple-webapp:1.1.0-prod"
11 | name: simple-webapp
12 | env:
13 | - name: UI_X_COLOR
14 | value: premium
15 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/folders-for-environments/overlays/qa/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | resources:
4 | - ../../base
5 | patches:
6 | - path: patch.yaml
7 | namePrefix: qa-
8 | commonLabels:
9 | variant: qa
10 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/folders-for-environments/overlays/qa/patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | spec:
6 | replicas: 1
7 | template:
8 | spec:
9 | containers:
10 | - image: "ghcr.io/la-cc/simple-webapp:1.1.5-new-ui"
11 | name: simple-webapp
12 | env:
13 | - name: UI_X_COLOR
14 | value: aqua
15 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/folders-for-environments/overlays/stage/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | resources:
4 | - ../../base
5 | patches:
6 | - path: patch.yaml
7 | namePrefix: stage-
8 | commonLabels:
9 | variant: stage
10 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-effective-git-repository-strategies/folders-for-environments/overlays/stage/patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | spec:
6 | replicas: 2
7 | template:
8 | spec:
9 | containers:
10 | - image: "ghcr.io/la-cc/simple-webapp:1.1.4-feature-login"
11 | name: simple-webapp
12 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-multitenancy-with-vcluster-and-argo-cd/bonus/connect_vcluster_fleet.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Check if context is provided
4 | if [ -z "$1" ]; then
5 | echo "You forgot to set the context!"
6 | echo "you can set the context with connectVcluster CONTEXT!"
7 | echo "e.g. connectVcluster sunrise-development"
8 | exit 1
9 | fi
10 |
11 | # Attempt to switch to the context passed as argument
12 | kubectl config use-context "$1" &>/dev/null
13 |
14 | # Check the exit status of the previous command
15 | if [ $? -ne 0 ]; then
16 | echo "Invalid context: $1"
17 | echo "Maybe should connect first vs the guest-cluster or checking if you making a typing error?"
18 | exit 1
19 | fi
20 |
21 | namespace=$(mktemp)
22 | hostname=$(mktemp)
23 | vcluster=$(mktemp)
24 |
25 | kubectl get namespaces -o=jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep vcluster >"$namespace"
26 |
27 | kubectl get namespaces -o=jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep vcluster | kubectl get ing -A -o json | jq -r '.items[] | select(.spec.rules[].host | contains("vcluster")) | .spec.rules[0].host' >"$hostname"
28 |
29 | paste -d ' ' "$namespace" "$hostname" >"$vcluster"
30 |
31 | mapfile -t list <$vcluster
32 |
33 | # Iterate over the list
34 | for item in "${list[@]}"; do
35 | # splitting the element into arguments
36 | args=($item)
37 |
38 | # output of the arguments in the desired form
39 | vcluster connect vcluster -n ${args[0]} --server=https://${args[1]} --kube-config-context-name ${args[0]} --context $1
40 | done
41 |
42 | rm "$namespace" "$hostname" "$vcluster"
43 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-multitenancy-with-vcluster-and-argo-cd/devteam-a/vcluster-application.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: vcluster-team-a
5 | namespace: argocd
6 | spec:
7 | destination:
8 | name: ""
9 | namespace: vcluster-team-a
10 | server: "https://kubernetes.default.svc"
11 | sources:
12 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
13 | targetRevision: main
14 | ref: valuesRepo
15 | # - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
16 | # path: "./chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/vcluster-team-a/"
17 | # targetRevision: main
18 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
19 | targetRevision: main
20 | path: "./chapter05/chapter-5-building-a-service-catalog-for-kubernetes/optimization/vcluster"
21 | helm:
22 | releaseName: "vcluster-team-a"
23 | valueFiles:
24 | - "values.yaml"
25 | - "$valuesRepo/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/vcluster-team-a/optimization/vcluster/values.yaml"
26 | project: default
27 | syncPolicy:
28 | automated:
29 | prune: false
30 | selfHeal: true
31 | syncOptions:
32 | - CreateNamespace=true
33 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-native-multitenancy-with-argo-cd/devteam-a/application-initializer.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: application-initializer-devteam-a
5 | namespace: argocd
6 | spec:
7 | project: devteam-a
8 | source:
9 | repoURL: https://dev.azure.com/ORGA-X/devteam-a/_git/application
10 | targetRevision: main
11 | path: ./applicationset
12 | destination:
13 | server: https://kubernetes.default.svc
14 | namespace: devteam-a
15 | # Sync policy
16 | syncPolicy:
17 | automated:
18 | selfHeal: true
19 | syncOptions:
20 | - Validate=false
21 | - PrunePropagationPolicy=foreground
22 | - PruneLast=true
23 | - FailOnSharedResource=true
24 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-native-multitenancy-with-argo-cd/devteam-a/argocd-project-devteam-a.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: AppProject
3 | metadata:
4 | name: devteam-a
5 | namespace: argocd
6 | # Finalizer that ensures that project is not deleted until it is not referenced by any application
7 | finalizers:
8 | - resources-finalizer.argocd.argoproj.io
9 | spec:
10 | # Project description
11 | description: Enable DevTeam-A Project to install new applications
12 |
13 | # Allow manifests to deploy from any Git repos
14 | sourceRepos:
15 | # Any other repo are restricted
16 | - "*"
17 |
18 | destinations:
19 | - namespace: "devteam-a"
20 | server: https://kubernetes.default.svc
21 |
22 | # Restrict Namespace cluster-scoped resources from being created
23 | clusterResourceBlacklist:
24 | - group: ""
25 | kind: "Namespace"
26 |
27 | # Restrict namespaced-scoped resources from being created
28 | namespaceResourceBlacklist:
29 | - group: "argoproj.io"
30 | kind: "AppProject"
31 | - group: "argoproj.io"
32 | kind: "Application"
33 | - group: ""
34 | kind: "ResourceQuota"
35 | - group: "networking.k8s.io"
36 | kind: "NetworkPolicy"
37 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-native-multitenancy-with-argo-cd/devteam-a/gitrepository-sealed.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: bitnami.com/v1alpha1
2 | kind: SealedSecret
3 | metadata:
4 | annotations:
5 | sealedsecrets.bitnami.com/namespace-wide: "true"
6 | name: application-devteam-a
7 | namespace: argocd
8 | spec:
9 | encryptedData:
10 | enableLfs: AQCMuK6ZdqBpszav4vA3K5F5kvoD2hCGr19ZoZYmhEsY6mRBPNVnhcFH5GD9pF6RqKQpvKSt18zh3difnbJ3czyQ5xeI1FmYpiuyeBmOEGfNe...
11 | forceHttpBasicAuth: AQBf5U79aldO4mAMe6ZEHFbFKGH6yXn54Ki5MZcJBJTVbsF0/mVgqge+6HVoowvKUoivqp5d01HjiBH7KFDDwF+R6Txo8sYW0XI...
12 | insecure: AQB4g1/T1IyOoRkl9igO5K3cG1SFOo1fub1EN2PYh9o0evG4Ynd6c9Ezlg4lz+sVORk1x7tCyHhQ2My6xbqhLx6e6Fze6A/6Hz/twpsa5AtIP...
13 | password: AQAjZSIynjRukkfTsfk7ihp/UD9s4Wpo0nPFEb1tUWUAYdic94ZHjwiNjS2Z4GyEB2QiKCboA6PqJVKpetsiiOXx+H/kQCnl1duyvkAOqoRH...
14 | url: AQAXsTLAn+8RBImuekH9KnigsRLo/dmxfiFkG+6adxLAxMqgYZwmEkZLDuMhvBvGi0F0LugREYoSsPRs3Cy+qQ+SF+3ie/Ew3AVfw7MDkYwmJgBVmF...
15 | username: AQA1RBHngkFyWm/Xec7OeQeT8siwbgLOF6flu2Pfgg9twE3Rdm6jzmkbpRypuqKjNsgJMUenCxG6mQMqmJaCo8zyM7kGnOt76j8YaoBhnqZm...
16 | template:
17 | metadata:
18 | annotations:
19 | sealedsecrets.bitnami.com/namespace-wide: "true"
20 | labels:
21 | argocd.argoproj.io/secret-type: repository
22 | name: application-devteam-a
23 | namespace: argocd
24 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-native-multitenancy-with-argo-cd/devteam-a/namespace.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: devteam-a
5 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-native-multitenancy-with-argo-cd/devteam-a/networkpolicy-deny-ingress.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: networking.k8s.io/v1
3 | kind: NetworkPolicy
4 | metadata:
5 | name: default-deny-ingress
6 | namespace: devteam-a
7 | spec:
8 | podSelector: {}
9 | policyTypes:
10 | - Ingress
11 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-native-multitenancy-with-argo-cd/devteam-a/rbac.yaml:
--------------------------------------------------------------------------------
1 | kind: RoleBinding
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | metadata:
4 | name: rolebinding-admin
5 | namespace: devteam-a
6 | roleRef:
7 | kind: ClusterRole
8 | name: admin
9 | apiGroup: rbac.authorization.k8s.io
10 | subjects:
11 | - kind: Group
12 | name: sso:DevTeam-A@your_domain
13 | apiGroup: rbac.authorization.k8s.io
14 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-native-multitenancy-with-argo-cd/devteam-a/resource-quotas.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ResourceQuota
3 | metadata:
4 | name: resource-quotas-ns-devteam-a
5 | namespace: devteam-a
6 | spec:
7 | hard:
8 | limits.cpu: "20"
9 | limits.ephemeral-storage: 100Gi
10 | limits.memory: 40Gi
11 | requests.cpu: "10"
12 | requests.ephemeral-storage: 50Gi
13 | requests.memory: 20Gi
14 | requests.storage: 100Gi
15 | services.loadbalancers: "1"
16 | pods: "20"
17 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-scale-with-applicationset-generators/nginx-ingress-applicationset-example/nginx-ingress-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: ingress-nginx
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev #cluster label
12 | values:
13 | branch: main #point to main branch
14 | - clusters:
15 | selector:
16 | matchLabels:
17 | env: prod #cluster label
18 | values:
19 | branch: main #point to main branch
20 | template:
21 | metadata:
22 | name: "{{name}}-ingress-nginx" #name of cluster + name of application
23 | annotations:
24 | argocd.argoproj.io/manifest-generate-paths: ".;.."
25 | spec:
26 | project: default
27 | sources:
28 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #custom values repo
29 | targetRevision: main #point to main branch of custom values repo
30 | ref: valuesRepo #use valuesRepo variable
31 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git #umbrella repo
32 | targetRevision: "{{values.branch}}" #point to the branch depending on the cluster branch pointer
33 | path: "./chapter05/chapter-5-building-a-service-catalog-for-kubernetes/networking/ingress-nginx" #path to the application in the umbrella repo
34 | helm:
35 | releaseName: "ingress-nginx" # Release name override (defaults to application name)
36 | valueFiles:
37 | - "values.yaml" #use values.yaml from umbrella repo
38 | - "$valuesRepo/chapter05/chapter-5-building-a-service-catalog-for-kubernetes/cluster/{{name}}/networking/ingress-nginx/values.yaml" #override umbrella values from custom values repo
39 | destination:
40 | name: "{{name}}"
41 | namespace: "ingress-nginx"
42 | syncPolicy:
43 | automated:
44 | prune: false
45 | selfHeal: true
46 | syncOptions:
47 | - CreateNamespace=true
48 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-app/application.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: app-of-app
5 | namespace: argocd
6 | finalizers:
7 | - resources-finalizer.argocd.argoproj.io
8 | spec:
9 | project: default
10 | source:
11 | repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
12 | targetRevision: main
13 | path: ./chapter05/chapter-5-the-app-of-apps-approach/chapter-5-the-app-of-apps-approach/app-of-app/simple-webapp
14 | directory:
15 | recurse: false
16 | destination:
17 | server: https://kubernetes.default.svc
18 | namespace: app-of-app
19 | syncPolicy:
20 | syncOptions:
21 | - CreateNamespace=true
22 | automated:
23 | prune: true
24 | selfHeal: true
25 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-app/simple-webapp/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: simple-webapp
10 | template:
11 | metadata:
12 | labels:
13 | app: simple-webapp
14 | spec:
15 | containers:
16 | - image: "ghcr.io/la-cc/simple-webapp:1.0.1"
17 | name: simple-webapp
18 | resources:
19 | requests:
20 | memory: "64Mi"
21 | cpu: "250m"
22 | limits:
23 | memory: "512Mi"
24 | cpu: "500m"
25 | ports:
26 | - containerPort: 8080
27 | securityContext:
28 | runAsUser: 1000
29 | dnsPolicy: ClusterFirst
30 | restartPolicy: Always
31 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-app/simple-webapp/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: simple-webapp-clusterip
5 | spec:
6 | selector:
7 | app: simple-webapp
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-apps/application.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: app-of-apps
5 | namespace: argocd
6 | finalizers:
7 | - resources-finalizer.argocd.argoproj.io
8 | spec:
9 | project: default
10 | source:
11 | repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
12 | targetRevision: main
13 | path: ./chapter05/chapter-5-the-app-of-apps-approach/app-of-apps/simple-webapps
14 | directory:
15 | recurse: true
16 | destination:
17 | server: https://kubernetes.default.svc
18 | namespace: app-of-apps
19 | syncPolicy:
20 | syncOptions:
21 | - CreateNamespace=true
22 | automated:
23 | prune: true
24 | selfHeal: true
25 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-apps/simple-webapps/simple-webapp-1/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp-1
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: simple-webapp-1
10 | template:
11 | metadata:
12 | labels:
13 | app: simple-webapp-1
14 | spec:
15 | containers:
16 | - image: "ghcr.io/la-cc/simple-webapp:1.0.1"
17 | name: simple-webapp-1
18 | resources:
19 | requests:
20 | memory: "64Mi"
21 | cpu: "250m"
22 | limits:
23 | memory: "512Mi"
24 | cpu: "500m"
25 | ports:
26 | - containerPort: 8080
27 | securityContext:
28 | runAsUser: 1000
29 | dnsPolicy: ClusterFirst
30 | restartPolicy: Always
31 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-apps/simple-webapps/simple-webapp-1/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: simple-webapp-1-clusterip
5 | spec:
6 | selector:
7 | app: simple-webapp-1
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-apps/simple-webapps/simple-webapp-2/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp-2
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: simple-webapp-2
10 | template:
11 | metadata:
12 | labels:
13 | app: simple-webapp-2
14 | spec:
15 | containers:
16 | - image: "ghcr.io/la-cc/simple-webapp:1.0.1"
17 | name: simple-webapp-2
18 | resources:
19 | requests:
20 | memory: "64Mi"
21 | cpu: "250m"
22 | limits:
23 | memory: "512Mi"
24 | cpu: "500m"
25 | ports:
26 | - containerPort: 8080
27 | securityContext:
28 | runAsUser: 1000
29 | dnsPolicy: ClusterFirst
30 | restartPolicy: Always
31 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-apps/simple-webapps/simple-webapp-2/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: simple-webapp-2-clusterip
5 | spec:
6 | selector:
7 | app: simple-webapp-2
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-apps/simple-webapps/simple-webapp-3/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp-3
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: simple-webapp-3
10 | template:
11 | metadata:
12 | labels:
13 | app: simple-webapp-3
14 | spec:
15 | containers:
16 | - image: "ghcr.io/la-cc/simple-webapp:1.0.1"
17 | name: simple-webapp-3
18 | resources:
19 | requests:
20 | memory: "64Mi"
21 | cpu: "250m"
22 | limits:
23 | memory: "512Mi"
24 | cpu: "500m"
25 | ports:
26 | - containerPort: 8080
27 | securityContext:
28 | runAsUser: 1000
29 | dnsPolicy: ClusterFirst
30 | restartPolicy: Always
31 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/app-of-apps/simple-webapps/simple-webapp-3/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: simple-webapp-3-clusterip
5 | spec:
6 | selector:
7 | app: simple-webapp-3
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/applicationsets/simple-webapp-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: simple-webapp
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: prod
12 | values:
13 | branch: main
14 | template:
15 | metadata:
16 | name: "{{name}}-simple-webapp"
17 | annotations:
18 | argocd.argoproj.io/manifest-generate-paths: ".;.."
19 | spec:
20 | project: default
21 | sources:
22 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
23 | targetRevision: "{{values.branch}}"
24 | path: ./chapter05/chapter-5-the-app-of-apps-approach/app-of-app/simple-webapp
25 | destination:
26 | name: "{{name}}"
27 | namespace: "argocd"
28 | syncPolicy:
29 | automated:
30 | prune: false
31 | selfHeal: true
32 | syncOptions:
33 | - CreateNamespace=true
34 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/applicationsets/simple-webapp/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: simple-webapp
5 | namespace: simple-webapp
6 | spec:
7 | replicas: 1
8 | selector:
9 | matchLabels:
10 | app: simple-webapp
11 | template:
12 | metadata:
13 | labels:
14 | app: simple-webapp
15 | spec:
16 | containers:
17 | - image: "ghcr.io/la-cc/simple-webapp:1.0.1"
18 | name: simple-webapp
19 | resources:
20 | requests:
21 | memory: "64Mi"
22 | cpu: "250m"
23 | limits:
24 | memory: "512Mi"
25 | cpu: "500m"
26 | ports:
27 | - containerPort: 8080
28 | securityContext:
29 | runAsUser: 1000
30 | dnsPolicy: ClusterFirst
31 | restartPolicy: Always
32 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/applicationsets/simple-webapp/namespace.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: simple-webapp
5 |
--------------------------------------------------------------------------------
/chapter05/chapter-5-the-app-of-apps-approach/applicationsets/simple-webapp/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: simple-webapp-clusterip
5 | namespace: simple-webapp
6 | spec:
7 | selector:
8 | app: simple-webapp
9 | ports:
10 | - port: 80
11 | targetPort: 8080
12 |
--------------------------------------------------------------------------------
/chapter06/chapter-6-centralized-kubernetes-cluster-creation/README.md:
--------------------------------------------------------------------------------
1 | # 0. Preparation steps
2 |
3 | ## Install the following tools:
4 |
5 | - kubectl
6 | - clusterctl
7 | - az
8 | - helm
9 |
10 | # 1. Initialize the management cluster
11 |
12 | export AZURE_SUBSCRIPTION_ID=""
13 |
14 | ## Create an Azure Service Principal and paste the output here
15 |
16 | export AZURE_TENANT_ID=""
17 | export AZURE_CLIENT_ID=""
18 | export AZURE_CLIENT_SECRET=""
19 |
20 | ## Base64 encode the variables
21 |
22 | export AZURE_SUBSCRIPTION_ID_B64="$(echo -n "$AZURE_SUBSCRIPTION_ID" | base64 | tr -d '\n')"
23 | export AZURE_TENANT_ID_B64="$(echo -n "$AZURE_TENANT_ID" | base64 | tr -d '\n')"
24 | export AZURE_CLIENT_ID_B64="$(echo -n "$AZURE_CLIENT_ID" | base64 | tr -d '\n')"
25 | export AZURE_CLIENT_SECRET_B64="$(echo -n "$AZURE_CLIENT_SECRET" | base64 | tr -d '\n')"
26 |
27 | ## Settings needed for AzureClusterIdentity used by the AzureCluster
28 |
29 | export AZURE_CLUSTER_IDENTITY_SECRET_NAME="cluster-identity-secret"
30 | export CLUSTER_IDENTITY_NAME="cluster-identity"
31 | export AZURE_CLUSTER_IDENTITY_SECRET_NAMESPACE="default"
32 |
33 | ## Create a secret to include the password of the Service Principal identity created in Azure
34 |
35 | ## This secret will be referenced by the AzureClusterIdentity used by the AzureCluster
36 |
37 | kubectl create secret generic "${AZURE_CLUSTER_IDENTITY_SECRET_NAME}" --from-literal=clientSecret="${AZURE_CLIENT_SECRET}" --namespace "${AZURE_CLUSTER_IDENTITY_SECRET_NAMESPACE}"
38 |
39 | ## Finally, initialize the management cluster
40 |
41 | clusterctl init --infrastructure azure
42 |
43 | # 2. Create your first workload cluster
44 |
45 | ## Name of the Azure datacenter location. Change this value to your desired location.
46 |
47 | export AZURE_LOCATION="westeurope"
48 |
49 | ## Select VM types.
50 |
51 | export AZURE_CONTROL_PLANE_MACHINE_TYPE="Standard_D2s_v3"
52 | export AZURE_NODE_MACHINE_TYPE="Standard_D2s_v3"
53 |
54 | ## [Optional] Select resource group. The default value is ${CLUSTER_NAME}.
55 |
56 | export AZURE_RESOURCE_GROUP=""
57 |
58 | ## Generating the cluster configuration
59 |
60 | clusterctl generate cluster capi-quickstart \
61 | --kubernetes-version v1.29.0 \
62 | --control-plane-machine-count=1 \
63 | --worker-machine-count=3 \
64 | > capi-quickstart.yaml
65 |
66 | # 3. Deploy the workload cluster
67 |
68 | _NOTE: You should do it the GitOps way._
69 |
70 | kubectl apply -f capi-quickstart.yaml
71 |
72 | # 4. Deploy Calico CNI
73 |
74 | First, check if the kubeadmcontrolplane is ready:
75 |
76 | clusterctl describe cluster capi-quickstart
77 |
78 | You should get an output similar to this:
79 |
80 | NAME READY SEVERITY REASON SINCE MESSAGE
81 | Cluster/capi-quickstart True 11s
82 | ├─ClusterInfrastructure - AzureCluster/capi-quickstart True 2m34s
83 | ├─ControlPlane - KubeadmControlPlane/capi-quickstart-control-plane True 11s
84 | │ └─Machine/capi-quickstart-control-plane-dct9z True 12s
85 | └─Workers
86 | └─MachineDeployment/capi-quickstart-md-0 False Warning WaitingForAvailableMachines 5m4s Minimum availability requires 3 replicas, current 0 available
87 | └─3 Machines... False Info WaitingForBootstrapData 3s
88 |
89 | After the first control plane node is up and running, we can retrieve the workload cluster Kubeconfig.
90 |
91 | clusterctl get kubeconfig capi-quickstart > capi-quickstart.kubeconfig
92 |
93 | Now Install Calico CNI with:
94 |
95 | helm repo add projectcalico https://docs.tigera.io/calico/charts --kubeconfig=./capi-quickstart.kubeconfig && \
96 | helm install calico projectcalico/tigera-operator --kubeconfig=./capi-quickstart.kubeconfig -f https://raw.githubusercontent.com/kubernetes-sigs/cluster-api-provider-azure/main/templates/addons/calico/values.yaml --namespace tigera-operator --create-namespace
97 |
98 | After a short while, nodes should be running and in Ready state, let’s check the status using kubectl get nodes
99 |
100 | kubectl get nodes --kubeconfig=./capi-quickstart.kubeconfig
101 |
--------------------------------------------------------------------------------
/chapter06/chapter-6-centralized-kubernetes-cluster-creation/capi-quickstart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cluster.x-k8s.io/v1beta1
2 | kind: Cluster
3 | metadata:
4 | name: capi-quickstart
5 | namespace: default
6 | spec:
7 | clusterNetwork:
8 | pods:
9 | cidrBlocks:
10 | - 192.168.0.0/16
11 | controlPlaneRef:
12 | apiVersion: controlplane.cluster.x-k8s.io/v1beta1
13 | kind: KubeadmControlPlane
14 | name: capi-quickstart-control-plane
15 | infrastructureRef:
16 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
17 | kind: AzureCluster
18 | name: capi-quickstart
19 | ---
20 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
21 | kind: AzureCluster
22 | metadata:
23 | name: capi-quickstart
24 | namespace: default
25 | spec:
26 | identityRef:
27 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
28 | kind: AzureClusterIdentity
29 | name: cluster-identity
30 | location: westeurope
31 | networkSpec:
32 | subnets:
33 | - name: control-plane-subnet
34 | role: control-plane
35 | - name: node-subnet
36 | role: node
37 | vnet:
38 | name: capi-quickstart-vnet
39 | resourceGroup: capi-quickstart
40 | subscriptionID:
41 | ---
42 | apiVersion: controlplane.cluster.x-k8s.io/v1beta1
43 | kind: KubeadmControlPlane
44 | metadata:
45 | name: capi-quickstart-control-plane
46 | namespace: default
47 | spec:
48 | kubeadmConfigSpec:
49 | clusterConfiguration:
50 | apiServer:
51 | extraArgs:
52 | cloud-provider: external
53 | timeoutForControlPlane: 20m
54 | controllerManager:
55 | extraArgs:
56 | allocate-node-cidrs: "false"
57 | cloud-provider: external
58 | cluster-name: capi-quickstart
59 | etcd:
60 | local:
61 | dataDir: /var/lib/etcddisk/etcd
62 | extraArgs:
63 | quota-backend-bytes: "8589934592"
64 | diskSetup:
65 | filesystems:
66 | - device: /dev/disk/azure/scsi1/lun0
67 | extraOpts:
68 | - -E
69 | - lazy_itable_init=1,lazy_journal_init=1
70 | filesystem: ext4
71 | label: etcd_disk
72 | - device: ephemeral0.1
73 | filesystem: ext4
74 | label: ephemeral0
75 | replaceFS: ntfs
76 | partitions:
77 | - device: /dev/disk/azure/scsi1/lun0
78 | layout: true
79 | overwrite: false
80 | tableType: gpt
81 | files:
82 | - contentFrom:
83 | secret:
84 | key: control-plane-azure.json
85 | name: capi-quickstart-control-plane-azure-json
86 | owner: root:root
87 | path: /etc/kubernetes/azure.json
88 | permissions: "0644"
89 | initConfiguration:
90 | nodeRegistration:
91 | kubeletExtraArgs:
92 | azure-container-registry-config: /etc/kubernetes/azure.json
93 | cloud-provider: external
94 | name: '{{ ds.meta_data["local_hostname"] }}'
95 | joinConfiguration:
96 | nodeRegistration:
97 | kubeletExtraArgs:
98 | azure-container-registry-config: /etc/kubernetes/azure.json
99 | cloud-provider: external
100 | name: '{{ ds.meta_data["local_hostname"] }}'
101 | mounts:
102 | - - LABEL=etcd_disk
103 | - /var/lib/etcddisk
104 | postKubeadmCommands: []
105 | preKubeadmCommands: []
106 | machineTemplate:
107 | infrastructureRef:
108 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
109 | kind: AzureMachineTemplate
110 | name: capi-quickstart-control-plane
111 | replicas: 1
112 | version: v1.29.0
113 | ---
114 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
115 | kind: AzureMachineTemplate
116 | metadata:
117 | name: capi-quickstart-control-plane
118 | namespace: default
119 | spec:
120 | template:
121 | spec:
122 | dataDisks:
123 | - diskSizeGB: 256
124 | lun: 0
125 | nameSuffix: etcddisk
126 | osDisk:
127 | diskSizeGB: 128
128 | osType: Linux
129 | sshPublicKey: ""
130 | vmSize: Standard_D2s_v3
131 | ---
132 | apiVersion: cluster.x-k8s.io/v1beta1
133 | kind: MachineDeployment
134 | metadata:
135 | name: capi-quickstart-md-0
136 | namespace: default
137 | spec:
138 | clusterName: capi-quickstart
139 | replicas: 3
140 | selector:
141 | matchLabels: null
142 | template:
143 | spec:
144 | bootstrap:
145 | configRef:
146 | apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
147 | kind: KubeadmConfigTemplate
148 | name: capi-quickstart-md-0
149 | clusterName: capi-quickstart
150 | infrastructureRef:
151 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
152 | kind: AzureMachineTemplate
153 | name: capi-quickstart-md-0
154 | version: v1.29.0
155 | ---
156 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
157 | kind: AzureMachineTemplate
158 | metadata:
159 | name: capi-quickstart-md-0
160 | namespace: default
161 | spec:
162 | template:
163 | spec:
164 | osDisk:
165 | diskSizeGB: 128
166 | osType: Linux
167 | sshPublicKey: ""
168 | vmSize: Standard_D2s_v3
169 | ---
170 | apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
171 | kind: KubeadmConfigTemplate
172 | metadata:
173 | name: capi-quickstart-md-0
174 | namespace: default
175 | spec:
176 | template:
177 | spec:
178 | files:
179 | - contentFrom:
180 | secret:
181 | key: worker-node-azure.json
182 | name: capi-quickstart-md-0-azure-json
183 | owner: root:root
184 | path: /etc/kubernetes/azure.json
185 | permissions: "0644"
186 | joinConfiguration:
187 | nodeRegistration:
188 | kubeletExtraArgs:
189 | azure-container-registry-config: /etc/kubernetes/azure.json
190 | cloud-provider: external
191 | name: '{{ ds.meta_data["local_hostname"] }}'
192 | preKubeadmCommands: []
193 | ---
194 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
195 | kind: AzureClusterIdentity
196 | metadata:
197 | labels:
198 | clusterctl.cluster.x-k8s.io/move-hierarchy: "true"
199 | name: cluster-identity
200 | namespace: default
201 | spec:
202 | allowedNamespaces: {}
203 | clientID:
204 | clientSecret:
205 | name: cluster-identity-secret
206 | namespace: default
207 | tenantID:
208 | type: ServicePrincipal
209 |
--------------------------------------------------------------------------------
/chapter08/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | trigger:
2 | - main
3 |
4 | resources:
5 | - repo: self
6 |
7 | variables:
8 |
9 | # Container registry service connection established during pipeline creation
10 | dockerRegistryServiceConnection: 'dd42c99b-7b18-42ae-9fe8-fa42ecd0bc04'
11 | imageRepository: 'myweatherappv1'
12 | containerRegistry: 'aksgitops3003204acr.azurecr.io'
13 | dockerfilePath: '**/src/dockerfile'
14 | tag: '$(Build.BuildId)'
15 | imagePullSecret: 'aksgitops3003204acra253-auth'
16 |
17 | # Agent VM image name
18 | vmImageName: 'ubuntu-latest'
19 |
20 |
21 | stages:
22 | - stage: Build
23 | displayName: Build stage
24 | jobs:
25 | - job: Build
26 | displayName: Build
27 | pool:
28 | vmImage: $(vmImageName)
29 | steps:
30 | - task: Docker@2
31 | displayName: Build and push an image to container registry
32 | inputs:
33 | command: buildAndPush
34 | repository: $(imageRepository)
35 | dockerfile: $(dockerfilePath)
36 | containerRegistry: $(dockerRegistryServiceConnection)
37 | tags: |
38 | $(tag)
39 |
40 | - upload: manifests
41 | artifact: manifests
42 |
43 | - stage: Deploy
44 | displayName: Deploy stage
45 | dependsOn: Build
46 |
47 | jobs:
48 | - deployment: Deploy
49 | displayName: Deploy
50 | pool:
51 | vmImage: $(vmImageName)
52 | environment: 'Development'
53 | strategy:
54 | runOnce:
55 | deploy:
56 | steps:
57 | - task: KubernetesManifest@0
58 | displayName: Create imagePullSecret
59 | inputs:
60 | action: createSecret
61 | secretName: $(imagePullSecret)
62 | dockerRegistryEndpoint: $(dockerRegistryServiceConnection)
63 |
64 | - task: KubernetesManifest@0
65 | displayName: Deploy to Kubernetes cluster
66 | inputs:
67 | action: deploy
68 | manifests: |
69 | $(Pipeline.Workspace)/manifests/deployment.yml
70 | $(Pipeline.Workspace)/manifests/service.yml
71 | imagePullSecrets: |
72 | $(imagePullSecret)
73 | containers: |
74 | $(containerRegistry)/$(imageRepository):$(tag)
75 |
76 |
--------------------------------------------------------------------------------
/chapter08/deployment/base/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: my-city-weather-app
5 | # namespace: gitopsk8sdeployments
6 | labels:
7 | app: my-city-weather-app
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: my-city-weather-app
13 | template:
14 | metadata:
15 | labels:
16 | app: my-city-weather-app
17 | spec:
18 | containers:
19 | - name: my-city-weather-app
20 | image: 637423230075.dkr.ecr.eu-central-1.amazonaws.com/weather-app:latest
21 | ports:
22 | - containerPort: 8080
--------------------------------------------------------------------------------
/chapter08/deployment/base/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: my-city-weather-app-service
5 | # namespace: gitopsk8sdeployments
6 | spec:
7 | type: LoadBalancer
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 | protocol: TCP
12 | selector:
13 | app: my-city-weather-app
14 |
--------------------------------------------------------------------------------
/chapter08/deployment/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | resources:
4 | - base
--------------------------------------------------------------------------------
/chapter08/iac/aws/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~>5"
6 | }
7 | }
8 | }
9 |
10 | provider "aws" {
11 | region = "eu-central-1"
12 |
13 | }
14 |
15 |
16 | # ECR creation
17 | resource "aws_ecr_repository" "ecr" {
18 | name = "weather-app"
19 | image_tag_mutability = "MUTABLE"
20 | }
21 |
22 | # VPC and subnets
23 |
24 | module "vpc" {
25 | source = "terraform-aws-modules/vpc/aws"
26 | version = "5.7.0"
27 |
28 | name = "eks-cluster-vpc"
29 | cidr = "10.0.0.0/16"
30 |
31 | # At least two AZs in the region are required for EKS.
32 | azs = ["eu-central-1a", "eu-central-1b"]
33 | private_subnets = ["10.0.0.0/19", "10.0.32.0/19"]
34 | public_subnets = ["10.0.64.0/19", "10.0.96.0/19"]
35 |
36 | enable_nat_gateway = true
37 | single_nat_gateway = true
38 |
39 | enable_dns_hostnames = true
40 |
41 |
42 | public_subnet_tags = {
43 | "kubernetes.io/cluster/eksgitopscluster" = "shared"
44 | "kubernetes.io/role/elb" = 1
45 | }
46 |
47 | private_subnet_tags = {
48 | "kubernetes.io/cluster/eksgitopscluster" = "shared"
49 | "kubernetes.io/role/internal-elb" = 1
50 | }
51 | }
52 |
53 | module "eks" {
54 | source = "terraform-aws-modules/eks/aws"
55 | version = "20.8.4"
56 |
57 | cluster_name = "eksgitopscluster"
58 | cluster_version = "1.29"
59 |
60 | cluster_endpoint_public_access = true
61 |
62 | vpc_id = module.vpc.vpc_id
63 | subnet_ids = module.vpc.private_subnets
64 |
65 | eks_managed_node_group_defaults = {
66 | instance_types = ["t3.small"]
67 | }
68 |
69 | eks_managed_node_groups = {
70 | one = {
71 | name = "node-group-1"
72 |
73 | instance_types = ["t3.small"]
74 |
75 | min_size = 1
76 | max_size = 2
77 | desired_size = 1
78 | }
79 | }
80 |
81 | create_node_security_group = true
82 | }
83 |
84 |
85 | # IAM role to allow EKS nodes to pull images from ECR
86 | resource "aws_iam_policy" "ecr_pull" {
87 | name = "ecr-pull-policy"
88 | path = "/"
89 | description = "IAM policy for EKS to pull images from ECR"
90 | policy = jsonencode({
91 | Version = "2012-10-17"
92 | Statement = [
93 | {
94 | Action = "ecr:GetAuthorizationToken",
95 | Effect = "Allow",
96 | Resource = "*"
97 | },
98 | {
99 | Action = [
100 | "ecr:BatchCheckLayerAvailability",
101 | "ecr:GetDownloadUrlForLayer",
102 | "ecr:GetRepositoryPolicy",
103 | "ecr:DescribeRepositories",
104 | "ecr:ListImages",
105 | "ecr:DescribeImages",
106 | "ecr:BatchGetImage",
107 | ],
108 | Effect = "Allow",
109 | Resource = aws_ecr_repository.ecr.arn
110 | },
111 | ]
112 | })
113 | }
114 |
115 | resource "aws_iam_role_policy_attachment" "ecr_pull_attach" {
116 | role = module.eks.cluster_iam_role_name
117 | policy_arn = aws_iam_policy.ecr_pull.arn
118 | }
--------------------------------------------------------------------------------
/chapter08/iac/azure/main.tf:
--------------------------------------------------------------------------------
1 | # Create a Resource Group
2 | resource "azurerm_resource_group" "aks" {
3 | name = "aks-k8s-deployments-rg"
4 | location = "switzerlandnorth"
5 | }
6 |
7 | # Create Azure Container Registry
8 | resource "azurerm_container_registry" "acr" {
9 | name = "aksgitops3003204acr"
10 | resource_group_name = azurerm_resource_group.aks.name
11 | location = azurerm_resource_group.aks.location
12 | sku = "Basic"
13 | admin_enabled = false
14 | }
15 |
16 | # Create Azure Kubernetes Service
17 | resource "azurerm_kubernetes_cluster" "aks" {
18 | name = "aksgitopscluster"
19 | location = azurerm_resource_group.aks.location
20 | resource_group_name = azurerm_resource_group.aks.name
21 | dns_prefix = "aksgitopscluster"
22 | sku_tier = "Free"
23 |
24 | default_node_pool {
25 | name = "default"
26 | node_count = 1
27 | vm_size = "Standard_B2s"
28 | }
29 |
30 | identity {
31 | type = "SystemAssigned"
32 | }
33 | }
34 |
35 | # Attach ACR to AKS using role assignment
36 | resource "azurerm_role_assignment" "acr_attach" {
37 | scope = azurerm_container_registry.acr.id
38 | role_definition_name = "AcrPull"
39 | principal_id = azurerm_kubernetes_cluster.aks.kubelet_identity[0].object_id
40 | }
--------------------------------------------------------------------------------
/chapter08/iac/azure/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | azurerm = {
4 | source = "hashicorp/azurerm"
5 | version = "~>3"
6 | }
7 | }
8 | required_version = ">= 1.0"
9 | }
10 |
11 | provider "azurerm" {
12 | features {
13 | resource_group {
14 | prevent_deletion_if_contains_resources = false
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/chapter08/manifests/deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion : apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: myweatherappv1
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: myweatherappv1
10 | template:
11 | metadata:
12 | labels:
13 | app: myweatherappv1
14 | spec:
15 | containers:
16 | - name: myweatherappv1
17 | image: aksgitops3003204acr.azurecr.io/myweatherappv1
18 | ports:
19 | - containerPort: 8080
--------------------------------------------------------------------------------
/chapter08/manifests/service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: myweatherappv1
5 | spec:
6 | type: LoadBalancer
7 | ports:
8 | - port: 8080
9 | selector:
10 | app: myweatherappv1
--------------------------------------------------------------------------------
/chapter08/src/data.csv:
--------------------------------------------------------------------------------
1 | Date,Temperature
2 | 2023-01-01,5
3 | 2023-01-02,6
4 | 2023-01-03,5
5 | 2023-01-04,6
6 | 2023-01-05,5
7 | 2023-01-06,5
8 | 2023-01-07,6
9 | 2023-01-08,6
10 | 2023-01-09,7
11 | 2023-01-10,6
12 | 2023-01-11,7
13 |
--------------------------------------------------------------------------------
/chapter08/src/dockerfile:
--------------------------------------------------------------------------------
1 | # Use an official Node.js runtime as a parent image
2 | FROM node:14
3 |
4 | # Set the working directory
5 | WORKDIR /usr/src/app
6 |
7 | # Copy package.json and install dependencies
8 | COPY package*.json ./
9 | RUN npm install
10 |
11 | # Bundle app source
12 | COPY . .
13 |
14 | # Expose the port the app runs on
15 | EXPOSE 8080
16 |
17 | # Define the command to run the app
18 | CMD ["node", "server.js"]
19 |
--------------------------------------------------------------------------------
/chapter08/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Temperature Chart
5 |
6 |
7 |
8 |
9 |
10 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/chapter08/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "temperature-chart-app",
3 | "version": "1.0.0",
4 | "description": "A simple web app displaying temperature charts from CSV data",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "dependencies": {
10 | "express": "^4.17.1"
11 | },
12 | "author": "",
13 | "license": "ISC"
14 | }
15 |
--------------------------------------------------------------------------------
/chapter08/src/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const port = 8080;
4 |
5 | app.use(express.static('.')); // Serve static files from the current directory
6 |
7 | app.listen(port, () => {
8 | console.log(`Server running at http://localhost:${port}`);
9 | });
10 |
--------------------------------------------------------------------------------
/chapter09/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | trigger:
2 | - main
3 |
4 | resources:
5 | - repo: self
6 |
7 | variables:
8 |
9 | # Container registry service connection established during pipeline creation
10 | dockerRegistryServiceConnection: 'dd42c99b-7b18-42ae-9fe8-fa42ecd0bc04'
11 | imageRepository: 'myweatherappv1'
12 | containerRegistry: 'aksgitops3003204acr.azurecr.io'
13 | dockerfilePath: '**/src/dockerfile'
14 | tag: '$(Build.BuildId)'
15 | imagePullSecret: 'aksgitops3003204acra253-auth'
16 |
17 | # Agent VM image name
18 | vmImageName: 'ubuntu-latest'
19 |
20 |
21 | stages:
22 | - stage: Build
23 | displayName: Build stage
24 | jobs:
25 | - job: Build
26 | displayName: Build
27 | pool:
28 | vmImage: $(vmImageName)
29 | steps:
30 | - task: Docker@2
31 | displayName: Build and push an image to container registry
32 | inputs:
33 | command: buildAndPush
34 | repository: $(imageRepository)
35 | dockerfile: $(dockerfilePath)
36 | containerRegistry: $(dockerRegistryServiceConnection)
37 | tags: |
38 | $(tag)
39 |
40 | - upload: manifests
41 | artifact: manifests
42 |
43 | - stage: Deploy
44 | displayName: Deploy stage
45 | dependsOn: Build
46 |
47 | jobs:
48 | - deployment: Deploy
49 | displayName: Deploy
50 | pool:
51 | vmImage: $(vmImageName)
52 | environment: 'Development'
53 | strategy:
54 | runOnce:
55 | deploy:
56 | steps:
57 | - task: KubernetesManifest@0
58 | displayName: Create imagePullSecret
59 | inputs:
60 | action: createSecret
61 | secretName: $(imagePullSecret)
62 | dockerRegistryEndpoint: $(dockerRegistryServiceConnection)
63 |
64 | - task: KubernetesManifest@0
65 | displayName: Deploy to Kubernetes cluster
66 | inputs:
67 | action: deploy
68 | manifests: |
69 | $(Pipeline.Workspace)/manifests/deployment.yml
70 | $(Pipeline.Workspace)/manifests/service.yml
71 | imagePullSecrets: |
72 | $(imagePullSecret)
73 | containers: |
74 | $(containerRegistry)/$(imageRepository):$(tag)
75 |
76 |
--------------------------------------------------------------------------------
/chapter09/deployment/base/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: my-city-weather-app
5 | # namespace: gitopsk8sdeployments
6 | labels:
7 | app: my-city-weather-app
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: my-city-weather-app
13 | template:
14 | metadata:
15 | labels:
16 | app: my-city-weather-app
17 | spec:
18 | containers:
19 | - name: my-city-weather-app
20 | image: 637423230075.dkr.ecr.eu-central-1.amazonaws.com/weather-app:latest
21 | ports:
22 | - containerPort: 8080
--------------------------------------------------------------------------------
/chapter09/deployment/base/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: my-city-weather-app-service
5 | # namespace: gitopsk8sdeployments
6 | spec:
7 | type: LoadBalancer
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 | protocol: TCP
12 | selector:
13 | app: my-city-weather-app
14 |
--------------------------------------------------------------------------------
/chapter09/deployment/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | resources:
4 | - base
--------------------------------------------------------------------------------
/chapter09/iac/aws/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~>5"
6 | }
7 | }
8 | }
9 |
10 | provider "aws" {
11 | region = "eu-central-1"
12 |
13 | }
14 |
15 |
16 | # ECR creation
17 | resource "aws_ecr_repository" "ecr" {
18 | name = "weather-app"
19 | image_tag_mutability = "MUTABLE"
20 | }
21 |
22 | # VPC and subnets
23 |
24 | module "vpc" {
25 | source = "terraform-aws-modules/vpc/aws"
26 | version = "5.7.0"
27 |
28 | name = "eks-cluster-vpc"
29 | cidr = "10.0.0.0/16"
30 |
31 | # At least two AZs in the region are required for EKS.
32 | azs = ["eu-central-1a", "eu-central-1b"]
33 | private_subnets = ["10.0.0.0/19", "10.0.32.0/19"]
34 | public_subnets = ["10.0.64.0/19", "10.0.96.0/19"]
35 |
36 | enable_nat_gateway = true
37 | single_nat_gateway = true
38 |
39 | enable_dns_hostnames = true
40 |
41 |
42 | public_subnet_tags = {
43 | "kubernetes.io/cluster/eksgitopscluster" = "shared"
44 | "kubernetes.io/role/elb" = 1
45 | }
46 |
47 | private_subnet_tags = {
48 | "kubernetes.io/cluster/eksgitopscluster" = "shared"
49 | "kubernetes.io/role/internal-elb" = 1
50 | }
51 | }
52 |
53 | module "eks" {
54 | source = "terraform-aws-modules/eks/aws"
55 | version = "20.8.4"
56 |
57 | cluster_name = "eksgitopscluster"
58 | cluster_version = "1.29"
59 |
60 | cluster_endpoint_public_access = true
61 |
62 | vpc_id = module.vpc.vpc_id
63 | subnet_ids = module.vpc.private_subnets
64 |
65 | eks_managed_node_group_defaults = {
66 | instance_types = ["t3.small"]
67 | }
68 |
69 | eks_managed_node_groups = {
70 | one = {
71 | name = "node-group-1"
72 |
73 | instance_types = ["t3.small"]
74 |
75 | min_size = 1
76 | max_size = 2
77 | desired_size = 1
78 | }
79 | }
80 |
81 | create_node_security_group = true
82 | }
83 |
84 |
85 | # IAM role to allow EKS nodes to pull images from ECR
86 | resource "aws_iam_policy" "ecr_pull" {
87 | name = "ecr-pull-policy"
88 | path = "/"
89 | description = "IAM policy for EKS to pull images from ECR"
90 | policy = jsonencode({
91 | Version = "2012-10-17"
92 | Statement = [
93 | {
94 | Action = "ecr:GetAuthorizationToken",
95 | Effect = "Allow",
96 | Resource = "*"
97 | },
98 | {
99 | Action = [
100 | "ecr:BatchCheckLayerAvailability",
101 | "ecr:GetDownloadUrlForLayer",
102 | "ecr:GetRepositoryPolicy",
103 | "ecr:DescribeRepositories",
104 | "ecr:ListImages",
105 | "ecr:DescribeImages",
106 | "ecr:BatchGetImage",
107 | ],
108 | Effect = "Allow",
109 | Resource = aws_ecr_repository.ecr.arn
110 | },
111 | ]
112 | })
113 | }
114 |
115 | resource "aws_iam_role_policy_attachment" "ecr_pull_attach" {
116 | role = module.eks.cluster_iam_role_name
117 | policy_arn = aws_iam_policy.ecr_pull.arn
118 | }
--------------------------------------------------------------------------------
/chapter09/iac/azure/main.tf:
--------------------------------------------------------------------------------
1 | # Create a Resource Group
2 | resource "azurerm_resource_group" "aks" {
3 | name = "aks-k8s-deployments-rg"
4 | location = "switzerlandnorth"
5 | }
6 |
7 | # Create Azure Container Registry
8 | resource "azurerm_container_registry" "acr" {
9 | name = "aksgitops3003204acr"
10 | resource_group_name = azurerm_resource_group.aks.name
11 | location = azurerm_resource_group.aks.location
12 | sku = "Basic"
13 | admin_enabled = false
14 | }
15 |
16 | # Create Azure Kubernetes Service
17 | resource "azurerm_kubernetes_cluster" "aks" {
18 | name = "aksgitopscluster"
19 | location = azurerm_resource_group.aks.location
20 | resource_group_name = azurerm_resource_group.aks.name
21 | dns_prefix = "aksgitopscluster"
22 | sku_tier = "Free"
23 |
24 | default_node_pool {
25 | name = "default"
26 | node_count = 1
27 | vm_size = "Standard_B2s"
28 | }
29 |
30 | identity {
31 | type = "SystemAssigned"
32 | }
33 | }
34 |
35 | # Attach ACR to AKS using role assignment
36 | resource "azurerm_role_assignment" "acr_attach" {
37 | scope = azurerm_container_registry.acr.id
38 | role_definition_name = "AcrPull"
39 | principal_id = azurerm_kubernetes_cluster.aks.kubelet_identity[0].object_id
40 | }
--------------------------------------------------------------------------------
/chapter09/iac/azure/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | azurerm = {
4 | source = "hashicorp/azurerm"
5 | version = "~>3"
6 | }
7 | }
8 | required_version = ">= 1.0"
9 | }
10 |
11 | provider "azurerm" {
12 | features {
13 | resource_group {
14 | prevent_deletion_if_contains_resources = false
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/chapter09/manifests/deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion : apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: myweatherappv1
5 | spec:
6 | replicas: 1
7 | selector:
8 | matchLabels:
9 | app: myweatherappv1
10 | template:
11 | metadata:
12 | labels:
13 | app: myweatherappv1
14 | spec:
15 | containers:
16 | - name: myweatherappv1
17 | image: aksgitops3003204acr.azurecr.io/myweatherappv1
18 | ports:
19 | - containerPort: 8080
--------------------------------------------------------------------------------
/chapter09/manifests/service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: myweatherappv1
5 | spec:
6 | type: LoadBalancer
7 | ports:
8 | - port: 8080
9 | selector:
10 | app: myweatherappv1
--------------------------------------------------------------------------------
/chapter09/src/data.csv:
--------------------------------------------------------------------------------
1 | Date,Temperature
2 | 2023-01-01,5
3 | 2023-01-02,6
4 | 2023-01-03,5
5 | 2023-01-04,6
6 | 2023-01-05,5
7 | 2023-01-06,5
8 | 2023-01-07,6
9 | 2023-01-08,6
10 | 2023-01-09,7
11 | 2023-01-10,6
12 | 2023-01-11,7
13 |
--------------------------------------------------------------------------------
/chapter09/src/dockerfile:
--------------------------------------------------------------------------------
1 | # Use an official Node.js runtime as a parent image
2 | FROM node:14
3 |
4 | # Set the working directory
5 | WORKDIR /usr/src/app
6 |
7 | # Copy package.json and install dependencies
8 | COPY package*.json ./
9 | RUN npm install
10 |
11 | # Bundle app source
12 | COPY . .
13 |
14 | # Expose the port the app runs on
15 | EXPOSE 8080
16 |
17 | # Define the command to run the app
18 | CMD ["node", "server.js"]
19 |
--------------------------------------------------------------------------------
/chapter09/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Temperature Chart
5 |
6 |
7 |
8 |
9 |
10 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/chapter09/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "temperature-chart-app",
3 | "version": "1.0.0",
4 | "description": "A simple web app displaying temperature charts from CSV data",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "dependencies": {
10 | "express": "^4.17.1"
11 | },
12 | "author": "",
13 | "license": "ISC"
14 | }
15 |
--------------------------------------------------------------------------------
/chapter09/src/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const port = 8080;
4 |
5 | app.use(express.static('.')); // Serve static files from the current directory
6 |
7 | app.listen(port, () => {
8 | console.log(`Server running at http://localhost:${port}`);
9 | });
10 |
--------------------------------------------------------------------------------
/chapter10/Docker/dockerfile:
--------------------------------------------------------------------------------
1 | # Last release of the tf-runner image
2 | # https://github.com/flux-iac/tf-runner-images/pkgs/container/tf-runner
3 | FROM ghcr.io/flux-iac/tf-runner:main-00031bcc@sha256:0a58ffb95144fdf738a4c925b4accfc312c4953b3c4dbf02677001ceb8453b88
4 |
5 | ARG TARGETARCH=amd64
6 | ARG TF_VERSION=1.8.1
7 |
8 | # Switch to root to have permissions for operations
9 | USER root
10 |
11 | ADD https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_${TARGETARCH}.zip /terraform_${TF_VERSION}_linux_${TARGETARCH}.zip
12 |
13 | # Overwrite the existing terraform binary
14 | RUN unzip -o -q /terraform_${TF_VERSION}_linux_${TARGETARCH}.zip -d /usr/local/bin/ && \
15 | rm /terraform_${TF_VERSION}_linux_${TARGETARCH}.zip && \
16 | chmod +x /usr/local/bin/terraform
17 |
18 | # Switch back to the non-root user after operations
19 | USER 65532:65532
20 |
--------------------------------------------------------------------------------
/chapter10/README.md:
--------------------------------------------------------------------------------
1 | # gitops-terraform-workflow
--------------------------------------------------------------------------------
/chapter10/flux-gitops-definitions/dev-iac-automation.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: infra.contrib.fluxcd.io/v1alpha2
2 | kind: Terraform
3 | metadata:
4 | name: dev-cluster-tf-automation
5 | namespace: flux-system
6 | spec:
7 | interval: 1m
8 | approvePlan: auto
9 | destroyResourcesOnDeletion: true
10 | path: ./multi-env/iac/azure/dev
11 | sourceRef:
12 | kind: GitRepository
13 | name: flux-system
14 | runnerPodTemplate:
15 | spec:
16 | env:
17 | - name: ARM_SUBSCRIPTION_ID
18 | valueFrom:
19 | secretKeyRef:
20 | name: azure-creds
21 | key: ARM_SUBSCRIPTION_ID
22 | - name: ARM_CLIENT_ID
23 | valueFrom:
24 | secretKeyRef:
25 | name: azure-creds
26 | key: ARM_CLIENT_ID
27 | - name: ARM_CLIENT_SECRET
28 | valueFrom:
29 | secretKeyRef:
30 | name: azure-creds
31 | key: ARM_CLIENT_SECRET
32 | - name: ARM_TENANT_ID
33 | valueFrom:
34 | secretKeyRef:
35 | name: azure-creds
36 | key: ARM_TENANT_ID
--------------------------------------------------------------------------------
/chapter10/flux-gitops-definitions/github-repository-definition.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: source.toolkit.fluxcd.io/v1
2 | kind: GitRepository
3 | metadata:
4 | name: gitops-terraform-repository
5 | namespace: flux-system
6 | spec:
7 | interval: 1m # Sync interval
8 | url: "https://github.com/[YOUR_REPOSITORY_GOES_HERE]"
9 | secretRef:
10 | name: github-repository-secret
11 | ref:
12 | branch: develop # Specify the branch to track
13 |
--------------------------------------------------------------------------------
/chapter10/flux-gitops-definitions/github-repository-secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: github-repository-secret
5 | namespace: flux-system
6 | type: Opaque
7 | data:
8 | username: [YOUR_ENCODED_USERNAME]
9 | password: [YOUR_ENCODED_PASSWORD]
10 |
--------------------------------------------------------------------------------
/chapter10/flux-gitops-definitions/prod-iac-automation.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: infra.contrib.fluxcd.io/v1alpha2
2 | kind: Terraform
3 | metadata:
4 | name: prod-cluster-tf-automation
5 | namespace: flux-system
6 | spec:
7 | interval: 1m
8 | approvePlan: auto
9 | destroyResourcesOnDeletion: true
10 | path: ./multi-env/iac/azure/prod
11 | sourceRef:
12 | kind: GitRepository
13 | name: flux-system
14 | runnerPodTemplate:
15 | spec:
16 | env:
17 | - name: ARM_SUBSCRIPTION_ID
18 | valueFrom:
19 | secretKeyRef:
20 | name: azure-creds
21 | key: ARM_SUBSCRIPTION_ID
22 | - name: ARM_CLIENT_ID
23 | valueFrom:
24 | secretKeyRef:
25 | name: azure-creds
26 | key: ARM_CLIENT_ID
27 | - name: ARM_CLIENT_SECRET
28 | valueFrom:
29 | secretKeyRef:
30 | name: azure-creds
31 | key: ARM_CLIENT_SECRET
32 | - name: ARM_TENANT_ID
33 | valueFrom:
34 | secretKeyRef:
35 | name: azure-creds
36 | key: ARM_TENANT_ID
--------------------------------------------------------------------------------
/chapter10/flux-gitops-definitions/staging-iac-automation.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: infra.contrib.fluxcd.io/v1alpha2
2 | kind: Terraform
3 | metadata:
4 | name: staging-cluster-tf-automation
5 | namespace: flux-system
6 | spec:
7 | interval: 1m
8 | approvePlan: auto
9 | destroyResourcesOnDeletion: true
10 | path: ./multi-env/iac/azure/staging
11 | sourceRef:
12 | kind: GitRepository
13 | name: flux-system
14 | runnerPodTemplate:
15 | spec:
16 | env:
17 | - name: ARM_SUBSCRIPTION_ID
18 | valueFrom:
19 | secretKeyRef:
20 | name: azure-creds
21 | key: ARM_SUBSCRIPTION_ID
22 | - name: ARM_CLIENT_ID
23 | valueFrom:
24 | secretKeyRef:
25 | name: azure-creds
26 | key: ARM_CLIENT_ID
27 | - name: ARM_CLIENT_SECRET
28 | valueFrom:
29 | secretKeyRef:
30 | name: azure-creds
31 | key: ARM_CLIENT_SECRET
32 | - name: ARM_TENANT_ID
33 | valueFrom:
34 | secretKeyRef:
35 | name: azure-creds
36 | key: ARM_TENANT_ID
--------------------------------------------------------------------------------
/chapter10/iac/azure/vnet/main.tf:
--------------------------------------------------------------------------------
1 | provider "azurerm" {
2 | features {}
3 | }
4 |
5 |
6 | resource "azurerm_resource_group" "gitops_terraform_rg" {
7 | name = "gitops-terraform-rg"
8 | location = "switzerlandnorth"
9 | }
10 |
11 | resource "azurerm_virtual_network" "gitops_terraform_vnet" {
12 | name = "gitops-terraform-vnet"
13 | resource_group_name = azurerm_resource_group.gitops_terraform_rg.name
14 | location = azurerm_resource_group.gitops_terraform_rg.location
15 | address_space = ["10.0.0.0/16"]
16 | }
17 |
18 | resource "azurerm_subnet" "default_subnet" {
19 | name = "default"
20 | resource_group_name = azurerm_resource_group.gitops_terraform_rg.name
21 | virtual_network_name = azurerm_virtual_network.gitops_terraform_vnet.name
22 | address_prefixes = ["10.0.0.0/24"]
23 | }
24 |
25 | resource "azurerm_subnet" "azure_bastion_subnet" {
26 | name = "AzureBastionSubnet"
27 | resource_group_name = azurerm_resource_group.gitops_terraform_rg.name
28 | virtual_network_name = azurerm_virtual_network.gitops_terraform_vnet.name
29 | address_prefixes = ["10.0.2.0/26"]
30 | }
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/base/main.tf:
--------------------------------------------------------------------------------
1 |
2 | provider "azurerm" {
3 | features {}
4 | }
5 |
6 |
7 | resource "azurerm_resource_group" "gitops_rg" {
8 | name = var.rg
9 | location = var.location
10 | }
11 |
12 | resource "azurerm_virtual_network" "gitops_vnet" {
13 | name = "gitops-${var.environment}-vnet"
14 | resource_group_name = azurerm_resource_group.gitops_rg.name
15 | location = azurerm_resource_group.gitops_rg.location
16 | address_space = ["10.0.0.0/16"]
17 | }
18 |
19 | resource "azurerm_subnet" "default_subnet" {
20 | name = "default"
21 | resource_group_name = azurerm_resource_group.gitops_rg.name
22 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
23 | address_prefixes = ["10.0.0.0/24"]
24 | }
25 |
26 | resource "azurerm_subnet" "aks_subnet" {
27 | name = "gitops-${var.environment}-aks-subnet"
28 | resource_group_name = azurerm_resource_group.gitops_rg.name
29 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
30 | address_prefixes = ["10.0.1.0/24"]
31 | }
32 |
33 | resource "azurerm_kubernetes_cluster" "gitops_aks" {
34 | name = "gitops-${var.environment}-aks"
35 | location = azurerm_resource_group.gitops_rg.location
36 | resource_group_name = azurerm_resource_group.gitops_rg.name
37 | dns_prefix = "gitops-${var.environment}-aks"
38 | kubernetes_version = "1.28.5"
39 | private_cluster_enabled = false
40 |
41 | default_node_pool {
42 | name = "default"
43 | node_count = 1
44 | vm_size = "Standard_B2s"
45 | os_disk_size_gb = 30
46 | type = "VirtualMachineScaleSets"
47 | }
48 |
49 | identity {
50 | type = "SystemAssigned"
51 | }
52 |
53 | network_profile {
54 | network_plugin = "azure"
55 | }
56 |
57 | }
58 |
59 | # Define an Azure Container Registry with the name gitops-terraform-acr in the same resource group as the AKS cluster.
60 | # The SKU is set to Basic and the admin user is disabled.
61 |
62 | resource "azurerm_container_registry" "gitops_acr" {
63 | name = "gitops${var.environment}acr"
64 | resource_group_name = azurerm_resource_group.gitops_rg.name
65 | location = azurerm_resource_group.gitops_rg.location
66 | sku = "Standard"
67 | admin_enabled = false
68 | }
69 |
70 | # Define a role assignment to allow the AKS cluster to pull images from the ACR.
71 |
72 | resource "azurerm_role_assignment" "gitops_acr_role" {
73 | scope = azurerm_container_registry.gitops_acr.id
74 | role_definition_name = "AcrPull"
75 | principal_id = azurerm_kubernetes_cluster.gitops_aks.kubelet_identity[0].object_id
76 | }
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/base/readme.md:
--------------------------------------------------------------------------------
1 | # Documentation
2 |
3 | This Terraform file is used to create resources in Azure.
4 |
5 | ### Provider
6 |
7 | The `azurerm` provider is used to interact with the many resources supported by Azure Resource Manager (AzureRM) through its APIs.
8 |
9 | ```
10 | provider "azurerm" {
11 | features {}
12 | }
13 | ```
14 |
15 | ## Resources
16 |
17 | ### Resource Group
18 |
19 | The azurerm_resource_group resource creates a new resource group in Azure. The name and location of the resource group are defined by the variables var.rg and var.location respectively.
20 |
21 | ```
22 | resource "azurerm_resource_group" "gitops_rg" {
23 | name = var.rg
24 | location = var.location
25 | }
26 | ```
27 |
28 | ### Virtual Network
29 |
30 | The azurerm_virtual_network resource creates a new virtual network in Azure. The name of the virtual network is a combination of the string "gitops-" and the value of var.environment, followed by "-vnet". The resource group name and location are derived from the previously created resource group. The address space for the virtual network is defined as "10.0.0.0/16".
31 |
32 | ```
33 | resource "azurerm_virtual_network" "gitops_vnet" {
34 | name = "gitops-${var.environment}-vnet"
35 | resource_group_name = azurerm_resource_group.gitops_rg.name
36 | location = azurerm_resource_group.gitops_rg.location
37 | address_space = ["10.0.0.0/16"]
38 | }
39 | ```
40 |
41 | ### Subnets
42 | Two subnets are created within the virtual network.
43 |
44 | ### Default Subnet
45 | The first subnet, named "default", uses the address prefix "10.0.0.0/24".
46 |
47 | ```
48 | resource "azurerm_subnet" "default_subnet" {
49 | name = "default"
50 | resource_group_name = azurerm_resource_group.gitops_rg.name
51 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
52 | address_prefixes = ["10.0.0.0/24"]
53 | }
54 | ```
55 |
56 | ### AKS Subnet
57 |
58 | The second subnet, named "aks_subnet", is not fully defined in the provided excerpt.
59 |
60 | ```
61 | resource "azurerm_subnet" "aks_subnet" {
62 | name = "gitops-${var.environment}-aks-subnet"
63 | resource_group_name = azurerm_resource_group.gitops_rg.name
64 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
65 | address_prefixes = ["10.0.1.0/24"]
66 | }
67 | ```
68 |
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/base/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 | description = "The environment in which the resources will be created."
3 | type = string
4 | default = "dev"
5 | }
6 |
7 | variable "location" {
8 | description = "The location in which the resources will be created."
9 | type = string
10 | default = "switzerlandnorth"
11 |
12 | }
13 |
14 | variable "rg" {
15 | description = "The name of the resource group in which to create the resources."
16 | type = string
17 | default = "gitops-dev-rg"
18 | }
19 |
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/dev/main.tf:
--------------------------------------------------------------------------------
1 |
2 | provider "azurerm" {
3 | features {}
4 | }
5 |
6 |
7 | resource "azurerm_resource_group" "gitops_rg" {
8 | name = var.rg
9 | location = var.location
10 | }
11 |
12 | resource "azurerm_virtual_network" "gitops_vnet" {
13 | name = "gitops-${var.environment}-vnet"
14 | resource_group_name = azurerm_resource_group.gitops_rg.name
15 | location = azurerm_resource_group.gitops_rg.location
16 | address_space = ["10.0.0.0/16"]
17 | }
18 |
19 | resource "azurerm_subnet" "default_subnet" {
20 | name = "default"
21 | resource_group_name = azurerm_resource_group.gitops_rg.name
22 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
23 | address_prefixes = ["10.0.0.0/24"]
24 | }
25 |
26 | resource "azurerm_subnet" "aks_subnet" {
27 | name = "gitops-${var.environment}-aks-subnet"
28 | resource_group_name = azurerm_resource_group.gitops_rg.name
29 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
30 | address_prefixes = ["10.0.1.0/24"]
31 | }
32 |
33 | resource "azurerm_kubernetes_cluster" "gitops_aks" {
34 | name = "gitops-${var.environment}-aks"
35 | location = azurerm_resource_group.gitops_rg.location
36 | resource_group_name = azurerm_resource_group.gitops_rg.name
37 | dns_prefix = "gitops-${var.environment}-aks"
38 | kubernetes_version = "1.28.5"
39 | private_cluster_enabled = false
40 |
41 | default_node_pool {
42 | name = "default"
43 | node_count = 1
44 | vm_size = "Standard_B2s"
45 | os_disk_size_gb = 30
46 | type = "VirtualMachineScaleSets"
47 | }
48 |
49 | identity {
50 | type = "SystemAssigned"
51 | }
52 |
53 | network_profile {
54 | network_plugin = "azure"
55 | }
56 |
57 | }
58 |
59 | # Define an Azure Container Registry with the name gitops-terraform-acr in the same resource group as the AKS cluster.
60 | # The SKU is set to Basic and the admin user is disabled.
61 |
62 | resource "azurerm_container_registry" "gitops_acr" {
63 | name = "gitops${var.environment}acr"
64 | resource_group_name = azurerm_resource_group.gitops_rg.name
65 | location = azurerm_resource_group.gitops_rg.location
66 | sku = "Standard"
67 | admin_enabled = false
68 | }
69 |
70 | # Define a role assignment to allow the AKS cluster to pull images from the ACR.
71 |
72 | resource "azurerm_role_assignment" "gitops_acr_role" {
73 | scope = azurerm_container_registry.gitops_acr.id
74 | role_definition_name = "AcrPull"
75 | principal_id = azurerm_kubernetes_cluster.gitops_aks.kubelet_identity[0].object_id
76 | }
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/dev/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 | description = "The environment in which the resources will be created."
3 | type = string
4 | default = "dev"
5 | }
6 |
7 | variable "location" {
8 | description = "The location in which the resources will be created."
9 | type = string
10 | default = "switzerlandnorth"
11 |
12 | }
13 |
14 | variable "rg" {
15 | description = "The name of the resource group in which to create the resources."
16 | type = string
17 | default = "gitops-dev-rg"
18 | }
19 |
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/prod/main.tf:
--------------------------------------------------------------------------------
1 |
2 | provider "azurerm" {
3 | features {}
4 | }
5 |
6 |
7 | resource "azurerm_resource_group" "gitops_rg" {
8 | name = var.rg
9 | location = var.location
10 | }
11 |
12 | resource "azurerm_virtual_network" "gitops_vnet" {
13 | name = "gitops-${var.environment}-vnet"
14 | resource_group_name = azurerm_resource_group.gitops_rg.name
15 | location = azurerm_resource_group.gitops_rg.location
16 | address_space = ["10.0.0.0/16"]
17 | }
18 |
19 | resource "azurerm_subnet" "default_subnet" {
20 | name = "default"
21 | resource_group_name = azurerm_resource_group.gitops_rg.name
22 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
23 | address_prefixes = ["10.0.0.0/24"]
24 | }
25 |
26 | resource "azurerm_subnet" "aks_subnet" {
27 | name = "gitops-${var.environment}-aks-subnet"
28 | resource_group_name = azurerm_resource_group.gitops_rg.name
29 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
30 | address_prefixes = ["10.0.1.0/24"]
31 | }
32 |
33 | resource "azurerm_kubernetes_cluster" "gitops_aks" {
34 | name = "gitops-${var.environment}-aks"
35 | location = azurerm_resource_group.gitops_rg.location
36 | resource_group_name = azurerm_resource_group.gitops_rg.name
37 | dns_prefix = "gitops-${var.environment}-aks"
38 | kubernetes_version = "1.28.5"
39 | private_cluster_enabled = false
40 |
41 | default_node_pool {
42 | name = "default"
43 | node_count = 1
44 | vm_size = "Standard_B2s"
45 | os_disk_size_gb = 30
46 | type = "VirtualMachineScaleSets"
47 | }
48 |
49 | identity {
50 | type = "SystemAssigned"
51 | }
52 |
53 | network_profile {
54 | network_plugin = "azure"
55 | }
56 |
57 | }
58 |
59 | # Define an Azure Container Registry with the name gitops-terraform-acr in the same resource group as the AKS cluster.
60 | # The SKU is set to Basic and the admin user is disabled.
61 |
62 | resource "azurerm_container_registry" "gitops_acr" {
63 | name = "gitops${var.environment}acr"
64 | resource_group_name = azurerm_resource_group.gitops_rg.name
65 | location = azurerm_resource_group.gitops_rg.location
66 | sku = "Standard"
67 | admin_enabled = false
68 | }
69 |
70 | # Define a role assignment to allow the AKS cluster to pull images from the ACR.
71 |
72 | resource "azurerm_role_assignment" "gitops_acr_role" {
73 | scope = azurerm_container_registry.gitops_acr.id
74 | role_definition_name = "AcrPull"
75 | principal_id = azurerm_kubernetes_cluster.gitops_aks.kubelet_identity[0].object_id
76 | }
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/prod/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 | description = "The environment in which the resources will be created."
3 | type = string
4 | default = "prod"
5 | }
6 |
7 | variable "location" {
8 | description = "The location in which the resources will be created."
9 | type = string
10 | default = "switzerlandnorth"
11 |
12 | }
13 |
14 | variable "rg" {
15 | description = "The name of the resource group in which to create the resources."
16 | type = string
17 | default = "gitops-prod-rg"
18 | }
19 |
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/staging/main.tf:
--------------------------------------------------------------------------------
1 |
2 | provider "azurerm" {
3 | features {}
4 | }
5 |
6 |
7 | resource "azurerm_resource_group" "gitops_rg" {
8 | name = var.rg
9 | location = var.location
10 | }
11 |
12 | resource "azurerm_virtual_network" "gitops_vnet" {
13 | name = "gitops-${var.environment}-vnet"
14 | resource_group_name = azurerm_resource_group.gitops_rg.name
15 | location = azurerm_resource_group.gitops_rg.location
16 | address_space = ["10.0.0.0/16"]
17 | }
18 |
19 | resource "azurerm_subnet" "default_subnet" {
20 | name = "default"
21 | resource_group_name = azurerm_resource_group.gitops_rg.name
22 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
23 | address_prefixes = ["10.0.0.0/24"]
24 | }
25 |
26 | resource "azurerm_subnet" "aks_subnet" {
27 | name = "gitops-${var.environment}-aks-subnet"
28 | resource_group_name = azurerm_resource_group.gitops_rg.name
29 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
30 | address_prefixes = ["10.0.1.0/24"]
31 | }
32 |
33 | resource "azurerm_kubernetes_cluster" "gitops_aks" {
34 | name = "gitops-${var.environment}-aks"
35 | location = azurerm_resource_group.gitops_rg.location
36 | resource_group_name = azurerm_resource_group.gitops_rg.name
37 | dns_prefix = "gitops-${var.environment}-aks"
38 | kubernetes_version = "1.28.5"
39 | private_cluster_enabled = false
40 |
41 | default_node_pool {
42 | name = "default"
43 | node_count = 1
44 | vm_size = "Standard_B2s"
45 | os_disk_size_gb = 30
46 | type = "VirtualMachineScaleSets"
47 | }
48 |
49 | identity {
50 | type = "SystemAssigned"
51 | }
52 |
53 | network_profile {
54 | network_plugin = "azure"
55 | }
56 |
57 | }
58 |
59 | # Define an Azure Container Registry with the name gitops-terraform-acr in the same resource group as the AKS cluster.
60 | # The SKU is set to Basic and the admin user is disabled.
61 |
62 | resource "azurerm_container_registry" "gitops_acr" {
63 | name = "gitops${var.environment}acr"
64 | resource_group_name = azurerm_resource_group.gitops_rg.name
65 | location = azurerm_resource_group.gitops_rg.location
66 | sku = "Standard"
67 | admin_enabled = false
68 | }
69 |
70 | # Define a role assignment to allow the AKS cluster to pull images from the ACR.
71 |
72 | resource "azurerm_role_assignment" "gitops_acr_role" {
73 | scope = azurerm_container_registry.gitops_acr.id
74 | role_definition_name = "AcrPull"
75 | principal_id = azurerm_kubernetes_cluster.gitops_aks.kubelet_identity[0].object_id
76 | }
--------------------------------------------------------------------------------
/chapter10/multi-env/iac/azure/staging/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 | description = "The environment in which the resources will be created."
3 | type = string
4 | default = "staging"
5 | }
6 |
7 | variable "location" {
8 | description = "The location in which the resources will be created."
9 | type = string
10 | default = "switzerlandnorth"
11 |
12 | }
13 |
14 | variable "rg" {
15 | description = "The name of the resource group in which to create the resources."
16 | type = string
17 | default = "gitops-staging-rg"
18 | }
19 |
--------------------------------------------------------------------------------
/chapter11/Step-01/ArgoCD-GitOps/argocd_deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: backend-api-weather-app
5 | namespace: argocd
6 | spec:
7 | destination:
8 | namespace: weather-app-for-real
9 | server: https://kubernetes.default.svc
10 | project: default
11 | source:
12 | repoURL: https://github.com/pietrolibro/gitops-for-real-world
13 | path: ./Step-01/deployment
14 | targetRevision: main
15 |
--------------------------------------------------------------------------------
/chapter11/Step-01/backend-api.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, request, jsonify
2 | import requests
3 | import os
4 |
5 | app = Flask(__name__)
6 |
7 | @app.route('/weather', methods=['GET'])
8 | def get_weather():
9 | city = request.args.get('city', 'Zurich') # Default to Zurich if no city parameter is provided
10 | api_key = os.getenv('WEATHER_API_KEY') # Get the API key from the environment variables
11 | if not api_key:
12 | return jsonify({'error': 'API key is not configured'}), 500
13 |
14 | url = f"http://api.weatherapi.com/v1/current.json"
15 | params = {
16 | 'key': api_key,
17 | 'q': city,
18 | 'aqi': 'no' # Air quality data is not needed for this basic example
19 | }
20 | response = requests.get(url, params=params)
21 | if response.status_code != 200:
22 | return jsonify({'error': 'Failed to fetch weather data'}), response.status_code
23 |
24 | return jsonify(response.json())
25 |
26 | if __name__ == '__main__':
27 | app.run(host='0.0.0.0', port=80)
28 |
--------------------------------------------------------------------------------
/chapter11/Step-01/deployment/backend-api-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: backend-api-weather-app
5 | namespace: weather-app-for-real
6 | spec:
7 | replicas: 1
8 | selector:
9 | matchLabels:
10 | app: backend-api-weather-app
11 | template:
12 | metadata:
13 | labels:
14 | app: backend-api-weather-app
15 | spec:
16 | containers:
17 | - name: weather-app-backend-api
18 | image: pietrolibro/weather-app-backend-api:vcc5fdd842988b0baf15b08c4bb4ad963ca1ebfea
19 | imagePullPolicy: Always # Forces Kubernetes to always pull the image
20 | ports:
21 | - containerPort: 80
22 | env:
23 | - name: WEATHER_API_KEY # Environment variable your app uses to access the API key
24 | valueFrom:
25 | secretKeyRef:
26 | name: weather-api-key # The name of the Kubernetes Secret
27 | key: WEATHER_API_KEY # The key of the data stored in the Secret
28 | ---
29 | apiVersion: v1
30 | kind: Service
31 | metadata:
32 | name: backend-api-service
33 | namespace: weather-app-for-real
34 | spec:
35 | selector:
36 | app: backend-api-weather-app
37 | ports:
38 | - protocol: TCP
39 | port: 80
40 | type: LoadBalancer # We are using LoadBalancer type to get a public ip address.
41 |
--------------------------------------------------------------------------------
/chapter11/Step-01/deployment/backend-api-secrets.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: weather-api-key
5 | namespace: weather-app-for-real
6 | type: Opaque
7 | data:
8 | WEATHER_API_KEY: YOUR_API_TOKEN_GOES_HERE
9 |
--------------------------------------------------------------------------------
/chapter11/Step-01/dockerfile:
--------------------------------------------------------------------------------
1 | # Use an official Python runtime as a parent image
2 | FROM python:3.9-slim
3 |
4 | # Set the working directory in the container to /app
5 | WORKDIR /app
6 |
7 | # Copy the current directory contents into the container at /app
8 | COPY . /app
9 |
10 | RUN ls -la
11 |
12 | # Install any needed packages specified in requirements.txt
13 | RUN pip install --no-cache-dir -r requirements.txt
14 |
15 | # Make port 80 available to the world outside this container
16 | # EXPOSE 5000
17 | EXPOSE 80
18 |
19 | # Define environment variable to store the API key
20 | ENV WEATHER_API_KEY=NOT_VALID_KEY
21 |
22 | # Run app.py when the container launches
23 | CMD ["python", "backend-api.py"]
24 |
--------------------------------------------------------------------------------
/chapter11/Step-01/requirements.txt:
--------------------------------------------------------------------------------
1 | blinker==1.8.1
2 | certifi==2024.2.2
3 | charset-normalizer==3.3.2
4 | click==8.1.7
5 | Flask==3.0.3
6 | idna==3.7
7 | itsdangerous==2.2.0
8 | Jinja2>=3.1.4
9 | MarkupSafe==2.1.5
10 | requests>=2.32.0
11 | urllib3==2.2.1
12 | Werkzeug>=3.0.3
13 |
--------------------------------------------------------------------------------
/chapter11/Step-01/scripts/get-argo-cd-external-ip.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Define the service and namespace
4 | SERVICE_NAME="argocd-server"
5 | NAMESPACE="argocd"
6 |
7 | # Initialize external IP
8 | export EXTERNAL_IP=""
9 |
10 | # Loop until the external IP is assigned
11 | while [ -z $EXTERNAL_IP ]; do
12 | echo "Waiting for external IP..."
13 | EXTERNAL_IP=$(kubectl get svc $SERVICE_NAME -n $NAMESPACE --template="{{range .status.loadBalancer.ingress}}{{.ip}}{{end}}")
14 | # Wait for 10 seconds before checking again
15 | sleep 10
16 | done
17 |
18 | echo "External IP assigned: $EXTERNAL_IP"
19 |
--------------------------------------------------------------------------------
/chapter11/Step-01/scripts/get-argo-cd-initial-password.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Define the namespace where Argo CD is installed
4 | NAMESPACE="argocd"
5 |
6 | # Define the name of the secret where the initial password is stored
7 | SECRET_NAME="argocd-initial-admin-secret"
8 |
9 | export ARGO_CD_PASSWORD=""
10 |
11 |
12 | # Function to get the Argo CD admin password
13 | function get_argo_cd_password() {
14 | # Loop until the password is retrieved
15 | while true; do
16 | # Extract the password from the secret
17 | ARGO_CD_PASSWORD=$(kubectl get secret $SECRET_NAME -n $NAMESPACE -o jsonpath="{.data.password}" 2>/dev/null | base64 --decode)
18 |
19 | # Check if password is successfully retrieved and non-empty
20 | if [[ ! -z "$ARGO_CD_PASSWORD" ]]; then
21 | echo "Successfully retrieved the initial password for Argo CD."
22 | echo "Initial Admin Password: $ARGO_CD_PASSWORD"
23 | break # Exit the loop if password is retrieved
24 | else
25 | echo "Waiting for the initial password to be set in the secret..."
26 | sleep 10 # Wait for 10 seconds before trying again
27 | fi
28 | done
29 | }
30 |
31 | # Check if the secret exists in the specified namespace
32 | if kubectl get secret $SECRET_NAME -n $NAMESPACE &> /dev/null; then
33 | echo "Secret '$SECRET_NAME' found in namespace '$NAMESPACE'."
34 | get_argo_cd_password
35 | else
36 | echo "Secret '$SECRET_NAME' not found in namespace '$NAMESPACE'."
37 | echo "Ensure Argo CD is installed and the secret name is correct."
38 | exit 1
39 | fi
40 |
--------------------------------------------------------------------------------
/chapter11/Step-01/terraform/main.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | azurerm = {
4 | source = "hashicorp/azurerm"
5 | version = "~> 3.103"
6 | }
7 | }
8 |
9 | backend "azurerm" {
10 | }
11 | }
12 |
13 | provider "azurerm" {
14 | features {}
15 | }
16 |
17 |
18 | resource "azurerm_resource_group" "gitops_rg" {
19 | name = var.rg
20 | location = var.location
21 | }
22 |
23 | resource "azurerm_virtual_network" "gitops_vnet" {
24 | name = "gitops-${var.environment}-vnet"
25 | resource_group_name = azurerm_resource_group.gitops_rg.name
26 | location = azurerm_resource_group.gitops_rg.location
27 | address_space = ["10.0.0.0/16"]
28 | }
29 |
30 | resource "azurerm_subnet" "default_subnet" {
31 | name = "default"
32 | resource_group_name = azurerm_resource_group.gitops_rg.name
33 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
34 | address_prefixes = ["10.0.0.0/24"]
35 | }
36 |
37 | resource "azurerm_subnet" "aks_subnet" {
38 | name = "gitops-${var.environment}-aks-subnet"
39 | resource_group_name = azurerm_resource_group.gitops_rg.name
40 | virtual_network_name = azurerm_virtual_network.gitops_vnet.name
41 | address_prefixes = ["10.0.1.0/24"]
42 | }
43 |
44 | resource "azurerm_kubernetes_cluster" "gitops_aks" {
45 | name = "gitops-${var.environment}-aks"
46 | location = azurerm_resource_group.gitops_rg.location
47 | resource_group_name = azurerm_resource_group.gitops_rg.name
48 | dns_prefix = "gitops-${var.environment}-aks"
49 | kubernetes_version = "1.28.5"
50 | private_cluster_enabled = false
51 |
52 | default_node_pool {
53 | name = "default"
54 | node_count = 1
55 | vm_size = "Standard_D2_v2"
56 | os_disk_size_gb = 30
57 | type = "VirtualMachineScaleSets"
58 | }
59 |
60 | identity {
61 | type = "SystemAssigned"
62 | }
63 |
64 | network_profile {
65 | network_plugin = "azure"
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/chapter11/Step-01/terraform/variables.tf:
--------------------------------------------------------------------------------
1 | variable "environment" {
2 | description = "The environment in which the resources will be created."
3 | type = string
4 | default = "real"
5 | }
6 |
7 | variable "location" {
8 | description = "The location in which the resources will be created."
9 | type = string
10 | default = "switzerlandnorth"
11 |
12 | }
13 |
14 | variable "rg" {
15 | description = "The name of the resource group in which to create the resources."
16 | type = string
17 | default = "gitops-real-rg"
18 | }
19 |
--------------------------------------------------------------------------------
/chapter11/Step-03-Scalability/deployment/backend-api-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: backend-api-weather-app
5 | namespace: weather-app-for-real
6 | spec:
7 | replicas: 1
8 | selector:
9 | matchLabels:
10 | app: backend-api-weather-app
11 | template:
12 | metadata:
13 | labels:
14 | app: backend-api-weather-app
15 | spec:
16 | containers:
17 | - name: weather-app-backend-api
18 | image: pietrolibro/weather-app-backend-api:v2.0
19 | imagePullPolicy: Always # Forces Kubernetes to always pull the image
20 | ports:
21 | - containerPort: 80
22 | env:
23 | - name: WEATHER_API_KEY # Environment variable your app uses to access the API key
24 | valueFrom:
25 | secretKeyRef:
26 | name: weather-api-key # The name of the Kubernetes Secret
27 | key: WEATHER_API_KEY # The key of the data stored in the Secret
28 | resources:
29 | requests:
30 | cpu: "100m"
31 | memory: "100Mi"
32 | limits:
33 | cpu: "150m"
34 | memory: "150Mi"
35 | ---
36 | apiVersion: v1
37 | kind: Service
38 | metadata:
39 | name: backend-api-service
40 | namespace: weather-app-for-real
41 | spec:
42 | selector:
43 | app: backend-api-weather-app
44 | ports:
45 | - protocol: TCP
46 | port: 80
47 | type: LoadBalancer # We are using LoadBalancer type to get a public ip address.
48 |
--------------------------------------------------------------------------------
/chapter11/Step-03-Scalability/deployment/hpa.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: autoscaling/v1
2 | kind: HorizontalPodAutoscaler
3 | metadata:
4 | name: weather-app-backend-api-hpa
5 | namespace: weather-app-for-real
6 | spec:
7 | scaleTargetRef:
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | name: backend-api-weather-app
11 | minReplicas: 1
12 | maxReplicas: 5
13 | targetCPUUtilizationPercentage: 5
--------------------------------------------------------------------------------
/chapter11/Step-03-Scalability/hpa-testing.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Base URL of your weather service
4 | baseUrl="http://[PUBLIC_IP]/weather"
5 |
6 | # Array of cities
7 | cities=("New York" "London" "Paris" "Berlin" "Tokyo" "Sydney" "Moscow" "Cairo" "Rio de Janeiro" "Johannesburg")
8 |
9 | # Function to get a random city from the list
10 | function get_random_city {
11 | echo ${cities[$RANDOM % ${#cities[@]}]}
12 | }
13 |
14 | # Infinite loop to continuously send requests
15 | while true; do
16 | # Select a random city from the list
17 | randomCity=$(get_random_city)
18 |
19 | # Generate a random number for the cache buster
20 | cacheBuster=$((RANDOM % 10000))
21 |
22 | # Construct the full URL with the city and cache buster
23 | url="${baseUrl}?city=${randomCity}&cb=${cacheBuster}"
24 |
25 | # Send the HTTP GET request
26 | response=$(curl --silent --write-out "HTTPSTATUS:%{http_code}" -X GET $url)
27 |
28 | # Extract the body and the status
29 | httpBody=$(echo $response | sed -e 's/HTTPSTATUS\:.*//g')
30 | httpStatus=$(echo $response | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
31 |
32 | # Optional: Print the status code and response body
33 | echo "Status Code: $httpStatus"
34 | echo "Response Body: $httpBody"
35 |
36 | # Wait for 100 milliseconds before sending the next request.
37 | sleep 0.1
38 | done
39 |
--------------------------------------------------------------------------------
/chapter11/Step-04-Security/weather-app-manager-role-binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | name: weather-app-manager-binding
5 | namespace: weather-app-for-real
6 | subjects:
7 | - kind: User
8 | name: weather-app-manager
9 | apiGroup: rbac.authorization.k8s.io
10 | roleRef:
11 | kind: Role
12 | name: weather-app-manager
13 | apiGroup: rbac.authorization.k8s.io
14 |
--------------------------------------------------------------------------------
/chapter11/Step-04-Security/weather-app-manager-role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: Role
3 | metadata:
4 | namespace: weather-app-for-real
5 | name: weather-app-manager
6 | rules:
7 | - apiGroups: ["", "apps"]
8 | resources: ["deployments", "replicasets", "pods", "services"]
9 | verbs: ["get", "list", "watch", "create", "update", "delete"]
10 |
--------------------------------------------------------------------------------
/chapter11/Step-04-Security/weather-app-operator-role-binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | name: weather-app-operator-binding
5 | namespace: weather-app-for-real
6 | subjects:
7 | - kind: User
8 | name: weather-app-operator
9 | apiGroup: rbac.authorization.k8s.io
10 | roleRef:
11 | kind: Role
12 | name: weather-app-operator
13 | apiGroup: rbac.authorization.k8s.io
14 |
--------------------------------------------------------------------------------
/chapter11/Step-04-Security/weather-app-operator-role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: Role
3 | metadata:
4 | namespace: weather-app-for-real
5 | name: weather-app-operator
6 | rules:
7 | - apiGroups: ["", "apps"]
8 | resources: ["pods", "services"]
9 | verbs: ["get", "list", "watch"]
--------------------------------------------------------------------------------
/chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/applicationsets/security/external-secrets-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: external-secrets
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev
12 | values:
13 | branch: development
14 | - clusters:
15 | selector:
16 | matchLabels:
17 | env: prod
18 | values:
19 | branch: main
20 | template:
21 | metadata:
22 | name: "{{name}}-external-secrets"
23 | annotations:
24 | argocd.argoproj.io/manifest-generate-paths: ".;.."
25 | spec:
26 | project: default
27 | sources:
28 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
29 | targetRevision: "{{values.branch}}"
30 | path: "./chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/security/external-secrets"
31 | helm:
32 | releaseName: "external-secrets" # Release name override (defaults to application name)
33 | valueFiles:
34 | - "values.yaml"
35 | destination:
36 | name: "{{name}}"
37 | namespace: "external-secrets"
38 | syncPolicy:
39 | automated:
40 | prune: false
41 | selfHeal: true
42 | syncOptions:
43 | - CreateNamespace=true
44 | retry:
45 | limit: 5
46 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/applicationsets/security/sealed-secrets-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: sealed-secrets
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev
12 | values:
13 | branch: development
14 | - clusters:
15 | selector:
16 | matchLabels:
17 | env: prod
18 | values:
19 | branch: main
20 | template:
21 | metadata:
22 | name: "{{name}}-sealed-secrets"
23 | annotations:
24 | argocd.argoproj.io/manifest-generate-paths: ".;.."
25 | spec:
26 | project: default
27 | sources:
28 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
29 | targetRevision: "{{values.branch}}"
30 | path: "./chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/security/sealed-secrets"
31 | helm:
32 | releaseName: "sealed-secrets" # Release name override (defaults to application name)
33 | valueFiles:
34 | - "values.yaml"
35 | destination:
36 | name: "{{name}}"
37 | namespace: "sealed-secrets"
38 | syncPolicy:
39 | automated:
40 | prune: false
41 | selfHeal: true
42 | syncOptions:
43 | - CreateNamespace=true
44 | retry:
45 | limit: 5
46 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/kustomize/.gitexplodee:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Implementing-GitOps-with-Kubernetes/123557c4905e8677fc4a6807aee28e146dcc192e/chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/kustomize/.gitexplodee
--------------------------------------------------------------------------------
/chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/security/external-secrets/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: external-secrets
3 | version: 1.0.0
4 | description: This Chart deploys external-secrets secrets.
5 | dependencies:
6 | - name: external-secrets
7 | version: "0.9.13"
8 | repository: https://charts.external-secrets.io
9 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/security/external-secrets/values.yaml:
--------------------------------------------------------------------------------
1 | external-secrets:
2 | serviceMonitor:
3 | # -- Specifies whether to create a ServiceMonitor resource for collecting Prometheus metrics
4 | enabled: true
5 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/security/sealed-secrets/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: sealed-secrets
3 | version: 1.0.0
4 | description: This Chart deploys sealed secrets.
5 | dependencies:
6 | - name: sealed-secrets
7 | version: 2.15.0
8 | repository: https://bitnami-labs.github.io/sealed-secrets
9 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-committing-everything-to-git-and-what-about-secrets/security/sealed-secrets/values.yaml:
--------------------------------------------------------------------------------
1 | sealed-secrets:
2 | crd:
3 | create: false
4 | resources:
5 | requests:
6 | memory: 75Mi
7 | cpu: 75m
8 | limits:
9 | memory: 125Mi
10 | cpu: 250m
11 | podSecurityContext:
12 | seccompProfile:
13 | type: RuntimeDefault
14 | containerSecurityContext:
15 | seccompProfile:
16 | type: RuntimeDefault
17 | allowPrivilegeEscalation: false
18 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-hardening-declarative-gitops-cd-on-kubernetes /end_user_threat_model.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Implementing-GitOps-with-Kubernetes/123557c4905e8677fc4a6807aee28e146dcc192e/chapter13/chapter-13-hardening-declarative-gitops-cd-on-kubernetes /end_user_threat_model.pdf
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/applicationsets/security/kyverno-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: kyverno
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev
12 | values:
13 | branch: development
14 | - clusters:
15 | selector:
16 | matchLabels:
17 | env: prod
18 | values:
19 | branch: main
20 | template:
21 | metadata:
22 | name: "{{name}}-kyverno"
23 | annotations:
24 | argocd.argoproj.io/manifest-generate-paths: ".;.."
25 | spec:
26 | project: default
27 | sources:
28 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
29 | targetRevision: "{{values.branch}}"
30 | path: "./chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base"
31 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
32 | targetRevision: "{{values.branch}}"
33 | path: "./chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/security/kyverno"
34 | helm:
35 | releaseName: "kyverno"
36 | valueFiles:
37 | - "values.yaml"
38 | destination:
39 | name: "{{name}}"
40 | namespace: "kyverno"
41 | syncPolicy:
42 | automated:
43 | prune: false
44 | selfHeal: true
45 | syncOptions:
46 | - CreateNamespace=true
47 | retry:
48 | limit: 5
49 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/.gitexplodee:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Implementing-GitOps-with-Kubernetes/123557c4905e8677fc4a6807aee28e146dcc192e/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/.gitexplodee
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/argocd-application-field-validation.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: application-field-validation
6 | annotations:
7 | policies.kyverno.io/title: Application Field Validation
8 | policies.kyverno.io/category: Argo
9 | policies.kyverno.io/severity: medium
10 | policies.kyverno.io/subject: Application
11 | kyverno.io/kyverno-version: 1.6.0
12 | policies.kyverno.io/minversion: 1.6.0
13 | kyverno.io/kubernetes-version: "1.23"
14 | policies.kyverno.io/description: >-
15 | This policy performs some best practices validation on Application fields.
16 | Path or chart must be specified but never both. And destination.name or
17 | destination.server must be specified but never both.
18 | spec:
19 | validationFailureAction: Audit
20 | background: true
21 | rules:
22 | - name: source-path-chart
23 | match:
24 | any:
25 | - resources:
26 | kinds:
27 | - Application
28 | validate:
29 | message: >-
30 | `spec.source.path` OR `spec.source.chart` should be specified but never both.
31 | anyPattern:
32 | - spec:
33 | source:
34 | path: '?*'
35 | X(chart):
36 | - spec:
37 | source:
38 | X(path):
39 | chart: '?*'
40 | - name: destination-server-name
41 | match:
42 | any:
43 | - resources:
44 | kinds:
45 | - Application
46 | validate:
47 | message: >-
48 | `spec.destination.server` OR `spec.destination.name` should be specified but never both.
49 | anyPattern:
50 | - spec:
51 | destination:
52 | server: '?*'
53 | X(name):
54 | - spec:
55 | destination:
56 | X(server):
57 | name: '?*'
58 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/argocd-application-prevent-default-project.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: application-prevent-default-project
6 | annotations:
7 | policies.kyverno.io/title: Prevent Use of Default Project
8 | policies.kyverno.io/category: Argo
9 | policies.kyverno.io/severity: medium
10 | kyverno.io/kyverno-version: 1.6.2
11 | policies.kyverno.io/minversion: 1.6.0
12 | kyverno.io/kubernetes-version: "1.23"
13 | policies.kyverno.io/subject: Application
14 | policies.kyverno.io/description: >-
15 | This policy prevents the use of the default project in an Application.
16 | spec:
17 | validationFailureAction: Audit
18 | background: true
19 | rules:
20 | - name: default-project
21 | match:
22 | any:
23 | - resources:
24 | kinds:
25 | - Application
26 | preconditions:
27 | all:
28 | - key: "{{ request.operation || 'BACKGROUND' }}"
29 | operator: NotEquals
30 | value: DELETE
31 | validate:
32 | message: "The default project may not be used in an Application."
33 | pattern:
34 | spec:
35 | project: "!default"
36 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/argocd-application-prevent-updates-project.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: application-prevent-updates-project
6 | annotations:
7 | policies.kyverno.io/title: Prevent Updates to Project
8 | policies.kyverno.io/category: Argo
9 | policies.kyverno.io/severity: medium
10 | kyverno.io/kyverno-version: 1.6.2
11 | policies.kyverno.io/minversion: 1.6.0
12 | kyverno.io/kubernetes-version: "1.23"
13 | policies.kyverno.io/subject: Application
14 | policies.kyverno.io/description: >-
15 | This policy prevents updates to the project field after an Application is created.
16 | spec:
17 | validationFailureAction: Audit
18 | background: true
19 | rules:
20 | - name: project-updates
21 | match:
22 | any:
23 | - resources:
24 | kinds:
25 | - Application
26 | preconditions:
27 | all:
28 | - key: "{{ request.operation || 'BACKGROUND' }}"
29 | operator: Equals
30 | value: UPDATE
31 | validate:
32 | message: "The spec.project cannot be changed once the Application is created."
33 | deny:
34 | conditions:
35 | any:
36 | - key: "{{request.object.spec.project}}"
37 | operator: NotEquals
38 | value: "{{request.oldObject.spec.project}}"
39 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/disallow-container-sock-mounts.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: disallow-container-sock-mounts
6 | annotations:
7 | policies.kyverno.io/title: Disallow CRI socket mounts
8 | policies.kyverno.io/category: Best Practices, EKS Best Practices
9 | policies.kyverno.io/severity: medium
10 | policies.kyverno.io/subject: Pod
11 | policies.kyverno.io/minversion: 1.6.0
12 | policies.kyverno.io/description: >-
13 | Container daemon socket bind mounts allows access to the container engine on the
14 | node. This access can be used for privilege escalation and to manage containers
15 | outside of Kubernetes, and hence should not be allowed. This policy validates that
16 | the sockets used for CRI engines Docker, Containerd, and CRI-O are not used.
17 | spec:
18 | validationFailureAction: Audit
19 | background: true
20 | rules:
21 | - name: validate-docker-sock-mount
22 | match:
23 | any:
24 | - resources:
25 | kinds:
26 | - Pod
27 | validate:
28 | message: "Use of the Docker Unix socket is not allowed."
29 | pattern:
30 | spec:
31 | =(volumes):
32 | - =(hostPath):
33 | path: "!/var/run/docker.sock"
34 | - name: validate-containerd-sock-mount
35 | match:
36 | any:
37 | - resources:
38 | kinds:
39 | - Pod
40 | validate:
41 | message: "Use of the Containerd Unix socket is not allowed."
42 | pattern:
43 | spec:
44 | =(volumes):
45 | - =(hostPath):
46 | path: "!/var/run/containerd.sock"
47 | - name: validate-crio-sock-mount
48 | match:
49 | any:
50 | - resources:
51 | kinds:
52 | - Pod
53 | validate:
54 | message: "Use of the CRI-O Unix socket is not allowed."
55 | pattern:
56 | spec:
57 | =(volumes):
58 | - =(hostPath):
59 | path: "!/var/run/crio.sock"
60 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/disallow-empty-ingress-host.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: disallow-empty-ingress-host
6 | annotations:
7 | policies.kyverno.io/title: Disallow empty Ingress host
8 | policies.kyverno.io/category: Best Practices
9 | policies.kyverno.io/minversion: 1.6.0
10 | policies.kyverno.io/severity: medium
11 | policies.kyverno.io/subject: Ingress
12 | policies.kyverno.io/description: >-
13 | An ingress resource needs to define an actual host name
14 | in order to be valid. This policy ensures that there is a
15 | hostname for each rule defined.
16 | spec:
17 | validationFailureAction: Audit
18 | background: true
19 | rules:
20 | - name: disallow-empty-ingress-host
21 | match:
22 | any:
23 | - resources:
24 | kinds:
25 | - Ingress
26 | validate:
27 | message: "The Ingress host name must be defined, not empty."
28 | deny:
29 | conditions:
30 | all:
31 | - key: "{{ request.object.spec.rules[].host || `[]` | length(@) }}"
32 | operator: NotEquals
33 | value: "{{ request.object.spec.rules[].http || `[]` | length(@) }}"
34 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/disallow-latest-tag.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: disallow-latest-tag
6 | annotations:
7 | policies.kyverno.io/title: Disallow Latest Tag
8 | policies.kyverno.io/category: Best Practices
9 | policies.kyverno.io/minversion: 1.6.0
10 | policies.kyverno.io/severity: medium
11 | policies.kyverno.io/subject: Pod
12 | policies.kyverno.io/description: >-
13 | The ':latest' tag is mutable and can lead to unexpected errors if the
14 | image changes. A best practice is to use an immutable tag that maps to
15 | a specific version of an application Pod. This policy validates that the image
16 | specifies a tag and that it is not called `latest`.
17 | spec:
18 | validationFailureAction: Audit
19 | background: true
20 | rules:
21 | - name: require-image-tag
22 | match:
23 | any:
24 | - resources:
25 | kinds:
26 | - Pod
27 | validate:
28 | message: "An image tag is required."
29 | pattern:
30 | spec:
31 | containers:
32 | - image: "*:*"
33 | - name: validate-image-tag
34 | match:
35 | any:
36 | - resources:
37 | kinds:
38 | - Pod
39 | validate:
40 | message: "Using a mutable image tag e.g. 'latest' is not allowed."
41 | pattern:
42 | spec:
43 | containers:
44 | - image: "!*:latest"
45 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/drop-all-capabilities.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: drop-all-capabilities
6 | annotations:
7 | policies.kyverno.io/title: Drop All Capabilities
8 | policies.kyverno.io/category: Best Practices
9 | policies.kyverno.io/severity: medium
10 | policies.kyverno.io/minversion: 1.6.0
11 | policies.kyverno.io/subject: Pod
12 | policies.kyverno.io/description: >-
13 | Capabilities permit privileged actions without giving full root access. All
14 | capabilities should be dropped from a Pod, with only those required added back.
15 | This policy ensures that all containers explicitly specify the `drop: ["ALL"]`
16 | ability. Note that this policy also illustrates how to cover drop entries in any
17 | case although this may not strictly conform to the Pod Security Standards.
18 | spec:
19 | validationFailureAction: Audit
20 | background: true
21 | rules:
22 | - name: require-drop-all
23 | match:
24 | any:
25 | - resources:
26 | kinds:
27 | - Pod
28 | preconditions:
29 | all:
30 | - key: "{{ request.operation || 'BACKGROUND' }}"
31 | operator: NotEquals
32 | value: DELETE
33 | validate:
34 | message: >-
35 | Containers must drop `ALL` capabilities.
36 | foreach:
37 | - list: request.object.spec.[ephemeralContainers, initContainers, containers][]
38 | deny:
39 | conditions:
40 | all:
41 | - key: ALL
42 | operator: AnyNotIn
43 | value: "{{ element.securityContext.capabilities.drop[].to_upper(@) || `[]` }}"
44 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/drop-cap-net-raw.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: drop-cap-net-raw
6 | annotations:
7 | policies.kyverno.io/title: Drop CAP_NET_RAW
8 | policies.kyverno.io/category: Best Practices
9 | policies.kyverno.io/minversion: 1.6.0
10 | policies.kyverno.io/severity: medium
11 | policies.kyverno.io/subject: Pod
12 | policies.kyverno.io/description: >-
13 | Capabilities permit privileged actions without giving full root access. The
14 | CAP_NET_RAW capability, enabled by default, allows processes in a container to
15 | forge packets and bind to any interface potentially leading to MitM attacks.
16 | This policy ensures that all containers explicitly drop the CAP_NET_RAW
17 | ability. Note that this policy also illustrates how to cover drop entries in any
18 | case although this may not strictly conform to the Pod Security Standards.
19 | spec:
20 | validationFailureAction: Audit
21 | background: true
22 | rules:
23 | - name: require-drop-cap-net-raw
24 | match:
25 | any:
26 | - resources:
27 | kinds:
28 | - Pod
29 | preconditions:
30 | all:
31 | - key: "{{ request.operation || 'BACKGROUND' }}"
32 | operator: NotEquals
33 | value: DELETE
34 | validate:
35 | message: >-
36 | Containers must drop the `CAP_NET_RAW` capability.
37 | foreach:
38 | - list: request.object.spec.[ephemeralContainers, initContainers, containers][]
39 | deny:
40 | conditions:
41 | all:
42 | - key: CAP_NET_RAW
43 | operator: AnyNotIn
44 | value: "{{ element.securityContext.capabilities.drop[].to_upper(@) || `[]` }}"
45 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | resources:
4 | - argocd-application-field-validation.yaml
5 | - argocd-application-prevent-default-project.yaml
6 | - argocd-application-prevent-updates-project.yaml
7 | - disallow-container-sock-mounts.yaml
8 | - disallow-empty-ingress-host.yaml
9 | - disallow-latest-tag.yaml
10 | - drop-all-capabilities.yaml
11 | - drop-cap-net-raw.yaml
12 | - require-labels.yaml
13 | - require-pod-probes.yaml
14 | - require-requests-limits.yaml
15 | - require-ro-rootfs.yaml
16 | - restrict-deprecated-registry.yaml
17 | - restrict-nodeport.yaml
18 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/require-labels.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: require-labels
6 | annotations:
7 | policies.kyverno.io/title: Require Labels
8 | policies.kyverno.io/category: Best Practices
9 | policies.kyverno.io/minversion: 1.6.0
10 | policies.kyverno.io/severity: medium
11 | policies.kyverno.io/subject: Pod, Label
12 | policies.kyverno.io/description: >-
13 | Define and use labels that identify semantic attributes of your application or Deployment.
14 | A common set of labels allows tools to work collaboratively, describing objects in a common manner that
15 | all tools can understand. The recommended labels describe applications in a way that can be
16 | queried. This policy validates that the label `app.kubernetes.io/name` is specified with some value.
17 | spec:
18 | validationFailureAction: Audit
19 | background: true
20 | rules:
21 | - name: check-for-labels
22 | match:
23 | any:
24 | - resources:
25 | kinds:
26 | - Pod
27 | validate:
28 | message: "The label `app.kubernetes.io/name` is required."
29 | pattern:
30 | metadata:
31 | labels:
32 | app.kubernetes.io/name: "?*"
33 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/require-pod-probes.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: require-pod-probes
6 | annotations:
7 | pod-policies.kyverno.io/autogen-controllers: DaemonSet,Deployment,StatefulSet
8 | policies.kyverno.io/title: Require Pod Probes
9 | policies.kyverno.io/category: Best Practices, EKS Best Practices
10 | policies.kyverno.io/severity: medium
11 | policies.kyverno.io/subject: Pod
12 | policies.kyverno.io/description: >-
13 | Liveness and readiness probes need to be configured to correctly manage a Pod's
14 | lifecycle during deployments, restarts, and upgrades. For each Pod, a periodic
15 | `livenessProbe` is performed by the kubelet to determine if the Pod's containers
16 | are running or need to be restarted. A `readinessProbe` is used by Services
17 | and Deployments to determine if the Pod is ready to receive network traffic.
18 | This policy validates that all containers have one of livenessProbe, readinessProbe,
19 | or startupProbe defined.
20 | spec:
21 | validationFailureAction: Audit
22 | background: true
23 | rules:
24 | - name: validate-probes
25 | match:
26 | any:
27 | - resources:
28 | kinds:
29 | - Pod
30 | preconditions:
31 | all:
32 | - key: "{{request.operation || 'BACKGROUND'}}"
33 | operator: AnyIn
34 | value:
35 | - CREATE
36 | - UPDATE
37 | validate:
38 | message: "Liveness, readiness, or startup probes are required for all containers."
39 | foreach:
40 | - list: request.object.spec.containers[]
41 | deny:
42 | conditions:
43 | all:
44 | - key: livenessProbe
45 | operator: AllNotIn
46 | value: "{{ element.keys(@)[] }}"
47 | - key: startupProbe
48 | operator: AllNotIn
49 | value: "{{ element.keys(@)[] }}"
50 | - key: readinessProbe
51 | operator: AllNotIn
52 | value: "{{ element.keys(@)[] }}"
53 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/require-requests-limits.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: require-requests-limits
6 | annotations:
7 | policies.kyverno.io/title: Require Limits and Requests
8 | policies.kyverno.io/category: Best Practices, EKS Best Practices
9 | policies.kyverno.io/severity: medium
10 | policies.kyverno.io/subject: Pod
11 | policies.kyverno.io/minversion: 1.6.0
12 | policies.kyverno.io/description: >-
13 | As application workloads share cluster resources, it is important to limit resources
14 | requested and consumed by each Pod. It is recommended to require resource requests and
15 | limits per Pod, especially for memory and CPU. If a Namespace level request or limit is specified,
16 | defaults will automatically be applied to each Pod based on the LimitRange configuration.
17 | This policy validates that all containers have something specified for memory and CPU
18 | requests and memory limits.
19 | spec:
20 | validationFailureAction: Audit
21 | background: true
22 | rules:
23 | - name: validate-resources
24 | match:
25 | any:
26 | - resources:
27 | kinds:
28 | - Pod
29 | validate:
30 | message: "CPU and memory resource requests and limits are required."
31 | pattern:
32 | spec:
33 | containers:
34 | - resources:
35 | requests:
36 | memory: "?*"
37 | cpu: "?*"
38 | limits:
39 | memory: "?*"
40 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/require-ro-rootfs.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: require-ro-rootfs
6 | annotations:
7 | policies.kyverno.io/title: Require Read-Only Root Filesystem
8 | policies.kyverno.io/category: Best Practices, EKS Best Practices, PSP Migration
9 | policies.kyverno.io/severity: medium
10 | policies.kyverno.io/subject: Pod
11 | policies.kyverno.io/minversion: 1.6.0
12 | policies.kyverno.io/description: >-
13 | A read-only root file system helps to enforce an immutable infrastructure strategy;
14 | the container only needs to write on the mounted volume that persists the state.
15 | An immutable root filesystem can also prevent malicious binaries from writing to the
16 | host system. This policy validates that containers define a securityContext
17 | with `readOnlyRootFilesystem: true`.
18 | spec:
19 | validationFailureAction: Audit
20 | background: true
21 | rules:
22 | - name: validate-readOnlyRootFilesystem
23 | match:
24 | any:
25 | - resources:
26 | kinds:
27 | - Pod
28 | validate:
29 | message: "Root filesystem must be read-only."
30 | pattern:
31 | spec:
32 | containers:
33 | - securityContext:
34 | readOnlyRootFilesystem: true
35 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/restrict-deprecated-registry.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: restrict-deprecated-registry
6 | annotations:
7 | policies.kyverno.io/title: Restrict Deprecated Registry
8 | policies.kyverno.io/category: Best Practices, EKS Best Practices
9 | policies.kyverno.io/severity: high
10 | policies.kyverno.io/minversion: 1.9.0
11 | policies.kyverno.io/subject: Pod
12 | policies.kyverno.io/description: >-
13 | Legacy k8s.gcr.io container image registry will be frozen in early April 2023
14 | k8s.gcr.io image registry will be frozen from the 3rd of April 2023.
15 | Images for Kubernetes 1.27 will not be available in the k8s.gcr.io image registry.
16 | Please read our announcement for more details.
17 | https://kubernetes.io/blog/2023/02/06/k8s-gcr-io-freeze-announcement/
18 | spec:
19 | validationFailureAction: Audit
20 | # validationFailureAction: Audit
21 | background: true
22 | rules:
23 | - name: restrict-deprecated-registry
24 | match:
25 | any:
26 | - resources:
27 | kinds:
28 | - Pod
29 | validate:
30 | message: 'The "k8s.gcr.io" image registry is deprecated. "registry.k8s.io" should now be used.'
31 | foreach:
32 | - list: "request.object.spec.[initContainers, ephemeralContainers, containers][]"
33 | deny:
34 | conditions:
35 | all:
36 | - key: "{{ element.image }}"
37 | operator: Equals
38 | value: "k8s.gcr.io/*"
39 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/kustomize/security/kyverno/policies/base/restrict-nodeport.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: kyverno.io/v1
3 | kind: ClusterPolicy
4 | metadata:
5 | name: restrict-nodeport
6 | annotations:
7 | policies.kyverno.io/title: Disallow NodePort
8 | policies.kyverno.io/category: Best Practices
9 | policies.kyverno.io/minversion: 1.6.0
10 | policies.kyverno.io/severity: medium
11 | policies.kyverno.io/subject: Service
12 | policies.kyverno.io/description: >-
13 | A Kubernetes Service of type NodePort uses a host port to receive traffic from
14 | any source. A NetworkPolicy cannot be used to control traffic to host ports.
15 | Although NodePort Services can be useful, their use must be limited to Services
16 | with additional upstream security checks. This policy validates that any new Services
17 | do not use the `NodePort` type.
18 | spec:
19 | validationFailureAction: Audit
20 | background: true
21 | rules:
22 | - name: validate-nodeport
23 | match:
24 | any:
25 | - resources:
26 | kinds:
27 | - Service
28 | validate:
29 | message: "Services of type NodePort are not allowed."
30 | pattern:
31 | spec:
32 | =(type): "!NodePort"
33 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/security/kyverno/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: kyverno
3 | version: 1.0.0
4 | description: This Chart deploys kyverno.
5 | dependencies:
6 | - name: kyverno
7 | version: 3.1.4
8 | repository: https://kyverno.github.io/kyverno/
9 |
--------------------------------------------------------------------------------
/chapter13/chapter-13-leveraging-a-policy-engine-for-policy-as-code-practices/security/kyverno/values.yaml:
--------------------------------------------------------------------------------
1 | kyverno:
2 | # CRDs configuration
3 | crds:
4 | # -- Whether to have Helm install the Kyverno CRDs, if the CRDs are not installed by Helm, they must be added before policies can be created
5 | install: true
6 | annotations:
7 | argocd.argoproj.io/sync-options: Replace=true
8 | strategy.spinnaker.io/replace: "true"
9 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/applicationsets/optimization/opencost-applicationset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: ApplicationSet
3 | metadata:
4 | name: opencost
5 | namespace: argocd
6 | spec:
7 | generators:
8 | - clusters:
9 | selector:
10 | matchLabels:
11 | env: dev
12 | values:
13 | branch: development
14 | - clusters:
15 | selector:
16 | matchLabels:
17 | env: prod
18 | values:
19 | branch: main
20 | template:
21 | metadata:
22 | name: "{{name}}-opencost"
23 | annotations:
24 | argocd.argoproj.io/manifest-generate-paths: ".;.."
25 | spec:
26 | project: default
27 | sources:
28 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
29 | targetRevision: main
30 | ref: valuesRepo
31 | - repoURL: git@github.com:PacktPublishing/Implementing-GitOps-with-Kubernetes.git
32 | targetRevision: "{{values.branch}}"
33 | path: "./chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/opencost"
34 | helm:
35 | releaseName: "opencost"
36 | valueFiles:
37 | - "values.yaml"
38 | - $valuesRepo/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/cluster/{{name}}/optimization/opencost/values.yaml
39 | destination:
40 | name: "{{name}}"
41 | namespace: "opencost"
42 | syncPolicy:
43 | automated:
44 | prune: false
45 | selfHeal: true
46 | syncOptions:
47 | - CreateNamespace=true
48 | retry:
49 | limit: 5
50 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/cluster/in-cluster-austria/optimization/opencost/values.yaml:
--------------------------------------------------------------------------------
1 | opencost:
2 | opencost:
3 | customPricing:
4 | # -- Enables custom pricing for on-premise setup.
5 | enabled: true
6 | configmapName: custom-pricing-model
7 | # -- Path for the pricing configuration.
8 | configPath: /tmp/custom-config
9 | # -- Configures the pricing model provided in the values file.
10 | createConfigmap: true
11 | # -- More information about these values here: https://www.opencost.io/docs/configuration/on-prem#custom-pricing-using-the-opencost-helm-chart
12 | costModel:
13 | description: Modified prices based on your internal pricing
14 | CPU: 1.0
15 | spotCPU: 0.006900
16 | RAM: 0.75
17 | spotRAM: 0.000999
18 | GPU: 1.25
19 | storage: 0.2
20 | zoneNetworkEgress: 0.02
21 | regionNetworkEgress: 0.02
22 | internetNetworkEgress: 0.15
23 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/cluster/in-cluster-germany/optimization/opencost/values.yaml:
--------------------------------------------------------------------------------
1 | opencost:
2 | opencost:
3 | customPricing:
4 | # -- Enables custom pricing for on-premise setup.
5 | enabled: true
6 | configmapName: custom-pricing-model
7 | # -- Path for the pricing configuration.
8 | configPath: /tmp/custom-config
9 | # -- Configures the pricing model provided in the values file.
10 | createConfigmap: true
11 | # -- More information about these values here: https://www.opencost.io/docs/configuration/on-prem#custom-pricing-using-the-opencost-helm-chart
12 | costModel:
13 | description: Modified prices based on your internal pricing
14 | CPU: 1.25
15 | spotCPU: 0.006655
16 | RAM: 0.50
17 | spotRAM: 0.000892
18 | GPU: 0.95
19 | storage: 0.25
20 | zoneNetworkEgress: 0.01
21 | regionNetworkEgress: 0.01
22 | internetNetworkEgress: 0.12
23 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/cluster/in-cluster-ireland/optimization/opencost/values.yaml:
--------------------------------------------------------------------------------
1 | opencost:
2 | opencost:
3 | customPricing:
4 | # -- Enables custom pricing for on-premise setup.
5 | enabled: true
6 | configmapName: custom-pricing-model
7 | # -- Path for the pricing configuration.
8 | configPath: /tmp/custom-config
9 | # -- Configures the pricing model provided in the values file.
10 | createConfigmap: true
11 | # -- More information about these values here: https://www.opencost.io/docs/configuration/on-prem#custom-pricing-using-the-opencost-helm-chart
12 | costModel:
13 | description: Modified prices based on your internal pricing
14 | CPU: 1.5
15 | spotCPU: 0.01023
16 | RAM: 0.75
17 | spotRAM: 0.00103
18 | GPU: 0.8
19 | storage: 0.3
20 | zoneNetworkEgress: 0.05
21 | regionNetworkEgress: 0.05
22 | internetNetworkEgress: 0.10
23 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/kubecost/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: kubecost
3 | version: 1.0.0
4 | description: This Chart deploys kubecost.
5 | dependencies:
6 | - name: cost-analyzer
7 | version: 2.3.0
8 | repository: https://kubecost.github.io/cost-analyzer/
9 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/kubecost/README.md:
--------------------------------------------------------------------------------
1 | # Kubecost
2 |
3 | In this simple example, I will show you how to set an alert via the UI, configure a budget with an alert via the UI, and how to set it up through the `values.yaml` in the Helm Chart for deployment via tools like Argo CD.
4 |
5 | Let's consider a straightforward use case:
6 |
7 | - **Project Budget**: $100 per month for Project X.
8 | - **Cluster Budget**: $500 per month for the entire cluster.
9 | - **Notification**: If any budget is exceeded, a notification should be sent to the FinOps team members.
10 |
11 | The goal of the FinOps team is not only to track the project but also to respond promptly when costs are too high, understand these costs, and identify cost drivers without needing to be Kubernetes experts.
12 |
13 | ## Kubecost UI
14 |
15 | If you haven't deployed Kubecost yet, there's a demo environment available that allows you to configure and test it. You can access this environment at https://demo.kubecost.io/.
16 |
17 | We will use this demo environment to configure an alert.
18 |
19 | ### Setup Alert in the Kubecost UI
20 |
21 | To set up alerts in the Kubecost UI, follow these steps:
22 |
23 | 1. **Navigate to the Alerts Section**: Open the Kubecost UI and go to the Alerts section.
24 | 2. **Create a New Alert**: Click on the "+ Create Alert" button.
25 | 3. **Configure Alert Parameters**: Select the type of alert you want to set up (e.g., budget, efficiency). Define the parameters such as the time window, aggregation level, filter criteria, and threshold.
26 | 4. **Set Notification Channels**: Specify the notification channels, such as email, Slack, or Microsoft Teams, by providing the necessary webhook URLs and email addresses.
27 | 5. **Save the Alert**: Review the configuration and save the alert.
28 |
29 | Here is an example of how you can set up an alert for a alerts in the Kubecost UI:
30 |
31 | 
32 |
33 | ### Setup Budget + Alert in the Kubecost UI
34 |
35 | To set up a budget with an alert in the Kubecost UI:
36 |
37 | 1. **Navigate to the Budgets Section**: Open the Kubecost UI and go to the Budgets section.
38 | 2. **Create a New Budget**: Click on the "+ Create Budget" button.
39 | 3. **Define Budget Parameters**: Enter the budget amount, select the time window (e.g., daily, weekly, monthly), and choose the aggregation level (e.g., namespace, cluster).
40 | 4. **Add Alert for the Budget**: Link the budget to an alert by configuring the alert settings. Specify the threshold at which the alert should trigger.
41 | 5. **Set Notification Channels**: Provide email addresses and webhook URLs for Slack or Microsoft Teams to receive notifications.
42 | 6. **Save the Budget and Alert**: Review the setup and save both the budget and the alert.
43 |
44 | Here is an example of how you can set up an alert for a budget + alert in the Kubecost UI:
45 |
46 | 
47 |
48 | ## Kubecost Helm Chart
49 |
50 | Kubecost can be deployed using the Helm Chart, which allows you to configure alerts and budgets using the `values.yaml` file. This approach is useful for setting up alerts as code and deploying them using GitOps tools like Argo CD.
51 |
52 | ### Setup Alert over values.yaml
53 |
54 | To set up alerts using the `values.yaml` file in the Kubecost Helm Chart:
55 |
56 | 1. **Edit values.yaml**: Open the `values.yaml` file in your preferred text editor.
57 | 2. **Add Alert Configuration**: Insert the alert configuration under the `alerts` section. Below is an example configuration:
58 |
59 | ```yaml
60 | ---
61 | alerts:
62 | - type: budget
63 | threshold: 100
64 | window: 30d
65 | aggregation: namespace
66 | filter: project-x
67 | ownerContact:
68 | - owner@example.com
69 | slackWebhookUrl: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
70 | msTeamsWebhookUrl: https://xxxxx.webhook.office.com/webhookb2/XXXXXXXXXXXXXXXXXXXXXXXX/IncomingWebhook/XXXXXXXXXXXXXXXXXXXXXXXX
71 | - type: budget
72 | threshold: 500
73 | window: 30d
74 | aggregation: cluster
75 | filter: cluster-one
76 | ```
77 |
78 | 3. **Apply the Helm Chart**: Deploy or update the Helm chart with the modified `values.yaml` file by running the following command:
79 | ```sh
80 | helm upgrade --install kubecost kubecost/cost-analyzer -f values.yaml -n kubecost --create-namespace
81 | ```
82 | 4. **Verify Alerts**: After deployment, verify that the alerts are correctly set up by checking the Kubecost UI or using the `kubectl` command to inspect the configuration.
83 |
84 | This configuration helps the FinOps team monitor and control the spending across different namespaces and clusters, ensuring they stay within the defined budgets and can take action if spending exceeds the limits or understand the cost drivers.
85 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/kubecost/images/kubecost_ui_budget_setup_alert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Implementing-GitOps-with-Kubernetes/123557c4905e8677fc4a6807aee28e146dcc192e/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/kubecost/images/kubecost_ui_budget_setup_alert.gif
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/kubecost/images/kubecost_ui_setup_alert.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Implementing-GitOps-with-Kubernetes/123557c4905e8677fc4a6807aee28e146dcc192e/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/kubecost/images/kubecost_ui_setup_alert.gif
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/kubecost/values.yaml:
--------------------------------------------------------------------------------
1 | cost-analyzer:
2 | global:
3 | notifications:
4 | alertConfigs:
5 | frontendUrl: http://localhost:9090
6 | alerts:
7 | - type: budget
8 | threshold: 100
9 | window: 30d
10 | aggregation: namespace
11 | filter: projec-x
12 | ownerContact:
13 | - owner@example.com
14 | # optional, used for alert-specific Slack and Microsoft Teams alerts
15 | slackWebhookUrl: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXXHALLO12
16 | msTeamsWebhookUrl: https://xxxxx.webhook.office.com/webhookb2/XXXXXXXXXXXXXXXXXXXXXXXX/IncomingWebhook/XXXXXXXXXXXXXXXXXXXXXXXX
17 | - type: budget
18 | threshold: 500
19 | window: 30d
20 | aggregation: cluster
21 | filter: cluster-one
22 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/opencost/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: opencost
3 | version: 1.0.0
4 | description: This Chart deploys opencost.
5 | dependencies:
6 | - name: opencost
7 | version: 1.30.0
8 | repository: https://opencost.github.io/opencost-helm-chart
9 |
--------------------------------------------------------------------------------
/chapter14/chapter-14-forecasting-and-monitoring-costs-with-gitops/optimization/opencost/values.yaml:
--------------------------------------------------------------------------------
1 | opencost:
2 | opencost:
3 | serviceAccount:
4 | # -- Specifies whether a service account should be created
5 | create: true
6 |
--------------------------------------------------------------------------------
/pod-gitops-terraform-automation-tf-runner.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | creationTimestamp: "2024-04-20T14:49:50Z"
5 | labels:
6 | app.kubernetes.io/created-by: tf-controller
7 | app.kubernetes.io/instance: tf-runner-dbc4ede1
8 | app.kubernetes.io/name: tf-runner
9 | infra.contrib.fluxcd.io/terraform: flux-system
10 | tf.weave.works/tls-secret-name: terraform-runner.tls-1713710973
11 | name: gitops-terraform-automation-tf-runner
12 | namespace: flux-system
13 | resourceVersion: "22441"
14 | uid: b2010fda-ea85-465e-b93b-df1c0dedad7e
15 | spec:
16 | containers:
17 | - args:
18 | - --grpc-port
19 | - "30000"
20 | - --tls-secret-name
21 | - terraform-runner.tls-1713710973
22 | - --grpc-max-message-size
23 | - "30"
24 | env:
25 | - name: ARM_CLIENT_ID
26 | valueFrom:
27 | secretKeyRef:
28 | key: ARM_CLIENT_ID
29 | name: azure-creds
30 | - name: ARM_CLIENT_SECRET
31 | valueFrom:
32 | secretKeyRef:
33 | key: ARM_CLIENT_SECRET
34 | name: azure-creds
35 | - name: ARM_TENANT_ID
36 | valueFrom:
37 | secretKeyRef:
38 | key: ARM_TENANT_ID
39 | name: azure-creds
40 | - name: POD_NAME
41 | valueFrom:
42 | fieldRef:
43 | apiVersion: v1
44 | fieldPath: metadata.name
45 | - name: POD_NAMESPACE
46 | valueFrom:
47 | fieldRef:
48 | apiVersion: v1
49 | fieldPath: metadata.namespace
50 | - name: ARM_SUBSCRIPTION_ID
51 | valueFrom:
52 | secretKeyRef:
53 | key: ARM_SUBSCRIPTION_ID
54 | name: azure-creds
55 | image: mcr.microsoft.com/azure-cli
56 | imagePullPolicy: IfNotPresent
57 | name: tf-runner
58 | ports:
59 | - containerPort: 30000
60 | name: grpc
61 | protocol: TCP
62 | resources: {}
63 | securityContext:
64 | allowPrivilegeEscalation: false
65 | capabilities:
66 | drop:
67 | - ALL
68 | readOnlyRootFilesystem: true
69 | runAsNonRoot: true
70 | runAsUser: 65532
71 | seccompProfile:
72 | type: RuntimeDefault
73 | terminationMessagePath: /dev/termination-log
74 | terminationMessagePolicy: File
75 | volumeMounts:
76 | - mountPath: /tmp
77 | name: temp
78 | - mountPath: /home/runner
79 | name: home
80 | - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
81 | name: kube-api-access-6xcws
82 | readOnly: true
83 | dnsPolicy: ClusterFirst
84 | enableServiceLinks: true
85 | nodeName: local-flux-cluster
86 | preemptionPolicy: PreemptLowerPriority
87 | priority: 0
88 | restartPolicy: Always
89 | schedulerName: default-scheduler
90 | securityContext: {}
91 | serviceAccount: tf-runner
92 | serviceAccountName: tf-runner
93 | terminationGracePeriodSeconds: 30
94 | tolerations:
95 | - effect: NoExecute
96 | key: node.kubernetes.io/not-ready
97 | operator: Exists
98 | tolerationSeconds: 300
99 | - effect: NoExecute
100 | key: node.kubernetes.io/unreachable
101 | operator: Exists
102 | tolerationSeconds: 300
103 | volumes:
104 | - emptyDir: {}
105 | name: temp
106 | - emptyDir: {}
107 | name: home
108 | - name: kube-api-access-6xcws
109 | projected:
110 | defaultMode: 420
111 | sources:
112 | - serviceAccountToken:
113 | expirationSeconds: 3607
114 | path: token
115 | - configMap:
116 | items:
117 | - key: ca.crt
118 | path: ca.crt
119 | name: kube-root-ca.crt
120 | - downwardAPI:
121 | items:
122 | - fieldRef:
123 | apiVersion: v1
124 | fieldPath: metadata.namespace
125 | path: namespace
126 | status:
127 | conditions:
128 | - lastProbeTime: null
129 | lastTransitionTime: "2024-04-20T14:49:52Z"
130 | status: "True"
131 | type: PodReadyToStartContainers
132 | - lastProbeTime: null
133 | lastTransitionTime: "2024-04-20T14:49:50Z"
134 | status: "True"
135 | type: Initialized
136 | - lastProbeTime: null
137 | lastTransitionTime: "2024-04-20T14:49:50Z"
138 | message: 'containers with unready status: [tf-runner]'
139 | reason: ContainersNotReady
140 | status: "False"
141 | type: Ready
142 | - lastProbeTime: null
143 | lastTransitionTime: "2024-04-20T14:49:50Z"
144 | message: 'containers with unready status: [tf-runner]'
145 | reason: ContainersNotReady
146 | status: "False"
147 | type: ContainersReady
148 | - lastProbeTime: null
149 | lastTransitionTime: "2024-04-20T14:49:50Z"
150 | status: "True"
151 | type: PodScheduled
152 | containerStatuses:
153 | - containerID: docker://8304f5f9eb98b020d2e9ad3e1a59e8581ef2127ed67ae9eb6d9d6c714caab916
154 | image: mcr.microsoft.com/azure-cli:latest
155 | imageID: docker-pullable://mcr.microsoft.com/azure-cli@sha256:eb21a975e39a46f49b86ab517ea2916e146560c7844d0c488cf85eee8a38398d
156 | lastState:
157 | terminated:
158 | containerID: docker://8304f5f9eb98b020d2e9ad3e1a59e8581ef2127ed67ae9eb6d9d6c714caab916
159 | exitCode: 127
160 | finishedAt: "2024-04-20T14:51:28Z"
161 | message: 'failed to create task for container: failed to create shim task:
162 | OCI runtime create failed: runc create failed: unable to start container
163 | process: exec: "--grpc-port": executable file not found in $PATH: unknown'
164 | reason: ContainerCannotRun
165 | startedAt: "2024-04-20T14:51:28Z"
166 | name: tf-runner
167 | ready: false
168 | restartCount: 4
169 | started: false
170 | state:
171 | waiting:
172 | message: back-off 1m20s restarting failed container=tf-runner pod=gitops-terraform-automation-tf-runner_flux-system(b2010fda-ea85-465e-b93b-df1c0dedad7e)
173 | reason: CrashLoopBackOff
174 | hostIP: 192.168.58.2
175 | hostIPs:
176 | - ip: 192.168.58.2
177 | phase: Running
178 | podIP: 10.244.0.25
179 | podIPs:
180 | - ip: 10.244.0.25
181 | qosClass: BestEffort
182 | startTime: "2024-04-20T14:49:50Z"
183 |
--------------------------------------------------------------------------------