Provides a nice Java API library and a Java CLI (Command-line interface) client for Vaadin Shepherd and Vaadin Shepherd Traefik
Requires Java 17+.
The library is available in Maven Central. To use, add this to your build.gradle:
dependencies {
implementation("com.github.mvysny.shepherd:shepherd-java-api:0.3")
}To use, simply instantiate an implementation of ShepherdClient:
val client: ShepherdClient = LinuxShepherdClient() // or FakeShepherdClient()LinuxShepherdClient requires Shepherd to be installed on this machine. However,
for development purposes, it's better to use FakeShepherdClient which doesn't
require anything to be installed on the dev machine, while providing reasonable
fake data.
Requires a configuration file to be placed in /etc/shepherd/java/config.json, example contents:
{
"memoryQuotaMb": 14102,
"concurrentJenkinsBuilders": 2,
"maxProjectRuntimeResources": {
"memoryMb": 512,
"cpu": 1
},
"maxProjectBuildResources": {
"memoryMb": 2500,
"cpu": 2
},
"jenkins": {
"url": "http://jenkins:8080",
"username": "admin",
"password": "admin"
},
"hostDNS": "mydomain.me",
"containerSystem": "traefik-docker"
}The memoryQuotaMb is the memory available both for project runtime and for project builds.
Every project's runtime memory + the build memory is guaranteed by Shepherd; if a project would be created
that overflows this quota, the project creation is prohibited. Calculate the quota value
as follows: Take the total host machine memory, subtract memory for Jenkins usage (by default 512mb), for Kubernetes itself (say 1000mb),
possibly 500mb for the future shepherd-ui project, and finally subtract memory for OS usage (say 200mb).
Additional configuration options:
hostDNS: where Shepherd is running, e.g. "v-herd.eu"googleSSOClientId(Shepherd-Web only): enable Google SSO login and use this client ID. See vaadin-google-oauth for more details.ssoOnlyAllowEmailsEndingWith(Shepherd-Web only): if not null, only e-mails ending with this string are allowed. Example:@vaadin.com. If null or empty, all e-mails are allowed.shepherdHomeShepherd home,/opt/shepherdfor Shepherd Kubernetes,/opt/shepherd-traefikfor Shepherd Traefik.containerSystemthe runtime container system to use for running project containers, either"kubernetes"or"traefik-docker".
Regarding the "jenkins" setting:
- In Traefik mode, Jenkins will be running in the same private Docker network as Shepherd; Shepherd
will talk to Jenkins directly at
http://jenkins:8080. - In Kubernetes mode, Jenkins runs directly at host, and so does Shepherd. Shepherd
therefore talks to Jenkins via
http://localhost:8080. Make sure to change the configuration accordingly.
The shepherd-cli project provides a command-line client for Shepherd.
Simply build the CLI via ./gradlew shepherd-cli:build, then scp
the shepherd-cli/build/distributions/*.zip to the target machine which runs Shepherd,
then unzip and run the shepherd-cli binary.
Shepherd CLI requires Java 17+.
shepherd-cli create requires the project descriptor json. It's really simple,
here's a very simple example for the vaadin-boot-example-gradle project:
{
"id": "vaadin-boot-example-gradle",
"description": "vaadin-boot-example-gradle",
"gitRepo": {
"url": "https://github.com/mvysny/vaadin-boot-example-gradle",
"branch": "master"
},
"owner": {
"name": "Martin Vysny",
"email": "mavi@vaadin.com"
},
"runtime": {
"resources": {
"memoryMb": 256,
"cpu": 1.0
}
},
"build": {
"resources": {
"memoryMb": 2048,
"cpu": 2.0
}
}
}A more complex example:
{
"id": "jdbi-orm-vaadin-crud-demo",
"description": "JDBI-ORM example project",
"gitRepo": {
"url": "https://github.com/mvysny/jdbi-orm-vaadin-crud-demo",
"branch": "master",
"credentialsID": "c4d257ce-0048-11ee-a0b5-ffedf9ffccf4"
},
"owner": {
"name": "Martin Vysny",
"email": "mavi@vaadin.com"
},
"runtime": {
"resources": {
"memoryMb": 256,
"cpu": 1.0
},
"envVars": {
"JDBC_URL": "jdbc:postgresql://postgres-service:5432/postgres",
"JDBC_USERNAME": "postgres",
"JDBC_PASSWORD": "mysecretpassword"
}
},
"build": {
"resources": {
"memoryMb": 2048,
"cpu": 2.0
},
"buildArgs": {
"offlinekey": "q3984askdjalkd9823"
},
"dockerFile": "vherd.Dockerfile"
},
"publication": {
"publishOnMainDomain": false,
"https": true,
"additionalDomains": [
"demo.jdbiorm.eu"
],
"ingressConfig": {
"maxBodySizeMb": 2,
"proxyReadTimeoutSeconds": 360
}
},
"additionalServices": [
{
"type": "Postgres"
}
]
}The project config json files are located at /etc/shepherd/java/projects/PROJECT_ID.json.
Do not edit the file in-place: copy it to /root/, edit it there, then run ./shepherd-cli update -f /root/file.json then delete it from /root/.
That way, Shepherd can track what has been changed, and can restart the project VM quickly if need be.
That's easy:
- Create the project JSON as above
- Create a Dockerfile as explained below.
- Run
./shepherd-cli create -f file.jsonto create the project. - Done - the project is now being built in Jenkins; when the build succeeds, it will be deployed in Kubernetes.
Shepherd expects the following from your project:
- It must have
Dockerfileat the root of its git repo. - The Docker image can be built via the
docker build -t test/xyz:latest .command; The image can be run viadocker run --rm -ti -p8080:8080 -m256m test/xyzcommand.
Generally, all you need is to place an appropriate Dockerfile to the root of your project's git repository.
See the following projects for examples:
- Gradle+Embedded Jetty packaged as zip: vaadin-boot-example-gradle, vaadin14-boot-example-gradle, karibu-helloworld-application, beverage-buddy-vok, vok-security-demo
- Maven+Embedded Jetty packaged as zip: vaadin-boot-example-maven
- Maven+Spring Boot packaged as executable jar: vaadin-spring-karibu-testing, Liukuri, my-hilla-app, vaadinplus, TextField Formatter Zen Vaadin Hilla Spring PetClinic
Please try building and running the app on your own machine first - debugging any Docker-related issues is far easier on dev machine than remotely on Shepherd.
IMPORTANT: pay attention to the -m256m switch - this sets the hard limit on how much memory
the container may use. If JVM asks for more, it will be hard-killed by the Linux OOM-killer,
without any warning or any log message (only host OS dmesg will log this). Make sure to have your Dockerfile run Java
with the -Xmx???m VM argument; that way the app will crash with OutOfMemoryException which should be visible in the logs.
The -Xmx value should be a bit lower value than the hard limit, to give a bit of room for JVM itself.
Private repositories may need private SSH key to access them. For example:
- A private GitHub repository
foo-repo. A GitHub userfoo-useris created and a private SSH key is generated for him. Thefoo-userthen needs to be invited to the private repository, in order to gain read-only access. Shepherd will have knowledge of the private SSH key. That way, Shepherd can impersonate thefoo-userand can accessfoo-repo.
Every credential has a unique identifier which needs to be passed in via the credentialsID in the config json file.
Before that, the credential needs to be registered:
- Either the Shepherd admin can do that directly, by creating the credential in Jenkins directly,
- or the users can create their own credentials, via the Java functions offered by the
ShepherdClientJava class (TBD)
The credentials are all ultimately stored in Jenkins, at Dashboard / Manage Jenkins / Credentials / System / Global credentials.
Every credential should contain information on which files it contains (e.g. /root/.ssh/id_rsa) and where it is used
(e.g. GitHub user foo-user).
Adding a persistent postgresql database is easy:
- Add the
Postgresadditional service to the project descriptor JSON:"additionalServices": [{"type": "Postgres"}] - Configure your app to connect to the
jdbc:postgresql://postgres:5432/postgresURL, with thepostgresusername andmysecretpasswordpassword.
For Vaadin Pro/Prime components you'll need a Vaadin License. The license must be present during
the build time; adding VAADIN_OFFLINE_KEY to runtime environment variables is not enough.
The license can be obtained at My Licenses. You'll need the "Server license key", NOT the "Offline development license key" since the Machine ID changes unpredictably in CI/CD Docker environment.
Once you have the key, edit the project in v-herd admin console and add the build argument
offlinekey with the value of the license key itself.
Note: the name of the build argument can be anything, but you need to correctly refer to it in your
Dockerfile.
Then, in your Dockerfile, you'll fetch the license key from the build argument into an environment variable:
ARG offlinekey
ENV VAADIN_OFFLINE_KEY=$offlinekeyTo test, build your app via
$ docker build -t test/xyz:latest --build-arg offlinekey=the_license_key .Find the working Dockerfile example in the vaadinplus project.
To speed up your build, you can cache your local Maven repository and the contents of the ~/.vaadin folder.
Edit your Dockerfile and update your Maven build command to mount the cache:
RUN --mount=type=cache,target=/root/.m2 --mount=type=cache,target=/root/.vaadin ./mvnw -C -e clean package -PproductionGradle:
RUN --mount=type=cache,target=/root/.gradle --mount=type=cache,target=/root/.vaadin ./gradlew clean build -Pvaadin.productionMode --no-daemon --info --stacktraceIt's important to keep the host Linux up-to-date, via apt. However,
rebooting while Jenkins is building a project is not safe. To safely restart the host machine,
it's best to shut down Jenkins gracefully first.
- Stop Jenkins, by logging to Shepherd Web, going to
/adminand clicking the "Shutdown" button. Refresh the page until the text "Shutting down" changes to "Shepherd is shut down" - Run
sudo reboot
Jenkins runs as a docker image and is never upgraded via apt. It is never exposed to public:
only Shepherd-Web has access to Jenkins, and therefore it's not important to keep Jenkins up-to-date.
cd /opt/shepherd-traefikdocker compose pulldocker compose up -d --no-deps shepherd
Docker will re-create shepherd container from the newest image, and will automatically join it to the correct 'admin.int' network.
Note that you must not update Traefik this way too, since docker compose will drop all container network bindings, which means that Traefik won't be able to route to apps. TODO Shepherd-Web could update Traefik and reconnect it to all app networks - investigate.
apt upgrade also frequently upgrades Jenkins since Jenkins issues frequent updates.
However, it's not safe to update Jenkins while it's building a project. To update the host system:
- Stop jenkins, via
./shepherd-cli shutdown- the script waits until it's safe to proceed further. sudo apt update && sudo apt dist-upgrade- If Jenkins is updated, it will be restarted and will start taking new jobs. If a reboot is needed:
- Run
./shepherd-cli shutdown - Run
sudo reboot
- Run