Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • s28840/ss22-devops-project-app
1 result
Show changes
Commits on Source (52)
...@@ -7,13 +7,15 @@ workflow: ...@@ -7,13 +7,15 @@ workflow:
variables: variables:
DOCKER_VERSION: '20.10.17' DOCKER_VERSION: '20.10.17'
K8S_VERSION: '1.24' MONGO_VERSION: '4.4'
GOOGLE_CLOUD_SDK_IMAGE_VERSION: '400.0.0'
K8S_NAMESPACE: 'todoapp-devops' K8S_NAMESPACE: 'todoapp-devops'
GOOGLE_CLOUD_SDK_IMAGE_VERSION: '400.0.0'
GCP_PROJECT_ID: ss22-devops-app
GCP_COMPUTE_REGION: 'europe-west4'
CONTAINER_TAG: '${CI_REGISTRY_IMAGE}:${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}' CONTAINER_TAG: '${CI_REGISTRY_IMAGE}:${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}'
stages: stages:
- test # - test
- build - build
- release - release
- deploy - deploy
...@@ -26,25 +28,31 @@ stages: ...@@ -26,25 +28,31 @@ stages:
- gcloud config set compute/zone ${GCP_COMPUTE_REGION} - gcloud config set compute/zone ${GCP_COMPUTE_REGION}
- gcloud container clusters get-credentials ${GCP_PROJECT_ID}-gke - gcloud container clusters get-credentials ${GCP_PROJECT_ID}-gke
test-image: # run-tests:
stage: test # stage: test
tags: # tags:
- docker-privileged # - docker-privileged
image: docker:${DOCKER_VERSION} # image: docker:${DOCKER_VERSION}
services: # services:
- name: docker:${DOCKER_VERSION}-dind # - name: docker:${DOCKER_VERSION}-dind
script: # alias: containerhost
- docker build # script:
--tag "${CONTAINER_TAG}-test" # - docker run -p 27017:27017 -d --name mongo-container --rm mongo:${MONGO_VERSION}
--target=test # - docker build
"./app" # --tag "${CONTAINER_TAG}-test"
after_script: # --target=test
- docker image rm --force "${CONTAINER_TAG}-test" # --build-arg MONGODB_URL_TEST=mongodb://containerhost:27017/todo-app
# --build-arg JWT_SECRET_TEST=my-very-secret-jwt
# "./app"
# after_script:
# - docker stop mongo-container
# - docker images -a | grep "mongo" | awk '{print $3}' | xargs docker rmi --force
# - docker image rm --force "${CONTAINER_TAG}-test"
build-image: build-image:
stage: build stage: build
needs: # needs:
- 'test-image' # - 'run-tests'
tags: tags:
- docker-privileged - docker-privileged
image: docker:${DOCKER_VERSION} image: docker:${DOCKER_VERSION}
...@@ -55,6 +63,7 @@ build-image: ...@@ -55,6 +63,7 @@ build-image:
script: script:
- docker image build - docker image build
--tag "${CONTAINER_TAG}" --tag "${CONTAINER_TAG}"
--target=build
"./app" "./app"
# - docker image build # - docker image build
# --tag "${CONTAINER_TAG}-google-sdk" # --tag "${CONTAINER_TAG}-google-sdk"
...@@ -67,9 +76,6 @@ build-image: ...@@ -67,9 +76,6 @@ build-image:
create-release_image: create-release_image:
stage: release stage: release
rules:
- if: $CI_COMMIT_REF_NAME =~ /main/
when: 'always'
tags: tags:
- docker-privileged - docker-privileged
image: docker:${DOCKER_VERSION} image: docker:${DOCKER_VERSION}
...@@ -108,7 +114,7 @@ deploy-image: ...@@ -108,7 +114,7 @@ deploy-image:
extends: .gke-initialization extends: .gke-initialization
stage: deploy stage: deploy
needs: needs:
- 'build-image' - 'create-release_image'
rules: rules:
- if: $CI_COMMIT_REF_NAME =~ /staging/ - if: $CI_COMMIT_REF_NAME =~ /staging/
when: 'always' when: 'always'
...@@ -124,13 +130,14 @@ deploy-image: ...@@ -124,13 +130,14 @@ deploy-image:
script: script:
- VERSION="${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}" - VERSION="${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}"
- IMAGE="${CI_REGISTRY_IMAGE}:${VERSION}" - IMAGE="${CI_REGISTRY_IMAGE}:${VERSION}"
- cd "app/k8s-manifests" - cd "./k8s-manifests"
- kubectl apply secrets.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" - kubectl apply -f secrets.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
- kubectl apply gitlab-registry-credentials.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" - kubectl apply -f gitlab-registry-credentials.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
- kubectl apply configmap.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" - kubectl apply -f configmap.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
- kubectl apply deployment.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" - DEPLOYMENT_MANIFEST=$(sed -e "s,{{image}},${IMAGE},g" ./deployment.yaml.tmpl)
- kubectl apply service.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" - echo "${DEPLOYMENT_MANIFEST}" | kubectl apply --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" --filename -
- kubectl apply ingress.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" - kubectl apply -f service.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
- kubectl apply -f ingress.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
Lecture: DevOps - Application SS22-DevOps-Deployable
============================= =============================
### Introduction
> :warning: __Invoking `make` in a path containing white spaces will break execution and may lead to This repository demonstrates the automated deployment of a simple web application. It consists of two branches - **staging** and **main**.
> unforeseen side effects like DATA LOSS !__ These branches are protected, meaning that commits targetting them will fail.
Changes to the codebase are realized through the use of other temporary branches and Merge Requests to **staging**. Merge Requests to **main** should
only be opened from **staging** as a source branch.
As soon as a new change through a MR is commited to **staging**, a pipeline is triggered. It deploys the application to the **staging-todoapp-devops** namespace in a Kubernetes cluster, run on GKE. After that, the application is accessible on the URL:
This repository contains the [application](./app/README.md) that can be used as *deployable workload* for the > https://staging.todoapp-devops.software
[project assignment](https://github.com/lucendio/lecture-devops-material/blob/master/assignments/project-work.md)
implementation.
In order to adapt the application and integrate it into your automation and deployment processes, it's usually not The same process happens with commits (merged from **staging**) on the **main** branch. The web application is then accessible on:
required to change any existing code. Instead, you probably just want to add some *pipeline* code or even a container
file (e.g. `Dockerfile`).
> https://todoapp-devops.software
### Getting started
For more information regarding the app, please take a look into its [README](./app/README.md).
The `Makefile` in this directory can be seen as the main entry point for this repository. It's meant to locally run the
application and allow to mess around with the source code in order to better understand how it works and to be able to
tear it apart if necessary.
Additionally, it documents various invocations that may help you adapting this application as *workload* for the
assignment.
**_Please note, that the `Makefile` is only meant to showcase steps that are usually taken to automate the deployment
lifecycle of such an application and code base.
It is NOT recommended to invoke `make` targets from the CI/CD, but rather to utilize platform-specific interfaces
(e.g. `.gitlab-ci.yml`, `Jenkinsfile`, etc.), which may then invoke commands shown in the `make` target or in the
`scripts` section of one of the `package.json` files._**
### Prerequisites
The following software must be installed and available in `${PATH}`:
* `node` ([NodeJS](https://nodejs.org/en/download)): latest v16
* `npm` ([npm](https://www.npmjs.com/get-npm)): latest v8
* `mongod` ([MongoDB](https://docs.mongodb.com/manual/installation/)): latest v4.4
*NOTE: the application in this repository has not been tested with versions newer than that*
Choose for yourself how you want to install these dependencies. Perhaps you can use the package manager
available on your operating system, or maybe you prefer using container images.
### Commands
The following commands are available from the root directory:
#### `make install`
* installs all *server* and *client* dependencies via package manager `npm`
#### `make build`
1. copy server source into some empty location
2. copy dependency manifest (`package*`) into the same location right next to the server source
3. install server dependencies there
4. install all client dependencies next to its source
5. build client code and put it next to the server source (step 1)
#### `make test`
*NOTE: requires/assumes a MongoDB service (see `MONGODB_URL`) to be reachable from the context where tests are being
executed*
* runs client & server tests in [CI mode](https://jestjs.io/docs/en/cli.html#--ci) (exits regardless of the test outcome;
closed tty)
#### `make dev-test-client`
*NOTE: only demonstrates a use case during local development and are not meant to run in any other context (e.g. automation)*
* runs client tests in a local development mode
#### `make dev-start-db`
*NOTE: only demonstrates a use case during local development and are not meant to run in any other context (e.g. automation)*
* starts a local MongoDB service
#### `make dev-start-app`
*NOTE (1): only demonstrates a use case during local development and are not meant to run in any other context (e.g. automation)*
*NOTE (2): it might be desired to first start a database service (e.g. `make dev-start-db`)*
* builds client
* starts server in development mode with development configuration
#### `make run`
*NOTE (1): showcases plain executable invocation with a shared parent process. Press `Ctrl+C` to send termination signal.*
*NOTE (2): in reality those two services would always be invoked independently and __never__ share a parent process!*
* starts a MongoDB service as a child process with an explicit inline-configuration
* starts the application service with variables being set in a way so that they are only *visible* to that invocation
(as an alternative to the configuration file `app/server/dev.env` that is used to set environment variables)
* blocks terminal and keeps it as output device until termination signal is being send.
#### `make clean`
* removes all `node_modules` dependencies that have been installed locally via `npm`
* removes other temporarily created folder in `.local`
...@@ -5,36 +5,42 @@ ARG NODEJS_VERSION='16.17.0' ...@@ -5,36 +5,42 @@ ARG NODEJS_VERSION='16.17.0'
FROM node:$NODEJS_VERSION-alpine AS base FROM node:$NODEJS_VERSION-alpine AS base
ARG \
MONGODB_URL_TEST=mongodb://host.docker.internal:27017/todo-app \
JWT_SECRET_TEST=<my-very-secret-jwt>
ENV \ ENV \
PORT=3000 \ PORT=3000 \
# For local dev - mongodb://host.docker.internal:27017/todo-app MONGODB_URL=<dynamically-set-from-k8s> \
MONGODB_URL=<should-be-dynamically-set> \ JWT_SECRET=<dynamically-set-from-k8s>
JWT_SECRET=<should-be-dynamically-set>
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# Test stage # # Test stage
FROM base AS test # FROM base AS test
# client tests # # client tests
WORKDIR /client # WORKDIR /client
COPY ./client . # COPY ./client .
# RUN npm ci --no-audit --no-fund
RUN npm ci --no-audit --no-fund # RUN npm run test
RUN npm run test # # server tests
# WORKDIR /server
# server tests # COPY ./server/src ./src/
WORKDIR /server
COPY ./server/src ./src/ # COPY ./server/package*.json ./
COPY ./server/package*.json . # RUN npm ci --no-audit --no-fund
RUN npm ci --no-audit --no-fund # ARG MONGODB_URL_TEST
# ARG JWT_SECRET_TEST
# MONGODB_URL and JWT_SECRET are available as env variables # # MONGODB_URL and JWT_SECRET are available as env variables
RUN npm run test # RUN MONGODB_URL=$MONGODB_URL_TEST JWT_SECRET=$JWT_SECRET_TEST npm run test
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# Build stage # Build stage
...@@ -47,7 +53,7 @@ WORKDIR $APP_BUILD_PATH ...@@ -47,7 +53,7 @@ WORKDIR $APP_BUILD_PATH
COPY ./server/src . COPY ./server/src .
COPY ./server/package*.json . COPY ./server/package*.json ./
RUN \ RUN \
npm ci --prod --no-audit --no-fund \ npm ci --prod --no-audit --no-fund \
......
Todo-App Todo-App
======== ========
d
This application represents the *deployable workload* for the This application represents the *deployable workload* for the
[lecture assignment](https://github.com/lucendio/lecture-devops-material/blob/master/assignments/exercise.md). [lecture assignment](https://github.com/lucendio/lecture-devops-material/blob/master/assignments/exercise.md).
......
...@@ -4,6 +4,4 @@ metadata: ...@@ -4,6 +4,4 @@ metadata:
name: todo-app-config name: todo-app-config
data: data:
PORT: 3000 PORT: 3000
# MONGODB_URL: mongodb://<mongo-service>:27017/todo-app
# mongodb-service.<namespace>?
MONGODB_URL: mongodb-service MONGODB_URL: mongodb-service
...@@ -19,7 +19,7 @@ spec: ...@@ -19,7 +19,7 @@ spec:
imagePullSecrets: imagePullSecrets:
- name: 'gitlab-registry-credentials' - name: 'gitlab-registry-credentials'
containers: containers:
- image: devops-app - image: {{image}}
name: todo-app-container name: todo-app-container
ports: ports:
- containerPort: 3000 - containerPort: 3000
......
...@@ -2,7 +2,6 @@ apiVersion: v1 ...@@ -2,7 +2,6 @@ apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: gitlab-registry-credentials name: gitlab-registry-credentials
namespace: default
type: kubernetes.io/dockerconfigjson type: kubernetes.io/dockerconfigjson
data: data:
.dockerconfigjson: ewogICAgImF1dGhzIjogewogICAgICAgICJodHRwczovL3JlZ2lzdHJ5LmdpdGxhYi5jb20iOnsKICAgICAgICAgICAgInVzZXJuYW1lIjoiazhzLXNlY3JldC1wdWxsZXIiLAogICAgICAgICAgICAicGFzc3dvcmQiOiJ5MnQzaXpaRTkxaUd6czlnYWg1eiIsCiAgICAgICAgICAgICJhdXRoIjoiYXpoekxYTmxZM0psZEMxd2RXeHNaWEk2ZVRKME0ybDZXa1U1TVdsSGVuTTVaMkZvTlhvPSIKICAgICAgICB9CiAgICB9Cn0K .dockerconfigjson: ewogICAgImF1dGhzIjogewogICAgICAgICJodHRwczovL3JlZ2lzdHJ5LmdpdGxhYi5jb20iOnsKICAgICAgICAgICAgInVzZXJuYW1lIjoiazhzLXNlY3JldC1wdWxsZXIiLAogICAgICAgICAgICAicGFzc3dvcmQiOiJ5MnQzaXpaRTkxaUd6czlnYWg1eiIsCiAgICAgICAgICAgICJhdXRoIjoiYXpoekxYTmxZM0psZEMxd2RXeHNaWEk2ZVRKME0ybDZXa1U1TVdsSGVuTTVaMkZvTlhvPSIKICAgICAgICB9CiAgICB9Cn0K
...@@ -4,4 +4,4 @@ metadata: ...@@ -4,4 +4,4 @@ metadata:
name: todo-app-secrets name: todo-app-secrets
type: Opaque type: Opaque
data: data:
JWT_SECRET: <jwt-secret in base64> JWT_SECRET: dmVyYmlnand0c2VjcmV0d293