├── .gitignore
├── src
├── main
│ ├── java
│ │ └── pl
│ │ │ └── piomin
│ │ │ └── samples
│ │ │ └── springboot
│ │ │ └── kubernetes
│ │ │ ├── domain
│ │ │ ├── Gender.java
│ │ │ ├── PersonV2.java
│ │ │ └── Person.java
│ │ │ ├── repository
│ │ │ └── PersonRepository.java
│ │ │ ├── service
│ │ │ └── PersonService.java
│ │ │ ├── SpringBootOnKubernetesApp.java
│ │ │ └── controller
│ │ │ └── PersonController.java
│ └── resources
│ │ └── application.yml
└── test
│ ├── resources
│ ├── application.yml
│ └── k6
│ │ ├── load-tests-get-by-age.js
│ │ └── load-tests-add.js
│ └── java
│ └── pl
│ └── piomin
│ └── samples
│ └── springboot
│ └── kubernetes
│ ├── SpringBootOnKubernetesAppTest.java
│ ├── MongoDBContainerDevMode.java
│ └── PersonControllerTest.java
├── k8s
├── jenkins-agent-pvc.yaml
├── kind-cluster-test.yaml
├── gitlab-serviceaccount.yaml
├── hpa.yaml
├── load-tests.js
├── helm-config.yaml
├── gitlab.yaml
├── knative-service.yaml
├── deployment-template.yaml
├── mongodb-deployment.yaml
├── deployment.yaml
└── jenkins-helm-config.yaml
├── okteto.yml
├── Tiltfile
├── renovate.json
├── .m2
└── settings.xml
├── Dockerfile
├── .gitlab-ci.yml
├── Jenkinsfile
├── skaffold.yaml
├── readme.md
├── .circleci
└── config.yml
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project exclude paths
2 | /target/
3 | .odo/env
4 | .odo/odo-file-index.json
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/kubernetes/domain/Gender.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes.domain;
2 |
3 | public enum Gender {
4 |
5 | MALE, FEMALE;
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/k8s/jenkins-agent-pvc.yaml:
--------------------------------------------------------------------------------
1 | kind: PersistentVolumeClaim
2 | apiVersion: v1
3 | metadata:
4 | name: jenkins-agent
5 | spec:
6 | accessModes:
7 | - ReadWriteMany
8 | resources:
9 | requests:
10 | storage: 2Gi
11 | storageClassName: hostpath
--------------------------------------------------------------------------------
/k8s/kind-cluster-test.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kind.x-k8s.io/v1alpha4
2 | kind: Cluster
3 | nodes:
4 | - role: control-plane
5 | extraPortMappings:
6 | - containerPort: 30000
7 | hostPort: 30000
8 | listenAddress: "0.0.0.0"
9 | protocol: tcp
--------------------------------------------------------------------------------
/okteto.yml:
--------------------------------------------------------------------------------
1 | name: sample-spring-boot-on-kubernetes
2 | image: okteto/maven
3 | command: ["mvn", "spring-boot:run" ]
4 | workdir: /app
5 | environment:
6 | - MONGO_USERNAME=okteto
7 | - MONGO_DATABASE=okteto
8 | - MONGO_PASSWORD=okteto
9 | forward:
10 | - 8080:8080
11 |
--------------------------------------------------------------------------------
/Tiltfile:
--------------------------------------------------------------------------------
1 | custom_build('piomin/sample-spring-boot-on-kubernetes',
2 | 'mvn package jib:dockerBuild -Pjib -Djib.to.image=$EXPECTED_REF',
3 | deps=['src'])
4 | k8s_yaml(['k8s/mongodb-deployment.yaml', 'k8s/deployment.yaml'])
5 | k8s_resource('sample-spring-boot-on-kubernetes-deployment',
6 | port_forwards=8000)
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base",":dependencyDashboard"
5 | ],
6 | "packageRules": [
7 | {
8 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
9 | "automerge": true
10 | }
11 | ],
12 | "prCreation": "not-pending"
13 | }
--------------------------------------------------------------------------------
/.m2/settings.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | registry-1.docker.io
6 | ${DOCKER_LOGIN}
7 | ${DOCKER_PASSWORD}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/test/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: sample-spring-boot-on-kubernetes
4 | data:
5 | mongodb:
6 | uri: mongodb://localhost:27071/test
7 | logging:
8 | pattern:
9 | console: "%d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n"
10 |
11 | management:
12 | endpoints:
13 | web:
14 | exposure:
15 | include: "*"
16 | endpoint:
17 | health:
18 | show-details: ALWAYS
--------------------------------------------------------------------------------
/k8s/gitlab-serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: gitlab
5 | namespace: kube-system
6 | ---
7 | apiVersion: rbac.authorization.k8s.io/v1beta1
8 | kind: ClusterRoleBinding
9 | metadata:
10 | name: gitlab-admin
11 | roleRef:
12 | apiGroup: rbac.authorization.k8s.io
13 | kind: ClusterRole
14 | name: cluster-admin
15 | subjects:
16 | - kind: ServiceAccount
17 | name: gitlab
18 | namespace: kube-system
19 |
--------------------------------------------------------------------------------
/src/test/resources/k6/load-tests-get-by-age.js:
--------------------------------------------------------------------------------
1 | import http from 'k6/http';
2 | import { check } from 'k6';
3 | import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
4 |
5 | export default function () {
6 | const age = randomIntBetween(10, 50);
7 | const res = http.get(`${__ENV.PERSONS_URI}/persons/age/${age}`);
8 | check(res, {
9 | 'is status 200': (res) => res.status === 200,
10 | 'body size is > 0': (r) => r.body.length > 0,
11 | });
12 | }
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/kubernetes/SpringBootOnKubernetesAppTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes;
2 |
3 | import org.springframework.boot.SpringApplication;
4 |
5 | public class SpringBootOnKubernetesAppTest {
6 |
7 | public static void main(String[] args) {
8 | SpringApplication.from(SpringBootOnKubernetesApp::main)
9 | .with(MongoDBContainerDevMode.class)
10 | .run(args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM maven:3.8.6-openjdk-11 as build
2 | WORKDIR /workspace/app
3 |
4 | COPY pom.xml .
5 |
6 | RUN mvn -B -e -C -T 1C org.apache.maven.plugins:maven-dependency-plugin:3.0.2:go-offline
7 |
8 | COPY . .
9 | RUN mvn clean package -Dmaven.test.skip=true
10 |
11 |
12 | FROM openjdk:21-buster
13 | VOLUME /tmp
14 | ARG DEPENDENCY=/workspace/app/target/dependency
15 | COPY --from=build /workspace/app/target/sample-spring-boot-on-kubernetes-1.3-SNAPSHOT.jar app.jar
16 | ENTRYPOINT ["java","-jar", "app.jar"]
--------------------------------------------------------------------------------
/k8s/hpa.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: autoscaling/v2beta2
2 | kind: HorizontalPodAutoscaler
3 | metadata:
4 | name: sample-hpa
5 | namespace: default
6 | spec:
7 | scaleTargetRef:
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | name: sample-spring-boot-on-kubernetes-deployment
11 | minReplicas: 1
12 | maxReplicas: 10
13 | metrics:
14 | - type: Pods
15 | pods:
16 | metric:
17 | name: http_server_requests_seconds_count_sum
18 | target:
19 | type: AverageValue
20 | averageValue: 100
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/kubernetes/repository/PersonRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes.repository;
2 |
3 | import java.util.Set;
4 |
5 | import pl.piomin.samples.springboot.kubernetes.domain.Person;
6 |
7 | import org.springframework.data.repository.CrudRepository;
8 |
9 | public interface PersonRepository extends CrudRepository {
10 |
11 | Set findByFirstNameAndLastName(String firstName, String lastName);
12 | Set findByAge(int age);
13 | Set findByAgeGreaterThan(int age);
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/k8s/load-tests.js:
--------------------------------------------------------------------------------
1 | import http from 'k6/http';
2 | import { check } from 'k6';
3 |
4 | export default function () {
5 |
6 | const payload = JSON.stringify({
7 | firstName: 'aaa',
8 | lastName: 'bbb',
9 | age: 50,
10 | gender: 'MALE'
11 | });
12 |
13 | const params = {
14 | headers: {
15 | 'Content-Type': 'application/json',
16 | },
17 | };
18 |
19 | const res = http.post(`http://localhost:8080/persons`, payload, params);
20 |
21 | check(res, {
22 | 'is status 200': (res) => res.status === 200,
23 | 'body size is > 0': (r) => r.body.length > 0,
24 | });
25 | }
--------------------------------------------------------------------------------
/k8s/helm-config.yaml:
--------------------------------------------------------------------------------
1 | prometheus:
2 | url: http://prometheus-server.default.svc
3 | port: 80
4 | path: ""
5 |
6 | rules:
7 | default: true
8 | custom:
9 | - seriesQuery: '{__name__=~"^http_server_requests_seconds_.*"}'
10 | resources:
11 | overrides:
12 | kubernetes_namespace:
13 | resource: namespace
14 | kubernetes_pod_name:
15 | resource: pod
16 | name:
17 | matches: "^http_server_requests_seconds_count(.*)"
18 | as: "http_server_requests_seconds_count_sum"
19 | metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>,uri=~"/persons.*"}) by (<<.GroupBy>>)
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/kubernetes/MongoDBContainerDevMode.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes;
2 |
3 | import org.springframework.boot.devtools.restart.RestartScope;
4 | import org.springframework.boot.test.context.TestConfiguration;
5 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
6 | import org.springframework.context.annotation.Bean;
7 | import org.testcontainers.containers.MongoDBContainer;
8 |
9 | @TestConfiguration
10 | public class MongoDBContainerDevMode {
11 |
12 | @Bean
13 | @ServiceConnection
14 | @RestartScope
15 | MongoDBContainer mongoDBContainer() {
16 | return new MongoDBContainer("mongo:5.0");
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: maven:latest
2 |
3 | stages:
4 | - build
5 | - test
6 | - image-build
7 | - deploy-tb
8 | - deploy-prod
9 |
10 | build:
11 | stage: build
12 | script:
13 | - mvn compile
14 |
15 | test:
16 | stage: test
17 | script:
18 | - mvn test
19 |
20 | image-build:
21 | stage: image-build
22 | script:
23 | - mvn -s .m2/settings.xml -P jib compile jib:build
24 |
25 | deploy-tb:
26 | image: bitnami/kubectl:latest
27 | stage: deploy
28 | only:
29 | - master
30 | script:
31 | - kubectl apply -f k8s/deployment.yaml -n test
32 |
33 | deploy-prod:
34 | image: bitnami/kubectl:latest
35 | stage: deploy
36 | only:
37 | - master
38 | when: manual
39 | script:
40 | - kubectl apply -f k8s/deployment.yaml -n prod
--------------------------------------------------------------------------------
/src/test/resources/k6/load-tests-add.js:
--------------------------------------------------------------------------------
1 | import http from 'k6/http';
2 | import { check } from 'k6';
3 | import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
4 | import { randomString } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
5 |
6 | export const options = {
7 | duration: '60s',
8 | vus: 5,
9 | thresholds: {
10 | http_req_failed: ['rate<0.25'],
11 | http_req_duration: ['p(95)<1000'],
12 | },
13 | };
14 |
15 | export default function () {
16 |
17 | const payload = JSON.stringify({
18 | firstName: randomString(6),
19 | lastName: randomString(6),
20 | age: randomIntBetween(10, 50),
21 | gender: 'MALE'
22 | });
23 |
24 | const params = {
25 | headers: {
26 | 'Content-Type': 'application/json',
27 | },
28 | };
29 |
30 | const res = http.post(`${__ENV.PERSONS_URI}/persons`, payload, params);
31 |
32 | check(res, {
33 | 'is status 200': (res) => res.status === 200,
34 | 'body size is > 0': (r) => r.body.length > 0,
35 | });
36 | }
--------------------------------------------------------------------------------
/k8s/gitlab.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: gitlab-deployment
5 | spec:
6 | selector:
7 | matchLabels:
8 | app: gitlab
9 | template:
10 | metadata:
11 | labels:
12 | app: gitlab
13 | spec:
14 | containers:
15 | - name: gitlab
16 | image: gitlab/gitlab-ee
17 | env:
18 | - name: GITLAB_OMNIBUS_CONFIG
19 | value: "external_url 'http://gitlab-service.default/';gitlab_rails['registry_enabled'] = true;gitlab_rails['registry_api_url'] = \"http://172.17.0.2:5000\""
20 | ports:
21 | - containerPort: 80
22 | name: http
23 | volumeMounts:
24 | - mountPath: /var/opt/gitlab
25 | name: data
26 | volumes:
27 | - name: data
28 | emptyDir: {}
29 | ---
30 | apiVersion: v1
31 | kind: Service
32 | metadata:
33 | name: gitlab-service
34 | spec:
35 | type: NodePort
36 | selector:
37 | app: gitlab
38 | ports:
39 | - port: 80
40 | targetPort: 80
41 | name: http
42 |
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: sample-spring-boot-on-kubernetes
4 | data:
5 | mongodb:
6 | host: ${MONGO_URL}
7 | port: 27017
8 | username: ${MONGO_USERNAME}
9 | password: ${MONGO_PASSWORD}
10 | database: ${MONGO_DATABASE}
11 | authentication-database: admin
12 | #logging:
13 | # pattern:
14 | # console: "%d{yyyy-MM-dd HH:mm:ss} ${LOG_LEVEL_PATTERN:-%5p} %m%n"
15 |
16 | management:
17 | endpoints:
18 | web:
19 | exposure:
20 | include: "*"
21 | endpoint.health:
22 | show-details: always
23 | group:
24 | readiness:
25 | include: mongo
26 | additional-path: server:/readiness
27 | probes:
28 | enabled: true
29 | server:
30 | port: 8081
31 |
32 | #management.endpoint.health.group.live.additional-path="server:/healthz"
33 |
34 | spring.output.ansi.enabled: ALWAYS
35 | #logging.pattern.console: "%clr(%d{HH:mm:ss.SSS}){blue} %clr(%5p) %clr(---){cyan} %clr([%15.15t]){yellow} %clr(:){red} %clr(%m){magenta}%n"
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/kubernetes/service/PersonService.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes.service;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 |
6 | import pl.piomin.samples.springboot.kubernetes.domain.Person;
7 | import pl.piomin.samples.springboot.kubernetes.repository.PersonRepository;
8 |
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.context.ApplicationEventPublisher;
11 | import org.springframework.stereotype.Service;
12 | import org.springframework.transaction.annotation.Transactional;
13 |
14 | @Service
15 | public class PersonService {
16 |
17 | @Autowired
18 | PersonRepository repository;
19 | @Autowired
20 | ApplicationEventPublisher applicationEventPublisher;
21 |
22 | @Transactional
23 | public Set doIt(Person... persons) {
24 | Set personSet = new HashSet<>();
25 | for (Person p: persons) {
26 | personSet.add(repository.save(p));
27 | }
28 | // applicationEventPublisher.publishEvent(new PersonAddEvent());
29 | return personSet;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/k8s/knative-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: serving.knative.dev/v1
2 | kind: Service
3 | metadata:
4 | annotations:
5 | autoscaling.knative.dev/target: "20"
6 | autoscaling.knative.dev/metric: "rps"
7 | name: sample-spring-boot-on-kubernetes
8 | spec:
9 | template:
10 | spec:
11 | containers:
12 | - image: piomin/sample-spring-boot-on-kubernetes
13 | livenessProbe:
14 | httpGet:
15 | path: /actuator/health/liveness
16 | readinessProbe:
17 | httpGet:
18 | path: /actuator/health/readiness
19 | env:
20 | - name: MONGO_DATABASE
21 | valueFrom:
22 | configMapKeyRef:
23 | name: mongodb
24 | key: database-name
25 | - name: MONGO_USERNAME
26 | valueFrom:
27 | secretKeyRef:
28 | name: mongodb
29 | key: database-user
30 | - name: MONGO_PASSWORD
31 | valueFrom:
32 | secretKeyRef:
33 | name: mongodb
34 | key: database-password
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label "default"
4 | }
5 | stages {
6 | stage('Checkout') {
7 | steps {
8 | script {
9 | git url: 'https://github.com/piomin/sample-spring-boot-on-kubernetes.git', credentialsId: 'github_credentials'
10 | sh 'ls -la'
11 | }
12 | }
13 | }
14 | stage('Build') {
15 | agent {
16 | label "maven"
17 | }
18 | steps {
19 | sh 'ls -la'
20 | sh 'mvn -version'
21 | sh 'mvn clean compile'
22 | }
23 | }
24 | stage('Test') {
25 | agent {
26 | label "maven"
27 | }
28 | steps {
29 | sh 'mvn test'
30 | }
31 | }
32 | stage('Image') {
33 | agent {
34 | label "maven"
35 | }
36 | steps {
37 | sh 'mvn -P jib -Djib.to.auth.username=piomin -Djib.to.auth.password=Piot_123 compile jib:build'
38 | }
39 | }
40 | stage('Deploy on test') {
41 | steps {
42 | script {
43 | env.PIPELINE_NAMESPACE = "test"
44 | kubernetesDeploy kubeconfigId: 'docker-desktop', configs: 'k8s/deployment-template.yaml'
45 | }
46 | }
47 | }
48 | stage('Deploy on prod') {
49 | steps {
50 | script {
51 | env.PIPELINE_NAMESPACE = "prod"
52 | kubernetesDeploy kubeconfigId: 'docker-desktop', configs: 'k8s/deployment-template.yaml'
53 | }
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/kubernetes/domain/PersonV2.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes.domain;
2 |
3 | import org.springframework.data.annotation.Id;
4 | import org.springframework.data.mongodb.core.mapping.Document;
5 |
6 | @Document(collection = "person")
7 | public class PersonV2 {
8 |
9 | @Id
10 | private String id;
11 | private String name;
12 | private int age;
13 | private Gender gender;
14 |
15 | public PersonV2() {
16 | }
17 |
18 | public PersonV2(String id, String name, int age, Gender gender) {
19 | this.id = id;
20 | this.name = name;
21 | this.age = age;
22 | this.gender = gender;
23 | }
24 |
25 | public String getId() {
26 | return id;
27 | }
28 |
29 | public void setId(String id) {
30 | this.id = id;
31 | }
32 |
33 | public String getName() {
34 | return name;
35 | }
36 |
37 | public void setName(String name) {
38 | this.name = name;
39 | }
40 |
41 | public int getAge() {
42 | return age;
43 | }
44 |
45 | public void setAge(int age) {
46 | this.age = age;
47 | }
48 |
49 | public Gender getGender() {
50 | return gender;
51 | }
52 |
53 | public void setGender(Gender gender) {
54 | this.gender = gender;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/skaffold.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: skaffold/v2beta22
2 | kind: Config
3 | metadata:
4 | name: sample-spring-boot-on-kubernetes
5 | build:
6 | artifacts:
7 | - image: piomin/sample-spring-boot-on-kubernetes
8 | jib:
9 | args:
10 | - -Pjib
11 | - -DskipTests
12 | deploy:
13 | kubectl:
14 | manifests:
15 | - k8s/mongodb-deployment.yaml
16 | - k8s/deployment.yaml
17 | profiles:
18 | - name: knative
19 | deploy:
20 | kubectl:
21 | manifests:
22 | - k8s/knative-service.yaml
23 | - name: buildpacks
24 | build:
25 | artifacts:
26 | - image: piomin/sample-spring-boot-on-kubernetes
27 | buildpacks:
28 | builder: paketobuildpacks/builder:base
29 | # builder: gcr.io/buildpacks/builder:v1
30 | # builder: heroku/buildpacks:20
31 | buildpacks:
32 | # - paketo-buildpacks/eclipse-openj9
33 | # - paketo-buildpacks/amazon-corretto
34 | # - paketo-buildpacks/oracle
35 | - paketo-buildpacks/adoptium
36 | # - paketo-buildpacks/azul-zulu
37 | # - paketo-buildpacks/alibaba-dragonwell
38 | # - paketo-buildpacks/microsoft-openjdk
39 | - paketo-buildpacks/java
40 | env:
41 | - BP_JVM_VERSION=17
42 | # - GOOGLE_RUNTIME_VERSION=17
43 | # tagPolicy:
44 | # envTemplate:
45 | # template: "{{.VENDOR}}"
46 |
47 |
48 |
--------------------------------------------------------------------------------
/k8s/deployment-template.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: sample-spring-boot-on-kubernetes-deployment
5 | namespace: ${PIPELINE_NAMESPACE}
6 | spec:
7 | selector:
8 | matchLabels:
9 | app: sample-spring-boot-on-kubernetes
10 | template:
11 | metadata:
12 | annotations:
13 | prometheus.io/path: /actuator/prometheus
14 | prometheus.io/scrape: "true"
15 | prometheus.io/port: "8080"
16 | labels:
17 | app: sample-spring-boot-on-kubernetes
18 | spec:
19 | containers:
20 | - name: sample-spring-boot-on-kubernetes
21 | image: piomin/sample-spring-boot-on-kubernetes
22 | ports:
23 | - containerPort: 8080
24 | env:
25 | - name: MONGO_DATABASE
26 | valueFrom:
27 | configMapKeyRef:
28 | name: mongodb
29 | key: database-name
30 | - name: MONGO_USERNAME
31 | valueFrom:
32 | secretKeyRef:
33 | name: mongodb
34 | key: database-user
35 | - name: MONGO_PASSWORD
36 | valueFrom:
37 | secretKeyRef:
38 | name: mongodb
39 | key: database-password
40 | ---
41 | apiVersion: v1
42 | kind: Service
43 | metadata:
44 | name: sample-spring-boot-on-kubernetes-service
45 | namespace: ${PIPELINE_NAMESPACE}
46 | spec:
47 | type: NodePort
48 | selector:
49 | app: sample-spring-boot-on-kubernetes
50 | ports:
51 | - port: 8080
52 |
--------------------------------------------------------------------------------
/k8s/mongodb-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: mongodb
5 | data:
6 | database-name: admin
7 | ---
8 | apiVersion: v1
9 | kind: Secret
10 | metadata:
11 | name: mongodb
12 | type: Opaque
13 | data:
14 | database-password: dGVzdDEyMw==
15 | database-user: dGVzdA==
16 | ---
17 | apiVersion: apps/v1
18 | kind: Deployment
19 | metadata:
20 | name: mongodb
21 | labels:
22 | app: mongodb
23 | spec:
24 | replicas: 1
25 | selector:
26 | matchLabels:
27 | app: mongodb
28 | template:
29 | metadata:
30 | labels:
31 | app: mongodb
32 | spec:
33 | containers:
34 | - name: mongodb
35 | image: mongo:5.0
36 | ports:
37 | - containerPort: 27017
38 | env:
39 | - name: MONGO_INITDB_DATABASE
40 | valueFrom:
41 | configMapKeyRef:
42 | name: mongodb
43 | key: database-name
44 | - name: MONGO_INITDB_ROOT_USERNAME
45 | valueFrom:
46 | secretKeyRef:
47 | name: mongodb
48 | key: database-user
49 | - name: MONGO_INITDB_ROOT_PASSWORD
50 | valueFrom:
51 | secretKeyRef:
52 | name: mongodb
53 | key: database-password
54 | ---
55 | apiVersion: v1
56 | kind: Service
57 | metadata:
58 | name: mongodb
59 | labels:
60 | app: mongodb
61 | spec:
62 | ports:
63 | - port: 27017
64 | protocol: TCP
65 | selector:
66 | app: mongodb
67 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/kubernetes/domain/Person.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes.domain;
2 |
3 | import org.springframework.data.annotation.Id;
4 | import org.springframework.data.mongodb.core.mapping.Document;
5 |
6 | @Document(collection = "person")
7 | public class Person {
8 |
9 | @Id
10 | private String id;
11 | private String firstName;
12 | private String lastName;
13 | private int age;
14 | private Gender gender;
15 |
16 | public Person() {
17 | }
18 |
19 | public Person(String id, String firstName, String lastName, int age, Gender gender) {
20 | this.id = id;
21 | this.firstName = firstName;
22 | this.lastName = lastName;
23 | this.age = age;
24 | this.gender = gender;
25 | }
26 |
27 | public String getId() {
28 | return id;
29 | }
30 |
31 | public void setId(String id) {
32 | this.id = id;
33 | }
34 |
35 | public String getFirstName() {
36 | return firstName;
37 | }
38 |
39 | public void setFirstName(String firstName) {
40 | this.firstName = firstName;
41 | }
42 |
43 | public String getLastName() {
44 | return lastName;
45 | }
46 |
47 | public void setLastName(String lastName) {
48 | this.lastName = lastName;
49 | }
50 |
51 | public int getAge() {
52 | return age;
53 | }
54 |
55 | public void setAge(int age) {
56 | this.age = age;
57 | }
58 |
59 | public Gender getGender() {
60 | return gender;
61 | }
62 |
63 | public void setGender(Gender gender) {
64 | this.gender = gender;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/kubernetes/SpringBootOnKubernetesApp.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.boot.context.event.ApplicationReadyEvent;
7 | import org.springframework.context.ApplicationListener;
8 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
9 | import pl.piomin.samples.springboot.kubernetes.domain.Gender;
10 | import pl.piomin.samples.springboot.kubernetes.domain.Person;
11 | import pl.piomin.samples.springboot.kubernetes.repository.PersonRepository;
12 |
13 | @SpringBootApplication
14 | @EnableMongoRepositories
15 | public class SpringBootOnKubernetesApp implements ApplicationListener {
16 |
17 | public static void main(String[] args) {
18 | SpringApplication.run(SpringBootOnKubernetesApp.class, args);
19 | }
20 |
21 | @Autowired
22 | PersonRepository repository;
23 |
24 | @Override
25 | public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
26 | if (repository.count() == 0) {
27 | repository.save(new Person(null, "XXX", "FFF", 20, Gender.MALE));
28 | repository.save(new Person(null, "AAA", "EEE", 30, Gender.MALE));
29 | repository.save(new Person(null, "ZZZ", "DDD", 40, Gender.FEMALE));
30 | repository.save(new Person(null, "BBB", "CCC", 50, Gender.MALE));
31 | repository.save(new Person(null, "YYY", "JJJ", 60, Gender.FEMALE));
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/k8s/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: sample-spring-boot-on-kubernetes-deployment
5 | spec:
6 | selector:
7 | matchLabels:
8 | app: sample-spring-boot-on-kubernetes
9 | template:
10 | metadata:
11 | annotations:
12 | prometheus.io/path: /actuator/prometheus
13 | prometheus.io/scrape: "true"
14 | prometheus.io/port: "8080"
15 | labels:
16 | app: sample-spring-boot-on-kubernetes
17 | spec:
18 | containers:
19 | - name: sample-spring-boot-on-kubernetes
20 | image: piomin/sample-spring-boot-on-kubernetes
21 | ports:
22 | - containerPort: 8080
23 | env:
24 | - name: MONGO_DATABASE
25 | valueFrom:
26 | configMapKeyRef:
27 | name: mongodb
28 | key: database-name
29 | - name: MONGO_USERNAME
30 | valueFrom:
31 | secretKeyRef:
32 | name: mongodb
33 | key: database-user
34 | - name: MONGO_PASSWORD
35 | valueFrom:
36 | secretKeyRef:
37 | name: mongodb
38 | key: database-password
39 | - name: MONGO_URL
40 | value: mongodb
41 | readinessProbe:
42 | httpGet:
43 | port: 8080
44 | path: /readiness
45 | scheme: HTTP
46 | timeoutSeconds: 1
47 | periodSeconds: 10
48 | successThreshold: 1
49 | failureThreshold: 3
50 | resources:
51 | limits:
52 | memory: 1024Mi
53 | ---
54 | apiVersion: v1
55 | kind: Service
56 | metadata:
57 | name: sample-spring-boot-on-kubernetes-service
58 | spec:
59 | type: NodePort
60 | selector:
61 | app: sample-spring-boot-on-kubernetes
62 | ports:
63 | - port: 8080
64 | nodePort: 30000
65 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Spring Boot on Kubernetes Demo Project [](https://twitter.com/piotr_minkowski)
2 |
3 | [](https://circleci.com/gh/piomin/sample-spring-boot-on-kubernetes)
4 |
5 | [](https://sonarcloud.io/dashboard?id=piomin_sample-spring-boot-on-kubernetes)
6 | [](https://sonarcloud.io/dashboard?id=piomin_sample-spring-boot-on-kubernetes)
7 | [](https://sonarcloud.io/dashboard?id=piomin_sample-spring-boot-on-kubernetes)
8 | [](https://sonarcloud.io/dashboard?id=piomin_sample-spring-boot-on-kubernetes)
9 |
10 | In this project I'm demonstrating different mechanisms of deploying application on Kubernetes. The example application application is simple Spring Boot app that exposes some HTTP endpoints for CRUD operations and connects to MongoDB on cluster.
11 |
12 | ## Getting Started
13 | Currently you may find here some examples of different techniques of deploying this application on Kubernetes. All the examples are described in a separated articles on my blog. Here's a full list of available examples:
14 | 1. Using [Okteto Cloud Platform](https://okteto.com/) - Kubernetes for Developers. A detailed guide may be find in the following article: [Development on Kubernetes with Okteto and Spring Boot](https://piotrminkowski.com/2020/06/15/development-on-kubernetes-with-okteto-and-spring-boot/)
15 |
16 | Mongo on Docker:
17 | ```shell
18 | $ docker run --name mongodb -d -p 27017:27017 \
19 | -e MONGO_INITDB_ROOT_USERNAME=springboot \
20 | -e MONGO_INITDB_ROOT_PASSWORD=springboot123 \
21 | -e MONGO_INITDB_DATABASE=springboot \
22 | mongo:latest
23 | ```
24 |
25 | Mongo for OpenShift:
26 | ```shell
27 | $ oc new-app https://raw.githubusercontent.com/openshift/origin/master/examples/db-templates/mongodb-ephemeral-template.json
28 | ```
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/kubernetes/PersonControllerTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes;
2 |
3 | import org.junit.jupiter.api.MethodOrderer;
4 | import org.junit.jupiter.api.Order;
5 | import org.junit.jupiter.api.Test;
6 | import org.junit.jupiter.api.TestMethodOrder;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.boot.test.web.client.TestRestTemplate;
10 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
11 | import org.testcontainers.containers.MongoDBContainer;
12 | import org.testcontainers.junit.jupiter.Container;
13 | import org.testcontainers.junit.jupiter.Testcontainers;
14 | import pl.piomin.samples.springboot.kubernetes.domain.Gender;
15 | import pl.piomin.samples.springboot.kubernetes.domain.Person;
16 |
17 | import static org.junit.jupiter.api.Assertions.assertEquals;
18 | import static org.junit.jupiter.api.Assertions.assertNotNull;
19 |
20 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
21 | @Testcontainers
22 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
23 | public class PersonControllerTest {
24 |
25 | private static String id;
26 |
27 | @Container
28 | @ServiceConnection
29 | static MongoDBContainer mongodb = new MongoDBContainer("mongo:5.0");
30 |
31 | @Autowired
32 | TestRestTemplate restTemplate;
33 |
34 | @Test
35 | @Order(1)
36 | void add() {
37 | Person person = new Person(null, "Test", "Test", 100, Gender.FEMALE);
38 | Person personAdded = restTemplate.postForObject("/persons", person, Person.class);
39 | assertNotNull(personAdded);
40 | assertNotNull(personAdded.getId());
41 | assertEquals(person.getLastName(), personAdded.getLastName());
42 | id = personAdded.getId();
43 | }
44 |
45 | @Test
46 | @Order(2)
47 | void findById() {
48 | Person person = restTemplate.getForObject("/persons/{id}", Person.class, id);
49 | assertNotNull(person);
50 | assertNotNull(person.getId());
51 | assertEquals(id, person.getId());
52 | }
53 |
54 | @Test
55 | @Order(2)
56 | void findAll() {
57 | Person[] persons = restTemplate.getForObject("/persons", Person[].class);
58 | assertEquals(6, persons.length);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/kubernetes/controller/PersonController.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.kubernetes.controller;
2 |
3 | import java.util.Optional;
4 | import java.util.Set;
5 |
6 | import pl.piomin.samples.springboot.kubernetes.domain.Gender;
7 | import pl.piomin.samples.springboot.kubernetes.domain.Person;
8 | import pl.piomin.samples.springboot.kubernetes.domain.PersonV2;
9 | import pl.piomin.samples.springboot.kubernetes.repository.PersonRepository;
10 | import pl.piomin.samples.springboot.kubernetes.service.PersonService;
11 |
12 | import org.springframework.beans.factory.annotation.Autowired;
13 | import org.springframework.web.bind.annotation.DeleteMapping;
14 | import org.springframework.web.bind.annotation.GetMapping;
15 | import org.springframework.web.bind.annotation.PathVariable;
16 | import org.springframework.web.bind.annotation.PostMapping;
17 | import org.springframework.web.bind.annotation.PutMapping;
18 | import org.springframework.web.bind.annotation.RequestBody;
19 | import org.springframework.web.bind.annotation.RequestMapping;
20 | import org.springframework.web.bind.annotation.RestController;
21 |
22 | @RestController
23 | @RequestMapping("/persons")
24 | public class PersonController {
25 |
26 | private PersonRepository repository;
27 | private PersonService service;
28 |
29 | PersonController(PersonRepository repository, PersonService service) {
30 | this.repository = repository;
31 | this.service = service;
32 | }
33 |
34 | @PostMapping
35 | public Person add(@RequestBody Person person) {
36 | return repository.save(person);
37 | }
38 |
39 | @PostMapping("/random")
40 | public Set add() {
41 | Person p1 = new Person();
42 | p1.setAge(1);
43 | p1.setFirstName("X");
44 | p1.setLastName("X");
45 | p1.setGender(Gender.MALE);
46 | Person p2 = new Person();
47 | p2.setAge(2);
48 | p2.setFirstName("Y");
49 | p2.setLastName("Y");
50 | p2.setGender(Gender.FEMALE);
51 | return service.doIt(p1, p2);
52 | }
53 |
54 | @PutMapping
55 | public Person update(@RequestBody Person person) {
56 | return repository.save(person);
57 | }
58 |
59 | @DeleteMapping("/{id}")
60 | public void delete(@PathVariable("id") String id) {
61 | repository.deleteById(id);
62 | }
63 |
64 | @GetMapping
65 | public Iterable findAll() {
66 | return repository.findAll();
67 | }
68 |
69 | @GetMapping("/{id}")
70 | public Optional findById(@PathVariable("id") String id) {
71 | return repository.findById(id);
72 | }
73 |
74 | @GetMapping("/v2/{id}")
75 | public PersonV2 findByIdV2(@PathVariable("id") String id) {
76 | Person p = repository.findById(id).orElseThrow();
77 | PersonV2 personV2 = new PersonV2();
78 | personV2.setAge(p.getAge());
79 | personV2.setGender(p.getGender());
80 | personV2.setName(p.getFirstName() + " " + p.getLastName());
81 | personV2.setId(p.getId());
82 | return personV2;
83 | }
84 |
85 | @GetMapping("/first-name/{firstName}/last-name/{lastName}")
86 | public Set findByFirstNameAndLastName(@PathVariable("firstName") String firstName,
87 | @PathVariable("lastName") String lastName) {
88 | return repository.findByFirstNameAndLastName(firstName, lastName);
89 | }
90 |
91 | @GetMapping("/age-greater-than/{age}")
92 | public Set findByAgeGreaterThan(@PathVariable("age") int age) {
93 | return repository.findByAgeGreaterThan(age);
94 | }
95 |
96 | @GetMapping("/age/{age}")
97 | public Set findByAge(@PathVariable("age") int age) {
98 | return repository.findByAge(age);
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | jobs:
4 | build:
5 | docker:
6 | - image: 'cimg/openjdk:21.0.9'
7 | steps:
8 | - checkout
9 | - run:
10 | name: Analyze on SonarCloud
11 | command: mvn verify sonar:sonar -DskipTests
12 | test:
13 | executor: machine_executor_amd64
14 | steps:
15 | - checkout
16 | - run:
17 | name: Install OpenJDK 21
18 | command: |
19 | java -version
20 | sudo apt-get update && sudo apt-get install openjdk-21-jdk
21 | sudo update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java
22 | sudo update-alternatives --set javac /usr/lib/jvm/java-21-openjdk-amd64/bin/javac
23 | java -version
24 | export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
25 | - run:
26 | name: Maven Tests
27 | command: mvn test
28 | deploy-k8s:
29 | executor: machine_executor_amd64
30 | steps:
31 | - checkout
32 | - run:
33 | name: Install Kubectl
34 | command: |
35 | curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
36 | chmod +x kubectl
37 | sudo mv ./kubectl /usr/local/bin/kubectl
38 | - run:
39 | name: Install Skaffold
40 | command: |
41 | curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64
42 | chmod +x skaffold
43 | sudo mv skaffold /usr/local/bin
44 | - run:
45 | name: Install Kind
46 | command: |
47 | [ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
48 | chmod +x ./kind
49 | sudo mv ./kind /usr/local/bin/kind
50 | - run:
51 | name: Install Grafana K6
52 | command: |
53 | sudo gpg -k
54 | sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
55 | echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
56 | sudo apt-get update
57 | sudo apt-get install k6
58 | - run:
59 | name: Install OpenJDK 21
60 | command: |
61 | java -version
62 | sudo apt-get update && sudo apt-get install openjdk-21-jdk
63 | sudo update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java
64 | sudo update-alternatives --set javac /usr/lib/jvm/java-21-openjdk-amd64/bin/javac
65 | java -version
66 | export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
67 | - run:
68 | name: Create Kind Cluster
69 | command: |
70 | kind create cluster --name c1 --config k8s/kind-cluster-test.yaml
71 | - run:
72 | name: Deploy to K8s
73 | command: |
74 | export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
75 | skaffold run
76 | - run:
77 | name: Run K6 Test
78 | command: |
79 | kubectl get svc
80 | k6 run src/test/resources/k6/load-tests-add.js -e PERSONS_URI=http://localhost:30000
81 | - run:
82 | name: Delete Kind Cluster
83 | command: |
84 | kind delete cluster --name c1
85 |
86 | executors:
87 | machine_executor_amd64:
88 | machine:
89 | image: ubuntu-2204:2023.10.1
90 | environment:
91 | architecture: "amd64"
92 | platform: "linux/amd64"
93 |
94 | workflows:
95 | maven_test:
96 | jobs:
97 | - test
98 | - build:
99 | context: SonarCloud
100 | - deploy-k8s:
101 | requires:
102 | - test
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.5.7
11 |
12 |
13 | pl.piomin.samples
14 | sample-spring-boot-on-kubernetes
15 | 1.3-SNAPSHOT
16 |
17 |
18 | 21
19 | piomin_sample-spring-boot-on-kubernetes
20 | piomin
21 | https://sonarcloud.io
22 | 1.21.4
23 |
24 |
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-web
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-actuator
33 |
34 |
35 | io.micrometer
36 | micrometer-registry-prometheus
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-data-mongodb
41 |
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-devtools
46 | true
47 |
48 |
49 | org.springframework.boot
50 | spring-boot-starter-test
51 | test
52 |
53 |
54 | org.testcontainers
55 | mongodb
56 | test
57 |
58 |
59 | org.testcontainers
60 | junit-jupiter
61 | test
62 |
63 |
64 | org.springframework.boot
65 | spring-boot-testcontainers
66 | test
67 |
68 |
69 | org.instancio
70 | instancio-junit
71 | 5.5.1
72 | test
73 |
74 |
75 |
76 |
77 |
78 |
79 | org.testcontainers
80 | testcontainers-bom
81 | ${testcontainers.version}
82 | pom
83 | import
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | org.springframework.boot
92 | spring-boot-maven-plugin
93 |
94 |
95 |
96 | build-info
97 |
98 |
99 |
100 |
101 | false
102 | piomin/sample-spring-boot-on-kubernetes:${project.version}
103 |
104 |
105 |
106 |
107 |
108 |
109 | org.cyclonedx
110 | cyclonedx-maven-plugin
111 |
112 |
113 | org.jacoco
114 | jacoco-maven-plugin
115 | 0.8.14
116 |
117 |
118 |
119 | prepare-agent
120 |
121 |
122 |
123 | report
124 | test
125 |
126 | report
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | jib
137 |
138 | false
139 |
140 |
141 |
142 |
143 | com.google.cloud.tools
144 | jib-maven-plugin
145 | 3.5.1
146 |
147 |
148 | eclipse-temurin:21-jdk-ubi9-minimal
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/k8s/jenkins-helm-config.yaml:
--------------------------------------------------------------------------------
1 | agent:
2 | podName: default
3 | customJenkinsLabels: default
4 | volumes:
5 | - type: PVC
6 | claimName: jenkins-agent
7 | mountPath: /home/jenkins
8 | readOnly: false
9 |
10 | additionalAgents:
11 | maven:
12 | podName: maven
13 | customJenkinsLabels: maven
14 | image: jenkins/jnlp-agent-maven
15 | tag: jdk11
16 | volumes:
17 | - type: PVC
18 | claimName: jenkins-agent
19 | mountPath: /home/jenkins
20 | readOnly: false
21 | resources:
22 | limits:
23 | cpu: "1"
24 | memory: "2048Mi"
25 |
26 | master:
27 | JCasC:
28 | configScripts:
29 | creds: |
30 | credentials:
31 | system:
32 | domainCredentials:
33 | - domain:
34 | name: "github.com"
35 | description: "GitHub domain"
36 | specifications:
37 | - hostnameSpecification:
38 | includes: "github.com"
39 | credentials:
40 | - usernamePassword:
41 | scope: GLOBAL
42 | id: github_credentials
43 | username: piomin
44 | password: Piot_123
45 | - credentials:
46 | - kubeconfig:
47 | id: "docker-desktop"
48 | kubeconfigSource:
49 | directEntry:
50 | content: |-
51 | apiVersion: v1
52 | kind: Config
53 | preferences: {}
54 | clusters:
55 | - cluster:
56 | certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01URXdPVEE1TkRjd04xb1hEVE13TVRFd056QTVORGN3TjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTStrCnhKd3l6UVEydXNvRHh5RmwxREdwZWZQTVA0RGFVaVJsK01SQ1p1S0NFWUFkL0ZQOWtFS0RlVXMydmVURi9jMXYKUjZpTDlsMVMvdmN6REoyRXRuZUd0TXVPdWFXNnFCWkN5OFJ2NmFReHd0UEpnWVZGTHBNM2dXYmhqTHp3RXplOApEQlhxekZDZkNobXl3SkdORVdWV0s4VnBuSlpMbjRVbUZKcE5RQndsSXZwRC90UDJVUVRiRGNHYURqUE5vY2c0Cms1SmNOc3h3SDV0NkhIN0JZMW9jTEFLUUhsZ2V4V2ZObWdRRkM2UUcrRVNsWkpDVEtNdVVuM2JsRWVlYytmUWYKaVk3YmdOb3BjRThrS0lUY2pzMG95dGVyNmxuY2ZLTVBqSnc2RTNXMmpXRThKU2Z2WDE2dGVhZUZISDEyTmRqWgpWTER2ZWc3eVBsTlRmRVJld25FQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZMWjUzVEhBSXp0bHljV0NrS1hhY2l4K0Y5a1FNQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFBZWllMTRoSlZkTHF3bUY0SGVPS0ZhNXVUYUt6aXRUSElJNHJhU3cxTUppZElQTmZERwprRk5KeXM1M2I4MHMveWFXQ3BPbXdCK1dEak9hWmREQjFxcXVxU1FxeGhkNWMxU2FBZ1VXTGp5OXdLa1dPMzBTCjB3RTRlVkY3Q1c5VGpSMEoyVXV6UEVXdFBKRWF4U2xKMGhuZlQyeFYvb0N5OE9kMm9EZjZkSFRvbE5UTUEyalcKZjRZdXl3U1Z5a2RNaXZYMU5xZzdmK3RrcEVwb25PdkQ4ZmFEL2dXZmpaWHNFdHo4NXRNcTVLd2NQNUh2ZDJ0ZgoyKzBSbEtFT0pyY1dyL1lEc2w3dWdDdkFJTVk4WGdJL1E5dTZZTjAzTngzWXdSS2UrMElpSzcyOHVuNVJaVEVXCmNZNHc0YkpudlN6WWpKeUJIaHNiQVNTNzN6NndXVEo4REhKSwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
57 | server: https://kubernetes.default
58 | name: docker-desktop
59 | contexts:
60 | - context:
61 | cluster: docker-desktop
62 | user: docker-desktop
63 | name: docker-desktop
64 | current-context: docker-desktop
65 | users:
66 | - name: docker-desktop
67 | user:
68 | client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURGVENDQWYyZ0F3SUJBZ0lJRnh2QzMyK2tPMEl3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TURFeE1Ea3dPVFEzTURkYUZ3MHlNVEV4TURrd09UUTNNekZhTURZeApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sc3dHUVlEVlFRREV4SmtiMk5yWlhJdFptOXlMV1JsCmMydDBiM0F3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQ3M0TXdUU3ByMkRoOTMKTlpERldsNWQyaWgwbllBdTJmTk1RYjZ2ZHR5RUVpTUVpNk5BM05qRGM4OWl5WUhOU2J4YmVNNlNUMzRlTFIwaQpXbHJJSlhhVjNBSXhnbFo4SkdqczVUSHRlM1FjNXZVSkJJWXhndFJFTFlJMGlJekpZdEhoU1NwMFU0eWNjdzl5CnVGSm1YTHVBRVdXR0tTcitVd2Y3RWtuWmJoaFRNQWI0RUF1NlR6dkpyRHhpTDAzU0UrSWhJMTJDV1Y3cVRqZ1gKdGI1OXdKcWkwK3ZJSDBSc3dxOUpnemtQTUhLNkFBZkgxbmFmZ3VCQjM2VEppcUR6YWFxV2VZTmludlIrSkVHMAptakV3NWlFN3JHdUgrZVBxSklvdTJlc1YvN1hyYWx2UEl2Zng2ajFvRWI4NWtna2RuV0JiQlNmTmJCdnhHQU1uCmdnLzdzNHdoQWdNQkFBR2pTREJHTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQWZCZ05WSFNNRUdEQVdnQlMyZWQweHdDTTdaY25GZ3BDbDJuSXNmaGZaRURBTkJna3Foa2lHOXcwQgpBUXNGQUFPQ0FRRUFpbUg1c1JqcjB6WjlDRkU2SVVwNVRwV2pBUXhma29oQkpPOUZmRGE3N2kvR1NsYm1jcXFrCldiWUVYRkl3MU9EbFVjUy9QMXZ5d3ZwV0Y0VXNXTGhtYkF5ZkZGbXZRWFNEZHhYbTlkamI2OEVtRWFPSlI1VTYKOHJOTkR0TUVEY25sbFd2Qk1CRXBNbkNtcm9KcXo3ZzVzeDFQSmhwcDBUdUZDQTIwT2FXb3drTUNNUXRIZlhLQgpVUDA2eGxRU2o1SGNOS1BSQWFyQzBtSzZPVUhybExBcUIvOCtDQlowVUY2MXhTTGN1WFJvYU52S1ZDWHZnQy9kCkQ4ckxuWXFmbWl6WHMvcHJ3dEhsaVFBR2lmemU1MmttbTkyR2RrS2V1SmFRbmM5RWwrd2RZaUVBSHVKU1YvK04Kc2VRelpTa0ZmT2ozbHUxdWtoSDg4dGcxUUp2TkpuM1FhQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
69 | client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBck9ETUUwcWE5ZzRmZHpXUXhWcGVYZG9vZEoyQUx0bnpURUcrcjNiY2hCSWpCSXVqClFOell3M1BQWXNtQnpVbThXM2pPa2s5K0hpMGRJbHBheUNWMmxkd0NNWUpXZkNSbzdPVXg3WHQwSE9iMUNRU0cKTVlMVVJDMkNOSWlNeVdMUjRVa3FkRk9NbkhNUGNyaFNabHk3Z0JGbGhpa3EvbE1IK3hKSjJXNFlVekFHK0JBTAp1azg3eWF3OFlpOU4waFBpSVNOZGdsbGU2azQ0RjdXK2ZjQ2FvdFByeUI5RWJNS3ZTWU01RHpCeXVnQUh4OVoyCm40TGdRZCtreVlxZzgybXFsbm1EWXA3MGZpUkJ0Sm94TU9ZaE82eHJoL25qNmlTS0x0bnJGZisxNjJwYnp5TDMKOGVvOWFCRy9PWklKSFoxZ1d3VW56V3diOFJnREo0SVArN09NSVFJREFRQUJBb0lCQVFDWEZZTGtYVEFlVit0aAo2RnRVVG96b0lxOTJjdXRDaHRHZFZGdk14dWtqTnlLSloydk9WUFBQcE5lYXN4YVFqWjlpcGFxS3JaUS8xUmVBCkhVejNXOTVPUzg5UzYyQ2Y3OFlQT3FLdXRGU2VxYTErS3drSUhobGFXQmRSeUFDYVE1VysrSTEweWt1NXNzak8KYm8zOHpaQkQ5WEF2bHF6dlJTdFZYZjlTV1doQzBlWnRKTm84QU4yZnpkdkRjUUgwOVRsejh1S05EaUNra2RYQQpHTTdZTUdoQktYWGd6YlcxSUVMejRlRUpDZDh0dklReitwcWtxRktIcHRjNnVJY1hLQjFxUGVGRDRSMm9iNUlNCnl5MUpBWlZyR0JHaUk5d1p5OFU1a253UW93emwwUTEwZXlRdUkwTG42SWthZG5SQktMRHcrczRGaE1UQVViOWYKT1NBR3JaVnRBb0dCQU9RTDJzSEN3T25KOW0xYmRiSlVLeTFsRHRsTmV4aDRKOGNERVZKR3NyRVNndlgrSi9ZZQpXb0NHZXc3cGNXakFWaWJhOUMzSFBSbEtOV2hXOExOVlpvUy9jQUxCa1lpSUZNdDlnT1NpZmtCOFhmeVJQT3hJCmNIc2ZjOXZ2OEFJcmxZVVpCNjI1ak8rUFZTOXZLOXZXUGtka3d0MlFSUHlqYlEwVS9mZmdvUWVIQW9HQkFNSVIKd0lqL3RVbmJTeTZzL1JXSlFiVmxPb1VPVjFpb0d4WmNOQjBkaktBV3YwUksvVHdldFBRSXh2dmd1d2RaVFdiTApSTGk5M3RPY3U0UXhNOFpkdGZhTnh5dWQvM2xXSHhPYzRZa0EwUHdSTzY4MjNMcElWNGxrc0tuNUN0aC80QTFJCmw3czV0bHVEbkI3dFdYTFM4MHcyYkE4YWtVZXlBbkZmWXpTTUR1a1hBb0dBSkRFaGNialg1d0t2Z21HT2gxUEcKV25qOFowNWRwOStCNkpxN0NBVENYVW5qME9pYUxQeGFQcVdaS0IreWFQNkZiYnM0SDMvTVdaUW1iNzNFaTZHVgpHS0pOUTVLMjV5VTVyNlhtYStMQ0NMZjBMcDVhUGVHdFFFMFlsU0k2UkEzb3Qrdm1CUk02bzlacW5aR1dNMWlJCkg4cUZCcWJiM0FDUDBSQ3cwY01ycTBjQ2dZRUFvMWM5cmhGTERMYStPTEx3OE1kdHZyZE00ZUNJTTk2SnJmQTkKREtScVQvUFZXQzJscG94UjBYUHh4dDRIak0vbERiZllSNFhIbm1RMGo3YTUxU1BhbTRJSk9QVHFxYjJLdW44NApkSTl6VmpWSy90WTJRYlBSdVpvOTkxSGRod3RhRU5RZ29UeVo5N3gyRXJIQ3I1cE5uTC9SZzRUZzhtOHBEek14CjFIQnR2RkVDZ1lFQTF5aHNPUDBRb3F2SHRNOUFuWVlua2JzQU12L2dqT3FrWUk5cjQ2c1V3Mnc3WHRJb1NTYlAKU0hmbGRxN0IxVXJoTmRJMFBXWXRWZ3kyV1NrN0FaeG8vUWtLZGtPbTAxS0pCY2xteW9JZDE0a0xCVkZxbUV6Rgp1c2l4MmpwdTVOTWhjUWo4aFY2Sk42aXdraHJkYjByZVpuMGo4MG1ZRE96d3hjMmpvTmxSWjN3PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
70 | scope: GLOBAL
--------------------------------------------------------------------------------