How to config spring cloud oauth2 in docker container - spring

I met some problems with micro-spring-docker , i think maybe the sso token-url is not correct.
The demo https://github.com/keryhu/micro-oauth2-docker
In local computer , sso service and auth-service works fine .
But not in docker container ,
the problem is that redirecting to auth-server Timeout .
SSO(pc-gateway service) application.yml:
security:
user:
password: none
oauth2:
client:
accessTokenUri: http://${AUTHSERVER_PORT_9999_TCP_ADDR:localhost}:9999/uaa/oauth/token
userAuthorizationUri: http://${AUTHSERVER_PORT_9999_TCP_ADDR:localhost}:9999/uaa/oauth/authorize
docker-compose.yml
eureka:
image: eureka:0.0.1-SNAPSHOT
container_name: eureka
hostname: eureka
ports:
- "8761:8761"
configserver:
image: config-server:0.0.1-SNAPSHOT
container_name: configserver
hostname: configserver
links:
- eureka
ports:
- "8888:8888"
authserver:
image: auth-server:0.0.1-SNAPSHOT
container_name: authserver
hostname: authserver
links:
- eureka
- configserver
ports:
- "9999:9999"
pcgateway:
image: pc-gateway:0.0.1-SNAPSHOT
container_name: pcgateway
hostname: pcgateway
links:
- eureka
- configserver
- authserver
ports:
- "8080:8080"
After starting in docker container :
http://192.168.99.100:8761/ showing :
Instances currently registered with Eureka
Application AMIs Availability Zones Status
AUTHSERVER n/a(1) (1) UP (1) - authserver:authserver:9999
CONFIGSERVER n/a(1) (1) UP (1) - configserver:configserver:8888
PCGATEWAY n/a(1) (1) UP (1) - pcgateway:pcgateway:8080
But when open the auth page: http://192.168.99.100:8080
It should be redirected to auth-server login page , but it opened Timeout , the Address Bar is:
http://172.17.0.4:9999/uaa/oauth/authorize?client_id=clientapp&redirect_uri=http://192.168.99.100:8080/login&response_type=code&state=cdXhfg
I don't know why , maybe the above sso tokenurl is not correct . How to resolve ?

The 172.17.0.4 IP-address is the IP-address of the authserver container on the internal (container-container) network, which is not reachable from outside the docker host (Virtual Machine).
This may be tricky, because (in this case) you need to provide the IP-address of the Virtual Machine that docker runs on, which may change, and definitely will be different in production.
If you change ${AUTHSERVER_PORT_9999_TCP_ADDR:localhost} to 192.168.99.100, it should work.
I suggest to make the IP-address (or domain) configurable using an environment-variable that you provide in the docker-compose.yml, so something like:
${DOMAIN_NAME:192.168.99.100}
Which defaults to the "standard" IP-address of the Virtual Machine. In production you can then pass the actual domain-name, or IP-address of the server your project runs on.
Note that the "link" environment variables are marked deprecated, and only will
be used on the default (bridge) network. The new linking feature won't create
these variables, but you can simply link to other containers by name. See
https://docs.docker.com/engine/userguide/networking/work-with-networks/#linking-containers-in-user-defined-networks

Related

Register Dockerized microservice to Eureka server - Springboot

In my production environment I have an Eureka Server running inside a docker container.
I can register to it other basic microservices with this kind of Application.yml
Application.yml:
server:
port: '8095'
spring:
application:
name: sap-listener
eureka:
instance:
preferIpAddress: true
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://172.17.0.2:8761/eureka
I create a DockerImage with this Dockerfile:
Dockerfile
FROM openjdk:17-jdk
ARG JAR_FILE=target/*.jar
COPY target/sap-listener-*.jar /sap-listener.jar
ENTRYPOINT ["java", "-jar", "/sap-listener.jar" ]
EXPOSE 8095
and then I run it in production with this command:
docker run -d -p 8095:8095 --name sap myrepo/sap-listener1.0:latest
The service is successfully registered to the Eureka server.
I came across to some problems when I try to run a bigger microservice which have a docker-compose file.
I send directly this docker-compose file in production:
Docker-compose
version: "3.3"
services:
docker-mysql:
image: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_DATABASE: 'db'
ports:
- "3007:3306"
phpmyadmin:
image: phpmyadmin
restart: always
container_name: php-my-admin-users
ports:
- "8081:80"
ldap-app:
image: myRepo/service1:latest
ports:
- "8090:8090"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://docker-mysql:3306/db
depends_on:
- docker-mysql
And I run it with docker-compose -f docker-compose.yml up -d
service1 application.yml have the same type of connection with Eureka server of the previous microservice.
service1 is correctly deployed but It can't register himself to the Eureka server, if I log out the container output I have this error:
2022-06-29 15:45:20.551 INFO 1 --- [ main] c.n.d.s.t.d.RedirectingEurekaHttpClient : Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://172.17.0.2:8761/eureka/}, exception=I/O error on GET request for "http://172.17.0.2:8761/eureka/apps/": Connect to 172.17.0.2:8761 [/172.17.0.2] failed: Connection timed out; nested exception is org.apache.http.conn.ConnectTimeoutException: Connect to 172.17.0.2:8761 [/172.17.0.2] failed: Connection timed out stacktrace=org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://172.17.0.2:8761/eureka/apps/": Connect to 172.17.0.2:8761 [/172.17.0.2] failed: Connection timed out; nested exception is org.apache.http.conn.ConnectTimeoutException: Connect to 172.17.0.2:8761 [/172.17.0.2] failed: Connection timed out
I read that someone directly insert the Eureka Server data as a service inside the Docker-Compose.yml file, but my Eureka Server is already deployed and is already listening to a specific port.
That is probably happening because docker-compose automatically assign a network to the containers.
Try adding network_mode: host to your services in the compose file, like so:
version: "3.3"
services:
docker-mysql:
network_mode: host
image: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_DATABASE: 'db'
ports:
- "3007:3306"
phpmyadmin:
network_mode: host
image: phpmyadmin
restart: always
container_name: php-my-admin-users
ports:
- "8081:80"
ldap-app:
network_mode: host
image: myRepo/service1:latest
ports:
- "8090:8090"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://docker-mysql:3306/db
depends_on:
- docker-mysql
First I would suggest you get familiar with how networking in Docker works and then have a look at networking in Docker-Compose.
When you run docker network ls when your containers are deployed you will see that they are running on different networks, which isolates them. Inspect the networks using docker network inspect <id> and you'll see they have different subnets. So for the services to be able to communicate they need to be on the same network.
You can manually create a network and use it in both compose and the docker cli.

RabbitMQ not working when services are dockerized

So I have 2 services. Kweet (which is a tweet) and User. When I run the 2 services manually + rest of the services in docker, it works. Rest of services include MongoDB, RabbitMQ, Spring Cloud Gateway, Eureka Discovery. But the moment I run the 2 (micro)services in Docker (I just use a docker compose), the rabbitmq functionality stops working. The normal API calls work, it's specifically the RabbitMQ calls that fail.
RabbitMQ functionality:
EditUsername, edits username in user-service.
Than sends data via RabbitMQ (this is where it goes wrong I think) to kweet-service where it edits the username of a kweet.
Docker-compose file:
version: '3.8'
services:
eureka-service:
build: ./eureka-discovery-service
restart: always
container_name: eureka-service
ports:
- 8087:8087
api-gateway:
build: ./api-gateway
restart: always
container_name: api-gateway
depends_on:
- eureka-service
ports:
- 8080:8080
user:
build: ./user
restart: unless-stopped
container_name: user-ms
ports:
- 8081:8081
depends_on:
- eureka-service
kweet:
build: ./kweet
restart: unless-stopped
container_name: kweet-ms
depends_on:
- eureka-service
ports:
- 8082:8082
mongodb:
image: mongo
restart: always
container_name: mongodb
ports:
- 27017:27017
rabbitmq:
image: rabbitmq:management
restart: always
container_name: rabbitmq
hostname: rabbitmq
ports:
- 5672:5672
- 15672:15672
When I try to make a call the console shows:
user-ms | 2022-04-27 08:52:04.823 INFO 1 --- [nio-8081-exec-4] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [localhost:5672]
The postman status I get back is 503 Service Unavailable which isn't from any try-catch's I made. Anybody have any clue where the problem might be?
EDIT[ConnectionFactory]:
I tried to use the documentation and added a the CachingConnectionFactory but it had the same result. Am I doing it wrong?
I added this to the RabbitMQ/Message-config (HOST, USERNAME, PASSWORD come from application.properties:
#Bean
public AmqpTemplate template() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(HOST);
connectionFactory.setUsername(USERNAME);
connectionFactory.setPassword(PASSWORD);
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(converter());
return rabbitTemplate;
}
EDIT [docker-compose]:
Found this source (https://www.linkedin.com/pulse/binding-your-docker-app-container-rabbitmq-phani-bushan/) that got rid of my 503 Service Unavailable error. The problem I found now is that whenever I start up the containers, it generates new queues and exchanges that aren't the ones I set up in my application.properties.
Now whenever I make a call, it shows this log:
user-ms | 2022-04-28 07:36:28.825 INFO 1 --- [nio-8081-exec-1] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#2ca65ce4:0/SimpleCo
nnection#7e7052f [delegate=amqp://guest#172.23.0.4:5672/, localPort= 43208]
Things tried:
change host to [rabbitmq-container-name] in code via CachingConnectionFactory
change host to [rabbitmq-container-name] in docker compose with environment: - spring_rabbitmq_host=[rabbitmq-container-name]
build: ./user
restart: unless-stopped
container_name: user-ms
depends_on:
- eureka-service
- rabbitmq
ports:
- 8081:8081
environment:
- spring_rabbitmq_host=[rabbitmq-container-name]
Instead of [rabbitmq-container-name] I've tried host.docker.internal and localhost
When you dockerize your services are no longer listening in localhost. If you need to network connect services you need to use container_name instead of localhost.
localhost points to the container itself, where only one service is listening. Do not mistake for when you develop on your laptop without containers, where everything is in localhost
More about this here
By default Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.
You must configure, somewhere in your user-ms application (we do not know what kind of applicatin is), that RabbitMQ service is listening at rabbitmq (container_name) not localhost.
Try to use the container with environment variables. For me it is enough for working.
rabbitmq:
image: rabbitmq:management
restart: always
container_name: rabbitmq
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
RABBITMQ_DEFAULT_VHOST: /
ports:
- 5672:5672
- 15672:15672
I can open this container in a browser: http://localhost:15672/
I use PHP FPM and Symfony and I pass env value to this container to connect to rabbitmq.
services:
php-fpm:
environment:
ENQUEUE_DSN: amqp://guest:guest#rabbitmq:5672
For Java you need to find and define or redefine application properties. The config may use the default value for localhost, like this spring.rabbitmq.host=localhost, but you need to use Docker's host, it is rabbitmq.
spring.rabbitmq.host=rabbitmq
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
I have made a grave mistake. 2 things that I did before it started working.
Change Dockerfile ADD command to COPY (Don't think that was the problem though)
Deleted all my images and containers and re-made them a.k.a instead of docker-compose up I should've been typing docker-compose up --build. This was most likely the issue
brb gonna cry in a corner

Api Gateway adding "localhost" to address on docker-compose

I'm trying to deploy SpringBoot microservices using docker-compose but I'm having a problem with API Gateway.
If I run the project locally it works ok, even if I deploy project using docker-compose but API Gateway locally, it works ok, so problem has to be "dockerizing" the API Gateway service.
Doing docker logs <container> it shows:
io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: localhost/127.0.0.1:8083
Is obvious there is a problem on host localhost/127.0.0.1. Why Gateway is trying to point a "repeated" address?.
docker-compose.yml looks like this:
version: '3.8'
services:
# more services
api-gateway:
build: ./path_to_dockerfile
depends_on:
- eureka-server
environment:
- eureka.client.serviceUrl.defaultZone=http://eureka-server:8761/eureka/
restart: always
container_name: gateway
ports:
- '9000:9000'
Dockerfile is as simple as this
FROM openjdk:8-jdk-alpine
ADD target/apigateway-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
And application.yml:
server:
port: 9000
spring:
application:
name: Api-Gateway-Service
cloud:
gateway:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE
globalcors:
# cors config
routes:
- id: <name>-microservice
uri: http://localhost:8083
predicates:
- Path=/<path>/**
- Method=GET,POST,DELETE,PUT,OPTIONS
# more routes on different ports
eureka:
# eureka config
So, why is adding "localhost" or "127.0.0.1" and calling twice?
Thanks in advance.
I don't think Connection refused: localhost/127.0.0.1:8083 means that it was trying to add or call localhost twice. It is just the way it shows the error.
In your application.yml, try changing uri to the name you used for your microservice inside docker-compose file.
routes:
- id: <name>-microservice
uri: <YOUR_SERVICE_NAME>
I guess the problem is that docker doesn't support network communication between containers by default. You can connect to the 8083 port from your host but not another container. If so, you should create a network and contact the container and network.

How do I keep the Eureka server url dynamic in the Eureka Client while using docker?

I am using the fabric8 docker-maven-plugin to build image for my Spring boot microservices.
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
The problem is that while running the application in docker containers I have to specify the Eureka Server Container name to Eureka Client. But if I run it directly as a "Spring Boot APP" I have to use "Localhost:8761/Eureka". Is there a way to make it work both with/without docker something like given below ?
eureka:
client:
service-url:
defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}
I am not able to pass the value of "EUREKA_SERVER" from the fabrib8 plugin. I have tried the below code to pass the value but it does not work.
<docker.env.JAVA_OPTS>-DEUREKA_SERVER=http://discovery:8761/eureka</docker.env.JAVA_OPTS>
Spring can pickup Environment Variables. So if you add Environment Variables to the Docker Container that Spring Boot is running in, they will work. This avoids the need to provide a static URL up front.
If you use Docker Compose, it could look like this:
services:
eureka:
image: springcloud/eureka
container_name: eureka
ports:
- "8761:8761"
networks:
- "discovery"
environment:
- EUREKA_INSTANCE_PREFERIPADDRESS=true
spring:
build:
context: .
dockerfile: ./src/main/docker/Dockerfile
depends_on:
- eureka
container_name: spring
ports:
- "8080:8080"
networks:
- "discovery"
environment:
- EUREKA_SERVICE_URL=http://eureka:8761 // This overrides your Spring Property
- EUREKA_INSTANCE_PREFER_IP_ADDRESS=true
- LOGGING_FILE=/tmp/admin.log
Note: Since Environment Variables are not YAML, you need to change the format a bit. https://docs.spring.io/spring-boot/docs/1.5.5.RELEASE/reference/html/boot-features-external-config.html#boot-features-external-config-relaxed-binding

Netflix Eureka Server and Client instance registration issue with Docker swarm

We are trying to deploy Netflix Eureka Server on Docker container which is a Spring Boot application. We are deploying it as a docker service with Docker swarm for container orchestration.
Everything works fine when we directly create the container and run the application.
Eureka server registers all the client application with correct IP addresses.
But when we create and deploy it as docker service it registers the application with the wrong IP address.
We have tried below solution as per spring documentation, updated property values in our configuration files.
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*
eureka:
instance:
preferIpAddress: true
Tried these solutions as well:
https://github.com/spring-cloud/spring-cloud-netflix/issues/1820
https://github.com/spring-cloud/spring-cloud-netflix/issues/2084
While we run the docker service, the container gets assigned one IP address lets say 172.16.1.3 and service started inside docker gets assigned new IP address 172.16.1.4, because of this issue client application registers its self with container's IP into Eureka server. But it is accessible with 172.16.1.4.
So why running docker as service assigns two IP addresses?
Here Eureka server is running on 10.255.53.172 IP address but as we can see in the second image it shows different IP in Instance Info
Eureka instance is registered with containers IP but it's accessible with its service IP in the network
We use this configuration on production using docker swarm mode:
Eureka server configuration
# Defines the Eureka server that is used by the Netflix OSS components to use as the registry
# for server discovery
eureka:
instance:
hostname: ${HOST_NAME:localhost}
appname: eureka-cluster
# enable to register multiple app instances with a random server port
instance-id: ${spring.application.name}:${random.uuid}
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 20
Eureka client configuration
eureka:
client:
registerWithEureka: ${REGISTER_WITH_EUREKA:true}
fetchRegistry: ${FETCH_REGISTRY:false}
serviceUrl:
defaultZone: ${EUREKA_DEFAULT_ZONE:http://localhost:8761/eureka/}
instance:
hostname: ${HOST_NAME:${spring.application.name}} # work on swarm
preferIpAddress: ${PREFER_IP_ADDRESS:false}
# enable to register multiple app instances with a random server port
instance-id: ${spring.application.name}:${random.uuid}
leaseRenewalIntervalInSeconds: ${LEASE_RENEWAL_INTERVAl_IN_SECONDS:10}
leaseExpirationDurationInSeconds: ${LEASE_EXPIRATION_DURATIONIN_SECONDS:20}
ON swarm service definition
rd-service:
image: my-eureka-server-microservice-image
ports:
- "8763:8763"
networks:
- backend
client-service:
image: my-eureka-client-microservice-image
networks:
- backend
networks:
environment:
- "EUREKA_DEFAULT_ZONE=http://rd-service:8763/eureka"
- "REGISTER_WITH_EUREKA=true"
- "FETCH_REGISTRY=true"
networks:
backend:
external: true
Important: Services must be in the same docker overlay network used on eureka server docker service.

Resources