久々にラズパイにUbuntuを入れて触ろうとしたんですが、毎回初期設定がめんどいですね。
SDカードを焼いて、ラズパイに挿して、LANケーブル挿して、電源を入れる。
その後Ansible Playbookを実行すると固定IPを設定するまでやってくれるようなやつを作りました。
完全に自分用なので、Ubuntu以外(さらにはUbuntu 22.04 以外)では動かない、かなり狭い守備範囲のPlaybookです。
(過去に似たような記事を書いたんですが、それのアップデートっぽい記事です)
https://github.com/kefi550/ansible-raspi
ラズパイ(その他Linux on SBC)の初期設定をAnsibleでやる場合の課題
ラズパイなどシングルボードにLinux入れるときは基本的には、ディスプレイ、キーボードつなげて初回ログインすると思います。これが一番個人的にはめんどく、Ansibleによって自動化したいステップです。
AnsibleでLinuxの初期設定をやるときの主な課題は以下です。
- 初回ログイン時にパスワードリセットを強制される
- DHCPアドレスから固定IPアドレスに変更したときにAnsibleとして接続が途切れてplaybookを継続できなくなる
これらに絞って紹介します。
初回ログイン時のパスワードリセット対応
初回ログインか(パスワードリセットが必要か)の判定
まずタスクはこんな感じ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
- name: ping inventory_hostname
ping:
ignore_unreachable: true
register: ping_inventory_hostname
- name: ping to ip
block:
- name: set static_address, new_user
set_fact:
ansible_user: "{{ users[0].name }}"
ansible_host: "{{ static_address }}"
when:
- ping_inventory_hostname.unreachable is defined
- ping_inventory_hostname.unreachable == true
- name: ping static_ip
ping:
ignore_unreachable: true
register: ping_static_ip
# static_ip への pingが成功しなかったら、dhcp_ip, default_userを使う
- name: set dhcp_address, default_user
set_fact:
ansible_user: "{{ default_user }}"
ansible_password: "{{ default_user_password }}"
ansible_host: "{{ dhcp_address }}"
when:
- ping_static_ip.unreachable is defined
- ping_static_ip.unreachable == true
- name: ping dhcp_ip
ping:
become: true
become_user: "{{ default_user }}"
ignore_unreachable: true
register: ping_dhcp_ip
failed_when:
- ping_dhcp_ip.module_stderr is defined
- '"Your password has expired" not in ping_dhcp_ip.module_stderr'
when:
- ping_static_ip.unreachable is defined
- ping_static_ip.unreachable == true
when:
- ping_inventory_hostname.unreachable is defined
- ping_inventory_hostname.unreachable == true
- name: check password init is required
set_fact:
password_init_required: "{{ ping_dhcp_ip.module_stderr is defined and 'Your password has expired' in ping_dhcp_ip.module_stderr }}"
- name: password init
include_role:
name: password_init
vars:
password_init_host: "{{ ansible_host }}"
password_init_default_user: "{{ default_user }}"
password_init_default_user_password: "{{ default_user_password }}"
when:
password_init_required == true
|
pingモジュールを使いました。
pingモジュールは ssh の疎通をチェックするモジュールです。
ubuntuの初期ログイン時は Your Password has expired
というようなメッセージとともに、pingモジュールとして失敗するようです。
そこでパスワードリセットが必要かどうかの判定は以下です。
- inventory_hostname (基本的にはDNS的な “名前” を想定) に対してssh疎通をチェックする -> このssh疎通ができたらパスワードリセットは必要ない
- static_ip (設定したい固定IPアドレス) に対してssh疎通をチェックする -> このssh疎通ができたらパスワードリセットは必要ない
- dhcp_ip に対してssh疎通をチェックする -> パスワードリセット済みならチェック合格、リセット必要ならチェック不合格になる
pingモジュールは宛先ホストを設定するようなパラメータはありません。
なので、ansible_host
変数を set_fact
を使って書き換えながら判定フローを回しています。
初期パスワードの変更
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
- name: Change password on initial login
delegate_to: 127.0.0.1
expect:
command: ssh {{ ssh_args }} {{ password_init_default_user }}@{{ password_init_host }}
timeout: 10
responses:
"(?i)@.*password": "{{ password_init_default_user_password }}"
"(?i)Current Password": "{{ password_init_default_user_password }}"
"(?i)New password": "{{ tmp_password }}"
"(?i)Retypr new password": "{{ tmp_password }}"
register: change_password_result
- name: change ssh password
set_fact:
ansible_ssh_pass: "{{ tmp_password }}"
|
以前の記事 でも書いたことですが、このようなタスクになりました。
expectモジュールを使ってパスワードリセットのプロンプトに対応しています。
また、パスワードリセット完了後にplaybookの処理を継続させるために、ansible_ssh_pass
変数を set_fact
で新しいパスワード(playbook実行完了までの一時パスワード)に変更しています
IPアドレスを変更したあともplaybookの処理を継続させる
固定IPアドレスを設定しようとすると、基本的には接続先IPが変わることでAnsibleがリモートホストに再接続できなくなり、playbookが継続できなくなります。
今回はnetplanを使って固定IPの設定をしていて、以下のようなタスクになりました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
---
- name: configure netplan
template:
src: netplan_config.yaml.j2
dest: /etc/netplan/config.yaml
become: true
register: configure_netplan
- name: netplan apply
block:
- name: netplan apply
become: true
command: netplan apply
async: 300
poll: 0
when: configure_netplan.changed
register: netplan_apply_result
- name: set ansible_host to static_ip
set_fact:
ansible_host: "{{ static_address }}"
- name: wait for connection to new ip
wait_for_connection:
delay: 10
timeout: 60
when: configure_netplan.changed
|
基本的には netplan apply でIPアドレスが変わって、再接続できなくなり、playbookが止まります。
今回は async
, poll
を使ってタスクを非同期にすることで処理を継続できるようにしました。
netplan applyタスクを非同期で開始した直後に ansible_host
変数を固定IP(新しいIP)に変更します。
その後、wait_for_connection
を使って、新しいIPアドレスへの疎通を確認させます。
まとめ
Ansibleで出来たてのUbuntuの初期設定をスムーズにやる方法を紹介しました。
初期パスワードリセットと、IPアドレス変更がめんどいとこだと個人的には思うので、一旦いい感じのplaybookができてよかったです。