スクリプトサーバー。 スクリプトをリモートで実行するWebUI

みなさんこんにちは。 この記事では、私のホームプロジェクトについてお話したいと思います。 要するに、スクリプトサーバーは、ユーザーにWebインターフェイスを介してスクリプトへのアクセスを提供するためのWebサーバーです。 サーバーとスクリプトはローカルで起動され、パラメーター化されてリモートで表示されます。











背景



新しい職場では、私の最初の仕事は非常に日常的で単調でした。 概略的には、次のようになりました。



  1. my_file.txtファイルを作成します
  2. そこに新しい構成を登録します
  3. サーバー上のZadlepit
  4. コミットする


より多くのステップがあり、それらはより複雑でしたが、2、3回の反復の後、怠inessが勝ち、いくつかのパラメーターで実行され、時には作業中に追加の入力データを要求するすべての反復タスクに対してシェルスクリプトが書き込まれました。 同時に、ワークフローは次のようになり始めました。



  1. プロジェクトマネージャーに必要なパラメーターを尋ねる
  2. スクリプトを実行する
  3. 準備状況の報告


プロジェクトマネージャーがパラメーターを指定し、スクリプトを自分で実行できるように、これらのポイントはすべて1つにまとめることを明確に求めていました。 しかし、彼にスクリプトを与えることは、いくつかの理由で非常に問題がありました-スクリプトは私の環境に適合しており、失敗する可能性があり、私にとっては十分に友好的でしたが、非技術者などはそうではありませんでした



ある時点で、そのようなタスクはすでに割り当てられなくなり、必要な適応なしで作成されたスクリプトには非常に幻想的な未来があったと言わなければなりません。



そのため、必要な環境を作成したり、より便利な形式で起動を構成したりせずに、スクリプトを他の人にリモートで実行できるツールを作成することにしました。



作成する途中



作業スキーム









サーバー管理者(つまり、I)は、各スクリプトの構成ファイルを作成します。このファイルには、スクリプトの目的、スタートアップパス、および必要なパラメーターが記述されています。 サーバーはこれらの構成ファイルを使用して、スクリプトに関するデータをユーザーに提供したり、スクリプトを実行したりします。 ユーザーのページの情報は、Ajaxリクエストによって送信されます。



ユーザーはパラメータを入力し、サーバー上でスクリプトを実行します。そこで、その実行は特別なハンドラーに渡されます。 非同期モードのハンドラーは、入力を受け入れて出力を提供し、スクリプト実行プロセスも監視します。



Webサーバーは、このハンドラーとページの間のレイヤーとして機能し、Webソケットを介してデータを交換します。



ツール選択



当時、私はPythonを学んでいたので、開発のためにそれを良い習慣として使用することにしました。 Python 3は、2番目のバージョンをサポートせずにプロジェクトに選ばれました。これは必要ないからで、あまり時間をかけたくありませんでした。



サーバーの開発の初期に、Flaskを使用しましたが、非同期とクライアントの接続/切断の監視ができなかったため( 読み、理解できませんでした)、すぐにすべてをTornadoに転送しました。



Web開発について:私はWeb開発者ではないので、ここで少し構文と基本を練習することにしました。 この点で、多かれ少なかれ適切な設計のために、マテリアライズを除き、フレームワークとライブラリはほとんど使用されませんでした。



Webページは、最小限のHTMLとAjaxリクエストによるJSでのコンテンツの作成を備えた「1ページのアプリケーション」です。



最初の使用と改善



約1か月後、少なくとも使用できるサーバーを入手し、これらのルーチンタスクを実行する必要のある仲間の開発者と共有しました(運が良ければ、この期間にほぼ終了しました)。



作業中に、バグと明らかに欠落している要素の両方が発見され、私はゆっくりと作業と改善を行いました。 同時に、パラメータタイプ、すべての起動のログなど、マイナーな改善が追加されました。



興味深い点:いくつかの小さなタスク(コンソールとスクリプトの操作に関連する)で、コンソールの使用を停止し、このUIに切り替えました。 つまり 他の人よりも頻繁に使用するスクリプト構成がいくつかあります。



実際のユーザー操作



しばらくローカルテストを行った後(私と他の開発者による)、ツールは最終的にプロジェクトマネージャーに提供され、プロジェクトマネージャーはゆっくりと使い始めました。 そして、彼は非常に満足していることに注意すべきです、なぜなら これは彼の時間さえも節約します。 「実装」とユーザートレーニングのプロセスには約5分かかりました。



プロセスと制限の特性により、スクリプトサーバーは主にテスト環境に使用されます。 しかし、その一部はすでに生産に使用され始めています(安全な部分)。



主な石



以下は、私が最も覚えている問題と私の個人的な発見のリストです。



スクリプト実行プロセスからの出力を非同期モードでページに転送します。 これは、FlaskとWebの両方での経験不足によるものです。そのような転送をどのように整理するのが最善かということです。 その結果、Webソケット(タイプとデータ)のイベントの形式でデータ転送を実装しました。 また、イベントでは、実行時に他のコマンドを送信できますが、これまでは使用されていません。



ユーザー入力を実行可能スクリプトに渡します。 スクリプトで入力を待っている間だけ入力を要求しようとしましたが、方法が見つかりませんでした。 したがって、ユーザーは、スクリプトの実行中に必要なだけ情報を入力できます(通常の端末は同じように動作します)。 データは同じWebソケットを介して送信されます。



ユーザーの切断を追跡します。 Flaskが拒否したのはこの問題のためでした。その瞬間、私はWebソケットではなく、SSEで(まあ、ほぼ)やっていたのです。 ただし、後で入力/出力スクリプトをWebソケットに転送したため、Flaskで転送できた可能性があります。 トルネードでは、単にWebソケットの閉鎖にサインアップできます。



スクリプト実行の出力を読み取ります。 現在利用可能なデータのみを読み取り、クライアントに送信するために問題が発生しました。 行ごとに読み取ることは不可能です。 たとえば、「read -p input_prompt」は次の行に折り返しません。 文字で読むことはできますが、クライアントに非常に多くのリクエストを送信する価値はありません。 バッファーを使用して読み取ると、文字列が不明瞭な場所でトリミングされます(UTF-8の場合でも、誤った文字が生成されます)。 現在のソリューションには妥協と松葉杖がたくさんありますが、一般的には、読み取りロックを無効にしてバッファリングされた読み取りです。



(非)ターミナルモードでスクリプトを出力します。 正直なところ、これは私にとって発見でした。スクリプトを端末または外部で実行すると、スクリプトの出力が異なる場合があります。 たとえば、同じ...



read -p input_prompt
      
      





...ターミナルモードでのみinput_promptを表示します。 何を正確に示すことなく、ユーザーに何かを期待するのは良くありません。 実行中のスクリプトをスプーフィングするために、ptyに対処する必要がありました。 このため、スクリプトを実行するための2種類のラッパーがあります。ターミナルサポートありとなしです。 最初の設定はデフォルトでオンになっており、2番目の設定は設定を使用してオンにできます(ターミナルモードが失敗する場合に備えて、左に設定してください)



自動スクロールスクリプトの出力。 一方では、これは非常に簡単に解決されますが、他方では、シャドウ、ユーザー入力などと同様に、自動的にオフにする設定(ユーザーが手動でスクロールするかテキストを選択した場合)に問題がありました。 異常なことは何もありませんでしたが、数時間を費やす必要がありました。



使用可能なすべてのスペースを占有するようにスクリプト出力を使用してパネルをセットアップしますが、同時にウィンドウを離れることはありません。 したがって、ページは常にウィンドウに収まり、出力パネル内にスクロールのみが追加されます。 これは私にとって特に重要でした。なぜなら、 私は、巻物の中の巻物が嫌いです。



非常に大量のスクリプト出力を伴うページのパフォーマンス。 それから、次のコードが私をだましました:



 htmlElement.innerText += "text";
      
      





事実は、 innerTextを取得すること自体が、単なるHTMLであっても、着信htmlを文字列として表示しようとするかなりリソースを消費する操作であるということです。 この操作を数千回の文字に対して数百回呼び出すと、効果は目立ちませんが、私の場合はもっと多くのデータがあり、ブラウザはこのロードから数分間ハングしました。 コードを次のように変更して決定しました。



 var textNode = document.createTextNode("text"); htmlElement.appendChild(textNode);
      
      





プロセスのハングアップ 誰も何もしませんが、実行可能なスクリプトがプロセスでハングします。 理由は異なります。たとえば、ユーザーがページを閉じたが、サーバーはこれを処理しません。 または、プライベートファイル記述子ではありません。 または、閉じられていない子プロセス。 そして、それは他の明らかなバグを数えていません。 しかし、グーグル、学習、修正によってすべてが排除されました。



安全性



彼女は全然違います。 このツールは、ローカルでの起動用に設計されており、信頼できるユーザーがいるローカルネットワークで動作します。 多くの場合、スクリプトも独自に作成されているため、特に保護されていません。 したがって、このようなサーバーを起動する前に、ファイアウォールとユーザーに100%自信を持つ必要があります。



データ隠蔽の観点から、スクリプトサーバーには現在何もありません。 パスワードはログに記録され、純粋な形式で保存されるため、パスワードをパラメーターとして、または構成に含めることは強くお勧めしません。



例とスクリーンショット



プロジェクトのデバッグのために、テストとデモンストレーションに使用できるテスト構成を開始しました。 このセクションでは、提供されるすべてのスクリーンショットはそれらに基づいています。



スクリプト構成



パラメーター、操作および印刷中のユーザー入力を含む比較的標準的なbashスクリプト: Write file



テキストの壁を表示し、段落に分割してユーザー入力を要求する単純なpythonスクリプト: Destroy world 。 この構成は、オプションを表示するときにユーザーインターフェイスをデバッグするために使用されます。 可能なすべてのタイプのパラメーターが含まれます: パラメーター化



スクリーンショット



全画面






左側はスクリプト選択エリア、右側は現在開いているスクリプトに関する情報です。





オプションパネル






このカオスでは、さまざまなタイプのパラメーターのファミリーを見ることができます:必須、リスト、数値、ブール値。 パラメーター化された構成に基づいて作成されたスクリーンショット



入出力パネル






開始ボタンと停止ボタンの上部。 スクリプトが実行されているため、最後のスクリプトのみがアクティブになります。 以下はスクリプト出力パネルです。一番下には入力データ入力用のフィールドがあります。 出力パネルはデモターゲットの高さで圧縮され、通常のウィンドウでははるかに高くなります。



さらなるステップ



現時点では、開発は継続されていません。 現在の形式では、スクリプトサーバーはそれに割り当てられたタスクを完全にカバーします。 また、現在のインターフェイスで何かを最適化する必要もありません。 したがって、私たちは両方とも新しい挑戦と問題を待っています。



特に、このツールがhabrasocietyの人にとっても有用であり、新しい改善が必要な場合は、非常に嬉しいです。



»プロジェクトリポジトリへのリンク: github.com/bugy/script-server



使用されているライブラリ/フレームワーク/感謝のリスト:





この記事を読んでくれたすべての人に感謝します。 価値のあるコメントと批判(アプローチ、アプリケーション、または実装機能の観点から)に感謝します。



All Articles