Q : "is there something I am missing here with Fargate/ECS/ZeroMQ???"
Maybe yes, maybe no.
Let's start in a structured way to drill down to a root-cause :
Step 0: a Broker Service Node
ZeroMQ was mentioned to be used, so we'll start from this point. Given your choice was to use an AccessPoint to DEALER on address ( *:4070 ) and an AccessPoint to ROUTER on address ( *:4080 ), and both using a .bind()-method for activating a tcp://-Transport Class inside a Broker-Microservice Node, our next step is to validate, whether and how is this Node actually visible for the rest of the world.
So, let it run.
Step 1: a Line-of-Sight Test
This is a first step to test - is the Broker-Node, whatever is its implementation, actually visible for the "intended audience" ? If not, there is not much to do about it inside ZeroMQ or other frameworks, but your task is to get the addresses, L1-signal interconnection, L2-arp/rarp MAC-detection/mapping, L3-routing permissions/access-lists/filters/xlations/etc, (dynamic) DNS-updates and all other configurations updated, so that you enable the (selective part of the) rest of the world see and get one step closer to  do a successful .connect()
$ #                                 is it L3-(in)-visible # a [ PASS ] | [ FAIL ]
$ ping <_a_Broker_Node_Assumed_TCP/IP_Address>            # a [ PASS ] | [ FAIL ]
Step 2: a port-number RTO-Test
$ #                                                  4070 # a [ PASS ] | [ FAIL ]
$ netcat -vz <_a_Broker_Node_visible_TCP/IP_Address> 4070 # a [ PASS ] | [ FAIL ]
$ ######
$ # OR :
$ ######
$ telnet     <_a_Broker_Node_visible_TCP/IP_Address> 4070 # a [ PASS ] | [ FAIL ]
Trying 
Connected to 
Escape character is '^]'.
https://<_a_Broker_Node_visible_TCP/IP_Address>:4070
HTTP/1.1 400 Bad Request
Server: nginx
Date: Mon, 03 May 2020 18:14:54 GMT
Content-Type: text/html
Content-Length: 150
Connection: close
<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx</center>
</body>
</html>
Connection closed by foreign host.
$
$ //                                             4080 // a [ PASS ] | [ FAIL ]
$ telnet <_a_Broker_Node_visible_TCP/IP_Address> 4080 // a [ PASS ] | [ FAIL ]
Step 3: an RTO-test of a local message sending
Replace the rather complicated realm of the REQ/ROUTER-Scalable Formal Communications Archetype Pattern, and let's test with a straightforward PUSH/PULL-message delivery test, which (for obvious reasons) matches the intended use for sending a message:
package main
import (
    zmq "github.com/pebbe/zmq4"
    "log"
    "fmt"
    "time"
    ...
)
func PushTASK() {
    aCtx, err    := zmq.NewContext()
    if err != nil {
        log.Fatalln( "__NACK: aCtx instantiation failed in zmq.NewContext()",
                      err )
    }
    aPusher, err := aCtx.NewSocket( zmq.PUSH )
    if err != nil {
        log.Fatalln( "__NACK: aPusher instantiation failed in aCtxNewSocket()",
                      err )
    }
    err = aPusher.SetLinger( 0 )
    if err != nil {
        log.Fatalln( "__NACK: aPusher instance failed to .SetLinger()",
                      err  )
    }
    err = aPusher.SetConflate( true )
    if err != nil {
        log.Fatalln( "__NACK: aPusher instance failed to .SetConflate()",
                      err  )
    }
    log.Println( "POSACK: aPusher instantiated and about to .connect( tcp://addr:port#)" )
    err = aPusher.Connect( "tcp://broker:4070" )
    if err != nil {
        log.Print( fmt.Errorf( "__NACK: aPusher failed to .connect(): %w",
                                err )
                   )
    }
    log.Println( "POSACK: aPusher RTO and about to .SendMessage*()-loop" )
    for aPush_NUMBER := 1; aPush_NUMBER < 10000; aPush_NUMBER++ {
        err = aPusher.SendMessageDontwait( aPush_NUMBER )
        if err != nil {
              log.Print( fmt.Errorf( "__NACK: aPusher failed to .SendMessageDontwait()[%d]: %w",
                                      aPush_NUMBER,
                                      err )
                         )
        }
        time.Sleep( 0.1 * time.Second )
    }
 // ---------------------------------------------------BE NICE TO RESOURCES USED
    err = aPusher.Disconnect( "tcp://broker:4070" )
    if err != nil {
        log.Print( fmt.Errorf( "__NACK: aPusher failed to .Disconnect( tcp://addr:port ): %w",
                                err )
                   )
    }
 // ---------------------------------------------------BE NICE TO RESOURCES USED
    err = aPusher.Close()
    if err != nil {
        log.Print( fmt.Errorf( "__NACK: aPusher failed to .Close(): %w",
                                err )
                   )
    }
 // ---------------------------------------------------BE NICE TO RESOURCES USED
    err = aCtx.Term()
    if err != nil {
        log.Print( fmt.Errorf( "__NACK: aCtx failed to .Term(): %w",
                                err )
                   )
    }
 // ---------------------------------------------------WE ARE CLEAR TO TERMINATE
}
Step 4: an RTO-test of a remote message receipt
If none of the [ PASS ] | [ FAIL ]-tests 've crashed, the next step is to reflect the PUSH-side concept for the "remote" Broker, yes, rewriting it to use a PULL-side and deploy it to see, if there are no crashes either and whether the messages arrive as they ought to in the still running or re-run the Step 3.
Step 5: Enjoy the powers of ZeroMQ
Once all the tests above indeed do [ PASS ], you are not only sure the ZeroMQ was not the show-stopper, but also may enhance the deployed principles into any further use-case scenarios, given the L1-/L2-/L3-/ZeroMQ-services were put in place in a correct and verifiable manner.