Running a 4 node Kubernetes Cluster on Raspberry Pi

What I wanted to achieve: Using 4 of my Raspis for Kubernetes: Part 1 Learn how to deploy a Spring Boot application: Part 2 Expose this application to the internet: Part 3 Part 1: Setting up the cluster For my setup I use 1 Raspi 4 and 3 Raspi 3B. The Raspi 4 serves as master node, the others as client nodes. First I flashed HypriotOS on all 4 Raspis. Before commencing don’t forget to: ...

March 4, 2021 · 1 min · Jens

Testing Spring Boot Configuration Classes

The class to test @Configuration @ConfigurationProperties(prefix = "scheduler") @Data public class SchedulerConfig { private String rate; private final Activetime activetime = new Activetime(); @Data public static class Activetime { private int starts; private int ends; } } The unit test class SchedulerConfigTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(); @EnableConfigurationProperties(SchedulerConfig.class) static class DummyConfigurationProps { } @Test void shouldConfigureScheduler() { this.contextRunner .withUserConfiguration(DummyConfigurationProps.class) .withPropertyValues("scheduler.rate=42","scheduler.activetime.starts=6","scheduler.activetime.ends=22") .run(context -> { SchedulerConfig schedulerConfig = context.getBean(SchedulerConfig.class); assertThat(schedulerConfig).isNotNull(); assertThat(schedulerConfig.getRate()).isEqualTo("42"); assertThat(schedulerConfig.getActivetime().getStarts()).isEqualTo(6); assertThat(schedulerConfig.getActivetime().getEnds()).isEqualTo(22); }); } }

March 4, 2021 · 1 min · Jens

Deploying To Minishift With Helm

What I wanted to achieve was: Pull a Spring Boot demo-app image from Dockerhub. Install that image to Minishift by using Helm. Step 1: Pull image from Dockerhub docker pull kharulp12/spring_hello_rest_api Step 2: Export Minishifts registry url to a variable for easy re-use export registry=$(minishift openshift registry) Step 3: Tag the image to be used by Minishifts internal image registry. Note the tags version, it is referenced in Helm chart later! ...

February 7, 2021 · 1 min · Jens

Removing A Persistent Volume Claim

I fiddled with persistent volume claims (pvc) on OpenShift. Creating a pvc was no problem, but afterwards I tried to delete it but it was stuck in “Terminating”-state. Here’s what I did to remove it: # Login to OpenShift, this can be obtained in web console with 'Copy Login Command' $ oc login --token=41cxWS0NnARW2zxRCK5p2GQb31VNf7zEz-wuYMdhw1k --server=https://openshift.cluster.host:6443 # Create a pvc $ oc set volume dc/testpvc --add --type pvc --claim-size=100Mi info: Generated volume name: volume-s9njq deploymentconfig.apps.openshift.io/testpvc volume updated # Check the status $ oc get pvc -w NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-gpvft Bound pvc-86cc776c-4190-4b76-bc27-5a8846c71fd8 1Gi RWO gp2 15s # Try to delete it... $ oc delete pvc/pvc-gpvft persistentvolumeclaim "pvc-gpvft" deleted # Check status...it's stuck in 'Terminating' $ oc get pvc -w NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-gpvft Terminating pvc-86cc776c-4190-4b76-bc27-5a8846c71fd8 1Gi RWO gp2 8m29s # Check deployment...the finalizer is the interesting part $ oc get pvc pvc-gpvft -o yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: (...) finalizers: - kubernetes.io/pvc-protection (...) spec: accessModes: - ReadWriteOnce resources: requests: storage: 100Mi storageClassName: gp2 volumeMode: Filesystem volumeName: pvc-86cc776c-4190-4b76-bc27-5a8846c71fd8 status: accessModes: - ReadWriteOnce capacity: storage: 1Gi phase: Bound # Patch the finalizer $ oc patch pvc pvc-gpvft -p '{"metadata":{"finalizers": []}}' --type=merge persistentvolumeclaim/pvc-gpvft patched # Check again...aaaand it's gone $ oc get pvc No resources found in test-space namespace.

September 2, 2020 · 2 min · Jens

Deploying Spring Boot Application To OpenShift With Maven

<!-- Retrofit pom with JKube-Plugin --> <build> ... <plugin> <groupId>org.eclipse.jkube</groupId> <artifactId>openshift-maven-plugin</artifactId> <version>${jkube.openshift.version}</version> </plugin> ... </build> # Login to OpenShift, this can be obtained in web console with 'Copy Login Command' $ oc login --token=41cxWS0NnARW2zxRCK5p2GQb31VNf7zEz-wuYMdhw1k --server=https://openshift.cluster.host:6443 # Build and deploy to OpenShift $ mvn oc:build oc:resource oc:apply # Watch the deployment $ oc get pods -w # Find out route $ oc get routes # Undeploy everything $ mvn oc:undeploy # As an alternative, remove everything related to this deployment $ oc delete all --selector app=myapplabel

August 18, 2020 · 1 min · Jens

Scheduled Kafka Consumer With Spring Boot

@Component @Slf4j public class ScheduledConsumer { private final BusinessService businessService; private final KafkaListenerEndpointRegistry registry; @Autowired public ScheduledConsumer(final BusinessService businessService, KafkaListenerEndpointRegistry registry) { this.businessService = businessService; this.registry = registry; } @Scheduled(cron = "${cron-expression.start}") public void resumeConsuming() { this.registry.getListenerContainers().forEach(MessageListenerContainer::resume); log.info("Resume consuming business objects..."); } @Scheduled(cron = "${cron-expression.stop}") public void pauseConsuming() { this.registry.getListenerContainers().forEach(MessageListenerContainer::pause); log.info("Pause consuming business objects..."); } @KafkaListener(id = "mycontainer", topics = "${topic}", autoStartup = "${consumer.autostart}") public void consume(final ConsumerRecord<String, BusinessObject> businessRecord, final Acknowledgment acknowledgment) { log.info("Processing business object..."); this.businessService.process(businessRecord.value()); acknowledgment.acknowledge(); } }

August 13, 2020 · 1 min · Jens

Git Rebasing Cheat Sheet

# create branch from develop git pull git checkout -b feature/my-feature # work on branch and commit work. repeat until work is done git commit -am "implement my feature" git push # squash commits with an interactive rebase # first, choose all commits to squash into one git log --oneline # second, squash the last x commits # pick the one to squash into (usually the topmost), squash # the others git rebase -i HEAD~x # forcepush these changes to the remote branch git push --force # refresh work done by others on develop in the meantime... git checkout develop git pull # change back to feature branch and rebase git checkout feature/my-feature git rebase develop # optional: resolve conflicts and then git add . git rebase --continue # since we rewrote history, force push changes to branch git push --force-with-lease # after that, branch can be merged into develop

April 19, 2020 · 1 min · Jens

Setting Timezone with Maven Jib-Plugin in Docker Image

I run a dockerized Spring Boot Application on a Raspberry Pi Zero (yes…that’s possible). It records the current temperatures to a MariaDB (which is running on another RPi). At some point I noticed that the time stamps in the database had a time difference of exactly minus two hours. However, the docker host had the correct time zone (CEST, Europe/Berlin). The running Docker Container had the UTC timezone, though: $ docker exec 9106cb56b3f6 date Thu Apr 4 20:00:00 UTC 2020 ...

April 9, 2020 · 1 min · Jens

Spring Boot Single Sign On with KeyCloak...How to log out?

import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Component public class KeycloakLogoutHandler implements LogoutHandler { private final RestTemplate restTemplate = new RestTemplate(); @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { OidcUser user = (OidcUser) authentication.getPrincipal(); String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout"; UriComponentsBuilder builder = UriComponentsBuilder .fromUriString(endSessionEndpoint) .queryParam("id_token_hint", user.getIdToken().getTokenValue()); ResponseEntity<String> logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class); if (!logoutResponse.getStatusCode().is2xxSuccessful()) { log.error("Unable to logout user"); } } }

April 3, 2020 · 1 min · Jens

Spring Boot Static Resources Cheat Sheet

Include Webjars-Locator in pom.xml: <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> </dependency> Don’t extend WebMvcConfigurerAdapter Don’t use @EnableWebMvc No leading slash for JS/CSS links with Thymeleaf: <script th:src="@{webjars/jquery/jquery.min.js}" type="text/javascript" ></script> Omit version number in JS/CSS links

April 2, 2020 · 1 min · Jens