### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

After a clean install using `pip install localstack`, running `localstack start` fails by producing the following output:

```

     __                     _______ __             __
    / /   ____  _________ _/ / ___// /_____ ______/ /__
   / /   / __ \/ ___/ __ `/ /\__ \/ __/ __ `/ ___/ //_/
  / /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,<
 /_____/\____/\___/\__,_/_//____/\__/\__,_/\___/_/|_|

 💻 LocalStack CLI 0.14.2.11

Traceback (most recent call last):
  File "/home/phii/Code/aws-test/.venv/bin/localstack", line 23, in <module>
    main()
  File "/home/phii/Code/aws-test/.venv/bin/localstack", line 19, in main
    main.main()
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/cli/main.py", line 6, in main
    cli()
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/cli/plugin.py", line 15, in __call__
    self.group(*args, **kwargs)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/cli/localstack.py", line 125, in cmd_start
    from localstack.utils import bootstrap
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/utils/bootstrap.py", line 16, in <module>
    from localstack.utils.container_networking import get_main_container_name
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/utils/container_networking.py", line 7, in <module>
    from localstack.utils.container_utils.container_client import ContainerException
  File "/home/phii/Code/aws-test/.venv/lib/python3.7/site-packages/localstack/utils/container_utils/container_client.py", line 13, in <module>
    from typing import Dict, List, Optional, Protocol, Tuple, Union
ImportError: cannot import name 'Protocol' from 'typing' (/home/phii/.pyenv/versions/3.7.4/lib/python3.7/typing.py)
```

As you can see from the logs, I am using python 3.7.4.
After googling the python docs, I saw that "Protocol" wasn't added to the "typing" module until subsequent python versions, however the docs clearly state that localstack supports python 3.7.

### Expected Behavior

I expected for localstack to start normally.

### How are you starting LocalStack?

Custom (please describe below)

### Steps To Reproduce

#### How are you starting localstack (e.g., `bin/localstack` command, arguments, or `docker-compose.yml`)

`bin/localstack`

#### Client commands (e.g., AWS SDK code snippet, or sequence of "awslocal" commands)

`localstack start`

### Environment

```markdown
- OS: ManjaroLinux 21.2.6
- LocalStack: latest
```
"""Select a subset of attributes from the given dict (returns a copy)"""
    attributes = attributes if is_list_or_tuple(attributes) else [attributes]
    return {k: v for k, v in obj.items() if k in attributes}
def remove_attributes(obj: Dict, attributes: List[str], recursive: bool = False) -> Dict:
    """Remove a set of attributes from the given dict (in-place)"""
    from localstack.utils.objects import recurse_object
    if recursive:
        def _remove(o, **kwargs):
            if isinstance(o, dict):
                remove_attributes(o, attributes)
            return o
        return recurse_object(obj, _remove)
    attributes = attributes if is_list_or_tuple(attributes) else [attributes]
    for attr in attributes:
        obj.pop(attr, None)
    return obj
def rename_attributes(
    obj: Dict, old_to_new_attributes: Dict[str, str], in_place: bool = False
) -> Dict:
    """Rename a set of attributes in the given dict object. Second parameter is a dict that maps old to
    new attribute names. Default is to return a copy, but can also pass in_place=True."""
    if not in_place:
        obj = dict(obj)
    for old_name, new_name in old_to_new_attributes.items():
        if old_name in obj:
            obj[new_name] = obj.pop(old_name)
    return obj
def is_list_or_tuple(obj) -> bool: