PackerでbuilderをAzureにProvisionをAnsibleでCentOS7イメージ作成しaz＋clud-initでVM作成するまで

ちょっとjenkinsをAzureに移設みたいな話で名前解決まわりで手間取ったりしましたので備忘録を。
（わりと長いかも）

ubuntuでいいし既成イメージでいいひとはこちら
https://docs.microsoft.com/ja-jp/azure/virtual-machines/linux/tutorial-jenkins-github-docker-cicd


PackerとはOSイメージをカスタムビルドするやつです。
書いたとおりにVM起動して処理を入れ込んでイメージを作ってくれる。

###Packer+Azureまわり参考

http://torumakabe.github.io/post/azure_packer_ansible_arm_sp/
https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer

###Packerのテンプレート

**builders**のtypeにawsとかazureとかcloudstackとかのパブリッククラウドかvirtualboxやdockerなどを指定し、
**variables**にある認証情報を`packer build`時に`-var`で一個ずつ変数渡すか`-var-file=`とかで丸ごと別途渡して、
**provisioner**でfileとかchefとかansibleとかshellとかを渡してイメージに埋め込んどきたい処理を指定しbuildすると。


```
{
  "variables": {
    "client_id": "",
    "client_secret": "",
    "resource_group": "",
    "storage_account": "",
    "subscription_id": "",
    "tenant_id": ""
  },
  "builders": [{
    "type": "azure-arm",
    "client_id": "{{user `client_id`}}",
    "client_secret": "{{user `client_secret`}}",
    "resource_group_name": "{{user `resource_group`}}",
    "storage_account": "{{user `storage_account`}}",
    "subscription_id": "{{user `subscription_id`}}",
    "tenant_id": "{{user `tenant_id`}}",
    "capture_container_name": "images",
    "capture_name_prefix": "packer",
    "os_type": "Linux",
    "image_publisher": "OpenLogic",
    "image_offer": "CentOS",
    "image_sku": "7.3",
    "location": "Japan West",
    "vm_size": "Standard_D1"
  }],
  "provisioners": [
    {
      "type": "shell",
      "execute_command": "sudo sh -x {{ .Path }}",
      "scripts": [
        "scripts/centos/centos-first.sh",
        "scripts/centos/ansible-install.sh"
      ]
    },
    {
      "type": "ansible-local",
      "playbook_file": "provision/ansible/baseimage.yml",
      "inventory_file": "provision/ansible/hosts",
      "role_paths": [
        "provision/ansible/roles/baseimage",
        "provision/ansible/roles/zabbix22-agent"
      ]
    },
    {
      "type": "file",
      "source": "tests/centos",
      "destination": "/tmp"
    },
    {
      "type": "shell",
      "execute_command": "sudo sh -x {{ .Path }}",
      "scripts": [
        "scripts/centos/serverspec.sh",
        "scripts/centos/initialization.sh",
        "scripts/centos/azure_deprovision.sh"
      ]
    }
  ]
}
```

provisionersのところでなにしてるかというと、
初期設定で開発ツールグループインストールしたり不要なサービス止めたりansible入れたり
ansibleで最低限のrole（ベースとzabbix-agentなど）プロビジョンさしたり
fileでserverspecのテスト置いてscriptsでserverspecでカンタンなテストさして
ログ消したりwaagentでデプロビジョンして初期化さしたりなどをしてる。

ビルドコマンドの例（Jenkins等でやると履歴がのこったりするのでべんり。）

```
./packer build -var-file=varilables.json builder-template.json
```

あと認証まわりにサービスプリンシパル登録してクレデンシャルにあたるもんを発行しといてビルド実行時に変数として与えるかんじですね
awsでいうとIAMロールつくっといて指定して実行さすみたいなこと。

waagentででプロビジョングして一般化しないとイメージとして利用することができなかったので、注意。
https://docs.microsoft.com/en-us/azure/virtual-machines/linux/build-image-with-packer
https://docs.microsoft.com/ja-jp/azure/virtual-machines/linux/agent-user-guide

Azureのお作法的な話で、
packerでビルドしたあとのそのままのvhdでディスク作ることもできるらしいけど
それだとAWSでいうところのS3ディスクという話になるらしく（s3よりは早いっぽい）
EBS相当の管理ディスクにするにはにイメージ化コマンド打ってそこからVMをCREATEしないといけないと。
terraformでやろうとしたところ自作イメージつかうやりかたが改造しないとできなくて既存ディスクアタッチする方法
でがんばるという感じだったのでそっちは今後に期待ということでcliでいいかなと思って以下のようにしたりなど。

```
#イメージつくる
az image create \
    --resource-group myresoucegrp \
    --name packer-c7zbx22ssd \
    --os-type linux \
    --source https://mystrageaccountname.blob.core.windows.net/system/Microsoft.Compute/Images/images/packer-osDisk.50a3795a-26a4-42ca-a3c1-****.vhd

#VMつくる
az vm create -g myresoucegrp -n vmname01 -l japanwest \
 --os-disk-caching ReadWrite  \
 --image packer-c7zbx22ssd \
 --size Standard_DS1 --nics vmname01-nic1 \
 --admin-username myuser-op  --admin-password xxxxxxxxxxxx \
 --custom-data cloud-init-jenkins.txt 
```

ここでも地味にAzure不慣れではまったりなど。
nic指定してればnsg(securitygroup)とPIP(EIP)とVNET(VPC)の指定はそこに紐づいてるから要らなかった等。
詳しい人に教えていただいてとても助かりました。

###cloudinitでのプロビジョンでの右往左往

結果として以下のような感じに。

```
#cloud-config
write_files:
  - path: /etc/dhclient-eth0.conf
    content: |
      prepend domain-name-servers 127.0.0.1;
  - path: /etc/resolv.conf
    content: |
      nameserver 8.8.8.8
      nameserver 8.8.8.4
yum_repos:
    jenkins:
        name: Jenkins
        baseurl: http://pkg.jenkins.io/redhat
        gpgcheck: true
packages:
  - java
  - docker
runcmd:
  - rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
  - yum -y install jenkins
  - gpasswd -a jenkins dockerroot
  - gpasswd -a myuser-op dockerroot
  - usermod -s /bin/bash jenkins
  - service jenkins restart
  - service network restart
  - systemctl start docker
  - systemctl enable docker
  - chown root:dockerroot /var/run/docker.sock
```

名前解決まわりの右往左往
http://blog.kenjiskywalker.org/blog/2015/02/07/update-resolv-conf/
http://blog.father.gedow.net/2016/02/12/networkmanager-dnsmasq/
http://mikotosnow.blog.fc2.com/blog-entry-11.html
http://server.etutsplus.com/centos-7-dns-resover/
https://thinkit.co.jp/story/2014/12/25/5412?page=0%2C1

IF設定のPeerDNSがyesでdhcpがクラウドのDNSをresolv.confに上書きするのは別にいいかなと思うが
今回ダメだったのはNetworkManagerがnameserver行を消すという悪行のほうだった。
(イメージ作成段階で仕込んでもwaagentがresolv.confを消す動きをすることもあり)
その名前解決ができないおかげであとにつづくyumインストールとかアップデートやwgetが根こそぎ失敗していた。

MSのCentOSベースのLinuxVMをつくる手順
https://docs.microsoft.com/en-us/azure/virtual-machines/linux/create-upload-centos
なぜかCentOS6はNetworkManager無効にしててNM_CONTROLLED=no、CentOS7だとNetworkManagerは停止せずNM_CONTROLLED=noで
そのおかげで今回のresolv.conf上書き事件が発生することになったような気がする。

cloud-initの実行順序などの話
http://qiita.com/toshihirock/items/81d6612511f0d1f5db77
http://dev.classmethod.jp/cloud/aws/probe-cloud-init-exec-timing/

とりあえずサンプルをじっと眺めると使い方はなんとなくわかる
http://cloudinit.readthedocs.io/en/latest/topics/examples.html
http://dev.classmethod.jp/cloud/aws/cloud-init-tips/

cloud_initもterraformなどと同様にプロビジョナー連携明示的につかえるのはchefとpuppetのみでansibleはなかった
cloud_initではなくてAutoScalingのLifecyclewookとかでansible使ってる人はいた。
http://dev.classmethod.jp/cloud/aws/using-ansible-at-autoscaling-launching/
runcmdでgit cloneでもってきて移動してansible-playbookで適用するくらいはできるのかもしれないけど試してはない。

cloud-init検証中にvm作りなおすのがめんどい人が動確シェルをつくってた（使ってない）
http://qiita.com/ngyuki/items/e2d0c13f54054e4f35e4

cloud-initでyumrepo指定はできるけどgpg鍵が事前準備が要るようだった
http://qiita.com/makisyu/items/982b4089656026c89c09

こんどはrumcmdに指定したコマンドがちゃんと動かなくてディレクトリ移動や変数が要らないように修正した。
マルチパートでシェル形式との共存方法を書いてる人は居た。
http://dev.classmethod.jp/cloud/aws/cloud-init-use-shell-and-cloud-config/

イメージ化の時にcloud-initのスクリプト置いてもよさそうではあった（共通な処理なら）
https://diary.shu-cream.net/2015/06/01/cloud-init-per-xxx.html

まともに動くイメージが出来上がるまでに主にNetworkManagerが名前解決まわり上書きする動きをつきとめるのに苦労して20回弱イメージ作り直しました。

waagentが居たら(≒Azureだったら)NetworkManager無効化処理をイメージ作成時のAnsibleのベースroleにいれた

```
- name: check if cloud provider is azure
  action: command [ -x /usr/sbin/waagent ]
  register: result
  ignore_errors: True
  tags: check-if-provider-is-azure
- name: disable NetworkManager on azure
  service:
    name: NetworkManager
    enabled: no
  tags: disable-NetworkManager-azure
  when: result|succeeded
```

###ADによせるjenkinsアカウント管理

http://iseebi.hatenablog.com/entry/2017/07/20/004458