Turbine can only find one host when using docker - java

I have 3 projects: A hystrix dashboard, a turbine server (using AMQP) and an API
When I start in development env, I set up 2 instances of the API (using port 8080 and 8081). To test the turbine aggregation, I make calls and in the dashboard, I can see Hosts: 2.
Although when I use Docker, even when the load balancer hits the 2 server, I only see one Host on the hystrix dashboard.
My assumptions:
1- as both containers start on the same port (8080), Turbine sees them as one
2- as I also dockerize RabbitMQ, this may be causing problems
here is my docker-compose.yml file
version: '2'
services:
postgres:
image: postgres:9.5
ports:
- "5432"
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: fq
volumes:
- /var/lib/postgresql
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672"
- "15672"
environment:
RABBITMQ_DEFAULT_USER: turbine
RABBITMQ_DEFAULT_PASS: turbine
volumes:
- /var/lib/rabbitmq/
hystrix:
build: hystrixdashboard/.
links:
- turbine_server
ports:
- "8989:8989"
turbine_server:
build: turbine/.
links:
- rabbitmq
ports:
- "8090:8090"
persona_api:
build: persona/.
ports:
- "8080"
links:
- postgres
- rabbitmq
lb:
image: 'dockercloud/haproxy:1.5.1'
links:
- persona_api
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 80:80
my persona_api config file
spring:
application:
name: persona_api
profiles:
active: dev
rabbitmq:
addresses: 127.0.0.1:5672
username: turbine
password: turbine
useSSL: false
server:
compression.enabled: true
port: ${PORT:8080}
params:
datasource:
driverClassName: org.postgresql.Driver
username: postgres
password: postgres
maximumPoolSize: 10
poolName: fq_connection_pool
spring.jpa:
show-sql: true
hibernate:
ddl-auto: update
turbine:
aggregator:
clusterConfig: persona_api
appConfig: persona_api
---
spring:
profiles: dev
params:
datasource:
jdbcUrl: jdbc:postgresql://127.0.0.1:5432/fq
---
spring:
profiles: docker
rabbitmq:
addresses: rabbitmq:5672
params:
datasource:
jdbcUrl: jdbc:postgresql://postgres:5432/fq
I'm afraid that if I deploy it to production (on Rancher or Docker cloud), I'll see the same problem.
here is a GIF of what is happening when I set up two APIs load balanced

try:
hystrix.stream.queue.send-id=false
in your API

I do assume your problem is the RabbitMQ connection you are using. Cause the connection string you are using is localhost but actually except the RabbitMQ container on none the connection will be available on localhost. I do suggest that you inject the Rabbit host into your Spring connection using environment variables. If I read your files correct it should be ${RABBITMQ_PORT_5672_TCP_ADDR} instead of localhost. But be aware that I couldn't try. Its just an educated guess. Better you double check by doing an env inside your persona_api container when everything is running.

It's should be fixed your issue.
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ipAddress}:${server.port} #make the application unique on the rancher service layer
spring:
application:
index: ${random.uuid} #make the application unique on the rancher containe layer,same service but with multi-instances.
https://github.com/spring-cloud/spring-cloud-netflix/issues/740
Need instance-id:${spring.cloud.client.ipAddress}:${server.port} and index: ${random.uuid} both

Related

Error creating a docker-compose connecting a java and a mysql containers

I am trying to connect the container of my springboot application with the container of a mysql image using docker-compose, however when I run docker-compose up my terminal starts a loop where it starts the spring application, try to connect with the MySQL container, fails and keep trying. The error that I get is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failures
docker-compose file:
version: '3.8'
services:
mysqldb:
image: mysql
platform: linux/x86_64
env_file: ./.env
restart: always
environment:
- MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
- MYSQL_DATABASE=$MYSQLDB_DATABASE
ports:
- $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT
volumes:
- db:/var/lib/mysql
app:
depends_on:
- mysqldb
build: .
restart: always
env_file: ./.env
ports:
- $APP_LOCAL_PORT:$APP_DOCKER_PORT
environment:
- DB_HOST=mysqldb
- DB_USER=$MYSQLDB_USER
- DB_PASSWORD=$MYSQLDB_ROOT_PASSWORD
- DB_NAME=$MYSQLDB_DATABASE
- DB_PORT=$MYSQLDB_DOCKER_PORT
stdin_open: true
tty: true
volumes:
db:
.env:
MYSQLDB_USER=root
MYSQLDB_ROOT_PASSWORD=12345678
MYSQLDB_DATABASE=dronefeederdb
MYSQLDB_LOCAL_PORT=3306
MYSQLDB_DOCKER_PORT=3306
APP_LOCAL_PORT=8080
APP_DOCKER_PORT=8080
Application.yaml:
server:
port: 8080
spring:
datasource:
username: ${DB_USER}
password: ${DB_PASSWORD}
url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}
jpa:
hibernate:
ddl-auto: update
show-sql: true
open-in-view: false
#https://ia-tec-development.medium.com/lombok-e-spring-data-jpa-142398897733
security.user:
name: dronefeeder
password: dronefeeder
#https://www.baeldung.com/spring-boot-security-autoconfiguration
resilience4j.circuitbreaker:
configs:
default:
waitDurationInOpenState: 10s
failureRateThreshold: 10
#instances:
#estudantes:
#baseConfig: default
Dockerfile:
FROM openjdk:11.0-jdk as build-image
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests
FROM openjdk:11.0-jre
COPY --from=build-image /app/target/*.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/app.jar"]
Repository link:
https://github.com/julia-baptista/dronefeeder/tree/docker-configuration
I believe the issue is your application's use of localhost for the SQL URL in the Application.yaml property file. Since your app runs on a container by itself it tries to look at localhost of the container, while your SQL server is in another container, with its own localhost. Localhost in docker container do not refer to the host, they refer to the localhost within the container itself. If you want to access the host machine, this is an excellent answer From inside of a Docker container, how do I connect to the localhost of the machine?
url: jdbc:mysql://localhost:3306/dronefeederdb
localhost should not be used, you need to use the sql continainer url.
The fastest option is to use host.docker.internal instead of localhost. But it's not the best.
Another quick option is to run the two containers on the same docker network. Define that in your compose file the same way as the volumes. Then set each container to that network. See Networking in Compose. Then you can set your SQL url to use the SQL container name instead of localhost. So this..
url: jdbc:mysql://localhost:3306/dronefeederdb becomes url: jdbc:mysql://mysql/dronefeederdb
Neither option is robust, since you're hardcoding the container name in the application property file. A better solution is to have an environment variable in your webApp image that can accept the URL to the SQL server. Then you can provide the SQL location when running the container, or in your compose file (Environment variables in Compose). This way the SQL server can be anywhere.
Update:
There were a couple of issues in the compose and env files that caused mySQL container to fail startup. Thus the webApp was not able to connect.
Credentials
MYSQL_USER was set to root. mySql already creates the user root. You cannot create it again. I changed that to foo. See the Environment Variables section in the official docker image readme for more.
MYSQL_PASSWORD was not set. This is the password for the user your app will use. I set this to pass!123
The apps DB_PASSWORD was set to user root. That would have been ok if sql had started and it was using the root user I guess. But I changed that to the non-root user since were setting DB_USER=foo
Network was not defined
The two containers need to be on the same "docker network" if they are to run together in docker in the same machine. There's more to this which is beyond my experience. But in this case it needs to be on the same network for app to access mysqldb by its container name. I created dronefeederNet and added each container to it.
Files:
.env
MYSQLDB_USER=foo
MYSQLDB_PASSWORD=pass!123
MYSQLDB_ROOT_PASSWORD=12345678
MYSQLDB_DATABASE=dronefeederdb
MYSQLDB_LOCAL_PORT=3307
MYSQLDB_DOCKER_PORT=3306
APP_LOCAL_PORT=8081
APP_DOCKER_PORT=8080
docker-compose.yml
version: '3.8'
services:
mysqldb:
image: mysql
platform: linux/x86_64
env_file: ./.env
restart: always
environment:
- MYSQL_USER=$MYSQLDB_USER
- MYSQL_PASSWORD=$MYSQLDB_PASSWORD
- MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
- MYSQL_DATABASE=$MYSQLDB_DATABASE
ports:
- $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT
volumes:
- db:/var/lib/mysql
networks:
- dronefeederNet
app:
depends_on:
- mysqldb
build: .
restart: always
env_file: ./.env
ports:
- $APP_LOCAL_PORT:$APP_DOCKER_PORT
environment:
- DB_HOST=mysqldb
- DB_USER=$MYSQLDB_USER
- DB_PASSWORD=$MYSQLDB_PASSWORD
- DB_NAME=$MYSQLDB_DATABASE
- DB_PORT=$MYSQLDB_DOCKER_PORT
stdin_open: true
tty: true
networks:
- dronefeederNet
volumes:
db:
networks:
dronefeederNet:
Give this a try and I hope it runs. I was able to start it up ok.
You need to add in the app definition block a depends on: sentence, to make docker compose to not boot the application until the database is up.
Check this documentation: Docker Compose Startup Order

Cant connect to database from springboot app: password authentication failed for user "user"

I am trying to connect spring boot app to the postgres on docker but I got org.postgresql.util.PSQLException: FATAL: password authentication failed for user "user"
even if I use correct password(I belive so)
docker-compose.yml in main project dir:
services:
postgres:
container_name: postgres
image: postgres
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
PGDATA: /data/postgres
volumes:
- postgres:/data/postgres
ports:
- "5432:5432"
networks:
- postgres
restart: unless-stopped
pgadmin:
container_name: pgadmin
image: dpage/pgadmin4
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4#pgadmin.org}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin}
PGADMIN_CONFIG_SERVER_MODE: 'False'
volumes:
- pgadmin:/var/lib/pgadmin
ports:
- "5050:80"
networks:
- postgres
restart: unless-stopped
networks:
postgres:
driver: bridge
volumes:
postgres:
pgadmin:
application.yml in springboot app dir:
server:
port: 8080
spring:
application:
name: customer
datasource:
password: password
url: jdbc:postgresql://localhost:5432/customer
username: user
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
pg_hba.conf in docker:
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
# IPv6 local connections:
host all all ::1/128 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all trust
host replication all 127.0.0.1/32 trust
host replication all ::1/128 trust
Steps I've taken to create database:
Docker compose up -d
starting containers
go to http://localhost:5050/browser/# and enter master password
creating server
creating database
and when I try to run spring boot app I got
org.postgresql.util.PSQLException: FATAL: password authentication failed for user "user"
I tried to restart images and delete them. Please help to find the bug in my configuration.
If you add the following to you docker-compose.yml, it will probably work.
POSTGRES_DB: customer
You did no create the "schema", and that's why you are getting the error.
The postgres image configuration with the fix:
postgres:
container_name: postgres
image: postgres
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
PGDATA: /data/postgres
POSTGRES_DB: customer
volumes:
- postgres:/data/postgres
ports:
- "5432:5432"
networks:
- postgres
restart: unless-stopped

How to use spring.application.name in RestTemplate url on a docker container?

I have two spring boot projects - album-service on port 8081 and song-service on port 8082 .
song-service has an entity class Song with the field albumId, which happens to be the primary key of the entity class Album in album-service.
I have a particular line in my service class SongService which returns an Album object by using RestTemplate:
Album album = restTemplate.getForObject("http://localhost:8082/find/"+song.getAlbumId(), Album.class);
Now, I thought to use application name instead of using localhost:xxxx on RestTemplate.
So in application.yml of album-service I wrote this line:
spring:
application:
name: ALBUM-SERVICE
and replaced the RestTemplate line with:
Album album = restTemplate.getForObject("http://ALBUM-SERVICE/find/"+song.getAlbumId(), Album.class);
This also seems to work just fine (when paired with a Eureka server and having album-service and song-service as Eureka clients), when both projects are running.
HOWEVER, issues come when these projects are on separate docker containers. When I run the API which uses the above line containing RestTemplate, I get an exception:
2021-05-03 11:14:51.588 ERROR 1 --- [nio-8082-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://ALBUM-SERVICE/albums/find/1": Connection refused; nested exception is java.net.ConnectException: Connection refused] with root cause
Here are the config files for everything:
application.yml of album-service:
server:
port: 8081
spring:
data:
mongodb:
host: mongodb-container
port: 27017
database: music
application:
name: ALBUM-SERVICE
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://service-registry-container:8761/eureka/
instance:
hostname: service-registry-container
application.yml of song-service:
server:
port: 8082
spring:
data:
mongodb:
host: mongodb-container
port: 27017
database: music
application:
name: SONG-SERVICE
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://service-registry-container:8761/eureka/
instance:
hostname: service-registry-container
application.yml of service-registry (Eureka server):
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
docker-compose.yml
version: "3"
services:
service-registry-container:
image: service-registry
ports:
- 8761:8761
networks:
- ms-music
album-service-container:
image: album-service
ports:
- 8081:8081
networks:
- ms-music
song-service-container:
image: song-service
ports:
- 8082:8082
networks:
- ms-music
mongodb-container:
image: arm64v8/mongo
ports:
- 27017:27017
volumes:
- ~/mongo/data:/data/db
networks:
- ms-music
networks:
ms-music:
Any tips on how I can replicate the local behavior on a docker container?
I can think of two options:
Use the container name as host
Since all your containers are within the same docker network, using a container's name INSIDE any given container will resolve to the correct container (thats a lot of containers lol). In your case the hostname would be song-service-container if you want to resolve to your songService.
Use host.docker.internal
If all the containers run on the same machine, you can try to put host.docker.internal as the host. It will resolve to your local machine, and sincethe ports of the containers are forwarded to your machine's, that should work too.

Cloud config got Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://localhost:8761/} from Eureka server

I'm trying to implement Discovery First Bootstrap way for my 4 micro-services. First is a config server that takes config from git, and second is an Eureka server. When i'm running docker-compose up my config server is not able to register with eureka. I've got:
Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://localhost:8761/} Connection refused
My config server
#EnableConfigServer
#EnableEurekaClient
#SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
application.yml
server:
port: 8888
spring:
cloud:
config:
server:
encrypt.enabled: false
git:
uri: https://github.com/Artuwok/artbook-config-repo/
searchPaths: userservice
username: Artuwok
password: Civilization1986
timeout: 10
force-pull: true
bootstrap.yml
spring:
application:
name: configserver
cloud:
config:
fail-fast: true
discovery.enabled: true
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl.defaultZone: http://localhost:8761/eureka/
Eureca class
#SpringBootApplication
#EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
Eureka bootstrap.yml
server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false
docker-compose.yml with full config
version: '3'
services:
configserver:
image: configserver:1.0
ports:
- "8888:8888"
depends_on:
- eurekaserver
restart: on-failure
eurekaserver:
image: eurekasvr:1.0
ports:
- "8761:8761"
users_db:
image: mysql:5.7
container_name: users_db
restart: always
environment:
- MYSQL_DATABASE=artbook
- MYSQL_ROOT_PASSWORD=password
- MYSQL_USER=user
- MYSQL_PASSWORD=password
volumes:
- users_db:/var/lib/mysql
ports:
- "3306:3306"
depends_on:
- eurekaserver
user-service:
image: userservice:1.0
ports:
- "8080:8080"
depends_on:
- eurekaserver
- configserver
restart: on-failure
volumes:
users_db:
So in the end i was able to start-up the services. Pay huge attention to URL on which you are discovering service. If you are using docker images you must use service name, not localhost in uri and urls's. My working config
spring:
application:
name: configserver
cloud:
config:
fail-fast: false
discovery.enabled: false
uri: http://configserver:8888
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl.defaultZone: http://eurekaserver:8761/eureka
You config server and Eureka server both run in Docker. So they can connect with each other using their names in Docker, not localhost.
So Eureka service URL should be: http://eurekaserver:8761/.
And you should add the code below to your configserver configuration in docker-compose.yml:
links:
- eurekaserver
add following in your application,properties file:
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
This makes your eureka server up on default port 8080, also these properties helps you to not register eurekaServer to itself. (EurekaServer is itself a client too.)
If you want to run this server on port 8761, add following in your application.properties file:
server.port=8761
PS- It helps when you are not using docker.
I was not using docker, but I was getting the same error, I searched a lot throughout the web but didn't find anything.
Then I just stop the server and updated my maven dependencies once and then tried to start it service discovery. It worked!!
I hope this will help someone, who is not using and docker and getting the same.
P.S.- Also make sure to have these configurations in application.properties or application.yml
server.port=8761
spring.application.name=eureka-discovery
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone: http://localhost:8761/eureka

Externalize application.properties to docker env

I'm developping an application using spring-boot and Docker. For security reasons I want to not use any more application.properties and use only environnement variable.
If you have best practices I will be grateful.
This is a snipet of my docker-compose.yml
version: "2.1"
services:
app_users:
image: images/app_users
container_name: app_user_ctn
build:
context: ../..
dockerfile: docker/dev/Dockerfile
ports:
- "30333:8080"
external_links:
- mysql
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql/myDB?autoReconnect=true
SPRING_DATASOURCE_USERNAME: mysqluser1
SPRING_DATASOURCE_PASSWORD: mysqlpwsword
SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver
LDAP_PASSWORD: ldapPswd
LDAP_URLS: ldap://myServer:389
LDAP_USERNAME: cn=admin,dc=com,dc=expl
When I make a request to ldap I get NulPointerException because ldap environnement variables are not initialize.
When I use application.yml it works.
...
spring:
ldap:
password: ldapPswd
urls: ldap://myServer:389
username: cn=admin,dc=com,dc=expl
....
Would you have any ideas ?
Best regards

Categories

Resources