I just started to use FIWARE. I downloaded the latest version on the website (v2) using docker-compose on a PopOs distro.
I'm using Postman to make requests (create the entities and subscriptions) and a Laravel application to listen the notification from the FIWARE subscriptions. But for some reason, today, when I started the docker service and start to send requests: the FIWARE notifications suddenly stopped to work.
When I access the subscriptions endpoint FIWARE returns:
"notification": {
"timesSent": 1,
"lastNotification": "2021-09-02T01:19:39.000Z",
"attrs": [],
"onlyChangedAttrs": false,
"attrsFormat": "keyValues",
"http": {
"url": "http://localhost:8000/api/notifications"
},
"lastFailure": "2021-09-02T01:19:39.000Z",
"lastFailureReason": "Couldn't connect to server"
}
FIWARE can't comunicate, but if I make a POST request using Postman for that endpoint (http://localhost:8000/api/notifications) it returns 200.
There's some aditional configuration between the FIWARE docker container and the local machine? Or I'm doing something wrong?
This is my entity:
// http://{{orion}}/v2/subscription
{
"id": "movie",
"type": "movie",
"name": {
"type": "text",
"value": "movie name"
},
"gender": {
"type": "text",
"value": "drama"
}
}
This is how I'm doing the subscription:
// http://{{orion}}/v2/subscriptions
{
"description": "Notify me about any movie of gender drama",
"subject": {
"entities": [{"idPattern": ".*","type": "movie"}],
"condition": {
"attrs": ["gender"],
"expression": {
"q": "gender==drama"
}
}
},
"notification": {
"http": {
"url": "http://127.0.0.1:8000/api/notifications"
}
}
}
If you are using Docker, then you need to consider what http://localhost:8000/api/notifications actually means. localhost will mean the localhost as experienced by the Orion container itself. Generally Orion listens on 1026 and there is nothing listening on 8000 within a dockerized Orion, therefore your subscription fails.
If you have another micro-service running within the same docker network and in a separate container you must use the hostname of that container (or an alias or defined IP) to describe the notification URL, not localhost.
So for example in the following tutorial where a subscription payload is displayed on screen:
curl -iX POST \
--url 'http://localhost:1026/v2/subscriptions' \
--header 'content-type: application/json' \
--data '{
"description": "Notify me of all product price changes",
"subject": {
"entities": [{"idPattern": ".*", "type": "Product"}],
"condition": {
"attrs": [ "price" ]
}
},
"notification": {
"http": {
"url": "http://tutorial:3000/subscription/price-change"
}
}
}'
refers to a container which is called tutorial within the docker network
tutorial:
image: fiware/tutorials.context-provider
hostname: tutorial
container_name: fiware-tutorial
depends_on:
- orion
networks:
default:
aliases:
- iot-sensors
- context-provider
expose:
- 3000
As it happens the tutorial container is also exposing its internal port 3000 to the localhost of the machine it is running on so it can be viewed by a user, but Orion can only access it via the hostname on the docker network.
Related
I have a scenario on a Citrix Xenserver server with 3 virtual machines (VM) Centos7 on the same network (10.0.1.0/24). Each VM are responsible to give a prediction done with Apache Spark on Scala (Logistic Regression). I use Orion Context broker (CB) on Docker to create subscriptions that will be triggered to ask predictions. The CB it is located only in 10.0.1.4 VM and I made some ports available to access from the other machines.
My docker-compose.yml:
mongo:
image: mongo:4.4
command: --nojournal
orion:
image: fiware/orion
links:
- mongo
ports:
- "1026:1026"
- "1027:1027"
- "1028:1028"
- "9090:9090"
command: -dbhost mongo -corsOrigin __ALL
For example, to access CB from 10.0.1.2 VM I use 10.0.1.4:1028/..... and so on.
This is the subscription I'm facing problems (and maybe the other related to 10.0.1.3 VM must have the same problem too)
curl -v localhost:1026/v2/subscriptions -s -S -H 'Content-Type: application/json' -d #- <<EOF
{
"description": "Suscripcion de anemia para monitorear al Paciente",
"subject": {
"entities": [
{
"id": "Paciente1",
"type": "Paciente"
}
],
"condition": {
"attrs": ["calculateAnaemia"]
},
"expression":{
"q":"calculateAnaemia:1"
}
},
"notification": {
"http": {
"url": "http://10.0.1.2:9002/notify"
},
"attrs": ["gender","age","hemoglobin","mch","mchc","mcv"]
},
"expires": "2040-01-01T14:00:00.00Z",
"throttling": 10
}
EOF
I have a code on the 10.0.1.2 VM that is listening about changes related to this subscription on port 9002 with Fiware Cosmos:
For eventStream variable on 10.0.1.4 VM got port 9004 and for 10.0.1.3 VM it is 9003 port
For conf variable I set "spark.driver.host" on 10.0.1.4 VM with 10.0.1.4 IP and for 10.0.1.3 VM to 10.0.1.3 IP
import esqMensajeria.ActorSysMensajeria.ActoresEsquema
import org.apache.spark._
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.fiware.cosmos.orion.spark.connector._
object Main extends App{
val conf = new SparkConf().setMaster("local[*]").setAppName("AnaemiaPrediction").set("spark.driver.host", "10.0.1.2")
val ssc = new StreamingContext(conf, Seconds(10))
// Create Orion Source. Receive notifications on port 9002
val eventStream = ssc.receiverStream(new OrionReceiver(9002))
// Esquema de mensajeria
val actorSysMsj = new ActoresEsquema ()
println("Esperando cambios para obtener informaciĆ³n...")
// Process event stream
val processedDataStream = eventStream
.flatMap(event => event.entities)
.map(entity => {
val gender: Int = entity.attrs("gender").value.asInstanceOf[Number].intValue()
val age: Int = entity.attrs("age").value.asInstanceOf[Number].intValue()
val hemoglobin: Double = entity.attrs("hemoglobin").value.asInstanceOf[Double].doubleValue()
val mch: Double = entity.attrs("mch").value.asInstanceOf[Double].doubleValue()
val mchc: Double = entity.attrs("mchc").value.asInstanceOf[Double].doubleValue()
val mcv: Double = entity.attrs("mcv").value.asInstanceOf[Double].doubleValue()
actorSysMsj.start((entity.id, gender,age,hemoglobin,mch,mchc,mcv),conf)
(entity.id, gender,age,hemoglobin,mch,mchc,mcv)
})
processedDataStream.print
ssc.start()
ssc.awaitTermination()
}
But when I trigger, the subscription fails showing the next (not only on 10.0.1.2 but 10.0.1.3 VM too):
{"id":"61cb8569a1e87a254e16066d",
"description":"Suscripcion de anemia para monitorear al Paciente",
"expires":"2040-01-01T14:00:00.000Z",
"status":"failed",
"subject":{"entities":[{"id":"Paciente1","type":"Paciente"}],
"condition":{"attrs":["calculateAnaemia"]}},
"notification":
{"timesSent":3,
"lastNotification":"2021-12-29T00:03:49.000Z",
"attrs":"gender","age","hemoglobin","mch","mchc","mcv"],"
onlyChangedAttrs":false,
"attrsFormat":"normalized",
http":{"url":"http://10.0.1.2:9002/notify"},
"lastFailure":"2021-12-29T00:03:54.000Z",
"lastFailureReason":"Timeout was reached"},
"throttling":10}]
The curious thing is when I worked with the subscription related to the 10.0.1.4 VM that has the CB container, the subscription remains active and i get my expected result.
This is the subscription:
curl -v localhost:1026/v2/subscriptions -s -S -H 'Content-Type: application/json' -d #- <<EOF
{
"description": "Suscripcion de deceso para monitorear al Paciente",
"subject": {
"entities": [
{
"id": "Paciente1",
"type": "Paciente"
}
],
"condition": {
"attrs": [
"calculateDeceased"
]
},
"expression":{
"q":"calculateDeceased:1"
}
},
"notification": {
"http": {
"url": "http://10.0.1.4:9004/notify"
},
"attrs": [
"gender","age","hasAnaemia","creatinePP","hasDiabetes","ejecFrac","highBloodP","platelets","serumCreatinine","serumSodium","smoking","time"
]
},
"expires": "2040-01-01T14:00:00.00Z"
}
EOF
This is the answer when it is triggered and process perfectly:
{"id":"61caab07a1e87a254e160665",
"description":"Suscripcion de deceso para monitorear al Paciente",
"expires":"2040-01-01T14:00:00.000Z",
"status":"active",
"subject":{"entities":[{"id":"Paciente1","type":"Paciente"}],
"condition":{"attrs":["calculateDeceased"]}},
"notification":{"timesSent":1,"lastNotification":"2021-12-28T06:15:41.000Z",
"attrs":["gender","age","hasAnaemia","creatinePP","hasDiabetes","ejecFrac","highBloodP","platelets","serumCreatinine","serumSodium","smoking","time"],
"onlyChangedAttrs":false,
"attrsFormat":"normalized",
"http":{"url":"http://10.0.1.4:9004/notify"},
"lastSuccess":"2021-12-28T06:15:43.000Z",
"lastSuccessCode":200}}
I have to say I'm new to Spark, Scala and even Fiware. But projects are projects and maybe I'm missing something I did not see in all I read to set up this project. Also, I stopped all firewalls (firewalld) because I was facing a "Couldn't connect to server" error on subscriptions related to 10.0.1.2 and 10.0.1.3 VMs. I did an sudo yum update too, I pinged all the VMs between each other and I got nice response. One thing that I do not know if it is important: I have internet on all my VMs but I can't ping for example... www.google.com or 8.8.8.8. So, any suggestions are welcome! I apologized for my english. Thanks in advance~
Well, after 3 days of keep looking and trying i just discovered that I need to turn off the 1.2 and 1.3 firewalls and keep it on 1.4.
I am setting up debugging of FastAPI running in a container with VS Code. When I launch the debugger, the FastAPI app runs in the container. But when I access the webpage from host, there is no response from server as the following:
However, if I start the container from command line with the following command, I can access the webpage from host.
docker run -p 8001:80/tcp with-batch:v2 uvicorn main:app --host 0.0.0.0 --port 80
Here is the tasks.json file:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "docker-run",
"label": "docker-run: debug",
"dockerRun": {
"image": "with-batch:v2",
"volumes": [
{
"containerPath": "/app",
"localPath": "${workspaceFolder}/app"
}
],
"ports": [
{
"containerPort": 80,
"hostPort": 8001,
"protocol": "tcp"
}
]
},
"python": {
"args": [
"main:app",
"--port",
"80"
],
"module": "uvicorn"
}
},
{
"type": "docker-build",
"label": "docker-build",
"platform": "python",
"dockerBuild": {
"tag": "with-batch:v2"
}
}
]
}
here is the launch.json file:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug Flask App",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"python": {
"pathMappings": [
{
"localRoot": "${workspaceFolder}/app",
"remoteRoot": "/app"
}
],
"projectType": "fastapi"
}
}
]
}
here is the debug console output:
here is the docker-run: debug terminal output:
here is the Python Debug Console terminal output:
Explanation
The reason you are not able to access your container at that port, is because VSCode builds your image with a random, unique localhost port mapped to the running container.
You can see this by running docker container inspect {container_name} which should print out a JSON representation of the running container. In your case you would write docker container inspect withbatch-dev
The JSON is an array of objects, in this case just the one object, with a key of "NetworkSettings" and a key in that object of "Ports" which would look similar to:
"Ports": {
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "55016"
}
]
}
That port 55016 would be the port you can connect to at localhost:55016
Solution
With some tinkering and documentation it seems the "projectType": "fastapi" should be launching your browser for you at that specific port. Additionally, your debug console output shows Uvicorn running on http://127.0.0.1:80. 127.0.0.1 is localhost (also known as the loopback interface), which means your process in the docker container is only listening to internal connections. Think of docker containers being in their own subnetwork relative to your computer (there are exceptions to this, but that's not important). If they want to listen to outside connections (your computer or other containers), they would need to tell the container's virtual network interface to do so. In the context of a server, you would use the address 0.0.0.0 to indicate you want to listen on all ipv4 addresses referencing this interface.
That got a little deep, but suffice it to say, you should be able to add --host 0.0.0.0 to your run arguments and you would be able to connect. You would add this to tasks.json, in the docker-run object, where your other python args are specified:
{
"type": "docker-run",
"label": "docker-run: debug",
"dockerRun": {
"image": "with-batch:v2",
"volumes": [
{
"containerPath": "/app",
"localPath": "${workspaceFolder}/app"
}
],
"ports": [
{
"containerPort": 80,
"hostPort": 8001,
"protocol": "tcp"
}
]
},
"python": {
"args": [
"main:app",
"--host",
"0.0.0.0",
"--port",
"80"
],
"module": "uvicorn"
}
},
I tried pushing a docker image of Eclipse theia to cf, however unable to start it (or rather connect to it). The image exposes port 3000 with EXPOSE 3000. The app works and running it locally opens the default theia home screen
On CF, Sufficient disk and memory are given.
When the default port health check is set, cf hangs at starting app.
Creating app theia-docker...
Mapping routes...
Staging app and tracing logs...
Cell 15fcfa4a-a364-4dc2-ab6b-349f5196bd80 creating container for instance bd4b9e65-946f-485a-9de1-5c7fc8d4ad01
Cell 15fcfa4a-a364-4dc2-ab6b-349f5196bd80 successfully created container for instance bd4b9e65-946f-485a-9de1-5c7fc8d4ad01
Staging...
Staging process started ...
Staging process finished
Exit status 0
Staging Complete
Cell 15fcfa4a-a364-4dc2-ab6b-349f5196bd80 stopping instance bd4b9e65-946f-485a-9de1-5c7fc8d4ad01
Cell 15fcfa4a-a364-4dc2-ab6b-349f5196bd80 destroying container for instance bd4b9e65-946f-485a-9de1-5c7fc8d4ad01
Cell 15fcfa4a-a364-4dc2-ab6b-349f5196bd80 successfully destroyed container for instance bd4b9e65-946f-485a-9de1-5c7fc8d4ad01
It eventually comes to FAILED
cf logs would show:
2021-06-12T14:37:25.40+0530 [APP/PROC/WEB/0] OUT root INFO Deploy plugins list took: 161.7 ms
2021-06-12T14:38:24.77+0530 [HEALTH/0] ERR Failed to make TCP connection to port 2375: connection refused; Failed to make TCP connection to port 2376: connection refused
2021-06-12T14:38:24.77+0530 [CELL/0] ERR Failed after 1m0.303s: readiness health check never passed.
Why is it taking the wrong PORT number?
If I try setting the port in the env variable as cf set-env PORT 3000, I would get
FAILED
Server error, status code: 400, error code: 100001, message: The app is invalid: environment_variables cannot set PORT
I then set the health check is set to process. Of course, this would start successfully (failure or not). Checking the logs it can be seen that the app has started successfully. When I ssh into the app (cf ssh theia-docker) I am able to curl the application as localhost:3000 and returns the HTML of the homepage.
~ % cf ssh theia-docker
bash-5.0$ curl localhost:3000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<script type="text/javascript" src="./bundle.js" charset="utf-8"></script>
</head>
<body>
<div class="theia-preload"></div>
</body>
</html>bash-5.0$
However, when I try to connect to the app via the application URL I get the error:
502 Bad Gateway: Registered endpoint failed to handle the request.
The reason I see for this is that the base image I have used for this is based on docker:dind and it seems like in the base image ports 2375 and 2376 are exposed.
Why does CF pick the ports exposed in the base image rather than the one exposed in the docker image that is created? Shouldn't the port in the current image take precedence?
Changing the route mappings helped.
The following steps helped:
Get app guid
~ % cf app theia-docker --guid
8032eea6-d146-4d27-9b17-c7331852b59b
Add the required port
cf curl /v2/apps/8032eea6-d146-4d27-9b17-c7331852b59b -X PUT -d '{"ports": [3000]}'
{
"metadata": {
"guid": "8032eea6-d146-4d27-9b17-c7331852b59b",
"url": "/v2/apps/8032eea6-d146-4d27-9b17-c7331852b59b",
"created_at": "2021-06-12T09:04:51Z",
"updated_at": "2021-06-12T17:58:03Z"
},
"entity": {
"name": "theia-docker",
"production": false,
.
.
.
"ports": [
3000,
2375,
2376
],
.
.
.
Get the routes attached to the app
~ % cf curl /v2/apps/8032eea6-d146-4d27-9b17-c7331852b59b/routes
{
"total_results": 1,
"total_pages": 1,
"prev_url": null,
"next_url": null,
"resources": [
{
"metadata": {
"guid": "21f89763-baab-456d-8151-aad383a3c28f",
.
.
.
Use the route-guid to find route_mappings:
cf curl /v2/routes/21f89763-baab-456d-8151-aad383a3c28f/route_mappings
{
"total_results": 1,
"total_pages": 1,
"prev_url": null,
"next_url": null,
"resources": [
{
"metadata": {
"guid": "33bde252-ad3e-49b4-91df-78543ac452b4",
"url": "/v2/route_mappings/33bde252-ad3e-49b4-91df-78543ac452b4",
"created_at": "2021-06-12T09:04:51Z",
"updated_at": "2021-06-12T09:04:51Z"
},
"entity": {
"app_port": null,
"app_guid": "8032eea6-d146-4d27-9b17-c7331852b59b",
"route_guid": "21f89763-baab-456d-8151-aad383a3c28f",
"app_url": "/v2/apps/8032eea6-d146-4d27-9b17-c7331852b59b",
"route_url": "/v2/routes/21f89763-baab-456d-8151-aad383a3c28f"
}
}
]
}
Update the route-mapping using the app_guid, route_guid and app_port:
~% cf curl /v2/route_mappings -X POST -d '{"app_guid":"8032eea6-d146-4d27-9b17-c7331852b59b","route_guid":"21f89763-baab-456d-8151-aad383a3c28f", "app_port":3000}'
{
"metadata": {
"guid": "a62a2ea6-859f-48cc-aa33-a8d6583081da",
"url": "/v2/route_mappings/a62a2ea6-859f-48cc-aa33-a8d6583081da",
"created_at": "2021-06-12T18:02:19Z",
"updated_at": "2021-06-12T18:02:19Z"
},
"entity": {
"app_port": 3000,
"app_guid": "8032eea6-d146-4d27-9b17-c7331852b59b",
"route_guid": "21f89763-baab-456d-8151-aad383a3c28f",
"app_url": "/v2/apps/8032eea6-d146-4d27-9b17-c7331852b59b",
"route_url": "/v2/routes/21f89763-baab-456d-8151-aad383a3c28f"
}
}
List the route mappings again:
~ % cf curl /v2/routes/21f89763-baab-456d-8151-aad383a3c28f/route_mappings
{
"total_results": 2,
"total_pages": 1,
"prev_url": null,
"next_url": null,
"resources": [
{
"metadata": {
"guid": "33bde252-ad3e-49b4-91df-78543ac452b4",
"url": "/v2/route_mappings/33bde252-ad3e-49b4-91df-78543ac452b4",
"created_at": "2021-06-12T09:04:51Z",
"updated_at": "2021-06-12T09:04:51Z"
},
"entity": {
"app_port": null,
"app_guid": "8032eea6-d146-4d27-9b17-c7331852b59b",
"route_guid": "21f89763-baab-456d-8151-aad383a3c28f",
"app_url": "/v2/apps/8032eea6-d146-4d27-9b17-c7331852b59b",
"route_url": "/v2/routes/21f89763-baab-456d-8151-aad383a3c28f"
}
},
{
"metadata": {
"guid": "a62a2ea6-859f-48cc-aa33-a8d6583081da",
"url": "/v2/route_mappings/a62a2ea6-859f-48cc-aa33-a8d6583081da",
"created_at": "2021-06-12T18:02:19Z",
"updated_at": "2021-06-12T18:02:19Z"
},
"entity": {
"app_port": 3000,
"app_guid": "8032eea6-d146-4d27-9b17-c7331852b59b",
"route_guid": "21f89763-baab-456d-8151-aad383a3c28f",
"app_url": "/v2/apps/8032eea6-d146-4d27-9b17-c7331852b59b",
"route_url": "/v2/routes/21f89763-baab-456d-8151-aad383a3c28f"
}
}
]
}
You will find the new route mapping that was created. Delete the unwanted one.
~ % cf curl /v2/route_mappings/33bde252-ad3e-49b4-91df-78543ac452b4 -X DELETE
That's about it. Any better solutions are welcome :). (Not involving to maintain the Dockerfile of the base image of course)
In an existing swarm, I created a service via a docker-compose yaml file using the 'docker stack' command.
When I check the service via 'docker service ls' command, the new service shows up on the list. it shows "0/1" in the REPLICAS column
When I check the service using the command below, it shows 'Running' as the Desired State
docker service ps --no-trunc (service id)
When I check if there is already a corresponding container for the service, I can see none
When I try to access the service via the browser, it seems to be not started.
What is difficult is I cannot see any logs to find the cause of why this is happening
docker service logs (service id)
I figured it may just be slow to start but I waited for about half an hour and it was still in that state. Not sure how can I find out the cause of this without any logs. Can anyone help me on this?
EDIT: Below is the result when I did a docker inspect of the service task
[
{
"ID": "wt2tdoz64j5wmci4gr3q3io2e",
"Version": {
"Index": 3407514
},
"CreatedAt": "2020-08-25T00:58:13.012900717Z",
"UpdatedAt": "2020-08-25T00:58:13.012900717Z",
"Labels": {},
"Spec": {
"ContainerSpec": {
"Image": "my-ui-image:1.8.006",
"Labels": {
"com.docker.stack.namespace": "myservice-stack"
},
"Env": [
"BACKEND_HOSTNAME=somewebsite.com",
"BACKEND_PORT=3421"
],
"Privileges": {
"CredentialSpec": null,
"SELinuxContext": null
},
"Hosts": [
"10.152.30.18 somewebsite.com"
],
"Isolation": "default"
},
"Resources": {},
"Placement": {},
"Networks": [
{
"Target": "lt87emwtgbeztof5k2r1z2v27",
"Aliases": [
"myui_poc2"
]
}
],
"ForceUpdate": 0
},
"ServiceID": "nbskoeofakkgxlgj3utgn45c5",
"Slot": 1,
"Status": {
"Timestamp": "2020-08-25T00:58:13.012883476Z",
"State": "new",
"Message": "created",
"PortStatus": {}
},
"DesiredState": "running"
}
]
If you store your images in private registry then you must be logged in by command docker login and deploy your services by docker stack deploy -c docker-compose.yml your_service --with-registry-auth.
From the docker service ps ... output, you will see a column with the task id. You can get further details of the state of that task by inspecting the task id:
docker inspect $taskid
My guess is that your app is not redirecting it's output to stdout and that's why you don't get any output when doing "docker service logs...".
I would start by looking at this: https://docs.docker.com/config/containers/logging/
How you redirect the apps output to stdout will depend on what language your app is developed in.
I have read about service discovery for Docker using Consul, but I can't understand it.
Could you explain to me, how can I run two docker containers, recognize from the first container host of the second using Consul and send some message to it?
You would need to run Consul Agent in client mode inside each Docker container. Each Docker Container will need a Consul Service Definition file to let the Agent know to advertize it's service to the Consul Servers.
They look like this:
{
"service": {
"name": "redis",
"tags": ["master"],
"address": "127.0.0.1",
"port": 8000,
"checks": [
{
"script": "/usr/local/bin/check_redis.py",
"interval": "10s"
}
]
}
}
And a Service Health Check to monitor the health of the service. Something like this:
{
"check": {
"id": "redis",
"name": "Redis",
"script": "/usr/local/bin/check_redis_ping_returns_pong.sh",
"interval": "10s"
}
}
In the other Docker Container your code would find the Redis service either via DNS or the Consul Servers HTTP API
dig #localhost -p 8500 redis.service.consul
curl $CONSUL_SERVER/v1/health/service/redis?passing