Ansible拡張まとめ
Ansibleの概要
AnsibleはPythonが開発したメンテナンスツールで、作業にはAnsibleに触れる必要があり、Ansibleに統合されることが多いため、Ansibleに対する理解が増えています.
Ansibleとは何なのでしょうか?私の理解では、サーバにログインして、いくつかの操作を完了するためにコマンドを実行する必要がありました.Ansibleは私たちの代わりにそれらのコマンドを実行します.また,Ansibleにより複数の機器を制御し,機器上でタスクの編成と実行を行うことができ,Ansibleではplaybookと呼ぶ.
では、Ansibleはどうやってやったのでしょうか.簡単に言えば、Ansibleは私たちが実行するコマンドをスクリプトを生成し、sftpを通じてコマンドを実行するサーバにスクリプトをアップロードし、sshプロトコルを通じてスクリプトを実行し、実行結果を返します.
では、Ansibleは具体的にどうやってやったのでしょうか.次に、モジュールとプラグインから、Ansibleがモジュールの実行をどのように完了するかを見てみましょう.
PS:以下の分析はすべてAnsibleに対していくつかの具体的な使用経験があった後に、ソースコードを読むことを通じて更に得た実行結論なので、本文を見る時、Ansibleに対して一定の理解がある基礎の上で創立することを望んで、少なくともAnsibleのいくつかの概念に対して解があって、例えばinventory、module、playbooksなど
Ansibleモジュール
モジュールはAnsibleが実行する最小単位であり、Pythonで作成してもよいし、Shellで作成してもよいし、他の言語で作成してもよい.モジュールには、特定の操作手順と実際の使用に必要なパラメータが定義されています.
実行されるスクリプトは、モジュールに基づいて実行可能なスクリプトを生成します.
では、Ansibleはどのようにしてこのスクリプトをサーバにアップロードし、結果を取得しますか?
Ansibleカード
接続プラグイン
プラグインを接続し、指定したsshパラメータに基づいて指定したサーバを接続し、実際にコマンドを実行するインタフェースを切断します.
shellプラグイン
コマンドプラグインは、shタイプに応じてconnectionで実行するコマンドを生成します.
strategyプラグイン
ポリシープラグインを実行します.デフォルトはリニアプラグインです.タスクの次のタスクが下に実行され、このプラグインはタスクを実行器に捨てて実行します.
Actionプラグイン
アクションプラグインは、実質的にタスクモジュールのすべての動作であり、ansibleのモジュールに特に作成されたactionプラグインがない場合、デフォルトではnormalまたはasync(この2つはモジュールがasyncかどうかによって選択される)であり、normalおよびasyncで定義されているのがモジュールの実行ステップである.たとえば、ローカルでテンポラリファイルを作成したり、テンポラリファイルをアップロードしたり、スクリプトを実行したり、スクリプトを削除したりします.すべてのモジュールに特別なステップを追加したい場合は、actionプラグインを追加することで拡張できます.
Ansible実行モジュールフロー ansibleコマンドは実質的にansible/cli/adhocを通過する.pyを実行し、パラメータ情報を収集します. Play情報を設定し、TaskQueueManagerでrun, を行います. TaskQueueManagerにはInventory(ノードウェアハウス)、variable_manager(収集変数)、options(コマンドラインで指定したパラメータ)、stdout_callback(コールバック関数) task_queue_manager.pyでrunを見つける 初期化時にキュー が設定.はoptions,,variable_に基づいてmanager,passwordsなどの情報はPlayContext情報(playbooks/playcontext.py) に設定されている.プラグイン情報callback_を設定するloader(コールバック)、strategy_loader(実行ポリシー)、module_loader(タスクモジュール) strategyを通過loader(strategyプラグイン)のrun(デフォルトのstrategyタイプはlinear、線形実行)は、すべてのタスクを順番に実行します(1つのモジュールを実行し、複数のタスクを実行する可能性があります) strategy_loaderプラグインrunの後、actionタイプが判断されます.metaタイプの場合は個別に実行され(具体的なansibleモジュールでない場合)、他のモジュールの場合はキュー_にロードされます.queue_task はキュー内でWorkerProcessを呼び出して処理し、workerprocessの実際のrunの後、TaskExecutorを使用して を実行する. TaskExecutorでconnectionプラグインが設定され、taskのタイプ(モジュール.またはincludeなど)に基づいてactionプラグインが取得されます.対応するモジュールです.モジュールにカスタム実行がある場合はカスタムactionが実行され、ない場合はnormalまたはasyncが使用されます.これはタスクのasync属性かどうかに基づいて が決定されます.は、Actionプラグインにおいて実行の順序と、例えば一時ディレクトリの生成、一時スクリプトの生成などの具体的な動作を定義しているので、統合モードで追加の処理を統合する場合には、Actionのメソッド を書き換えることができる. Connectionプラグインによりアクションを実行する各操作ステップ .
拡張Ansibleインスタンス
実行ノードPython環境拡張
実際のニーズでは、拡張されたいくつかのAnsibleモジュールでは、3つのライブラリを使用する必要がありますが、各ノードにこれらのライブラリをインストールするのは管理しにくい場合があります.ansible実行モジュールの本質はノードのpython環境で生成されたスクリプトを実行することであるので,ノード上のPython環境を指定し,ローカルエリアネットワーク内のpython環境をnfsとして共有することを提案する.Actionプラグインを拡張することで、ノードにnfsをマウントし、実行が終了した後にノードのnfsをアンインストールします.具体的な実施手順は以下の通りである.
拡張コード:
ActionBaseのexecuteを書き換えるmoduleメソッド
normalに統合pyとasync.pyでは、この2つのプラグインをansibleに挿入することを覚えておいてください.cfgでの構成 ansibleを配置する.cfgは、拡張されたプラグインをansibleに必要なactionプラグイン として指定する.プラグインの書き換え方法、ポイントはexecute_module 実行コマンドではPython環境を指定する必要があり、nfsマウントおよびアンインストールのパラメータ に必要なパラメータを追加する.
AnsibleはPythonが開発したメンテナンスツールで、作業にはAnsibleに触れる必要があり、Ansibleに統合されることが多いため、Ansibleに対する理解が増えています.
Ansibleとは何なのでしょうか?私の理解では、サーバにログインして、いくつかの操作を完了するためにコマンドを実行する必要がありました.Ansibleは私たちの代わりにそれらのコマンドを実行します.また,Ansibleにより複数の機器を制御し,機器上でタスクの編成と実行を行うことができ,Ansibleではplaybookと呼ぶ.
では、Ansibleはどうやってやったのでしょうか.簡単に言えば、Ansibleは私たちが実行するコマンドをスクリプトを生成し、sftpを通じてコマンドを実行するサーバにスクリプトをアップロードし、sshプロトコルを通じてスクリプトを実行し、実行結果を返します.
では、Ansibleは具体的にどうやってやったのでしょうか.次に、モジュールとプラグインから、Ansibleがモジュールの実行をどのように完了するかを見てみましょう.
PS:以下の分析はすべてAnsibleに対していくつかの具体的な使用経験があった後に、ソースコードを読むことを通じて更に得た実行結論なので、本文を見る時、Ansibleに対して一定の理解がある基礎の上で創立することを望んで、少なくともAnsibleのいくつかの概念に対して解があって、例えばinventory、module、playbooksなど
Ansibleモジュール
モジュールはAnsibleが実行する最小単位であり、Pythonで作成してもよいし、Shellで作成してもよいし、他の言語で作成してもよい.モジュールには、特定の操作手順と実際の使用に必要なパラメータが定義されています.
実行されるスクリプトは、モジュールに基づいて実行可能なスクリプトを生成します.
では、Ansibleはどのようにしてこのスクリプトをサーバにアップロードし、結果を取得しますか?
Ansibleカード
接続プラグイン
プラグインを接続し、指定したsshパラメータに基づいて指定したサーバを接続し、実際にコマンドを実行するインタフェースを切断します.
shellプラグイン
コマンドプラグインは、shタイプに応じてconnectionで実行するコマンドを生成します.
strategyプラグイン
ポリシープラグインを実行します.デフォルトはリニアプラグインです.タスクの次のタスクが下に実行され、このプラグインはタスクを実行器に捨てて実行します.
Actionプラグイン
アクションプラグインは、実質的にタスクモジュールのすべての動作であり、ansibleのモジュールに特に作成されたactionプラグインがない場合、デフォルトではnormalまたはasync(この2つはモジュールがasyncかどうかによって選択される)であり、normalおよびasyncで定義されているのがモジュールの実行ステップである.たとえば、ローカルでテンポラリファイルを作成したり、テンポラリファイルをアップロードしたり、スクリプトを実行したり、スクリプトを削除したりします.すべてのモジュールに特別なステップを追加したい場合は、actionプラグインを追加することで拡張できます.
Ansible実行モジュールフロー
拡張Ansibleインスタンス
実行ノードPython環境拡張
実際のニーズでは、拡張されたいくつかのAnsibleモジュールでは、3つのライブラリを使用する必要がありますが、各ノードにこれらのライブラリをインストールするのは管理しにくい場合があります.ansible実行モジュールの本質はノードのpython環境で生成されたスクリプトを実行することであるので,ノード上のPython環境を指定し,ローカルエリアネットワーク内のpython環境をnfsとして共有することを提案する.Actionプラグインを拡張することで、ノードにnfsをマウントし、実行が終了した後にノードのnfsをアンインストールします.具体的な実施手順は以下の通りである.
拡張コード:
ActionBaseのexecuteを書き換えるmoduleメソッド
# execute_module
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
import pipes
from ansible.compat.six import text_type, iteritems
from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.release import __version__
try:
from __main__ import display
except ImportError:
from ansible.utils.display import Display
display = Display()
class MagicStackBase(object):
def _mount_nfs(self, ansible_nfs_src, ansible_nfs_dest):
cmd = ['mount',ansible_nfs_src, ansible_nfs_dest]
cmd = [pipes.quote(c) for c in cmd]
cmd = ' '.join(cmd)
result = self._low_level_execute_command(cmd=cmd, sudoable=True)
return result
def _umount_nfs(self, ansible_nfs_dest):
cmd = ['umount', ansible_nfs_dest]
cmd = [pipes.quote(c) for c in cmd]
cmd = ' '.join(cmd)
result = self._low_level_execute_command(cmd=cmd, sudoable=True)
return result
def _execute_module(self, module_name=None, module_args=None, tmp=None, task_vars=None, persist_files=False, delete_remote_tmp=True):
'''
Transfer and run a module along with its arguments.
'''
# display.v(task_vars)
if task_vars is None:
task_vars = dict()
# if a module name was not specified for this execution, use
# the action from the task
if module_name is None:
module_name = self._task.action
if module_args is None:
module_args = self._task.args
# set check mode in the module arguments, if required
if self._play_context.check_mode:
if not self._supports_check_mode:
raise AnsibleError("check mode is not supported for this operation")
module_args['_ansible_check_mode'] = True
else:
module_args['_ansible_check_mode'] = False
# Get the connection user for permission checks
remote_user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user
# set no log in the module arguments, if required
module_args['_ansible_no_log'] = self._play_context.no_log or C.DEFAULT_NO_TARGET_SYSLOG
# set debug in the module arguments, if required
module_args['_ansible_debug'] = C.DEFAULT_DEBUG
# let module know we are in diff mode
module_args['_ansible_diff'] = self._play_context.diff
# let module know our verbosity
module_args['_ansible_verbosity'] = display.verbosity
# give the module information about the ansible version
module_args['_ansible_version'] = __version__
# set the syslog facility to be used in the module
module_args['_ansible_syslog_facility'] = task_vars.get('ansible_syslog_facility', C.DEFAULT_SYSLOG_FACILITY)
# let module know about filesystems that selinux treats specially
module_args['_ansible_selinux_special_fs'] = C.DEFAULT_SELINUX_SPECIAL_FS
(module_style, shebang, module_data) = self._configure_module(module_name=module_name, module_args=module_args, task_vars=task_vars)
if not shebang:
raise AnsibleError("module (%s) is missing interpreter line" % module_name)
# get nfs info for mount python packages
ansible_nfs_src = task_vars.get("ansible_nfs_src", None)
ansible_nfs_dest = task_vars.get("ansible_nfs_dest", None)
# a remote tmp path may be necessary and not already created
remote_module_path = None
args_file_path = None
if not tmp and self._late_needs_tmp_path(tmp, module_style):
tmp = self._make_tmp_path(remote_user)
if tmp:
remote_module_filename = self._connection._shell.get_remote_filename(module_name)
remote_module_path = self._connection._shell.join_path(tmp, remote_module_filename)
if module_style in ['old', 'non_native_want_json']:
# we'll also need a temp file to hold our module arguments
args_file_path = self._connection._shell.join_path(tmp, 'args')
if remote_module_path or module_style != 'new':
display.debug("transferring module to remote")
self._transfer_data(remote_module_path, module_data)
if module_style == 'old':
# we need to dump the module args to a k=v string in a file on
# the remote system, which can be read and parsed by the module
args_data = ""
for k,v in iteritems(module_args):
args_data += '%s=%s ' % (k, pipes.quote(text_type(v)))
self._transfer_data(args_file_path, args_data)
elif module_style == 'non_native_want_json':
self._transfer_data(args_file_path, json.dumps(module_args))
display.debug("done transferring module to remote")
environment_string = self._compute_environment_string()
remote_files = None
if args_file_path:
remote_files = tmp, remote_module_path, args_file_path
elif remote_module_path:
remote_files = tmp, remote_module_path
# Fix permissions of the tmp path and tmp files. This should be
# called after all files have been transferred.
if remote_files:
self._fixup_perms2(remote_files, remote_user)
# mount nfs
if ansible_nfs_src and ansible_nfs_dest:
result = self._mount_nfs(ansible_nfs_src, ansible_nfs_dest)
if result['rc'] != 0:
raise AnsibleError("mount nfs failed!!! {0}".format(result['stderr']))
cmd = ""
in_data = None
if self._connection.has_pipelining and self._play_context.pipelining and not C.DEFAULT_KEEP_REMOTE_FILES and module_style == 'new':
in_data = module_data
else:
if remote_module_path:
cmd = remote_module_path
rm_tmp = None
if tmp and "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
if not self._play_context.become or self._play_context.become_user == 'root':
# not sudoing or sudoing to root, so can cleanup files in the same step
rm_tmp = tmp
cmd = self._connection._shell.build_module_command(environment_string, shebang, cmd, arg_path=args_file_path, rm_tmp=rm_tmp)
cmd = cmd.strip()
sudoable = True
if module_name == "accelerate":
# always run the accelerate module as the user
# specified in the play, not the sudo_user
sudoable = False
res = self._low_level_execute_command(cmd, sudoable=sudoable, in_data=in_data)
# umount nfs
if ansible_nfs_src and ansible_nfs_dest:
result = self._umount_nfs(ansible_nfs_dest)
if result['rc'] != 0:
raise AnsibleError("umount nfs failed!!! {0}".format(result['stderr']))
if tmp and "tmp" in tmp and not C.DEFAULT_KEEP_REMOTE_FILES and not persist_files and delete_remote_tmp:
if self._play_context.become and self._play_context.become_user != 'root':
# not sudoing to root, so maybe can't delete files as that other user
# have to clean up temp files as original user in a second step
tmp_rm_cmd = self._connection._shell.remove(tmp, recurse=True)
tmp_rm_res = self._low_level_execute_command(tmp_rm_cmd, sudoable=False)
tmp_rm_data = self._parse_returned_data(tmp_rm_res)
if tmp_rm_data.get('rc', 0) != 0:
display.warning('Error deleting remote temporary files (rc: {0}, stderr: {1})'.format(tmp_rm_res.get('rc'), tmp_rm_res.get('stderr', 'No error string available.')))
# parse the main result
data = self._parse_returned_data(res)
# pre-split stdout into lines, if stdout is in the data and there
# isn't already a stdout_lines value there
if 'stdout' in data and 'stdout_lines' not in data:
data['stdout_lines'] = data.get('stdout', u'').splitlines()
display.debug("done with _execute_module (%s, %s)" % (module_name, module_args))
return data
normalに統合pyとasync.pyでは、この2つのプラグインをansibleに挿入することを覚えておいてください.cfgでの構成
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.action import ActionBase
from ansible.utils.vars import merge_hash
from common.ansible_plugins import MagicStackBase
class ActionModule(MagicStackBase, ActionBase):
def run(self, tmp=None, task_vars=None):
if task_vars is None:
task_vars = dict()
results = super(ActionModule, self).run(tmp, task_vars)
# remove as modules might hide due to nolog
del results['invocation']['module_args']
results = merge_hash(results, self._execute_module(tmp=tmp, task_vars=task_vars))
# Remove special fields from the result, which can only be set
# internally by the executor engine. We do this only here in
# the 'normal' action, as other action plugins may set this.
#
# We don't want modules to determine that running the module fires
# notify handlers. That's for the playbook to decide.
for field in ('_ansible_notify',):
if field in results:
results.pop(field)
return results
ansible 51 -m mysql_db -a "state=dump name=all target=/tmp/test.sql" -i hosts -u root -v -e "ansible_nfs_src=172.16.30.170:/web/proxy_env/lib64/python2.7/site-packages ansible_nfs_dest=/root/.pyenv/versions/2.7.10/lib/python2.7/site-packages ansible_python_interpreter=/root/.pyenv/versions/2.7.10/bin/python"