├── .gitignore ├── LICENSE ├── README.md ├── app-homepage.png ├── ci ├── k8s.template.yml ├── pipeline.yml ├── vars-cnb-javawar.yml ├── vars-cnb-nodejs.yml └── vars-cnb-springboot.yml ├── concourse-building.png ├── concourse-dashboard-building.png ├── concourse-dashboard.png ├── concourse-pipeline.png ├── grafana-dashboard-secured.png ├── grafana-dashboard.png ├── grafana-jvm-update.png ├── grafana └── kpack-dashboard.json ├── k8s ├── cnb-javawar.yml ├── cnb-nodejs.yml └── cnb-springboot.yml └── kpack ├── 01-namespace.yml ├── 02-service-account.yml ├── 03-custom-stack.yml ├── 04-custom-java-builder.yml ├── 05-custom-nodejs-builder.yml ├── credentials ├── dockerhub-creds.template.yml └── github-creds.template.yml ├── images ├── cnb-javawar-image.yml ├── cnb-nodejs-image.yml └── cnb-springboot-image.yml ├── jvm-update └── jvm-update.yml ├── latest ├── jvm-latest.yml └── stack-latest.yml └── stack-update └── stack-update.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | k8s.yml 3 | *-creds.yml 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building container images at scale using kpack 2 | 3 | This project shows how to leverage [kpack](https://github.com/pivotal/kpack), 4 | an open-source project from [Pivotal](https://pivotal.io), to build container 5 | images in Kubernetes at scale. 6 | 7 | Using kpack, you don't need to write a `Dockerfile` anymore for your app: 8 | [Cloud-Native Buildpacks](https://buildpacks.io) will take care of building your app 9 | from source code, adding runtime environments (such as a JVM or a NodeJS engine), 10 | as well as configuring your app to run well in a containerized environment 11 | ([ever heard of `oom` when using Java in a container?](https://success.docker.com/article/java-app-is-killed-by-docker)). 12 | 13 | kpack runs in a Kubernetes environment: you interact with kpack using YAML files, 14 | which will create Kubernetes native objects. When configuring kpack for your app, 15 | you basically only need to define where to get your app (binary form or source code), 16 | container registry credentials (to store the resulting image) and the container image 17 | destination. 18 | [Please see this GitHub repo to learn how to use kpack](https://github.com/alexandreroman/kpack101). 19 | 20 | In this project, we'll use a kpack installation with 21 | [3 Spring Boot apps](https://github.com/alexandreroman/cnb-springboot) 22 | deployed to a Kubernetes cluster. 23 | [A simple Concourse pipeline](https://github.com/alexandreroman/concourse-k8s-deploy-pipeline) 24 | is used to automatically deploy the latest container images to Kubernetes: 25 | you may use any CI/CD tools as a replacement (such as Jenkins, GitLab, Spinnaker, etc.). 26 | 27 | 28 | 29 | For the purpose of the demonstration, these container images are built with 30 | old JVM versions and an old stack (containing middleware libraries 31 | such as OpenSSL or glibc). In a real-world project, container images are expected 32 | to use the latest buildpacks and stacks: when an update is available, kpack 33 | would automatically update container images. 34 | 35 | We'll simulate these buildpack / stack updates by using a custom kpack Java builder. 36 | 37 | ## How to use it? 38 | 39 | ### Deploying kpack 40 | 41 | Make sure 42 | [kpack is deployed to your Kubernetes cluster](https://github.com/pivotal/kpack/blob/master/docs/install.md). 43 | 44 | This repository has been tested with kpack version 45 | [0.0.8](https://github.com/pivotal/kpack/releases/tag/v0.0.8) 46 | and [Paketo](https://paketo.io/) buildpacks. 47 | 48 | You should have no issues deploying kpack to your favorite Kubernetes cluster. 49 | 50 | kpack resources are running in the `kpack` namespace: 51 | ```bash 52 | $ kubectl -n kpack get pods 53 | NAME READY STATUS RESTARTS AGE 54 | kpack-controller-5f66c774d8-sl9md 1/1 Running 0 7d17h 55 | kpack-webhook-847c887dd-xgtp5 1/1 Running 0 7d17h 56 | ``` 57 | 58 | You may access kpack logs to see how the controller reacts to events 59 | (buildpack update, source code change, etc.): 60 | ```bash 61 | $ kubectl -n kpack logs -l app=kpack-controller -f 62 | 2020-02-04T10:42:20.384Z INFO controller/controller.go:351 Reconcile succeeded. Time taken: 1.816832832s. {"knative.dev/traceid": "212e8703-d437-4cf0-967e-0b1e1148caef", "knative.dev/key": "default"} 63 | 2020-02-04T10:42:32.179Z DEBUG controller/controller.go:313 Processing from queue kpack-builders/app-bar-source (depth: 0) 64 | 2020-02-04T10:42:32.723Z DEBUG controller/controller.go:267 Adding to queue kpack-builders/app-bar-source (delay: 1m0s, depth: 0) 65 | 2020-02-04T10:42:32.723Z INFO controller/controller.go:351 Reconcile succeeded. Time taken: 543.884902ms. {"knative.dev/traceid": "d34aedc3-56b0-4399-8cf7-457fbec8b9d1", "knative.dev/key": "kpack-builders/app-bar-source"} 66 | 2020-02-04T10:42:36.597Z DEBUG controller/controller.go:313 Processing from queue kpack-builders/app-foo-source (depth: 0) 67 | 2020-02-04T10:42:36.949Z DEBUG controller/controller.go:313 Processing from queue kpack-builders/app-baz-source (depth: 0) 68 | 2020-02-04T10:42:36.969Z DEBUG controller/controller.go:267 Adding to queue kpack-builders/app-foo-source (delay: 1m0s, depth: 0) 69 | 2020-02-04T10:42:36.969Z INFO controller/controller.go:351 Reconcile succeeded. Time taken: 372.030594ms. {"knative.dev/traceid": "46e19fe7-d200-4093-9538-7d90fabea828", "knative.dev/key": "kpack-builders/app-foo-source"} 70 | 2020-02-04T10:42:37.296Z DEBUG controller/controller.go:267 Adding to queue kpack-builders/app-baz-source (delay: 1m0s, depth: 0) 71 | 2020-02-04T10:42:37.296Z INFO controller/controller.go:351 Reconcile succeeded. Time taken: 347.11171ms. {"knative.dev/traceid": "3b7a349e-2eed-4904-8f09-825800be4f90", "knative.dev/key": "kpack-builders/app-baz-source"} 72 | ``` 73 | 74 | ### Configuring kpack 75 | 76 | kpack is up and running: let's configure it. 77 | 78 | Create new file `kpack/credentials/dockerhub-creds.yml` from template 79 | `kpack/credentials/dockerhub-creds.template.yml`, and set up your Docker Hub 80 | credentials (you may also use your own container registry): 81 | ```yaml 82 | apiVersion: v1 83 | kind: Secret 84 | metadata: 85 | name: dockerhub-creds 86 | namespace: kpack-builders 87 | annotations: 88 | build.pivotal.io/docker: https://index.docker.io/v1/ 89 | type: kubernetes.io/basic-auth 90 | stringData: 91 | username: johndoe 92 | password: changeme 93 | ``` 94 | 95 | Same story for GitHub access: kpack will monitor your Git repository, so that 96 | new container images are built when you update your source code. 97 | 98 | Create new file `kpack/credentials/github-creds.yml` from template 99 | `kpack/credentials/github-creds.template.yml` (feel free to use any Git repository): 100 | ```yaml 101 | apiVersion: v1 102 | kind: Secret 103 | metadata: 104 | name: github-creds 105 | namespace: kpack-builders 106 | annotations: 107 | build.pivotal.io/git: https://github.com 108 | type: kubernetes.io/basic-auth 109 | stringData: 110 | username: johndoe 111 | password: changeme 112 | ``` 113 | 114 | Please note you may need to use a 115 | [GitHub access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) 116 | if Two-Factor Authentication is enabled for your GitHub account. 117 | 118 | Then you need to edit container image definitions, targeting your container registry 119 | (see files `kpack/images/app-XXX-image.yml`) with the tag property: 120 | ```yaml 121 | apiVersion: build.pivotal.io/v1alpha1 122 | kind: Image 123 | metadata: 124 | name: app-foo 125 | namespace: kpack-builders 126 | spec: 127 | tag: alexandreroman/cnb-springboot-foo 128 | serviceAccount: kpack-service-account 129 | builder: 130 | name: custom-java-builder 131 | kind: CustomBuilder 132 | cacheSize: "2Gi" 133 | source: 134 | git: 135 | url: https://github.com/alexandreroman/cnb-springboot.git 136 | revision: testing 137 | ``` 138 | 139 | You're done! 140 | 141 | Please note a custom Java builder is defined in `kpack/custom-java-builder.yml`. 142 | This Java builder is already configured to use old buildpacks / stacks: 143 | later in this page we'll update this builder in place to reference newer 144 | buildpacks / stacks. 145 | 146 | Deploy this configuration to your Kubernetes cluster: 147 | ```bash 148 | $ kubectl apply -f kpack/credentials 149 | $ kubectl apply -f kpack/custom-java-builder.yml 150 | $ kubectl apply -f kpack/images 151 | ``` 152 | 153 | Check kpack configuration (the builder state is expected to be `BuilderReady`): 154 | ```bash 155 | $ kubectl -n kpack-builders describe image app-foo 156 | Name: app-foo 157 | Namespace: kpack-builders 158 | Labels: 159 | Annotations: kubectl.kubernetes.io/last-applied-configuration: 160 | {"apiVersion":"build.pivotal.io/v1alpha1","kind":"Image","metadata":{"annotations":{},"name":"app-foo","namespace":"kpack-builders"},"spec... 161 | API Version: build.pivotal.io/v1alpha1 162 | Kind: Image 163 | Metadata: 164 | Creation Timestamp: 2020-02-04T08:07:36Z 165 | Generation: 1 166 | Resource Version: 19276900 167 | Self Link: /apis/build.pivotal.io/v1alpha1/namespaces/kpack-builders/images/app-foo 168 | UID: 677aacb8-4725-11ea-a1b5-42010a000c0a 169 | Spec: 170 | Builder: 171 | Kind: CustomBuilder 172 | Name: custom-java-builder 173 | Cache Size: 2Gi 174 | Failed Build History Limit: 10 175 | Image Tagging Strategy: BuildNumber 176 | Service Account: kpack-service-account 177 | Source: 178 | Git: 179 | Revision: testing 180 | URL: https://github.com/alexandreroman/cnb-springboot.git 181 | Success Build History Limit: 10 182 | Tag: alexandreroman/cnb-springboot-foo 183 | Status: 184 | Build Cache Name: app-foo-cache 185 | Build Counter: 4 186 | Conditions: 187 | Last Transition Time: 2020-02-04T09:13:28Z 188 | Status: True 189 | Type: Ready 190 | Last Transition Time: 191 | Status: True 192 | Type: BuilderReady 193 | Latest Build Ref: app-foo-build-4-j7b92 194 | Latest Image: index.docker.io/alexandreroman/cnb-springboot-foo@sha256:14d03b8dbce97cd94a83e66f7875f04a75f6a3c9cad6ec0187ad51bdbb372457 195 | Latest Stack: org.cloudfoundry.stacks.cflinuxfs3 196 | Observed Generation: 1 197 | Events: 198 | ``` 199 | 200 | ### Deploying sample apps 201 | 202 | Edit Kubernetes descriptors in directory `k8s` to use your container registry: 203 | ```yaml 204 | spec: 205 | containers: 206 | - image: alexandreroman/cnb-springboot-foo 207 | name: app 208 | ``` 209 | 210 | Deploy the 3 sample apps: 211 | ```bash 212 | $ kubectl apply -f k8s 213 | ``` 214 | 215 | Make sure these apps are running 216 | (in 3 namespaces: `app-foo`, `app-bar`, `app-baz`): 217 | ```bash 218 | $ kubectl get pod,svc --all-namespaces -l app=cnb-springboot 219 | NAMESPACE NAME READY STATUS RESTARTS AGE 220 | app-bar pod/app-797997fb6b-zm8r5 1/1 Running 0 4m1s 221 | app-baz pod/app-76b89bc94b-dbxhm 1/1 Running 0 4m1s 222 | app-foo pod/app-5959488d8f-zt6rv 1/1 Running 0 4m1s 223 | 224 | NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 225 | app-bar service/app LoadBalancer 10.100.200.97 34.77.235.10 80:31084/TCP 18h 226 | app-baz service/app LoadBalancer 10.100.200.106 34.77.72.214 80:32176/TCP 18h 227 | app-foo service/app LoadBalancer 10.100.200.119 35.195.215.162 80:31606/TCP 18h 228 | ``` 229 | 230 | 231 | 232 | ### Deploying Concourse pipelines 233 | 234 | Let's create deployment pipelines: we want to automatically deploy the latest 235 | container image to our Kubernetes cluster. We expect kpack to build new container images 236 | as we'll simulate buildpack / stack updates. 237 | 238 | Deployment pipelines are implemented with 239 | [Concourse](https://concourse-ci.org). 240 | A simple `kubectl` command is executed to update Kubernetes deployments 241 | with new container image tags. Each pipeline is started when a new container image 242 | appears in the container registry. 243 | 244 | You first need to set configuration properties according to your environment. 245 | 246 | Create new file `ci/k8s.yml` starting from template `ci/k8s.template.yml`: 247 | ```yaml 248 | kubernetes-config: | 249 | >--- PASTE YOUR .kube/config HERE ---< 250 | 251 | # Select a context from the Kubernetes config. Leave empty to use default context. 252 | kubernetes-context: 253 | 254 | # Match this attribute with your cluster version. 255 | kubernetes-version: 1.15 256 | 257 | # Set to true to disable TLS verification when using kubectl commands. 258 | kubernetes-skip-tls-verification: false 259 | 260 | # Set how long to wait for pods to run (seconds), 0 means "don't wait". 261 | kubernetes-wait-until-ready: 180 262 | ``` 263 | 264 | You should only need to paste your `.kube/config` file in the configuration properties. 265 | 266 | Make sure you also update the pipeline configuration for each app (`ci/app-XXX.yml`) 267 | to use your container registry: 268 | ```yaml 269 | container-image: alexandreroman/cnb-springboot-foo 270 | ``` 271 | 272 | When you're done, deploy 3 pipelines (one for each app) to your Concourse instance: 273 | ```bash 274 | $ fly -t concourse set-pipeline -p app-foo -c ci/pipeline.yml -l ci/k8s.yml -l ci/app-foo.yml 275 | $ fly -t concourse set-pipeline -p app-bar -c ci/pipeline.yml -l ci/k8s.yml -l ci/app-bar.yml 276 | $ fly -t concourse set-pipeline -p app-baz -c ci/pipeline.yml -l ci/k8s.yml -l ci/app-baz.yml 277 | ``` 278 | 279 | Don't forget to unpause all pipelines. 280 | 281 | At this point, every time a new container image is deployed to your container registry, 282 | a pipeline will start to update the Kubernetes deployment with zero downtime. 283 | 284 | 285 | 286 | ### Monitoring application state 287 | 288 | Each app instance (pod) exposes metrics, leveraging Spring Boot Actuator and Micrometer. 289 | Metrics are available at `/actuator/metrics`, but you may also use `/actuator/prometheus` 290 | with Prometheus to scrape metrics. 291 | 292 | A custom metric is available in the app under `app_info`: this counter includes the 293 | Java version and the OpenSSL library version used in a container image. 294 | See `MetricsConfig.java` for implementation details. 295 | 296 | You may manually access this metric to see these values: 297 | ```bash 298 | $ http 35.195.215.162/actuator/metrics/app.info 299 | HTTP/1.1 200 300 | Connection: keep-alive 301 | Content-Disposition: inline;filename=f.txt 302 | Content-Type: application/vnd.spring-boot.actuator.v3+json 303 | Date: Tue, 04 Feb 2020 11:27:30 GMT 304 | Keep-Alive: timeout=60 305 | Transfer-Encoding: chunked 306 | 307 | { 308 | "availableTags": [ 309 | { 310 | "tag": "java.version", 311 | "values": [ 312 | "11.0.5" 313 | ] 314 | }, 315 | { 316 | "tag": "openssl.version", 317 | "values": [ 318 | "OpenSSL 1.1.1 11 Sep 2018 built on: Thu Jun 20 17:36:28 2019 UTC" 319 | ] 320 | } 321 | ], 322 | "baseUnit": null, 323 | "description": "Get application info", 324 | "measurements": [ 325 | { 326 | "statistic": "COUNT", 327 | "value": 1.0 328 | } 329 | ], 330 | "name": "app.info" 331 | } 332 | ``` 333 | 334 | You'd better use a visual dashboard, such as Grafana, to monitor application state. 335 | How lucky you are: this project includes such a dashboard! 336 | 337 | You may not have Prometheus / Grafana available in your Kubernetes cluster: 338 | [use this GitHub repository to deploy these components](https://github.com/alexandreroman/k8s-toolbox). 339 | 340 | Once you're ready, create a new dashboard using file `grafana/kpack-dashboard.json`: 341 | 342 | 343 | 344 | Next step: monitor application state while we'll simulate buildpack / stack updates. 345 | 346 | ### Updating JVM for all container images 347 | 348 | Let's assume a new JVM is out: this version includes many bugfixes, which may resolve 349 | security issues. You want all apps running in production to use this JVM version, right? 350 | How could you update all your container images with this JVM, at scale? 351 | 352 | Search no more: we'll let kpack handle this update for us. 353 | 354 | Apps currently use Java `11.0.5`: we want these apps to use Java `11.0.6`. 355 | 356 | Update the kpack Java builder with new buildpacks: 357 | ```bash 358 | $ kubectl apply -f kpack/jvm-update 359 | ``` 360 | 361 | If you observe kpack build logs, a new build should start soon 362 | (using `logs` CLI from kpack): 363 | ```bash 364 | $ logs -namespace kpack-builders -image app-foo 365 | [prepare] prepare:fetch.go:66: Successfully cloned "https://github.com/alexandreroman/cnb-springboot.git" @ "8532c6d23ff8a89f1de4854cbe0615de3cc0c415" in path "/workspace" 366 | [detect] org.cloudfoundry.openjdk v1.2.0 367 | [detect] org.cloudfoundry.buildsystem v1.2.0 368 | [detect] org.cloudfoundry.jvmapplication v1.1.0 369 | [detect] org.cloudfoundry.springboot v1.2.0 370 | [analyze] Restoring metadata for "org.cloudfoundry.openjdk:openjdk-jre" from app image 371 | [analyze] Restoring metadata for "org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b" from cache 372 | [analyze] Restoring metadata for "org.cloudfoundry.openjdk:6dd0c9c8a740e6c19149e98034fba8e368fd9aa16ab417aa636854d40db1a161" from cache 373 | [analyze] Restoring metadata for "org.cloudfoundry.openjdk:openjdk-jdk" from cache 374 | [analyze] Restoring metadata for "org.cloudfoundry.buildsystem:build-system-cache" from cache 375 | [analyze] Restoring metadata for "org.cloudfoundry.jvmapplication:executable-jar" from app image 376 | [analyze] Restoring metadata for "org.cloudfoundry.springboot:spring-boot" from app image 377 | [restore] Restoring data for "org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b" from cache 378 | [restore] Restoring data for "org.cloudfoundry.openjdk:6dd0c9c8a740e6c19149e98034fba8e368fd9aa16ab417aa636854d40db1a161" from cache 379 | [restore] Restoring data for "org.cloudfoundry.openjdk:openjdk-jdk" from cache 380 | [restore] Restoring data for "org.cloudfoundry.buildsystem:build-system-cache" from cache 381 | [restore] Restoring data for "org.cloudfoundry.jvmapplication:executable-jar" from cache 382 | [restore] Restoring data for "org.cloudfoundry.springboot:spring-boot" from cache 383 | [build] 384 | [build] Cloud Foundry OpenJDK Buildpack v1.2.0 385 | [build] OpenJDK JDK 11.0.6: Contributing to layer 386 | [build] Downloading from https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.6%2B10/OpenJDK11U-jdk_x64_linux_hotspot_11.0.6_10.tar.gz 387 | [build] Verifying checksum 388 | [build] Expanding to /layers/org.cloudfoundry.openjdk/openjdk-jdk 389 | [build] Writing JAVA_HOME to build 390 | [build] Writing JDK_HOME to build 391 | [build] OpenJDK JRE 11.0.6: Contributing to layer 392 | [build] Downloading from https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.6%2B10/OpenJDK11U-jre_x64_linux_hotspot_11.0.6_10.tar.gz 393 | [build] Verifying checksum 394 | [build] Expanding to /layers/org.cloudfoundry.openjdk/openjdk-jre 395 | [build] Writing JAVA_HOME to shared 396 | [build] Writing MALLOC_ARENA_MAX to shared 397 | [build] Writing .profile.d/active-processor-count 398 | ... 399 | [export] Reusing layer 'launcher' 400 | [export] Adding layer 'org.cloudfoundry.openjdk:class-counter' 401 | [export] Adding layer 'org.cloudfoundry.openjdk:java-security-properties' 402 | [export] Adding layer 'org.cloudfoundry.openjdk:jvmkill' 403 | [export] Adding layer 'org.cloudfoundry.openjdk:link-local-dns' 404 | [export] Adding layer 'org.cloudfoundry.openjdk:memory-calculator' 405 | [export] Adding layer 'org.cloudfoundry.openjdk:openjdk-jre' 406 | [export] Adding layer 'org.cloudfoundry.openjdk:security-provider-configurer' 407 | [export] Reusing layer 'org.cloudfoundry.jvmapplication:executable-jar' 408 | [export] Reusing layer 'org.cloudfoundry.springboot:spring-boot' 409 | [export] Adding 6/6 app layer(s) 410 | [export] Adding layer 'config' 411 | [export] *** Images (sha256:93936ad676a59795cd4ea28d050b98bed9bd4239d968d4cf847c3044c333d8ed): 412 | [export] alexandreroman/cnb-springboot-foo 413 | [export] index.docker.io/alexandreroman/cnb-springboot-foo:b5.20200204.114613 414 | [export] Adding cache layer 'org.cloudfoundry.openjdk:openjdk-jdk' 415 | [export] Adding cache layer 'org.cloudfoundry.buildsystem:build-system-application' 416 | [export] Reusing cache layer 'org.cloudfoundry.buildsystem:build-system-cache' 417 | [export] Reusing cache layer 'org.cloudfoundry.jvmapplication:executable-jar' 418 | [export] Reusing cache layer 'org.cloudfoundry.springboot:spring-boot' 419 | [completion] Build successful 420 | ``` 421 | 422 | New container images are built by kpack: you don't need to edit a `Dockerfile` 423 | to use a new JVM. You don't even need to update your source code! 424 | 425 | Look at the deployment pipeline: 426 | 427 | 428 | 429 | As Kubernetes deployments are getting updated with new container images, 430 | new pods are deployed with the new JVM: 431 | 432 | 433 | 434 | [Remember Heartbleed, the OpenSSL vulnerability issue?](https://en.wikipedia.org/wiki/Heartbleed) 435 | 436 | Suddenly, all container images using OpenSSL (that's a lot of container images!) had 437 | to be patched with a new version of this library. Depending on the number of container 438 | images you run, patching everything could take a lot of time. 439 | 440 | Thankfully we now have kpack! 441 | Let's see how to patch our apps with a newer OpenSSL library version. 442 | 443 | Update the Java builder with a new stack 444 | (which contains middleware librairies such as OpenSSL and glibc): 445 | ```bash 446 | $ kubectl apply -f kpack/stack-update 447 | ``` 448 | 449 | kpack soon discovers there's a new stack available, and a new build starts 450 | for each container image: 451 | ```bash 452 | $ logs -namespace kpack-builders -image app-foo 453 | [rebase] *** Images (sha256:14041569872bff7a85c1ed39cf6d41e0c5a5c1b8c6665b90cf89980c9ece3ddb): 454 | [rebase] alexandreroman/cnb-springboot-foo 455 | [rebase] index.docker.io/alexandreroman/cnb-springboot-foo:b6.20200204.120052 456 | [rebase] 457 | [rebase] *** Digest: sha256:14041569872bff7a85c1ed39cf6d41e0c5a5c1b8c6665b90cf89980c9ece3ddb 458 | [completion] Build successful 459 | ``` 460 | 461 | Please note that patching container images with a new stack is actually very fast. 462 | kpack leverages Cloud-Native Buildpacks to build container images. Container images 463 | built with this technology are optimized with dedicated layers for key components. 464 | Patching a stack only requires an update to a single layer. Moreover, since all 465 | layers are available in container registries, there is no need to build a new 466 | container image: existing container images are remotely "patched", by replacing 467 | the old stack layer by the newer one. This is why you only see a rebase operation 468 | here. 469 | 470 | In the end, all deployment pipelines will start since new container images are 471 | available: 472 | 473 | 474 | 475 | All running pods are now using fully patched container images: 476 | 477 | 478 | 479 | **Building and securing container images at scale is so easy with kpack!** 480 | 481 | ## Contribute 482 | 483 | Contributions are always welcome! 484 | 485 | Feel free to open issues & send PR. 486 | 487 | ## License 488 | 489 | Copyright © 2020 [VMware, Inc. or its affiliates](https://vmware.com). 490 | 491 | This project is licensed under the [Apache Software License version 2.0](https://www.apache.org/licenses/LICENSE-2.0). 492 | -------------------------------------------------------------------------------- /app-homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexandreroman/kpack-at-scale-demo/cae5a839d00df597c5f097b5ea252e01e77c644d/app-homepage.png -------------------------------------------------------------------------------- /ci/k8s.template.yml: -------------------------------------------------------------------------------- 1 | kubernetes-config: | 2 | >--- PASTE YOUR .kube/config HERE ---< 3 | 4 | # Select a context from the Kubernetes config. Leave empty to use default context. 5 | kubernetes-context: 6 | 7 | # Match this attribute with your cluster version. 8 | kubernetes-version: 1.15 9 | 10 | # Set to true to disable TLS verification when using kubectl commands. 11 | kubernetes-skip-tls-verification: false 12 | 13 | # Set how long to wait for pods to run (seconds), 0 means "don't wait". 14 | kubernetes-wait-until-ready: 180 15 | -------------------------------------------------------------------------------- /ci/pipeline.yml: -------------------------------------------------------------------------------- 1 | resource_types: 2 | - name: kubernetes 3 | type: docker-image 4 | source: 5 | repository: zlabjp/kubernetes-resource 6 | tag: "{{kubernetes-version}}" 7 | 8 | resources: 9 | - name: kubernetes-cluster 10 | type: kubernetes 11 | icon: kubernetes 12 | source: 13 | insecure_skip_tls_verify: {{kubernetes-skip-tls-verification}} 14 | kubeconfig: {{kubernetes-config}} 15 | context: {{kubernetes-context}} 16 | namespace: {{kubernetes-namespace}} 17 | - name: container-image 18 | type: docker-image 19 | icon: docker 20 | source: 21 | repository: ((container-image)) 22 | 23 | jobs: 24 | - name: deploy-latest-container-image 25 | plan: 26 | - get: container-image 27 | trigger: true 28 | params: 29 | skip_download: true 30 | - put: kubernetes-cluster 31 | params: 32 | kubectl: | 33 | set image deployment ((kubernetes-deployment)) ((kubernetes-container))=$(cat container-image/repository)@$(cat container-image/digest) 34 | wait_until_ready: {{kubernetes-wait-until-ready}} 35 | -------------------------------------------------------------------------------- /ci/vars-cnb-javawar.yml: -------------------------------------------------------------------------------- 1 | container-image: alexandreroman/cnb-javawar-kpack 2 | kubernetes-namespace: cnb-javawar 3 | kubernetes-deployment: app 4 | kubernetes-container: app 5 | -------------------------------------------------------------------------------- /ci/vars-cnb-nodejs.yml: -------------------------------------------------------------------------------- 1 | container-image: alexandreroman/cnb-nodejs-kpack 2 | kubernetes-namespace: cnb-nodejs 3 | kubernetes-deployment: app 4 | kubernetes-container: app 5 | -------------------------------------------------------------------------------- /ci/vars-cnb-springboot.yml: -------------------------------------------------------------------------------- 1 | container-image: alexandreroman/cnb-springboot-kpack 2 | kubernetes-namespace: cnb-springboot 3 | kubernetes-deployment: app 4 | kubernetes-container: app 5 | -------------------------------------------------------------------------------- /concourse-building.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexandreroman/kpack-at-scale-demo/cae5a839d00df597c5f097b5ea252e01e77c644d/concourse-building.png -------------------------------------------------------------------------------- /concourse-dashboard-building.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexandreroman/kpack-at-scale-demo/cae5a839d00df597c5f097b5ea252e01e77c644d/concourse-dashboard-building.png -------------------------------------------------------------------------------- /concourse-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexandreroman/kpack-at-scale-demo/cae5a839d00df597c5f097b5ea252e01e77c644d/concourse-dashboard.png -------------------------------------------------------------------------------- /concourse-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexandreroman/kpack-at-scale-demo/cae5a839d00df597c5f097b5ea252e01e77c644d/concourse-pipeline.png -------------------------------------------------------------------------------- /grafana-dashboard-secured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexandreroman/kpack-at-scale-demo/cae5a839d00df597c5f097b5ea252e01e77c644d/grafana-dashboard-secured.png -------------------------------------------------------------------------------- /grafana-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexandreroman/kpack-at-scale-demo/cae5a839d00df597c5f097b5ea252e01e77c644d/grafana-dashboard.png -------------------------------------------------------------------------------- /grafana-jvm-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexandreroman/kpack-at-scale-demo/cae5a839d00df597c5f097b5ea252e01e77c644d/grafana-jvm-update.png -------------------------------------------------------------------------------- /grafana/kpack-dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": "-- Grafana --", 7 | "enable": true, 8 | "hide": true, 9 | "iconColor": "rgba(0, 211, 255, 1)", 10 | "name": "Annotations & Alerts", 11 | "type": "dashboard" 12 | } 13 | ] 14 | }, 15 | "editable": true, 16 | "gnetId": null, 17 | "graphTooltip": 0, 18 | "id": 1, 19 | "links": [], 20 | "panels": [ 21 | { 22 | "cacheTimeout": null, 23 | "colorBackground": false, 24 | "colorValue": false, 25 | "colors": [ 26 | "#299c46", 27 | "rgba(237, 129, 40, 0.89)", 28 | "#d44a3a" 29 | ], 30 | "datasource": null, 31 | "format": "none", 32 | "gauge": { 33 | "maxValue": 100, 34 | "minValue": 0, 35 | "show": false, 36 | "thresholdLabels": false, 37 | "thresholdMarkers": true 38 | }, 39 | "gridPos": { 40 | "h": 2, 41 | "w": 4, 42 | "x": 0, 43 | "y": 0 44 | }, 45 | "id": 10, 46 | "interval": null, 47 | "links": [], 48 | "mappingType": 1, 49 | "mappingTypes": [ 50 | { 51 | "name": "value to text", 52 | "value": 1 53 | }, 54 | { 55 | "name": "range to text", 56 | "value": 2 57 | } 58 | ], 59 | "maxDataPoints": 100, 60 | "nullPointMode": "connected", 61 | "nullText": null, 62 | "options": {}, 63 | "postfix": "", 64 | "postfixFontSize": "50%", 65 | "prefix": "", 66 | "prefixFontSize": "50%", 67 | "rangeMaps": [ 68 | { 69 | "from": "null", 70 | "text": "N/A", 71 | "to": "null" 72 | } 73 | ], 74 | "sparkline": { 75 | "fillColor": "rgba(31, 118, 189, 0.18)", 76 | "full": false, 77 | "lineColor": "rgb(31, 120, 193)", 78 | "show": false, 79 | "ymax": null, 80 | "ymin": null 81 | }, 82 | "tableColumn": "", 83 | "targets": [ 84 | { 85 | "expr": "sum(app_info_total)", 86 | "instant": true, 87 | "refId": "A" 88 | } 89 | ], 90 | "thresholds": "", 91 | "timeFrom": null, 92 | "timeShift": null, 93 | "title": "Pods Running", 94 | "type": "singlestat", 95 | "valueFontSize": "80%", 96 | "valueMaps": [ 97 | { 98 | "op": "=", 99 | "text": "N/A", 100 | "value": "null" 101 | } 102 | ], 103 | "valueName": "current" 104 | }, 105 | { 106 | "aliasColors": { 107 | "sum(app_info_total{java_version=\"11.0.5\"})": "red" 108 | }, 109 | "bars": false, 110 | "dashLength": 10, 111 | "dashes": false, 112 | "datasource": null, 113 | "fill": 1, 114 | "fillGradient": 0, 115 | "gridPos": { 116 | "h": 8, 117 | "w": 9, 118 | "x": 4, 119 | "y": 0 120 | }, 121 | "hiddenSeries": false, 122 | "id": 2, 123 | "legend": { 124 | "avg": false, 125 | "current": false, 126 | "max": false, 127 | "min": false, 128 | "show": false, 129 | "total": false, 130 | "values": false 131 | }, 132 | "lines": true, 133 | "linewidth": 2, 134 | "nullPointMode": "null as zero", 135 | "options": { 136 | "dataLinks": [] 137 | }, 138 | "percentage": false, 139 | "pointradius": 2, 140 | "points": false, 141 | "renderer": "flot", 142 | "seriesOverrides": [], 143 | "spaceLength": 10, 144 | "stack": false, 145 | "steppedLine": false, 146 | "targets": [ 147 | { 148 | "expr": "sum(app_info_total{java_version=\"11.0.6\"})", 149 | "instant": false, 150 | "refId": "A" 151 | } 152 | ], 153 | "thresholds": [], 154 | "timeFrom": null, 155 | "timeRegions": [], 156 | "timeShift": null, 157 | "title": "Java 11.0.6", 158 | "tooltip": { 159 | "shared": true, 160 | "sort": 0, 161 | "value_type": "individual" 162 | }, 163 | "type": "graph", 164 | "xaxis": { 165 | "buckets": null, 166 | "mode": "time", 167 | "name": null, 168 | "show": true, 169 | "values": [] 170 | }, 171 | "yaxes": [ 172 | { 173 | "decimals": 0, 174 | "format": "short", 175 | "label": null, 176 | "logBase": 1, 177 | "max": null, 178 | "min": "0", 179 | "show": true 180 | }, 181 | { 182 | "format": "short", 183 | "label": null, 184 | "logBase": 1, 185 | "max": null, 186 | "min": null, 187 | "show": true 188 | } 189 | ], 190 | "yaxis": { 191 | "align": false, 192 | "alignLevel": null 193 | } 194 | }, 195 | { 196 | "aliasColors": { 197 | "sum(app_info_total{openssl_version=\"OpenSSL 1.1.1 11 Sep 2018 built on: Thu Jun 20 17:36:28 2019 UTC\"})": "red" 198 | }, 199 | "bars": false, 200 | "dashLength": 10, 201 | "dashes": false, 202 | "datasource": null, 203 | "fill": 1, 204 | "fillGradient": 0, 205 | "gridPos": { 206 | "h": 8, 207 | "w": 9, 208 | "x": 13, 209 | "y": 0 210 | }, 211 | "hiddenSeries": false, 212 | "id": 6, 213 | "legend": { 214 | "avg": false, 215 | "current": false, 216 | "max": false, 217 | "min": false, 218 | "show": false, 219 | "total": false, 220 | "values": false 221 | }, 222 | "lines": true, 223 | "linewidth": 2, 224 | "nullPointMode": "null as zero", 225 | "options": { 226 | "dataLinks": [] 227 | }, 228 | "percentage": false, 229 | "pointradius": 2, 230 | "points": false, 231 | "renderer": "flot", 232 | "seriesOverrides": [], 233 | "spaceLength": 10, 234 | "stack": false, 235 | "steppedLine": false, 236 | "targets": [ 237 | { 238 | "expr": "sum(app_info_total{openssl_version=\"OpenSSL 1.1.1 11 Sep 2018 built on: Thu Jun 20 17:36:28 2019 UTC\"})", 239 | "refId": "A" 240 | } 241 | ], 242 | "thresholds": [], 243 | "timeFrom": null, 244 | "timeRegions": [], 245 | "timeShift": null, 246 | "title": "OpenSSL Jun 2019", 247 | "tooltip": { 248 | "shared": true, 249 | "sort": 0, 250 | "value_type": "individual" 251 | }, 252 | "type": "graph", 253 | "xaxis": { 254 | "buckets": null, 255 | "mode": "time", 256 | "name": null, 257 | "show": true, 258 | "values": [] 259 | }, 260 | "yaxes": [ 261 | { 262 | "decimals": 0, 263 | "format": "short", 264 | "label": null, 265 | "logBase": 1, 266 | "max": null, 267 | "min": "0", 268 | "show": true 269 | }, 270 | { 271 | "format": "short", 272 | "label": null, 273 | "logBase": 1, 274 | "max": null, 275 | "min": null, 276 | "show": true 277 | } 278 | ], 279 | "yaxis": { 280 | "align": false, 281 | "alignLevel": null 282 | } 283 | }, 284 | { 285 | "cacheTimeout": null, 286 | "colorBackground": true, 287 | "colorPrefix": false, 288 | "colorValue": false, 289 | "colors": [ 290 | "#C4162A", 291 | "#FA6400", 292 | "#37872D" 293 | ], 294 | "datasource": null, 295 | "format": "none", 296 | "gauge": { 297 | "maxValue": 100, 298 | "minValue": 0, 299 | "show": false, 300 | "thresholdLabels": false, 301 | "thresholdMarkers": true 302 | }, 303 | "gridPos": { 304 | "h": 2, 305 | "w": 4, 306 | "x": 0, 307 | "y": 2 308 | }, 309 | "id": 14, 310 | "interval": null, 311 | "links": [], 312 | "mappingType": 1, 313 | "mappingTypes": [ 314 | { 315 | "name": "value to text", 316 | "value": 1 317 | }, 318 | { 319 | "name": "range to text", 320 | "value": 2 321 | } 322 | ], 323 | "maxDataPoints": 100, 324 | "nullPointMode": "connected", 325 | "nullText": null, 326 | "options": {}, 327 | "postfix": "", 328 | "postfixFontSize": "50%", 329 | "prefix": "", 330 | "prefixFontSize": "50%", 331 | "rangeMaps": [ 332 | { 333 | "from": "null", 334 | "text": "N/A", 335 | "to": "null" 336 | } 337 | ], 338 | "sparkline": { 339 | "fillColor": "rgba(31, 118, 189, 0.18)", 340 | "full": false, 341 | "lineColor": "rgb(31, 120, 193)", 342 | "show": false, 343 | "ymax": null, 344 | "ymin": null 345 | }, 346 | "tableColumn": "", 347 | "targets": [ 348 | { 349 | "expr": "sum(app_info_total{java_version=\"11.0.7\"})", 350 | "instant": true, 351 | "refId": "A" 352 | } 353 | ], 354 | "thresholds": "1,2", 355 | "timeFrom": null, 356 | "timeShift": null, 357 | "title": "Java Pods Up-to-date", 358 | "type": "singlestat", 359 | "valueFontSize": "80%", 360 | "valueMaps": [ 361 | { 362 | "op": "=", 363 | "text": "0", 364 | "value": "null" 365 | } 366 | ], 367 | "valueName": "current" 368 | }, 369 | { 370 | "cacheTimeout": null, 371 | "colorBackground": true, 372 | "colorPrefix": false, 373 | "colorValue": false, 374 | "colors": [ 375 | "#C4162A", 376 | "#FA6400", 377 | "#37872D" 378 | ], 379 | "datasource": null, 380 | "format": "none", 381 | "gauge": { 382 | "maxValue": 100, 383 | "minValue": 0, 384 | "show": false, 385 | "thresholdLabels": false, 386 | "thresholdMarkers": true 387 | }, 388 | "gridPos": { 389 | "h": 2, 390 | "w": 4, 391 | "x": 0, 392 | "y": 4 393 | }, 394 | "id": 15, 395 | "interval": null, 396 | "links": [], 397 | "mappingType": 1, 398 | "mappingTypes": [ 399 | { 400 | "name": "value to text", 401 | "value": 1 402 | }, 403 | { 404 | "name": "range to text", 405 | "value": 2 406 | } 407 | ], 408 | "maxDataPoints": 100, 409 | "nullPointMode": "connected", 410 | "nullText": null, 411 | "options": {}, 412 | "postfix": "", 413 | "postfixFontSize": "50%", 414 | "prefix": "", 415 | "prefixFontSize": "50%", 416 | "rangeMaps": [ 417 | { 418 | "from": "null", 419 | "text": "N/A", 420 | "to": "null" 421 | } 422 | ], 423 | "sparkline": { 424 | "fillColor": "rgba(31, 118, 189, 0.18)", 425 | "full": false, 426 | "lineColor": "rgb(31, 120, 193)", 427 | "show": false, 428 | "ymax": null, 429 | "ymin": null 430 | }, 431 | "tableColumn": "", 432 | "targets": [ 433 | { 434 | "expr": "sum(app_info_total{openssl_version=\"OpenSSL 1.1.1 11 Sep 2018 built on: Tue Nov 12 16:58:35 2019 UTC\"})", 435 | "instant": true, 436 | "refId": "A" 437 | } 438 | ], 439 | "thresholds": "1,3", 440 | "timeFrom": null, 441 | "timeShift": null, 442 | "title": "OpenSSL Up-to-date", 443 | "type": "singlestat", 444 | "valueFontSize": "80%", 445 | "valueMaps": [ 446 | { 447 | "op": "=", 448 | "text": "0", 449 | "value": "null" 450 | } 451 | ], 452 | "valueName": "current" 453 | }, 454 | { 455 | "aliasColors": {}, 456 | "bars": false, 457 | "dashLength": 10, 458 | "dashes": false, 459 | "datasource": null, 460 | "fill": 1, 461 | "fillGradient": 0, 462 | "gridPos": { 463 | "h": 8, 464 | "w": 9, 465 | "x": 4, 466 | "y": 8 467 | }, 468 | "hiddenSeries": false, 469 | "id": 4, 470 | "legend": { 471 | "avg": false, 472 | "current": false, 473 | "max": false, 474 | "min": false, 475 | "show": false, 476 | "total": false, 477 | "values": false 478 | }, 479 | "lines": true, 480 | "linewidth": 2, 481 | "nullPointMode": "null as zero", 482 | "options": { 483 | "dataLinks": [] 484 | }, 485 | "percentage": false, 486 | "pointradius": 2, 487 | "points": false, 488 | "renderer": "flot", 489 | "seriesOverrides": [], 490 | "spaceLength": 10, 491 | "stack": false, 492 | "steppedLine": false, 493 | "targets": [ 494 | { 495 | "expr": "sum(app_info_total{java_version=\"11.0.7\"})", 496 | "refId": "A" 497 | } 498 | ], 499 | "thresholds": [], 500 | "timeFrom": null, 501 | "timeRegions": [], 502 | "timeShift": null, 503 | "title": "Java 11.0.7", 504 | "tooltip": { 505 | "shared": true, 506 | "sort": 0, 507 | "value_type": "individual" 508 | }, 509 | "type": "graph", 510 | "xaxis": { 511 | "buckets": null, 512 | "mode": "time", 513 | "name": null, 514 | "show": true, 515 | "values": [] 516 | }, 517 | "yaxes": [ 518 | { 519 | "decimals": 0, 520 | "format": "short", 521 | "label": null, 522 | "logBase": 1, 523 | "max": null, 524 | "min": "0", 525 | "show": true 526 | }, 527 | { 528 | "format": "short", 529 | "label": null, 530 | "logBase": 1, 531 | "max": null, 532 | "min": null, 533 | "show": true 534 | } 535 | ], 536 | "yaxis": { 537 | "align": false, 538 | "alignLevel": null 539 | } 540 | }, 541 | { 542 | "aliasColors": {}, 543 | "bars": false, 544 | "dashLength": 10, 545 | "dashes": false, 546 | "datasource": null, 547 | "fill": 1, 548 | "fillGradient": 0, 549 | "gridPos": { 550 | "h": 8, 551 | "w": 9, 552 | "x": 13, 553 | "y": 8 554 | }, 555 | "hiddenSeries": false, 556 | "id": 8, 557 | "legend": { 558 | "avg": false, 559 | "current": false, 560 | "max": false, 561 | "min": false, 562 | "show": false, 563 | "total": false, 564 | "values": false 565 | }, 566 | "lines": true, 567 | "linewidth": 2, 568 | "nullPointMode": "null as zero", 569 | "options": { 570 | "dataLinks": [] 571 | }, 572 | "percentage": false, 573 | "pointradius": 2, 574 | "points": false, 575 | "renderer": "flot", 576 | "seriesOverrides": [], 577 | "spaceLength": 10, 578 | "stack": false, 579 | "steppedLine": false, 580 | "targets": [ 581 | { 582 | "expr": "sum(app_info_total{openssl_version=\"OpenSSL 1.1.1 11 Sep 2018 built on: Tue Nov 12 16:58:35 2019 UTC\"})", 583 | "refId": "A" 584 | } 585 | ], 586 | "thresholds": [], 587 | "timeFrom": null, 588 | "timeRegions": [], 589 | "timeShift": null, 590 | "title": "OpenSSL Nov 2019", 591 | "tooltip": { 592 | "shared": true, 593 | "sort": 0, 594 | "value_type": "individual" 595 | }, 596 | "type": "graph", 597 | "xaxis": { 598 | "buckets": null, 599 | "mode": "time", 600 | "name": null, 601 | "show": true, 602 | "values": [] 603 | }, 604 | "yaxes": [ 605 | { 606 | "decimals": 0, 607 | "format": "short", 608 | "label": null, 609 | "logBase": 1, 610 | "max": null, 611 | "min": "0", 612 | "show": true 613 | }, 614 | { 615 | "format": "short", 616 | "label": null, 617 | "logBase": 1, 618 | "max": null, 619 | "min": null, 620 | "show": true 621 | } 622 | ], 623 | "yaxis": { 624 | "align": false, 625 | "alignLevel": null 626 | } 627 | } 628 | ], 629 | "refresh": "10s", 630 | "schemaVersion": 21, 631 | "style": "dark", 632 | "tags": [], 633 | "templating": { 634 | "list": [] 635 | }, 636 | "time": { 637 | "from": "now-30m", 638 | "to": "now" 639 | }, 640 | "timepicker": { 641 | "refresh_intervals": [ 642 | "5s", 643 | "10s", 644 | "30s", 645 | "1m", 646 | "5m", 647 | "15m", 648 | "30m", 649 | "1h", 650 | "2h", 651 | "1d" 652 | ] 653 | }, 654 | "timezone": "", 655 | "title": "kpack at scale", 656 | "uid": "DOcXpv8Zz", 657 | "version": 3 658 | } -------------------------------------------------------------------------------- /k8s/cnb-javawar.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: cnb-javawar 5 | --- 6 | apiVersion: apps/v1 7 | kind: Deployment 8 | metadata: 9 | name: app 10 | namespace: cnb-javawar 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: cnb-javawar 16 | template: 17 | metadata: 18 | labels: 19 | app: cnb-javawar 20 | annotations: 21 | prometheus.io/scrape: "true" 22 | prometheus.io/path: "/metrics" 23 | prometheus.io/port: "8080" 24 | spec: 25 | containers: 26 | - image: alexandreroman/cnb-javawar-kpack 27 | name: app 28 | resources: 29 | limits: 30 | memory: 1Gi 31 | ports: 32 | - containerPort: 8080 33 | livenessProbe: 34 | httpGet: 35 | port: 8080 36 | path: /api/info 37 | initialDelaySeconds: 60 38 | periodSeconds: 2 39 | readinessProbe: 40 | httpGet: 41 | port: 8080 42 | path: /api/info 43 | initialDelaySeconds: 10 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | name: app 49 | namespace: cnb-javawar 50 | labels: 51 | app: cnb-javawar 52 | spec: 53 | type: LoadBalancer 54 | ports: 55 | - port: 80 56 | protocol: TCP 57 | targetPort: 8080 58 | selector: 59 | app: cnb-javawar 60 | -------------------------------------------------------------------------------- /k8s/cnb-nodejs.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: cnb-nodejs 5 | --- 6 | apiVersion: apps/v1 7 | kind: Deployment 8 | metadata: 9 | name: app 10 | namespace: cnb-nodejs 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: cnb-nodejs 16 | template: 17 | metadata: 18 | labels: 19 | app: cnb-nodejs 20 | annotations: 21 | prometheus.io/scrape: "true" 22 | prometheus.io/path: "/metrics" 23 | prometheus.io/port: "8080" 24 | spec: 25 | containers: 26 | - image: alexandreroman/cnb-nodejs-kpack 27 | name: app 28 | resources: 29 | limits: 30 | memory: 1Gi 31 | ports: 32 | - containerPort: 8080 33 | livenessProbe: 34 | httpGet: 35 | port: 8080 36 | path: /info 37 | initialDelaySeconds: 30 38 | periodSeconds: 2 39 | readinessProbe: 40 | httpGet: 41 | port: 8080 42 | path: /info 43 | initialDelaySeconds: 5 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | name: app 49 | namespace: cnb-nodejs 50 | labels: 51 | app: cnb-nodejs 52 | spec: 53 | type: LoadBalancer 54 | ports: 55 | - port: 80 56 | protocol: TCP 57 | targetPort: 8080 58 | selector: 59 | app: cnb-nodejs 60 | -------------------------------------------------------------------------------- /k8s/cnb-springboot.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: cnb-springboot 5 | --- 6 | apiVersion: apps/v1 7 | kind: Deployment 8 | metadata: 9 | name: app 10 | namespace: cnb-springboot 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: cnb-springboot 16 | template: 17 | metadata: 18 | labels: 19 | app: cnb-springboot 20 | annotations: 21 | prometheus.io/scrape: "true" 22 | prometheus.io/path: "/actuator/prometheus" 23 | prometheus.io/port: "8080" 24 | spec: 25 | containers: 26 | - image: alexandreroman/cnb-springboot-kpack 27 | name: app 28 | resources: 29 | limits: 30 | memory: 1Gi 31 | ports: 32 | - containerPort: 8080 33 | livenessProbe: 34 | httpGet: 35 | port: 8080 36 | path: /actuator/health 37 | initialDelaySeconds: 60 38 | periodSeconds: 2 39 | readinessProbe: 40 | httpGet: 41 | port: 8080 42 | path: /actuator/health 43 | initialDelaySeconds: 10 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | name: app 49 | namespace: cnb-springboot 50 | labels: 51 | app: cnb-springboot 52 | spec: 53 | type: LoadBalancer 54 | ports: 55 | - port: 80 56 | protocol: TCP 57 | targetPort: 8080 58 | selector: 59 | app: cnb-springboot 60 | -------------------------------------------------------------------------------- /kpack/01-namespace.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: kpack-builders 6 | -------------------------------------------------------------------------------- /kpack/02-service-account.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: kpack-service-account 6 | namespace: kpack-builders 7 | secrets: 8 | - name: dockerhub-creds 9 | - name: github-creds 10 | -------------------------------------------------------------------------------- /kpack/03-custom-stack.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 3 | kind: Stack 4 | metadata: 5 | name: bionic-stack 6 | namespace: kpack-builders 7 | spec: 8 | id: "io.buildpacks.stacks.bionic" 9 | buildImage: 10 | image: "cloudfoundry/build:0.0.1-base-cnb" 11 | runImage: 12 | image: "cloudfoundry/run:0.0.1-base-cnb" 13 | -------------------------------------------------------------------------------- /kpack/04-custom-java-builder.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 3 | kind: Store 4 | metadata: 5 | name: custom-java-store 6 | namespace: kpack-builders 7 | spec: 8 | sources: 9 | - image: cloudfoundry/cnb:0.0.81-bionic 10 | --- 11 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 12 | kind: CustomBuilder 13 | metadata: 14 | name: custom-java-builder 15 | namespace: kpack-builders 16 | spec: 17 | tag: alexandreroman/kpack-custom-java-builder 18 | serviceAccount: kpack-service-account 19 | stack: bionic-stack 20 | store: custom-java-store 21 | order: 22 | - group: 23 | - id: org.cloudfoundry.openjdk 24 | - id: org.cloudfoundry.buildsystem 25 | optional: true 26 | - id: org.cloudfoundry.jvmapplication 27 | optional: true 28 | - id: org.cloudfoundry.tomcat 29 | optional: true 30 | - id: org.cloudfoundry.springboot 31 | optional: true 32 | - id: org.cloudfoundry.distzip 33 | optional: true 34 | -------------------------------------------------------------------------------- /kpack/05-custom-nodejs-builder.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 3 | kind: Store 4 | metadata: 5 | name: custom-nodejs-store 6 | namespace: kpack-builders 7 | spec: 8 | sources: 9 | - image: gcr.io/paketo-buildpacks/builder:base 10 | --- 11 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 12 | kind: CustomBuilder 13 | metadata: 14 | name: custom-nodejs-builder 15 | namespace: kpack-builders 16 | spec: 17 | tag: alexandreroman/kpack-custom-nodejs-builder 18 | serviceAccount: kpack-service-account 19 | stack: bionic-stack 20 | store: custom-nodejs-store 21 | order: 22 | - group: 23 | - id: paketo-buildpacks/nodejs 24 | - id: paketo-buildpacks/node-engine 25 | - id: paketo-buildpacks/npm 26 | -------------------------------------------------------------------------------- /kpack/credentials/dockerhub-creds.template.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: kpack-builders 6 | --- 7 | apiVersion: v1 8 | kind: Secret 9 | metadata: 10 | name: dockerhub-creds 11 | namespace: kpack-builders 12 | annotations: 13 | build.pivotal.io/docker: https://index.docker.io/v1/ 14 | type: kubernetes.io/basic-auth 15 | stringData: 16 | username: 17 | password: 18 | -------------------------------------------------------------------------------- /kpack/credentials/github-creds.template.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: kpack-builders 6 | --- 7 | apiVersion: v1 8 | kind: Secret 9 | metadata: 10 | name: github-creds 11 | namespace: kpack-builders 12 | annotations: 13 | build.pivotal.io/git: https://github.com 14 | type: kubernetes.io/basic-auth 15 | stringData: 16 | username: 17 | password: 18 | -------------------------------------------------------------------------------- /kpack/images/cnb-javawar-image.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: build.pivotal.io/v1alpha1 3 | kind: Image 4 | metadata: 5 | name: cnb-javawar 6 | namespace: kpack-builders 7 | spec: 8 | tag: alexandreroman/cnb-javawar-kpack 9 | serviceAccount: kpack-service-account 10 | builder: 11 | name: custom-java-builder 12 | kind: CustomBuilder 13 | cacheSize: "2Gi" 14 | source: 15 | git: 16 | url: https://github.com/alexandreroman/cnb-javawar.git 17 | revision: testing 18 | -------------------------------------------------------------------------------- /kpack/images/cnb-nodejs-image.yml: -------------------------------------------------------------------------------- 1 | apiVersion: build.pivotal.io/v1alpha1 2 | kind: Image 3 | metadata: 4 | name: cnb-nodejs 5 | namespace: kpack-builders 6 | spec: 7 | tag: alexandreroman/cnb-nodejs-kpack 8 | serviceAccount: kpack-service-account 9 | builder: 10 | name: custom-nodejs-builder 11 | kind: CustomBuilder 12 | cacheSize: "2Gi" 13 | source: 14 | git: 15 | url: https://github.com/alexandreroman/cnb-nodejs.git 16 | revision: testing 17 | -------------------------------------------------------------------------------- /kpack/images/cnb-springboot-image.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: build.pivotal.io/v1alpha1 3 | kind: Image 4 | metadata: 5 | name: cnb-springboot 6 | namespace: kpack-builders 7 | spec: 8 | tag: alexandreroman/cnb-springboot-kpack 9 | serviceAccount: kpack-service-account 10 | builder: 11 | name: custom-java-builder 12 | kind: CustomBuilder 13 | cacheSize: "2Gi" 14 | source: 15 | git: 16 | url: https://github.com/alexandreroman/cnb-springboot.git 17 | revision: testing 18 | -------------------------------------------------------------------------------- /kpack/jvm-update/jvm-update.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 3 | kind: Store 4 | metadata: 5 | name: custom-java-store 6 | namespace: kpack-builders 7 | spec: 8 | sources: 9 | - image: gcr.io/paketo-buildpacks/builder:0.0.100-bionic 10 | --- 11 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 12 | kind: CustomBuilder 13 | metadata: 14 | name: custom-java-builder 15 | namespace: kpack-builders 16 | spec: 17 | tag: alexandreroman/kpack-custom-java-builder 18 | serviceAccount: kpack-service-account 19 | stack: bionic-stack 20 | store: custom-java-store 21 | order: 22 | - group: 23 | - id: paketo-buildpacks/bellsoft-liberica 24 | - id: paketo-buildpacks/build-system 25 | optional: true 26 | - id: paketo-buildpacks/executable-jar 27 | optional: true 28 | - id: paketo-buildpacks/apache-tomcat 29 | optional: true 30 | - id: paketo-buildpacks/spring-boot 31 | optional: true 32 | - id: paketo-buildpacks/dist-zip 33 | optional: true 34 | -------------------------------------------------------------------------------- /kpack/latest/jvm-latest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 3 | kind: Store 4 | metadata: 5 | name: custom-java-store 6 | namespace: kpack-builders 7 | spec: 8 | sources: 9 | - image: gcr.io/paketo-buildpacks/builder:base 10 | --- 11 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 12 | kind: CustomBuilder 13 | metadata: 14 | name: custom-java-builder 15 | namespace: kpack-builders 16 | spec: 17 | tag: alexandreroman/kpack-custom-java-builder 18 | serviceAccount: kpack-service-account 19 | stack: bionic-stack 20 | store: custom-java-store 21 | order: 22 | - group: 23 | - id: paketo-buildpacks/bellsoft-liberica 24 | - id: paketo-buildpacks/maven 25 | optional: true 26 | - id: paketo-buildpacks/executable-jar 27 | optional: true 28 | - id: paketo-buildpacks/apache-tomcat 29 | optional: true 30 | - id: paketo-buildpacks/spring-boot 31 | optional: true 32 | - id: paketo-buildpacks/dist-zip 33 | optional: true 34 | -------------------------------------------------------------------------------- /kpack/latest/stack-latest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 3 | kind: Stack 4 | metadata: 5 | name: bionic-stack 6 | namespace: kpack-builders 7 | spec: 8 | id: "io.buildpacks.stacks.bionic" 9 | buildImage: 10 | image: "gcr.io/paketo-buildpacks/build:base-cnb" 11 | runImage: 12 | image: "gcr.io/paketo-buildpacks/run:base-cnb" 13 | -------------------------------------------------------------------------------- /kpack/stack-update/stack-update.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: experimental.kpack.pivotal.io/v1alpha1 3 | kind: Stack 4 | metadata: 5 | name: bionic-stack 6 | namespace: kpack-builders 7 | spec: 8 | id: "io.buildpacks.stacks.bionic" 9 | buildImage: 10 | image: "gcr.io/paketo-buildpacks/build:0.0.12-base-cnb" 11 | runImage: 12 | image: "gcr.io/paketo-buildpacks/run:0.0.13-base-cnb" 13 | --------------------------------------------------------------------------------