1. Allow build, lint, test & bash with the celery container using `make` commands.
2. Added CI to build the docker image to make sure it doesn't get broken in a future PR without knowing.
3. Default to Python 3.11 in the Dockerfile if not set otherwise via .python-version (pyenv).

For example running `make docker-lint` from the `celery` repo root:
```bash
make docker-lint

[+] Creating 4/0
 ✔ Container docker-azurite-1   Running                                                                                                                                           0.0s
 ✔ Container docker-rabbit-1    Running                                                                                                                                           0.0s
 ✔ Container docker-dynamodb-1  Running                                                                                                                                           0.0s
 ✔ Container docker-redis-1     Running                                                                                                                                           0.0s
ROOT: will run in automatically provisioned tox, host /home/developer/.pyenv/versions/3.11.5/bin/python3.11 is missing [requires (has)]: tox-gh-actions
ROOT: recreate env because python changed version_info=[3, 9, 13, 'final', 0]->[3, 11, 5, 'final', 0] | executable='/Users/nusnus/.pyenv/versions/3.9.13/bin/python3.9'->'/home/developer/.pyenv/versions/3.11.5/bin/python3.11' | virtualenv version='20.24.4'->'20.24.5'
ROOT: remove tox env folder /home/developer/celery/.tox/.tox
ROOT: install_deps> python -I -m pip install tox tox-gh-actions
ROOT: provision> .tox/.tox/bin/python -m tox -e lint
ROOT: tox-gh-actions won't override envlist because tox is not running in GitHub Actions
.pkg: _optional_hooks> python /home/developer/celery/.tox/.tox/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_editable> python /home/developer/celery/.tox/.tox/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: build_editable> python /home/developer/celery/.tox/.tox/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
lint: install_package> python -I -m pip install --force-reinstall --no-deps /home/developer/celery/.tox/.tmp/package/17/celery-5.3.4-0.editable-py3-none-any.whl
lint: commands[0]> pre-commit run --all-files --show-diff-on-failure
[INFO] Initializing environment for https://github.com/asottile/pyupgrade.
[INFO] Initializing environment for https://github.com/PyCQA/flake8.
[INFO] Initializing environment for https://github.com/asottile/yesqa.
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Initializing environment for https://github.com/pycqa/isort.
[INFO] Initializing environment for https://github.com/pre-commit/mirrors-mypy.
[INFO] Installing environment for https://github.com/asottile/pyupgrade.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/PyCQA/flake8.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/asottile/yesqa.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pycqa/isort.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pre-commit/mirrors-mypy.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
pyupgrade................................................................Passed
flake8...................................................................Passed
Strip unnecessary `# noqa`s..............................................Passed
check for merge conflicts................................................Passed
check toml...............................................................Passed
check yaml...............................................................Passed
mixed line ending........................................................Passed
isort....................................................................Passed
mypy.....................................................................Passed
.pkg: _exit> python /home/developer/celery/.tox/.tox/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
  lint: OK (35.25=setup[3.34]+cmd[31.92] seconds)
  congratulations :) (37.49 seconds)
```


The `make help` is also modified:
```bash
make help
docs                 - Build documentation.
test-all             - Run tests for all supported python versions.
distcheck ---------- - Check distribution for problems.
  test               - Run unittests using current python.
  lint ------------  - Check codebase for problems.
    apicheck         - Check API reference coverage.
    configcheck      - Check configuration reference coverage.
    readmecheck      - Check README.rst encoding.
    contribcheck     - Check CONTRIBUTING.rst encoding
    flakes --------  - Check code for syntax and style errors.
      flakecheck     - Run flake8 on the source code.
      flakepluscheck - Run flakeplus on the source code.
readme               - Regenerate README.rst file.
contrib              - Regenerate CONTRIBUTING.rst file
clean-dist --------- - Clean all distribution build artifacts.
  clean-git-force    - Remove all uncommitted files.
  clean ------------ - Non-destructive clean
    clean-pyc        - Remove .pyc/__pycache__ files
    clean-docs       - Remove documentation build artifacts.
    clean-build      - Remove setup artifacts.
bump                 - Bump patch version number.
bump-minor           - Bump minor version number.
bump-major           - Bump major version number.
release              - Make PyPI release.

Docker-specific commands:
  docker-build			- Build celery docker container.
  docker-lint        		- Run tox -e lint on docker container.
  docker-unit-tests		- Run unit tests on docker container, use '-- -k <TEST NAME>' for specific test run.
  docker-bash        		- Get a bash shell inside the container.
```
Celery Docker Upgrades. 1. Allow build, lint, test & bash with the celery container using `make` commands.
2. Added CI to build the docker image to make sure it doesn't get broken in a future PR without knowing.
3. Default to Python 3.11 in the Dockerfile if not set otherwise via .python-version (pyenv).

For example running `make docker-lint` from the `celery` repo root:
```bash
make docker-lint

[+] Creating 4/0
 ✔ Container docker-azurite-1   Running                                                                                                                                           0.0s
 ✔ Container docker-rabbit-1    Running                                                                                                                                           0.0s
 ✔ Container docker-dynamodb-1  Running                                                                                                                                           0.0s
 ✔ Container docker-redis-1     Running                                                                                                                                           0.0s
ROOT: will run in automatically provisioned tox, host /home/developer/.pyenv/versions/3.11.5/bin/python3.11 is missing [requires (has)]: tox-gh-actions
ROOT: recreate env because python changed version_info=[3, 9, 13, 'final', 0]->[3, 11, 5, 'final', 0] | executable='/Users/nusnus/.pyenv/versions/3.9.13/bin/python3.9'->'/home/developer/.pyenv/versions/3.11.5/bin/python3.11' | virtualenv version='20.24.4'->'20.24.5'
ROOT: remove tox env folder /home/developer/celery/.tox/.tox
ROOT: install_deps> python -I -m pip install tox tox-gh-actions
ROOT: provision> .tox/.tox/bin/python -m tox -e lint
ROOT: tox-gh-actions won't override envlist because tox is not running in GitHub Actions
.pkg: _optional_hooks> python /home/developer/celery/.tox/.tox/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_editable> python /home/developer/celery/.tox/.tox/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: build_editable> python /home/developer/celery/.tox/.tox/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
lint: install_package> python -I -m pip install --force-reinstall --no-deps /home/developer/celery/.tox/.tmp/package/17/celery-5.3.4-0.editable-py3-none-any.whl
lint: commands[0]> pre-commit run --all-files --show-diff-on-failure
[INFO] Initializing environment for https://github.com/asottile/pyupgrade.
[INFO] Initializing environment for https://github.com/PyCQA/flake8.
[INFO] Initializing environment for https://github.com/asottile/yesqa.
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Initializing environment for https://github.com/pycqa/isort.
[INFO] Initializing environment for https://github.com/pre-commit/mirrors-mypy.
[INFO] Installing environment for https://github.com/asottile/pyupgrade.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/PyCQA/flake8.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/asottile/yesqa.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pycqa/isort.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pre-commit/mirrors-mypy.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
pyupgrade................................................................Passed
flake8...................................................................Passed
Strip unnecessary `# noqa`s..............................................Passed
check for merge conflicts................................................Passed
check toml...............................................................Passed
check yaml...............................................................Passed
mixed line ending........................................................Passed
isort....................................................................Passed
mypy.....................................................................Passed
.pkg: _exit> python /home/developer/celery/.tox/.tox/lib/python3.11/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
  lint: OK (35.25=setup[3.34]+cmd[31.92] seconds)
  congratulations :) (37.49 seconds)
```


The `make help` is also modified:
```bash
make help
docs                 - Build documentation.
test-all             - Run tests for all supported python versions.
distcheck ---------- - Check distribution for problems.
  test               - Run unittests using current python.
  lint ------------  - Check codebase for problems.
    apicheck         - Check API reference coverage.
    configcheck      - Check configuration reference coverage.
    readmecheck      - Check README.rst encoding.
    contribcheck     - Check CONTRIBUTING.rst encoding
    flakes --------  - Check code for syntax and style errors.
      flakecheck     - Run flake8 on the source code.
      flakepluscheck - Run flakeplus on the source code.
readme               - Regenerate README.rst file.
contrib              - Regenerate CONTRIBUTING.rst file
clean-dist --------- - Clean all distribution build artifacts.
  clean-git-force    - Remove all uncommitted files.
  clean ------------ - Non-destructive clean
    clean-pyc        - Remove .pyc/__pycache__ files
    clean-docs       - Remove documentation build artifacts.
    clean-build      - Remove setup artifacts.
bump                 - Bump patch version number.
bump-minor           - Bump minor version number.
bump-major           - Bump major version number.
release              - Make PyPI release.

Docker-specific commands:
  docker-build			- Build celery docker container.
  docker-lint        		- Run tox -e lint on docker container.
  docker-unit-tests		- Run unit tests on docker container, use '-- -k <TEST NAME>' for specific test run.
  docker-bash        		- Get a bash shell inside the container.
```
"""Periodically store events in a database.

Consuming the events as a stream isn't always suitable
so this module implements a system to take snapshots of the
state of a cluster at regular intervals.  There's a full
implementation of this writing the snapshots to a database
in :mod:`djcelery.snapshots` in the `django-celery` distribution.
"""
from kombu.utils.limits import TokenBucket

from celery import platforms
from celery.app import app_or_default
from celery.utils.dispatch import Signal
from celery.utils.imports import instantiate
from celery.utils.log import get_logger
from celery.utils.time import rate
from celery.utils.timer2 import Timer

__all__ = ('Polaroid', 'evcam')

logger = get_logger('celery.evcam')


class Polaroid:
    """Record event snapshots."""

    timer = None
    shutter_signal = Signal(name='shutter_signal', providing_args={'state'})
    cleanup_signal = Signal(name='cleanup_signal')
    clear_after = False

    _tref = None
    _ctref = None

    def __init__(self, state, freq=1.0, maxrate=None,
                 cleanup_freq=3600.0, timer=None, app=None):
        self.app = app_or_default(app)
        self.state = state
        self.freq = freq
        self.cleanup_freq = cleanup_freq
        self.timer = timer or self.timer or Timer()
        self.logger = logger
        self.maxrate = maxrate and TokenBucket(rate(maxrate))

    def install(self):
        self._tref = self.timer.call_repeatedly(self.freq, self.capture)
        self._ctref = self.timer.call_repeatedly(
            self.cleanup_freq, self.cleanup,
        )

    def on_shutter(self, state):
        pass

    def on_cleanup(self):
        pass

    def cleanup(self):
        logger.debug('Cleanup: Running...')
        self.cleanup_signal.send(sender=self.state)
        self.on_cleanup()

    def shutter(self):
        if self.maxrate is None or self.maxrate.can_consume():
            logger.debug('Shutter: %s', self.state)
            self.shutter_signal.send(sender=self.state)
            self.on_shutter(self.state)

    def capture(self):
        self.state.freeze_while(self.shutter, clear_after=self.clear_after)

    def cancel(self):
        if self._tref:
            self._tref()  # flush all received events.
            self._tref.cancel()
        if self._ctref:
            self._ctref.cancel()

    def __enter__(self):
        self.install()
        return self

    def __exit__(self, *exc_info):
        self.cancel()


def evcam(camera, freq=1.0, maxrate=None, loglevel=0,
          logfile=None, pidfile=None, timer=None, app=None,
          **kwargs):
    """Start snapshot recorder."""
    app = app_or_default(app)

    if pidfile:
        platforms.create_pidlock(pidfile)

    app.log.setup_logging_subsystem(loglevel, logfile)

    print(f'-> evcam: Taking snapshots with {camera} (every {freq} secs.)')
    state = app.events.State()
    cam = instantiate(camera, state, app=app, freq=freq,
                      maxrate=maxrate, timer=timer)
    cam.install()
    conn = app.connection_for_read()
    recv = app.events.Receiver(conn, handlers={'*': state.event})
    try:
        try:
            recv.capture(limit=None)
        except KeyboardInterrupt:
            raise SystemExit
    finally:
        cam.cancel()
        conn.close()

"""Periodically store events in a database.

Consuming the events as a stream isn't always suitable
so this module implements a system to take snapshots of the
state of a cluster at regular intervals.  There's a full
implementation of this writing the snapshots to a database
in :mod:`djcelery.snapshots` in the `django-celery` distribution.
"""
from kombu.utils.limits import TokenBucket

from celery import platforms
from celery.app import app_or_default
from celery.utils.dispatch import Signal
from celery.utils.imports import instantiate
from celery.utils.log import get_logger
from celery.utils.time import rate
from celery.utils.timer2 import Timer

__all__ = ('Polaroid', 'evcam')

logger = get_logger('celery.evcam')


class Polaroid:
    """Record event snapshots."""

    timer = None
    shutter_signal = Signal(name='shutter_signal', providing_args={'state'})
    cleanup_signal = Signal(name='cleanup_signal')
    clear_after = False

    _tref = None
    _ctref = None

    def __init__(self, state, freq=1.0, maxrate=None,
                 cleanup_freq=3600.0, timer=None, app=None):
        self.app = app_or_default(app)
        self.state = state
        self.freq = freq
        self.cleanup_freq = cleanup_freq
        self.timer = timer or self.timer or Timer()
        self.logger = logger
        self.maxrate = maxrate and TokenBucket(rate(maxrate))

    def install(self):
        self._tref = self.timer.call_repeatedly(self.freq, self.capture)
        self._ctref = self.timer.call_repeatedly(
            self.cleanup_freq, self.cleanup,
        )

    def on_shutter(self, state):
        pass

    def on_cleanup(self):
        pass

    def cleanup(self):
        logger.debug('Cleanup: Running...')
        self.cleanup_signal.send(sender=self.state)
        self.on_cleanup()

    def shutter(self):
        if self.maxrate is None or self.maxrate.can_consume():
            logger.debug('Shutter: %s', self.state)
            self.shutter_signal.send(sender=self.state)
            self.on_shutter(self.state)

    def capture(self):
        self.state.freeze_while(self.shutter, clear_after=self.clear_after)

    def cancel(self):
        if self._tref:
            self._tref()  # flush all received events.
            self._tref.cancel()
        if self._ctref:
            self._ctref.cancel()

    def __enter__(self):
        self.install()
        return self

    def __exit__(self, *exc_info):
        self.cancel()


def evcam(camera, freq=1.0, maxrate=None, loglevel=0,
          logfile=None, pidfile=None, timer=None, app=None,
          **kwargs):
    """Start snapshot recorder."""
    app = app_or_default(app)

    if pidfile:
        platforms.create_pidlock(pidfile)

    app.log.setup_logging_subsystem(loglevel, logfile)

    print(f'-> evcam: Taking snapshots with {camera} (every {freq} secs.)')
    state = app.events.State()
    cam = instantiate(camera, state, app=app, freq=freq,
                      maxrate=maxrate, timer=timer)
    cam.install()
    conn = app.connection_for_read()
    recv = app.events.Receiver(conn, handlers={'*': state.event})
    try:
        try:
            recv.capture(limit=None)
        except KeyboardInterrupt:
            raise SystemExit
    finally:
        cam.cancel()
        conn.close()
