How to call one microservice from another microservice using docker images - java

I have two SpringBoot microservices M1(port 2002) and M2(port 2004)
M1 and M2 are communicating successfully if I run them using eclipse (run as Java Project or SpringBoot Project).
However, I want to communicate them using Docker container.
So I build images for both Microservices (M1 and M2) using the command:
docker build -f Dockerfile -t image_name .
And run the images using:
docker run -p 2004:2004 image_name
Note: I am exposing same port from docker as defined above
But the M1 and M2 are not able to communicate.
I am using RestTemplate
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Boolean> isUp = restTemplate.getForEntity("http://localhost:2002/apis/test",Boolean.class);
I am getting below exception :
I/O error on GET request for \"http://localhost:2002/apis/test\": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)
However, If I call the other microservice using my machine's IP, It's communicating successfully
ResponseEntity<Boolean> isUp = restTemplate.getForEntity("http://XX.XX.XX.XXX:2002/apis/test",Boolean.class);
Can someone please tell if I am doing it write(using IP address) or there is another good approach to call one microservice from another using Docker?

Trying to communicate with the other container won't work with localhost.
You should create a custom bridged network, which will allow you to refer to the containers by name. And there is no need to publish the ports if you are only talking internally.
# create network
docker network create -d bridge mynet
# container 1
docker container run --network mynet --name container1 -d image_name
# container 2
docker container run --network mynet --name container2 -d some_other_image_name
The IP in code snippet can then be replaced with the name of the other container
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Boolean> isUp = restTemplate.getForEntity("http://container2:2002/apis/test",Boolean.class)

Alternately, you can also link the two containers together by --link. Assuming you want container1 as client to container2, you can use below:
sudo docker run --link container2 --name=container1 -d image_name

Related

ECS EC2 Instance unable to make HTTP requests

Hi I am using ECS to run Docker tasks in EC2 instances. The tasks are run in Java and are looking to write to a SQS. The problem is my EC2 instance does not seem to be able to make any HTTP requests - this is what the Java error is when trying to write to that SQS Queue:
Exception in thread "main" com.amazonaws.SdkClientException: Unable to execute HTTP request. failed: connect timed out
I have tested command line commands such as curl and the connection also times out in the task but works fine if I connect and run it on the EC2 instance the task is being run on.
The same code via ECS runs fine in Fargate.
My EC2 instances are in public subnets, have a public IP assigned to them and the security group that is assigned to these instance have the following rules Inbound and Outbound:
Does anybody have any ideas as to why the tasks in these EC2 instances always timeout on HTTP requests thanks.
Edit(Adding Dockerfile) -
DockerFile:
FROM alpine/git as clone
WORKDIR /app
RUN git clone https://GITLINK.git
FROM maven:3.5-jdk-8-alpine as build
WORKDIR /app
COPY --from=clone /app/aws_poll /app
RUN mvn package
FROM openjdk:8-jre-alpine
WORKDIR /app
COPY --from=build /app/target/aws-examples-1.0.jar /app
EXPOSE 8080
EXPOSE 443
EXPOSE 80
ENTRYPOINT ["sh", "-c"]
CMD ["java -jar aws-sqs-examples-1.0.jar"]
HEALTHCHECK --interval=25s --timeout=3s --retries=2 CMD ["java", "HealthCheck", "||", "echo", "HEALTH CHECK:FAILED"]
The EC2 Instance role has to be added as a resource that can use the SQS queue, in other words you need to modify the SQS Policy and add the EC2 instance role in the resource ARN, also make sure the instance role is allowed to talk to SQS by adding the relevant policy under IAM service.

Can't read data from Nifi output port using Flink-nifi connector

I have an instance of Nifi in docker on virtual machine with exposed ports: 8080 and 10000.
On thin instance i created a simple pipeline with output port named "flink" and i want to read this data using flink-nifi connector:
SiteToSiteClientConfig clientConfig = new SiteToSiteClient.Builder()
.url("http://vm-address:8080/nifi")
.portName("flink")
.requestBatchCount(100)
.buildConfig();
DataStream<NiFiDataPacket> nifi = environment.addSource(new NiFiSource(clientConfig));
nifi.map(new MapFunction<NiFiDataPacket, JsonNode>() {
#Override
public JsonNode map(NiFiDataPacket value) throws Exception {
return DataConverter.byte2Json(value.getContent());
}
}).print();
In this case i got error:
Exception in thread "main" org.apache.flink.runtime.client.JobExecutionException: java.net.UnknownHostException
If i add localAddress in config:
SiteToSiteClientConfig clientConfig = new SiteToSiteClient.Builder()
.url("http://vm-address:8080/nifi")
.localAddress(InetAddress.getByName("vm-address"))
.portName("flink")
.requestBatchCount(100)
.buildConfig();
I got this error:
Exception in thread "main" org.apache.flink.runtime.client.JobExecutionException: java.net.BindException: Cannot assign requested address: JVM_Bind
I run this code from local pc on windows and flink is started in standalone mode.
Also, i tried to run this directly on virtual machine but i got the same error.
In logs there is a lot of retries:
execchain.RetryExec: I/O exception (java.net.BindException) caught
when processing request to
/vm-address->{}->http://vm-address:8080: Cannot assign requested
address: JVM_Bind
Finally, solved it!
The problem was in my docker configuration. Firstly, i run nifi like this:
docker run --name nifi -p 8008:8080 -p 10000:10000 -d apache/nifi:1.7.1
The network, by default, was bridge. In this case my container has some random hostname and i don't communicate with container directly, but through the docker.
When i choose network=host:
docker run --name nifi --network host -d apache/nifi:1.7.1 everything goes well.
Probably, i could solve it another way (maybe, explicitly resolve container hostname), but this was the easiest way

Testcontainers with a company proxy

Each start of different testcontainers will throw com.github.dockerjava.api.exception.InternalServerErrorException: {"message":"Get https://quay.io/v1/_ping: dial tcp x.x.x.x: getsockopt: connection refused"}
This is no surprise (docker is behind a company proxy). How can I configure testcontainers to use a specific HTTP proxy?
Another approach could be disabling the "ping" command and using our company docker repo.
You can by specifying env variables when you are building an image or running a container. For example, below I'm building an Elasticsearch container by passing proxy configuration:
GenericContainer container = new GenericContainer("docker.elastic.co/elasticsearch/elasticsearch:6.1.1")
.withExposedPorts(9200)
.withEnv("discovery.type", "single-node")
.withEnv("HTTP_PROXY", "http://127.0.0.1:3001")
.withEnv("HTTPS_PROXY", "http://127.0.0.1:3001")
.waitingFor(Wait.forHttp("/_cat/health?v&pretty")
.forStatusCode(200));
Otherwise, you can set your proxy settings globally in docker. For windows with a docker machine you have to connect to it and the HTTP proxy in boot2docker profile.
docker-machine ssh default
sudo -s
echo "export HTTP_PROXY=http://your.proxy" >> /var/lib/boot2docker/profile
echo "export HTTPS_PROXY=http://your.proxy" >> /var/lib/boot2docker/profile
On Linux, you can create a file ~/.docker/config.json like :
{
"proxies":
{
"default":
{
"httpProxy": "http://127.0.0.1:3001",
"noProxy": "*.test.example.com,.example2.com"
}
}
}

Connecting an Oracle DB Container and a Java application Container (Docker)

I am now working on a docker project with two docker containers - one for the oracle db and the other with a java application.
The container for oracle db is working ok. I used the already built image for oracle and created my tablespaces and users in it.
Commands I used to pull and use the oracle db container is as given below:
docker pull wnameless/oracle-xe-11g
docker run -d -p 49160:22 -p 49161:1521 -e ORACLE_ALLOW_REMOTE=true wnameless/oracle-xe-11g
Now I have my own Java application that interacts with the oracle db and I run it using the command given below:
docker run -it --name mypgm myrepo/oracletesting
It runs an interactive java program that asks for the Oracle DB details and allows users to interact with the DB.
However I could not figure out how I have to specify details such as
Driver Name, Connection URL, Username, and Password
The values I gave are as given below:
Driver Name: oracle.jdbc.OracleDriver
Connection URL:jdbc:oracle:thin:#localhost:1521:orcl11g
Username: imtheuser
Password: **********
I dont know whats going wrong where but its not working.
I tried giving different inputs for Connection URL after inspecting the docker container ip address as well:
Connection URL: jdbc:oracle:thin:#172.17.0.2:1521:orcl11g
Am I giving the Connection URL and/or the port number correct? Can someone help me out to correctly connect these two containers and to get the project moving?
Thanks for your kind help..
You have to link the containers.
The oracle container should have a name.
try the following:
docker network create my-network # Create a network for containers
docker run -d -p 49160:22 -p 49161:1521 --network my-network --name oracle-db -e ORACLE_ALLOW_REMOTE=true wnameless/oracle-xe-11g
docker run -it --network my-network --name mypgm myrepo/oracletesting
Use as connection url to following string jdbc:oracle:thin:#oracle-db:1521:orcl11g
You can use a domain name in oracle connection string: oracle.dbhost.com, and use a --addhost oracle.dbhost.com:[ip address] when running your app in docker, or configure a dns to resolve the domain name.

Docker: Linking Spring Boot container with Mongo DB container

How can I link my Spring Boot application container with MongoDB container?
Spring Boot app is using MongoDBRespository which by default connects to localhost:27017.
You should use container linking. From the docs:
When you set up a link, you create a conduit between a source container and a recipient container. The recipient can then access select data about the source
When two containers are linked, Docker will set some environment variables in the target container to enable programmatic discovery of information related to the source container.
Basically what this means is the following
your MongoDB-container should expose some ports (either via the EXPOSE entry in the Dockerfile or via the -p option to docker run).
your Spring Boot-container should be started with the --link option that points to the MongoDB-container.
The MongoDBRepository should be configured to use the address that is provided in the environment variables by the linking.
Check out this article on how to link containers for more info.
#Himanshu Yadav , you can try this resource. it did solve my problem. :-) It has got a full tutorial in that regard
https://www.jiwhiz.com/blogs/Spring_Boot_Docker_MySQL_Demo
Quote
docker run -p 8080:8080 --name demo-app --link demo-mysql:mysql -d jiwhiz/spring-boot-docker-mysql
I wonder if you got this to work by linking containers, it didn't work for me, tried using a linked container alias as a dbhost name in my Springboot app. I did not try it as MongoClientURI though.
I did opt a work around, with mongodb containers and spring apps containers, I had to set up mongo containers host and port to spring app containers while creating the apps containers as Containers ENV variables , as shown below
Mongo container
docker run -d -p 27027:27017 -p 28027:28017 --name mongodb --volumes-from dbdata iamiddy/mongodb
SpringBoot Apps containers
docker run -d -p 8000:8080 --name AppDockerContainer
-e db.host.name=EC2-HOSTING-MONGO-CONTAINER
-e db.host.port=DB-HOST-PORT AppDockerImage
application.properties
db.host.port=27017
db.host.name=localhost
MongoConfig
public class MongoRepositoryConfig extends AbstractMongoConfiguration {
#Value("${db.host.port}")
private int port;
#Value("${db.host.name}")
private String host;
#Value("${db.name}")
private String dbname;
#Override
public Mongo mongo() throws Exception {
ServerAddress serverAdress = new ServerAddress(host,port);
Mongo mongo = new MongoClient(serverAdress);
mongo.setWriteConcern(WriteConcern.ACKNOWLEDGED);
return mongo;
}
public #Bean MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongo(), getDatabaseName());
}
#Override
protected String getDatabaseName() {
return dbname;
}
}
As docker links are now deprecated, one can use user defined networks to achieve communication between containers.
Referring to Docker Networking, docker has 3 different types of networks - Bridge, Host, Network. Using docker network ls you can list all networks currently defined. By default docker comes with 1 for each type pre-configured.
While running your container, one can specify(--network=abcd) network the process will join - by default its the docker0 of type bridge.
Now, for the problem statement in the question here, the simplest approach is to use --network=host while launching mongo & spring-boot-app container.
docker run -d -P --name=my-mongo --network=host mongo:latest
Then in your Spring boot app, you will have something like this:-
spring:
application:
name: my-app
data:
mongodb:
database: app
host: 192.168.99.100 // your machine private ip.
port: 27017
Run you spring boot app from image using :-
docker run -d -P --name=my-boot-app --network=host my-app-image
You can then use/reach mongo & app instances as if they were run without docker.
(While using docker-toolbox on Windows, you are actually running docker inside a VM that has different IP - I found it to be 192.168.99.100 . So remember to use this ip instead of your local ip)

Categories

Resources