diff --git a/deployment/Jenkinsfile b/deployment/Jenkinsfile new file mode 100644 index 0000000..bd20e73 --- /dev/null +++ b/deployment/Jenkinsfile @@ -0,0 +1,111 @@ +def PIPELINE_ID = "${env.BUILD_NUMBER}" + +def getImageTag() { + def dateFormat = new java.text.SimpleDateFormat('yyyyMMddHHmmss') + def currentDate = new Date() + return dateFormat.format(currentDate) +} + +podTemplate( + label: "${PIPELINE_ID}", + serviceAccount: 'jenkins', + containers: [ + containerTemplate(name: 'gradle', image: 'gradle:jdk17', ttyEnabled: true, command: 'cat'), + containerTemplate(name: 'podman', image: "mgoltzsche/podman", ttyEnabled: true, command: 'cat', privileged: true), + containerTemplate(name: 'azure-cli', image: 'hiondal/azure-kubectl:latest', command: 'cat', ttyEnabled: true), + containerTemplate(name: 'envsubst', image: "hiondal/envsubst", command: 'sleep', args: '1h') + ], + volumes: [ + emptyDirVolume(mountPath: '/home/gradle/.gradle', memory: false), + emptyDirVolume(mountPath: '/root/.azure', memory: false) + ] +) { + node(PIPELINE_ID) { + def props + def imageTag = getImageTag() + def manifest = "deploy.yaml" + + stage("Get Source") { + checkout scm + props = readProperties file: "deployment/deploy_env_vars" + } + + stage("Setup AKS") { + container('azure-cli') { + withCredentials([azureServicePrincipal('azure-credentials')]) { + sh """ + az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID + az aks get-credentials --resource-group ictcoe-edu --name ${props.teamid}-aks --overwrite-existing + kubectl create namespace ${props.namespace} --dry-run=client -o yaml | kubectl apply -f - + """ + } + } + } + + stage('Build Applications') { + container('gradle') { + sh """ + chmod +x gradlew + ./gradlew :member:clean :member:build + ./gradlew :mysub-infra:clean :mysub-infra:build + ./gradlew :recommend:clean :recommend:build + """ + } + } + + stage('Build & Push Images') { + container('podman') { + withCredentials([usernamePassword( + credentialsId: 'acr-credentials', + usernameVariable: 'USERNAME', + passwordVariable: 'PASSWORD' + )]) { + def services = ['member', 'mysub', 'recommend'] + + sh "podman login ${props.registry} --username \$USERNAME --password \$PASSWORD" + + services.each { service -> + def buildDir = service == 'mysub' ? 'mysub-infra' : service + def jarFile = service == 'mysub' ? 'mysub.jar' : "${service}.jar" + + sh """ + podman build \ + --build-arg BUILD_LIB_DIR="${buildDir}/build/libs" \ + --build-arg ARTIFACTORY_FILE="${jarFile}" \ + -f container/Dockerfile \ + -t ${props.registry}/${props.image_org}/${service}:${imageTag} . + + podman push ${props.registry}/${props.image_org}/${service}:${imageTag} + """ + } + } + } + } + + stage('Generate & Apply Manifest') { + container('envsubst') { + services.each { service -> + sh """ + export ${service}_image_path=${props.registry}/${props.image_org}/${service}:${imageTag} + """ + } + + sh """ + envsubst < deployment/${manifest}.template > deployment/${manifest} + cat deployment/${manifest} + """ + } + + container('azure-cli') { + sh """ + kubectl apply -f deployment/${manifest} + + echo "Waiting for deployments to be ready..." + kubectl -n ${props.namespace} wait --for=condition=available deployment/member --timeout=300s + kubectl -n ${props.namespace} wait --for=condition=available deployment/mysub --timeout=300s + kubectl -n ${props.namespace} wait --for=condition=available deployment/recommend --timeout=300s + """ + } + } + } +} \ No newline at end of file diff --git a/deployment/deploy.yaml.template b/deployment/deploy.yaml.template new file mode 100644 index 0000000..fa604ec --- /dev/null +++ b/deployment/deploy.yaml.template @@ -0,0 +1,309 @@ +# ConfigMap +apiVersion: v1 +kind: ConfigMap +metadata: + name: common-config + namespace: ${namespace} +data: + ALLOWED_ORIGINS: ${allowed_origins} + JPA_DDL_AUTO: update + JPA_SHOW_SQL: 'true' + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: member-config + namespace: ${namespace} +data: + POSTGRES_DB: member + POSTGRES_HOST: member-postgresql + POSTGRES_PORT: '5432' + SERVER_PORT: '8081' + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mysub-config + namespace: ${namespace} +data: + FEE_LEVEL_ADDICT: '100000' + FEE_LEVEL_COLLECTOR: '50000' + POSTGRES_DB: mysub + POSTGRES_HOST: mysub-postgresql + POSTGRES_PORT: '5432' + SERVER_PORT: '8082' + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: recommend-config + namespace: ${namespace} +data: + POSTGRES_DB: recommend + POSTGRES_HOST: recommend-postgresql + POSTGRES_PORT: '5432' + SERVER_PORT: '8083' + +--- +# Secrets +apiVersion: v1 +kind: Secret +metadata: + name: common-secret + namespace: ${namespace} +stringData: + JWT_SECRET_KEY: ${jwt_secret_key} +type: Opaque + +--- +apiVersion: v1 +kind: Secret +metadata: + name: member-secret + namespace: ${namespace} +stringData: + JWT_ACCESS_TOKEN_VALIDITY: '3600000' + JWT_REFRESH_TOKEN_VALIDITY: '86400000' + POSTGRES_PASSWORD: ${postgres_password} + POSTGRES_USER: ${postgres_user} +type: Opaque + +--- +apiVersion: v1 +kind: Secret +metadata: + name: mysub-secret + namespace: ${namespace} +stringData: + POSTGRES_PASSWORD: ${postgres_password} + POSTGRES_USER: ${postgres_user} +type: Opaque + +--- +apiVersion: v1 +kind: Secret +metadata: + name: recommend-secret + namespace: ${namespace} +stringData: + POSTGRES_PASSWORD: ${postgres_password} + POSTGRES_USER: ${postgres_user} +type: Opaque + +--- +# Deployments +apiVersion: apps/v1 +kind: Deployment +metadata: + name: member + namespace: ${namespace} +spec: + replicas: ${replicas} + selector: + matchLabels: + app: member + template: + metadata: + labels: + app: member + spec: + containers: + - name: member + image: ${member_image_path} + imagePullPolicy: Always + envFrom: + - configMapRef: + name: common-config + - configMapRef: + name: member-config + - secretRef: + name: common-secret + - secretRef: + name: member-secret + resources: + requests: + cpu: ${resources_requests_cpu} + memory: ${resources_requests_memory} + limits: + cpu: ${resources_limits_cpu} + memory: ${resources_limits_memory} + ports: + - containerPort: 8081 + startupProbe: + httpGet: + path: /actuator/health + port: 8081 + failureThreshold: 30 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /actuator/health + port: 8081 + initialDelaySeconds: 60 + periodSeconds: 15 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8081 + initialDelaySeconds: 10 + periodSeconds: 5 + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mysub + namespace: ${namespace} +spec: + replicas: ${replicas} + selector: + matchLabels: + app: mysub + template: + metadata: + labels: + app: mysub + spec: + containers: + - name: mysub + image: ${mysub_image_path} + imagePullPolicy: Always + envFrom: + - configMapRef: + name: common-config + - configMapRef: + name: mysub-config + - secretRef: + name: common-secret + - secretRef: + name: mysub-secret + resources: + requests: + cpu: ${resources_requests_cpu} + memory: ${resources_requests_memory} + limits: + cpu: ${resources_limits_cpu} + memory: ${resources_limits_memory} + ports: + - containerPort: 8082 + startupProbe: + httpGet: + path: /actuator/health + port: 8082 + failureThreshold: 30 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /actuator/health + port: 8082 + initialDelaySeconds: 60 + periodSeconds: 15 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8082 + initialDelaySeconds: 10 + periodSeconds: 5 + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: recommend + namespace: ${namespace} +spec: + replicas: ${replicas} + selector: + matchLabels: + app: recommend + template: + metadata: + labels: + app: recommend + spec: + containers: + - name: recommend + image: ${recommend_image_path} + imagePullPolicy: Always + envFrom: + - configMapRef: + name: common-config + - configMapRef: + name: recommend-config + - secretRef: + name: common-secret + - secretRef: + name: recommend-secret + resources: + requests: + cpu: ${resources_requests_cpu} + memory: ${resources_requests_memory} + limits: + cpu: ${resources_limits_cpu} + memory: ${resources_limits_memory} + ports: + - containerPort: 8083 + startupProbe: + httpGet: + path: /actuator/health + port: 8083 + failureThreshold: 30 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /actuator/health + port: 8083 + initialDelaySeconds: 60 + periodSeconds: 15 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8083 + initialDelaySeconds: 10 + periodSeconds: 5 + +--- +# Services +apiVersion: v1 +kind: Service +metadata: + name: member + namespace: ${namespace} +spec: + selector: + app: member + ports: + - port: 80 + targetPort: 8081 + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + name: mysub + namespace: ${namespace} +spec: + selector: + app: mysub + ports: + - port: 80 + targetPort: 8082 + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + name: recommend + namespace: ${namespace} +spec: + selector: + app: recommend + ports: + - port: 80 + targetPort: 8083 + type: ClusterIP \ No newline at end of file diff --git a/deployment/deploy_env_vars b/deployment/deploy_env_vars new file mode 100644 index 0000000..15ccd2b --- /dev/null +++ b/deployment/deploy_env_vars @@ -0,0 +1,23 @@ +# Team Settings +teamid=dg0200 +root_project=lifesub +namespace=${teamid}-${root_project}-ns + +# Container Registry Settings +registry=${teamid}cr.azurecr.io +image_org=lifesub + +# Application Settings +replicas=2 +allowed_origins=http://localhost:18080,http://localhost:18081 + +# Security Settings +jwt_secret_key=8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ +postgres_user=admin +postgres_password=Passw0rd + +# Resource Settings +resources_requests_cpu=256m +resources_requests_memory=256Mi +resources_limits_cpu=1024m +resources_limits_memory=1024Mi \ No newline at end of file diff --git a/deployment/manifest/deployments/member-deployment.yaml b/deployment/manifest/deployments/member-deployment.yaml index 324aefc..9e50388 100644 --- a/deployment/manifest/deployments/member-deployment.yaml +++ b/deployment/manifest/deployments/member-deployment.yaml @@ -4,7 +4,7 @@ kind: Deployment metadata: name: member spec: - replicas: 1 + replicas: 2 selector: matchLabels: app: member