Skip to content

Commit

Permalink
Add New Relic + devcontainer goodies (#201)
Browse files Browse the repository at this point in the history
* Installing new relic dependency in project

* Installing fzf

* Setting up New Relic upon gunicorn startup

* Added NewRelic to list of mypy ignored package

* Configure devcontainer with docker-compose, latest goodies and docker notify network
  • Loading branch information
jimleroyer authored Nov 27, 2024
1 parent fc59224 commit 8c66ce2
Show file tree
Hide file tree
Showing 9 changed files with 364 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ RUN apt-get update \
emacs \
exa \
fd-find \
fzf \
git \
iproute2 \
iputils-ping \
less \
libmagic-dev \
libsodium-dev \
Expand All @@ -22,6 +24,7 @@ RUN apt-get update \
npm \
openssh-client \
procps \
ripgrep \
sudo \
tldr \
unzip \
Expand All @@ -35,4 +38,6 @@ RUN pip install --upgrade pip

COPY .devcontainer/scripts/notify-dev-entrypoint.sh /usr/local/bin/

ENV SHELL /bin/zsh

EXPOSE 7000
27 changes: 18 additions & 9 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "notification-document-download-api",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"dockerComposeFile": "docker-compose.yml",
"service": "notify-dd-api",
"workspaceFolder": "/workspace",
"shutdownAction": "stopCompose",
"remoteEnv": {
"PATH": "/home/vscode/.local/bin:${containerEnv:PATH}" // give our installed Python modules precedence
},
Expand All @@ -18,17 +18,26 @@
},"python.pythonPath": "/usr/local/bin/python"
},
"extensions": [
"ms-python.python",
"charliermarsh.ruff",
"donjayamanne.python-extension-pack",
"eamodio.gitlens",
"fill-labs.dependi",
"GitHub.copilot",
"github.copilot-chat",
"github.vscode-pull-request-github",
"kaiwood.center-editor-window",
"matangover.mypy",
"ms-python.python",
"ms-python.vscode-pylance",
"tamasfe.even-better-toml",
"charliermarsh.ruff"
"timonwong.shellcheck",
"visualstudioexptteam.vscodeintellicode",
"vsliveshare.vsliveshare",
"wenfangdu.jump",
"yzhang.markdown-all-in-one"
]
}
},
"forwardPorts": [
7000
],
"postCreateCommand": "notify-dev-entrypoint.sh",
"remoteUser": "vscode"
}
32 changes: 32 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: '3'
services:
# Update this to the name of the service you want to work with in your docker-compose.yml file
notify-dd-api:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
# If you want add a non-root user to your Dockerfile, you can use the "remoteUser"
# property in devcontainer.json to cause VS Code its sub-processes (terminals, tasks,
# debugging) to execute as the user. Uncomment the next line if you want the entire
# container to run as this user instead. Note that, on Linux, you may need to
# ensure the UID and GID of the container user you create matches your local user.
# See https://aka.ms/vscode-remote/containers/non-root for details.
user: vscode

volumes:
# Update this to wherever you want VS Code to mount the folder of your project
- ..:/workspace:cached

# Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker-compose for details.
# - /var/run/docker.sock:/var/run/docker.sock

# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
expose:
- "7000"
networks:
- notify-network

networks:
notify-network:
external: true
3 changes: 3 additions & 0 deletions .devcontainer/scripts/notify-dev-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ echo -e "alias ll='exa -alh@ --git'" >> ~/.zshrc
echo -e "alias lt='exa -al -T -L 2'" >> ~/.zshrc
echo -e "alias poe='poetry run poe'" >> ~/.zshrc

echo -e "# fzf key bindings and completion" >> ~/.zshrc
echo -e "source /usr/share/doc/fzf/examples/key-bindings.zsh" >> ~/.zshrc
echo -e "source /usr/share/doc/fzf/examples/completion.zsh" >> ~/.zshrc

# Poetry autocomplete
echo -e "fpath+=/.zfunc" >> ~/.zshrc
Expand Down
40 changes: 39 additions & 1 deletion gunicorn_config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import os
import sys
import time
import traceback

import newrelic.agent # See https://bit.ly/2xBVKBH

environment = os.environ.get("NOTIFY_ENVIRONMENT")
newrelic.agent.initialize(environment=environment) # noqa: E402

workers = 4
worker_class = "gevent"
worker_connections = 256
Expand All @@ -13,10 +19,40 @@
# to be larger than the idle timeout configured for the load balancer.
# > By default, Elastic Load Balancing sets the idle timeout value for your load balancer to 60 seconds.
# https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#connection-idle-timeout
on_aws = os.environ.get("NOTIFY_ENVIRONMENT", "") in ["production", "staging", "scratch", "dev"]
on_aws = environment in ["production", "staging", "scratch", "dev"]
if on_aws:
# To avoid load balancers reporting errors on shutdown instances, see AWS doc
# > We also recommend that you configure the idle timeout of your application
# > to be larger than the idle timeout configured for the load balancer.
# > By default, Elastic Load Balancing sets the idle timeout value for your load balancer to 60 seconds.
# https://docs.aws.amazon.com/elasticloadbalancing/latest/application/application-load-balancers.html#connection-idle-timeout
keepalive = 75

# The default graceful timeout period for Kubernetes is 30 seconds, so
# make sure that the timeouts defined here are less than the configured
# Kubernetes timeout. This ensures that the gunicorn worker will exit
# before the Kubernetes pod is terminated. This is important because
# Kubernetes will send a SIGKILL to the pod if it does not terminate
# within the grace period. If the worker is still processing requests
# when it receives the SIGKILL, it will be terminated abruptly and
# will not be able to finish processing the request. This can lead to
# 502 errors being returned to the client.
#
# Also, some libraries such as NewRelic might need some time to finish
# initialization before the worker can start processing requests. The
# timeout values should consider these factors.
#
# Gunicorn config:
# https://docs.gunicorn.org/en/stable/settings.html#graceful-timeout
#
# Kubernetes config:
# https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/
graceful_timeout = 85
timeout = 90

# Start timer for total running time
start_time = time.time()


def on_starting(server):
server.log.info("Starting Document Download API")
Expand All @@ -29,7 +65,9 @@ def worker_abort(worker):


def on_exit(server):
elapsed_time = time.time() - start_time
server.log.info("Stopping Document Download API")
server.log.info("Total gunicorn API running time: {:.2f} seconds".format(elapsed_time))


def worker_int(worker):
Expand Down
3 changes: 3 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ ignore_missing_imports = True
ignore_missing_imports = True

[mypy-aws_xray_sdk.*]
ignore_missing_imports = True

[mypy-newrelic.*]
ignore_missing_imports = True
Loading

0 comments on commit 8c66ce2

Please sign in to comment.