ローカルPCにAnsible環境を構築&AWSのEC2にデプロイしてみた

# Agenda

ここでは、以下の方を対象としています。

- Ansibleを利用する初めの一歩が踏み出せない
- Ansible使いたいけど、何からやればいいかわからない
- 難しいことは分からんが、とりあえずAnsible使ってみたい

Ansibleというツールを知った時、なんて便利なツールなんだと思いましたが、
実際にどうやって使えばいいのか、ぶっちゃけ分からないと私は思っていました。

なので、ここでの目標は

- Ansibleの導入方法
- Ansibleを使ったリモートサーバーへのデプロイ方法

を理解できるようにしていきましょう。

# Ansibleとは？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/c3a34f9c-51c2-44d8-76c7-f0e3bf350d19.png)

他の動画や記事でも分かりやすく説明されているので、ここではざっくりと説明します。

Ansibleというのは、Red Hatという会社が提供しているシステム構成管理ツールです。
Red Hatは、Linuxディストリビューションを提供しているので有名ですかね。

AnsibleはPythonで作られたOSS(オープンソースソフトウェア)です。
YAML形式でコードを記述し、サーバーやNW機器、クラウドリソースを構築、管理、運用などを行えます。
いわゆる、Infrastrucrture as Code (IaC)です。

構成管理ツールには、ChefやPuppetというものもありますが、Ansibleはエージェントレスで、
PythonとSSHが使えればすぐに利用できるのが特徴です。

また、自動化に役立つモジュールが幅広く用意されいてるので、一から作成する必要もないです。

# Ansibleのアーキテクチャ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/7ede1125-135a-d338-5cfb-8c5e87699419.png)

ざっくりとした概要はこんな感じですが、
もう一つおさえておきたいのが、Ansibleのアーキテクチャについてです。

他のサイトでもAnsibleの使い方について、解説されている記事がありますが、
古めの記事とかだとAnsibleのバージョンが2.9以前のものを使っていたりします。
既にサーバーに導入されているAnsibleとかを使っている場合とかは2.9になっているかもしれません。

2.9は既に開発が終了しており、メンテナンス対象外となっているため、アップデートが推奨されています。
Ansible2.9を使う場合は新しいバージョン3系を利用することを推奨します。

ansible2.10以降はansibleパッケージの構成が大幅に変わりました。
少し歴史的な話をすると、ansible2.9までは、ansibleは1つのパッケージで全てのモジュールを管理していました。
そして、その時点でモジュールは約3000程あり、モジュールに新規機能を追加するたびにパッケージも更新させる必要があり、
リリースサイクルが遅いという課題があったそうです。

そこで2.10では、よく使われるプラグインとモジュールをコアパッケージ、
その他の用途別に使うモジュールやプラグインをコレクションという管理方法で分ける構成となりました。

このようにコアパッケージとコレクションという２構成にすることで、リリースサイクルの早いコレクションのモジュールは
適宜リリースして、必要に応じてコレクション経由でモジュールをダウンロードすることができるようになりました。

このコアパッケージは、2.10では`ansible-base`、2.11以降では`ansible-core`という名前で提供されています。

そして、今現在で普及しているパッケージが`Ansible Community Package`であり、これは`ansible-core`と
コミュニティが選択したコレクションをセットにしたパッケージとなります。

今回は、このパッケージを利用していきます。
バージョンは`ansible-core 2.15`を提供する`Ansible Community Package 8.x`系を利用していきます。


バージョンに関する情報は、公式のリリース情報を確認しましょう
[Releases and maintenance](https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html)

# ハンズオン概要

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/fbabcf86-a588-6733-a84d-ba50b264007b.png)

それでは今回のハンズオンの概要についてです。
今回は、ローカルPC上に実際にAnsibleをインストールし、
AWS上のEC2インスタンスに対してデプロイが行える状況まで持っていきます。

1. PC上にAnsibleのインストール、セットアップを行い
2. AWS上のEC2インスタンスに、Ansible実行ユーザーを作成し
3. 実際にEC2に対してping疎通確認を行うためのPlaybookを実行していきます

ハンズオンの手順は具体的には以下のとおり
1. ローカルPC
  - Ansibleインストール
  - Ansible環境構築
2. AWS
   - CloudFormationでテスト環境構築
   - EC2サーバーセットアップ
3. テスト
   - Ansibleプロジェクトフォルダ作成
   - Playbook実行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/ee065636-61cd-aeea-85b0-e4d4c1664446.png)

今回は以下のような環境でハンズオンを行います
OS : MacOS (Ventura)
Python : 3.10
Ansible : 8.5.x (ansible-core 2.15)

また、前提条件は以下のとおり
- AWSアカウントは作成済みであること
- Python3が既にローカルPCにインストールされていること
  - 未インストールの方は事前にインストールをしておいてください。
- EC2インスタンス、VPC環境構築の知識は既に持っていること
  (今回はCloudFormationで実装していく)
- ハンズオン環境はMacを利用します
  - Windows利用の方は、手順に差分が出てくるかもなのでご了承
  - 要望があればWindows版も作成するかも

それでは実際にハンズオンを行っていきましょう。

# 手順

## Python仮想環境(venv)構築
公式では、Pythonのパッケージマネージャー(pip)を使用することが推奨されているので、これで構築します。
pipを使うことで、Pythonの仮想環境virtualenv(venv)を構築でき、python本体の環境を汚さずにAnsibleを使えるようになります。
また、pipはAnsibleで依存するPythonライブラリも自動的に取得してくれるので実装が楽になります。


1. pipインストール確認
```sh
`python3 -m pip -V`
```

2. (option)以下のように出る場合はpipインストールが必要なのでインストールする
```sh
$ python3 -m pip install
/usr/bin/python3: No module named pip
```

3. (option)pipインストール
```sh
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3 get-pip.py --user
```

4. venv作成
```sh
python3 -m venv ansible-venv
```

5. 仮想環境にアクティベート
```sh
source ansible-venv/bin/activate
```

6. (option)仮想環境を抜ける(ディアクティベート)
```sh
deactivate
```

## Ansibleのインストール
仮想環境に入ったら、Ansibleを実際にインストール

1. Ansibleをインストール
```sh
pip install ansible
```

2. インストール確認
```sh
# バージョン確認
ansible --version

# インストール確認
pip list
pip freeze
```

## Ansible実行用のSSHキーペア作成
そしたらAnsibleではデプロイ先のサーバー(Managed Node)にSSH経由で接続をするため、
接続用のキーペアを作成していきます。
ローカルPCで実行していきます。

※ここではAWSの仕様に合わせ、OpenSSHに標準搭載されている`ssh-keygen`を使用していきます。
 OpenSSHをPC上にインストールされていない場合は、事前にインストールをお願いします。

1. ansible実行用SSHキー作成
   ここではEDCSA形式で作成(RSA形式でもok)

```sh
ssh-keygen -t ecdsa -b 521
キーペア名: `~/.ssh/ansible`  # デフォルトの.sshフォルダ配下に配置(場所は任意でok)
キーフレーズ: 入力しない            # キーフレーズは空にする
```

## AWS環境構築

次に、AWS側の環境を構築していきましょう。

まずは、EC2環境を作っていきますが、事前に用意したCloudFormationの
テンプレートを使って作成していきましょう。
テンプレートは以下URLのGitHubテンプレート上に記載していますのでそちらから確認してください。

[006_deploy-ec2-with-ansible](https://github.com/bamtech1904/aws-hacks/tree/main/006_deploy-ec2-with-ansible)

※ AWSにログインし、リージョンが東京リージョン(`ap-northeast-1`)が選択されている状態から始めます。

1. CloudFormationのStacks画面から[Create stack] - [With new resources(standard)]をクリック
   ![ansible-handson01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/d87ed195-bf13-8e60-3fc6-9a77c499727a.png)



2. 事前に用意した`ansible-cfn`ファイルをアップロードしてNextをクリック
  ![ansible-handson02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/2d8f6cd0-9f79-c671-f423-a410588479b2.png)

3. Stack nameを入力してNext(Parameterは変更不要)
   ![ansible-handson03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/60e433b9-76ed-65e6-3741-2a12e63e6c2e.png)

4. Review画面まで遷移し、Capabilitiesの項目にチェックを入れて[Submit]をクリック
   ![ansible-handson04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/b5c56b5c-5abb-ad4b-2652-8b67e411250a.png)

5. StackのStatusがCompleteになったら環境構築完了
   ![ansible-handson05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/c1320b29-b21d-75c8-ec37-eed69559e683.png)

6. 今回のテスト用サーバーが2台作成されているはず
  ![ansible-handson06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/19dc755c-0c52-e62d-bc3e-3be804e3a696.png)


## Manage Node(EC2インスタンス)セットアップ
EC2サーバーにAnsible実行のためのユーザーを設定していきます。

### EC2インスタンスに接続
ここではEC2インスタンスにSession Managerで接続していきます。
SSHで接続したい人は、別途秘密鍵を取得して接続をしてください。

1. EC2のInstances画面から`sv01`の方のインスタンスを選択し[Connect]を選択
   ![ansible-handson07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/ba8c8626-b91c-aca1-fad1-5ed1665b32e2.png)

2. Session Managerタブを選択し、[Connect]を選択
   ![ansible-handson08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/96fe2a2b-6f95-f25c-1276-ec6682b89efe.png)

3. コンソール画面が開くのでコマンドを実行していく
  ![ansible-handson09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/0caa5918-d082-8652-9a44-d224a96d0de7.png)

### Ansible実行ユーザー作成
1. rootに昇格
```sh
sudo su -
```

2. ansible実行ユーザー作成
```sh
useradd -m ansible
```

3. wheelグループに追加
```sh
gpasswd -a ansible wheel
```

4. ansibleユーザーにパスワード設定
```sh
passwd ansible
```

### 公開鍵設定
ユーザー作成が完了したら、SSH接続に必要な公開鍵情報を登録していきます。

1. ansibleユーザーに遷移し、認証ファイル作成
```sh
# Ansible実行ユーザーにスイッチ
su - ansible

# Ansibleユーザーで認証ファイル格納フォルダを作成
mkdir .ssh
touch .ssh/authorized_keys

# 作成したフォルダと認証ファイルの権限レベルを変更
chmod 700 .ssh
chmod 600 .ssh/authorized_keys
```

2. ローカル上で作成したSSH公開鍵情報を`authorized_keys`ファイルにコピペ
```sh
# 以下で公開鍵の情報をコピペ
vi .ssh/authorized_keys

# コントロールノードにSSH接続ができることを確認
ssh ansible@${接続先IP}
```

※ `ssh-copy-id`コマンドでマネージドノードに公開鍵を登録してもok
  その場合はマネージドノード側のSSH設定でパスワード認証を有効化する必要があるので注意。
```sh
ssh-copy-id -i .ssh/{秘密鍵} {ansible}@{host}
``` 

以下の記事とかが分かりやすかった
https://maku.blog/p/2mzbmw8/


3. ローカルからEC2にSSH接続ができることを確認
```sh
ssh -i ~/.ssh/ansible ansible@[パブリックIP]
```

接続できない場合は、.sshフォルダや秘密鍵の権限が違っていたり、
SSH設定が問題になっているかもしれないので見直してみましょう。

## プロジェクトフォルダ作成

プロジェクトフォルダを作成していきます。
ここではGitHub上に事前に用意していたサンプルフォルダを使っていきます。

概要欄に記載のリンクから`sample_project`フォルダをダウンロードしてローカル環境に配置してください。

`sample_project`フォルダの構成は以下の通り
```
sample_project        # プロジェクトルート
├── ansible.cfg       # ansible設定ファイル
├── group_vars
│   └── all.yml       # 全体共通の変数定義ファイル
├── host_vars
│   ├── dev-sv01.yml  # dev-sv01用変数定義ファイル
│   └── dev-sv02.yml  # dev-sv02用変数定義ファイル
├── hosts             # inventoryファイル
├── playbooks
│   └── ping.yml      # ping実行用playbook
└── roles
    └── ping          # ping実行用role
        └── tasks
            └── main.yml
```

### パブリックIP情報を設定

そしたら、`dev-sv01.yml`と`dev-sv02.yml`に、パブリックIP情報を
設定してください。

```yaml
---
ansible_host: xx.xx.xx.xx # replace Public IP
```

### ansible設定ファイルの確認
`ansible.cfg`はプロジェクト固有のAnsibleの動作を設定できるファイルです。

```conf
[defaults]
# リモートホストへの SSH 接続時の秘密鍵の場所
private_key_file = ~/.ssh/ansible

# ロールのデフォルトの場所
roles_path = ./roles

# デフォルトで色を有効化
ansible_color = auto

# リモートコマンドの実行時に警告を非表示にする
ansible_ssh_common_args = -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no

# SSH接続時に公開鍵のフィンガープリントをチェック
host_key_checking=False

[privilege_escalation]
# 特権昇格 (sudo) の設定
become = yes
```

`private_key_file`で、秘密鍵の場所を設定しています。
また、playbookは基本的にsudoで行うものが多いと思うので
`become`プロパティを有効にし、常にsudoでplaybookを実行させています。


## 動作テスト

プロジェクトルートに遷移し、以下コマンドを実行

```sh
ansible-playbook -i hosts playbooks/ping-test.yml
```

問題なければ、以下のようにTASK実行結果がOKで返ってくるはず
```sh
% ansible-playbook -i hosts playbooks/ping-test.yml
PLAY [dev] ***************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [dev-sv01]
ok: [dev-sv02]

TASK [ping : Try to connect to host using an Ansible Playbook] ***********************************************************************************
ok: [dev-sv02]
ok: [dev-sv01]

PLAY RECAP ***************************************************************************************************************************************
dev-sv01                   : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
dev-sv02                   : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```

以上で、Ansible実行に必要な最低限の環境が整いました。


## 後片付け

CloudFormationで作成したスタックは削除しておきましょう。

![ansible-handson10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524408/f3b42994-489c-7ef9-dfc4-e074adcd77c0.png)

継続利用する場合は、課金にご注意ください。

# 終わりに

今回はAnsibleをインストールし、AWSのEC2インスタンスへのデプロイ環境を構築しました。
Ansibleの魅力は、SSH接続が行えれば、新規サーバーへのデプロイもすぐに行えるという点です。

Ansibleの使い方としては、主にサーバー内部の設定などで使うのが良いかなと思うので、
AWS環境はCloudFormattion、サーバー環境はAnsibleで実装することで、
より快適なIaC運用ができるのではと思っています。

興味がある人は、ぜひAnsibleを勉強してみてください。

## 今回使用したリポジトリ

以下です

[006_deploy-ec2-with-ansible](https://github.com/bamtech1904/aws-hacks/tree/main/006_deploy-ec2-with-ansible)