[Deployment] CentOS8上でAnsibleを使う

CentOS7環境とは少し違うかな?と言う気がした。

CentOS7環境に対して過去にAnsibleをインストール・実装したことはあったのだけど、実はCentOS8に関してはAnsibleをインストールしたことはなかったので手順の確認を行いました。

  • パッケージ管理ソフトウェアがdnfに変わっていること
  • Python3が基本的に使用されること

ってのが気になったポイントですが、果たしてどんなもんかーと思って導入してみることに。

実施手順

前提環境の整備

サーバとして前提としているパッケージのインストールと既存パッケージのアップデートを行います。

 dnf -y install bash-completion net-tools nfs-utils bind-utils rsync wget nc tcpdump sysstat lsof strace telnet psmisc zip unzip net-snmp net-snmp-libs
 dnf -y update

EPELリポジトリの導入、pip, sshpassを導入します。

 dnf -y install python3-pip epel-release sshpass

SELINUX無効化を目的としてコンフィグ設定を変更します。

 vi /etc/selinux/config

内部LANで動かすことからFirewalldを停止します。自動起動抑止も行います。

 systemctl stop firewalld
 systemctl disable firewalld

我が家環境の内部サーバはNFSサーバを使ってるので自動NFSマウント設定を実装します。

 vi /etc/fstab
 mkdir /data
 mount /data

監視にZABBIXを使ってるのでエージェントソフトをダウンロードするために、ZABBIXリポジトリの導入を行います。その上でエージェントを導入します。

 rpm -Uvh https://repo.zabbix.com/zabbix/5.2/rhel/8/x86_64/zabbix-release-5.2-1.el8.noarch.rpm
 dnf -y install zabbix-agent
 vi /etc/zabbix/zabbix_agent.conf

エージェントの自動起動設定を有効にします。

 systemctl enable zabbix-agent

一旦再起動します。

 shutdown -r now

Ansible実行ユーザを作成します。加えてパスワード変更を行います。

 useradd ansible
 passwd ansible

実行ユーザがsudoできるよう、Wheelグループに実行ユーザを追加します。

 gpasswd -a ansible wheel

これで前提環境がそろったので、ansible実行ユーザにスイッチします。

 su - ansible

Ansibleの導入

pip3を使用してansibleパッケージを導入します。

 pip3 install ansible --user

ansibleのバージョン確認を行います。

 ansible --version

以下は出力結果ですが、2020年11月14日時点の最新バージョンは2.10.3のようです。また、使用するPythonのバージョンは3.6となっており、Python3前提で動作する設定になることが見て分かります。

また、pip3を通じてインストールした際に「–user」オプションスイッチをつけたことにより、ユーザのローカル領域にansibleがインストールされた状態になっています。

ansible 2.10.3
  config file = None
  configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ansible/.local/lib/python3.6/site-packages/ansible
  executable location = /home/ansible/.local/bin/ansible
  python version = 3.6.8 (default, Apr 16 2020, 01:36:27) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]

SSH鍵セットを作成します。

 ssh-keygen -t rsa

この鍵を使用して、ログインするLinuxサーバ上のAuthorized_Keysへの登録を行います。「-o StrictHostKeyChecking=no」を加えることで、署名チェックを回避することが出来るようです。

 ssh-copy-id -o StrictHostKeyChecking=no -i /home/ansible/.ssh/id_rsa.pub root@<管理対象IP or hostname>

Windowsの遠隔操作を可能にするため、WinRM連携をするためのモジュールインストールを行います。

 pip3 install pywinrm --user

これで一通りインストールするものとしてはそろったかな?という感じになります。

hosts、Playbooksの配置・作成等

hostsとplaybooksを配置するディレクトリを作ります。

 mkdir ~/ansible-hosts
 mkdir ~/ansible-playbook-libs

その上で ansible-hosts/servers-linux.hosts を作成しました。

[linux-servers]
192.168.100.133
192.168.100.143
192.168.100.144
192.168.100.155
192.168.100.168
192.168.100.169
192.168.220.202

でもって、 ansible-playbook-libs/server-setup.yml を作成したらこんな感じ。

---
- hosts: linux-servers
  user: root
  tasks:
  - name: upadte exist packages
    yum: name='*' state=latest

  - name: install via yum
    yum: name={{ item }} state=installed
    with_items:
    - bash-completion
    - net-tools
    - bind-utils
    - rsync
    - wget
    - nc
    - tcpdump
    - sysstat
    - lsof
    - strace
    - telnet
    - psmisc
    - zip
    - unzip
    - nfs-utils
    - net-snmp
    - net-snmp-libs

  - name: (0) Stop Firewalld
    service:
      name: firewalld
      enabled: no

  - name: (1) Install selinux module
    yum: name=libselinux-python state=installed

  - name: (2) Disable selinux
    selinux: state=disabled
    register: selinux

  - name: (3) get portnum SSHd
    set_fact:
      ssh_port: "{{ hostvars[inventory_hostname].ansible_port if 'ansible_port' in hostvars[inventory_hostname] else 22 }}"
    when: selinux.reboot_required

  - name: (4) Reboot
    shell: "sleep 2 &amp;&amp; reboot"
    async: 1
    poll: 0
    when: selinux.reboot_required

  - name: (5) wait for reboot complete
    local_action: wait_for host={{ inventory_hostname }} port={{ ssh_port }} state=stopped
    when: selinux.reboot_required

  - name: (6) wait for machine startup
    local_action: wait_for host={{ inventory_hostname }} port={{ ssh_port }} state=started
    when: selinux.reboot_required

実行してみます。

ansible-playbook -i /home/ansible/ansible-hosts/servers-linux-20201114a.hosts server-setup.yml -vvvv

--------------------中略------------------------

PLAY RECAP **
192.168.100.133 : ok=6 changed=1 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0
192.168.100.143 : ok=6 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0
192.168.100.144 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
192.168.100.155 : ok=4 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
192.168.100.168 : ok=4 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
192.168.100.169 : ok=4 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
192.168.220.202 : ok=6 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0

Failedになったのは、どうやらパッケージ管理ソフトの相違があったのが原因っぽいですね。いくらWrapper入れてもyumプラグインで対応できるというわけではどうやらなさそうです。

Windows版も便利そう

Windowsに対して設定自動化をする機能も思った以上に豊富なようで、割と簡単な流れでWindowsに対するアプローチも行えました。ただ、事前準備としてPowerShellの導入が必要そうです。(今回試した環境はWindows Server 2019だったので、殆ど考慮するポイントはないと思いますが・・)

Windowsに対しては先に以下のものを管理者権限を持ったPowerShellウィンドウ上で実行します。

$url = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"
$file = "$env:temp\ConfigureRemotingForAnsible.ps1"
(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)
powershell.exe -ExecutionPolicy ByPass -File $file

$url変数を見れば分かるんですが、このスクリプトを実行するにはインターネットへの接続環境が必要なようです。これの入手が難しいときは、別途上記URLからPowerShellスクリプトファイルをダウンロードしてその場所を指定すれば良いのかなと思います。

結果として、以下の通り出力されてればWinRMが使えるようになってるっぽいです。他にもコマンド叩く必要はありそうだけど、取り敢えずこれだけの確認でちゃんとつながるはつながったス。

Self-signed SSL certificate generated; thumbprint: C65974F03E5C86BFC75DA7EF8FA9258744AEC5D6


wxf                 : http://schemas.xmlsoap.org/ws/2004/09/transfer
a                   : http://schemas.xmlsoap.org/ws/2004/08/addressing
w                   : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
lang                : ja-JP
Address             : http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
ReferenceParameters : ReferenceParameters

OK

hostsの設定

こんな風に定義をしました。Linuxとは異なり、別途varsにていくつかの変数を指定する必要があるようです。

[servers-windows]
Uriel ansible_host=192.168.120.202

[servers-windows:vars]
ansible_user=Administrator
ansible_password=********
ansible_port=5986
ansible_winrm_transport=ntlm
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore

Playbookの作成

こんな風に構成しています。思った以上に多機能でした。

- hosts: windows-servers
  tasks:
    - win_user:
        name: localadms
        password: ********
        state: present
        groups:
          - Administrators
          - Users

    - name: Install all security, critical, and rollup updates without a scheduled task
      win_updates:
        category_names:
          - SecurityUpdates

1つ目はローカルユーザを構成するタスク、2つ目はセキュリティパッチを全て適用するタスクです。既に処理済みのものに動かしたので余り特筆すべきポイントはないんですが、どうやら走ってくれたようです。

$ ansible-playbook -i /home/ansible/ansible-hosts/servers-windows-20201114a.hosts win-server-setup.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

PLAY [servers-windows] ****************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************
ok: [192.168.120.202]
ok: [192.168.100.203]

TASK [win_user] ***********************************************************************************************************************************
fatal: [192.168.100.203]: FAILED! => {"changed": true, "msg": "Failed to remove Domain Users: Exception calling \"Remove\" with \"1\" argument(s): \"この操作をこの特殊グループに対して実行することはできません。\r\n\""}
ok: [192.168.120.202]

TASK [Install all security, critical, and rollup updates without a scheduled task] ****************************************************************
ok: [192.168.120.202]

PLAY RECAP ****************************************************************************************************************************************
192.168.100.203            : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
192.168.120.202            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

特に興味を引いたのは2つ目ですが、実際にどういう挙動を示したかというと以下のような感じでした。

  • Windows Updateの画面からは更新実施してるようには見えない
  • しかし、プロセス一覧を見るとTrusted Installerプロセスが見えており、アップデート処理が行われて居るであろう事は読み取れた
赤枠で囲んだ箇所がWindows Updateの関連プロセス

  • 全部無事に適用されたか?と言うとそうではなく、一部Windows Updateの処理と競合したようで、1つだけパッチがWindows Update経由じゃないとインストールできなくなっていた
  • 試しにWindows Server 2012 R2のドメインコントローラに対してセキュリティアップデート処理を施したら以下のようになった。
fatal: [192.168.100.203]: FAILED! => 
{
	"changed": true, 
	"failed_update_count": 1, 
	"filtered_updates": {}, 
	"found_update_count": 2, 
	"installed_update_count": 1, 
	"msg": "Failed to install one or more updates", 
	"reboot_required": false, 
	"updates": {
		"131c93ce-765a-4fe3-8cca-3c7674361a8c": {
			"categories": [
				"Microsoft SQL Server 2016", 
				"Security Updates"
			], 
			"failed": true, 
			"failure_hresult_code": -2147023293, 
			"id": "131c93ce-765a-4fe3-8cca-3c7674361a8c", 
			"installed": false, 
			"kb": ["4505220"], 
			"title": "SQL Server 2016 Service Pack 2 GDR のセキュリティ更新プログラム (KB4505220)"
		}, 
		"362b5ee9-d309-4e86-a7b9-aae1c112254b": {
			"categories": [
				"Security Updates", "Windows Server 2012 R2"
			], 
			"id": "362b5ee9-d309-4e86-a7b9-aae1c112254b", 
			"installed": true, 
			"kb": ["4566425"], 
			"title": "2020-07x64 ベース システム用 Windows Server 2012 R2 サービス スタック更新プログラム (KB4566425)"
		}
	}
}

その他、サービス状態やインストール処理に関するところを自動化させることが出来そうです。もう少しモジュールの詳細を掘ってみようかなーと考えています。