AnsibleコンテナでPackerを起動するのにドハマりした

■はじめに

約1週間ぶりのエントリとなります。
サボってた訳ではなくて、チョーハマってました。。

何に? AnsibleコンテナからPackerを起動するのにです。

以前諸事情によってAnsibleコンテナを組んでいたのですが、今回も諸事情によりAnsibleコンテナ上にPackerを構築する必要があったのですが、まーうまくいかなかった。
今回はこの備忘録になります。

■今回の問題点

結論から書くと2点ありました。

1つ目:コンテナから普通にPacker起動するとAnsibleプレイブックをデフォルトユーザで無条件に起動する

ScaleSetを準備するために今回もPackerでイメージを作成したのですが、以前と異なる点はコンテナからの起動という部分です。

PackerのprovisionersでPlaybookを流用するためにはAnsibleの動作環境上にPackerを準備する必要があります。
なのでAnsibleコンテナ内にPackerをインストールすることになります。

まずDockerfileに以下の記述を追記してPackerが実行できるようにします。

RUN wget https://releases.hashicorp.com/packer/1.6.1/packer_1.6.1_linux_amd64.zip 
RUN unzip packer_1.6.1_linux_amd64.zip 
RUN mv packer /usr/bin

(中略)

COPY packer_node_cru.json /etc/ansible

(中略)

CMD ["packer", "build", "packer_node.json"]

packer.jsonはまずは以前のものを使用します。

これを起動するとどうなるかというと

    azure-arm: TASK [Gathering Facts] *********************************************************
    azure-arm:
    azure-arm: fatal: [default]: UNREACHABLE! => {"changed": false, "msg": "Failed to create temporary directory.In some cases, you may have been able to authenticate and did not have permissions on the target directory. Consider changing the remote tmp path in ansible.cfg to a path rooted in \"/tmp\", for more error information use -vvv. Failed command was: ( umask 77 && mkdir -p \"` echo /root/.ansible/tmp `\"&& mkdir /root/.ansible/tmp/ansible-tmp-1598529517.5336177-86-76721091478119 && echo ansible-tmp-1598529517.5336177-86-76721091478119=\"` echo /root/.ansible/tmp/ansible-tmp-1598529517.5336177-86-76721091478119 `\" ), exited with result 1", "unreachable": true}
    azure-arm:
    azure-arm: PLAY RECAP *********************************************************************
    azure-arm: default                    : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0

と言って怒られる訳です。
どうも/root/.ansible/tmpがなさそう。

で、Failed command was:以下のエラーについてググると諸説出てきてよくわからなかったのですが、一番参考になったエントリはこちらでした。
感謝。

www.ketsuago.com

このエントリを見た後暫し考察。

ssh先のホームディレクトリが存在しない?
root?どこが?Ansibleでプロビジョニングしてる対象環境が?
それってAzureでVM立てた際のuserだよね。。?
rootではないから当然落ちる。。

あー!

そうなんです。ボクはコンテナから実行しているのです。コンテナのユーザって誰でしたっけ?
そう、rootです。
でもそもそもplaybookでremote user設定してなかったっけ?
これが最大の罠でした。

www.packer.io

Note: Any remote_user defined in tasks will be ignored. Packer will always connect with the user given in the json config for this provisioner.

なんとremote-userは無視されます。
という訳でコンテナのユーザを変えないといけないですね。

以下が正解。

# syntax=docker/dockerfile:1.1-experimental
FROM alpine:3.12.0

RUN adduser -S [Azureのユーザ] root

RUN set -x \
  && apk add --no-cache \

(中略)

RUN --mount=type=secret,id=ssh,target=/root/.ssh/id_rsa \
  cat ~/.ssh/id_ed25519.pub | ssh -i /root/.ssh/id_rsa [Azureのユーザ]@selenoid-qa-example-02.eastus.cloudapp.azure.com 'cat >> .ssh/authorized_keys'


USER [Azureのユーザ]

#CMD ["packer", "inspect", "packer_node_cru.json"]
CMD ["packer", "build", "packer_node_cru.json"]

packerコマンドを叩く前にAzureVMユーザにしましょう。
(ここにたどり着くまでに紆余曲折あってえらい時間を食ってしまいました。。)

その2:packerにサービスプリンシパルをホスト環境変数から設定する

これは比較的すんなり行ったのですが、ちょっと詰まったのはsyntaxの部分。

{
  "variables":{
    "client_id": "{{env `ARM_CLIENT_ID`}}",
    "client_secret": "{{env `ARM_CLIENT_SECRET`}}",
    "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}",
    "tenant_id": "{{env `ARM_TENANT_ID`}}"
  },
  "builders":[{
    "type": "azure-arm",

    "client_id": "{{user `client_id`}}",
    "client_secret": "{{user `client_secret`}}",
    "subscription_id": "{{user `subscription_id`}}",
    "tenant_id": "{{user `tenant_id`}}",

    "managed_image_resource_group_name": "tfrg",

(以下省略)

ホストで環境変数を設定し、それをdocker run時に-eで渡します。

> docker container run -itd -e ARM_CLIENT_ID -e ARM_CLIENT_SECRET -e ARM_SUBSCRIPTION_ID -e ARM_TENANT_ID --name ansible_packer test/ansible_packer:0.1

前提としてARM_CLIENT_ID・ARM_CLIENT_SECRET・ARM_SUBSCRIPTION_ID・ARM_TENANT_IDは全てホスト側でexportしています。
これで実行するとサービスプリンシパルをどこにも書かないで済むのですが、ボクはこの `(バッククォート)をずっと'(シングルクォート)だと思い込んでいました。

error interpolating default value for 'client_id': template: root:1: malformed
character constant: 'ARM_CLIENT_ID'

おかげで全然動きゃしないというorz。

■改めて動作した記載

Dockerfile_packer

# syntax=docker/dockerfile:1.1-experimental
FROM alpine:3.12.0

RUN adduser -S [Azureのユーザ] root

RUN set -x \
  && apk add --no-cache \
    bash \
    wget \
    ansible \
    python3 \
    openssh-client

RUN set -x \
  && mkdir -p /etc/ansible/selenoid \
  && mkdir -p -m 0700 ~/.ssh \
  && ssh-keyscan selenoid-qa-example-02.eastus.cloudapp.azure.com >> ~/.ssh/known_hosts \
  && wget https://releases.hashicorp.com/packer/1.6.1/packer_1.6.1_linux_amd64.zip \
  && unzip packer_1.6.1_linux_amd64.zip \
  && mv packer /usr/bin

COPY inventory_node.ini /etc/ansible
COPY playbook_node_docker.yml /etc/ansible
COPY ansible.cfg /etc/ansible
COPY ./selenoid /etc/ansible/selenoid
COPY packer_node_cru.json /etc/ansible

WORKDIR /etc/ansible

RUN ssh-keygen -q -N '' -t ed25519 -f ~/.ssh/id_ed25519
RUN --mount=type=secret,id=ssh,target=/root/.ssh/id_rsa \
  cat ~/.ssh/id_ed25519.pub | ssh -i /root/.ssh/id_rsa [Azureのユーザ]@selenoid-qa-example-02.eastus.cloudapp.azure.com 'cat >> .ssh/authorized_keys'

USER [Azureのユーザ]

#CMD ["packer", "inspect", "packer_node_cru.json"]
CMD ["packer", "build", "packer_node_cru.json"]

packer_node_cru.json

{
  "variables":{
    "client_id": "{{env `ARM_CLIENT_ID`}}",
    "client_secret": "{{env `ARM_CLIENT_SECRET`}}",
    "subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}",
    "tenant_id": "{{env `ARM_TENANT_ID`}}"
  },
  "builders":[{
    "type": "azure-arm",

    "client_id": "{{user `client_id`}}",
    "client_secret": "{{user `client_secret`}}",
    "subscription_id": "{{user `subscription_id`}}",
    "tenant_id": "{{user `tenant_id`}}",

    "managed_image_resource_group_name": "tfrg",
    "managed_image_name": "selenoidImage",

    "os_type": "Linux",
    "image_publisher": "Canonical",
    "image_offer": "UbuntuServer",
    "image_sku": "18.04-LTS",

    "azure_tags": {
        "dept": "Engineering",
        "task": "Image deployment"
    },

    "location": "eastus",
    "vm_size": "Standard_F2"
  }],
  "provisioners":[
    {
      "type": "ansible",
      "playbook_file": "playbook_node_docker.yml"
    }
  ]
}

準備が終わったら以下を実行

その1:環境変数の設定(ホスト側)

>export ARM_CLIENT_ID="appID"
>export ARM_CLIENT_SECRET="パスワード"
>export ARM_SUBSCRIPTION_ID="サブスクリプションID"
>export ARM_TENANT_ID="テナントID"

その2:docker image build

> docker image build --no-cache -f Dockerfile_packer -t test/ansible_packer:0.1 --secret id=ssh,src=$HOME/.ssh/id_rsa .

その3:docker container run

> docker container run -itd -e ARM_CLIENT_ID -e ARM_CLIENT_SECRET -e ARM_SUBSCRIPTION_ID -e ARM_TENANT_ID --name ansible_packer test/ansible_packer:0.1

うまくいけばしっかりImageが作成できています。
f:id:theboyalex:20200828224901p:plain



いやー、今回はまじで苦しんだ。色々と。

ではでは。