The following code works brilliantly in local machine as well as in deployment at AWS Elastic BeanStalk - serves filxed image as well as dynamically created image. But, as soon as I scale the deployment to multiple EC2 instances, the dynamically created image becomes unavailable, even though I can find (by ssh) that dynamic image is present in the EC2 instance!
I suspect, it has something to do with how I am referencing to the static images in html or the way the load balancer handles static images. But, have no clue where to begin the investigation. Help please! :(
<code>
from flask import Flask, render_template, request, url_for
import os, datetime
import thread
import time
from copy import deepcopy
import pyqrcode # generating image of QR code from given string
application = Flask(__name__)
class QR(object):
def __init__(self):
self.input_string = None
self.output_string = None
self.input_image_filename = None
def encode(self, input_string, QR_image_filename, QR_image_scale):
"""Return the image of QR code"""
if not input_string:
raise RuntimeError('Input string is empty!') # checking that input_string is not empty
else:
self.input_string = input_string
try:
qr = pyqrcode.create(self.input_string)
qr.png(QR_image_filename, scale=QR_image_scale)
return True
except Exception as e:
print "printing exception : " + str(e)
raise RuntimeError('QR creation failed!')
def background_deletion_task(path):
print "wait starts"
# gevent.sleep(5)
time.sleep(5)
print "wait done"
if os.path.exists(path):
os.remove(path)
print "deletion done"
@application.route('/')
def hello():
gen_qr = QR()
gen_qr.qr_time = datetime.datetime.utcnow().isoformat()
gen_qr.image_file_name = str(gen_qr.qr_time[-6:]) + ".png"
gen_qr.full_path = os.path.join(application.root_path, 'static', gen_qr.image_file_name)
gen_qr.encode(str(gen_qr.image_file_name), gen_qr.full_path, 4)
# ---------------deletion by thread------------
#thread.start_new_thread(background_deletion_task, (gen_qr.full_path,))
return render_template('hello.html', gen_qr = gen_qr)
if __name__ == "__main__":
#application.debug=True
application.run()
</code>
<html> <body> Fixed Image <img src="/static/EYN_Logo.png" style="max-width: 150px;"> Dynamically Created Image <img src="/static/{{gen_qr.image_file_name}}"> </body> </html>
It seems like you are creating the instance on a single server, but in a load-balanced environment you probably have more than one server, and those servers can come & go at any time.
So basically, never store any state information on an EC2 instance.
To get around this, you have a couple of options. The best option would be to store the images in S3 - if you still want them to be referenced under /static/ then you could use Cloudfront to map that path to an S3 bucket.
The other option would be to use EFS to mount a shared file system on all of your EC2 instance.
Keep in mind that EFS is about 10x the cost of S3 storage.
I found a workaround which works, but not completely convinced that's the right approach. This is what works for me now.
First, I added a ANY_NAME.config
under .exbextension with the following option_settings: aws:elasticbeanstalk:container:python:staticfiles: "/static/": "static/"
This is to explicitly say to EBS where to find the static files.
Second, I have invoked the html by <img src="{{ url_for('static', filename=fname ) }}">
instead of <img src="/{{fname}}">
where return render_template('hello.html', fname = fname)
After this, eb scale 2
serves well the dynamic files.
As chris mentioned, although it works, perhaps the better approach is to serve the images ( whether dynamically created or static) from S3 bucket.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.