プラグインによるAnsible機能の拡張:パート2



d2c.ioサービスの内部では、プロバイダークラウドでの仮想マシンの作成から必要なソフトウェアのインストール、クライアントアプリケーションでのDockerコンテナーの管理まで、Ansibleを積極的に使用しています。







最初の部分では、Ansibleがサポートするプラグインのタイプを調べ、テスト、フィルター、アクション、コールバックのプラグインをいくつか作成しました。 この記事では、より複雑な変更を試みます。







変異コールバック



コールバックプラグインの最も一般的な使用法は、ログとアラートです。 ただし、彼らの助けを借りて、イベントのパッシブモニタリングを実行できるだけでなく、プレイブックの進行に積極的に影響を与えることもできます。







一部のロールから特定のタスクのみを実行できるようにするために、D2Cではタグを積極的に使用しています。 たとえば、 build



タグでロールを開始すると、サービスは最初から完全にアセンブルされ、 update-configs



タグで起動すると、構成ファイルのみが更新されて適用されます。 すぐに使えるオプションでは、Ansibleはプレイブック全体にタグの単一セットを適用できます。







MySQLサービスのマスタースレーブレプリケーションを起動するタスクを分析しましょう。









各タスクには独自のタグがあります。 このプロセスを1つのプレイブックに結合するために、3つのプレイ(プレイはプレイブックが多数で構成される構成単位です)を説明できます。ウィザードの準備、レプリカの準備、クリーニングです。 ただし、プレイブック全体のtags



パラメーターを介して設定されるため、各パーツに個別にタグを指定することはできません。 コールバックプラグインを使用してこれを修正しましょう。







 from ansible.plugins.callback import CallbackBase from ansible.parsing.yaml.objects import AnsibleUnicode from ansible.compat.six import string_types import json import os class CallbackModule(CallbackBase): CALLBACK_VERSION = 2.0 CALLBACK_NAME = 'use_tags' def __init__(self): super(CallbackModule, self).__init__() self.tmp_context = None self.warn = False if os.environ.get('ANSIBLE_D2C_NO_WARN') else True def v2_playbook_on_play_start(self, play): vm = play.get_variable_manager() extra_vars = vm.extra_vars enable_use_tags = False if 'enable_use_tags' in extra_vars: if extra_vars['enable_use_tags']: enable_use_tags = True play_vars = vm.get_vars(play._loader, play=play) if enable_use_tags: tags = self.tmp_context.only_tags tags.clear() if 'use_tags' in play_vars: use_tags = play_vars['use_tags'] if isinstance(use_tags, (string_types, AnsibleUnicode)): use_tags = [t.strip() for t in use_tags.split(',')] if isinstance(use_tags, list): for t in use_tags: tags.add(t) else: tags.add('all') self._display.display(' [INFO]: "use_tags" variable is set, but unparsable (type "{}" is not a list or a string): {}'.format(type(use_tags),use_tags), color='cyan') else: self._display.display(' [INFO]: "use_tags" variable is not set, but "enable_use_tags" is set', color='cyan') tags.add('all') if self.warn: self._display.warning('Tags modified to: {}'.format(json.dumps(list(tags)))) def set_play_context(self, play_context): self.tmp_context = play_context
      
      





プラグインでは、メインキャラクターはv2_playbook_on_play_start



メソッドです。 プレイの初期化(変数の入力、ホストのリストの定義など)の後、タスク自体(タスク)の実行を開始する前に呼び出されます。







追加の変数(追加の変数) enable_use_tags



を、オンザフライでタグの変更を使用するサインとして使用し、再生レベルの変数(再生var) use_tags



を使用して必要なタグのリストを作成します。







すべては問題ありませんが、タグは初期化中に他の多くのランタイム情報とともにPlayContext



オブジェクトにコピーされますが、 v2_playbook_on_play_start



メソッドにはリンクがありv2_playbook_on_play_start



。 これに対処するため、Ansibleのキューマネージャーは、接続されたプラグインのset_play_context



メソッドをチェックし、もしあれば、それを呼び出してこのコンテキストを渡します。







PlayContext



可変であり、Ansibleが一度に1人のプレーヤーでしか動作しないという状況を利用して、プラグインに次のアルゴリズムを実装します。









これで、このようなプレイブックを実行できます。







ansible-playbook -e enable_use_tags=1 make_mysql_slave.yml









 - hosts: master vars: use_tags: update-configs, replication-init, replication-sync roles: - mysql - hosts: slave vars: use_tags: build, replication-init, replication-sync roles: - mysql - hosts: all vars: use_tags: replication-sync-cleanup roles: - mysql
      
      





この場合、Ansibleは各プレイに独自のタグセットを使用します。 これにより、単一のプレイブックで複雑な構成のオーケストレーションを並べることができます。







接続



接続プラグインは、ターゲットホストとの接続を確立するために使用されます。 要するに、プラグインは、接続を確立および終了し、ファイルを送信し、リモートコマンドを実行する機能を提供する必要があります。 すぐに使えるプラグインの例: local



ssh



(デフォルトで使用)、 winrm



winrm









独自の仮想化システムなど、完全に特別なターゲットホストがある場合は、プラグインを最初から作成する必要があります。 ただし、既存の機能に少し機能を追加する必要がある場合は、プラグインから「そのまま」継承し、必要なメソッドをオーバーライドできます。







ポートknokingを使用したSSH接続の例を考えてみましょう。 基本的に、これらのsshセッションは通常のセッションと違いはありませんが、リモートマシンに接続する前に、サーバーがポート22を開いてssh接続を受け入れるように特定のポートを「ノック」する必要があります。







基本的なssh



プラグインを完成させます(./connection_plugins/ssh_pkn.pyに入れssh



):







 from ansible.plugins.connection.ssh import Connection as ConnectionSSH from ansible.errors import AnsibleError from socket import create_connection from time import sleep try: from __main__ import display except ImportError: from ansible.utils.display import Display display = Display() class Connection(ConnectionSSH): def __init__(self, *args, **kwargs): super(Connection, self).__init__(*args, **kwargs) display.vvv("SSH_PKN (Port KNock) connection plugin is used for this host", host=self.host) def set_host_overrides(self, host, hostvars=None): if 'knock_ports' in hostvars: ports = hostvars['knock_ports'] if not isinstance(ports, list): raise AnsibleError("knock_ports parameter for host '{}' must be list!".format(host)) delay = 0.5 if 'knock_delay' in hostvars: delay = hostvars['knock_delay'] for p in ports: display.vvv("Knocking to port: {0}".format(p), host=self.host) try: create_connection((self.host, p), 0.5) except: pass display.vvv("Waiting for {0} seconds after knock".format(delay), host=self.host) sleep(delay)
      
      





set_host_overrides



メソッドを使用します。これにより、プラグインはホスト/グループ変数に応じて動作を変更できます。 このメソッドは、再利用が使用されていないときに新しい接続が作成されたときに呼び出されます。 この場合、ポートをもう一度「タップ」しないでください。







このプラグインを使用するためのインベントリファイルの例:







 [pkn] myserver ansible_host=my.server.at.example.com [pkn:vars] ansible_connection=ssh_pkn knock_ports=[8000,9000] knock_delay=2
      
      





pkn



グループ内のすべてのホストに対して、 pkn



接続プラグインが使用されることを示しました。 set_host_overrides



メソッド内でプラグインを初期化すると、 knock_ports



変数が定義されているという条件が機能します。 次に、リスト内の各ポートに対して、2秒のknock_delay



間隔で接続が試行されます。 ほとんどの場合、「タッピング」用のポートが閉じられ、接続の試行が失敗するため、 create_connection



からのすべての例外もキャッチします。 ただし、これは私たちにとって特に重要ではありません-いずれにしても、サーバーは試行を確認します。







戦略



戦略タイプのプラグインは、タスク(タスク)の起動順序を決定し、多くの「フードワーク」を実行します。ファクトの動的な追加、ホストのステータスの監視(ヘルス/失敗/到達不能)、コールバックの呼び出しなどです。 最初のパートでは、「すぐに使える」戦略プラグインについて詳しく書きました。







このようなカスタムプラグインは非常にまれです。 たとえば、 許可されていないプルリクエスト18460では、分散ロールの柔軟性を高めるために、プレイブックの任意の場所にタスクを挿入できるプラグインが提供されました。 より現実的な戦略プラグインを作成します。







./strategy_plugins/step_critical.pyに配置します:







 from ansible.plugins.strategy.linear import StrategyModule as LinearStrategyModule import os try: from __main__ import display except ImportError: from ansible.utils.display import Display display = Display() class StrategyModule(LinearStrategyModule): def __init__(self, tqm): super(StrategyModule, self).__init__(tqm) display.vv('Safenet strategy: will give a prompt at critical tasks!') force_step = os.environ.get('ANSIBLE_FORCE_STEP', None) if force_step and force_step.lower() in ['1','y','yes','true','on']: display.vv('Safenet: "step" option is forced via environment!') self._step = True def _take_step(self, task, host=None): v = task.get_vars() ret = True if 'is_critical' in v: if v['is_critical']: display.vv('Safenet: critical task detected!') return super(StrategyModule, self)._take_step(task, host) return ret
      
      





このプラグインは、-- --step



パラメーターの動作を変更するため、Ansibleは、 is_critical



変数がis_critical



、その値がTrue



であるタスクに対してのみ許可を要求します。







--step



パラメーターだけでなく、環境変数ANSIBLE_FORCE_STEP



使用して、確認モードを強制的に有効にすることもできます。 それ以外の場合、このプラグインはlinear



プラグインの動作を継承します。







次のプレイブックでプラグインの動作を確認できます。







 --- - hosts: localhost strategy: step_critical gather_facts: no tasks: - name: Ensure user exists debug: msg: user_module - name: Drop database debug: msg: db_module vars: is_critical: yes - name: Ensure permissions debug: msg: permission_module
      
      








合計



Ansible拡張オプションに関する2つの記事で、Ansible 2.3でサポートされているすべてのタイプのプラグインを取り上げました。 また、私はそれらのほとんどの例を示しました。







プラグインに関する質問が開かれていない場合は、コメントを記入してください-私は答えようとします。







それまでの間、Ansible用のモジュールの作成に関する記事の準備を始めています。 お楽しみに!








All Articles