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

  • 0
    Like
  • 0
    Comment

    ちょっと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