- Newest
- Most votes
- Most comments
Judging from absence of any meaningful log output from Gunicorn or Flask or nginx, I think there may be some entry point or launch command misconfiguration. It appears that, after 3 attempts to start a compute node running your containers, the system deemed this deployment a failure.
Here's my attempt at standing up a configuration similar to what you're describing in your post.
Here's the list of directories and files I've made, followed by the contents of each file:
static/index.html
static/readme.txt
hello.py
Dockerfile-flask
nginx.conf
Dockerfile-nginx
static/index.html:
<!DOCTYPE html>
<html>
<head><title>Static Content Root</title></head>
<body><p>Your static content is here.</p></body>
</html>
static/readme.txt:
Did you read me?
hello.py:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
Dockerfile-flask:
FROM python:alpine
RUN pip3 install Flask gunicorn
RUN mkdir /app
ADD hello.py /app
# nginx container will be sending traffic here via localhost:5555,
# see nginx.conf
EXPOSE 5555
WORKDIR /app
ENTRYPOINT ["gunicorn", "--bind", "localhost:5555", "--access-logfile", "-", "hello:app"]
nginx.conf:
server {
listen 80;
server_name localhost;
location / {
root /static;
index index.html;
}
location /flask/ {
# forward /flask/... requests to the gunicorn+flask container running on
# the same compute node, listening on port 5555.
proxy_pass http://localhost:5555/;
}
}
Dockerfile-nginx:
FROM nginx:alpine
COPY static /static/
COPY nginx.conf /etc/nginx/conf.d/default.conf
Next, I've built the docker images locally. The one tagged gunicorn-flask-app
is the flask application image. The one tagged nginx-ingress
is the nginx
image that has some static content and is configured to send some traffic to the
gunicorn/flask container:
docker build . -t gunicorn-flask-app --platform=linux/amd64 -f Dockerfile-flask
docker build . -t nginx-ingress --platform=linux/amd64 -f Dockerfile-nginx
Now let's create the container service:
aws lightsail create-container-service --service-name example --power nano --scale 1
It takes some time for it to become ready for deployments. I use aws lightsail get-container-services --service-name example
to see if it is ready, or check on it in Lightsail console.
Let's push the locally built images to Lightsail.
Push the flask image:
aws lightsail push-container-image --service-name example --label flask --image gunicorn-flask-app
Output (note that the sequence number 48
in the image reference may be completely different for you):
...
Digest: sha256:d46b4439329e8de4c608cf4f61865beea9b988fe732309c8a1ff0a83cd16b82c
Image "gunicorn-flask-app" registered.
Refer to this image as ":example.flask.48" in deployments.
Push the nginx image:
aws lightsail push-container-image --service-name example --label nginx --image nginx-ingress
Output:
...
Digest: sha256:bd008e587fcc59cc71f4421af0b0397a7a04d56e2ac2f4d0fc6e52a1adcc6ec6
Image "nginx-ingress" registered.
Refer to this image as ":example.nginx.47" in deployments.
With all that done, we can now create a deployment:
aws lightsail create-container-service-deployment --service-name example \
--containers '{
"app": {
"image": ":example.flask.latest"
},
"ingress": {
"image": ":example.nginx.latest",
"ports": {"80": "HTTP"}
}
}' \
--public-endpoint '{"containerName": "ingress", "containerPort": 80}'
As deployment proceeds, I see logs like these for app
container that runs gunicorn+flask application:
[deployment:3] Creating your deployment
... [1] [INFO] Starting gunicorn 20.1.0
... [1] [INFO] Listening at: http://127.0.0.1:5555 (1)
... [1] [INFO] Using worker: sync
... [7] [INFO] Booting worker with pid: 7
[deployment:3] Reached a steady state
... and these logs for ingress
container that runs our nginx server:
[deployment:3] Creating your deployment
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
...
/docker-entrypoint.sh: Configuration complete; ready for start up
... [notice] 1#1: using the "epoll" event method
... [notice] 1#1: nginx/1.23.3
... [notice] 1#1: built by gcc 12.2.1 20220924 (Alpine 12.2.1_git20220924-r4)
... [notice] 1#1: OS: Linux 5.10.167-147.601.amzn2.x86_64
... [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1024:4096
... [notice] 1#1: start worker processes
... [notice] 1#1: start worker process 29
... [notice] 1#1: start worker process 30
[deployment:3] Reached a steady state
When I visit my service via its public endpoint
https://example.qwerty1234567.us-west-2.cs.amazonlightsail.com/
, I can see
that it returns the static content as expected. And when I visit
https://example.qwerty1234567.us-west-2.cs.amazonlightsail.com/flask/
, I see
the hello world message from the Flask app.
Note: qwerty1234567.us-west-2
part of URL will differ for you, and will be
something unique to your account and the region where you're deploying your
service. You can find this URL in the container service details in Lightsail
console or by running aws lightsail get-container-services --service-name example
.
Prerequisites
I am doing this on a Mac with Docker Desktop & Homebrew installed.
In order to push locally built images for use in Lightsail container service deployments, you need to install:
- AWS CLI:
brew install awscli
- lightsailctl:
brew install aws/tap/lightsailctl
Thank you so much for spinning this up! I was able to take this, tweak it a bit to suit my setup, and my deployment was successful!
You're welcome, thank you for using Lightsail. BTW, you probably can simplify further: you don't need nginx, Flask has static file serving middleware as far as I understand. I've added nginx into the example above to more fully answer the original question.
Relevant content
- asked 2 years ago
- AWS OFFICIALUpdated a year ago
- AWS OFFICIALUpdated a year ago
- AWS OFFICIALUpdated a year ago
"a Flask/Gunicorn image built from Python-alpine and an Nginx image. Locally, I can spin them up with docker-compose and they work beautifully." Can you please amend your question with examples of Dockerfile-s you've set up? Since you're using compose for local dev, it seems like you're deploying 2 containers that work in tandem. Is it because you use nginx as the front-end container that sends some traffic to Flask/gunicorn container, and also serves some requests all by itself (e.g. static content)? The example of your compose file would help too.
I have updated the question, but yes, basically nginx is the frontend container that also serves static content. My goal is to eventually have statics served via CDN, but I thought (perhaps incorrectly) that this was an intermediary step as I learned how to manage Lightsail. Thank you!