diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b8cc189eb86c68d8a0d834fe4b11b9d02911b3d3..0674d6678ba3d07812cfbef68b526a49b595a88a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,88 +1,136 @@ workflow: rules: - - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - when: always - if: >- - $CI_PIPELINE_SOURCE == "push" - && ($CI_COMMIT_BRANCH == "staging" || $CI_COMMIT_BRANCH == "master") + $CI_COMMIT_BRANCH == "staging" || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: always - when: never variables: - DOCKER_VERSION: '20.10.12' - K8S_NAMESPACE: 'devops-s22-deployable' + DOCKER_VERSION: '20.10.17' + K8S_VERSION: '1.24' + GOOGLE_CLOUD_SDK_IMAGE_VERSION: '400.0.0' + K8S_NAMESPACE: 'todoapp-devops' CONTAINER_TAG: '${CI_REGISTRY_IMAGE}:${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}' stages: - test - build + - release - deploy -job_test-image: +.gke-initialization: + before_script: + - echo $BASE64_GOOGLE_CREDENTIALS | base64 -d > ~/service_account.json + - gcloud auth activate-service-account --key-file ~/service_account.json + - gcloud config set project ${GCP_PROJECT_ID} + - gcloud config set compute/zone ${GCP_COMPUTE_REGION} + - gcloud container clusters get-credentials ${GCP_PROJECT_ID}-gke + +test-image: stage: test tags: - docker-priviliged - image: docker:$DOCKER_VERSION + image: docker:${DOCKER_VERSION} services: - - name: docker:$DOCKER_VERSION-dind + - name: docker:${DOCKER_VERSION}-dind script: - docker build --tag "${CONTAINER_TAG}-test" --target=test "./" after_script: - - docker image rm --force "${CI_REGISTRY_IMAGE}:${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}" + - docker image rm --force "${CONTAINER_TAG}-test" -job_build-image: +build-image: stage: build needs: - - 'job_test-image' - rules: - - if: $CI_COMMIT_BRANCH != "staging" && $CI_COMMIT_BRANCH != "master" - when: 'never' + - 'test-image' tags: - docker-priviliged - image: docker:$DOCKER_VERSION + image: docker:${DOCKER_VERSION} services: - - name: docker:$DOCKER_VERSION-dind + - name: docker:${DOCKER_VERSION}-dind + before_script: + - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY} script: - - SERVER_PUBLIC_URL="" - - DB_HOST="" - - docker build + - docker image build --tag "${CONTAINER_TAG}" - --build-arg JWT_SECRET=${JWT_SECRET} - --build-arg SERVER_PUBLIC_URL=${SERVER_PUBLIC_URL} - --build-arg DB_HOST=${DB_HOST} - "./" + "./app" + # - docker image build + # --tag "${CONTAINER_TAG}-google-sdk" + # "./gcloud-image" + - docker push "${CONTAINER_TAG}" + # - docker push "${CONTAINER_TAG}-google-sdk" + after_script: + - docker image rm --force "${CONTAINER_TAG}" + # - docker image rm --force "${CONTAINER_TAG}-google-sdk" + +create-release_image: + stage: release + rules: + - if: $CI_COMMIT_REF_NAME =~ /main/ + when: 'always' + tags: + - docker-privileged + image: docker:${DOCKER_VERSION} + services: + - name: docker:${DOCKER_VERSION}-dind + before_script: - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY} - - docker push "${CI_REGISTRY_IMAGE}:${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}" + - docker pull "${CONTAINER_TAG}" + script: + - VERSION=$(cat ./app/client/package.json | docker run --interactive stedolan/jq --raw-output '.version') + - docker tag "${CONTAINER_TAG}" "${CI_REGISTRY_IMAGE}:${VERSION}" + - docker push "${CI_REGISTRY_IMAGE}:${VERSION}" after_script: - - docker image rm --force "${CI_REGISTRY_IMAGE}:${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}" + - docker image rm --force + $(docker images --format '{{.Repository}}{{.Tag}}' | grep '${CI_REGISTRY_IMAGE}') + +# create-release_tag: +# stage: release +# needs: +# - 'create-release_image' +# rules: +# - if: $CI_COMMIT_REF_NAME =~ /main/ +# when: 'always' +# tags: +# - docker-privileged +# image: docker:${DOCKER_VERSION}-git +# services: +# - name: docker:${DOCKER_VERSION}-dind +# script: +# - VERSION=$(cat ./src/package.json | docker run --interactive stedolan/jq --raw-output '.version') +# - echo ${VERSION} +# #- git tag "v${VERSION}" && git push origin "v${VERSION}" + -job_deploy-image: +deploy-image: + extends: .gke-initialization stage: deploy needs: - - 'job_build-image' + - 'build-image' rules: - if: $CI_COMMIT_REF_NAME =~ /staging/ when: 'always' variables: ENVIRONMENT_NAME: 'staging' - - if: $CI_COMMIT_REF_NAME !~ /staging/ + - if: $CI_COMMIT_REF_NAME !~ /main/ when: 'always' variables: - ENVIRONMENT_NAME: 'prod' + ENVIRONMENT_NAME: 'production' tags: - docker - image: - name: k8s-image - entrypoint: [''] + image: google/cloud-sdk:${GOOGLE_CLOUD_SDK_IMAGE_VERSION} script: - # - IMAGE="${CI_REGISTRY_IMAGE}:${VERSION}" - - kubectl apply - --kubeconfig ${SECRET_KUBECONFIG_PATH} - --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" - --kustomize - + - VERSION="${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}" + - IMAGE="${CI_REGISTRY_IMAGE}:${VERSION}" + - cd "app/k8s-manifests" + - kubectl apply secrets.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" + - kubectl apply gitlab-registry-credentials.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" + - kubectl apply configmap.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" + - kubectl apply deployment.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" + - kubectl apply service.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" + - kubectl apply ingress.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" diff --git a/app/.dockerignore b/app/.dockerignore index 5734ba9c6b204ea1f73f9464e3ccc2588ef34b8a..114cdfa370b22bc268c72cbaca6e5e0bfa142f77 100644 --- a/app/.dockerignore +++ b/app/.dockerignore @@ -2,3 +2,5 @@ client/node_modules server/node_modules .dockerignore Dockerfile +README.md +npm-debug.log diff --git a/app/Dockerfile b/app/Dockerfile index 2ac29aaa73300b4afd5e6bc8e0a979e427f0b3dc..e2085b54bb78cb0f1bb350c5a0fb5cb21ec10e39 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -6,7 +6,7 @@ ARG NODEJS_VERSION='16.17.0' FROM node:$NODEJS_VERSION-alpine AS base ENV \ - PORT=3002 \ + PORT=3000 \ # For local dev - mongodb://host.docker.internal:27017/todo-app MONGODB_URL=<should-be-dynamically-set> \ JWT_SECRET=<should-be-dynamically-set> diff --git a/app/README.md b/app/README.md index 50acb84f248753553cecf00728626276be21b869..61839f4e246f1f7f4ce9a2bdfc5de254f3c43791 100644 --- a/app/README.md +++ b/app/README.md @@ -1,6 +1,6 @@ Todo-App ======== - +d This application represents the *deployable workload* for the [lecture assignment](https://github.com/lucendio/lecture-devops-material/blob/master/assignments/exercise.md). diff --git a/app/k8s-manifests/configmap.yaml b/app/k8s-manifests/configmap.yaml deleted file mode 100644 index 967de9f7d051a9e1147df7e38a0d4bbbd0d68682..0000000000000000000000000000000000000000 --- a/app/k8s-manifests/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: app-config -data: - PORT: 3000 - # MONGODB_URL: mongo-service.namespace (from infra repo. Does this work? Or do I have to dynamically set it?) - MONGODB_URL: mongodb://<mongo-service>:27017/todo-app diff --git a/app/k8s-manifests/service.yaml b/app/k8s-manifests/service.yaml deleted file mode 100644 index c5a84a2260de00652aef4030547308593b3d7a85..0000000000000000000000000000000000000000 --- a/app/k8s-manifests/service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: todo-app-service -spec: - selector: - app: todo-app - ports: - - port: 8080 - targetPort: 3000 - # port for external IP address (that you need to put in your browser) - # nodePort: 30000 - # type: LoadBalancer diff --git a/k8s-manifests/configmap.yaml b/k8s-manifests/configmap.yaml new file mode 100644 index 0000000000000000000000000000000000000000..30146169f0677f4cc8bd819ae191f69bde0dc17f --- /dev/null +++ b/k8s-manifests/configmap.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: todo-app-config +data: + PORT: 3000 + # MONGODB_URL: mongodb://<mongo-service>:27017/todo-app + # mongodb-service.<namespace>? + MONGODB_URL: mongodb-service diff --git a/app/k8s-manifests/deployment.yaml b/k8s-manifests/deployment.yaml similarity index 79% rename from app/k8s-manifests/deployment.yaml rename to k8s-manifests/deployment.yaml index 97eb0bf960f6a39814da2ce7fee6cd25bc54ffb4..2de3f43c765498278154fba0a25f3ee2cb4758c6 100644 --- a/app/k8s-manifests/deployment.yaml +++ b/k8s-manifests/deployment.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: app-deployment + name: todo-app-deployment labels: app: todo-app spec: @@ -24,8 +24,9 @@ spec: ports: - containerPort: 3000 # imagePullPolicy: IfNotPresent + # env for mongodb username and pass? envFrom: - configMapRef: - name: app-config + name: todo-app-config - secretRef: - name: app-secrets + name: todo-app-secrets diff --git a/app/registry-credentials.yaml b/k8s-manifests/gitlab-registry-credentials.yaml similarity index 100% rename from app/registry-credentials.yaml rename to k8s-manifests/gitlab-registry-credentials.yaml diff --git a/k8s-manifests/ingress.yaml b/k8s-manifests/ingress.yaml new file mode 100644 index 0000000000000000000000000000000000000000..675762207cbe8609b5b265db97b48d3ce8e356fb --- /dev/null +++ b/k8s-manifests/ingress.yaml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: todo-app-ingress + annotations: + kubernetes.io/ingress.class: "gce" # an external load-balancer + kubernetes.io/ingress.allow-http: "false" # disabling HTTP + kubernetes.io/ingress.global-static-ip-name: todoapp-ip # staging-todoapp-ip for staging + networking.gke.io/managed-certificates: todoapp-managed-cert # defined in the infrastructure repository +spec: + # rules: + # - http: + # paths: + # - path: /* + # pathType: ImplementationSpecific + # backend: + # serviceName: todo-app-service + # servicePort: 80 + defaultBackend: + service: + name: todo-app-service + port: + number: 8080 diff --git a/app/k8s-manifests/secrets.yaml b/k8s-manifests/secrets.yaml similarity index 78% rename from app/k8s-manifests/secrets.yaml rename to k8s-manifests/secrets.yaml index 9097a62158fe9bcd8d67f77802123b0d176069fd..c61ce1a2dd0ce707d504b2ccb64d92130327c68a 100644 --- a/app/k8s-manifests/secrets.yaml +++ b/k8s-manifests/secrets.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Secret metadata: - name: app-secrets + name: todo-app-secrets type: Opaque data: JWT_SECRET: <jwt-secret in base64> diff --git a/k8s-manifests/service.yaml b/k8s-manifests/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4a69e79411f9e844ef6c3bc223cb38081d5f3970 --- /dev/null +++ b/k8s-manifests/service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: todo-app-service + annotations: + cloud.google.com/app-protocols: '{"https-port":"HTTPS","http-port":"HTTP"}' +spec: + type: NodePort + selector: + app: todo-app + ports: + - name: https-port + port: 443 + targetPort: 3000 + - name: http-port + port: 8080 + targetPort: 3000