# AMZ006453 AWSwebテスト作業ログ ## 課題1 方針:apache+flask+modwsgiでdeployを行う flaskのアプリケーションは/var/www/aws_testに置く ### リポジトリのアップデート サーバのターミナルで以下のコマンドを実行した sudo yum update ### apacheのインストールと自動起動設定 サーバのターミナルで以下のコマンドを実行した sudo yum install httpd sudo service httpd start sudo chkconfig httpd on ### Flaskのアプリケーションを置くディレクトリを作成 flaskのアプリケーションを/var/www/aws_testに配置するため,以下のコマンドを実行した sudo mkdir /var/www/aws_test ### SFTPの設定 サーバのターミナルのvim上ではなく自分のローカルのVScode上でファイルを編集するためにSFTPを設定した ①VScodeの拡張機能「SFTP」をインストール ②ctrl+shift+p からSFTP:Configを呼び出し,jsonファイルを作成 ③jsonファイルを以下のように設定 { "name": "My Server", "host": "(ipアドレスを記述)", "protocol": "sftp", "port": 22, "username": "ec2-user", "privateKeyPath" : "(秘密鍵のパスを設定)", "passphrase": true, "syncMode": "full", "remotePath": "/", "uploadOnSave": true, "rootPath": "/var/www/aws_test" } ④ctrl+shift+pからSFTP:List_Allを選択し,編集したサーバ上のフォルダを選択 ### Flaskのインストール サーバのターミナル上で以下のコマンドを実行した wget https://bootstrap.pypa.io/get-pip.py sudo python27 get-pip.py sudo cp /usr/local/bin/pip /usr/sbin/ pip install Flask ### mod_wsgiのインストール サーバのターミナル上で以下のコマンドを実行した sudo yum install mod_wsgi-python27 ### /var/wwwのパーミッション設定 sudo groupadd www sudo usermod -a -G www ec2-user sudo chown -R root:www /var/www sudo chmod 2775 /var/www find /var/www -type d -exec sudo chmod 2775 {} \; find /var/www -type f -exec sudo chmod 0664 {} \; ### /etc/httpd/conf.dにflask.confの追加 以下のコマンドを実行し,/etc/httpd/conf.dにflask.confを作成および編集を行った. sudo vim /etc/httpd/conf.d/flask.conf 以下がflask.confの内容である. WSGISocketPrefix /var/run/wsgi <VirtualHost *:80> ServerName example.com WSGIPassAuthorization On WSGIDaemonProcess aws_test user=ec2-user group=www threads=5 WSGIScriptAlias / /var/www/aws_test/deploy.wsgi <Directory /var/www/aws_test> WSGIProcessGroup aws_test WSGIApplicationGroup %{GLOBAL} Order deny,allow Allow from all </Directory> </VirtualHost> ### /var/www/aws_test/deploy.wsgiの追加 以下のコマンドを実行し,/var/www/aws_testにdeploy.wsgiを作成および編集を行った. sudo vim /var/www/aws_test/deploy.wsgi 以下がdeploy.wsgiの内容である. import sys, os sys.path.append('/var/www/aws_test') from app import app as application ### /var/www/aws_test/app.pyの編集 /var/www/aws_test/app.pyに以下の内容を追加した /----------------------------/ from flask import Flask app = Flask(__name__) @app.route("/") def amazon(): return "AMAZON\n" if __name__ == '__main__': app.run() /----------------------------/ ## 課題2 方針:flask_httpauthを使ってベーシック認証を行う ### flask-httpauthのインストール サーバのターミナル上で以下のコマンドを実行した pip install flask-httpauth ### /var/www/aws_test/app.pyの編集 /var/www/aws_test/app.pyに以下の内容を追加した ただし,HTTPBasicAuth→HTTPDigestAuthとすることでDigest認証も実装できる /----------------------------/ from flask_httpauth import HTTPBasicAuth #Basic認証の宣言 auth = HTTPBasicAuth() #Basic認証で利用するユーザの宣言 users = { "amazon": "candidate", } #ユーザ名とパスワードを照合する関数 @auth.get_password def get_pw(username): if username in users: return users.get(username) return None #/secret/で呼び出される関数 @app.route("/secret/") @auth.login_required def secret(): return "SUCCESS\n" /----------------------------/ ## 課題3 方針:flaskのrequestを使用して引数を取得する.pythonの正規表現モジュールreを用いて,引数が数式かを判別する ### /var/www/aws_test/app.pyの編集 /var/www/aws_test/app.pyに以下の内容を追加した /----------------------------/ from flask import Flask,request import re #/calcで呼び出される関数 @app.route("/calc") def calc_strings(): #/calc?以降の引数を取得 strings = request.query_string #文字列のカッコの数が合わない場合,数式に関わるもの以外が含まれる場合はERRORを出力 if strings.count("(") != strings.count(")") or re.search("[^\+\-\*\/()0-9]",strings): return "ERROR" else: #文字列の算術計算を行う try: ans = eval(strings) #文字列が計算を行えないものだった場合(例: ++,1+) except SyntaxError: return "ERROR" #文字列が()だった場合 if type(ans) is tuple: return "ERROR" return '%d'%ans /----------------------------/ ## 課題4 方針:Flask-SQLAlchemyを利用して,データベースを作成.データベースを用いて在庫管理を行う. ただしデータベースは以下に配置するものとした. var/www/aws_test/Stock.db ### Flask-SQLAlchemyのインストール ターミナル上で以下のコマンドを実行した sudo pip install Flask-SQLAlchemy ### /var/www/aws_test/app.pyの編集 /var/www/aws_test/app.pyに以下の内容を追加した /----------------------------/ from flask_sqlalchemy import SQLAlchemy app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////var/www/aws_test/Stock.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) #データベースで利用するモデルの宣言(課題4) class Stock(db.Model): __tablename__ = 'Stock&Sales' id = db.Column(db.Integer, primary_key = True) name = db.Column(db.String(8), unique = True) amount = db.Column(db.Integer) sales = db.Column(db.Float) def __init__(self, name, amount, sales): self.name = name self.amount = amount self.sales = sales def __repr__(self): return '<Name %r>' % self.name #/stockerで呼び出される関数 @app.route("/stocker") def get_function(): #必要な引数の宣言 function = "" name = None amount = None price = None #/stocker?以降の文字列を取得 strings = request.query_string #引数が含まれていたら取得する if "function=" in strings: function = request.args.get("function",type=str) if "name=" in strings: name = request.args.get("name",type=str) if "amount=" in strings: amount = request.args.get("amount",type=str) if "price=" in strings: price = request.args.get("price",type=str) #amountのエラーチェック if amount is None: amount = "1" #数字以外の要素(記号や英字)が含まれているか判別 elif amount.isdigit() is False: return "ERROR" #priceのエラーチェック if price is None: pass else: #数字として扱える文字列か判別(例: 1.1.1,abeなど) try: float(price) except ValueError: return "ERROR" #小数点を除いた文字列が数字のみかを判別 if price.replace(".","").isdigit() is False: return "ERROR" #functionに対応した関数を呼び出す if function == "addstock": if name is None: return "ERROR" else: addstock(name,amount) return "" elif function == "checkstock": product_string = checkstock(name) return product_string elif function == "sell": if name is None: return "ERROR" else: product_string = sell(name,amount,price) return product_string elif function == "checksales": product_string = checksales() return product_string elif function == "deleteall": deleteall() return "" else: return "ERROR" #function=addstockで呼び出される関数 def addstock(name,amount): amount = int(amount) #テーブルからnameの列を呼び出す stock = Stock.query.filter_by(name=name).first() if stock is None: stock = Stock(name,amount,0) else: stock.amount += amount #テーブルにnameの列を追加or上書き db.session.add(stock) #テーブルの保存 db.session.commit() #function=checkstockで呼び出される関数 def checkstock(name): product_string = "" if name is None: #テーブルの全ての列をnameで昇順にソートして呼び出す products = Stock.query.order_by(Stock.name.asc()).all() for product in products: product_string += (product.name + ": " + str(product.amount) + "\n") return product_string else: product = Stock.query.filter_by(name=name).first() if product is None: return "ERROR" else: return product.name + ": " + str(product.amount) #function=sellで呼び出される関数 def sell(name,amount,price): amount = int(amount) stock = Stock.query.filter_by(name=name).first() if stock is None: return "ERROR" #在庫が足りなかったらERRORを出力 elif stock.amount < amount: return "ERROR" else: stock.amount = stock.amount - amount #価格が指定されたら売り上げを計算 if price is not None: price = float(price) stock.sales = stock.sales + amount * price db.session.add(stock) db.session.commit() return "" #function=checksalesで呼び出される関数 def checksales(): #テーブルの全ての列を呼び出す products = Stock.query.all() total_sales = 0 for product in products: total_sales += product.sales #売り上げが小数か整数か判別 if total_sales == int(total_sales): total_sales = int(total_sales) else: total_sales = round(total_sales,2) return "sales: " + str(total_sales) #function=deleteallで呼び出される関数 def deleteall(): #全てのテーブルの列を削除 Stock.query.delete() db.session.commit() /----------------------------/ ### データベースの作成 app.pyの編集後にターミナルで以下のコマンドを実行し,データベースを作成した cd /var/www/aws_test ipython [1] from app import db [2] db.create_all() ## AWS面接 流れ 〇自己紹介(5分くらい?) ESに書いたことを簡単に聞かれたりはした(興味のある技術のこと) 〇面接官からの質問(技術的なことをかなり細かく聞かれた) 時間配分は技術テスト:提出課題で3:7くらいの割合だったと思う まず,技術テストのことについて聞かれた.例:課題3でeval関数を使っているけど,これを使うことのデメリットはありますか?今回作ったプログラムを複数のサーバーで同時に運用しているときに,プログラムをアップデートしたいときにサーバーを止めずにアップデートする方法はありますか?など 次に提出した課題についてかなり細かく聞かれた.例:この部分でサーバーにエラーが発生した際はどこで検知できるか?この部分を付けているがそのメリットは何か?この機能はどういうものか? 正直課題で使った機能とそれが及ぶ範囲は全部把握しといてもいいんじゃないかって思えるレベルだった. 〇逆質問