Authenticating with StreamManager in python Docker container

0

In the developer guide for Greengrass V2, it says that to use inter-process communication, a docker container must have the following environment variables set.

AWS_REGION

SVCUID

AWS_GG_NUCLEUS_DOMAIN_SOCKET_FILEPATH_FOR_COMPONENT

AWS_CONTAINER_AUTHORIZATION_TOKEN

AWS_CONTAINER_CREDENTIALS_FULL_URI

https://docs.aws.amazon.com/greengrass/v2/developerguide/run-docker-container.html

I have written a simple Docker container that simply installs the stream manager sdk, and attempts to create a StreamManagerClient instance. The Dockerfile looks like this:

FROM python:3.9-alpine
ENV PYTHONUNBUFFERED=1
WORKDIR /workdir
RUN apk add git && pip install git+https://github.com/aws-greengrass/aws-greengrass-stream-manager-sdk-python
COPY test_connect.py test_connect.py
CMD [ "python", "test_connect.py" ]

The python script test_connect.py looks like this:

from stream_manager import StreamManagerClient

def main():
    client = StreamManagerClient()
    print(f"Connected to client: {client}")

if __name__ == "__main__":
    main()    

The "Run" Lifecycle step in my component recipe looks like this:

docker run -v /greengrass/v2:/greengrass/v2 -e AWS_REGION=$AWS_REGION -e SVCUID=$SVCUID -e AWS_GG_NUCLEUS_DOMAIN_SOCKET_FILEPATH_FOR_COMPONENT=$AWS_GG_NUCLEUS_DOMAIN_SOCKET_FILEPATH_FOR_COMPONENT -e AWS_CONTAINER_AUTHORIZATION_TOKEN=$AWS_CONTAINER_AUTHORIZATION_TOKEN -e AWS_CONTAINER_CREDENTIALS_FULL_URI=$AWS_CONTAINER_CREDENTIALS_FULL_URI --rm stream-test-docker

When this component runs, the python script fails with the following error:

Connection error while connecting to server: [Errno 111] Connect call failed ('127.0.0.1', 8088)
Traceback (most recent call last):
  File "/workdir/test_connect.py", line 10, in <module>
    main()    
  File "/workdir/test_connect.py", line 5, in main
    client = StreamManagerClient()
  File "/usr/local/lib/python3.9/site-packages/stream_manager/streammanagerclient.py", line 112, in __init__
    UtilInternal.sync(self.__connect(), loop=self.__loop)
  File "/usr/local/lib/python3.9/site-packages/stream_manager/utilinternal.py", line 39, in sync
    return asyncio.run_coroutine_threadsafe(coro, loop=loop).result()
  File "/usr/local/lib/python3.9/concurrent/futures/_base.py", line 440, in result
    return self.__get_result()
  File "/usr/local/lib/python3.9/concurrent/futures/_base.py", line 389, in __get_result
    raise self._exception
  File "/usr/local/lib/python3.9/site-packages/stream_manager/streammanagerclient.py", line 140, in __connect
    self.__reader, self.__writer = await asyncio.wait_for(
  File "/usr/local/lib/python3.9/asyncio/tasks.py", line 481, in wait_for
    return fut.result()
  File "/usr/local/lib/python3.9/asyncio/streams.py", line 52, in open_connection
    transport, _ = await loop.create_connection(
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 1056, in create_connection
    raise exceptions[0]
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 1041, in create_connection
    sock = await self._connect_sock(
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 955, in _connect_sock
    await self.sock_connect(sock, address)
  File "/usr/local/lib/python3.9/asyncio/selector_events.py", line 502, in sock_connect
    return await fut
  File "/usr/local/lib/python3.9/asyncio/selector_events.py", line 537, in _sock_connect_cb
    raise OSError(err, f'Connect call failed {address}')
ConnectionRefusedError: [Errno 111] Connect call failed ('127.0.0.1', 8088)

I have confirmed that the relevant environment variables are being mapped into the container, as is my greengrass root dir "/greengrass/v2". This is the same error returned by any other non-greengrass client on the system (i.e. if I run the same python script outside of the docker container). I have also tried setting the StreamManager config STREAM_MANAGER_AUTHENTICATE_CLIENT to "false", with no luck (though I don't believe this should be the case, as the container is running via Greengrass and has all the recommended environment variables set; the documentation was quite vague on this ).

The only successful workaround I was able to find was to set --network=host in my docker run command, but this is not ideal, as there should be no need to operate this particular component's container in host networking mode aside from this. Is this possibly the only way?

So in summary, my questions:

What is the appropriate method for interacting with StreamManager inside a greengrass-managed docker container?

What is the correct StreamManager configuration value for STREAM_MANAGER_AUTHENTICATE_CLIENT when using a Greengrass-managed docker container (as opposed to a docker container or other process unrelated to Greengrass).

asked 3 years ago798 views
2 Answers
0
Accepted Answer

Hi,
Stream Manager binds to the localhost which is probably the reason why the client inside the container is not able to connect to the SM server.
I suggest you try connecting via the gateway of the network used by your container. If you are using the default network, this is most probably the network named bridge.
You can list the available networks by running docker network ls

For instance my available networks are:

NETWORK ID          NAME                DRIVER              SCOPE  
9d81c010d50a        bridge              bridge              local  
d1fdeb535122        host                host                local  
72f2a273972a        none                null                local  

Now you should find the network your container is running in.
To do that run docker inspect --format='{{json .NetworkSettings.Networks}}' <NAME_OF_THE_CONTAINER>
For me it is bridge:

{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"NetworkID":"9d81c010d50a3e2b2d175e68bcdac6edb6c440677af479dc4256f612f6d4146c","EndpointID":"702afd9f1cab8d837baf9e0917a3a28fc7131e2b92acc8124500e2d05a178f58","Gateway":"240.10.0.1","IPAddress":"240.10.0.2","IPPrefixLen":24,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:f0:0a:00:02","DriverOpts":null}}  

Ie, my container is running with bridge network which is also the default. The gateway of the bridge network should be 172.17.0.1 by default, we can confirm that using docker network inspect bridge

Once you have the gateway address. You need to configure the Stream Manager client to talk over the gateway.

Modifying your example:

from stream_manager import StreamManagerClient  
   
def main():  
    client = StreamManagerClient(host='172.17.0.1')  
    print(f"Connected to client: {client}")  
   
if __name__ == "__main__":  
    main()    

To learn more about docker networking: https://docs.docker.com/engine/tutorials/networkingcontainers/

You can also get the address outside the container by running HOST_GATEWAY_ADDRESS=$(ip addr show | grep "\binet\b.*\bdocker0\b" | awk '{print $2}' | cut -d '/' -f 1) and then injected the variable into the container to use in code.

Thanks

answered 3 years ago
0

Thank you for the detailed reply! That makes much more sense.

answered 3 years ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions