Cloud SQL connection for Kubernetes using proxy - mysql

I'm currently running a Spring Boot Pod in Kubernetes. There's a side car in the pod for the cloud SQL proxy.
Below is my spring Boot application.properties configuration:
server.port=8081
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.continue-on-error=true
spring.datasource.url=jdbc:mysql://localhost:3306/<database_name>
spring.datasource.username=<user_name>
spring.datasource.password=<password>
Below is my pom.xml extract with plugins and dependencies:
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.performance.common</groupId>
<artifactId>common-http</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.google.cloud.sql</groupId>
<artifactId>mysql-socket-factory</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-sql-mysql</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
And this is my deployment.yaml file:
apiVersion: v1
kind: Service
metadata:
name: app-dummy-name
spec:
selector:
app: app-dummy-name
ports:
- port: 81
name: http-app-dummy-name
targetPort: http-api
type: LoadBalancer
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: app-dummy-name
spec:
replicas: 1
selector:
matchLabels:
app: app-dummy-name
template:
metadata:
labels:
app: app-dummy-name
spec:
containers:
- name: app-dummy-name
image: <image url>
ports:
- containerPort: 8081
name: http-api
env:
- name: DB_HOST
value: 127.0.0.1:3306
- name: DB_USER
valueFrom:
secretKeyRef:
name: cloudsql-db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: cloudsql-db-credentials
key: password
- name: cloudsql-proxy
image: gcr.io/cloudsql-docker/gce-proxy:1.11
command: ["/cloud_sql_proxy",
"-instances=<INSTANCE_CONNECTION_NAME>=:3306",
"-credential_file=/secrets/cloudsql/credentials.json"]
securityContext:
runAsUser: 2 # non-root user
allowPrivilegeEscalation: false
volumeMounts:
- name: cloudsql-instance-credentials
mountPath: /secrets/cloudsql
readOnly: true
volumes:
- name: cloudsql-instance-credentials
secret:
secretName: cloudsql-instance-credentials
I followed the instructions from this link, so I created the secrets and the service account. However, I'm constantly getting connection refusal errors when I deploy the previous yaml file in Kubernetes after creating the secrets:
org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta-data;
nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection;
nested exception is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure.
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
I even tested the Spring boot application locally using the proxy and the same application.properties configuration and it was working fine.

Im adding my deployment yaml which worked for me, check if adding the following will help:
under volumes:
volumes:
- name: cloudsql
emptyDir:
in the connection: --dir=/cloudsql
- name: cloudsql-proxy
image: gcr.io/cloudsql-docker/gce-proxy:1.11
command: ["/cloud_sql_proxy", "--dir=/cloudsql",
"-instances=<INSTANCE_CONNECTION_NAME=tcp:5432>",
"-credential_file=/secrets/cloudsql/credentials.json"]
also make sure you enabled the Cloud SQL Administration API
here is my full deployment yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: app-dummy-name
spec:
replicas: 1
revisionHistoryLimit: 1
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: app-dummy-name
tier: backend
spec:
securityContext:
runAsUser: 0
runAsNonRoot: false
containers:
- name: app-dummy-name
image: <image url>
ports:
- containerPort: 80
env:
- name: DB_HOST
value: localhost
- name: DB_USER
valueFrom:
secretKeyRef:
name: cloudsql-db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: cloudsql-db-credentials
key: password
# proxy_container
- name: cloudsql-proxy
image: gcr.io/cloudsql-docker/gce-proxy:1.11
command: ["/cloud_sql_proxy", "--dir=/cloudsql",
"-instances=my-project-id:us-central1:postgres-instance-name=tcp:5432",
"-credential_file=/secrets/cloudsql/credentials.json"]
volumeMounts:
- name: cloudsql-instance-credentials
mountPath: /secrets/cloudsql
readOnly: true
- name: cloudsql
mountPath: /cloudsql
# volumes
volumes:
- name: cloudsql-instance-credentials
secret:
secretName: cloudsql-instance-credentials
- name: cloudsql
emptyDir:
here are my pre-delpoy script:
#!/bin/bash
# https://cloud.google.com/sql/docs/mysql/connect-kubernetes-engine
# 1. Go to the Cloud SQL Service accounts page of the Google Cloud Platform Console.
# GO TO THE SERVICE ACCOUNTS PAGE
# 2. If needed, select the project that contains your Cloud SQL instance.
# 3. Click Create service account.
# 4. In the Create service account dialog, provide a descriptive name for the service account.
# 5. For Role, select Cloud SQL > Cloud SQL Client.
# Alternatively, you can use the primitive Editor role by selecting Project > Editor, but the Editor role includes permissions across Google Cloud Platform.
#
# 6. If you do not see these roles, your Google Cloud Platform user might not have the resourcemanager.projects.setIamPolicy permission. You can check your permissions by going to the IAM page in the Google Cloud Platform Console and searching for your user id.
# Change the Service account ID to a unique value that you will recognize so you can easily find this service account later if needed.
# 7. Click Furnish a new private key.
# 8. The default key type is JSON, which is the correct value to use.
# 9. Click Create.
# 10. enable Cloud SQL Administration API [here](https://console.developers.google.com/apis/api/sqladmin.googleapis.com/overview)
# make sure to choose your project
echo "create cloudsql secret"
kubectl create secret generic cloudsql-instance-credentials \
--from-file=credentials.json=postgres-sql-credential.json
echo "create cloudsql user and password"
kubectl create secret generic cloudsql-db-credentials \
--from-literal=username=postgres --from-literal=password=123456789
postgres-sql-credential.json file:
{
"type": "service_account",
"project_id": "my-project",
"private_key_id": "1234567890",
"private_key": "-----BEGIN PRIVATE KEY-----\n123445556\n123445\n-----END PRIVATE KEY-----\n",
"client_email": "postgres-sql#my-project.iam.gserviceaccount.com",
"client_id": "1234567890",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/postgres-sq%my-project.iam.gserviceaccount.com"
}

Related

Kubernetes php container can't seem to connect to mysql service

Strange one, i have a php container with symfony, and a mysql pod that is attached to a service called mysql-service. I send the mysql connection details in the env variables on the php deployment.
Weirdly when symfony can't connect to the mysql pod using the service name.When i describe the php pod it says:
An exception occurred in driver: SQLSTATE[HY000] [2002] Connection refused
I can see it's the name resolution:
getaddrinfo failed: Temporary failure in name resolution
If i change the deployment config so the DB_HOST env variable is that of another mysql server on the local network it connects just fine, and i know the mysql user and pass are correct as the mysql deployment and the php deployment both use the mysql secret file; i have also logged into the shell of the mysql pod and connected the database with the same user and pass no problem.
It looks like it's something about the mysql service itself.
Php deployment (some of it's removed as not relevant to my problem):
containers:
- name: php
lifecycle:
postStart:
exec:
command: ["/bin/bash", "-c", "cd /usr/share/nginx/html && php bin/console cache:clear"]
env:
- name: APP_ENV
value: "prod"
- name: DB_NAME
valueFrom:
secretKeyRef:
name: mysql-secret
key: database
- name: DB_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
- name: DB_HOST
value: "mysql-service"
- name: DB_PORT
value: "3306"
imagePullPolicy: Always
image: php-image-here
ports:
- containerPort: 9000
the mysql deployment and service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-deployment
labels:
deploy: mysql
spec:
replicas: 1
selector:
matchLabels:
deploy: mysql
template:
metadata:
labels:
deploy: mysql
spec:
containers:
- name: mysql
imagePullPolicy: Always
image: mysql-image-here
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: mysql-volume
env:
- name: MYSQL_RANDOM_ROOT_PASSWORD
value: "yes"
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: username
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: mysql-secret
key: database
volumes:
- name: mysql-volume
hostPath:
path: /data/mysql
type: DirectoryOrCreate
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
selector:
deploy: mysql
ports:
- protocol: TCP
port: 3306
targetPort: 3306
The service and the mysql pod are up and running:
NAME READY STATUS RESTARTS AGE
pod/backend-deployment-7d585fd8fd-9z5dp 1/2 CrashLoopBackOff 5 (2m54s ago) 7m9s
pod/mysql-deployment-7cb7999d98-blpzr 1/1 Running 0 50m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/backend-service NodePort 10.97.250.92 <none> 80:30002/TCP 38m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d18h
service/mysql-service ClusterIP 10.111.226.120 <none> 3306/TCP 50m

Unable to deploy Keycloak (9.0.0) deployment on Minishift (1.34.0): keycloak-add-user.json (Permission denied)

I am unable to launch Keycloak (9.0.0) on Minishift (v1.34.0+f5db7cb) and getting Crash loop back off error. This Deployment will be integrated with a Postgres deployment.
Keycloak Pod logs:
/opt/jboss/keycloak/standalone/configuration/keycloak-add-user.json (Permission denied)
Here is the yaml file which I deployed through the console (oc apply -f):
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.16.0 (0c01309)
creationTimestamp: null
labels:
io.kompose.service: keycloak
name: keycloak
spec:
selector:
matchLabels:
io.kompose.service: keycloak
replicas: 1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
io.kompose.service: keycloak
spec:
containers:
- env:
- name: DB_ADDR
value: postgres
- name: DB_DATABASE
value: keycloak
- name: DB_PASSWORD
value: password
- name: DB_SCHEMA
value: public
- name: DB_USER
value: keycloak
- name: DB_VENDOR
value: POSTGRES
- name: KEYCLOAK_LOGLEVEL
value: DEBUG
- name: KEYCLOAK_PASSWORD
value: Pa55w0rd
- name: KEYCLOAK_USER
value: admin
image: localhost:5000/keycloak
name: keycloak
ports:
- containerPort: 8080
- containerPort: 8443
resources: {}
restartPolicy: Always
status: {}
---
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.16.0 (0c01309)
creationTimestamp: null
labels:
io.kompose.service: keycloak
name: keycloak
spec:
ports:
- name: "8880"
port: 8880
targetPort: 8080
- name: "8888"
port: 8888
targetPort: 8443
type: LoadBalancer
selector:
io.kompose.service: keycloak
Is there any way to resolve this? Thanks in advance!
keycloak-add-user.json is generated by KEYCLOAK_HOME/bin/add-user-keycloak.sh utility. Keycloak server on startup checks presence of this file and if found specified user will be added.
In its turn Keycloak pod during startup resolve whether there is an variables for user creation like KEYCLOAK_USER and KEYCLOAK_PASSWORD, and if they exist, add-user-keycloak.sh utility would be called with those params to create user.
So in your case you should make /opt/jboss/keycloak/standalone/configuration directory writable.

cant run mysql statefulset in kubernetes

I've just started learning kubernetes and was playing around in katakoda platform. I created a statefulset for mysql. It is just a test so i didnt declare any pvc and mount any volumes. It's declaration and the service's declaration in yml:
---
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
labels:
run: mysql-sts-demo
spec:
ports:
- port: 3306
name: db
selector:
run: mysql-sts-demo
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-sts-demo
spec:
serviceName: "mysql-headless"
replicas: 1
selector:
matchLabels:
run: mysql-sts-demo
template:
metadata:
labels:
run: mysql-sts-demo
spec:
containers:
- name: mysql
image: mysql:5.7.8
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: ROOT_PASSWORD
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: mysql-secrets
key: DBNAME
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: USER
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: PASSWORD
it creates those resources successfully , but when I type kubectl get statefulsets , my ss is always being displayed as not ready. What may the issue be? Btw I need it for using with a spring petclinic app which I declared and launched previously as a deployment .
can you paste the logs for statefulsets
or an output of kubectl get events and kubectl describe <your stateful-set name>
now coming to secrets can you check whether those secrets which you are using in your stateful-sets definitions are already present using kubectl get secrets

hawkular-metrics installation failed

Unable to proceed with hawkular-metrics installation as hawkular_metrics_schema_job.yaml failed to find schema image.
Failed to pull image "docker.io/openshift/origin-metrics-schema-installer:v3.11.0": rpc error: code = Unknown desc = repository docker.io/openshift/origin-metrics-schema-installer not found: does not exist or no pull access
cat /tmp/openshift-metrics-ansible-ABoWRf/templates/hawkular_metrics_schema_job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: hawkular-metrics-schema
labels:
metrics-infra: hawkular-metrics
name: hawkular-metrics-schema
spec:
template:
spec:
version: v1
metadata:
labels:
metrics-infra: hawkular-metrics
#name: hawkular-metrics
containers:
- name: hawkular-metrics-schema
image: docker.io/openshift/origin-metrics-schema-installer:v3.11.0
imagePullPolicy: IfNotPresent
env:
- name: TRUSTSTORE_AUTHORITIES
value: "/hawkular-metrics-certs/tls.truststore.crt"
volumeMounts:
- mountPath: /hawkular-metrics-certs
name: hawkular-metrics-certs
- mountPath: /hawkular-account
name: hawkular-metrics-account
volumes:
- name: hawkular-metrics-certs
secret:
secretName: hawkular-metrics-certs
- name: hawkular-metrics-account
secret:
secretName: hawkular-metrics-account
restartPolicy: OnFailure
docker pull origin-metrics-schema-installer Using default tag: latest
Trying to pull repository docker.io/library/origin-metrics-schema-installer ...
repository docker.io/origin-metrics-schema-installer not found: does not exist or no pull access
On one hand, if you are using OKD v3.10, official docker metrics images for 3.10 are tagged as "v3.10.0-rc.0" (not "v3.10").
If you are using 3.11 they are well tagged: https://hub.docker.com/r/openshift/origin-metrics-hawkular-metrics/tags/
On the other hand
openshift/origin-metrics-schema-installer doesn't exist, and someone built the image to:
https://hub.docker.com/r/alv91/origin-metrics-schema-installer/ (and he/she tagged the image as "v3.10").
So in your inventory file you should have for OKD v3.10:
openshift_metrics_install_metrics=True
openshift_metrics_cassandra_image=docker.io/openshift/origin-metrics-cassandra:v3.10.0-rc.0
openshift_metrics_hawkular_metrics_image=docker.io/openshift/origin-metrics-hawkular-metrics:v3.10.0-rc.0
openshift_metrics_heapster_image=docker.io/openshift/origin-metrics-heapster:v3.10.0-rc.0
openshift_metrics_schema_installer_image=docker.io/alv91/origin-metrics-schema-installer:v3.10
And for OKD 3.11:
openshift_metrics_install_metrics=True
openshift_metrics_schema_installer_image:docker.io/alv91/origin-metrics-schema-installer:v3.10
https://github.com/openshift/openshift-ansible/issues/9948
https://github.com/openshift/origin-metrics/issues/429

Openshift: Change in trigger behavior between 3.6.1 and 3.7.1

I have two Openshift environments that I publish to from a Gitlab pipeline. Let's call the first one DEV and it runs Openshift v3.7.1+c2ce2c0-1. The second one is INT and runs v3.6.1+008f2d5. Recently DEV got upgraded from 3.6.1 to 3.7.1 and after that I noticed a strange change in the behavior of redeployment triggers.
In short, what I see is that existing deployments in the DEV environment are triggered to redeploy with an "Image changed" message when an unchanged deployment config template is applied and while the Docker image also remains unchanged. This means that for example a MongoDB or Jenkins deployment recreates all containers and loses all data with every run of the CI pipeline.
Yes, there is the theoretically the possibility to use persistent volumes but the Openshift installations are not under my control. The point here is that redeployments happen when neither image nor configuration changes.
The command that I am using from Gitlab is this:
oc process -f openshift-mongodb-ephemeral.yml -v MONGODB_DATABASE=mydb -v DATABASE_SERVICE_NAME=mongodb -l template=northbound-mongodb | oc apply -f -
Here is one of the deployment config templates:
apiVersion: v1
kind: Template
labels:
template: mongodb-ephemeral-template
metadata:
annotations:
description: |-
MongoDB database service, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mongodb-container/blob/master/3.2/README.md.
WARNING: Any data stored will be lost upon pod destruction. Only use this template for testing
iconClass: icon-mongodb
openshift.io/display-name: MongoDB (Ephemeral)
tags: database,mongodb
creationTimestamp: 2017-03-14T11:25:13Z
name: mongodb-ephemeral
resourceVersion: "483"
selfLink: /oapi/v1/namespaces/openshift/templates/mongodb-ephemeral
uid: e41b7f8e-08a8-11e7-9120-000d3a266151
objects:
- apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
name: ${DATABASE_SERVICE_NAME}
spec:
ports:
- name: mongo
nodePort: 0
port: 27017
protocol: TCP
targetPort: 27017
selector:
name: ${DATABASE_SERVICE_NAME}
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
- apiVersion: v1
kind: DeploymentConfig
metadata:
creationTimestamp: null
name: ${DATABASE_SERVICE_NAME}
spec:
replicas: 1
selector:
name: ${DATABASE_SERVICE_NAME}
strategy:
type: Recreate
template:
metadata:
creationTimestamp: null
labels:
name: ${DATABASE_SERVICE_NAME}
spec:
containers:
- capabilities: {}
env:
- name: MONGODB_USER
value: ${MONGODB_USER}
- name: MONGODB_PASSWORD
valueFrom:
secretKeyRef:
key: database-password
name: myproject-secrets
- name: MONGODB_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
key: database-admin-password
name: myproject-secrets
- name: MONGODB_DATABASE
value: ${MONGODB_DATABASE}
image: ' '
imagePullPolicy: IfNotPresent
livenessProbe:
initialDelaySeconds: 30
tcpSocket:
port: 27017
timeoutSeconds: 1
name: mongodb
ports:
- containerPort: 27017
protocol: TCP
readinessProbe:
exec:
command:
- /bin/sh
- -i
- -c
- mongo 127.0.0.1:27017/$MONGODB_DATABASE -u $MONGODB_USER -p $MONGODB_PASSWORD
--eval="quit()"
initialDelaySeconds: 3
timeoutSeconds: 1
resources:
limits:
memory: ${MEMORY_LIMIT}
securityContext:
capabilities: {}
privileged: false
terminationMessagePath: /dev/termination-log
volumeMounts:
- mountPath: /var/lib/mongodb/data
name: ${DATABASE_SERVICE_NAME}-data
dnsPolicy: ClusterFirst
restartPolicy: Always
volumes:
- emptyDir:
medium: ""
name: ${DATABASE_SERVICE_NAME}-data
triggers:
- imageChangeParams:
automatic: true
containerNames:
- mongodb
from:
kind: ImageStreamTag
name: mongodb:${MONGODB_VERSION}
namespace: ${NAMESPACE}
lastTriggeredImage: ""
type: ImageChange
- type: ConfigChange
status: {}
parameters:
- description: Maximum amount of memory the container can use.
displayName: Memory Limit
name: MEMORY_LIMIT
required: true
value: 512Mi
- description: The OpenShift Namespace where the ImageStream resides.
displayName: Namespace
name: NAMESPACE
value: openshift
- description: The name of the OpenShift Service exposed for the database.
displayName: Database Service Name
name: DATABASE_SERVICE_NAME
required: true
value: mongodb
- description: Name of the MongoDB database accessed.
displayName: MongoDB Database Name
name: MONGODB_DATABASE
required: true
value: sampledb
- description: Version of MongoDB image to be used (2.4, 2.6, 3.2 or latest).
displayName: Version of MongoDB Image
name: MONGODB_VERSION
required: true
value: "3.2"
- description: Name of user to access MongoDB.
displayName: MongoDB user
name: MONGODB_USER
required: true
value: "mongouser"
The oc process and oc apply commands get executed with each execution of the Gitlab CI pipeline. The pipeline triggers whenever someone merges into e.g. the develop branch. I would like to keep this behavior because this guarantees if someone changes the configuration of MongoDB, Jenkins, etc. those get updated and redeployed automatically (in which case a loss of data is acceptable).
Does anybody know what change in OS could have prompted this change in behavior and how to achieve the old behavior again?
Credits go to Graham Dumpleton who answered my question, albeit through a comment. Since I did not hear back from him I am leaving this answer myself for completeness' sake.
After removing the entries
creationTimestamp
lastTriggeredImage
status
image
from the deployment config template, the unwanted redeployments stopped. I do not know which entry specifically started triggering the behavior in Openshift 3.7.1 but in any case, these four entries are information generated during an Openshift deployment process and should not be in a template to begin with.