How to use an existent network with testcontainers? - docker

I have a test that will run with some controlled containers on an environment that already have an existent external docker network.
How can I make my test container connect to said network?
I tried the code bellow with no success:
public static final GenericContainer tcpController;
static {
Network network = Network.builder().id("existent-external-network").build();
tcpController = new GenericContainer("tcp_controller:0.0.1")
.withExposedPorts(3005)
.withEnv("TCP_PORT", "3005")
.withNetwork(network);
tcpController.start();
}
Essentially I want to do the equivalent of the following docker-compose
version: "3.4"
services:
machine:
image: tcp_controller:0.0.1
environment:
- TCP_PORT=3005
networks:
default:
external:
name: existent-external-network
EDIT 1:
What Vitaly suggested worked.
Here is what I actually did using his suggestions and the docs
Consider TcpHandler just a class that needed the IP and port
public static final DockerComposeContainer compose;
static {
compose = new DockerComposeContainer(
new File("src/test/java/docker-compose.yml")
)
.withExposedService("machine", 3005)
.withLocalCompose(true);
compose.start();
}
#BeforeAll
static void setup() throws IOException, TimeoutException {
settings = Settings.getInstance();
// We need to get the actual host and port using service name
settings.tcpURL = compose.getServiceHost("machine", 3005);
settings.tcpPort = compose.getServicePort("machine", 3005);
tcp = new TCPHandler(settings.tcpURL, settings.tcpPort);
tcp.start();
}

Fabio, not sure if you tried that - would using docker with local compose work for you? Like:
#Container
public static DockerComposeContainer docker = new DockerComposeContainer(
new File("src/test/resources/compose-mysql-test.yml")
)
.withLocalCompose(true);

Related

Nomad Connect Two docker Containers

I am having trouble establishing communication between two docker containers via nomad. Containers are in the same task group but still unable to reach each other. Even when using NOMAD_ADDR_ environment variable. Can anyone help in this regard? I tried both host and bridge network mode.
My nomad config is given below. Images are pulled and the Redis container and application container starts, but then app container crashes with Redis Connection Refused error
The second issue is, as you might have guessed is of prettifying the code with proper indentation etc. Just like Javascript or HTML or YAML is automatically formatted in VS code. I am unable to find a code prettifier for the HCL language.
job "app-deployment" {
datacenters = ["dc1"]
group "app" {
network {
mode = "bridge"
port "web-ui" { to = 5000 }
port "redis" { to = 6379 }
}
service {
name = "web-ui"
port = "web-ui"
// check {
// type = "http"
// path = "/health"
// interval = "2s"
// timeout = "2s"
// }
}
task "myapp" {
driver = "docker"
config {
image_pull_timeout = "10m"
image = "https://docker.com"
ports = ["web-ui"]
}
env {
REDIS_URL="redis://${NOMAD_ADDR_redis}"
// REDIS_URL="redis://$NOMAD_IP_redis:$NOMAD_PORT_redis"
NODE_ENV="production"
}
}
task "redis" {
driver = "docker"
config {
image = "redis"
ports = ["redis"]
}
}
}
}
So I was able to resolve it, basically, when you start nomad agent in dev mode, by default it binds to the loopback interface and that is why you get 127.0.0.1 as IP and node port in NOMAD env variables. 127.0.0.1 resolves to localhost inside container and hence it is unable to reach the Redis server.
To fix the issue, simply run
ip a
Identify the primary network interface for me it was my wifi interface. Then start the nomad like below.
nomad agent -dev -network-interface="en0"
# where en0 is the primary network interface
That way u will still be able to access the nomad UI on localhost:4646 but your containers will get the HOST IP from your network rather then 127.0.0.1

How do I connect my NATS docker image to my k8s NATS cluster?

I deployed NATS to my kubernetes cluster, and the nats-box image in my cluster (installed alongside my NATS image vis helm) can apparently connect to it, but I can't seem to get my own microservice to connect to it. How is nats-box successful, but my own microservice is not?
helm install my-nats nats/nats
installs NATS with statefulset called "my-nats" and a service called "my-nats":
my-nats ClusterIP None <none>
4222/TCP,6222/TCP,8222/TCP,7777/TCP,7422/TCP,7522/TCP
But my test app uses stdin to accept a url input and tries to connect to "my-nats" and yet it fails:
public static void Main(string[] args)
{
Console.Write("=>");
string url = Console.ReadLine();
Console.WriteLine($"Connecting to {url}");
try
{
using (IConnection pubConnection = new ConnectionFactory().CreateConnection(url))
{
Console.WriteLine($"Connected to {url}!");
}
}
catch (NATSNoServersException)
{
Console.WriteLine($"No Server found for url, '{url}'!");
return;
}
...
docker run -it testapp
=>my-nats
Connecting to my-nats
No Server found for url, 'my-nats'!
How can I get my microservice to connect to my "my-nats" cluster just like nats-box does?
Are you trying to connect from local(microservice) to nats in your cluster(K8s running in your machine).
if so you might need to do a port forward of the nats from your cluster then use http://localhost:4222 as the url for your microservice nats connection url.

Socket connections between docker containers fails

I have multiple containers being deployed through a docker-compose file seen below
version: '3'
services:
module2:
restart: always
build:
dockerfile: Dockerfile
context: ./Module-2
ports:
- '16667:16667'
module3:
build:
dockerfile: Dockerfile
context: ./Module-3
ports:
- '16669:16669'
Module 2 takes a socket request from an outside source and works as intended. The trouble begins when module 2 tries to connect with module 3
Module 2 code (JAVA)
private int socket_port = 16669;
private String server = "127.0.0.1";
public TextOutputSocket() {
}
public TextOutputSocket(String host, int socket_port) {
this.server = host;
this.socket_port = socket_port;
}
public void sendText(String textToSend) {
OutputStream os = null;
Socket sock = null;
try {
System.out.println("Connecting to " + server + ":" + socket_port);
sock = new Socket(server, socket_port);
os = sock.getOutputStream();
module 3 code (GO)
ln, err := net.Listen("tcp", ":16669")
if err != nil {
fmt.Println(err)
// handle error
}
Module 2 recieves a connection refused error when ever i try to send the request.
I feel I don't have the best understanding of docker networks and i assume this is where the problem lies.
Thank you for the help in advance
In your case, when you spin up docker-compose, module2 and module3 2 containers will be in the same docker network and they can connect to each other using their DNS names i.e. module2 and module3 respectively.
As a result, you should update your module2 code to be like this
private int socket_port = 16669;
private String server = "module3";
public TextOutputSocket() {
}
...
Note that you will not need to do a port mapping like - '16667:16667' or - '16669:16669' in order for these 2 modules to talk to each other.
First you need to understand how docker containers work. Each of you applications are deployed in two seperate containers. So when trying to connect to a different container you need to give the ip or the hostname of that specific container.
Here you have tried to connect to 1669 of localhost, instead what you should be doing is try to connect to the other container. This can be done by setting the container name of the module3 container and docker dns will resolve the ip address for you.
Simple replace 127.0.0.1 with module3

How do I get my IP address from inside an ECS container running with the awsvpc network mode?

From a regular ECS container running with the bridge mode, or from a standard EC2 instance, I usually run
curl http://169.254.169.254/latest/meta-data/local-ipv4
to retrieve my IP.
In an ECS container running with the awsvpc network mode, I get the IP of the underlying EC2 instance which is not what I want. I want the address of the ENI attached to my container. How do I do that?
A new convenience environment variable is injected by the AWS container agent into every container in AWS ECS: ${ECS_CONTAINER_METADATA_URI}
This contains the URL to the metadata endpoint, so now you can do
curl ${ECS_CONTAINER_METADATA_URI}
The output looks something like
{
"DockerId":"redact",
"Name":"redact",
"DockerName":"ecs-redact",
"Image":"redact",
"ImageID":"redact",
"Labels":{ },
"DesiredStatus":"RUNNING",
"KnownStatus":"RUNNING",
"Limits":{ },
"CreatedAt":"2019-04-16T22:39:57.040286277Z",
"StartedAt":"2019-04-16T22:39:57.29386087Z",
"Type":"NORMAL",
"Networks":[
{
"NetworkMode":"awsvpc",
"IPv4Addresses":[
"172.30.1.115"
]
}
]
}
Under the key Networks you'll find IPv4Address
You application code can then look something like this (python)
METADATA_URI = os.environ['ECS_CONTAINER_METADATA_URI']
container_metadata = requests.get(METADATA_URI).json()
ALLOWED_HOSTS.append(container_metadata['Networks'][0]['IPv4Addresses'][0])
import * as publicIp from 'public-ip';
const publicIpAddress = await publicIp.v4(); // your container's public IP

How to connect socket.io inside docker-compose between containers

I have one container that is serving http on port 4000.
it has socket server attached
docker-compose:
dashboard-server:
image: enginetonic:compose1.2
container_name: dashboard-server
command: node src/service/endpoint/dashboard/dashboard-server/dashboard-server.js
restart: on-failure
ports:
- 4000:4000
integration-test:
image: enginetonic:compose1.2
container_name: integration-test
testRegex "(/integration/.*|(\\.|/)(integration))\\.jsx?$$"
tty: true
server:
const http = require('http').createServer(handler)
const io = Io(http)
io.on('connection', socket => {
logger.debug('socket connected')
})
io.use((socket, next) => {
logger.debug('socket connection established.')
})
http.listen(4000, '127.0.0.1', () => {
console.log(
`Server running at http://127.0.0.1:4000/`
)
output in docker:
Server running at http://127.0.0.1:4000/
https is listening: true
Now, I am trying to connect to this server from another container like this:
file:
const url = `ws://dashboard-server:4000`
const ioc = IoC.connect(url)
ioc.on('error', error => {
console.log(error.message)
})
ioc.on('connect', res => {
console.log('connect')
})
ioc.on('connect_error', (error) => {
console.log(error.message)
})
output:
xhr poll error
When I run both locally in terminal, I get correct response
{"message":"socket connection established","level":"debug"}
Why isnt socket making connection inside container, but locally it is?
What am I doing wrong?
edit: only part of files are displayed for readability. socket connects normaly on local machine with with spawning both files in separate terminals
You need to link the docker containers and refer to them by name, not 127.0.0.1. https://docs.docker.com/compose/networking provides more doc. You'll also need to listen to '0.0.0.0' so that you accept connections across the docker network.
I only see one container in your compose file. If you're trying to connect to the docker containers from outside docker, you'll have to expose a port. The same reference shows you how.
http.listen(4000, '127.0.0.1', () => {
should become
http.listen(4000, '0.0.0.0', () => {
so that the server is listening on all addresses, including the address that docker is automatically allocating on a docker network.
Then the client has to refer to the server by the name given in docker compose, so
const url = `ws://127.0.0.1:4000`
becomes
const url = `ws://dashboard-server:4000`

Resources