You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by ka...@apache.org on 2020/06/03 09:35:17 UTC
[beam] branch master updated: [BEAM-5863] Jenkins job to deploy
Beam Metrics infrastructure automatically upon code changes (#11816)
This is an automated email from the ASF dual-hosted git repository.
kamilwu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new 1875e24 [BEAM-5863] Jenkins job to deploy Beam Metrics infrastructure automatically upon code changes (#11816)
1875e24 is described below
commit 1875e24ea5571797415f7abbac7d6f9a11e1ba1a
Author: Kamil Wasilewski <ka...@polidea.com>
AuthorDate: Wed Jun 3 11:34:43 2020 +0200
[BEAM-5863] Jenkins job to deploy Beam Metrics infrastructure automatically upon code changes (#11816)
---
.test-infra/jenkins/CommonJobProperties.groovy | 17 --------
.test-infra/jenkins/Kubernetes.groovy | 30 ++++++++++-----
.test-infra/jenkins/README.md | 1 +
.../job_PostCommit_BeamMetrics_Publish.groovy | 40 +++++++++++++++++++
.test-infra/metrics/build.gradle | 45 +++++++++++++++++++++-
.../metrics/build_and_publish_containers.sh | 22 +++--------
...beam-grafana-etcdata-persistentvolumeclaim.yaml | 3 +-
...beam-grafana-libdata-persistentvolumeclaim.yaml | 3 +-
...beam-grafana-logdata-persistentvolumeclaim.yaml | 3 +-
.../{ => kubernetes}/beam-influxdb-autobackup.yaml | 2 +
...eam-influxdb-backups-persistentvolumeclaim.yaml | 2 +
...eam-influxdb-storage-persistentvolumeclaim.yaml | 2 +
.../metrics/{ => kubernetes}/beam-influxdb.yaml | 2 +-
...beam-postgresql-data-persistentvolumeclaim.yaml | 3 +-
.../{ => kubernetes}/beamgrafana-deploy.yaml | 10 ++---
.test-infra/metrics/sync/github/Dockerfile | 8 ++--
.test-infra/metrics/sync/jenkins/Dockerfile | 5 ++-
.test-infra/metrics/sync/jira/Dockerfile | 5 ++-
18 files changed, 137 insertions(+), 66 deletions(-)
diff --git a/.test-infra/jenkins/CommonJobProperties.groovy b/.test-infra/jenkins/CommonJobProperties.groovy
index 519d561..df0d83a 100644
--- a/.test-infra/jenkins/CommonJobProperties.groovy
+++ b/.test-infra/jenkins/CommonJobProperties.groovy
@@ -242,23 +242,6 @@ class CommonJobProperties {
return argList.join(' ')
}
- static def setupKubernetes(def context, def namespace, def kubeconfigLocation) {
- context.steps {
- shell('gcloud container clusters get-credentials io-datastores --zone=us-central1-a --verbosity=debug')
- shell("cp /home/jenkins/.kube/config ${kubeconfigLocation}")
-
- shell("kubectl --kubeconfig=${kubeconfigLocation} create namespace ${namespace}")
- shell("kubectl --kubeconfig=${kubeconfigLocation} config set-context \$(kubectl config current-context) --namespace=${namespace}")
- }
- }
-
- static def cleanupKubernetes(def context, def namespace, def kubeconfigLocation) {
- context.steps {
- shell("kubectl --kubeconfig=${kubeconfigLocation} delete namespace ${namespace}")
- shell("rm ${kubeconfigLocation}")
- }
- }
-
// Namespace must contain lower case alphanumeric characters or '-'
static String getKubernetesNamespace(def jobName) {
jobName = jobName.replaceAll("_", "-").toLowerCase()
diff --git a/.test-infra/jenkins/Kubernetes.groovy b/.test-infra/jenkins/Kubernetes.groovy
index 6e50383..6f8270a 100644
--- a/.test-infra/jenkins/Kubernetes.groovy
+++ b/.test-infra/jenkins/Kubernetes.groovy
@@ -22,16 +22,21 @@ class Kubernetes {
private static final String KUBERNETES_SCRIPT = "${KUBERNETES_DIR}/kubernetes.sh"
+ private static final String DEFAULT_CLUSTER = 'io-datastores'
+
private static def job
private static String kubeconfigLocation
private static String namespace
- private Kubernetes(job, String kubeconfigLocation, String namespace) {
+ private static String cluster
+
+ private Kubernetes(job, String kubeconfigLocation, String namespace, String cluster) {
this.job = job
this.kubeconfigLocation = kubeconfigLocation
this.namespace = namespace
+ this.cluster = cluster
}
/**
@@ -39,10 +44,12 @@ class Kubernetes {
*
* @param job - jenkins job
* @param kubeconfigLocation - place where kubeconfig will be created
- * @param namepsace - kubernetes namespace
+ * @param namespace - kubernetes namespace. If empty, the default namespace will be used
+ * @param cluster - name of the cluster to get credentials for
*/
- static Kubernetes create(job, String kubeconfigLocation, String namespace) {
- Kubernetes kubernetes = new Kubernetes(job, kubeconfigLocation, namespace)
+ static Kubernetes create(job, String kubeconfigLocation, String namespace = '',
+ String cluster = DEFAULT_CLUSTER) {
+ Kubernetes kubernetes = new Kubernetes(job, kubeconfigLocation, namespace, cluster)
setupKubeconfig()
setupNamespace()
addCleanupSteps()
@@ -55,14 +62,17 @@ class Kubernetes {
environmentVariables {
env('KUBECONFIG', kubeconfigLocation)
}
+ shell("gcloud container clusters get-credentials ${cluster} --zone=us-central1-a")
}
}
private static void setupNamespace() {
- job.steps {
- shell("${KUBERNETES_SCRIPT} createNamespace ${namespace}")
- environmentVariables {
- env('KUBERNETES_NAMESPACE', namespace)
+ if (!namespace.isEmpty()) {
+ job.steps {
+ shell("${KUBERNETES_SCRIPT} createNamespace ${namespace}")
+ environmentVariables {
+ env('KUBERNETES_NAMESPACE', namespace)
+ }
}
}
}
@@ -71,7 +81,9 @@ class Kubernetes {
job.publishers {
postBuildScripts {
steps {
- shell("${KUBERNETES_SCRIPT} deleteNamespace ${namespace}")
+ if (!namespace.isEmpty()) {
+ shell("${KUBERNETES_SCRIPT} deleteNamespace ${namespace}")
+ }
shell("rm ${kubeconfigLocation}")
}
onlyIfBuildSucceeds(false)
diff --git a/.test-infra/jenkins/README.md b/.test-infra/jenkins/README.md
index de03165..6723cc0 100644
--- a/.test-infra/jenkins/README.md
+++ b/.test-infra/jenkins/README.md
@@ -47,6 +47,7 @@ Beam Jenkins overview page: [link](https://builds.apache.org/view/A-D/view/Beam/
| Name | Link | PR Trigger Phrase | Cron Status |
|------|------|-------------------|-------------|
+| beam_PostCommit_BeamMetrics_Publish | [cron](https://builds.apache.org/job/beam_PostCommit_BeamMetrics_Publish/) | N/A | [![Build Status](https://builds.apache.org/job/beam_PostCommit_BeamMetrics_Publish/badge/icon)](https://builds.apache.org/job/beam_PostCommit_BeamMetrics_Publish) |
| beam_PostCommit_CrossLanguageValidatesRunner | [cron](https://builds.apache.org/job/beam_PostCommit_XVR_Flink/), [phrase](https://builds.apache.org/job/beam_PostCommit_XVR_Flink_PR/) | `Run XVR_Flink PostCommit` | [![Build Status](https://builds.apache.org/job/beam_PostCommit_XVR_Flink/badge/icon)](https://builds.apache.org/job/beam_PostCommit_XVR_Flink) |
| beam_PostCommit_Go | [cron](https://builds.apache.org/job/beam_PostCommit_Go/), [phrase](https://builds.apache.org/job/beam_PostCommit_Go_PR/) | `Run Go PostCommit` | [![Build Status](https://builds.apache.org/job/beam_PostCommit_Go/badge/icon)](https://builds.apache.org/job/beam_PostCommit_Go) |
| beam_PostCommit_Go_VR_Flink | [cron](https://builds.apache.org/job/beam_PostCommit_Go_VR_Flink/), [phrase](https://builds.apache.org/job/beam_PostCommit_Go_VR_Flink_PR/) | `Run Go Flink ValidatesRunner` | [![Build Status](https://builds.apache.org/job/beam_PostCommit_Go_VR_Flink/badge/icon)](https://builds.apache.org/job/beam_PostCommit_Go_VR_Flink/) |
diff --git a/.test-infra/jenkins/job_PostCommit_BeamMetrics_Publish.groovy b/.test-infra/jenkins/job_PostCommit_BeamMetrics_Publish.groovy
new file mode 100644
index 0000000..629fd51
--- /dev/null
+++ b/.test-infra/jenkins/job_PostCommit_BeamMetrics_Publish.groovy
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, eit her express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import CommonJobProperties as commonJobProperties
+import Kubernetes
+import PostcommitJobBuilder
+
+PostcommitJobBuilder.postCommitJob('beam_PostCommit_BeamMetrics_Publish', '',
+ 'Beam Metrics deployment', this) {
+
+ description('Applies new configuration to Beam Metrics infrastructure.')
+
+ // Set common parameters.
+ commonJobProperties.setTopLevelMainJobProperties(delegate)
+
+ Kubernetes.create(delegate, 'config-metrics', '', 'metrics')
+
+ steps {
+ gradle {
+ rootBuildScriptDir(commonJobProperties.checkoutDir)
+ tasks(':beam-test-infra-metrics:deploy')
+ commonJobProperties.setGradleSwitches(delegate)
+ }
+ }
+}
diff --git a/.test-infra/metrics/build.gradle b/.test-infra/metrics/build.gradle
index 9bd5a6d..5eebd6a 100644
--- a/.test-infra/metrics/build.gradle
+++ b/.test-infra/metrics/build.gradle
@@ -20,6 +20,7 @@ plugins {
id 'org.apache.beam.module'
// https://github.com/avast/gradle-docker-compose-plugin
id 'com.avast.gradle.docker-compose'
+ id 'org.ajoberstar.grgit'
}
applyGroovyNature()
@@ -50,9 +51,49 @@ dockerCompose {
dockerCompose.isRequiredBy(testMetricsStack)
-task preCommit { dependsOn testMetricsStack }
+task validateConfiguration(type: Exec) {
+ commandLine 'sh', '-c', 'kubectl apply --dry-run=true -Rf kubernetes'
+}
+
+task preCommit {
+ dependsOn validateConfiguration
+ dependsOn testMetricsStack
+}
+
+task buildAndPublishContainers(type: Exec) {
+ commandLine './build_and_publish_containers.sh', 'true'
+}
+
+// Applies new configuration to all resources labeled with `app=beammetrics`
+// and forces Kubernetes to re-pull images.
+task applyConfiguration() {
+ doLast {
+ assert grgit : 'Cannot use outside of git repository'
+
+ def git = grgit.open()
+ def commitedChanges = git.log(paths: ['.test-infra/metrics']).findAll {
+ it.dateTime > ZonedDateTime.now().minusHours(6)
+ }
+
+ if (!commitedChanges.isEmpty()) {
+ exec {
+ executable 'sh'
+ args '-c', 'kubectl apply --selector=app=beammetrics --prune=true -Rf kubernetes && \
+ kubectl wait --selector=app=beammetrics --for=condition=available deployment && \
+ kubectl get --selector=app=beammetrics -o name deployments | xargs -r kubectl rollout restart'
+ }
+ } else {
+ println 'No changes committed since the last 6 hours.'
+ }
+ }
+}
+
+task deploy {
+ dependsOn buildAndPublishContainers
+ dependsOn applyConfiguration
+}
-task checkProber(type:Test) {
+task checkProber(type: Test) {
include "**/ProberTests.class"
}
diff --git a/.test-infra/metrics/build_and_publish_containers.sh b/.test-infra/metrics/build_and_publish_containers.sh
index 53b3237..886008b 100755
--- a/.test-infra/metrics/build_and_publish_containers.sh
+++ b/.test-infra/metrics/build_and_publish_containers.sh
@@ -19,14 +19,11 @@
#
# This script will:
# * build containers for beam metrics
-# * publish containers if second parameter is provided
-# * update beamgrafana-deploy.yaml with new container versions
+# * publish containers if first parameter is provided
+#
+# Usage:
+# "build_and_publish_container.sh [<should_publish_containers>] [<container_tag_name>]"
#
-
-if [ "$#" = 0 ]; then
- echo "build_and_publish_container.sh <container_tag_name> [<should_publish_containers>]"
- exit 0;
-fi
if [ -z "${PROJECT_ID}" ]; then
echo "PROJECT_ID not set, trying to automatically get it from gcloud config."
@@ -36,8 +33,8 @@ fi
echo
echo ===========Start==========
-CONTAINER_VERSION_NAME=$1
-DO_PUSH=$2
+DO_PUSH=$1
+CONTAINER_VERSION_NAME=${2:-latest}
echo
echo ===========Building containers==========
@@ -54,10 +51,3 @@ if [ "$DO_PUSH" = true ]; then
docker push gcr.io/${PROJECT_ID}/beammetricssyncjira:$CONTAINER_VERSION_NAME
docker push gcr.io/${PROJECT_ID}/beammetricssyncgithub:$CONTAINER_VERSION_NAME
fi
-
-echo
-echo ===========Updating deployment script==========
-set -o xtrace
-sed -i "s/\( *image: gcr.io\/${PROJECT_ID}\/beam.*:\).*$/\1$CONTAINER_VERSION_NAME/g" ./beamgrafana-deploy.yaml
-set +o xtrace
-
diff --git a/.test-infra/metrics/beam-grafana-etcdata-persistentvolumeclaim.yaml b/.test-infra/metrics/kubernetes/beam-grafana-etcdata-persistentvolumeclaim.yaml
similarity index 96%
rename from .test-infra/metrics/beam-grafana-etcdata-persistentvolumeclaim.yaml
rename to .test-infra/metrics/kubernetes/beam-grafana-etcdata-persistentvolumeclaim.yaml
index 1b85f3b..04fcac5 100644
--- a/.test-infra/metrics/beam-grafana-etcdata-persistentvolumeclaim.yaml
+++ b/.test-infra/metrics/kubernetes/beam-grafana-etcdata-persistentvolumeclaim.yaml
@@ -19,9 +19,9 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
- creationTimestamp: null
labels:
io.kompose.service: beam-grafana-etcdata
+ app: beammetrics
name: beam-grafana-etcdata
spec:
accessModes:
@@ -29,4 +29,3 @@ spec:
resources:
requests:
storage: 100Mi
-status: {}
diff --git a/.test-infra/metrics/beam-grafana-libdata-persistentvolumeclaim.yaml b/.test-infra/metrics/kubernetes/beam-grafana-libdata-persistentvolumeclaim.yaml
similarity index 96%
rename from .test-infra/metrics/beam-grafana-libdata-persistentvolumeclaim.yaml
rename to .test-infra/metrics/kubernetes/beam-grafana-libdata-persistentvolumeclaim.yaml
index 8fbacb1..9f346b2 100644
--- a/.test-infra/metrics/beam-grafana-libdata-persistentvolumeclaim.yaml
+++ b/.test-infra/metrics/kubernetes/beam-grafana-libdata-persistentvolumeclaim.yaml
@@ -17,9 +17,9 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
- creationTimestamp: null
labels:
io.kompose.service: beam-grafana-libdata
+ app: beammetrics
name: beam-grafana-libdata
spec:
accessModes:
@@ -27,4 +27,3 @@ spec:
resources:
requests:
storage: 100Mi
-status: {}
diff --git a/.test-infra/metrics/beam-grafana-logdata-persistentvolumeclaim.yaml b/.test-infra/metrics/kubernetes/beam-grafana-logdata-persistentvolumeclaim.yaml
similarity index 96%
rename from .test-infra/metrics/beam-grafana-logdata-persistentvolumeclaim.yaml
rename to .test-infra/metrics/kubernetes/beam-grafana-logdata-persistentvolumeclaim.yaml
index 5856e1a..c41bcfd 100644
--- a/.test-infra/metrics/beam-grafana-logdata-persistentvolumeclaim.yaml
+++ b/.test-infra/metrics/kubernetes/beam-grafana-logdata-persistentvolumeclaim.yaml
@@ -19,9 +19,9 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
- creationTimestamp: null
labels:
io.kompose.service: beam-grafana-logdata
+ app: beammetrics
name: beam-grafana-logdata
spec:
accessModes:
@@ -29,4 +29,3 @@ spec:
resources:
requests:
storage: 100Mi
-status: {}
diff --git a/.test-infra/metrics/beam-influxdb-autobackup.yaml b/.test-infra/metrics/kubernetes/beam-influxdb-autobackup.yaml
similarity index 98%
rename from .test-infra/metrics/beam-influxdb-autobackup.yaml
rename to .test-infra/metrics/kubernetes/beam-influxdb-autobackup.yaml
index b513aac..0723165 100644
--- a/.test-infra/metrics/beam-influxdb-autobackup.yaml
+++ b/.test-infra/metrics/kubernetes/beam-influxdb-autobackup.yaml
@@ -20,6 +20,8 @@
apiVersion: batch/v1beta1
kind: CronJob
metadata:
+ labels:
+ app: beammetrics
name: influxdb-autobackup
spec:
schedule: "@weekly"
diff --git a/.test-infra/metrics/beam-influxdb-backups-persistentvolumeclaim.yaml b/.test-infra/metrics/kubernetes/beam-influxdb-backups-persistentvolumeclaim.yaml
similarity index 97%
rename from .test-infra/metrics/beam-influxdb-backups-persistentvolumeclaim.yaml
rename to .test-infra/metrics/kubernetes/beam-influxdb-backups-persistentvolumeclaim.yaml
index 493337c..fe6c194 100644
--- a/.test-infra/metrics/beam-influxdb-backups-persistentvolumeclaim.yaml
+++ b/.test-infra/metrics/kubernetes/beam-influxdb-backups-persistentvolumeclaim.yaml
@@ -20,6 +20,8 @@ apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: influxdb-backups
+ labels:
+ app: beammetrics
spec:
accessModes:
- ReadWriteOnce
diff --git a/.test-infra/metrics/beam-influxdb-storage-persistentvolumeclaim.yaml b/.test-infra/metrics/kubernetes/beam-influxdb-storage-persistentvolumeclaim.yaml
similarity index 97%
rename from .test-infra/metrics/beam-influxdb-storage-persistentvolumeclaim.yaml
rename to .test-infra/metrics/kubernetes/beam-influxdb-storage-persistentvolumeclaim.yaml
index 7b3a858..44fa692 100644
--- a/.test-infra/metrics/beam-influxdb-storage-persistentvolumeclaim.yaml
+++ b/.test-infra/metrics/kubernetes/beam-influxdb-storage-persistentvolumeclaim.yaml
@@ -20,6 +20,8 @@ apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: influxdb-storage
+ labels:
+ app: beammetrics
spec:
accessModes:
- ReadWriteOnce
diff --git a/.test-infra/metrics/beam-influxdb.yaml b/.test-infra/metrics/kubernetes/beam-influxdb.yaml
similarity index 99%
rename from .test-infra/metrics/beam-influxdb.yaml
rename to .test-infra/metrics/kubernetes/beam-influxdb.yaml
index 6b261c0..32c9eb5 100644
--- a/.test-infra/metrics/beam-influxdb.yaml
+++ b/.test-infra/metrics/kubernetes/beam-influxdb.yaml
@@ -29,7 +29,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
labels:
- app: influxdb
+ app: beammetrics
name: influxdb
spec:
replicas: 1
diff --git a/.test-infra/metrics/beam-postgresql-data-persistentvolumeclaim.yaml b/.test-infra/metrics/kubernetes/beam-postgresql-data-persistentvolumeclaim.yaml
similarity index 96%
rename from .test-infra/metrics/beam-postgresql-data-persistentvolumeclaim.yaml
rename to .test-infra/metrics/kubernetes/beam-postgresql-data-persistentvolumeclaim.yaml
index 940b03f..3c6c8ba 100644
--- a/.test-infra/metrics/beam-postgresql-data-persistentvolumeclaim.yaml
+++ b/.test-infra/metrics/kubernetes/beam-postgresql-data-persistentvolumeclaim.yaml
@@ -19,9 +19,9 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
- creationTimestamp: null
labels:
io.kompose.service: beam-postgresql-data
+ app: beammetrics
name: beam-postgresql-data
spec:
accessModes:
@@ -29,4 +29,3 @@ spec:
resources:
requests:
storage: 100Mi
-status: {}
diff --git a/.test-infra/metrics/beamgrafana-deploy.yaml b/.test-infra/metrics/kubernetes/beamgrafana-deploy.yaml
similarity index 95%
rename from .test-infra/metrics/beamgrafana-deploy.yaml
rename to .test-infra/metrics/kubernetes/beamgrafana-deploy.yaml
index 7c5a83d..f548259 100644
--- a/.test-infra/metrics/beamgrafana-deploy.yaml
+++ b/.test-infra/metrics/kubernetes/beamgrafana-deploy.yaml
@@ -21,7 +21,7 @@ kind: Deployment
metadata:
name: beamgrafana
labels:
- app: beamgrafana
+ app: beammetrics
spec:
selector:
matchLabels:
@@ -37,7 +37,7 @@ spec:
fsGroup: 1000
containers:
- name: beamgrafana
- image: gcr.io/apache-beam-testing/beamgrafana:beammetrics20200526
+ image: gcr.io/apache-beam-testing/beamgrafana
securityContext:
runAsUser: 0
env:
@@ -104,7 +104,7 @@ spec:
mountPath: /secrets/cloudsql
readOnly: true
- name: beammetricssyncjenkins
- image: gcr.io/apache-beam-testing/beammetricssyncjenkins:beammetrics20200506
+ image: gcr.io/apache-beam-testing/beammetricssyncjenkins
env:
- name: DB_HOST
value: 127.0.0.1
@@ -123,7 +123,7 @@ spec:
name: beammetrics-psql-db-credentials
key: password
- name: beammetricssyncjira
- image: gcr.io/apache-beam-testing/beammetricssyncjira:beammetrics20200506
+ image: gcr.io/apache-beam-testing/beammetricssyncjira
env:
- name: DB_HOST
value: 127.0.0.1
@@ -142,7 +142,7 @@ spec:
name: beammetrics-psql-db-credentials
key: password
- name: beammetricssyncgithub
- image: gcr.io/apache-beam-testing/beammetricssyncgithub:beammetrics20200506
+ image: gcr.io/apache-beam-testing/beammetricssyncgithub
env:
- name: DB_HOST
value: 127.0.0.1
diff --git a/.test-infra/metrics/sync/github/Dockerfile b/.test-infra/metrics/sync/github/Dockerfile
index 0ee43c5..1ecedc0 100644
--- a/.test-infra/metrics/sync/github/Dockerfile
+++ b/.test-infra/metrics/sync/github/Dockerfile
@@ -16,14 +16,14 @@
# limitations under the License.
################################################################################
-FROM python:3
+FROM python:3-slim
WORKDIR /usr/src/app
-COPY . .
+COPY requirements.txt .
-RUN pip install pylint yapf nose
-RUN pip install --no-cache-dir -r requirements.txt
+RUN pip install --no-cache-dir -r requirements.txt pylint yapf nose
+COPY . .
CMD python ./sync.py
diff --git a/.test-infra/metrics/sync/jenkins/Dockerfile b/.test-infra/metrics/sync/jenkins/Dockerfile
index ac74829..167e5b8 100644
--- a/.test-infra/metrics/sync/jenkins/Dockerfile
+++ b/.test-infra/metrics/sync/jenkins/Dockerfile
@@ -16,13 +16,14 @@
# limitations under the License.
################################################################################
-FROM python:3
+FROM python:3-slim
WORKDIR /usr/src/app
-COPY . .
+COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
+COPY . .
CMD python ./syncjenkins.py
diff --git a/.test-infra/metrics/sync/jira/Dockerfile b/.test-infra/metrics/sync/jira/Dockerfile
index afd681e..02264c6 100644
--- a/.test-infra/metrics/sync/jira/Dockerfile
+++ b/.test-infra/metrics/sync/jira/Dockerfile
@@ -16,13 +16,14 @@
# limitations under the License.
################################################################################
-FROM python:3
+FROM python:3-slim
WORKDIR /usr/src/app
-COPY . .
+COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
+COPY . .
CMD python ./syncjira.py