简体   繁体   中英

Scaling Instances in Elastic Beanstalk breaks dynamic image link of python flask app

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM