{"id":5147,"date":"2023-05-08T15:02:27","date_gmt":"2023-05-08T12:02:27","guid":{"rendered":"https:\/\/bentego.com\/?p=5147"},"modified":"2025-10-20T16:09:19","modified_gmt":"2025-10-20T16:09:19","slug":"apache-spark-structured-streaming-via-docker-compose","status":"publish","type":"post","link":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/","title":{"rendered":"Apache Spark Structured Streaming Via Docker Compose"},"content":{"rendered":"<p id=\"3351\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" style=\"text-align: left;\" data-selectable-paragraph=\"\">Building a data pipeline could be challenging especially when you have to take into account portability, flexibility, scalability etc. To overcome these challenges docker is one the well-known solution. In this article, we are gonna talk about building a data pipeline via docker-compose file.<\/p>\n<p data-selectable-paragraph=\"\"><a href=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/1*pwOjdPB8z5Snj4e6qiX6xg.png\" rel=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/img\/architecture.PNG\"><img fetchpriority=\"high\" decoding=\"async\" class=\"bg oe of c aligncenter\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/1*pwOjdPB8z5Snj4e6qiX6xg.png\" alt=\"\" width=\"700\" height=\"265\" \/><\/a><\/p>\n<pre><strong>0. Installation Processes<\/strong><\/pre>\n<p id=\"a767\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">You are able to install all required components to realize this project using the given steps.<\/p>\n<pre id=\"676d\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>Installation of ROS<\/strong><\/pre>\n<p id=\"71da\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">We won\u2019t address the whole installation process of ROS but you can access all required info from<span style=\"color: #000000;\">\u00a0<span style=\"text-decoration: underline;\"><a style=\"color: #000000; text-decoration: underline;\" href=\"http:\/\/wiki.ros.org\/noetic\/Installation\/Ubuntu\" target=\"_blank\" rel=\"noopener ugc nofollow\">ROS Noetic &amp; Ubuntu 20.04 Installation<\/a>.<\/span><\/span><\/p>\n<pre id=\"02a2\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>Installation of Docker on Ubuntu<\/strong><\/pre>\n<p id=\"4177\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">You can utilize this\u00a0<span style=\"text-decoration: underline; color: #000000;\"><a style=\"color: #000000; text-decoration: underline;\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-install-and-use-docker-on-ubuntu-20-04\" target=\"_blank\" rel=\"noopener ugc nofollow\">URL<\/a><\/span><\/p>\n<pre id=\"e249\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>Installation of Kafka-Python Library used for publishing data received from ROS to Kafka<\/strong><\/pre>\n<p id=\"9d0c\" class=\"mt mu rc mv b mw mx my mz na nb nc nd rd nf ng nh re nj nk nl rf nn no np nq gm bj\" data-selectable-paragraph=\"\"><em class=\"gt\">\u2757 If you haven\u2019t installed<span style=\"text-decoration: underline;\"><span style=\"color: #000000; text-decoration: underline;\">\u00a0<\/span><\/span><\/em><span style=\"text-decoration: underline;\"><span style=\"color: #000000; text-decoration: underline;\"><a class=\"af qy\" style=\"color: #000000; text-decoration: underline;\" href=\"https:\/\/kafka-python.readthedocs.io\/en\/master\/\" target=\"_blank\" rel=\"noopener ugc nofollow\"><em class=\"gt\">kafka-python<\/em><\/a><\/span><\/span><em class=\"gt\"><strong>,<\/strong> use the given command and then run given files.<\/em><\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\">pip install kafka-python\n<\/span><\/pre>\n<\/blockquote>\n<pre id=\"b160\" class=\"pe pf gt be pg ph pi pj pk pl pm pn po pp pq pr ps pt pu pv pw px py pz qa qb bj\"><strong>1. Prepare a robotic simulation environment<\/strong><\/pre>\n<p id=\"5ecc\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\"><span style=\"text-decoration: underline;\"><span style=\"color: #000000; text-decoration: underline;\"><a style=\"color: #000000; text-decoration: underline;\" href=\"http:\/\/wiki.ros.org\/\" target=\"_blank\" rel=\"noopener ugc nofollow\">ROS (Robot Operating System)<\/a><\/span><\/span>allows us to design a robotic environment. In this project, we will use ROS as a data provider. \u201codom\u201d is a type of message that represents the position of a vehicle. We utilize the given code that generates arbitrary \u201codom\u201d data and publishes them.<\/p>\n<blockquote>\n<pre>#!\/usr\/bin\/env python3\nimport math\nfrom math import sin, cos, pi\nimport rospy\nimport tf\nfrom nav_msgs.msg import Odometry\nfrom geometry_msgs.msg import Point, Pose, Quaternion, Twist, Vector3\nrospy.init_node('odometry_publisher')\nodom_pub = rospy.Publisher(\"odom\", Odometry, queue_size=50)\nodom_broadcaster = tf.TransformBroadcaster()\nx = 0.0\ny = 0.0\nth = 0.0\nvx = 0.1\nvy = -0.1\nvth = 0.1\ncurrent_time = rospy.Time.now()\nlast_time = rospy.Time.now()\nr = rospy.Rate(1.0)\nwhile not rospy.is_shutdown():\n    current_time = rospy.Time.now()\n    # compute odometry in a typical way given the velocities of the robot\n    dt = (current_time - last_time).to_sec()\n    delta_x = (vx * cos(th) - vy * sin(th)) * dt\n    delta_y = (vx * sin(th) + vy * cos(th)) * dt\n    delta_th = vth * dt\n    x += delta_x\n    y += delta_y\n    th += delta_th\n    # since all odometry is 6DOF we'll need a quaternion created from yaw\n    odom_quat = tf.transformations.quaternion_from_euler(0, 0, th)\n    # first, we'll publish the transform over tf\n    odom_broadcaster.sendTransform(\n        (x, y, 0.),\n        odom_quat,\n        current_time,\n        \"base_link\",\n        \"odom\"\n    )\n    # next, we'll publish the odometry message over ROS\n    odom = Odometry()\n    odom.header.stamp = current_time\n    odom.header.frame_id = \"odom\"\n    # set the position\n    odom.pose.pose = Pose(Point(x, y, 0.), Quaternion(*odom_quat))\n    # set the velocity\n    odom.child_frame_id = \"base_link\"\n    odom.twist.twist = Twist(Vector3(vx, vy, 0), Vector3(0, 0, vth))\n    # publish the message\n    odom_pub.publish(odom)\n    last_time = current_time\n    r.sleep()\n\n<\/pre>\n<\/blockquote>\n<pre id=\"465a\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>Run the given code and analysis the data we will use<\/strong><\/pre>\n<p id=\"25f5\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">This script publishes odometry data with ROS \u201codom\u201d topic. So, we can see the published data with the given command:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"8655\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># run the script environment\npython3 odomPublisher.py<\/span><span id=\"7486\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># check the topic to see data\nrostopic echo \/odom<\/span><\/pre>\n<\/blockquote>\n<p id=\"9fac\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">In this use case, we will just interested the given part of the data:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"bb5c\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\">position: \n      x: -2.000055643960576\n      y: -0.4997879642933192\n      z: -0.0010013932644100873\n    orientation: \n      x: -1.3486164084605e-05\n      y: 0.0038530870521455017\n      z: 0.0016676819550213058\n      w: 0.9999911861487526<\/span><\/pre>\n<\/blockquote>\n<pre id=\"d64c\" class=\"pe pf gt be pg ph pi pj pk pl pm pn po pp pq pr ps pt pu pv pw px py pz qa qb bj\"><strong>2. Prepare Docker-Compose File<\/strong><\/pre>\n<p id=\"919b\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">First of all, we generated a network called datapipeline for the architecture. The architecture consists of 4 services and each has a static IP address and uses the default port as the given below:<\/p>\n<blockquote><p>Spark: 172.18.0.2<br \/>\nZookeeper: 172.18.0.3<br \/>\nKafka: 172.18.0.4<br \/>\nCassandra : 172.18.0.5<\/p><\/blockquote>\n<p id=\"f3d7\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">We use \u201cvolumes\u201d to import our scripts to containers.<\/p>\n<p id=\"7516\" class=\"mt mu rc mv b mw mx my mz na nb nc nd rd nf ng nh re nj nk nl rf nn no np nq gm bj\" data-selectable-paragraph=\"\"><em class=\"gt\">\u2757 You have to implement \u201c ..\/streamingProje:\/home\u201d part for your system.<\/em><\/p>\n<p id=\"cd2c\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">You can access the<span style=\"color: #000000;\"> <a style=\"color: #000000;\" href=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/docker-compose.yml\" target=\"_blank\" rel=\"noopener ugc nofollow\"><span style=\"text-decoration: underline;\">docker-compose<\/span><\/a>\u00a0\u00a0<\/span>and replace your configs.<\/p>\n<figure class=\"nu nv nw nx ny nz\">\n<div class=\"ca je l dj\">\n<div class=\"afm rp l\">\n<blockquote>\n<pre>version: '3'\nnetworks:\n    datapipeline:\n        driver: bridge\n        ipam:\n            driver: default\n            config:\n                - subnet: \"172.18.0.0\/16\"\n\nservices:  \n  spark:\n    image: docker.io\/bitnami\/spark:3\n    container_name: spark_master\n    hostname: spark_master\n    user: root\n    environment:\n      - SPARK_MODE=master\n      - SPARK_RPC_AUTHENTICATION_ENABLED=no\n      - SPARK_RPC_ENCRYPTION_ENABLED=no\n      - SPARK_LOCAL_STORAGE_ENCRYPTION_ENABLED=no\n      - SPARK_SSL_ENABLED=no\n    ports:\n      - '8080:8080'\n    volumes:\n      - ..\/streamingProje:\/home\n      - \/opt\/spark\/conf\/spark-defaults.conf:\/opt\/bitnami\/spark\/conf\/spark-defaults.conf\n      - \/opt\/spark\/jars:\/opt\/bitnami\/spark\/ivy:z\n    networks:\n      datapipeline:\n        ipv4_address: 172.18.0.2\n  zookeeper:\n    image: 'bitnami\/zookeeper:latest'\n    container_name: zookeeper\n    hostname: zookeeper\n    ports:\n      - '2181:2181'\n    environment:\n      - ALLOW_ANONYMOUS_LOGIN=yes\n    networks:\n      datapipeline:\n        ipv4_address: 172.18.0.3\n  kafka:\n    image: 'bitnami\/kafka:latest'\n    container_name: kafka\n    hostname: kafka\n    ports:\n      - '9092:9092'\n    environment:\n      - KAFKA_BROKER_ID=1\n      - KAFKA_CFG_LISTENERS=PLAINTEXT:\/\/172.18.0.4:9092\n      - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT:\/\/172.18.0.4:9092\n      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181\n      - ALLOW_PLAINTEXT_LISTENER=yes\n    depends_on:\n      - zookeeper\n    volumes:\n      - ..\/streamingProje:\/home\n    networks:\n      datapipeline:\n        ipv4_address: 172.18.0.4\n  cassandra:\n    image: 'bitnami\/cassandra:latest'\n    container_name: cassandra\n    hostname: cassandra\n    ports:\n      - '9042:9042'\n    volumes:\n      - ..\/streamingProje:\/home\n    networks:\n      datapipeline:\n        ipv4_address: 172.18.0.5<\/pre>\n<\/blockquote>\n<\/div>\n<\/div>\n<\/figure>\n<pre id=\"dad8\" class=\"pe pf gt be pg ph pi pj pk pl pm pn po pp pq pr ps pt pu pv pw px py pz qa qb bj\"><strong>3. Running docker-compose file<\/strong><\/pre>\n<p id=\"c2c0\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">Open your workspace folder which includes all files provided and run the given command as below.<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"a708\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># run docker-compose file\ndocker-compose up<\/span><\/pre>\n<\/blockquote>\n<p id=\"4c23\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">You will have a view like:<\/p>\n<figure class=\"nu nv nw nx ny nz nr ns paragraph-image\">\n<div class=\"oa ob dj oc bg od\" tabindex=\"0\" role=\"button\">\n<div class=\"nr ns rz\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*9IBn8vlLxaV2l2Rp.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*9IBn8vlLxaV2l2Rp.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*9IBn8vlLxaV2l2Rp.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*9IBn8vlLxaV2l2Rp.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*9IBn8vlLxaV2l2Rp.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*9IBn8vlLxaV2l2Rp.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*9IBn8vlLxaV2l2Rp.jpg 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" \/><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*9IBn8vlLxaV2l2Rp.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*9IBn8vlLxaV2l2Rp.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*9IBn8vlLxaV2l2Rp.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*9IBn8vlLxaV2l2Rp.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*9IBn8vlLxaV2l2Rp.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*9IBn8vlLxaV2l2Rp.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*9IBn8vlLxaV2l2Rp.jpg 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\" \/><\/picture><a href=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/0*9IBn8vlLxaV2l2Rp.jpg\" rel=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/img\/docker_ps_output.jpg\"><img decoding=\"async\" class=\"bg oe of c aligncenter\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/0*9IBn8vlLxaV2l2Rp.jpg\" alt=\"\" width=\"700\" height=\"54\" \/><\/a><\/div>\n<\/div>\n<\/figure>\n<p id=\"4fa9\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">After all, container is running, you can set up your environment.<\/p>\n<pre id=\"a52f\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>Prepare Kafka for Use Case<\/strong><\/pre>\n<p id=\"5ed9\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">First of all, we will create a new Kafka topic namely\u00a0<em class=\"rc\">odometry<\/em>\u00a0for ROS odom data using the given commands:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"99c2\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># Execute kafka container with container id given above\ndocker exec -it 1c31511ce206 bash<\/span><span id=\"e7cb\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># Create Kafka \"odometry\" topic for ROS odom data\nkafka$ bin\/kafka-topics.sh --create --topic odom --partitions 1 --replication-factor 1 \n-bootstrap-server localhost:9092<\/span><\/pre>\n<\/blockquote>\n<pre id=\"07e4\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>Check Kafka setup through Zookeeper<\/strong><\/pre>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"0cb8\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># Execute zookeeper container with container id given above\ndocker exec -it 1c31511ce206 bash<\/span><span id=\"7216\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># run command\nopt\/bitnami\/zookeeper\/bin\/zkCli.sh -server localhost:2181 \n<\/span><span id=\"5c4f\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># list all brokers topic\nls \/brokers\/topics<\/span><\/pre>\n<\/blockquote>\n<p id=\"0f0a\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">You will have a view like:<\/p>\n<figure class=\"nu nv nw nx ny nz nr ns paragraph-image\">\n<div class=\"nr ns sa\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*ZI46o4MOHIPOhNFu.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*ZI46o4MOHIPOhNFu.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*ZI46o4MOHIPOhNFu.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*ZI46o4MOHIPOhNFu.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*ZI46o4MOHIPOhNFu.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*ZI46o4MOHIPOhNFu.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:988\/format:webp\/0*ZI46o4MOHIPOhNFu.jpg 988w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 494px\" \/><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*ZI46o4MOHIPOhNFu.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*ZI46o4MOHIPOhNFu.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*ZI46o4MOHIPOhNFu.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*ZI46o4MOHIPOhNFu.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*ZI46o4MOHIPOhNFu.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*ZI46o4MOHIPOhNFu.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:988\/0*ZI46o4MOHIPOhNFu.jpg 988w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 494px\" data-testid=\"og\" \/><\/picture><a href=\"https:\/\/miro.medium.com\/v2\/resize:fit:741\/0*ZI46o4MOHIPOhNFu.jpg\" rel=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/img\/zookeeper.jpg\"><img decoding=\"async\" class=\"bg oe of c aligncenter\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:741\/0*ZI46o4MOHIPOhNFu.jpg\" alt=\"\" width=\"494\" height=\"65\" \/><\/a><\/div>\n<\/figure>\n<pre id=\"ad00\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>Prepare Cassandra for Use Case<\/strong><\/pre>\n<p id=\"426e\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">Initially, we will create a\u00a0<em class=\"rc\">keyspace<\/em>\u00a0and then a\u00a0<em class=\"rc\">topic<\/em>\u00a0in it using the given command:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"3a1c\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># Execute cassandra container with container id given above\ndocker exec -it 1c31511ce206 bash\n\n<\/span><span id=\"553a\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># Open the cqlsh\ncqlsh -u cassandra -p cassandra<\/span><span id=\"0721\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># Run the command to create 'ros' keyspace\n\ncqlsh&gt; CREATE KEYSPACE ros WITH replication = {\n'class':'SimpleStrategy', \n'replication_factor' : 1};\n\n<\/span><span id=\"b9b0\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># Then, run the command to create 'odometry' topic in 'ros'\ncqlsh&gt; create table ros.odometry(\n        id int primary key, \n        posex float,\n        posey float,\n        posez float,\n        orientx float,\n        orienty float,\n        orientz float,\n        orientw float);<\/span><span id=\"b790\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># Check your setup is correct\ncqlsh&gt; DESCRIBE ros.odometry<\/span><\/pre>\n<\/blockquote>\n<p id=\"d686\" class=\"mt mu rc mv b mw mx my mz na nb nc nd rd nf ng nh re nj nk nl rf nn no np nq gm bj\" data-selectable-paragraph=\"\"><em class=\"gt\">\u26a0\ufe0f The content of topic has to be the same as Spark schema: Be very careful here!<\/em><\/p>\n<pre id=\"e21f\" class=\"pe pf gt be pg ph pi pj pk pl pm pn po pp pq pr ps pt pu pv pw px py pz qa qb bj\"><strong>4. Prepare Apache Spark structured streaming<\/strong><\/pre>\n<p id=\"a804\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">You are able to write analysis results to either console or Cassandra.<\/p>\n<pre id=\"05ba\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>(First Way) Prepare Apache Spark Structured Streaming Pipeline Kafka to Cassandra<\/strong><\/pre>\n<p id=\"1f54\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">We will write a streaming script that reads\u00a0<em class=\"rc\">odometry<\/em>\u00a0topics from Kafka, analyzes them, and then write results to Cassandra. We will use<span style=\"color: #000000;\">\u00a0<span style=\"text-decoration: underline;\"><a style=\"color: #000000; text-decoration: underline;\" href=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/streamingKafka2Cassandra.py\" target=\"_blank\" rel=\"noopener ugc nofollow\">streamingKafka2Cassandra.py<\/a><\/span>\u00a0<\/span>to do it.<\/p>\n<p id=\"abeb\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">First of all, we create a schema the same as we already defined in Cassandra.<\/p>\n<p id=\"5f95\" class=\"mt mu rc mv b mw mx my mz na nb nc nd rd nf ng nh re nj nk nl rf nn no np nq gm bj\" data-selectable-paragraph=\"\"><em class=\"gt\">\u26a0\ufe0f The content of schema has to be the same as Casssandra table: Be very careful here!<\/em><\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"5265\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\">odometrySchema = StructType([\n                StructField(\"id\",IntegerType(),False),\n                StructField(\"posex\",FloatType(),False),\n                StructField(\"posey\",FloatType(),False),\n                StructField(\"posez\",FloatType(),False),\n                StructField(\"orientx\",FloatType(),False),\n                StructField(\"orienty\",FloatType(),False),\n                StructField(\"orientz\",FloatType(),False),\n                StructField(\"orientw\",FloatType(),False)\n            ])<\/span><\/pre>\n<\/blockquote>\n<p id=\"f3a8\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">Then, we create a Spark Session and specify our config here:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"daf6\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\">spark = SparkSession \\\n    .builder \\\n    .appName(\"SparkStructuredStreaming\") \\\n    .config(\"spark.cassandra.connection.host\",\"172.18.0.5\")\\\n    .config(\"spark.cassandra.connection.port\",\"9042\")\\\n    .config(\"spark.cassandra.auth.username\",\"cassandra\")\\\n    .config(\"spark.cassandra.auth.password\",\"cassandra\")\\\n    .config(\"spark.driver.host\", \"localhost\")\\\n    .getOrCreate()<\/span><\/pre>\n<\/blockquote>\n<p id=\"0507\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">In order to read the Kafka stream, we use readStream() and specify Kafka configurations as the given below:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"2270\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\">df = spark \\\n  .readStream \\\n  .format(\"kafka\") \\\n  .option(\"kafka.bootstrap.servers\", \"172.18.0.4:9092\") \\\n  .option(\"subscribe\", \"rosmsgs\") \\\n  .option(\"delimeter\",\",\") \\\n  .option(\"startingOffsets\", \"earliest\") \\\n  .load()<\/span><\/pre>\n<\/blockquote>\n<p id=\"d8bc\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">Since Kafka sends data as binary, first we need to convert the binary value to String using selectExpr() as the given below:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"2144\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\">df1 = df.selectExpr(\"CAST(value AS STRING)\").select(from_json(col(\"value\"),\nodometrySchema).alias(\"data\")).select(\"data.*\")\ndf1.printSchema()<\/span><\/pre>\n<\/blockquote>\n<p id=\"6cc9\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">Although Apache Spark isn\u2019t capable of directly writing stream data to Cassandra yet (using writeStream()), we can do it by use foreachBatch() as the given below:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"dacc\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\">def writeToCassandra(writeDF, _):\n  writeDF.write \\\n    .format(\"org.apache.spark.sql.cassandra\")\\\n    .mode('append')\\\n    .options(table=\"odometry\", keyspace=\"ros\")\\\n    .save()\n\n<\/span><span id=\"c34d\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\">df1.writeStream \\\n    .foreachBatch(writeToCassandra) \\\n    .outputMode(\"update\") \\\n    .start()\\\n    .awaitTermination()\ndf1.show()<\/span><\/pre>\n<\/blockquote>\n<p id=\"11db\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">Finally, we got the given script\u00a0<span style=\"text-decoration: underline;\"><span style=\"color: #000000;\"><a style=\"color: #000000; text-decoration: underline;\" href=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/streamingKafka2Cassandra.py\" target=\"_blank\" rel=\"noopener ugc nofollow\">streamingKafka2Cassandra.py<\/a><\/span><\/span><strong><em>:<\/em><\/strong><\/p>\n<figure class=\"nu nv nw nx ny nz\">\n<div class=\"ca je l dj\">\n<div class=\"afo rp l\">\n<blockquote>\n<pre>from pyspark.sql import SparkSession\nfrom pyspark.sql.types import StructType,StructField,FloatType,IntegerType\nfrom pyspark.sql.functions import from_json,col\nodometrySchema = StructType([\n                StructField(\"id\",IntegerType(),False),\n                StructField(\"posex\",FloatType(),False),\n                StructField(\"posey\",FloatType(),False),\n                StructField(\"posez\",FloatType(),False),\n                StructField(\"orientx\",FloatType(),False),\n                StructField(\"orienty\",FloatType(),False),\n                StructField(\"orientz\",FloatType(),False),\n                StructField(\"orientw\",FloatType(),False)\n            ])\nspark = SparkSession \\\n    .builder \\\n    .appName(\"SparkStructuredStreaming\") \\\n    .config(\"spark.cassandra.connection.host\",\"172.18.0.5\")\\\n    .config(\"spark.cassandra.connection.port\",\"9042\")\\\n    .config(\"spark.cassandra.auth.username\",\"cassandra\")\\\n    .config(\"spark.cassandra.auth.password\",\"cassandra\")\\\n    .config(\"spark.driver.host\", \"localhost\")\\\n    .getOrCreate()\nspark.sparkContext.setLogLevel(\"ERROR\")\n\ndf = spark \\\n  .readStream \\\n  .format(\"kafka\") \\\n  .option(\"kafka.bootstrap.servers\", \"172.18.0.4:9092\") \\\n  .option(\"subscribe\", \"rosmsgs\") \\\n  .option(\"delimeter\",\",\") \\\n  .option(\"startingOffsets\", \"earliest\") \\\n  .load() \ndf.printSchema()\ndf1 = df.selectExpr(\"CAST(value AS STRING)\").select(from_json(col(\"value\"),\nodometrySchema).alias(\"data\")).select(\"data.*\")\ndf1.printSchema()\n\ndef writeToCassandra(writeDF, _):\n  writeDF.write \\\n    .format(\"org.apache.spark.sql.cassandra\")\\\n    .mode('append')\\\n    .options(table=\"odometry\", keyspace=\"ros\")\\\n    .save()\ndf1.writeStream \\\n    .foreachBatch(writeToCassandra) \\\n    .outputMode(\"update\") \\\n    .start()\\\n    .awaitTermination()<\/pre>\n<\/blockquote>\n<\/div>\n<\/div>\n<\/figure>\n<pre id=\"fb87\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>(Second Way) Prepare Apache Spark Structured Streaming Pipeline Kafka to Console<\/strong><\/pre>\n<p id=\"39cc\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">There are a few differences between writing to the console and writing to Cassandra. We directly write stream to console. With writeStream() we can write stream data directly to the console.<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"ed01\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\">df1.writeStream \\\n  .outputMode(\"update\") \\\n  .format(\"console\") \\\n  .option(\"truncate\", False) \\\n  .start() \\\n  .awaitTermination()<\/span><\/pre>\n<\/blockquote>\n<p id=\"23d8\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">The rest of the process takes place in the same way as the previous one. Finally, we got the given script\u00a0<span style=\"text-decoration: underline;\"><span style=\"color: #000000;\"><a style=\"color: #000000; text-decoration: underline;\" href=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/streamingKafka2Console.py\" target=\"_blank\" rel=\"noopener ugc nofollow\">streamingKafka2Console.py<\/a><\/span><\/span><em><strong>:<\/strong><\/em><\/p>\n<figure class=\"nu nv nw nx ny nz\">\n<div class=\"ca je l dj\">\n<div class=\"afp rp l\">\n<blockquote>\n<pre>from pyspark.sql import SparkSession\nfrom pyspark.sql.types import StructType,StructField,IntegerType,FloatType\nfrom pyspark.sql.functions import split,from_json,col\nodometrySchema = StructType([\n                StructField(\"id\",IntegerType(),False),\n                StructField(\"posex\",FloatType(),False),\n                StructField(\"posey\",FloatType(),False),\n                StructField(\"posez\",FloatType(),False),\n                StructField(\"orientx\",FloatType(),False),\n                StructField(\"orienty\",FloatType(),False),\n                StructField(\"orientz\",FloatType(),False),\n                StructField(\"orientw\",FloatType(),False)\n            ])\nspark = SparkSession \\\n    .builder \\\n    .appName(\"SSKafka\") \\\n    .config(\"spark.driver.host\", \"localhost\")\\\n    .getOrCreate()\nspark.sparkContext.setLogLevel(\"ERROR\")\ndf = spark \\\n  .readStream \\\n  .format(\"kafka\") \\\n  .option(\"kafka.bootstrap.servers\", \"172.18.0.4:9092\") \\\n  .option(\"subscribe\", \"rosmsgs\") \\\n  .option(\"delimeter\",\",\") \\\n  .option(\"startingOffsets\", \"earliest\") \\\n  .load() \ndf.printSchema()\ndf1 = df.selectExpr(\"CAST(value AS STRING)\").select(from_json(col(\"value\"),\nodometrySchema).alias(\"data\")).select(\"data.*\")\ndf1.printSchema()\ndf1.writeStream \\\n  .outputMode(\"update\") \\\n  .format(\"console\") \\\n  .option(\"truncate\", False) \\\n  .start() \\\n  .awaitTermination()<\/pre>\n<\/blockquote>\n<\/div>\n<\/div>\n<\/figure>\n<pre id=\"dca7\" class=\"pe pf gt be pg ph pi pj pk pl pm pn po pp pq pr ps pt pu pv pw px py pz qa qb bj\"><strong>5. Demonstration &amp; Results<\/strong><\/pre>\n<p id=\"2825\" class=\"pw-post-body-paragraph mt mu gt mv b mw qc my mz na qd nc nd ne qe ng nh ni qf nk nl nm qg no np nq gm bj\" data-selectable-paragraph=\"\">If you are sure that all preparations are done, you can start a demo. You have to follow the given steps.<\/p>\n<pre id=\"698f\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>Start ROS and publish odom data to Kafka.<\/strong><\/pre>\n<ul class=\"\">\n<li id=\"d325\" class=\"mt mu gt mv b mw qc my mz na qd nc nd rd qe ng nh re qf nk nl rf qg no np nq rr rs rt bj\" data-selectable-paragraph=\"\">roscore : starts ROS master<\/li>\n<li id=\"d23d\" class=\"mt mu gt mv b mw ru my mz na rv nc nd rd rw ng nh re rx nk nl rf ry no np nq rr rs rt bj\" data-selectable-paragraph=\"\">odomPublisher.py : generates random odom data and publishes them along network<\/li>\n<li id=\"43f6\" class=\"mt mu gt mv b mw ru my mz na rv nc nd rd rw ng nh re rx nk nl rf ry no np nq rr rs rt bj\" data-selectable-paragraph=\"\">ros2Kafka.py : subscribes odom topic and writes odom data into kafka container<\/li>\n<\/ul>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"6077\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># these all are implemented in your local pc\n# open a terminal and start roscore\n$ roscore<\/span><span id=\"6ccb\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># open another terminal and run odomPublisher.py\n$ python3 odomPublisher.py<\/span><\/pre>\n<\/blockquote>\n<figure class=\"nu nv nw nx ny nz nr ns paragraph-image\">\n<div class=\"oa ob dj oc bg od\" tabindex=\"0\" role=\"button\">\n<div class=\"nr ns rz\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*hsHsicvJZ6f4Vc4E.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*hsHsicvJZ6f4Vc4E.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*hsHsicvJZ6f4Vc4E.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*hsHsicvJZ6f4Vc4E.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*hsHsicvJZ6f4Vc4E.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*hsHsicvJZ6f4Vc4E.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*hsHsicvJZ6f4Vc4E.jpg 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" \/><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*hsHsicvJZ6f4Vc4E.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*hsHsicvJZ6f4Vc4E.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*hsHsicvJZ6f4Vc4E.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*hsHsicvJZ6f4Vc4E.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*hsHsicvJZ6f4Vc4E.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*hsHsicvJZ6f4Vc4E.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*hsHsicvJZ6f4Vc4E.jpg 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\" \/><\/picture><a href=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/0*hsHsicvJZ6f4Vc4E.jpg\" rel=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/img\/odomPublisher.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"bg oe of c aligncenter\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/0*hsHsicvJZ6f4Vc4E.jpg\" alt=\"\" width=\"700\" height=\"186\" \/><\/a><\/div>\n<\/div>\n<\/figure>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"5115\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># open another terminal and run ros2Kafka.py\n$ python3 ros2Kafka.py<\/span><\/pre>\n<\/blockquote>\n<figure class=\"nu nv nw nx ny nz nr ns paragraph-image\">\n<div class=\"oa ob dj oc bg od\" tabindex=\"0\" role=\"button\">\n<div class=\"nr ns rz\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*TBQq5u0G8V-5DNYe.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*TBQq5u0G8V-5DNYe.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*TBQq5u0G8V-5DNYe.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*TBQq5u0G8V-5DNYe.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*TBQq5u0G8V-5DNYe.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*TBQq5u0G8V-5DNYe.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*TBQq5u0G8V-5DNYe.jpg 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" \/><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*TBQq5u0G8V-5DNYe.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*TBQq5u0G8V-5DNYe.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*TBQq5u0G8V-5DNYe.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*TBQq5u0G8V-5DNYe.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*TBQq5u0G8V-5DNYe.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*TBQq5u0G8V-5DNYe.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*TBQq5u0G8V-5DNYe.jpg 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\" \/><\/picture><a href=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/0*TBQq5u0G8V-5DNYe.jpg\" rel=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/img\/ros2kafka.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"bg oe of c aligncenter\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/0*TBQq5u0G8V-5DNYe.jpg\" alt=\"\" width=\"700\" height=\"248\" \/><\/a><\/div>\n<\/div>\n<\/figure>\n<pre id=\"7a1f\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>(Option-1) Start Streaming to Console<\/strong><\/pre>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"f7cc\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># Execute spark container with container id given above\ndocker exec -it e3080e48085c bash<\/span><span id=\"1b0e\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># go to \/home and run given command\nspark-submit --packages \norg.apache.spark:spark-sql-kafka-0-10_2.12:3.0.0 streamingKafka2Console.py<\/span><\/pre>\n<\/blockquote>\n<pre id=\"0ce2\" class=\"qh pf gt be pg qi qj qk pk ql qm qn po ne qo qp qq ni qr qs qt nm qu qv qw qx bj\"><strong>(Option-2) Start Streaming to Cassandra<\/strong><\/pre>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"bfad\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># Execute spark container with container id given above\ndocker exec -it e3080e48085c bash<\/span><span id=\"1bcd\" class=\"qh pf gt rh b it rq rm l jc rn\" data-selectable-paragraph=\"\"># go to \/home and run given command\nspark-submit --packages \norg.apache.spark:spark-sql-kafka-0-10_2.12:3.0.0,com.datastax.spark:spark-cassandra-connector_2.12:3.0.0 streamingKafka2Console.py<\/span><\/pre>\n<\/blockquote>\n<p id=\"e42b\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">After the spark job is started, you can see the schema on the screen.<\/p>\n<figure class=\"nu nv nw nx ny nz nr ns paragraph-image\">\n<div class=\"nr ns sb\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*kTYKsSrDtNXPQHO4.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*kTYKsSrDtNXPQHO4.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*kTYKsSrDtNXPQHO4.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*kTYKsSrDtNXPQHO4.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*kTYKsSrDtNXPQHO4.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*kTYKsSrDtNXPQHO4.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:814\/format:webp\/0*kTYKsSrDtNXPQHO4.jpg 814w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 407px\" \/><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*kTYKsSrDtNXPQHO4.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*kTYKsSrDtNXPQHO4.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*kTYKsSrDtNXPQHO4.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*kTYKsSrDtNXPQHO4.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*kTYKsSrDtNXPQHO4.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*kTYKsSrDtNXPQHO4.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:814\/0*kTYKsSrDtNXPQHO4.jpg 814w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 407px\" data-testid=\"og\" \/><\/picture><a href=\"https:\/\/miro.medium.com\/v2\/resize:fit:611\/0*kTYKsSrDtNXPQHO4.jpg\" rel=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/img\/schema.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"bg oe of c aligncenter\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:611\/0*kTYKsSrDtNXPQHO4.jpg\" alt=\"\" width=\"407\" height=\"320\" \/><\/a><\/div>\n<\/figure>\n<p id=\"5793\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">If you run option-1, you will have a view as the given below on your terminal screen.<\/p>\n<figure class=\"nu nv nw nx ny nz nr ns paragraph-image\">\n<div class=\"nr ns sc\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*6atvMTX0iIoGWbDO.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*6atvMTX0iIoGWbDO.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*6atvMTX0iIoGWbDO.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*6atvMTX0iIoGWbDO.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*6atvMTX0iIoGWbDO.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*6atvMTX0iIoGWbDO.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1148\/format:webp\/0*6atvMTX0iIoGWbDO.jpg 1148w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 574px\" \/><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*6atvMTX0iIoGWbDO.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*6atvMTX0iIoGWbDO.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*6atvMTX0iIoGWbDO.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*6atvMTX0iIoGWbDO.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*6atvMTX0iIoGWbDO.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*6atvMTX0iIoGWbDO.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1148\/0*6atvMTX0iIoGWbDO.jpg 1148w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 574px\" data-testid=\"og\" \/><\/picture><a href=\"https:\/\/miro.medium.com\/v2\/resize:fit:861\/0*6atvMTX0iIoGWbDO.jpg\" rel=\"https:\/\/github.com\/zekeriyyaa\/Apache-Spark-Structured-Streaming-Via-Docker-Compose\/blob\/main\/img\/streaming.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"bg oe of c aligncenter\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:861\/0*6atvMTX0iIoGWbDO.jpg\" alt=\"\" width=\"574\" height=\"152\" \/><\/a><\/div>\n<\/figure>\n<p id=\"5b71\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">After all the process is done, we got the data in our Cassandra table as the given below:<\/p>\n<p id=\"0ed9\" class=\"pw-post-body-paragraph mt mu gt mv b mw mx my mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq gm bj\" data-selectable-paragraph=\"\">You can query the given command to see your table:<\/p>\n<blockquote>\n<pre class=\"nu nv nw nx ny rg rh ri rj ax rk bj\"><span id=\"3265\" class=\"qh pf gt rh b it rl rm l jc rn\" data-selectable-paragraph=\"\"># Open the cqlsh \ncqlsh\n# Then write select query to see content of the table\ncqlsh&gt; select * from ros.odometry<\/span><\/pre>\n<\/blockquote>\n<figure class=\"nu nv nw nx ny nz nr ns paragraph-image\">\n<div class=\"oa ob dj oc bg od\" tabindex=\"0\" role=\"button\">\n<div class=\"nr ns sd\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*SV32mVJEjjZsxP2v.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*SV32mVJEjjZsxP2v.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*SV32mVJEjjZsxP2v.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*SV32mVJEjjZsxP2v.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*SV32mVJEjjZsxP2v.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*SV32mVJEjjZsxP2v.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*SV32mVJEjjZsxP2v.jpg 1400w\" type=\"image\/webp\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" \/><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*SV32mVJEjjZsxP2v.jpg 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*SV32mVJEjjZsxP2v.jpg 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*SV32mVJEjjZsxP2v.jpg 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*SV32mVJEjjZsxP2v.jpg 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*SV32mVJEjjZsxP2v.jpg 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*SV32mVJEjjZsxP2v.jpg 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*SV32mVJEjjZsxP2v.jpg 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" data-testid=\"og\" \/><\/picture><a href=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/0*SV32mVJEjjZsxP2v.jpg\" rel=\"ng-Via-Docker-Compose\/blob\/main\/img\/cassandra.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"bg oe of c aligncenter\" role=\"presentation\" src=\"https:\/\/miro.medium.com\/v2\/resize:fit:1050\/0*SV32mVJEjjZsxP2v.jpg\" alt=\"\" width=\"700\" height=\"611\" \/><\/a><\/div>\n<\/div>\n<\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Building a data pipeline could be challenging especially when you have to take into account portability, flexibility, scalability etc. To overcome these [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1013,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[81,23,55],"class_list":["post-5147","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-cassandra","tag-kafka","tag-spark"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Apache Spark Structured Streaming Via Docker Compose - Bentego<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Apache Spark Structured Streaming Via Docker Compose - Bentego\" \/>\n<meta property=\"og:description\" content=\"Building a data pipeline could be challenging especially when you have to take into account portability, flexibility, scalability etc. To overcome these [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/\" \/>\n<meta property=\"og:site_name\" content=\"Bentego\" \/>\n<meta property=\"article:published_time\" content=\"2023-05-08T12:02:27+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-10-20T16:09:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/bentego.com\/wp-content\/uploads\/2025\/06\/Frame-85__.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2400\" \/>\n\t<meta property=\"og:image:height\" content=\"1600\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Bentego\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Bentego\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/\"},\"author\":{\"name\":\"Bentego\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#\\\/schema\\\/person\\\/0348418b7b0cbca83fdd7a899d54821e\"},\"headline\":\"Apache Spark Structured Streaming Via Docker Compose\",\"datePublished\":\"2023-05-08T12:02:27+00:00\",\"dateModified\":\"2025-10-20T16:09:19+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/\"},\"wordCount\":670,\"publisher\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/bentego.com\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/Frame-85__.png\",\"keywords\":[\"cassandra\",\"kafka\",\"spark\"],\"articleSection\":[\"Blog\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/\",\"url\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/\",\"name\":\"Apache Spark Structured Streaming Via Docker Compose - Bentego\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/bentego.com\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/Frame-85__.png\",\"datePublished\":\"2023-05-08T12:02:27+00:00\",\"dateModified\":\"2025-10-20T16:09:19+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/#primaryimage\",\"url\":\"https:\\\/\\\/bentego.com\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/Frame-85__.png\",\"contentUrl\":\"https:\\\/\\\/bentego.com\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/Frame-85__.png\",\"width\":2400,\"height\":1600},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/apache-spark-structured-streaming-via-docker-compose\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/bentego.com\\\/en\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Apache Spark Structured Streaming Via Docker Compose\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/bentego.com\\\/en\\\/\",\"name\":\"Bentego\",\"description\":\"Turning data into enterprise value\",\"publisher\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/bentego.com\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#organization\",\"name\":\"Bentego\",\"url\":\"https:\\\/\\\/bentego.com\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/bentego.com\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/logo-bentego.svg\",\"contentUrl\":\"https:\\\/\\\/bentego.com\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/logo-bentego.svg\",\"width\":433,\"height\":109,\"caption\":\"Bentego\"},\"image\":{\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/bentego.com\\\/en\\\/#\\\/schema\\\/person\\\/0348418b7b0cbca83fdd7a899d54821e\",\"name\":\"Bentego\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Apache Spark Structured Streaming Via Docker Compose - Bentego","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/","og_locale":"en_US","og_type":"article","og_title":"Apache Spark Structured Streaming Via Docker Compose - Bentego","og_description":"Building a data pipeline could be challenging especially when you have to take into account portability, flexibility, scalability etc. To overcome these [&hellip;]","og_url":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/","og_site_name":"Bentego","article_published_time":"2023-05-08T12:02:27+00:00","article_modified_time":"2025-10-20T16:09:19+00:00","og_image":[{"width":2400,"height":1600,"url":"https:\/\/bentego.com\/wp-content\/uploads\/2025\/06\/Frame-85__.png","type":"image\/png"}],"author":"Bentego","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Bentego","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/#article","isPartOf":{"@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/"},"author":{"name":"Bentego","@id":"https:\/\/bentego.com\/en\/#\/schema\/person\/0348418b7b0cbca83fdd7a899d54821e"},"headline":"Apache Spark Structured Streaming Via Docker Compose","datePublished":"2023-05-08T12:02:27+00:00","dateModified":"2025-10-20T16:09:19+00:00","mainEntityOfPage":{"@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/"},"wordCount":670,"publisher":{"@id":"https:\/\/bentego.com\/en\/#organization"},"image":{"@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/#primaryimage"},"thumbnailUrl":"https:\/\/bentego.com\/wp-content\/uploads\/2025\/06\/Frame-85__.png","keywords":["cassandra","kafka","spark"],"articleSection":["Blog"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/","url":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/","name":"Apache Spark Structured Streaming Via Docker Compose - Bentego","isPartOf":{"@id":"https:\/\/bentego.com\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/#primaryimage"},"image":{"@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/#primaryimage"},"thumbnailUrl":"https:\/\/bentego.com\/wp-content\/uploads\/2025\/06\/Frame-85__.png","datePublished":"2023-05-08T12:02:27+00:00","dateModified":"2025-10-20T16:09:19+00:00","breadcrumb":{"@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/#primaryimage","url":"https:\/\/bentego.com\/wp-content\/uploads\/2025\/06\/Frame-85__.png","contentUrl":"https:\/\/bentego.com\/wp-content\/uploads\/2025\/06\/Frame-85__.png","width":2400,"height":1600},{"@type":"BreadcrumbList","@id":"https:\/\/bentego.com\/en\/apache-spark-structured-streaming-via-docker-compose\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/bentego.com\/en\/"},{"@type":"ListItem","position":2,"name":"Apache Spark Structured Streaming Via Docker Compose"}]},{"@type":"WebSite","@id":"https:\/\/bentego.com\/en\/#website","url":"https:\/\/bentego.com\/en\/","name":"Bentego","description":"Turning data into enterprise value","publisher":{"@id":"https:\/\/bentego.com\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/bentego.com\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/bentego.com\/en\/#organization","name":"Bentego","url":"https:\/\/bentego.com\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/bentego.com\/en\/#\/schema\/logo\/image\/","url":"https:\/\/bentego.com\/wp-content\/uploads\/2025\/05\/logo-bentego.svg","contentUrl":"https:\/\/bentego.com\/wp-content\/uploads\/2025\/05\/logo-bentego.svg","width":433,"height":109,"caption":"Bentego"},"image":{"@id":"https:\/\/bentego.com\/en\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/bentego.com\/en\/#\/schema\/person\/0348418b7b0cbca83fdd7a899d54821e","name":"Bentego"}]}},"_links":{"self":[{"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/posts\/5147","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/comments?post=5147"}],"version-history":[{"count":1,"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/posts\/5147\/revisions"}],"predecessor-version":[{"id":5468,"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/posts\/5147\/revisions\/5468"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/media\/1013"}],"wp:attachment":[{"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/media?parent=5147"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/categories?post=5147"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bentego.com\/en\/wp-json\/wp\/v2\/tags?post=5147"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}