26 Aug 2019 | tags: Kubernetes Gcloud
Rationale TL;DR
Imagine that you have multiple Google cloud accounts and you want to switch among them easily. You would expect kubectl
to work seamlessly. This is usually not the case.
Needed dependencies
To get started you would need to have installed gcloud
and optionally kubectl
.
To get started you need an account configured. To list the accounts:
gcloud config configurations list
If you want to configure a new account:
This will ask you some questions and finally open the browser to finish the process. Once you have that, you would be able to see the new config:
NAME IS_ACTIVE ACCOUNT PROJECT DEFAULT_ZONE DEFAULT_REGION
default True gustau.perez@lalala.com default europe-west1-b europe-west1
gusi False gustau.perez@gmail.com gusitest europe-west3-c europe-west3
Now, to switch from the default configuration to the gusi configuration you would need to first activate the gusi config. To do so:
gcloud config configurations activate gusi
Now, you would expect kubectl
and other commands to work out of the box. That is not the case. You would be able to list the clusters in the account with gcloud container clusters list
:
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
gusicluster2 europe-west3-b 1.13.7-gke.19 XX.XX.XX.XX g1-small 1.13.7-gke.19 3 RUNNING
However, kubectl
won’t still work with the new cluster. You need to fetch the cluster credentials:
$ ▶ gcloud container clusters get-credentials --zone europe-west3-c gusi
Fetching cluster endpoint and auth data.
kubeconfig entry generated for gusi.
Now you would be able to list the nodes of the gusi cluster:
$ ▶ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-gusicluster2-default-pool-06044af2-djcv Ready <none> 3d18h v1.13.7-gke.19
gke-gusicluster2-default-pool-06044af2-fdb4 Ready <none> 3d18h v1.13.7-gke.19
gke-gusicluster2-default-pool-06044af2-tf8j Ready <none> 3d18h v1.13.7-gke.19
02 Jul 2019 | tags: Spring Boot Java Actuator
Rationale TL;DR
Spring Boot Actuator is a artifact that provides the ability to monitoring our app, gathering metrics, understanding traffic or the state of our database.
Needed dependencies
To pull in Spring Boot actuator, we simply need to add the spring-boot-starter-actuator dependency. If we are using maven:
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
...
</dependencies>
Endpoints published
Actuator provides a set of endpoints to gather info from or even interact with the application. Some of them are sensitive, which means that they are not fully public and thus they will omitt some information.
We can set which endpoints publish. We can tell actuator not to publish any endpoint but a list of endpoints:
endpoints.enabled: false
endpoints.metrics.enabled: true
endpoints.metrics.sensitive: false
endpoints.prometheus.enabled: true
endpoints.prometheus.sensitive: false
endpoints.health.enabled: true
endpoints.health.sensitive: false
In this example, only metrics, prometheus and health are published by actuator.
To provide security to Actuator 1.X we can use the following on our application.properties file:
management.security.enabled=true
Bear in mind that Actuator 1.X will apply its own security model. So that we need to tell Spring Boot Security to not apply its rules to all endpoints:
security.basic.enabled: false
Also, we can configure a user and actuator will pick it up and use it to grant access to its endpoints:
security:
user:
name: admin
password: admin
That way, we can use basic authentication with the actuator endpoints but the app endpoints will not use Spring Boot Security:
─ $ ▶ curl -XGET -vv -u 'admin:admin' http://localhost:8080/app/metrics
Note: Unnecessary use of -X or --request, GET is already inferred.
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'admin'
> GET /app/metrics HTTP/1.1
> Host: localhost:8080
> Authorization: Basic Z3Vlc3RtYXRlLWFwcDpndWVzdG1hdGUtYXBw
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200
< X-Application-Context: app:local
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< Content-Type: application/vnd.spring-boot.actuator.v1+json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 02 Jul 2019 06:32:52 GMT
<
* Connection #0 to host localhost left intact
{"mem":1649575,"mem.free":1248587,"processors":4,"instance.uptime":200403,"uptime":288597,"systemload.average":2.73828125,"heap.committed":1535488,"heap.init":262144,"heap.used":286900,"heap":3728384,"nonheap.committed":118784,"nonheap.init":2496,"nonheap.used":114087,"nonheap":0,"threads.peak":47,"threads.daemon":41,"threads.totalStarted":57,"threads":44,"classes":13275,"classes.loaded":13275,"classes.unloaded":0,"gc.ps_scavenge.count":14,"gc.ps_scavenge.time":532,"gc.ps_marksweep.count":3,"gc.ps_marksweep.time":506,"datasource.primary.active":0,"datasource.primary.usage":0.0,"gauge.response.health":17.0,"gauge.response.metrics":24.0,"gauge.response.prometheus":39.0,"gauge.response.star-star":20.0,"counter.status.200.metrics":1,"counter.status.401.health":1,"counter.status.404.star-star":4,"counter.status.503.health":3,"counter.status.200.prometheus":1
We can also use a different port to expose the actuator. This only works if we’re using a embedded webserver, using a standalone application server will not work because the app will not be able to bind to a different port than the ports configured by the application server:
For Actuator 2.X, there are a lot of changes. Will dig into it in a future post.
01 Jul 2019 | tags: Spring Boot RabbitMQ Javas
Rationale TL;DR
In a microservices world, avoiding coupling is always a good practice. Using a message broker might help avoiding coupling between microservices because:
- One service does not need to know who is going to handle a task
- That service shall not need to wait until the task is done
If that is feasible, then using a message broker can be of a great help.
Let’s assume we know the basic AMQP concepts, like the queue, exchange and binding concepts.
Needed dependencies
To pull in Spring Boot AMQP, we simply need to add the spring-boot-starter-amqp dependency. If we are using maven:
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
...
</dependencies>
RabbitMQ and Spring Boot. Configuring a publisher.
With Spring Boot, we can easily create a publisher. To do so, we need to declare some beans. If we want to use a direct exchange, we need four beans:
- A Queue bean
- A DirectExchange bean
- A Binding bean
- And a RabbitTemplate bean
Bear in mind that we don’t need to give them specific names if we’re only using one bean of each type. Otherwise, we need to give them proper different names so that we can properly use them:
@Configuration
public class RabbitProducerMailConfig {
@Value("${rabbit.mail.queue.name}")
private String queueName;
@Value("${rabbit.mail.exchange}")
private String exchange;
@Value("${rabbit.mail.routingkey}")
private String routingKey;
@Bean
Queue queueMail() {
return new Queue(queueName, true);
}
@Bean
DirectExchange exchangeMail() {
return new DirectExchange(exchange);
}
@Bean
Binding bindingMail(Queue queueMail, DirectExchange exchangeMail) {
return BindingBuilder.bind(queueMail).to(exchangeMail).with(routingKey);
}
@Bean
public RabbitTemplate rabbitProducerTemplateMail(final ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
return rabbitTemplate;
}
@Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
Here, I’m declaring a queue bean called queueMail, a direct exchange called exchangeMail and then a binding called bindingMail. When injecting the bindingMail bean, I need to use the queue and exchange beans by name, note that I’m using the names I previosly gave them. This is because I have other beans of the same type, if I don’t use names Spring Boot will get confused.
I usually declare the needed beans on a specific class and then I declare another class that would be injected and used to publish messages.
The important stuff is the RabbitTemplate bean, that bean will be used to publish messages on the exchange.
RabbitMQ and Spring Boot. Registering a publisher.
Now, let’s create another class that will be used to publish messages on the exchange. That class will be injectable, so we need to use the @Service annotation. We need also to autowire the RabbitTemplate bean we declared previouly:
@Service
public class MailQueueProduder {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MailQueueProduder.class);
@Autowired
@Qualifier("rabbitProducerTemplateMail")
private RabbitTemplate rabbitTemplate;
@Value("${rabbit.mail.routingkey}")
private String routingKey;
@Value("${rabbit.mail.exchange}")
private String exchange;
public void publishMessageInQueue(MailForQueue mailForQueue) {
log.info("[MailQueueProduder.publishMessageInQueue] Publishing mail to {}", rabbitTemplate.getExchange());
this.rabbitTemplate.convertAndSend(exchange, routingKey, mailForQueue);
}
That was easy. We just needed to autowire the RabbitTemplate bean using the @Qualifier annotation. We could just use the right bean name, however I wanted to just see if that would also work, using the right name on the same class is easy, but in a different class makes things harder to read, that’s why I think the @Qualifier here makes sense, it makes it easier for me to understand that there’s a bean declared elsewhere.
RabbitMQ and Spring Boot. Using the publisher.
Now it’s just a matter of autowiring the MailQueueProduder and call its publishMessageInQueue method. Waayy!! The convertAndSend will serialize the MailForQueue instance into json using the Jackson2JsonMessageConverter bean and publish it in the DirectExchange exchange.