Skip to main content

테라폼으로 실행하는 사용자 스크립트

이번 포스팅은 테라폼을 사용하여 NHN Cloud의 사용자 스크립트를 실행하는 과정에 있어서 에러 로그를 확인하여 트러블 슈팅하는 과정을 담았습니다.

테라폼을 사용하여 인스턴스를 프로비저닝하면 인스턴스 구동에 필요한 여러 구성들을 하게 되고 이 구성이 끝나고 지정한 파일에 담긴 스크립트를 실행할 수 있도록 인스턴스 및 가상머신 안에 user_data 키를 제공하고 있습니다.

하지만 스크립트 적용이 잘 되지 않아서 아래와 같이 로그를 확인하였습니다. 사용자 스크립트 로그는 보통 /var/log/cloud-init-output.log에서 확인할 수 있으며 아래와 같은 내용입니다.

/var/log/cloud-init-output.log

Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'init-local' at Sat, 20 May 2023 12:24:17 +0000. Up 5.26 seconds.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'init' at Sat, 20 May 2023 12:24:24 +0000. Up 12.18 seconds.
ci-info: +++++++++++++++++++++++++++++++++++++++Net device info+++++++++++++++++++++++++++++++++++++++
ci-info: +--------+------+------------------------------+---------------+--------+-------------------+
ci-info: | Device | Up | Address | Mask | Scope | Hw-Address |
ci-info: +--------+------+------------------------------+---------------+--------+-------------------+
ci-info: | eth0 | True | 192.168.0.10 | 255.255.255.0 | global | fa:16:3e:ba:c7:53 |
ci-info: | eth0 | True | fe80::f816:3eff:feba:c753/64 | . | link | fa:16:3e:ba:c7:53 |
ci-info: | lo | True | 127.0.0.1 | 255.0.0.0 | host | . |
ci-info: | lo | True | ::1/128 | . | host | . |
ci-info: +--------+------+------------------------------+---------------+--------+-------------------+
ci-info: +++++++++++++++++++++++++++++++Route IPv4 info+++++++++++++++++++++++++++++++
ci-info: +-------+---------------+-------------+-----------------+-----------+-------+
ci-info: | Route | Destination | Gateway | Genmask | Interface | Flags |
ci-info: +-------+---------------+-------------+-----------------+-----------+-------+
ci-info: | 0 | 0.0.0.0 | 192.168.0.1 | 0.0.0.0 | eth0 | UG |
ci-info: | 1 | 8.8.8.8 | 192.168.0.1 | 255.255.255.255 | eth0 | UGH |
ci-info: | 2 | 164.x.x.x | 192.168.0.1 | 255.255.255.255 | eth0 | UGH |
ci-info: | 3 | 192.168.0.0 | 0.0.0.0 | 255.255.255.0 | eth0 | U |
ci-info: | 4 | 192.168.0.0 | 0.0.0.0 | 255.255.255.0 | eth0 | U |
ci-info: | 5 | 192.168.0.1 | 0.0.0.0 | 255.255.255.255 | eth0 | UH |
ci-info: +-------+---------------+-------------+-----------------+-----------+-------+
ci-info: +++++++++++++++++++Route IPv6 info+++++++++++++++++++
ci-info: +-------+-------------+---------+-----------+-------+
ci-info: | Route | Destination | Gateway | Interface | Flags |
ci-info: +-------+-------------+---------+-----------+-------+
ci-info: | 1 | fe80::/64 | :: | eth0 | U |
ci-info: | 3 | local | :: | eth0 | U |
ci-info: | 4 | multicast | :: | eth0 | U |
ci-info: +-------+-------------+---------+-----------+-------+
2023-05-20 12:24:24,193 - schema.py[WARNING]: Invalid cloud-config provided: Please run 'sudo cloud-init schema --system' to see the schema errors.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'modules:config' at Sat, 20 May 2023 12:24:26 +0000. Up 14.14 seconds.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'modules:final' at Sat, 20 May 2023 12:24:26 +0000. Up 14.58 seconds.
Created symlink /etc/systemd/system/multi-user.target.wants/toast-sysmon.service → /etc/systemd/system/toast-sysmon.service.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 finished at Sat, 20 May 2023 12:24:27 +0000. Datasource DataSourceOpenStackLocal [net,ver=2]. Up 15.80 seconds

위 /var/log/cloud-init-output.log 파일의 내용을 살펴보면, 사용자 스크립트 (user_data)가 실행되지 않은 것으로 보입니다. cloud-init의 동작은 다음의 단계로 진행되는데, 각 단계가 로그로 기록됩니다.

    1. init-local: 네트워크를 사용하지 않는 초기화
    1. init: 네트워크를 사용하는 초기화
    1. modules:config: 시스템 및 애플리케이션 구성
    1. modules:final: 부트 프로세스가 끝난 후 실행되는 모듈

위 단계에서 사용자 스크립트는 modules:config에서 실행되는데 여기서 특별한 동작이나 에러가 없고 modules:final에서도 문제가 없는 것으로 확인됩니다.

하지만 로그 "Invalid cloud-config provided: Please run 'sudo cloud-init schema --system' to see the schema errors." 경고 메시지를 확인하여 경고문에서 알려준 명령어를 실행해봅니다.

sudo cloud-init schema --system
...
Error:
Cloud config schema errors: format-l1.c1: File None needs to begin with "#cloud-config

이 에러 내용으로 볼 때 cloud-init이 Cloud-Config 형식의 스크립트 파일에서 #cloud-config으로 시작하지 않아서 발생한 것으로 추측되어 셔뱅(#!/bin/bash)위에 #cloud-config을 추가해서 인스턴스만 다시 재생성해보겠습니다.

# 테라폼 리소스 조회
terraform state list

# 재생성하려는 테라폼 리소스 인스턴스만 지정
terraform taint openstack_compute_instance_v2.vm

# plan
terraform plan

# apply
terraform apply -auto-approve -lock=false

/var/log/cloud-init-output.log


Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'init-local' at Sat, 20 May 2023 14:20:52 +0000. Up 5.19 seconds.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'init' at Sat, 20 May 2023 14:21:01 +0000. Up 13.53 seconds.
ci-info: +++++++++++++++++++++++++++++++++++++++Net device info+++++++++++++++++++++++++++++++++++++++
ci-info: +--------+------+------------------------------+---------------+--------+-------------------+
ci-info: | Device | Up | Address | Mask | Scope | Hw-Address |
ci-info: +--------+------+------------------------------+---------------+--------+-------------------+
ci-info: | eth0 | True | 192.168.0.10 | 255.255.255.0 | global | fa:16:3e:48:c9:40 |
ci-info: | eth0 | True | fe80::f816:3eff:fe48:c940/64 | . | link | fa:16:3e:48:c9:40 |
ci-info: | lo | True | 127.0.0.1 | 255.0.0.0 | host | . |
ci-info: | lo | True | ::1/128 | . | host | . |
ci-info: +--------+------+------------------------------+---------------+--------+-------------------+
ci-info: +++++++++++++++++++++++++++++++Route IPv4 info+++++++++++++++++++++++++++++++
ci-info: +-------+---------------+-------------+-----------------+-----------+-------+
ci-info: | Route | Destination | Gateway | Genmask | Interface | Flags |
ci-info: +-------+---------------+-------------+-----------------+-----------+-------+
ci-info: | 0 | 0.0.0.0 | 192.168.0.1 | 0.0.0.0 | eth0 | UG |
ci-info: | 1 | 8.8.8.8 | 192.168.0.1 | 255.255.255.255 | eth0 | UGH |
ci-info: | 2 | 164.x.x.x | 192.168.0.1 | 255.255.255.255 | eth0 | UGH |
ci-info: | 3 | 192.168.0.0 | 0.0.0.0 | 255.255.255.0 | eth0 | U |
ci-info: | 4 | 192.168.0.0 | 0.0.0.0 | 255.255.255.0 | eth0 | U |
ci-info: | 5 | 192.168.0.1 | 0.0.0.0 | 255.255.255.255 | eth0 | UH |
ci-info: +-------+---------------+-------------+-----------------+-----------+-------+
ci-info: +++++++++++++++++++Route IPv6 info+++++++++++++++++++
ci-info: +-------+-------------+---------+-----------+-------+
ci-info: | Route | Destination | Gateway | Interface | Flags |
ci-info: +-------+-------------+---------+-----------+-------+
ci-info: | 1 | fe80::/64 | :: | eth0 | U |
ci-info: | 3 | local | :: | eth0 | U |
ci-info: | 4 | multicast | :: | eth0 | U |
ci-info: +-------+-------------+---------+-----------+-------+
2023-05-20 14:21:01,781 - util.py[WARNING]: Failed loading yaml blob. Yaml load allows (<class 'dict'>,) root types, but got str instead
2023-05-20 14:21:01,788 - util.py[WARNING]: Failed loading yaml blob. Yaml load allows (<class 'dict'>,) root types, but got str instead
2023-05-20 14:21:01,788 - cloud_config.py[WARNING]: Failed at merging in cloud config part from part-001: empty cloud config
2023-05-20 14:21:01,803 - schema.py[WARNING]: Invalid cloud-config provided: Please run 'sudo cloud-init schema --system' to see the schema errors.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'modules:config' at Sat, 20 May 2023 14:21:03 +0000. Up 15.54 seconds.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'modules:final' at Sat, 20 May 2023 14:21:04 +0000. Up 15.89 seconds.
Created symlink /etc/systemd/system/multi-user.target.wants/toast-sysmon.service → /etc/systemd/system/toast-sysmon.service.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 finished at Sat, 20 May 2023 14:21:05 +0000. Datasource DataSourceOpenStackLocal [net,ver=2]. Up 17.39 seconds

이번엔 내용이 전과 다르지만 여전히 사용자 스크립트가 실행되지 않고 있는데요. 이번 에러 메시지들은 cloud-init이 'user_data'에 제공된 정보를 YAML로 해석하려고 시도했으나 해당 데이터가 올바른 형식이 아니라고 합니다. 이 에러 메시지는 이번에 #cloud-config를 content-type이 text/cloud-config 파일이 아닌 text/x-shellscript인 파일에 추가해서 에러가 발생하였습니다.

sudo cloud-init schema --system
...
Valid cloud-config: system userdata

아래는 각 파일명과 수정한 내용입니다.

cloutinit.tf

data "cloudinit_config" "cloudinit-example" {
gzip = false
base64_encode = false

part {
filename = "init.cfg"
content_type = "text/cloud-config"
content = file("scripts/init.cfg")
}

part {
content_type = "text/x-shellscript"
content = file("scripts/payload.sh")
}
}

init.cfg

#cloud-config

repo_update: true
repo_upgrade: all

packages:
- lvm2

runcmd:
- echo 'Hello, World!' > /home/ubuntu/helloworld.txt
- [ ls, -l, /home/ubuntu ]
- [ sh, -xc, "echo $(date) ': hello world!'" ]
- [ sh, -c, echo "=========End of cloud-init UserData========" ]


output:
all: '| tee -a /var/log/cloud-init-output.log'

final_message: "Cloud init is done!"

payload.sh

#!/bin/bash

echo 'Hello, World!' > /home/ubuntu/helloworld2.txt

/var/log/cloud-init-output.log

2023-05-20 15:20:58,514 - schema.py[WARNING]: Invalid cloud-config provided: Please run 'sudo cloud-init schema --system' to see the schema errors.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'modules:config' at Sat, 20 May 2023 15:21:00 +0000. Up 13.64 seconds.
Cloud-init v. 22.4.2-0ubuntu0~22.04.1 running 'modules:final' at Sat, 20 May 2023 15:21:00 +0000. Up 14.02 seconds.
Created symlink /etc/systemd/system/multi-user.target.wants/toast-sysmon.service → /etc/systemd/system/toast-sysmon.service.
total 4
-rw-r--r-- 1 root root 14 May 21 00:21 helloworld.txt
+ date
+ echo Sun May 21 00:21:02 KST 2023 : hello world!
Sun May 21 00:21:02 KST 2023 : hello world!
=========End of cloud-init UserData========
Cloud init is done!

위 로그를 보면 제가 작성한 스크립트가 모두 잘 실행된 것으로 나오며 /home/ubuntu 경로에도 helloworld2.txt 파일과 helloworld.txt 파일 모두 잘 생성된 것을 확인하여 이로써 포스팅을 마치도록 하겠습니다.