Ansibleでクラウド構築(Azure)

##■初めに
　前回の記事からの続編です。
　前回まででDocker環境にJenkinsとAnsibleのコンテナを作り、JenkinsからAnsible実行までをやりました。今回は最終としてPlaybookを作成しAzureの環境にVMを作成します。またAzureで作成するリソースは｢ResourceGroup｣｢VNET｣｢SUBNET｣｢NSG｣｢VM｣の５つを作成します。最終的にプレイブックをJenkinsジョブで実行するところまで行います。
　これができることによりPlaybookを作りこむことでサーバ一台でWEBUI（Jenkins）からAzureのリソースを手軽に作れる環境になるかと思います。

■予定
　
　V1　：　Dockerインストール,Dockerfile（Jenkins）作成からイメージ起動,コンテナへの直接SSH
　URL　：　[DockerのインストールとDockerFile](https://qiita.com/yo_dazy/private/30b696bb7bf60daa2854)
　V2 　：　Ansibleイメージ起動,コンテナ間のSSH設定,Ansibleの稼働確認
　URL　：　[DockerコンテナでAnsible実行](https://qiita.com/yo_dazy/private/b5ed1fa1bb816b1de560)

　V3 　：　Jenkins設定（JenkinsでのSSH接続）、リモートホストへのジョブ実行
　URL　：　[Jenkinsでコンテナ間接続とジョブ実行](https://qiita.com/yo_dazy/private/34f9f5986155d2044b45)
　<Font Color="Red">
　V4 　：　AnsiblePlaybook（AZVMと関連リソースの作成）、稼働確認(Jenkinsからの実行)
　</font>

■イメージ

![DOC04.png](https://qiita-image-store.s3.amazonaws.com/0/150678/d6bd07bd-ac98-f6ea-142b-b3f701a9fe02.png)

■環境
-AzureVM
　host : VMDO001
　OSType : CentOS 7.3
　Size : Standard D2s v3
-NSG
　SSH (Any/Any) Accept
　7033 (Any/Any) Accept　※後述するコンテナとホストのポートバインドで使用
　8033 (Any/Any) Accept　※後述するコンテナとホストのポートバインドで使用

■playbookで作成する環境

|リソース|名称|種別|
|:-:|:-:|:-:|
|ResourceGroup|RG02|リソースグループ|
|Vnet|VN02|仮想ネットワーク|
|Subnet|SN02|サブネット|
|NSG|NSG02|ネットワークセキュリティグループ|
|VM|VMCE02|仮想マシン|
※ルールに関しては｢SSH｣｢RDP｣を許可

■注意
・Azureのアカウントを持っている事
・マシンがAzureへログイン可能であること

■やること

【0】Docker コンテナ起動
【1】Azure環境へのログイン
【2】サービスプリンシパルの作成
【3】プレイブック作成
【4】Jenkinsジョブ作成,実行
【5】稼働確認

---

##■手順
####【0】Docker コンテナ起動
　（１）．[Dockerホスト]にログインし[Jenkinsコンテナ]と[Ansibleコンテナ]を起動する

```ruby:コマンド(root)(Dockerhost(vmdo01)から)

docker run -p <hostport(8033):8080> -p <hostport(7033):22> -itd --privileged <REPOSITORY(Jenkinsコンテナ):TAG> /sbin/init

docker run -p <hostport(9033):22> -itd --privileged <REPOSITORY(Ansibleコンテナ):TAG> /sbin/init
```

```ruby:Result
[root@VMDO001 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
[root@VMDO001 ~]# docker run -p 8033:8080 -p 7033:22 -itd --privileged centos/jenkinsbuild:latest /sbin/init
efadf1d923a41d08ff413bbb06d71818e5d201a3b648a2108f2191cbc8e5e006
[root@VMDO001 ~]# docker ps -a
CONTAINER ID        IMAGE                        COMMAND             CREATED             STATUS              PORTS                                          NAMES
efadf1d923a4        centos/jenkinsbuild:latest   "/sbin/init"        38 seconds ago      Up 36 seconds       0.0.0.0:7033->22/tcp, 0.0.0.0:8033->8080/tcp   cocky_keller
[root@VMDO001 ~]# docker run -p 9033:22 -itd --privileged centos/ansiblebuild:1 /sbin/init
1650f986860ebb59f505055af5f2c52719e81530d056f70e827f0f275a335208
[root@VMDO001 ~]# docker ps -a
CONTAINER ID        IMAGE                        COMMAND             CREATED             STATUS              PORTS                                          NAMES
1650f986860e        centos/ansiblebuild:1        "/sbin/init"        7 seconds ago       Up 5 seconds        0.0.0.0:9033->22/tcp                           thirsty_kare
efadf1d923a4        centos/jenkinsbuild:latest   "/sbin/init"        3 minutes ago       Up 3 minutes        0.0.0.0:7033->22/tcp, 0.0.0.0:8033->8080/tcp   cocky_keller
[root@VMDO001 ~]#
```

　（２）．[Ansibleコンテナ]と[Jenkinsコンテナ]を起動
　　　　　※「前記事」でSSH設定済みの為「SSHクライアント」からつなげます
　　　　　※　左が[Jenkinsコンテナ]で右が[Ansibleコンテナ]になります

![DOC10.png](https://qiita-image-store.s3.amazonaws.com/0/150678/aacfc312-bf42-d746-4b8a-decb6f8d0018.png)


####【1】Azure環境へのログイン
　[Ansibleコンテナ]からAzureCLIを使用しAzure環境へログインする

　（１）．[Ansibleコンテナ]よりAzure環境へログインする
　　　　　※＜SubScriptionID＞は記憶しておく。

```Ruby:コマンド(root)
az login -u <Azureaccount> -p <password>
```

```Ruby:Result
[root@1650f986860e /]# az login -u <AzureAccount> -p <password>
[
  {
    "cloudName": "AzureCloud",
    "id": "<SubScriptionID>",
    "isDefault": true,
    "name": "<SubScriptionNAME>",
    "state": "<Status>",
    "tenantId": "<TenantID>",
    "user": {
      "name": "<AzureAccount>",
      "type": "<AccountType>"
    }
  }
[root@1650f986860e /]#
```

　（２）．[SubScription]をセットし使用するAzure環境を決定

```Ruby:コマンド(root)
az account set --subscription <SubScriptionID>
```

```Ruby:Result
[root@1650f986860e /]# az account set --subscription <SubScriptionID>
[root@1650f986860e /]#
```

####【2】サービスプリンシパルと資格情報ファイルの作成
　サービスプリンシパルとはユーザーによって作成されたアプリやサービス、オートメーション ツールが特定の Azure リソースにアクセスする際に使うセキュリティ IDになります。AnsibleのplaybookをAzure環境へ実行させる際に必要になります。

【2-1】サービスプリンシパルの作成
　（１）．以下｢Azureコンテナ｣よりコマンドを実行し｢サービスプリンシパル｣を作成する
　　　　　※実行結果をテキスト等へ保存

```Ruby:コマンド(root)
az ad sp create-for-rbac --query [appId,password,tenant]
```

```Ruby:Result
[
  "<appId>",
  "<password>",
  "<tenant>"
]
```

【2-2】資格情報ファイルの作成
　（１）．以下｢Azureコンテナ｣よりコマンドを実行し[資格情報ファイル]を作成する
　　　　　※実行ユーザのホームディレクトリに作成すること（今回はroot）

```Ruby:コマンド(root)
# 実行前に存在しているか確認しなければ作成
mkdir ~/.azure
vi ~/.azure/credentials
```

```Ruby:credentials内容
# 【2-1】実行時に得た情報を記載する
[default]
subscription_id=<SubScriptionID>
client_id=<appId>
secret=<password>
tenant=<tenant>
```

####【3】プレイブック作成
　プロビジョニングを行うファイルを作成します。基本的な構造はベストプラティクスを採用しているつもりですが、違っていたらごめんなさい。筆者の勉強不足です。とりあえず言える事は以下の構成で目的を達成することはできました。

　（１）．以下コマンドを実行しディレクトリを作成

```Ruby:コマンド(root)
mkdir /playbook
mkdir /playbook/host_vars
mkdir /playbook/roles
mkdir -p /playbook/roles/1_azrg/tasks/
mkdir -p /playbook/roles/2_aznsg/tasks/
mkdir -p /playbook/roles/3_azvnet/tasks/
mkdir -p /playbook/roles/4_aznicpub/tasks/
mkdir -p /playbook/roles/5_azvm/tasks/
```

　（２）．以下コマンドを実行し、[roles]を作成
　　　　　※これから作成するファイルでAzure環境へ処理を行わせます。

【1_azrg】

```Ruby:コマンド(root)
vi /playbook/roles/1_azrg/tasks/main.yml
```

```ruby:1_azrg/tasks/main.yml内容
# {{ 文字列 }}は変数
# 変数の値はパラメータとなる
---
- name: Create resourcegroup
  azure_rm_resourcegroup: name={{ rgname }} location={{ reasion }}
```

【2_aznsg】

```Ruby:コマンド(root)
vi /playbook/roles/2_aznsg/tasks/main.yml
```

```ruby:2_aznsg/tasks/main.yml内容
---
- name: Create NSG
  azure_rm_securitygroup:		#モジュール名称
    resource_group: "{{ rgname }}"	#リソースグループ
    name: "{{ nsgname }}"		#NSG名
    rules:				#NSGに関連付けるルール
      - name: SSH			#ルール名
        protocol: Tcp			#使用するプロトコル
        destination_port_range: 22	#ポート番号の指定
        access: Allow			#アクセスのタイプ（Allow,Deny）
        priority: 1001			#ルールの優先度
        direction: Inbound		#ルールのタイプ（受信か送信か）
      - name: RDP						
        protocol: Tcp					
        destination_port_range: 3389	
        access: Allow					
        priority: 1002					
        direction: Inbound				
```

【3_azvnet】
 ｢サブネット｣も同時作成。またネットワークセキュリティグループはサブネットに付与している。

```Ruby:コマンド(root)
vi /playbook/roles/3_azvnet/tasks/main.yml
```

```ruby:3_azvnet/tasks/main.yml内容
---
- name: Create VNET
  azure_rm_virtualnetwork:					# VNET作成モジュール
   resource_group: "{{ rgname }}"			# リソースグループ
   name: "{{ vnname }}"					# VNETの名前
   address_prefixes: "{{ vnaddress }}"		# VNETに付与するアドレス帯

- name: Create SUBNET
  azure_rm_subnet:							# SubNet作成モジュール
   resource_group: "{{ rgname }}"			# リソースグループ
   name: "{{ snname }}"						# SubNetの名前
   address_prefix: "{{ snaddress }}"		# SubNetに付与するアドレス帯
   virtual_network: "{{ vnname }}"			# SubNetが関連するVNET
   security_group_name: "{{ nsgname }}"		# SubNetに紐づくNSG

```

【4_aznicpub】

```Ruby:コマンド(root)
vi /playbook/roles/4_aznicpub/tasks/main.yml
```

```ruby:4_aznicpub/tasks/main.yml内容
---
- name: Create a public ip address
  azure_rm_publicipaddress:					# publicip作成モジュール
    resource_group: "{{ rgname }}"			# リソースグループ
    name: "{{ pubname }}"					# publicipの名前
    allocation_method: Static				# 動的か静的か

- name: Create NIC
  azure_rm_networkinterface:				# Nic作成モジュール
    resource_group: "{{ rgname }}"			# リソースグループ
    name: "{{ nicname }}"					# NICの名前
    virtual_network: "{{ vnname }}"			# 関連付けを行うVNET
    subnet: "{{ snname }}"					# 関連付けを行うSubNet
    public_ip_name: "{{ pubname }}"			# 関連付けを行うpublicip
    security_group: "{{ nsgname }}"			# 関連付けを行うNSG(指定なしでも既定のルールで作られる)
```

【5_azvm】

```Ruby:コマンド(root)
vi /playbook/roles/5_azvm/tasks/main.yml
```

```ruby:5_azvm/tasks/main.yml内容
---
- name: Create VM
  azure_rm_virtualmachine:					# VM作成モジュール
    resource_group: "{{ rgname }}"			# リソースグループ
    name: "{{ machinename }}"				# VM名
    vm_size: "{{ machinesize }}"			# VMサイズ
    network_interface_names: "{{ nicname }}"# 関連付けるNIC
    admin_username: "{{ machineuser }}"		# ログインユーザ名
    admin_password: "{{ userpass }}"		# ユーザパスワード
    os_type: "{{ machinetype }}"                # OStype(Linux Or Windows)
    image:									# OSイメージの設定
      offer: "{{ machineos }}"				# OS（例：CentOS,Windows）
      publisher: "{{ machinepubli }}"		# Publisher名(例：OpenLogic,MicrosoftWindowsServer)
      sku: "{{ machinesku }}"				# エディション(7.3,2016-Datacenter)
      version: "{{ machinever }}"			# イメージファイルVersion
```

　（３）．以下コマンドを実行し、[VARS（インベントリファイル）]を作成
　　　　　※このファイルに上記[roles]で記載した変数の値を記載

```Ruby:コマンド(root)
vi /playbook/host_vars/localhost
```

```ruby:/playbook/host_vars/localhost内容
---
### var ###
machinename: vmce02					# VM名
rgname: RG02						# リソースグループ
reasion: japaneast					# リージョン
nsgname: NSG022						# ネットワークセキュリティグループ
vnname: VN02						# 仮想ネットワーク
vnaddress: <VNETipaddress>			# 仮想ネットワークのアドレス帯
snname: SN02						# サブネット
snaddress: <SubNetipaddress>		# サブネットのアドレス帯
pubname: "{{ machinename }}-pub"	# PublicIP名
nicname: "{{ machinename }}-nic"	# NIC名
machinesize: Standard_DS1_v2		# VMサイズ
machineuser: <user>					# ログインユーザ
userpass: <userpassword>			# ログインユーザのパスワード
machinetype: Linux                                      # OS種別
machineos: CentOS					# OSオファー
machinepubli: OpenLogic				# Publisher
machinesku: '7.3'					# エディション
machinever: latest					# イメージのバージョン
```

　（４）．以下コマンドを実行し、[Site.yml]を作成
　　　　　※role名を書いて記載純通りに実行させる

```Ruby:コマンド(root)
vi /playbook/site.yml
```

```Ruby:/playbook/site.yml内容
---
- name: azresorcegroupmake
  hosts: localhost				# 実行させるグループ
  connection: local				# localhostに対してつなげるときの宣言。SSHではなく直接実行する
  roles:						# 実行させる処理（記載された順番に実行される
   - 1_azrg
   - 2_aznsg
   - 3_azvnet
   - 4_aznicpub
   - 5_azvm
```

####【4】Jenkinsジョブ作成,実行
　前記事で作成したJenkinsコンテナにプレイブックを実行させるためのジョブを作成する。本稿は前の記事と同様の作成手順になりますので詳細は割愛します。
　前記事URL：[Jenkinsでコンテナ間接続とジョブ実行](https://qiita.com/yo_dazy/private/34f9f5986155d2044b45)

　（１）．JenkinsのWEBにアクセスしログイン
　　　　URL：https://＜hostserverIP＞:8033
　　　　login :  jenkinsadmin   …　前記事で作成
　　　　pass  :  <password>　　 …　前記事で作成
　（２）．[新規ジョブ]をクリック
　（３）．以下を入力し「フリースタイル・プロジェクトのビルド」をクリック
　　　　[Enter an item name]　：　ジョブ名（AnsibleJOB2）
　（４）．[ビルド]､[ビルド手順の追加]より[リモートホストでシェルを実行]を選択する
　（５）．SSHサイトとシェルスクリプトにそれぞれ設定を行い保存する
　　　　　SSHサイト：前記事で作成したSSHサイト
　　　　　シェルスクリプト　：　Ansibleコンテナで実行させるコマンド

```ruby:シェルスクリプト
#実行ユーザ確認
whoami
#Ansible実行
ansible-playbook /playbook/site.yml
```

![DOC05.png](https://qiita-image-store.s3.amazonaws.com/0/150678/bb80aa52-2d04-82eb-a3db-130d1122ea8f.png)


　（６）．「保存」して「ビルドを実行」する

　（７）．[実行したビルド]をクリックする

![DOC06.png](https://qiita-image-store.s3.amazonaws.com/0/150678/73e5ce23-7ef3-23c6-3250-e7816415fd21.png)

　（８）．「コンソール出力」をクリックして「コンソール出力内」で｢Success｣を確認

![DOC07.png](https://qiita-image-store.s3.amazonaws.com/0/150678/48da4743-3ae9-b616-eb3f-4a6b75c1dbb9.png)

####【5】稼働確認
　ここまでで目標を達成することはできました。最後は作成した「AzureVM」にログインできることを確認して終了。
　本記事で作成したAzureリソースはページ上部の（■playbookで作成する環境）に記載。

【5-1】稼働確認
　（１）．「Azure Portal」へログイン
　（２）．「作成したリソースグループ（RG02）」へ移動
　（３）．「作成したリソースグループ（RG02）」ないのリソースが「Playbookで作成した内容」と同じかどうか判断する
![DOC08.png](https://qiita-image-store.s3.amazonaws.com/0/150678/472f64e0-d21e-245b-d324-a29444377f4b.png)


　（４）．「作成した仮想マシン」に実際にログインできるかどうか確認する
![DOC09.png](https://qiita-image-store.s3.amazonaws.com/0/150678/5efa9f44-865e-1e86-1c0b-bec8dde17eac.png)

【5-2】コンテナイメージのコミット
　現段階で構築は終了となるため次回実行の為に今現在の｢Ansibleコンテナ｣と｢Jenkinsコンテナ｣の状態をコミットします

　（１）．｢Ansibleコンテナ｣と｢Jenkinsコンテナ｣を[Exit]で閉じる
　（２）．[Dockerホスト]にて以下のコマンドを実行し、｢Ansibleコンテナ｣と｢Jenkinsコンテナ｣をコミットする

```ruby:コマンド[Dockerホスト]
# コミット前状況確認
docker images
# dockerプロセスの確認
docker ps -a
# dockerプロセスをコミット(｢Ansibleコンテナ｣)
docker commit <containerID> centos/ansiblebuild:2
# dockerプロセスをコミット(｢Jenkinsコンテナ｣)
docker commit <containerID> centos/jenkinsbuild:2
# コミット後状況確認
docker images
```

```ruby:Result
[root@VMDO001 ~]# docker ps -a
CONTAINER ID        IMAGE                        COMMAND             CREATED             STATUS              PORTS                                          NAMES
1650f986860e        centos/ansiblebuild:1        "/sbin/init"        27 hours ago        Up 27 hours         0.0.0.0:9033->22/tcp                           thirsty_kare
efadf1d923a4        centos/jenkinsbuild:latest   "/sbin/init"        27 hours ago        Up 27 hours         0.0.0.0:7033->22/tcp, 0.0.0.0:8033->8080/tcp   cocky_keller
[root@VMDO001 ~]# docker commit 1650f986860e centos/ansiblebuild:2
sha256:a072db3085eaefd6e3937d275e2de703aea5abd3ed4ef07d46ef454b16d2e244
[root@VMDO001 ~]# docker commit efadf1d923a4 centos/jenkinsbuild:2
sha256:6eb530cfca1230c1b3cf74ff790e60949ac65c3374fb5ff8a95c2eebee0e9cb7
[root@VMDO001 ~]# docker images
REPOSITORY            TAG                 IMAGE ID            CREATED              SIZE
centos/jenkinsbuild   2                   6eb530cfca12        6 seconds ago        1.12 GB
centos/ansiblebuild   2                   a072db3085ea        About a minute ago   738.8 MB
centos/ansiblebuild   1                   5ff360895e08        27 hours ago         738.8 MB
centos/jenkinsbuild   latest              0640197d6307        47 hours ago         728.7 MB
docker.io/centos      latest              ff426288ea90        6 weeks ago          207.2 MB
[root@VMDO001 ~]#
```

⇒　次回からは以下のイメージを起動させる事でコミット時の状況で起動する

```ruby:[Ansibleコンテナ]起動
docker run -p 9033:22 -itd --privileged --name ansible centos/ansiblebuild:2 /sbin/init
```

```ruby:[Jenkinsコンテナ]起動
docker run -p 8033:8080 -p 7033:22 -itd --privileged --name jenkins centos/jenkinsbuild:2 /sbin/init
```

---
##■最後に
　全４回にわたり、Dockerを起動させてPublicクラウドへのリソース作成環境を作成してみました。
Ansibleの作成していた時に思ったのですが、モジュールはAWSもありました。という事はアカウントとプレイブック次第ではAWSでも同様のことができると思います。
　またDockerでSSH設定を行ったのは2台のコンテナを切り替えながらやるのが面倒だった為それぞれの環境にSSHで切るようにしていました。
　一台のサーバで複数のコンテナを上げることができるDockerは今後外せない技術になるのではないでしょうか？
　また、同時複数のエージェントに対し、ソフトウェアを使用しないでプロビジョニングを行えるAnsibleもやっぱり外せない技術だと思います。