This blog is a comprehensive guide to build and deploy apps to Kubernetes with Skaffold. This tool provides simple workflow to use for local development of apps. It’s portable tool and can be used to automate SDLC use cases for modules in an application. Skaffold has got multiple options to choose for building apps in different programming languages and to follow various deployment models.
Skaffold is an open source project from Google.
Awesome features of Skaffold
Skaffold is built with many devops workflow options
End-to-end Pipelines options:
run Run a pipeline
dev Run a pipeline in development mode
debug Run a pipeline in debug mode
Pipeline Building Blocks options:
build Build the artifacts
test Run tests against your built application images
deploy Deploy pre-built artifacts
delete Delete any resources deployed by Skaffold
render Perform all image builds, and output rendered Kubernetes manifests
apply Apply hydrated manifests to a cluster
Skaffold pipeline options can be used to build GitOps workflows
Skaffold is built as lightweight tool (binary or docker image) with opinionated options to use in Dev or external CI/CD components.
It detects changes in your source code and handles the pipeline to build, push, test and deploy your application automatically
It presents simplistic way for logging and port-forwarding for local and incremental app Development
Skaffold is packaged with many devops value adding components like profiles, local use config, environment variables and build/deploy options
Initial setup
Install Docker: We have installed Docker (Version: 20.10.11) in the local workstation ( which is running mac os ).
Install Skaffold CLI
The latest stable binaries can be found here. The install step for Mac OS :
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-darwin-amd64 && \
sudo install skaffold /usr/local/bin/
Note : Skaffold can also be installed with Homebrew.
brew install skaffold
Install kubectl – Kubectl can be installed on workstation following instructions here .
install kubernetes cluster or connect to existing cluster.
For local development, we can use either minikube or kind kubernetes cluster or docker based Kubernetes cluster.
NOTE: For devops or production use case, we can configure the current-context of kubectl on the workstation with an existing production grade kubernetes cluster.(eg: Tanzu kubernetes Grid , Openshift or Rancher ).
$ k config use-context kind-mycluster
Switched to context " kind-mycluster " .
$ kubectl config current-context
kind-mycluster
Note: For the current POC’s, we would be using kind cluster to connect deploy the artifacts.
An application with source-code to build and deploy for various Skaffold use cases.
We would use the Helloappjava to build and deploy in various example. Detailed steps to build and maintain this application is demonstrated here .
skaffold init
To configure any project with Skaffold, need to provide it’s config in base classpath location. These configs are mentioned in skaffold.yaml . For a new project, we can let Skaffold config get created automatically with build and deploy stages (as a minimum config).
To configure the project with Skaffold, we invoke skaffold init
.
$ skaffold init
apiVersion: skaffold/v2beta26
kind: Config
metadata:
name: helloappjava
build:
artifacts:
- image: helloappjava
buildpacks:
builder: gcr.io/buildpacks/builder:v1
deploy:
kubectl:
manifests:
- kubernetes/deployment.yml
- kubernetes/service.yml
? Do you want to write this configuration to skaffold.yaml ? Yes
Configuration skaffold.yaml was written
You can now run [skaffold build] to build the artifacts
or [skaffold run] to build and deploy
or [skaffold dev] to enter development mode, with auto-redeploy
This project does not contain any Dockerfile yet, thus Skaffold has picked buildpack as the default build mechanism for this project.
$ tree -L 4 -C .
.
├── README.md
├── kubernetes
│ ├── deployment.yml
│ └── service.yml
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── com
NOTE: For a detailed discussion on buildpack, we have another blog for reference. In my opinion, buildpack is an awesome choice for building apps and it comes with many features better then Dockerfile. So, unless we are working on an existing app, it’s always better to prepare to build apps with buildpack.
Skaffold scans through the project and identifies the Kubernetes resources for deploy stage.
We can override all the configs and also add more options and stages based on our need.
In case of conflicts where more then single build and deploy mechanism can be used for the project, we can provide the chosen option via below params
--artifact = ' {"builder":"Docker","payload":{"path":"/web/Dockerfile.web"},"image":"gcr.io/web-project/image"} ' )
--compose-file = '' : Initialize from a docker-compose file
--default-kustomization = '' : Default Kustomization overlay path
--kubernetes-manifest = [ ]: A path or a glob pattern to kubernetes manifests ( can be non-existent ) to be added to the kubectl deployer ( overrides detection of kubernetes manifests )
Instead of providing kubernetes resources, we can let them auto-generated with --generate-manifests=true
option. For this change, we have optionally removed the Kubernetes resources from the source folder.
$ skaffold init --generate-manifests=true
? Select port to forward for pom-xml-image ( leave blank for none ) : 8080
apiVersion: skaffold/v2beta26
kind: Config
metadata:
name: helloappjava
build:
artifacts:
- image: pom-xml-image
buildpacks:
builder: gcr.io/buildpacks/builder:v1
deploy:
kubectl:
manifests:
- deployment.yaml
portForward:
- resourceType: service
resourceName: pom-xml-image
port: 8080
deployment.yaml - apiVersion: v1
kind: Service
metadata:
name: pom-xml-image
labels:
app: pom-xml-image
spec:
ports:
- port: 8080
protocol: TCP
clusterIP: None
selector:
app: pom-xml-image
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: pom-xml-image
labels:
app: pom-xml-image
spec:
replicas: 1
selector:
matchLabels:
app: pom-xml-image
template:
metadata:
labels:
app: pom-xml-image
spec:
containers:
- name: pom-xml-image
image: pom-xml-image
? Do you want to write this configuration, along with the generated k8s manifests, to skaffold.yaml ? Yes
Generated manifest deployment.yaml was written
Configuration skaffold.yaml was written
$ cat skaffold.yaml
apiVersion: skaffold/v2beta26
kind: Config
metadata:
name: helloappjava
build:
artifacts:
- image: pom-xml-image
buildpacks:
builder: gcr.io/buildpacks/builder:v1
deploy:
kubectl:
manifests:
- deployment.yaml
portForward:
- resourceType: service
resourceName: pom-xml-image
port: 8080
The deployment and service resource is created within the deployment.yaml file in project base location and added into the Scaffold config. Also, Skaffold is smart enough to add the port-forward stage to expose the service to localhost for developer’s convenience.
Local development
We can use skaffold dev
for continuous build & deploy on code changes. For local development, we assume that docker would be installed on same workstation as the source-code and the kubernetes cluster would be configured.
skaffold dev analyse the source code to identify the config files and app language. It watches the source code and executes Skaffold pipeline every time a change is detected.
App build with buildpack
NOTE: The helloappjava application, is built with Java version 17, and the default builder image used by buildpack is configured for Java version 11. Thus, we have to do a modification to the skaffold.yaml to change the builder and runimage as mentioned below :
$ cat skaffold.yaml
apiVersion: skaffold/v2beta26
kind: Config
metadata:
name: helloappjava
build:
artifacts:
- image: pom-xml-image
buildpacks:
builder: paketobuildpacks/builder:base
runImage: paketobuildpacks/run:base-cnb
env:
- " BP_JVM_VERSION=17 "
...
We can use the skaffold.yaml to perform skaffold dev now :
$ skaffold dev
Listing files to watch...
- pom-xml-image
Generating tags...
- pom-xml-image - > pom-xml-image:dafd051-dirty
Checking cache...
- pom-xml-image: Not found. Building
Starting build...
Found [kind-mycluster] context, using local docker daemon.
Building [pom-xml-image]...
base: Pulling from paketobuildpacks/builder
4f4fb700ef54: Pull complete
Digest: sha256:7308b52dc7dfd9ef2d1874ae905b44d489b1867bdb9c3c6fd21c28f941437abd
Status: Downloaded newer image for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
bf99a8b93828: Already exists
1d5075f6f6b5: Pull complete
Digest: sha256:50c74ebc95c169ea54ee4650d39d3c71d0fc738a3f0ad89b23d646cfd9ea36f2
Status: Downloaded newer image for paketobuildpacks/run:base-cnb
0.13.2: Pulling from buildpacksio/lifecycle
ee78fc634fdc: Pull complete
Digest: sha256:b7f2b9f44525ed0e962bdf5be186a266e36adc502b3dc0595dd2aac7f2b92e75
Status: Downloaded newer image for buildpacksio/lifecycle:0.13.2
=== > DETECTING
[ detector ] 8 of 19 buildpacks participating
[ detector ] paketo-buildpacks/ca-certificates 3.0.2
[ detector ] paketo-buildpacks/bellsoft-liberica 9.0.2
[ detector ] paketo-buildpacks/syft 1.3.1
[ detector ] paketo-buildpacks/maven 6.0.2
[ detector ] paketo-buildpacks/executable-jar 6.0.2
[ detector ] paketo-buildpacks/apache-tomcat 7.0.3
[ detector ] paketo-buildpacks/dist-zip 5.0.2
[ detector ] paketo-buildpacks/spring-boot 5.3.0
=== > ANALYZING
[ analyzer ] Previous image with name " pom-xml-image:latest " not found
=== > RESTORING
=== > BUILDING
[ builder ] Paketo CA Certificates Buildpack 3.0.2
[ builder ] https://github.com/paketo-buildpacks/ca-certificates
[ builder ] Launch Helper: Contributing to layer
[ builder ] Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
[ builder ] Paketo BellSoft Liberica Buildpack 9.0.2
[ builder ] https://github.com/paketo-buildpacks/bellsoft-liberica
[ builder ] Build Configuration:
[ builder ] $BP_JVM_TYPE JRE the JVM type - JDK or JRE
[ builder ] $BP_JVM_VERSION 17 the Java version
[ builder ] Launch Configuration:
[ builder ] $BPL_DEBUG_ENABLED false enables Java remote debugging support
[ builder ] $BPL_DEBUG_PORT 8000 configure the remote debugging port
[ builder ] $BPL_DEBUG_SUSPEND false configure whether to suspend execution until a debugger has attached
[ builder ] $BPL_HEAP_DUMP_PATH write heap dumps on error to this path
[ builder ] $BPL_JAVA_NMT_ENABLED true enables Java Native Memory Tracking ( NMT )
[ builder ] $BPL_JAVA_NMT_LEVEL summary configure level of NMT, summary or detail
[ builder ] $BPL_JFR_ARGS configure custom Java Flight Recording ( JFR ) arguments
[ builder ] $BPL_JFR_ENABLED false enables Java Flight Recording ( JFR )
[ builder ] $BPL_JMX_ENABLED false enables Java Management Extensions ( JMX )
[ builder ] $BPL_JMX_PORT 5000 configure the JMX port
[ builder ] $BPL_JVM_HEAD_ROOM 0 the headroom in memory calculation
[ builder ] $BPL_JVM_LOADED_CLASS_COUNT 35% of classes the number of loaded classes in memory calculation
[ builder ] $BPL_JVM_THREAD_COUNT 250 the number of threads in memory calculation
[ builder ] $JAVA_TOOL_OPTIONS the JVM launch flags
[ builder ] BellSoft Liberica JDK 17.0.1: Contributing to layer
[ builder ] Downloading from https://github.com/bell-sw/Liberica/releases/download/17.0.1+12/bellsoft-jdk17.0.1+12-linux-amd64.tar.gz
[ builder ] Verifying checksum
[ builder ] Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jdk
[ builder ] Adding 128 container CA certificates to JVM truststore
[ builder ] Writing env.build/JAVA_HOME.override
[ builder ] Writing env.build/JDK_HOME.override
[ builder ] BellSoft Liberica JRE 17.0.1: Contributing to layer
[ builder ] Downloading from https://github.com/bell-sw/Liberica/releases/download/17.0.1+12/bellsoft-jre17.0.1+12-linux-amd64.tar.gz
[ builder ] Verifying checksum
[ builder ] Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
[ builder ] Adding 128 container CA certificates to JVM truststore
[ builder ] Writing env.launch/BPI_APPLICATION_PATH.default
[ builder ] Writing env.launch/BPI_JVM_CACERTS.default
[ builder ] Writing env.launch/BPI_JVM_CLASS_COUNT.default
[ builder ] Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
[ builder ] Writing env.launch/JAVA_HOME.default
[ builder ] Writing env.launch/JAVA_TOOL_OPTIONS.append
[ builder ] Writing env.launch/JAVA_TOOL_OPTIONS.delim
[ builder ] Writing env.launch/MALLOC_ARENA_MAX.default
[ builder ] Launch Helper: Contributing to layer
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/active-processor-count
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/java-opts
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jvm-heap
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/link-local-dns
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/openssl-certificate-loader
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-configurer
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jmx
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jfr
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/nmt
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-classpath-9
[ builder ] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/debug-9
[ builder ] Java Security Properties: Contributing to layer
[ builder ] Writing env.launch/JAVA_SECURITY_PROPERTIES.default
[ builder ] Writing env.launch/JAVA_TOOL_OPTIONS.append
[ builder ] Writing env.launch/JAVA_TOOL_OPTIONS.delim
[ builder ] Paketo Syft Buildpack 1.3.1
[ builder ] https://github.com/paketo-buildpacks/syft
[ builder ] Downloading from https://github.com/anchore/syft/releases/download/v0.33.0/syft_0.33.0_linux_amd64.tar.gz
[ builder ] Verifying checksum
[ builder ] Writing env.build/SYFT_CHECK_FOR_APP_UPDATE.default
[ builder ] Paketo Maven Buildpack 6.0.2
[ builder ] https://github.com/paketo-buildpacks/maven
[ builder ] Build Configuration:
[ builder ] $BP_MAVEN_BUILD_ARGUMENTS -Dmaven.test.skip = true package the arguments to pass to Maven
[ builder ] $BP_MAVEN_BUILT_ARTIFACT target/ * . [ ejw ] ar the built application artifact explicitly. Supersedes $BP_MAVEN_BUILT_MODULE
[ builder ] $BP_MAVEN_BUILT_MODULE the module to find application artifact in
[ builder ] $BP_MAVEN_POM_FILE pom.xml the location of the main pom.xml file, relative to the application root
[ builder ] Creating cache directory /home/cnb/.m2
[ builder ] Compiled Application: Contributing to layer
[ builder ] Executing mvnw --batch-mode -Dmaven.test.skip = true package
[ builder ] [ INFO ] Scanning for projects...
[ builder ] [ INFO ] --------------------- < com.txconsole:helloappjava > ---------------------
[ builder ] [ INFO ] Building helloappjava 0.0.1-SNAPSHOT
[ builder ] [ INFO ] -------------------------------- [ jar ] ---------------------------------
[ builder ] [ INFO ] Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-maven-plugin/2.6.2/spring-boot-maven-plugin-2.6.2.pom
[ builder ] [ INFO ] Replacing main artifact with repackaged archive
[ builder ] [ INFO ] ------------------------------------------------------------------------
[ builder ] [ INFO ] BUILD SUCCESS
[ builder ] [ INFO ] ------------------------------------------------------------------------
[ builder ] [ INFO ] Total time: 05:50 min
[ builder ] [ INFO ] Finished at: 2021-12-29T09:22:19Z
[ builder ] [ INFO ] ------------------------------------------------------------------------
[ builder ] Removing source code
[ builder ] Paketo Executable JAR Buildpack 6.0.2
[ builder ] https://github.com/paketo-buildpacks/executable-jar
[ builder ] Class Path: Contributing to layer
[ builder ] Writing env/CLASSPATH.delim
[ builder ] Writing env/CLASSPATH.prepend
[ builder ] Process types:
[ builder ] executable-jar: java org.springframework.boot.loader.JarLauncher ( direct )
[ builder ] task: java org.springframework.boot.loader.JarLauncher ( direct )
[ builder ] web: java org.springframework.boot.loader.JarLauncher ( direct )
[ builder ] Paketo Spring Boot Buildpack 5.3.0
[ builder ] https://github.com/paketo-buildpacks/spring-boot
[ builder ] Creating slices from layers index
[ builder ] dependencies
[ builder ] spring-boot-loader
[ builder ] snapshot-dependencies
[ builder ] application
[ builder ] Launch Helper: Contributing to layer
[ builder ] Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings
[ builder ] Spring Cloud Bindings 1.8.0: Contributing to layer
[ builder ] Downloading from https://repo.spring.io/release/org/springframework/cloud/spring-cloud-bindings/1.8.0/spring-cloud-bindings-1.8.0.jar
[ builder ] Verifying checksum
[ builder ] Copying to /layers/paketo-buildpacks_spring-boot/spring-cloud-bindings
[ builder ] Web Application Type: Contributing to layer
[ builder ] Servlet web application detected
[ builder ] Writing env.launch/BPL_JVM_THREAD_COUNT.default
[ builder ] 4 application slices
[ builder ] Image labels:
[ builder ] org.opencontainers.image.title
[ builder ] org.opencontainers.image.version
[ builder ] org.springframework.boot.version
=== > EXPORTING
[ exporter ] Setting default process type ' web '
[ exporter ] Saving pom-xml-image:latest...
[ exporter ] *** Images ( ae0f87dd22a6 ):
[ exporter ] pom-xml-image:latest
[ exporter ] Adding cache layer ' paketo-buildpacks/bellsoft-liberica:jdk '
[ exporter ] Adding cache layer ' paketo-buildpacks/syft:syft '
[ exporter ] Adding cache layer ' paketo-buildpacks/maven:application '
[ exporter ] Adding cache layer ' paketo-buildpacks/maven:cache '
Tags used in deployment:
- pom-xml-image - > pom-xml-image:ae0f87dd22a69604b1ff2c89089f4740eceec74fa357704367283b1a76ce9e9f
Starting deploy...
Loading images into kind cluster nodes...
- pom-xml-image:ae0f87dd22a69604b1ff2c89089f4740eceec74fa357704367283b1a76ce9e9f - > Loaded
Images loaded in 16.666 seconds
- service/pom-xml-image created
- deployment.apps/pom-xml-image created
Waiting for deployments to stabilize...
- deployment/pom-xml-image is ready.
Deployments stabilized in 2.193 seconds
Port forwarding service/pom-xml-image in namespace default, remote port 8080 - > http://127.0.0.1:8080
Press Ctrl+C to exit
Watching for changes...
[ pom-xml-image ] Setting Active Processor Count to 4
[ pom-xml-image ] WARNING: Unable to convert memory limit " max " from path " /sys/fs/cgroup/memory.max " as int: memory size " max " does not match pattern " ^([ \\ d]+)([kmgtKMGT]?)$ "
[ pom-xml-image ] Calculating JVM memory based on 953836K available memory
[ pom-xml-image ] Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize = 10M -Xmx362738K -XX:MaxMetaspaceSize=79097K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 953836K, Thread Count: 250, Loaded Class Count: 11551, Headroom: 0% )
[ pom-xml-image ] Enabling Java Native Memory Tracking
[ pom-xml-image ] Adding 128 container CA certificates to JVM truststore
[ pom-xml-image ] Spring Cloud Bindings Enabled
[ pom-xml-image ] Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties = /layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx362738K -XX:MaxMetaspaceSize=79097K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true
[ pom-xml-image ]
[ pom-xml-image ] . ____ _ __ _ _
[ pom-xml-image ] /\\ / ___ ' _ __ _ _(_)_ __ __ _ \ \ \ \
[pom-xml-image] ( ( )\___ | ' _ | '_| | ' _ \/ _ ` | \ \ \ \
[pom-xml-image] \\ / ___) | | _ ) | | | | | || ( _ | | ) ) ) )
[ pom-xml-image ] ' |____| .__|_| |_|_| |_\__, | / / / /
[pom-xml-image] =========|_|==============|___/=/_/_/_/
[pom-xml-image] :: Spring Boot :: (v2.6.2)
[pom-xml-image]
[pom-xml-image] 2021-12-29 09:23:01.286 INFO 1 --- [ main] c.t.h.HelloappjavaApplication : Starting HelloappjavaApplication v0.0.1-SNAPSHOT using Java 17.0.1 on pom-xml-image-57dbcd56f7-h4npq with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
[pom-xml-image] 2021-12-29 09:23:01.289 INFO 1 --- [ main] c.t.h.HelloappjavaApplication : No active profile set, falling back to default profiles: default
[pom-xml-image] 2021-12-29 09:23:03.127 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
[pom-xml-image] 2021-12-29 09:23:03.156 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
[pom-xml-image] 2021-12-29 09:23:03.157 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.56]
[pom-xml-image] 2021-12-29 09:23:03.281 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
[pom-xml-image] 2021-12-29 09:23:03.282 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1919 ms
[pom-xml-image] 2021-12-29 09:23:03.829 INFO 1 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
[pom-xml-image] 2021-12-29 09:23:04.166 INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 13 endpoint(s) beneath base path ' /manage '
[pom-xml-image] 2021-12-29 09:23:04.243 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
[pom-xml-image] 2021-12-29 09:23:04.268 INFO 1 --- [ main] c.t.h.HelloappjavaApplication : Started HelloappjavaApplication in 3.761 seconds (JVM running for 4.696)
[pom-xml-image] 2021-12-29 09:23:31.500 INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet ' dispatcherServlet '
[pom-xml-image] 2021-12-29 09:23:31.501 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet ' dispatcherServlet '
[pom-xml-image] 2021-12-29 09:23:31.506 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
[pom-xml-image] 2021-12-29 09:23:31.577 INFO 1 --- [nio-8080-exec-1] c.t.h.controllers.GreetingController : LOG: index - {greet=Hello, name=KingKong, appversion=v1.0.0}
^CCleaning up...
- service "pom-xml-image" deleted
- deployment.apps "pom-xml-image" deleted
App build with Dockerfile
To test the app build with Dockerfile, we have added Dockerfile to the source root folder.
$ cat Dockerfile
FROM openjdk:17-jdk-alpine
MAINTAINER txconsole.com
COPY target/ * .jar app.jar
ENTRYPOINT [ " java " , " -jar " , " /app.jar " ]
We can remove the existing skaffold.yaml (if any) and now, we can trigger Skaffold init
, to start from scratch and create new Skaffold config for the project. It would recognize that the app can be built in 2 ways – Dockerfile and Buildpack. Thus, present an option for the developer to choose the option.
$ skaffold init
? Choose the builder to build image helloappjava [ Use arrows to move, type to filter ]
> Buildpacks ( pom.xml )
Docker (Dockerfile)
None (image not built from these sources )
NOTE: The above build selection can be overridden with skaffold init --artifact='{"builder":"Docker","payload":{"path":"Dockerfile"},"image":"helloappjava"}'
.
Since we are using kind cluster without any ingress, we can add another stage for port-forwarding the service, so that developer can access the application on the localhost after it’s deployed.
$ cat skaffold.yaml
apiVersion: skaffold/v2beta26
kind: Config
metadata:
name: helloappjava
build:
artifacts:
- image: helloappjava
docker:
dockerfile: Dockerfile
deploy:
kubectl:
manifests:
- kubernetes/deployment.yml
- kubernetes/service.yml
portForward:
- resourceType: service
resourceName: helloappjava
port: 8080
Since we are working with java project which is built with maven, so we can run mvn clean package prior to executing skaffold to pick the right *.jar for image build and deploy purpose.
We can safely trigger the build and deploy of the application locally.
$ skaffold dev
Listing files to watch...
- helloappjava
Generating tags...
- helloappjava - > helloappjava:dafd051-dirty
Checking cache...
- helloappjava: Not found. Building
Starting build...
Found [kind-mycluster] context, using local docker daemon.
Building [helloappjava]...
Sending build context to Docker daemon 21.86MB
Step 1/4 : FROM openjdk:17-jdk-alpine
17-jdk-alpine: Pulling from library/openjdk
5843afab3874: Already exists
53c9466125e4: Already exists
d8d715783b80: Already exists
Digest: sha256:4b6abae565492dbe9e7a894137c966a7485154238902f2f25e9dbd9784383d81
Status: Downloaded newer image for openjdk:17-jdk-alpine
--- > 264c9bdce361
Step 2/4 : MAINTAINER txconsole.com
--- > Running in f74d926c5c18
--- > fefd8b6517ba
Step 3/4 : COPY target/ * .jar app.jar
--- > 1792c8647de7
Step 4/4 : ENTRYPOINT [ " java " , " -jar " , " /app.jar " ]
--- > Running in f68ddae3ef09
--- > 60bf6d822209
Successfully built 60bf6d822209
Successfully tagged helloappjava:dafd051-dirty
Tags used in deployment:
- helloappjava - > helloappjava:60bf6d8222092bb2ef141dddf5425b8311412b0d869c7aa68bc2ca6855d23fb4
Starting deploy...
Loading images into kind cluster nodes...
- helloappjava:60bf6d8222092bb2ef141dddf5425b8311412b0d869c7aa68bc2ca6855d23fb4 - > Loaded
Images loaded in 18.094 seconds
- deployment.apps/helloappjava created
- service/helloappjava created
Waiting for deployments to stabilize...
- helloapp:deployment/helloappjava is ready.
Deployments stabilized in 2.188 seconds
Press Ctrl+C to exit
Watching for changes...
[ helloappjava ]
[ helloappjava ] . ____ _ __ _ _
[ helloappjava ] /\\ / ___ ' _ __ _ _(_)_ __ __ _ \ \ \ \
[helloappjava] ( ( )\___ | ' _ | '_| | ' _ \/ _ ` | \ \ \ \
[helloappjava] \\ / ___) | | _ ) | | | | | || ( _ | | ) ) ) )
[ helloappjava ] ' |____| .__|_| |_|_| |_\__, | / / / /
[helloappjava] =========|_|==============|___/=/_/_/_/
[helloappjava] :: Spring Boot :: (v2.6.2)
[helloappjava]
[helloappjava] 2021-12-29 12:52:49.943 INFO 1 --- [ main] c.t.h.HelloappjavaApplication : Starting HelloappjavaApplication v0.0.1-SNAPSHOT using Java 17-ea on helloappjava-7ccd79f4b6-s4z77 with PID 1 (/app.jar started by root in /)
[helloappjava] 2021-12-29 12:52:49.950 INFO 1 --- [ main] c.t.h.HelloappjavaApplication : No active profile set, falling back to default profiles: default
[helloappjava] 2021-12-29 12:52:52.194 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
[helloappjava] 2021-12-29 12:52:52.211 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
[helloappjava] 2021-12-29 12:52:52.211 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.56]
[helloappjava] 2021-12-29 12:52:52.343 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
[helloappjava] 2021-12-29 12:52:52.344 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2299 ms
[helloappjava] 2021-12-29 12:52:53.408 INFO 1 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index
[helloappjava] 2021-12-29 12:52:53.814 INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 13 endpoint(s) beneath base path ' /manage '
[helloappjava] 2021-12-29 12:52:53.875 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
[helloappjava] 2021-12-29 12:52:53.895 INFO 1 --- [ main] c.t.h.HelloappjavaApplication : Started HelloappjavaApplication in 4.955 seconds (JVM running for 5.747)
^CCleaning up...
- deployment.apps "helloappjava" deleted
- service "helloappjava" deleted
Wrapping Up
We have seen 2 most common ways of building the application and deploying apps to kubernetes cluster. We have seen deploying apps to Kubernetes with kubectl which is the most common approach. Along with it, we can use other means to deploy to kubernetes – kustomize and helm . Since the topic of current discussion is for local development, thus kubectl would be a perfect fit for most of the use cases. Other deploy means can be used for production deploy or devops workflows.
Skaffold can also be used for production grade CI/CD workflow implementations which we would cover in another blog.