├── .ansible ├── docker │ ├── group_vars │ │ └── all.yaml │ ├── requirements.yaml │ └── site.yaml └── inventory.ini ├── .dockerignore ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab ├── gitlab-service-account.yaml ├── jobs │ ├── deploy.gitlab-ci.yaml │ ├── packaging.gitlab-ci.yaml │ ├── publish.gitlab-ci.yaml │ ├── review.gitlab-ci.yaml │ └── testing.gitlab-ci.yaml ├── prometheus │ ├── pv.yaml │ └── pvc.yaml └── runners │ ├── entrypoint.sh │ └── regcred-entrypoint.sh ├── .gitmodules ├── .k8s ├── .gitkeep └── bash_profile.sh ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── docs ├── 01-build-docker.markdown ├── 02-centos-create-kubernetes-cluster.markdown ├── 02-ubuntu-create-kubernetes-cluster.markdown ├── 03-setup-gitlab-runner.markdown ├── 04-gitlab-kubernetes-integration.markdown ├── 05-multi-cluster-gitlab-runner.markdown ├── 06-setup-kubernetes-dashboard.markdown ├── 07-prometheus-matrics.markdown ├── 08-metrics-server.markdown ├── 09-loadbalancer-metallb.markdown ├── 10-ingress-controller.markdown └── images │ ├── gitlab-integration │ ├── .gitkeep │ ├── 01-integrate-cluster.png │ ├── 02-connect-existing.png │ ├── 03-cluster-registered.png │ └── 04-cicd-variables.png │ ├── gitlab-runner │ └── 01-gitlab-runner-register.png │ ├── kubernetes-cluster │ └── .gitkeep │ ├── kubernetes-dashboard │ ├── 01-dashboard-login.png │ └── 02-dashboard-login.png │ ├── metrixs │ └── prometheus.png │ ├── multi-clusters │ ├── 01-kubernetes-integration.png │ ├── 02-environments.png │ ├── 03-env-variables.png │ ├── 04-git-tags.png │ ├── 05-pipeline-workflow.png │ ├── 06-pipeline-review-apps.png │ ├── 07-environment-review-apps.png │ ├── 08-deploying-button.png │ ├── 09-pipeline-deploy-prod.png │ └── 10-env-production-apps.png │ ├── virtual-machines │ └── .gitkeep │ └── workflow │ ├── node-stack.png │ └── workflow-devops.png ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── maryanto │ │ └── dimas │ │ └── example │ │ └── SpringbootK8sExampleApplication.java ├── kubernetes │ ├── deployment.yaml │ └── service.yaml └── resources │ ├── application.yaml │ ├── db │ └── migration │ │ └── V20210517131608__example-table.sql │ └── templates │ └── index.html └── test └── java └── com └── maryanto └── dimas └── example └── SpringbootK8sExampleApplicationTests.java /.ansible/docker/group_vars/all.yaml: -------------------------------------------------------------------------------- 1 | docker_storage_driver: overlay2 2 | docker_insecure_registries_enabled: true 3 | docker_insecure_registries_conf: 4 | - url: "192.168.100.250:8088" 5 | auth: 6 | docker_login: true 7 | user: tabeldata 8 | password: tabeldata -------------------------------------------------------------------------------- /.ansible/docker/requirements.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - ansible.posix 4 | - community.docker -------------------------------------------------------------------------------- /.ansible/docker/site.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install docker-ce 3 | hosts: ["dockerd"] 4 | become: true 5 | roles: 6 | - dimmaryanto93.docker 7 | -------------------------------------------------------------------------------- /.ansible/inventory.ini: -------------------------------------------------------------------------------- 1 | master1 ansible_port=22 ansible_user=ansible ansible_host=192.168.59.146 2 | 3 | [dockerd] 4 | 5 | [dockerd:children] 6 | kubernetes 7 | 8 | [k8s_control_panel] 9 | master1 10 | 11 | [k8s_worker] 12 | 13 | [kubernetes:children] 14 | k8s_control_panel 15 | k8s_worker -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | ### Maven template 2 | src/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | .mvn/ 11 | 12 | ### JetBrains template 13 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 14 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 15 | 16 | # User-specific stuff 17 | .idea/ 18 | 19 | # Gradle and Maven with auto-import 20 | # When using Gradle or Maven with auto-import, you should exclude module files, 21 | # since they will be recreated, and may cause churn. Uncomment if using 22 | # auto-import. 23 | # .idea/artifacts 24 | # .idea/compiler.xml 25 | # .idea/jarRepositories.xml 26 | # .idea/modules.xml 27 | # .idea/*.iml 28 | # .idea/modules 29 | *.iml 30 | *.ipr 31 | *.iws 32 | 33 | # CMake 34 | cmake-build-*/ 35 | 36 | # Mongo Explorer plugin 37 | .idea/**/mongoSettings.xml 38 | 39 | # File-based project format 40 | 41 | # IntelliJ 42 | out/ 43 | 44 | # mpeltonen/sbt-idea plugin 45 | .idea_modules/ 46 | 47 | # JIRA plugin 48 | atlassian-ide-plugin.xml 49 | 50 | # Cursive Clojure plugin 51 | .idea/replstate.xml 52 | 53 | # Crashlytics plugin (for Android Studio and IntelliJ) 54 | com_crashlytics_export_strings.xml 55 | crashlytics.properties 56 | crashlytics-build.properties 57 | fabric.properties 58 | 59 | # Android studio 3.1+ serialized cache file 60 | .idea/caches/build_file_checksums.ser 61 | 62 | ## GIT 63 | .git/ 64 | 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | ### MacOS #### 36 | .DS_Store 37 | 38 | ### Kubernetes deployment ### 39 | .k8s/*.yaml 40 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - test 3 | - quality-control 4 | - build 5 | - publish 6 | - performances 7 | - review 8 | - deploy 9 | 10 | variables: 11 | DOCKER_TLS_CERTDIR: "" 12 | CI_IMAGE_VERSION: $CI_COMMIT_SHORT_SHA 13 | MAVEN_CLI_OPTS: "--show-version -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Ddocker-registry-image=${CI_REGISTRY_IMAGE} -Ddocker-registry-host-push=${CI_REGISTRY} -Ddocker-registry-host-pull=${CI_REGISTRY_PULL} -Ddocker-registry-group=${CI_REGISTRY_NAMESPACE}" 14 | DOCKER_REGCRED: "nexus-regcred" 15 | CI_REGISTRY_IMAGE: "${CI_PROJECT_NAME}" 16 | CI_REGISTRY_NAMESPACE: "${CI_PROJECT_NAMESPACE}" 17 | ### TODO change this value then inside job folder for `after_script` to get service port at `NODE_PORT_ENV=$(kubectl get service .... -o=jsonpath='{.spec.ports[?(@.port==8080)].nodePort}` if you need custome expose port 18 | CI_IMAGE_EXPOSE_PORT: 8080 19 | NODE_CLUSTER_IP: "192.168.88.120" 20 | 21 | cache: 22 | paths: 23 | - .m2/repository 24 | 25 | image: $CI_REGISTRY_PULL/maven:3.6.3-jdk-11 26 | 27 | include: 28 | - local: .gitlab/jobs/testing.gitlab-ci.yaml 29 | - local: .gitlab/jobs/packaging.gitlab-ci.yaml 30 | - local: .gitlab/jobs/publish.gitlab-ci.yaml 31 | - local: .gitlab/jobs/review.gitlab-ci.yaml # required `publish.gitlab-ci.yaml` 32 | - local: .gitlab/jobs/deploy.gitlab-ci.yaml # required `publish.gitlab-ci.yaml` 33 | -------------------------------------------------------------------------------- /.gitlab/gitlab-service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: gitlab 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1 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 | --- 20 | kind: Role 21 | apiVersion: rbac.authorization.k8s.io/v1 22 | metadata: 23 | name: gitlab-role 24 | namespace: gitlab-managed-apps 25 | rules: 26 | - apiGroups: 27 | - "" 28 | - extensions 29 | resources: 30 | - '*' 31 | verbs: 32 | - '*' 33 | --- 34 | kind: RoleBinding 35 | apiVersion: rbac.authorization.k8s.io/v1 36 | metadata: 37 | name: gitlab-rb 38 | namespace: gitlab-managed-apps 39 | subjects: 40 | - kind: ServiceAccount 41 | name: gitlab 42 | namespace: kube-system 43 | roleRef: 44 | kind: Role 45 | name: gitlab-role 46 | apiGroup: rbac.authorization.k8s.io 47 | -------------------------------------------------------------------------------- /.gitlab/jobs/deploy.gitlab-ci.yaml: -------------------------------------------------------------------------------- 1 | deploying: 2 | image: 3 | name: $CI_REGISTRY_PULL/dimmaryanto93/k8s-kubectl-helm:1.21.1-release 4 | stage: deploy 5 | tags: 6 | - docker 7 | environment: 8 | name: production 9 | url: http://NODE_CLUSTER_IP:$NODE_PORT 10 | when: manual 11 | before_script: 12 | - kubectl config set-context --current --namespace="$KUBE_NAMESPACE" 13 | - sh .gitlab/runners/entrypoint.sh 14 | - sh .gitlab/runners/regcred-entrypoint.sh 15 | script: 16 | - kubectl apply -f .k8s/deployment.yaml -n $KUBE_NAMESPACE 17 | - kubectl apply -f .k8s/service.yaml -n $KUBE_NAMESPACE 18 | after_script: 19 | - NODE_PORT_ENV=$(kubectl get service $CI_PROJECT_PATH_SLUG -n $KUBE_NAMESPACE -o=jsonpath='{.spec.ports[?(@.port==8080)].nodePort}') 20 | - echo "NODE_PORT=$NODE_PORT_ENV" >> deploy.env 21 | - echo "NODE_CLUSTER_IP=$NODE_CLUSTER_IP" >> deploy.env 22 | artifacts: 23 | reports: 24 | dotenv: deploy.env 25 | paths: 26 | - .k8s/ 27 | name: $CI_PROJECT_NAME-$CI_COMMIT_TAG 28 | only: 29 | - /-release/ 30 | -------------------------------------------------------------------------------- /.gitlab/jobs/packaging.gitlab-ci.yaml: -------------------------------------------------------------------------------- 1 | building: 2 | stage: build 3 | before_script: 4 | - mvn -s $M2_SETTINGS_XML versions:set -DnewVersion=$CI_IMAGE_VERSION 5 | script: 6 | - mvn -s $M2_SETTINGS_XML $MAVEN_CLI_OPTS package -DskipTests 7 | only: 8 | - /-release/ 9 | tags: 10 | - docker 11 | artifacts: 12 | paths: 13 | - target/*.jar 14 | name: $CI_PROJECT_NAME-$CI_COMMIT_TAG 15 | -------------------------------------------------------------------------------- /.gitlab/jobs/publish.gitlab-ci.yaml: -------------------------------------------------------------------------------- 1 | docker-publish: 2 | stage: build 3 | services: 4 | - name: $CI_REGISTRY_PULL/docker:18.09-dind 5 | # Enabled insecure registry into docker inside docker 6 | entrypoint: [ "dockerd-entrypoint.sh" ] 7 | # TODO changed variable `--insecure-registry` 8 | command: [ 9 | "--insecure-registry=repository.dimas-maryanto.com:8087", 10 | "--insecure-registry=repository.dimas-maryanto.com:8086" 11 | ] 12 | alias: dockerd 13 | variables: 14 | # modified file /etc/hosts inside docker container 15 | DOCKER_HOST: tcp://dockerd:2375 16 | DOCKER_DRIVER: overlay2 17 | script: 18 | - mvn -s $M2_SETTINGS_XML versions:set -DnewVersion=$CI_IMAGE_VERSION 19 | - mvn -s $M2_SETTINGS_XML $MAVEN_CLI_OPTS -DskipTests package dockerfile:build dockerfile:push 20 | only: 21 | - /-release/ 22 | tags: 23 | - docker 24 | -------------------------------------------------------------------------------- /.gitlab/jobs/review.gitlab-ci.yaml: -------------------------------------------------------------------------------- 1 | review_apps: 2 | image: 3 | name: $CI_REGISTRY_PULL/dimmaryanto93/k8s-kubectl-helm:1.21.1-release 4 | stage: review 5 | tags: 6 | - docker 7 | environment: 8 | name: review 9 | on_stop: stop_review_apps 10 | url: http://NODE_CLUSTER_IP:$NODE_PORT 11 | auto_stop_in: 1 week 12 | before_script: 13 | - kubectl config set-context --current --namespace="$KUBE_NAMESPACE" 14 | - sh .gitlab/runners/entrypoint.sh 15 | - sh .gitlab/runners/regcred-entrypoint.sh 16 | script: 17 | - kubectl apply -f .k8s/deployment.yaml -n $KUBE_NAMESPACE 18 | - kubectl apply -f .k8s/service.yaml -n $KUBE_NAMESPACE 19 | after_script: 20 | - NODE_PORT_ENV=$(kubectl get service $CI_PROJECT_PATH_SLUG -n $KUBE_NAMESPACE -o=jsonpath='{.spec.ports[?(@.port==8080)].nodePort}') 21 | - echo "NODE_PORT=$NODE_PORT_ENV" >> deploy.env 22 | - echo "NODE_CLUSTER_IP=$NODE_CLUSTER_IP" >> deploy.env 23 | allow_failure: true 24 | artifacts: 25 | reports: 26 | dotenv: deploy.env 27 | paths: 28 | - .k8s/ 29 | name: $CI_PROJECT_NAME-$CI_COMMIT_TAG 30 | only: 31 | - /-release/ 32 | 33 | stop_review_apps: 34 | image: 35 | name: $CI_REGISTRY_PULL/dimmaryanto93/k8s-kubectl-helm:1.21.1-release 36 | stage: review 37 | tags: 38 | - docker 39 | environment: 40 | name: review 41 | action: stop 42 | when: manual 43 | before_script: 44 | - kubectl config set-context --current --namespace="$KUBE_NAMESPACE" 45 | - sh .gitlab/runners/entrypoint.sh 46 | script: 47 | - kubectl delete -f .k8s/deployment.yaml -n $KUBE_NAMESPACE 48 | - kubectl delete -f .k8s/service.yaml -n $KUBE_NAMESPACE 49 | only: 50 | - /-release/ 51 | -------------------------------------------------------------------------------- /.gitlab/jobs/testing.gitlab-ci.yaml: -------------------------------------------------------------------------------- 1 | testing: 2 | stage: test 3 | services: 4 | - name: postgres:12.3 5 | alias: spring-k8s-example 6 | variables: 7 | POSTGRES_DB: testing 8 | POSTGRES_USER: testing 9 | POSTGRES_PASSWORD: testing 10 | DATABASE_HOST: spring-k8s-example 11 | DATABASE_USER: $POSTGRES_USER 12 | DATABASE_PASSWORD: $POSTGRES_PASSWORD 13 | DATABASE_NAME: $POSTGRES_DB 14 | DATABASE_PORT: 5432 15 | script: 16 | - mvn -s $M2_SETTINGS_XML versions:set -DnewVersion=$CI_IMAGE_VERSION 17 | - mvn -s $M2_SETTINGS_XML $MAVEN_CLI_OPTS clean test 18 | tags: 19 | - docker 20 | only: 21 | - /-release/ 22 | -------------------------------------------------------------------------------- /.gitlab/prometheus/pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | labels: 5 | app: prometheus 6 | app.kubernetes.io/managed-by: Helm 7 | chart: prometheus-14.6.0 8 | component: server 9 | heritage: Helm 10 | release: prometheus 11 | name: prometheus-prometheus-server-pv 12 | spec: 13 | accessModes: 14 | - ReadWriteOnce 15 | capacity: 16 | storage: 10Gi 17 | claimRef: 18 | apiVersion: v1 19 | kind: PersistentVolumeClaim 20 | name: prometheus-prometheus-server 21 | namespace: gitlab-managed-apps 22 | mountOptions: 23 | - hard 24 | - nfsvers=4.1 25 | nfs: 26 | ## changed this value to your nfs server 27 | path: /var/lib/prometheus/data 28 | server: 192.168.100.250 29 | persistentVolumeReclaimPolicy: Retain 30 | volumeMode: Filesystem 31 | -------------------------------------------------------------------------------- /.gitlab/prometheus/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | annotations: 5 | meta.helm.sh/release-name: prometheus 6 | meta.helm.sh/release-namespace: gitlab-managed-apps 7 | pv.kubernetes.io/bind-completed: "yes" 8 | finalizers: 9 | - kubernetes.io/pvc-protection 10 | labels: 11 | app: prometheus 12 | app.kubernetes.io/managed-by: Helm 13 | chart: prometheus-14.6.0 14 | component: server 15 | heritage: Helm 16 | release: prometheus 17 | name: prometheus-prometheus-server 18 | namespace: gitlab-managed-apps 19 | spec: 20 | accessModes: 21 | - ReadWriteOnce 22 | resources: 23 | requests: 24 | storage: 8Gi 25 | volumeMode: Filesystem 26 | ## add this value 27 | volumeName: prometheus-prometheus-server-pv 28 | status: 29 | accessModes: 30 | - ReadWriteOnce 31 | capacity: 32 | storage: 10Gi 33 | -------------------------------------------------------------------------------- /.gitlab/runners/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | export WORKING_DIRECTORY=src/main/kubernetes 4 | export DEPLOYMENT_FILE=$WORKING_DIRECTORY/deployment.yaml 5 | export SERVICE_FILE=$WORKING_DIRECTORY/service.yaml 6 | export DIRECTORY_OUTPUT=.k8s 7 | export CI_NODE_PORT_EXPOSE=$(printf "3%04d" $CI_PROJECT_ID) 8 | 9 | envsubst < $DEPLOYMENT_FILE > $DIRECTORY_OUTPUT/deployment.yaml && \ 10 | cat $DIRECTORY_OUTPUT/deployment.yaml && \ 11 | envsubst < $SERVICE_FILE > $DIRECTORY_OUTPUT/service.yaml && \ 12 | cat $DIRECTORY_OUTPUT/service.yaml 13 | 14 | exec "$@" 15 | -------------------------------------------------------------------------------- /.gitlab/runners/regcred-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | mkdir -p .docker/ 5 | cat $DOCKER_CONF_JSON > .docker/config.json 6 | 7 | # shellcheck disable=SC2126 8 | # shellcheck disable=SC2155 9 | export NEXUS_REGCRED_IS_EXIST=$(kubectl get secret "$DOCKER_REGCRED" -n "$KUBE_NAMESPACE" -o json --ignore-not-found=true | grep "$DOCKER_REGCRED" | wc -l) 10 | 11 | if [ ${NEXUS_REGCRED_IS_EXIST} -eq 0 ]; then 12 | kubectl create secret generic "$DOCKER_REGCRED" --from-file=.dockerconfigjson=.docker/config.json --type=kubernetes.io/dockerconfigjson --namespace="$KUBE_NAMESPACE" 13 | else 14 | echo "Docker Registry Credential was created" 15 | fi 16 | 17 | exec "$@" 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ansible-kubernetes"] 2 | path = .ansible/kubernetes 3 | url = git@repository.dimas-maryanto.com:examples/ansible/ansible-kubernetes-example.git 4 | -------------------------------------------------------------------------------- /.k8s/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/.k8s/.gitkeep -------------------------------------------------------------------------------- /.k8s/bash_profile.sh: -------------------------------------------------------------------------------- 1 | export CI_PROJECT_PATH_SLUG=springboot-example 2 | export CI_IMAGE_VERSION=$(git log --pretty="%h" | head -n 1) 3 | export CI_ENVIRONMENT_SLUG=review 4 | export CI_REGISTRY=repository.dimas-maryanto.com:8087 5 | export CI_REGISTRY_NAMESPACE=examples/k8s-gitlab-cicd 6 | export CI_REGISTRY_IMAGE=springboot-example 7 | export CI_IMAGE_EXPOSE_PORT=8080 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG JDK_VERSION=11-oraclelinux8 2 | 3 | FROM openjdk:${JDK_VERSION} 4 | LABEL maintainer="Dimas Maryanto " 5 | 6 | # Created user & folder 7 | RUN groupadd www-data && \ 8 | adduser -r -g www-data www-data 9 | 10 | # Create folder & give access to read and write 11 | ENV FILE_UPLOAD_STORED=/var/lib/spring-boot/data 12 | RUN mkdir -p ${FILE_UPLOAD_STORED} && \ 13 | chmod -R 777 ${FILE_UPLOAD_STORED}/ 14 | 15 | # set working directory 16 | WORKDIR /usr/local/share/applications 17 | # set user 18 | USER www-data 19 | 20 | ARG JAR_FILE="springboot-k8s-example-0.0.1-SNAPSHOT.jar" 21 | # copy file from local to images then rename to spring-boot.jar 22 | ADD --chown=www-data:www-data target/$JAR_FILE spring-boot.jar 23 | 24 | ENV APPLICATION_PORT=8080 25 | ENV DATABASE_USER=postgres 26 | ENV DATABASE_PASSWORD=postgres 27 | ENV DATABASE_HOST=localhost 28 | ENV DATABASE_NAME=postgres 29 | ENV DATABASE_PORT=5432 30 | ENV FLYWAY_ENABLED=true 31 | 32 | # define volume for documentation 33 | VOLUME ${FILE_UPLOAD_STORED}/ 34 | 35 | # reqired command to run application 36 | ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "spring-boot.jar"] 37 | # set default command params 38 | CMD ["--server.port=${APPLICATION_PORT}"] 39 | 40 | # Health check every 5 minutes and set timeout 3 seconds using curl 41 | EXPOSE ${APPLICATION_PORT} 42 | HEALTHCHECK --interval=5m --timeout=3s \ 43 | CMD curl -f http://localhost:${APPLICATION_PORT}/actuator || exit 1 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dimas Maryanto 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 | # Gitlab CI/CD deploy to Kubernetes cluster 2 | 3 | How to deploy to kubernetes using Gitlab CI/CD from existing kubernetes cluster 4 | 5 | ref: 6 | - https://docs.gitlab.com/ee/user/project/clusters/add_remove_clusters.html 7 | - https://docs.gitlab.com/runner/executors/kubernetes.html 8 | - https://docs.gitlab.com/ee/ci/environments/ 9 | 10 | 11 | ## Workflow 12 | 13 | Konfigurasi Server & Workflow untuk Automated DevOps menggunakan Gitlab CI/CD & Kubernetes 14 | 15 | ![servers](docs/images/workflow/node-stack.png) 16 | 17 | ![workflow](docs/images/workflow/workflow-devops.png) 18 | -------------------------------------------------------------------------------- /docs/01-build-docker.markdown: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/01-build-docker.markdown -------------------------------------------------------------------------------- /docs/02-centos-create-kubernetes-cluster.markdown: -------------------------------------------------------------------------------- 1 | # Installing Kubernetes cluster 2 | 3 | Kita siapkan host yang akan di install kubernetes, sebagai contoh disini saya menggunakan CentOS 8 dengan konfigurasi minimal sebagai berikut: 4 | 5 | ```yaml 6 | Master-Node: 7 | - NodeName: 'k8s-master' 8 | CPU: '2 Cores' or more. 9 | RAM: '4 GB' or more 10 | Storage: '50 GB' 11 | partision: 12 | - / = "20 Gb" 13 | - /var = "30 Gb" 14 | - swap = "Disabled" 15 | Network: # Full network connectivity between all machines in the cluster (public or private network is fine). 16 | - IP4: 'Brige (192.168.88.140)' 17 | - hostname: 'k8s-cpdev01.dimas-maryanto.com' # Unique hostname, MAC address, and product_uuid for every node. 18 | Worker-Nodes: 19 | - NodeName: 'k8s-worker1' 20 | CPU: '2 Cores' or more. 21 | RAM: '2 GB' or more. 22 | Storage: '50 GB' 23 | partision: 24 | - / = "20 Gb" 25 | - /var = "30 Gb" 26 | - swap = "Disabled" 27 | Network: # Full network connectivity between all machines in the cluster (public or private network is fine). 28 | - IP4: 'Brige (192.168.88.14x)' 29 | - hostname: 'k8s-wdev01.dimas-maryanto.com' # Unique hostname, MAC address, and product_uuid for every node. 30 | ``` 31 | 32 | Verify the MAC address and product_uuid are unique for every node 33 | 34 | 1. You can get the MAC address of the network interfaces using the command `ip link` or `ifconfig -a` 35 | 2. The product_uuid can be checked by using the command `sudo cat /sys/class/dmi/id/product_uuid` 36 | 37 | It is very likely that hardware devices will have unique addresses, although some virtual machines may have identical values. Kubernetes uses these values to uniquely identify the nodes in the cluster. If these values are not unique to each node, the installation process may [fail](https://github.com/kubernetes/kubeadm/issues/31). 38 | 39 | ## Setup & install commons package 40 | 41 | Sebelum kita install, disini saya mau install dulu commons package seperti `curl`, `wget`, `yum-utils`, `net-tools` dan lain-lain. 42 | 43 | ```bash 44 | # update system 45 | yum update -y && \ 46 | yum install -y epel-release && \ 47 | yum install -y net-tools curl wget yum-utils vim tmux tc && \ 48 | yum install -y device-mapper-persistent-data lvm2 fuse-overlayfs 49 | ``` 50 | 51 | Disable swap partition permanently, edit file `/etc/fstab` comment `/dev/mapper/cl-swap` like this: 52 | 53 | ```conf 54 | # 55 | # /etc/fstab 56 | # Created by anaconda on Tue Jul 20 08:07:33 2021 57 | # 58 | # Accessible filesystems, by reference, are maintained under '/dev/disk/'. 59 | # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info. 60 | # 61 | # After editing this file, run 'systemctl daemon-reload' to update systemd 62 | # units generated from this file. 63 | # 64 | /dev/mapper/cl-root / xfs defaults 0 0 65 | UUID=4ec37475-d403-4466-b2bf-318dfd409092 /boot ext4 defaults 1 2 66 | /dev/mapper/cl-var /var xfs defaults 0 0 67 | #/dev/mapper/cl-swap swap swap defaults 0 0 68 | ``` 69 | 70 | Setelah itu kita set selinux = `permissive` dengan mengedit file `/etc/selinux/config` 71 | 72 | ```bash 73 | sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config && \ 74 | systemctl disable firewalld && \ 75 | systemctl stop firewalld 76 | ``` 77 | 78 | Kemudian `reboot` . 79 | 80 | Setelah itu kita setup untuk networking (iptables) di kubernetes. 81 | 82 | Make sure that the `br_netfilter` module is loaded. This can be done by running `lsmod | grep br_netfilter`. To load it explicitly call `sudo modprobe br_netfilter`. 83 | As a requirement for your Linux Node’s iptables to correctly see bridged traffic, you should ensure `net.bridge.bridge-nf-call-iptables` is set to 1 in your `sysctl` config, e.g. 84 | 85 | ```bash 86 | lsmod | grep br_netfilter && \ 87 | sudo modprobe br_netfilter 88 | 89 | cat < mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 169 | # link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 170 | # inet 127.0.0.1/8 scope host lo 171 | # valid_lft forever preferred_lft forever 172 | # inet6 ::1/128 scope host 173 | # valid_lft forever preferred_lft forever 174 | #2: enp0s3: mtu 1500 qdisc fq_codel state UP group default qlen 1000 175 | # link/ether 08:00:27:82:7e:72 brd ff:ff:ff:ff:ff:ff 176 | # inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s3 177 | # valid_lft 85608sec preferred_lft 85608sec 178 | # inet6 fe80::af80:fc40:bb9d:a6a/64 scope link noprefixroute 179 | # valid_lft forever preferred_lft forever 180 | #3: enp0s8: mtu 1500 qdisc fq_codel state UP group default qlen 1000 181 | # link/ether 08:00:27:0b:23:b1 brd ff:ff:ff:ff:ff:ff 182 | # inet 192.168.88.140/24 brd 192.168.88.255 scope global noprefixroute enp0s8 183 | # valid_lft forever preferred_lft forever 184 | # inet6 fe80::7b04:464f:315e:f223/64 scope link dadfailed tentative noprefixroute 185 | # valid_lft forever preferred_lft forever 186 | # inet6 fe80::deda:2329:2c79:32a8/64 scope link noprefixroute 187 | # valid_lft forever preferred_lft forever 188 | #4: docker0: mtu 1500 qdisc noqueue state DOWN group default 189 | # link/ether 02:42:dd:8a:d8:d6 brd ff:ff:ff:ff:ff:ff 190 | # inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 191 | # valid_lft forever preferred_lft forever 192 | ``` 193 | 194 | Maka saya mau pake IP Address `192.168.xx.xx` maka saya menggunakan network `enp0s8` untuk Network cni `--apiserver-advertise-address` 195 | 196 | ```bash 197 | export KUBE_NET_INTERFACE=enp0s8 && \ 198 | kubeadm config images pull && \ 199 | kubeadm init \ 200 | --apiserver-advertise-address=$(ip -f inet a show $KUBE_NET_INTERFACE | grep inet | awk '{ print $2 }' | cut -d/ -f1) \ 201 | --pod-network-cidr=10.244.0.0/16 && \ 202 | mkdir -p $HOME/.kube && \ 203 | sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config && \ 204 | sudo chown $(id -u):$(id -g) $HOME/.kube/config 205 | ``` 206 | 207 | ## Installing Addons Networking and Network Policy 208 | 209 | Untuk network plugin and policy, sebetulnya ada banyak implementasinya. bisa di check [disini](https://kubernetes.io/docs/concepts/cluster-administration/addons/) namun di materi kali ini kita akan menggunakan flannel seperti berikut untuk menginstallnya: 210 | 211 | ```bash 212 | kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 213 | ``` 214 | 215 | ## Joining Kubernetes Workers node 216 | 217 | Untuk multiple nodes, kita bisa join ke master / control panel dengan perintah yang tadi yaitu 218 | 219 | ```bash 220 | kubeadm join 192.168.88.140:6443 --token 6bo11m.i5517ihphsnuuj67 \ 221 | --discovery-token-ca-cert-hash sha256:9ee47b6f4a02623839c33281a8692ac637f41537913e0baa33b53cddc3647335 222 | ``` 223 | 224 | Or you can create new token 225 | 226 | ```bash 227 | kubeadm token create --print-join-command 228 | ``` 229 | 230 | ## Control plane node isolation (Optional) 231 | 232 | By default, your cluster will not schedule Pods on the control-plane node for security reasons. If you want to be able to schedule Pods on the control-plane node, for example for a single-machine Kubernetes cluster for development, run: 233 | 234 | ```bash 235 | kubectl taint nodes --all node-role.kubernetes.io/master- 236 | ``` 237 | 238 | ## Adding a ip address to the Kubernetes API Server Certificate 239 | 240 | ref: 241 | - https://blog.scottlowe.org/2019/07/30/adding-a-name-to-kubernetes-api-server-certificate/ 242 | -------------------------------------------------------------------------------- /docs/02-ubuntu-create-kubernetes-cluster.markdown: -------------------------------------------------------------------------------- 1 | # Installing Kubernetes cluster 2 | 3 | Kita siapkan host yang akan di install kubernetes, sebagai contoh disini saya menggunakan Ubuntu 20.04.3 dengan konfigurasi minimal sebagai berikut: 4 | 5 | ```yaml 6 | Master-Node: 7 | - NodeName: 'k8s-master' 8 | CPU: '2 Cores' or more. 9 | RAM: '4 GB' or more 10 | Storage: '50 GB' 11 | partision: 12 | - / = "20 Gb" 13 | - /var = "30 Gb" 14 | - swap = "Disabled" 15 | Network: # Full network connectivity between all machines in the cluster (public or private network is fine). 16 | - IP4: 'Brige (192.168.88.140)' 17 | - hostname: 'k8s-cpdev01.dimas-maryanto.com' # Unique hostname, MAC address, and product_uuid for every node. 18 | Worker-Nodes: 19 | - NodeName: 'k8s-worker1' 20 | CPU: '2 Cores' or more. 21 | RAM: '2 GB' or more. 22 | Storage: '50 GB' 23 | partision: 24 | - / = "20 Gb" 25 | - /var = "30 Gb" 26 | - swap = "Disabled" 27 | Network: # Full network connectivity between all machines in the cluster (public or private network is fine). 28 | - IP4: 'Brige (192.168.88.14x)' 29 | - hostname: 'k8s-wdev01.dimas-maryanto.com' # Unique hostname, MAC address, and product_uuid for every node. 30 | ``` 31 | 32 | Verify the MAC address and product_uuid are unique for every node 33 | 34 | 1. You can get the MAC address of the network interfaces using the command `ip link` or `ifconfig -a` 35 | 2. The product_uuid can be checked by using the command `sudo cat /sys/class/dmi/id/product_uuid` 36 | 37 | It is very likely that hardware devices will have unique addresses, although some virtual machines may have identical values. Kubernetes uses these values to uniquely identify the nodes in the cluster. If these values are not unique to each node, the installation process may [fail](https://github.com/kubernetes/kubeadm/issues/31). 38 | 39 | ## Setup & install commons package 40 | 41 | Sebelum kita install, disini saya mau install dulu commons package seperti `curl`, `wget`, `yum-utils`, `net-tools` dan lain-lain. 42 | 43 | ```bash 44 | # update system 45 | apt-get update && apt-get upgrade -y && \ 46 | apt-get install -y net-tools \ 47 | curl \ 48 | wget \ 49 | vim \ 50 | tmux \ 51 | apt-transport-https \ 52 | ca-certificates \ 53 | gnupg \ 54 | lsb-release 55 | ``` 56 | 57 | Disable swap partition permanently, edit file `/etc/fstab` comment `/dev/mapper/cl-swap` like this: 58 | 59 | ```conf 60 | # /etc/fstab: static file system information. 61 | # 62 | # Use 'blkid' to print the universally unique identifier for a 63 | # device; this may be used with UUID= as a more robust way to name devices 64 | # that works even if disks are added and removed. See fstab(5). 65 | # 66 | # 67 | # / was on /dev/ubuntu-vg/ubuntu-lv during curtin installation 68 | /dev/disk/by-id/dm-uuid-LVM-pLKndmSgBkA5Lk8mWuP7bJc9XiABYFegLL5BawnlKjsJ0EPRyGiKIiprV2ZM1FMI / ext4 defaults 0 1 69 | # /boot was on /dev/sda2 during curtin installation 70 | /dev/disk/by-uuid/9306ff44-a2d1-4d49-a7a3-b9278929a3e3 /boot ext4 defaults 0 1 71 | #/swap.img none swap sw 0 0 72 | ``` 73 | 74 | Kemudian `reboot`. 75 | 76 | Setelah itu kita setup untuk networking (iptables) di kubernetes. 77 | 78 | Make sure that the `br_netfilter` module is loaded. This can be done by running `lsmod | grep br_netfilter`. To load it explicitly call `sudo modprobe br_netfilter`. 79 | As a requirement for your Linux Node’s iptables to correctly see bridged traffic, you should ensure `net.bridge.bridge-nf-call-iptables` is set to 1 in your `sysctl` config, e.g. 80 | 81 | ```bash 82 | lsmod | grep br_netfilter && \ 83 | sudo modprobe br_netfilter 84 | 85 | cat < /dev/null 109 | 110 | ## install docker-ce 111 | apt-get update && \ 112 | apt-get -y install docker-ce docker-ce-cli containerd.io 113 | 114 | ## configure daemon 115 | sudo mkdir -p /etc/docker && \ 116 | cat < mtu 1500 qdisc fq_codel state UP group default qlen 1000 170 | inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3 171 | 3: enp0s8: mtu 1500 qdisc fq_codel state UP group default qlen 1000 172 | inet 192.168.100.48/24 brd 192.168.100.255 scope global dynamic enp0s8 173 | ``` 174 | 175 | Maka saya mau pake IP Address `192.168.xx.xx` maka saya menggunakan network `enp0s8` untuk Network cni `--apiserver-advertise-address` 176 | 177 | ```bash 178 | export KUBE_NET_INTERFACE=enp0s8 && \ 179 | kubeadm config images pull && \ 180 | kubeadm init \ 181 | --apiserver-advertise-address=$(ip -f inet a show $KUBE_NET_INTERFACE | grep inet | awk '{ print $2 }' | cut -d/ -f1) \ 182 | --pod-network-cidr=10.244.0.0/16 && \ 183 | mkdir -p $HOME/.kube && \ 184 | sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config && \ 185 | sudo chown $(id -u):$(id -g) $HOME/.kube/config 186 | ``` 187 | 188 | ## Installing Addons Networking and Network Policy 189 | 190 | Untuk network plugin and policy, sebetulnya ada banyak implementasinya. bisa di check [disini](https://kubernetes.io/docs/concepts/cluster-administration/addons/) namun di materi kali ini kita akan menggunakan flannel seperti berikut untuk menginstallnya: 191 | 192 | ```bash 193 | kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml 194 | ``` 195 | 196 | ## Joining Kubernetes Workers node 197 | 198 | Untuk multiple nodes, kita bisa join ke master / control panel dengan perintah yang tadi yaitu 199 | 200 | ```bash 201 | kubeadm join 192.168.88.140:6443 --token 6bo11m.i5517ihphsnuuj67 \ 202 | --discovery-token-ca-cert-hash sha256:9ee47b6f4a02623839c33281a8692ac637f41537913e0baa33b53cddc3647335 203 | ``` 204 | 205 | Or you can create new token 206 | 207 | ```bash 208 | kubeadm token create --print-join-command 209 | ``` 210 | 211 | ## Control plane node isolation (Optional) 212 | 213 | By default, your cluster will not schedule Pods on the control-plane node for security reasons. If you want to be able to schedule Pods on the control-plane node, for example for a single-machine Kubernetes cluster for development, run: 214 | 215 | ```bash 216 | kubectl taint nodes --all node-role.kubernetes.io/master- 217 | ``` 218 | 219 | ## Adding a ip address to the Kubernetes API Server Certificate 220 | 221 | ref: 222 | - https://blog.scottlowe.org/2019/07/30/adding-a-name-to-kubernetes-api-server-certificate/ 223 | -------------------------------------------------------------------------------- /docs/03-setup-gitlab-runner.markdown: -------------------------------------------------------------------------------- 1 | ## Installing Gitlab Runner 2 | 3 | Untuk installing gitlab runner kita bisa menggunakan Linux Repository distribution yaitu dengan menggunakan perintah berikut: 4 | 5 | ```bash 6 | curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash && \ 7 | sudo yum install gitlab-runner 8 | ``` 9 | 10 | Setelah di install, kita Register gitlab-runner ke Gitlab Repository ada pun yang harus di perhatikan adalah 11 | 12 | 1. Registered as Global 13 | 2. Registered by group 14 | 3. Registered by project 15 | 16 | Pilih yang mana? ini tergantung dari kebutuhan ada yang ingin semua project pake gitlab runner brati kita register sebagai Global (Menu `Admin -> Runners`), ada yang per project (Menu `Your project -> Settings -> CI/CD -> Runners`) jadi kita pilih by project. Karena disini saya mau general kita pilih yang Global. yang kita perlukan adalah `URL` dan `Registration token` seperti berikut: 17 | 18 | ![gitlab-runner-register](images/gitlab-runner/01-gitlab-runner-register.png) 19 | 20 | Sekarang kita register, gitlab runner agent ke gitlab dengan menggunakan perintah berikut: 21 | 22 | ```bash 23 | export GITLAB_URL=http://localhost:1234 && \ 24 | export GITLAB_RUNNER_TOKEN=TY2wETK5UiFrXu6vhXHA && \ 25 | export GITLAB_RUNNER_EXTRA_HOST=subdomain.domain.com:localhost && \ 26 | sudo gitlab-runner register \ 27 | -r=${GITLAB_RUNNER_TOKEN} \ 28 | --name=gitlab-runner-docker-executor \ 29 | --non-interactive \ 30 | --url=${GITLAB_URL} \ 31 | --clone-url=${GITLAB_URL} \ 32 | --executor="docker" \ 33 | --docker-image="alpine:latest" \ 34 | --docker-disable-entrypoint-overwrite=false \ 35 | --docker-oom-kill-disable=false \ 36 | --env="DOCKER_TLS_CERTDIR=" \ 37 | --docker-extra-hosts=${GITLAB_RUNNER_EXTRA_HOST} \ 38 | --docker-privileged=true \ 39 | --tag-list="docker" 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/04-gitlab-kubernetes-integration.markdown: -------------------------------------------------------------------------------- 1 | ## How to Integrate Gitlab with Existing kubernetes cluster 2 | 3 | Pertama, create project misalnya dengan nama `springboot-gitlab-cicd-k8s-example`. 4 | 5 | ## Register kubernetes cluster 6 | 7 | Kemudian ke menu `Infrastructure -> Kubernetes cluster -> Integrate with a cluster certificate` seperti berikut: 8 | 9 | ![integrate-cluster](images/gitlab-integration/01-integrate-cluster.png) 10 | 11 | Setelah itu pilih `Connect existing cluster` seperti berikut: 12 | 13 | ![connect-exist-cluster](images/gitlab-integration/02-connect-existing.png) 14 | 15 | Kemudian isi fieldnya sesuai dengan kubernetes cluster. 16 | 17 | - Kubernetes cluster name: `dev.cluster.k8s.dimas-maryanto.com` 18 | - Environment scope: `*` 19 | - API URL: jalankan perintah `kubectl cluster-info | grep -E 'Kubernetes master|Kubernetes control plane' | awk '/http/ {print $NF}'` 20 | - CA Certificate: cari secret dengan prefix `default-token-xxxxx` menggunakan perintah `kubectl get sercrets`, kemudian ambil menggunakan perintah `kubectl get secret -o jsonpath="{['data']['ca\.crt']}" | base64 --decode` or `kubectl get secret $(kubectl get secret | awk '/^default-token/ {print $1}') -o jsonpath="{['data']['ca\.crt']}" | base64 --decode` 21 | - Service Token: jalankan file `kubectl create namespace gitlab-managed-apps && kubectl apply -f .gitlab/gitlab-service-account.yaml`, kemudian ambil tokennya dengan perintah `kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep gitlab | awk '{print $1}')` 22 | 23 | Jika sudah maka hasilnya, seperti berikut: 24 | 25 | ![cluster-registered](images/gitlab-integration/03-cluster-registered.png) 26 | 27 | Setelah itu set `Settings -> CI/CD -> Variables` seperti berikut: 28 | 29 | ![cicd-variables](images/gitlab-integration/04-cicd-variables.png) 30 | 31 | CI/CD Variables description: 32 | 33 | ```yaml 34 | - CI_REGISTRY: 35 | Value: "docker.io" # diisi dengan url docker registry atau insecure registry (hosted) 36 | Type: "Variable" 37 | Environtment scope: "All" 38 | - CI_REGISTRY_PULL: 39 | Value: "docker.io" # diisi dengan url docker registry atau insecure registry (proxy atau group) 40 | Type: "Variable" 41 | Environtment scope: "All" 42 | - DOCKER_CONF_JSON: 43 | Value: "{ auths: { ... }}" # ambil dari file `cat .docker/config.json` 44 | Type: "File" 45 | Environtment scope: "All" 46 | - KUBE_CONFIG: 47 | Value: "apiVersion: v1 clusters: - cluster:..." # ambil dari file `cat .kube/config` 48 | Type: "File" 49 | Environtment scope: "All" 50 | - M2_SETTINGS_XML: 51 | Value: " Environments` untuk yang kita inginkan sebagai contoh `review` dan `production` seperti berikut: 8 | 9 | ![setup-environtment](images/multi-clusters/02-environments.png) 10 | 11 | Setelah itu kita setup juga `Settings -> CI/CD -> Environment Variable`, Kita tambahkan env `KUBE_CONFIG` untuk Environment Scope `production` dan `review` seperti berikut: 12 | 13 | ![setup-env-variables](images/multi-clusters/03-env-variables.png) 14 | 15 | ## Trigger build 16 | 17 | Untuk men-trigger source-code kita bisa membuat `git tag`, atau bisa melalui gitlab menu `Repository -> Tags` kemudian buat tag dengan prefix `-release` contohnya seperti berikut: 18 | 19 | ![trigger-build](images/multi-clusters/04-git-tags.png) 20 | 21 | Kemudian kita bisa check di menu `CI/CD -> Pipeline` seperti berikut workflow 22 | 23 | ![cicd-pipeline](images/multi-clusters/05-pipeline-workflow.png) 24 | 25 | Sekarang kita bisa liat di Stage `Review -> review-apps` maka hasilnya seperti berikut: 26 | 27 | ![review-apps-stage](images/multi-clusters/06-pipeline-review-apps.png) 28 | 29 | Selain itu juga kita bisa check di `Deployments -> Environtment -> Review` seperti berikut: 30 | 31 | ![env-review-apps](images/multi-clusters/07-environment-review-apps.png) 32 | 33 | Aplikasi kita sudah terdeploy di kubernetes cluster dengan node `dev.dimas-maryanto.com` 34 | 35 | Selanjutnya kita kita mau deploy ke stage `production`, kita bisa jalankan `Play Button` pada menu `CI/CD -> Pipeline -> your version tags -> Run Manual -> Deploying` seperti berikut 36 | 37 | ![production-stage](images/multi-clusters/08-deploying-button.png) 38 | 39 | Setelah itu check stage Pipeline `deploying` maka hasilnya akan mendeploy ke kubernetes cluster production yaitu `prod.dimas-maryanto.com` seperti berikut: 40 | 41 | ![production-pipeline](images/multi-clusters/09-pipeline-deploy-prod.png) 42 | 43 | Kemudian kita liat di sisi `Deployments -> Environments -> Production` hasilnya seperti berikut: 44 | 45 | ![env-production-apps](images/multi-clusters/10-env-production-apps.png) 46 | 47 | Jadi kesimpulannya, kita bisa deployment secara otomatis ke environment `development`, `staging`, bahkan `production` dengan sangat simple yaitu menggunakan trigger 48 | -------------------------------------------------------------------------------- /docs/06-setup-kubernetes-dashboard.markdown: -------------------------------------------------------------------------------- 1 | # Installing Kubernetes Dashboard 2 | 3 | Create file `dashboard-service-account.yaml` seperti berikut: 4 | 5 | ```yaml 6 | apiVersion: v1 7 | kind: ServiceAccount 8 | metadata: 9 | name: admin-user 10 | namespace: kubernetes-dashboard 11 | --- 12 | apiVersion: rbac.authorization.k8s.io/v1 13 | kind: ClusterRoleBinding 14 | metadata: 15 | name: admin-user 16 | roleRef: 17 | apiGroup: rbac.authorization.k8s.io 18 | kind: ClusterRole 19 | name: cluster-admin 20 | subjects: 21 | - kind: ServiceAccount 22 | name: admin-user 23 | namespace: kubernetes-dashboard 24 | ``` 25 | 26 | Kemudian, kita jalankan perintah berikut: 27 | 28 | ```bash 29 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.0/aio/deploy/recommended.yaml && \ 30 | kubectl apply -f dashboard-service-account.yaml && \ 31 | kubectl get secret -n kubernetes-dashboard $(kubectl get serviceaccount admin-user -n kubernetes-dashboard -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" | base64 --decode 32 | ``` 33 | 34 | You’ll then see an output of a long string of seemingly random characters. Untuk menakses kubernetes Dashboard saya lebih suka menggunakan service. jadi kita edit servicenya dengan perintah berikut: 35 | 36 | ```bash 37 | kubectl -n kubernetes-dashboard edit service kubernetes-dashboard 38 | ``` 39 | 40 | You should see yaml representation of the service. Change type: ClusterIP to type: NodePort and save file. If it's already changed go to next step. 41 | 42 | ```yaml 43 | # Please edit the object below. Lines beginning with a '#' will be ignored, 44 | # and an empty file will abort the edit. If an error occurs while saving this file will be 45 | # reopened with the relevant failures. 46 | # 47 | apiVersion: v1 48 | kind: Service 49 | metadata: 50 | annotations: 51 | kubectl.kubernetes.io/last-applied-configuration: | 52 | {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"k8s-app":"kubernetes-dashboard"},"name":"kubernetes-dashboard","namespace":"kubernetes-dashboard"},"spec":{"ports":[{"port":443,"targetPort":8443}],"selector":{"k8s-app":"kubernetes-dashboard"}}} 53 | creationTimestamp: "2021-07-22T08:18:38Z" 54 | labels: 55 | k8s-app: kubernetes-dashboard 56 | name: kubernetes-dashboard 57 | namespace: kubernetes-dashboard 58 | resourceVersion: "9465" 59 | uid: be33647d-7305-4013-b95d-d803fbfc7d8a 60 | spec: 61 | clusterIP: 10.103.180.102 62 | clusterIPs: 63 | - 10.103.180.102 64 | externalTrafficPolicy: Cluster 65 | ipFamilies: 66 | - IPv4 67 | ipFamilyPolicy: SingleStack 68 | ports: 69 | - nodePort: 30106 70 | port: 443 71 | protocol: TCP 72 | targetPort: 8443 73 | selector: 74 | k8s-app: kubernetes-dashboard 75 | sessionAffinity: None 76 | type: NodePort 77 | status: 78 | loadBalancer: {} 79 | ``` 80 | 81 | Kemudian, untuk mendapatkan nilai portnya gunakan perintah seperti berikut: 82 | 83 | ```bash 84 | kubectl -n kubernetes-dashboard get service kubernetes-dashboard 85 | ``` 86 | 87 | hasilnya seperti berikut: 88 | 89 | ```bash 90 | [devops@dev01 ~]$ kubectl -n kubernetes-dashboard get service kubernetes-dashboard 91 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 92 | kubernetes-dashboard NodePort 10.103.180.102 443:30106/TCP 14m 93 | ``` 94 | 95 | Kemudian coba akses [https://host:port](https://localhost:30106) hasilnya seperti berikut: 96 | 97 | ![kubernetes-dashboard](images/kubernetes-dashboard/01-dashboard-login.png) 98 | 99 | Setelah itu kita bisa masukan token yang telah kita dapatkan tadi kemudian login, maka hasilnya seperti berikut: 100 | 101 | ![kubernetes-dashboard-login](images/kubernetes-dashboard/02-dashboard-login.png) 102 | -------------------------------------------------------------------------------- /docs/07-prometheus-matrics.markdown: -------------------------------------------------------------------------------- 1 | ## Prometheus Prerequisites 2 | 3 | To use this integration: 4 | 5 | 1. Prometheus must be installed in your cluster in the gitlab-managed-apps namespace. 6 | 2. The Service resource for Prometheus must be named prometheus-prometheus-server. 7 | 8 | ```bash 9 | # Create the required Kubernetes namespace 10 | kubectl create ns gitlab-managed-apps 11 | 12 | # Download Helm chart values that is compatible with the requirements above. 13 | # These are included in the Cluster Management project template. 14 | wget https://gitlab.com/gitlab-org/project-templates/cluster-management/-/raw/master/applications/prometheus/values.yaml 15 | 16 | # Add the Prometheus community Helm chart repository 17 | helm repo add prometheus-community https://prometheus-community.github.io/helm-charts 18 | 19 | # Install Prometheus 20 | helm install prometheus prometheus-community/prometheus -n gitlab-managed-apps --values values.yaml 21 | ``` 22 | 23 | Jika pods `prometheus-server-xxx` statusnya pending karena persistencevolumeclaim, kita edit 24 | 25 | ```bash 26 | ## edit resource 27 | kubectl edit pv prometheus-prometheus-server -n gitlab-managed-apps 28 | ``` 29 | 30 | Tambahakan propery `spec.volumeName: "prometheus-prometheus-server-pv"` 31 | 32 | Kemudian buat PersistenceVolume dengan menjalankan perintah berikut: 33 | 34 | ```bash 35 | kubectl apply -f .gitlab/prometheus/pv.yaml -n gitlab-managed-apps 36 | ``` 37 | 38 | ## Enable Prometheus integration for your cluster 39 | 40 | To enable the Prometheus integration for your cluster: 41 | 42 | 1. Go to the cluster's page: 43 | 1. For a project-level cluster, navigate to your project's 44 | Infrastructure > Kubernetes clusters. 45 | 2. For a group-level cluster, navigate to your group's 46 | Kubernetes page. 47 | 3. For an instance-level cluster, navigate to your instance's 48 | Kubernetes page. 49 | 2. Select the Integrations tab. 50 | 3. Check the Enable Prometheus integration checkbox. 51 | 4. Click Save changes. 52 | 5. Go to the Health tab to see your cluster's metrics. 53 | 54 | ## View monitor 55 | 56 | To View the Monitoring your project: 57 | 58 | 1. Go to project repository -> Menu -> Monitor -> Metrics 59 | 2. Select **K8s pod health** -> your environment -> select your **Pod name** 60 | 61 | hasilnya seperti berikut: 62 | 63 | ![metrix-cpu](images/metrixs/prometheus.png) 64 | -------------------------------------------------------------------------------- /docs/08-metrics-server.markdown: -------------------------------------------------------------------------------- 1 | ## metrics-server 2 | 3 | Enable [metrics-server](https://github.com/kubernetes-sigs/metrics-server) for auto scale horizontal/vertical pod scaler. Metrics Server can be installed either directly from YAML manifest or via the official [Helm chart](https://artifacthub.io/packages/helm/metrics-server/metrics-server). To install the latest Metrics Server release from the components.yaml manifest, run the following command. 4 | 5 | ```bash 6 | kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml 7 | ``` 8 | 9 | ### Configuration 10 | 11 | Depending on your cluster setup, you may also need to change flags passed to the Metrics Server container. Most useful flags: 12 | 13 | 1. `--kubelet-preferred-address-types` - The priority of node address types used when determining an address for connecting to a particular node (default [Hostname,InternalDNS,InternalIP,ExternalDNS,ExternalIP]) 14 | 2. `--kubelet-insecure-tls` - Do not verify the CA of serving certificates presented by Kubelets. For testing purposes only. 15 | 3. `--requestheader-client-ca-file` - Specify a root certificate bundle for verifying client certificates on incoming requests. 16 | 17 | If pods `metrics-server-xxx` is pending because you should ignore secure tls using command: 18 | 19 | ```bash 20 | [root@vm1 ~]# kubectl get deploy -n kube-system 21 | NAME READY UP-TO-DATE AVAILABLE AGE 22 | coredns 2/2 2 2 5m32s 23 | metrics-server 0/1 1 0 113s 24 | 25 | ## add `--kubelet-insecure-tls` args in spec.template.spec.container.args using this command 26 | 27 | kubectl edit deploy/metrics-server -n kube-system 28 | 29 | ## look like this 30 | spec: 31 | replicas: 1 32 | template: 33 | spec: 34 | containers: 35 | - args: 36 | - --cert-dir=/tmp 37 | - --secure-port=4443 38 | - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname 39 | - --kubelet-use-node-status-port 40 | - --kubelet-insecure-tls 41 | - --metric-resolution=15s 42 | 43 | ## after word, this the result 44 | [root@vm1 ~]# kubectl get deploy -n kube-system 45 | NAME READY UP-TO-DATE AVAILABLE AGE 46 | coredns 2/2 2 2 9m8s 47 | metrics-server 1/1 1 1 5m29s 48 | ``` -------------------------------------------------------------------------------- /docs/09-loadbalancer-metallb.markdown: -------------------------------------------------------------------------------- 1 | ## Install loadbalancer using metallb 2 | 3 | [MetalLB](https://metallb.universe.tf/concepts/) hooks into your Kubernetes cluster, and provides a network load-balancer implementation. In short, it allows you to create Kubernetes services of type LoadBalancer in clusters that don’t run on a cloud provider, and thus cannot simply hook into paid products to provide load balancers. 4 | 5 | If you are trying to automate this change, these shell snippets may help you: 6 | 7 | ```bash 8 | # actually apply the changes, returns nonzero returncode on errors only 9 | kubectl get configmap kube-proxy -n kube-system -o yaml | \ 10 | sed -e "s/strictARP: false/strictARP: true/" | \ 11 | kubectl apply -f - -n kube-system 12 | ``` 13 | 14 | To install MetalLB, apply the manifest: 15 | 16 | ```bash 17 | kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml 18 | kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml 19 | ``` 20 | 21 | This will deploy MetalLB to your cluster, under the metallb-system namespace. The components in the manifest are: 22 | 23 | 1. The metallb-system/controller deployment. This is the cluster-wide controller that handles IP address assignments. 24 | 2. The metallb-system/speaker daemonset. This is the component that speaks the protocol(s) of your choice to make the services reachable. 25 | 3. Service accounts for the controller and speaker, along with the RBAC permissions that the components need to function. 26 | 27 | ## Configuration 28 | 29 | MetalLB remains idle until configured. This is accomplished by creating and deploying a config map into the same namespace (metallb-system) as the deployment. 30 | 31 | There is an example config map in [manifests/example-config.yaml](https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/example-config.yaml), annotated with explanatory comments. 32 | 33 | 34 | ```yaml 35 | apiVersion: v1 36 | kind: ConfigMap 37 | metadata: 38 | namespace: metallb-system 39 | name: config 40 | data: 41 | config: | 42 | address-pools: 43 | - name: metallb.domain.example/address-pool 44 | protocol: layer2 45 | addresses: 46 | - 192.168.100.11-192.168.100.15 47 | ``` 48 | 49 | Save as `metallb-config.yaml` then `kubectl apply -f metallb-config.yaml` -------------------------------------------------------------------------------- /docs/10-ingress-controller.markdown: -------------------------------------------------------------------------------- 1 | In order for the Ingress resource to work, the cluster must have an ingress controller running. 2 | 3 | [ingress-nginx](https://github.com/kubernetes/ingress-nginx) is an Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer. 4 | 5 | This section is applicable to Kubernetes clusters deployed on bare metal servers, as well as "raw" VMs where Kubernetes was installed manually, using generic Linux distros (like CentOS, Ubuntu...) 6 | 7 | For quick testing, you can use a [NodePort](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport). This should work on almost every cluster, but it will typically use a port in the range 30000-32767. 8 | 9 | ```bash 10 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/baremetal/deploy.yaml 11 | ``` 12 | 13 | Now we can check service port run at using command: 14 | 15 | ```bash 16 | [root@vm1 ~]# kubectl get svc -n ingress-nginx 17 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 18 | ingress-nginx-controller NodePort 10.99.206.95 80:30886/TCP,443:30590/TCP 3m10s 19 | ingress-nginx-controller-admission ClusterIP 10.101.46.178 443/TCP 3m10s 20 | 21 | [root@vm1 ~]# curl -D- localhost:30886 22 | HTTP/1.1 404 Not Found 23 | Date: Wed, 23 Feb 2022 06:08:30 GMT 24 | Content-Type: text/html 25 | Content-Length: 146 26 | Connection: keep-alive 27 | 28 | 29 | 404 Not Found 30 | 31 |

404 Not Found

32 |
nginx
33 | 34 | 35 | ``` 36 | 37 | For testing perpose, we can deploy 2 container then mapped to `/web1` and `/web2` using this command: 38 | 39 | ```bash 40 | kubectl apply -f https://gist.githubusercontent.com/dimMaryanto93/a3a01b83910cf07914935a25a62d30ce/raw/1fa9b1bae3248f3e8b622314264c3677f1b5b2fd/02f-minikube-ingress.yaml 41 | 42 | [root@vm1 ~]# kubectl get ing 43 | NAME CLASS HOSTS ADDRESS PORTS AGE 44 | webapp-ingress nginx.example.info 80 30s 45 | 46 | [root@vm1 ~]# curl -D- -H "Host: nginx.example.info" localhost:30886/web1 47 | HTTP/1.1 200 OK 48 | Date: Wed, 23 Feb 2022 06:11:19 GMT 49 | Content-Type: text/html 50 | Content-Length: 615 51 | Connection: keep-alive 52 | Last-Modified: Tue, 25 Jan 2022 15:03:52 GMT 53 | ETag: "61f01158-267" 54 | Accept-Ranges: bytes 55 | 56 | 57 | 58 | 59 | Welcome to nginx! 60 | 65 | 66 | 67 |

Welcome to nginx!

68 |

If you see this page, the nginx web server is successfully installed and 69 | working. Further configuration is required.

70 | 71 |

For online documentation and support please refer to 72 | nginx.org.
73 | Commercial support is available at 74 | nginx.com.

75 | 76 |

Thank you for using nginx.

77 | 78 | 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/images/gitlab-integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/gitlab-integration/.gitkeep -------------------------------------------------------------------------------- /docs/images/gitlab-integration/01-integrate-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/gitlab-integration/01-integrate-cluster.png -------------------------------------------------------------------------------- /docs/images/gitlab-integration/02-connect-existing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/gitlab-integration/02-connect-existing.png -------------------------------------------------------------------------------- /docs/images/gitlab-integration/03-cluster-registered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/gitlab-integration/03-cluster-registered.png -------------------------------------------------------------------------------- /docs/images/gitlab-integration/04-cicd-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/gitlab-integration/04-cicd-variables.png -------------------------------------------------------------------------------- /docs/images/gitlab-runner/01-gitlab-runner-register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/gitlab-runner/01-gitlab-runner-register.png -------------------------------------------------------------------------------- /docs/images/kubernetes-cluster/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/kubernetes-cluster/.gitkeep -------------------------------------------------------------------------------- /docs/images/kubernetes-dashboard/01-dashboard-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/kubernetes-dashboard/01-dashboard-login.png -------------------------------------------------------------------------------- /docs/images/kubernetes-dashboard/02-dashboard-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/kubernetes-dashboard/02-dashboard-login.png -------------------------------------------------------------------------------- /docs/images/metrixs/prometheus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/metrixs/prometheus.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/01-kubernetes-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/01-kubernetes-integration.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/02-environments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/02-environments.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/03-env-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/03-env-variables.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/04-git-tags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/04-git-tags.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/05-pipeline-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/05-pipeline-workflow.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/06-pipeline-review-apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/06-pipeline-review-apps.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/07-environment-review-apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/07-environment-review-apps.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/08-deploying-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/08-deploying-button.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/09-pipeline-deploy-prod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/09-pipeline-deploy-prod.png -------------------------------------------------------------------------------- /docs/images/multi-clusters/10-env-production-apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/multi-clusters/10-env-production-apps.png -------------------------------------------------------------------------------- /docs/images/virtual-machines/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/virtual-machines/.gitkeep -------------------------------------------------------------------------------- /docs/images/workflow/node-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/workflow/node-stack.png -------------------------------------------------------------------------------- /docs/images/workflow/workflow-devops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimMaryanto93/springboot-gitlab-cicd-k8s-example/6686f9bfba26178ff31813e680b838f3ed344d8b/docs/images/workflow/workflow-devops.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.5 9 | 10 | 11 | com.maryanto.dimas.example 12 | springboot-k8s-example 13 | 0.0.1-SNAPSHOT 14 | springboot-k8s-example 15 | Demo project for Spring Boot 16 | 17 | 18 | 11 19 | ${project.artifactId} 20 | example/k8s-springboot 21 | 192.168.88.50:8087 22 | 192.168.88.50:8086 23 | 1.1.1 24 | 0.8.4 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.flywaydb 34 | flyway-core 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-data-jpa 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-jdbc 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-thymeleaf 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-validation 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-web 56 | 57 | 58 | 59 | org.postgresql 60 | postgresql 61 | 62 | 63 | org.projectlombok 64 | lombok 65 | true 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-test 70 | test 71 | 72 | 73 | 74 | 75 | ${project.name}-${project.version} 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-maven-plugin 80 | 81 | true 82 | 83 | 84 | 85 | org.codehaus.mojo 86 | versions-maven-plugin 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-compiler-plugin 91 | 92 | 11 93 | 11 94 | 95 | 96 | 97 | com.spotify 98 | dockerfile-maven-plugin 99 | 1.4.13 100 | 101 | true 102 | ${docker-registry-host-push}/${docker-registry-group}/${docker-registry-image} 103 | ${project.version} 104 | 105 | ${project.build.finalName}.${project.packaging} 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/main/java/com/maryanto/dimas/example/SpringbootK8sExampleApplication.java: -------------------------------------------------------------------------------- 1 | package com.maryanto.dimas.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringbootK8sExampleApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringbootK8sExampleApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kubernetes/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ${CI_PROJECT_PATH_SLUG} 5 | annotations: 6 | app.gitlab.com/app: ${CI_PROJECT_PATH_SLUG} 7 | app.gitlab.com/env: ${CI_ENVIRONMENT_SLUG} 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: ${CI_PROJECT_PATH_SLUG} 12 | env: ${CI_ENVIRONMENT_SLUG} 13 | template: 14 | metadata: 15 | labels: 16 | app: ${CI_PROJECT_PATH_SLUG} 17 | env: ${CI_ENVIRONMENT_SLUG} 18 | annotations: 19 | app.gitlab.com/app: ${CI_PROJECT_PATH_SLUG} 20 | app.gitlab.com/env: ${CI_ENVIRONMENT_SLUG} 21 | spec: 22 | containers: 23 | - name: ${CI_PROJECT_PATH_SLUG} 24 | image: ${CI_REGISTRY}/${CI_REGISTRY_NAMESPACE}/${CI_REGISTRY_IMAGE}:${CI_IMAGE_VERSION} 25 | ports: 26 | - containerPort: ${CI_IMAGE_EXPOSE_PORT} 27 | env: 28 | - name: DATABASE_HOST 29 | value: "${CI_PROJECT_PATH_SLUG}-db.${KUBE_NAMESPACE}.svc.cluster.local" 30 | - name: DATABASE_PORT 31 | value: "5432" 32 | - name: DATABASE_NAME 33 | value: "k8s_spring_example" 34 | - name: DATABASE_USER 35 | value: "k8s_spring_example" 36 | - name: DATABASE_PASSWORD 37 | value: "k8s_spring_example" 38 | - name: APPLICATION_PORT 39 | value: "${CI_IMAGE_EXPOSE_PORT}" 40 | imagePullSecrets: 41 | - name: ${DOCKER_REGCRED} 42 | --- 43 | apiVersion: apps/v1 44 | kind: Deployment 45 | metadata: 46 | name: ${CI_PROJECT_PATH_SLUG}-db 47 | annotations: 48 | app.gitlab.com/app: ${CI_PROJECT_PATH_SLUG} 49 | app.gitlab.com/env: ${CI_ENVIRONMENT_SLUG} 50 | spec: 51 | selector: 52 | matchLabels: 53 | app: ${CI_PROJECT_PATH_SLUG}-db 54 | env: ${CI_ENVIRONMENT_SLUG}-db 55 | template: 56 | metadata: 57 | labels: 58 | app: ${CI_PROJECT_PATH_SLUG}-db 59 | env: ${CI_ENVIRONMENT_SLUG}-db 60 | annotations: 61 | app.gitlab.com/app: ${CI_PROJECT_PATH_SLUG} 62 | app.gitlab.com/env: ${CI_ENVIRONMENT_SLUG} 63 | spec: 64 | containers: 65 | - name: ${CI_PROJECT_PATH_SLUG}-db 66 | image: ${CI_REGISTRY_PULL}/postgres:12.6 67 | ports: 68 | - containerPort: 5432 69 | env: 70 | - name: POSTGRES_PASSWORD 71 | value: "k8s_spring_example" 72 | - name: POSTGRES_USER 73 | value: "k8s_spring_example" 74 | - name: POSTGRES_DB 75 | value: "k8s_spring_example" 76 | imagePullSecrets: 77 | - name: ${DOCKER_REGCRED} 78 | -------------------------------------------------------------------------------- /src/main/kubernetes/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: ${CI_PROJECT_PATH_SLUG} 6 | spec: 7 | ports: 8 | - port: ${CI_IMAGE_EXPOSE_PORT} 9 | targetPort: ${CI_IMAGE_EXPOSE_PORT} 10 | name: ${CI_PROJECT_PATH_SLUG} 11 | nodePort: ${CI_NODE_PORT_EXPOSE} 12 | selector: 13 | app: ${CI_PROJECT_PATH_SLUG} 14 | env: ${CI_ENVIRONMENT_SLUG} 15 | type: NodePort 16 | --- 17 | apiVersion: v1 18 | kind: Service 19 | metadata: 20 | name: ${CI_PROJECT_PATH_SLUG}-db 21 | spec: 22 | ports: 23 | - port: 5432 24 | targetPort: 5432 25 | name: ${CI_PROJECT_PATH_SLUG}-db 26 | selector: 27 | app: ${CI_PROJECT_PATH_SLUG}-db 28 | env: ${CI_ENVIRONMENT_SLUG}-db 29 | type: ClusterIP 30 | -------------------------------------------------------------------------------- /src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | spring: 4 | mvc: 5 | dispatch-options-request: true 6 | datasource: 7 | url: jdbc:postgresql://${DATABASE_HOST:localhost}:${DATABASE_PORT:5432}/${DATABASE_NAME:k8s_spring_example} 8 | username: ${DATABASE_USER:k8s_spring_example} 9 | password: ${DATABASE_PASSWORD:k8s_spring_example} 10 | jpa: 11 | generate-ddl: false 12 | hibernate: 13 | ddl-auto: validate 14 | show-sql: true 15 | properties: 16 | hibernate: 17 | dialect: org.hibernate.dialect.PostgreSQL95Dialect 18 | enable_lazy_load_no_trans: true 19 | id: 20 | new_generator_mappings: false 21 | current_session_context_class: org.springframework.orm.hibernate5.SpringSessionContext 22 | globally_quoted_identifiers: true 23 | temp: 24 | use_jdbc_metadata_defaults: false 25 | flyway: 26 | encoding: UTF-8 27 | enabled: ${FLYWAY_ENABLED:true} 28 | baseline-on-migrate: true 29 | check-location: true 30 | locations: classpath:/db/migration 31 | clean-disabled: false 32 | clean-on-validation-error: true 33 | connect-retries: 2 34 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/V20210517131608__example-table.sql: -------------------------------------------------------------------------------- 1 | create table example_table 2 | ( 3 | id varchar(64) not null primary key, 4 | nama varchar(100) 5 | ); 6 | -------------------------------------------------------------------------------- /src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Testing works 9 | 10 | 11 |

Hello World!!!

12 | 13 | 14 | -------------------------------------------------------------------------------- /src/test/java/com/maryanto/dimas/example/SpringbootK8sExampleApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.maryanto.dimas.example; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringbootK8sExampleApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------