Skip to content

Commit 0ff9f7f

Browse files
authored
Merge pull request #16 from oracle-devrel/build-caching
OCI DevOps Build Caching Example
2 parents 2b219ae + ca40eea commit 0ff9f7f

File tree

11 files changed

+365
-0
lines changed

11 files changed

+365
-0
lines changed

oci-build-examples/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ All about OCI devops build samples ..
88

99
</details>
1010

11+
<details>
12+
<summary>Build Caching - click to expand</summary>
13+
14+
* [Speed up builds with caching](./oci-build-caching/)
15+
16+
</details>
1117

1218

1319
### Back to examples.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Maven build container
2+
3+
FROM maven:3.8.5-openjdk-11 AS maven_build
4+
5+
COPY pom.xml /tmp/
6+
7+
COPY src /tmp/src/
8+
9+
WORKDIR /tmp/
10+
11+
RUN --mount=type=cache,target=/root/.m2 mvn package
12+
13+
#pull base image
14+
15+
FROM openjdk
16+
17+
#expose port 8080
18+
EXPOSE 8080
19+
20+
#default command
21+
CMD java -jar /data/hello-world-0.1.0.jar
22+
23+
#copy hello world to docker image from builder image
24+
25+
COPY --from=maven_build /tmp/target/hello-world-0.1.0.jar /data/hello-world-0.1.0.jar
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# OCI DevOps - Speed up builds with caching
2+
3+
In most cases, the build time is consumed in downloading or preparing certain build dependancies like maven dependencies, node_modules, pip dependancies, etc... This procedure talks about on how to reduce time by caching those dependencies for the subsequent runs.
4+
5+
In this way, the first run would take full time to download and setup build dependancies. But the subsequent runs will be more efficient through the cache.
6+
7+
For this, sample java maven-based application is used to demonstrate build caching.
8+
9+
### Prerequisites/Assumptions
10+
* Assumed you are already familiar with OCI DevOps. Please refer [Documentation](https://www.oracle.com/devops/devops-service/)
11+
* To know how to run the this maven project in your local. Please refer [this](./SETUP-PROJECT.md)
12+
* Assumed that you are using docker build inside your `build_spec.yaml`
13+
* As OCI Object storage is used to store the cache, Please create a bucket and set right access policies for the same. [Click here](https://docs.oracle.com/en-us/iaas/Content/Object/Concepts/objectstorageoverview.htm) to know more about Object Storage.
14+
15+
### References
16+
* [OCI DevOps CICD Reference Architecture](https://docs.oracle.com/en/solutions/ci-cd-pipe-oci-devops/index.html)
17+
* [OCI DevOps Documentation](https://docs.oracle.com/en-us/iaas/Content/devops/using/home.htm)
18+
* [Creating DevOps Project](https://docs.oracle.com/en-us/iaas/Content/devops/using/create_project.htm)
19+
* [Creating Repository](https://docs.oracle.com/en-us/iaas/Content/devops/using/managing_coderepo.htm)
20+
* [Creating Build Pipeline](https://docs.oracle.com/en-us/iaas/Content/devops/using/managing_build_pipelines.htm)
21+
* [Creating Deploy Pipeline](https://docs.oracle.com/en-us/iaas/Content/devops/using/deployment_pipelines.htm)
22+
23+
24+
### Overview of Changes
25+
For any project, you may tweek `build_spec.yaml` to enable build cache.
26+
27+
#### Step 1
28+
* Create OCI Object Storage bucket `build-cache`
29+
* Create Policies, for build to access Object Storage bucket.
30+
```
31+
Allow dynamic-group <dg-devops-build-pipeline> to read buckets in compartment <compartment-name>
32+
33+
Allow dynamic-group <dg-devops-build-pipeline> to manage objects in compartment <compartment-name> where all {target.bucket.name='build-cache'}
34+
```
35+
36+
#### Step 2
37+
Docker BuildKit is installed to enable few advanced docker build commands for caching.
38+
```
39+
- type: Command
40+
name: "Docker BuildKit Setup"
41+
timeoutInSeconds: 140
42+
command: |
43+
wget https://github.com/docker/buildx/releases/download/v0.8.2/buildx-v0.8.2.linux-amd64 -O docker-buildx
44+
mkdir -p ~/.docker/cli-plugins
45+
mv docker-buildx ~/.docker/cli-plugins/
46+
chmod +x ~/.docker/cli-plugins/docker-buildx
47+
docker buildx install
48+
```
49+
#### Step 3
50+
`Build Cache Restore` stage is used to download the pre-uploaded cache from OCI Object Storage.
51+
52+
```
53+
- type: Command
54+
name: "Build Cache Restore"
55+
timeoutInSeconds: 140
56+
command: |
57+
oci os object get --bucket-name build-cache --file ${BUILD_CACHE_OS_FILE_NAME} --name ${BUILD_CACHE_OS_FILE_NAME} && unzip ${BUILD_CACHE_OS_FILE_NAME}
58+
echo "Done..."
59+
```
60+
61+
#### Step 4
62+
In actual build stage, below comands are used in the place of regular `docker build`.
63+
```
64+
- type: Command
65+
name: "Docker build"
66+
command: |
67+
export DOCKER_BUILDKIT=1
68+
export DOCKER_CLI_EXPERIMENTAL=enabled
69+
docker buildx create --use
70+
docker buildx build -t="hello-world-java" --cache-from=type=local,src=./cache --cache-to=type=local,dest=./cache --load .
71+
echo "DONE"
72+
```
73+
74+
#### Step 5
75+
`Build Cache Upload` stage is added to collect the generated build cache and upload to OCI Object Storage bucket. This is used for subsequent builds.
76+
77+
```
78+
- type: Command
79+
name: "Build Cache Upload"
80+
timeoutInSeconds: 140
81+
command: |
82+
rm ${BUILD_CACHE_OS_FILE_NAME} && zip -r ${BUILD_CACHE_OS_FILE_NAME} cache/*
83+
oci os object put --bucket-name build-cache --file ${BUILD_CACHE_OS_FILE_NAME} --force
84+
```
85+
86+
#### Step 6
87+
In `Dockerfile`, We need to pass `--mount` argument to `RUN` command to use cache for the specific build command as below.
88+
89+
```
90+
RUN --mount=type=cache,target=/root/.m2 mvn package
91+
```
92+
93+
### Results
94+
95+
#### Before OCI Build Cache
96+
![6m 33s](images/BeforeOCIBuildCache.png "Before Caching")
97+
98+
#### After OCI Build Cache
99+
![0m 49s](images/AfterOCIBuildCache.png "After Caching")
100+
101+
### Contributors
102+
103+
- Author : Ashok CM
104+
- Collaborators : NA
105+
- Last release : May 2022
106+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
Hello World sample shows how to deploy [SpringBoot](http://projects.spring.io/spring-boot/) RESTful web service application with [Docker](https://www.docker.com/)
2+
3+
#### Prerequisite
4+
5+
Installed:
6+
[Docker](https://www.docker.com/)
7+
[git](https://www.digitalocean.com/community/tutorials/how-to-contribute-to-open-source-getting-started-with-git)
8+
9+
Optional:
10+
[Docker-Compose](https://docs.docker.com/compose/install/)
11+
[Java 1.8 or 11.1](https://www.oracle.com/technetwork/java/javase/overview/index.html)
12+
[Maven 3.x](https://maven.apache.org/install.html)
13+
14+
#### Steps
15+
16+
##### Clone source code from git
17+
```
18+
$ git clone https://github.com/dstar55/docker-hello-world-spring-boot .
19+
```
20+
21+
##### Build Docker image
22+
```
23+
$ docker build -t="hello-world-java" .
24+
```
25+
Maven build will be executes during creation of the docker image.
26+
27+
>Note:if you run this command for first time it will take some time in order to download base image from [DockerHub](https://hub.docker.com/)
28+
29+
##### Run Docker Container
30+
```
31+
$ docker run -p 8080:8080 -it --rm hello-world-java
32+
```
33+
34+
##### Test application
35+
36+
```
37+
$ curl localhost:8080
38+
```
39+
40+
the respone should be:
41+
```
42+
Hello World
43+
```
44+
45+
##### Stop Docker Container:
46+
```
47+
docker stop `docker container ls | grep "hello-world-java:*" | awk '{ print $1 }'`
48+
```
49+
50+
## Run with docker-compose
51+
52+
Build and start the container by running
53+
54+
```
55+
$ docker-compose up -d
56+
```
57+
58+
##### Test application with ***curl*** command
59+
60+
```
61+
$ curl localhost:8080
62+
```
63+
64+
the respone should be:
65+
```
66+
Hello World
67+
```
68+
69+
##### Stop Docker Container:
70+
```
71+
docker-compose down
72+
```
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
version: 0.1
2+
component: build
3+
timeoutInSeconds: 1000
4+
shell: bash
5+
env:
6+
# these are local variables to the build config
7+
variables:
8+
BUILD_CACHE_OS_BUCKET_NAME: build-cache
9+
BUILD_CACHE_OS_FILE_NAME: cache.zip
10+
BUILDX_VERSION: 0.8.2
11+
12+
# exportedVariables are made available to use as parameters in sucessor Build Pipeline stages
13+
# For this Build to run, the Build Pipeline needs to have a BUILDRUN_HASH parameter set
14+
exportedVariables:
15+
- BUILDRUN_HASH
16+
17+
steps:
18+
- type: Command
19+
name: "Define unique image tag"
20+
timeoutInSeconds: 140
21+
command: |
22+
echo "OCI_BUILD_RUN_ID: ${OCI_BUILD_RUN_ID}"
23+
export BUILDRUN_HASH=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7`
24+
echo "BUILDRUN_HASH: " $BUILDRUN_HASH
25+
- type: Command
26+
name: "Docker BuildKit Setup"
27+
timeoutInSeconds: 140
28+
command: |
29+
wget https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-amd64 -O docker-buildx
30+
mkdir -p ~/.docker/cli-plugins
31+
mv docker-buildx ~/.docker/cli-plugins/
32+
chmod +x ~/.docker/cli-plugins/docker-buildx
33+
docker buildx install
34+
- type: Command
35+
name: "Build Cache Restore"
36+
timeoutInSeconds: 140
37+
command: |
38+
oci os object get --bucket-name build-cache --file ${BUILD_CACHE_OS_FILE_NAME} --name ${BUILD_CACHE_OS_FILE_NAME} && unzip ${BUILD_CACHE_OS_FILE_NAME}
39+
echo "Done..."
40+
- type: Command
41+
name: "Docker build"
42+
command: |
43+
export DOCKER_BUILDKIT=1
44+
export DOCKER_CLI_EXPERIMENTAL=enabled
45+
docker buildx create --use
46+
docker buildx build -t="hello-world-java" --cache-from=type=local,src=./cache --cache-to=type=local,dest=./cache --load .
47+
echo "DONE"
48+
onFailure:
49+
- type: Command
50+
command: |
51+
echo "Handling Failure"
52+
build_result=FAILURE
53+
echo "Failure successfully handled"
54+
- type: Command
55+
name: "Build Cache Upload"
56+
timeoutInSeconds: 140
57+
command: |
58+
rm ${BUILD_CACHE_OS_FILE_NAME} && zip -r ${BUILD_CACHE_OS_FILE_NAME} cache/*
59+
oci os object put --bucket-name build-cache --file ${BUILD_CACHE_OS_FILE_NAME} --force
60+
outputArtifacts:
61+
- name: Build_output_image
62+
type: DOCKER_IMAGE
63+
location: "hello-world-java"
1.53 MB
Loading
1.62 MB
Loading
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.dockerforjavadevelopers</groupId>
7+
<artifactId>hello-world</artifactId>
8+
<version>0.1.0</version>
9+
10+
<parent>
11+
<groupId>org.springframework.boot</groupId>
12+
<artifactId>spring-boot-starter-parent</artifactId>
13+
<version>2.6.6</version>
14+
</parent>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>org.springframework.boot</groupId>
19+
<artifactId>spring-boot-starter-web</artifactId>
20+
</dependency>
21+
22+
<dependency>
23+
<groupId>junit</groupId>
24+
<artifactId>junit</artifactId>
25+
<version>4.13.1</version>
26+
<scope>test</scope>
27+
</dependency>
28+
29+
</dependencies>
30+
31+
<properties>
32+
<start-class>com.dockerforjavadevelopers.hello.Application</start-class>
33+
<log4j2.version>2.17.0</log4j2.version>
34+
</properties>
35+
36+
<build>
37+
<plugins>
38+
<plugin>
39+
<groupId>org.springframework.boot</groupId>
40+
<artifactId>spring-boot-maven-plugin</artifactId>
41+
</plugin>
42+
</plugins>
43+
</build>
44+
45+
</project>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.dockerforjavadevelopers.hello;
2+
3+
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
6+
import org.springframework.context.ApplicationContext;
7+
import org.springframework.context.annotation.ComponentScan;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
@Configuration
11+
@EnableAutoConfiguration
12+
@ComponentScan
13+
public class Application {
14+
15+
public static void main(String[] args) {
16+
ApplicationContext ctx = SpringApplication.run(Application.class, args);
17+
18+
}
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.dockerforjavadevelopers.hello;
2+
3+
4+
import org.springframework.web.bind.annotation.RestController;
5+
import org.springframework.web.bind.annotation.RequestMapping;
6+
7+
@RestController
8+
public class HelloController {
9+
10+
@RequestMapping("/")
11+
public String index() {
12+
return "Hello World123\n";
13+
}
14+
15+
}

0 commit comments

Comments
 (0)