Configuring opentelemetry, Jaeger, Prometheus, Grafana – problem determination

It took me a long time to get all of the parts of the solution working. There were many reasons; I was new to the environment, some things were not obvious, and some of the documentation feels like it is written by experts for experts.

I learned the debug techniques while writing the Configuring opentelemetry, Jaeger, Prometheus, Grafana 101 the basics,102 doing processing, and 103 http endpoints posts.

Topics

Opentelemetry configuration problems.

You can specify print-initial-config as in

otel/opentelemetry-collector-contrib:latest print-initial-config –config otel-config.yaml

This is useful if you have more than one –config file.

Reading test data from a file

You can have JSON data read from a file, so you do not need to have a test creating data for you. It also means you get consistent data while developing the scenario

receivers:
otlp_json_file:
include:
- /fooin.file
start_at: beginning
...
service:
pipelines:

traces/a:
receivers: [otlp_json_file] # oltp
exporters: [span_metrics] # otlp_http,debug

Monitoring traffic coming out of a pipe

The output end of the pipe is defined using an exporter. There can be more than one end point defined, you can add an exported which writes to the terminal, or writes to a file.

For example the debug writes data to the terminal log. You can use the file to write data to a file.

exporters:

file/b:
path: /b.file
debug:
verbosity: detailed # Options: basic, normal, detailed
# sampling_initial: 2
# sampling_thereafter: 10
service:
pipelines:
traces/a:
receivers: [otlp_json_file] # oltp
exporters: [span_metrics] # otlp_http,debug
metrics:
receivers: [span_metrics]
exporters: [file/b,debug]

You can tell debug to write every row, or the first n rows, then sample the remainder.

Using docker makes it easier and harder.

Using docker it was easy to spin up the parts and get them working. All pre-reqs were installed etc.
Docker uses it’s own internal network, and getting the component to talk to each other was harder.

Displaying network traffic between containers

You can use the docker command

docker network ls

this gives information like

NETWORK ID     NAME                  DRIVER    SCOPE
26d793ac45cf bridge bridge local
4e4dc158fa88 host host local
0cfcba5bb0ff none null local
03617d139834 otel-jaeger-network bridge local

Where the docker network I was using was called otel-jaeger-network.

On Linux the ip link command gave

4: br-03617d139834: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
link/ether 12:87:1d:dc:7d:9a brd ff:ff:ff:ff:ff:ff
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 9a:9f:53:76:9d:a6 brd ff:ff:ff:ff:ff:ff

with the network prefixed with br ( for bridge).

Starting wireshark gave me a list of networks including br-03617d139834. I used this to display the traffic.

You can filter the traffic using filters like

tcp.dstport==4317

which says display records where the destination port was 4317. You could also use tcp.port=4317

This will allow you to see the traffic between containers.

Is z/OS data gathered writing data?

Initially it was challenging to see if data was being sent from z/OS. I had the situation where the first data was sent from z/OS, but then no more. I had to restart the data gatherer on z/OS.

I used

IJO="$IJO -Djavax.net.debug=ssl:handshake " 

to display the TLS handshake, and the message flow.

Example output

"certificate" : {                                                      
"version" : "v3",
"serial number" : "02:ba",
"signature algorithm": "SHA256withECDSA",
"issuer" : "CN=SSCA256, OU=CA, O=SSS, C=GB",
"not before" : "2026-06-29 02:36:41.000 EDT",
"not after" : "2029-01-30 11:46:00.000 EST",
"subject" : "CN=tempcert, O=cpwebuse2, C=GB",
"subject public key" : "RSA",
....

javax.net.ssl|DEBUG|D2|OkHttp https://10.1.0.2:4317/...|
2026-07-10 03:05:31.784 EDT|SSLSocketOutputRecord.java:334|
WRITE: TLSv1.3 application_data, length = 4854
javax.net.ssl|DEBUG|F2|OkHttp 10.1.0.2
|2026-07-10 03:05:31.789 EDT|SSLSocketInputRecord.java:214
|READ: TLSv1.2 application_data, length = 47
...

You can also use Wireshark to monitor traffic. With TLS 1.2 you can see details in the TLS handshake. With TLS 1.3 the details of the handshake are encrypted, and you just see the flow, and the data length.

Configuring opentelemetry, Jaeger, Prometheus, Grafana – 103 http endpoints

The above packages take tracking data or metrics and can display them in dashboards. I found it a struggle to understand how they were configured, as the documentation assumes you are an expert, and I could not find any “starting from zero” documentation.

This is one of my blog posts on using the above packages with Docker. See Configuring opentelemetry, Jaeger, Prometheus, Grafana

Understanding endpoints

If you think of the processing as a pipe line. Data from one or more sources go into the pipe, and at the remote end of the pipe the data can be copied to one or more destinations (fanned out).

When you define the pipeline you define

  • receivers – these identify what goes into the pipe
  • exporters – these identify what goes out of a pipe

Pass on data

As well as writing data to a file, or displaying it using debug, the most common pattern is to pass data on to another package

For example with Opentelemetry data it is passed it on to the Jaeger package which displays transaction delay information in near real time.

The code snippet below takes oltp data and exports it to a driver which exports otlp data over http.

The otlphttp definition sends it to the url http://jaeger2:4318. Where jaeger2 is some Docker magic which routes it to the Docker image called jaeger2.

...
exporters:
otlphttp:
endpoint: "http://jaeger2:4318"
tls:
insecure: true
service:
pipelines:
traces:
receivers: [ otlp ]
processors:
exporters: [otlphttp]

The jaeger2 configuration has

receivers:
otlp:
protocols:
grpc:
endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:4317"
http:
endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:4318"

This defines a receiver of type otlp, for http data, read from port 4318.

Endpoints

You might see

${env:JAEGER_LISTEN_HOST:-localhost}           

This is a configuration syntax commonly used in Jaeger or OpenTelemetry to set default values for environment variables.

It evaluates as follows:

  • If JAEGER_LISTEN_HOST is set: It uses the IP address or host defined in the environment variable.
  • If JAEGER_LISTEN_HOST is empty or not set: It falls back to the default value of localhost.

A typical flow between containers is Opentelemetry, Jaeger, Prometheus, Grafana. You do not need to know what these are, just they are just containers which do processing.

Push and pull.

Push

For the connection between Opentelemetry and Jaeger, data is pushed to Jaeger.

  • The Jaeger acts like a server, opens a TCP/IP socket, and listens for connections to it. You typically specify a network address of 0.0.0.0, which means listen on all networks on this box for the specified port. You can specify an address such as 172.26.5.0 which says only clients using addresses 172.26.5.* can connect.
  • The Opentelemetry container acts like a client, and has the IP address and port of the Jaeger connection. It initiates a session to Jaeger.

Running with docker adds a layer of complexity to it. Docker uses its own (sub) network. I could run two docker environments on my laptop, and both could use port 999. Each environment has its own network, and each subnetwork can have its own ports. The client has to use the correct IP address,

To get my docker environment working consistently, I used a definiton like

endpoint: "http://jaeger2:4318"

where jaeger2 is the name of the docker image of the jaeger container. Docker then substitues the IP addess of that container.

Pull

The situation is more complex than I have written.

For the Jaeger to Prometheus connect, typically Prometheus “scrapes” the data from Jaeger. For a few hours my mental picture was Jaeger pushing data to Prometheus; so Jaeger needed the IP address of Prometheus. This is the wrong way round.

Prometheus contacts Jaeger and says “send me your data”. Prometheus is the client, and Jaeger is the server. For this connection, Prometheus has to have a definitions like

endpoint: "http://jaeger2:7654"

Receivers

With a receiver you just specify a port; to be more specific you specify an adapter and a port.

  • Localhost is 127.0.0.0 is the internal adaptor
  • 0.0.0.0 stands for all adaptors

You specify an endpoint like

receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317

The endpoint url of 0.0.0.0 seems to work. The documentation says use localhost, but this did not always work for me.

The 4317 port is an external port, I can send data to it from z/OS.

In the docker container, the internal value above is mapped to the external using

  --publish 4317:4317 

Exporters

You specify an endpoint like

exporters:

otlp_http:
endpoint: "http://jaeger2:4318"
tls:
insecure: true

This says use the “driver” to export otlp data over http. The jaeger2 directs the data to the docker image with name jaeger2. You do not need

  --publish 4318:4318 

in the docker file for this to work, because it is internal to the docker configuration.

Getting in and out of the docker environment.

Because docker runs its own sub-network, you need to configure external ports to docker.

To pass data from z/OS down to the Opentelemetry container I used port 4317 on my laptop.

The Opentelemetry configuration is

receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
tls:
cert_file: /server.pem
key_file: /server.key.pem
min_version: "1.3" # Enforces TLS 1.3 as the minimum requirement
max_version: "1.3" # Locks maximum version to TLS 1.3
cipher_suites: []
reload_interval: "1h"

The docker configuration has

 --publish 4317:4317 \

which is of the format external:internal.

Configuring opentelemetry, Jaeger, Prometheus, Grafana – 102 processing data

This is one of my blog posts on using the above packages with Docker.

See Configuring opentelemetry, Jaeger, Prometheus, Grafana

The above packages take tracking data or metrics and can display them in dashboards. I found it a struggle to understand how they were configured, as the documentation assumes you are an expert, and I could not find any “starting from zero” documentation.

This follows on from the 101 the basics.

Doing processing – counting records

With the configuration file

receivers:
otlp_json_file:
include:
- /fooin.file
start_at: beginning
exporters:
file/a:
path: /fooout.file
file/b:
path: /b.file

connectors:
count:

service:
pipelines:
traces:
receivers: [otlp_json_file]
exporters: [count]
metrics:
receivers: [count]
exporters: [file/b]

This reads the input file, and copies the data to “count”. Count is a pipeline stage which reads the input, counts the records and outputs a summary of the data to the receiver count, which maps to the file b.file.

This runs in Docker, so there is b.file maps to a real file.

 -v "$(pwd)/foob.json":"/b.file" \

The file needs to exist ( use touch foob.json) and the userid docker needs write to it. I used chmod 777 foob.josn

Another example using span_metrics

This counts the number of span records from opentelemetry collector. It is defined here.

The documentation says

Exporter Pipeline TypeReceiver Pipeline Type
tracesmetrics

This says it receives data with a type of metrics, and outputs data with a type of trace.

  • traces: … exporters: [span_metrics]
  • metrics: receivers: [span_metrics]
receivers:
otlp_json_file:
include:
- /fooin.file
start_at: beginning

exporters:
file/a:
path: /fooout.file

file/b:
path: /b.file

debug:
verbosity: detailed # Options: basic, normal, detailed
sampling_initial: 2
sampling_thereafter: 10


connectors:
count:
span_metrics:

processors:
batch:

service:
pipelines:

traces:
receivers: [otlp_json_file]
exporters: [span_metrics]
metrics:
receivers: [span_metrics]

exporters: [file/b,debug]

Configuring opentelemetry, Jaeger, Prometheus, Grafana – 101

The above packages take tracking data or metrics and can display them in dashboards. I found it a struggle to understand how they were configured, as the documentation assumes you are an expert, and I could not find any “starting from zero” documentation.

These packages often run under Docker, which introduces additional complexity.

My mission

My missions was to take the Opentelemetry data from MQ on z/OS and display a summary of the data in Grafana, so I could see “the average transaction time over the last hour was ..”.

I can capture the data and send it down to Opentelemetry running on Ubuntu. I can display it in Jaeger so I know the basics work.

The basics

Configuration is done using YAML. This is a good interface and easy to use. Sub parameters are indented.

There is documentation on Opentelemetry collector configuration.

The configuration breaks down to

  • input definitions ( receivers)
  • output definitions ( exporters)
  • processing

The simplest configuration file for Opentelemetry is

receivers:
otlp_json_file:
include:
- /fooin.file
start_at: beginning
exporters:
file:
path: /fooout.file

service:
pipelines:
traces:
receivers: [otlp_json_file]
exporters: [file]

Within the receivers and exporters you have “driver definitions” (my term). These drivers are like external functions. otlp_json_file is a driver for reading from a json file. Someone has written this (in go). It is not in the default opentelemetry package, so you have to use the package which includes these drivers.

For example the docker definition is

docker run --rm  --name otelcollector \
...
otel/opentelemetry-collector-contrib:latest ...

where the standard package is otel/opentelemetry-collector:latest, without the -contrib.

The parameters for this driver are

     include: /fooin.file
start_at: beginning
  • read the file fooin.file
  • and start at the beginning.

There are many parameters you can specify – for example which code page the data is in. See the code on github.

In a similar way there is a “file driver” which writes data to the file with path: /fooout.file.

You cannot use a random name as a driver – it has to be available in the configuration.

Docker

When running under Docker, there is a level of indirection. In my Docker configuration I have

  -v "$(pwd)/myfoo.file":"/foo.file" 

Where /foo.file mentioned in the configuration file, and this maps to myfoo.file in the current directory. If you are using an output file, create it (use the touch myfoo.file command), and use chmod 777 myfoo.file so the container ( running under the docker userid) can access it.

The processing

There is a section

service:
pipelines:
traces:
receivers: [otlp_json_file]
exporters: [file]

Which says create a pipe line between the input receiver(s) and the output exporters. For trace data read from the device driver otlp_json_file and write it to the device driver file.

This just reads from the input file and writes the output to the output file.

A more complex example

receivers:
otlp_json_file:
include:
- /fooin.file
start_at: beginning

exporters:
file/a:
path: /fooout.file
file/b:
path: /b.file


service:
pipelines:
traces:
receivers: [otlp_json_file]
exporters: [file/a,file/b]

This has two exporters file/a,file/b. In the exporters section, there is still the same device driver file, but there are now two of them file/a and file/b.

With only one definition you could use just file, or you could have a more descriptive definition file/mydata.

Can I just write it to the terminal?

Yes use debug

exporters:
debug:
verbosity: detailed # Options: basic, normal, detailed
...

exporters: [file/b,debug]

This can produce a lot of output, so only use it when there is only a little data.

You can use sampling_initial and sampling_thereafter to display the first few messages, then sample the rest, so you do not get flooded.

Running under docker

Within the docker definition I have code

  -v "$(pwd)/foo.file":"/fooin.file" \
-v "$(pwd)/fooout.json":"/fooout.file" \
-v "$(pwd)/b.json":"/b.file" \

which maps the name in the yaml, eg /fooin.file to the name outside of docker $(pwd)/foo.file, and so the file used by b.file is actually b.json

Useful docker commands

Docker commands

Docker network commands


Monitoring docker

Docker image

High level summary of what’s active. For all containers

docker container ls

Gave (I’ve formatted it to make it easy to read – the data is a very long line)

CONTAINER ID :f6e04a6aabac   
IMAGE :otel/opentelemetry-collector
COMMAND :"/otelcol --config o…"
CREATED :8 minutes ago
STATUS : Up 8 minutes
PORTS: :0.0.0.0:4318->4318/tcp, [::]:4318->4318/tcp, 55679/tcp, 0.0.0.0:9999->4317/tcp, [::]:9999->4317/tcp
NAME :otelcollector

Issue a command in a container

To issue the ls command in the jaeger2 container.

docker exec jaeger2 ls

Create and run an image

docker run -p 4317:4317 -p 8888:8888 -p 9464:9464 –name otel otel/opentelemetry-collector:latest -v /home/colin/otel/otel.yaml:/etc/oteltol/config.yaml

  • This runs otel/opentelemetry-collector:latest (downloading it if necessary)
  • -p map port -p external_port:internal_port
  • -v map volume(file) -v external:internal the otel image uses a file /etc/oteltol/config.yaml. When the program running in the container accesses /etc/oteltol/config.yaml it uses the file /home/colin/otel/otel.yaml in Linux.

Display active images docker ps

Displays all images docker ps -a

colin@ColinNew:~/otel$ docker ps -a
CONTAINER ID IMAGE COMMAND ... NAMES
ee578b476ac1 prom/prometheus "/bin/prometheus --c…" ... prom
0f51508e07a2 otel/opentelemetry...:latest "/otelcol -v /home/c…" ... otel

You can refer to a container using the container id (ee578b476ac1) or the name(prom).

Docker inspect – show the configuration

docker inspect otel

gave

[
{
"Id": "2fb850a85e31125f936eb6994440757be33c509d2c80ef66864f55341219c6a0",
"Created": "2026-06-28T10:33:31.048547624Z",
"Path": "/otelcol",
"Args": [
"--config",
"/etc/otelcol/config.yaml"
],
"State": {
"Status": "exited",
...
"StartedAt": "2026-06-28T10:33:31.151675197Z",
"FinishedAt": "2026-06-28T10:33:35.449264219Z"
},
"Image": "sha256:140cdb56eeea12ebc33bb4f7109fd4eef90391933f8d85b33384fcfe1cf040c4",

Docker network

List active docker networks

docker network ls

gave

NETWORK ID     NAME                  DRIVER    SCOPE
b5bbab0b08f1 bridge bridge local
4e4dc158fa88 host host local
0cfcba5bb0ff none null local
b027e6a0f6ed otel-jaeger-network bridge local

Give details about one network

docker network inspect otel-jaeger-network 

gave

[
{
"Name": "otel-jaeger-network",
"Id": "b027e6a0f6edc5f290b25c10852cf6022cb3dc05ab47c5ec6175910354c1d8ce",
"Created": "2026-06-28T17:32:51.894552966+01:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv4": true,
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"IPRange": "",
"Gateway": "172.18.0.1"
}
]
},
...
"Containers": {
"a979647ba520ccc44ff600dd445b50606a91252fb0850318b823b7b51d3d2329": {
"Name": "otelcollector",
...
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
...

Which ports are in use within a container?

docker  port  otelcollector
4317/tcp -> 0.0.0.0:4317
4317/tcp -> [::]:4317
4318/tcp -> 0.0.0.0:4318
4318/tcp -> [::]:4318

What is my container doing ?

docker  stats   otelcollector

CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
68cf3ad47887 otel... 0.00% 16.24MiB / 27.05GiB 0.06% 85.3kB / 12.3kB 131kB / 0B 14

Images

What images do I have installed?

docker image ls

gave

IMAGE                           ID             DISK USAGE   CONTENT SIZE   EXTRA
cr.jaegertrac...jaeger:2.19.0 ede4864215be 192MB 56.6MB
jaegertracing/all-in-one:... ab6f1a1f0fb4 123MB 37.4MB
otel/opentelemet... 140cdb56eeea 240MB 48.5MB U
prom/prometheus:latest a75c5a35bc21 427MB 118MB

Details of an image

docker image inspect jaegertracing/all-in-one
[
{
"Id": "sha256:ab6f1a1f0fb49ea08bcd19f6b84f6081d0d44b364b6de148e1798eb5816bacac",
"RepoTags": [
"jaegertracing/all-in-one:1.76.0",
"jaegertracing/all-in-one:latest"
],
"RepoDigests": [
"jaegertracing/all-in-one@sha256:ab6f1a1f0fb49ea08bcd19f6b84f6081d0d44b364b6de148e1798eb5816bacac"
],
"Comment": "buildkit.dockerfile.v0",
"Created": "2025-12-03T17:07:31.304698052Z",
"Config": {
"User": "10001",
"ExposedPorts": {
"14250/tcp": {},
"14268/tcp": {},
"16686/tcp": {},
"4317/tcp": {},
"4318/tcp": {},
"9411/tcp": {}
},
...

Delete an image

You want to remove the executable image.

You can remove unneeded image

docker image prune

or to delete an image

docker image ls
docker image rm name

gave

IMAGE                                ID            ...
otel/opentelemetry-collector:latest 140cdb56eeea
prom/prometheus:latest a75c5a35bc21

docker image rm 140cdb56eeea
or
docker image rm otel/opentelemetry-collector:latest

One minute – docker

This is one of the “one minute MVS” series of topics to provide the core of what you need to know on a subject to get started.

The problem

I want to run a self contained application suites on my laptop. There are two versions, old and new. Both use the same IP ports, and configuration files (/etc/myapp/config.yaml). How do I do it?

One solution is to use docker.

Install docker

You can use one of…

sudo snap install docker         # version 29.3.1, or
sudo apt install docker.io # version 29.1.3-0ubuntu3~24.04.2
sudo apt install podman-docker # version 4.9.3+ds1-1ubuntu0.2

If you want to manage your docker sessions from a window, (giving CPU usage etc) you can use docker-desktop.

Docker is container management.

You need to install docker on your machine. I used sudo apt install docker.

My first docker image

docker run --rm --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 5778:5778 \
-p 9411:9411 \
cr.jaegertracing.io/jaegertracing/jaeger:2.19.0

When you run this command

  • the first time, it downloads a prebuilt image cr.jaegertracing.io/jaegertracing/jaeger from a docker site. The second time is uses the previously downloaded image
  • it will download version 2.19.0 (you can specify latest)
  • it is given a name jaeger
  • it also has a number which can be used in commands
  • it maps its ports -p external:internal. In my config file I have endpoint: “0.0.0.0:4317”. This maps to the 4317 on my laptop. I could have a second container and pass parameters -p 5317: 4317. The same config file can be used. To talk to the second container, I need to use port 5317.
  • when it shuts down, the image is removed (–rm)
  • you stop the image using docker stop jaeger. You can restart it with docker start jaeger.
  • you remove the image using docker rm jaeger.

Commands

See docker commands.

Using files within a docker image

The program may use a configuration file for example /etc/myprog/config. You can map this to a file outside of the container.

For example

docker run --rm  --name otelcollector \
--volume "$(pwd)/o1.yaml":/otel-config.yaml \
-v "/mnt/Colin/ssl/ssl2/tempcert.pem":"/server.pem" \
-v "/mnt/Colin/ssl/ssl2/tempcert.key.pem":"/server.key.pem" \
...
otel/opentelemetry-collector --config otel-config.yaml

This says when the application accesses /server.key.pem, this is mapped to file /mnt/Colin/ssl/ssl2/tempcert.key.pem on my laptop.

In my config file I have

receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
tls:
cert_file: server.pem
key_file: server.key.pem

min_version: "1.3" # Enforces TLS 1.3 as the minimum requirement
max_version: "1.3" # Locks maximum version to TLS 1.3
cipher_suites: []
reload_interval: "1h"
http:

Docker to Docker

If you want more than one docker image, for example you want opentelemetry, Jaeger, Prometheus and Grafana to collect and display a dash board. Then you need to consider using docker compose.

I have one docker image with OpenTelemetry (otel), and another with Jaeger, which is used to display information produces by the otel image. I found this article useful

Create a docker network (once)

docker network create otel-jaeger-network

This can be used

docker run --rm  --name jaeger \
...\
--network otel-jaeger-network \
jaegertracing/all-in-one:latest

and

docker run --rm  --name otelcollector \
--volume "$(pwd)/o1.yaml":/otel-config.yaml \
...
--network otel-jaeger-network \
otel/opentelemetry-collector --config otel-config.yaml

The otel config file has

endpoint: "0.0.0.0:4317"

The jaeger has default of localhost:4317 because these share the same network they are each end of a socket.

Docker to docker

The problem

I have one application running in one docker container, and another application in another docker container. How do I get them to talk to each other?

Docker to Docker

I have one docker image with OpenTelemetry (Otel), and another with Jaeger, which is used to display information produces by the Otel image. I found this article useful. You do not need to know what Otel and Jaeger are, you just need to know that data from an external site is sent to Otel, and Otel passes some transformed data to Jaeger.

You need to create a docker network, then have the docker images use this network.

Create a docker network (once)

docker network create otel-jaeger-network

Use it

This can be used

docker run --rm  --name jaeger \
...\
--network otel-jaeger-network \
jaegertracing/all-in-one:latest

and

docker run --rm  --name otelcollector \
--volume "$(pwd)/o1.yaml":/otel-config.yaml \
...
--network otel-jaeger-network \
otel/opentelemetry-collector --config otel-config.yaml

Sending data from z/OS to Otel

A program running on z/OS sends data to my laptop and the data goes to the Otel container.

I’ve configured the Otel container

docker run ... 
--publish 9999:4317

My z/OS code sends data to port 9999 on my laptop. The docker code maps port 9999 to port 4317 within the Otel container.

The Otel config file has

endpoint: "0.0.0.0:4317"

and so the Otel code can process the data sent from z/OS

In practice it would be easier to use port 4317 from z/OS and use

 --publish 4317:4317 

Sending data from Otel to Jaeger

My Otel configuration has

exporters:
otlphttp/jaeger:
endpoint: "http://jaeger2:4318"
tls:
insecure: true

The docker code extracts the value jaeger2 from the endpoint.

This ties up with the Jaeger container definition

docker run --rm  --name jaeger2 ...

The program in the Jaeger container is listening on port 4318, and so the data is sent from the Otel container to the Jaeger container.