Puppet คือระบบการจัดการการกำหนดค่า ใช้เพื่อนำโฮสต์ไปสู่สถานะที่ต้องการและรักษาสถานะนี้
ฉันทำงานกับ Puppet มากว่าห้าปีแล้ว ข้อความนี้เป็นการรวบรวมประเด็นสำคัญจากเอกสารอย่างเป็นทางการที่แปลและจัดเรียงใหม่ ซึ่งจะช่วยให้ผู้เริ่มต้นเข้าใจสาระสำคัญของ Puppet ได้อย่างรวดเร็ว
ข้อมูลพื้นฐาน
ระบบปฏิบัติการของ Puppet คือไคลเอนต์-เซิร์ฟเวอร์ แม้ว่าจะรองรับการทำงานแบบไร้เซิร์ฟเวอร์ด้วยฟังก์ชันที่จำกัดก็ตาม
มีการใช้โมเดลการดำเนินการแบบดึง: ตามค่าเริ่มต้น ทุกๆ ครึ่งชั่วโมง ไคลเอนต์จะติดต่อกับเซิร์ฟเวอร์เพื่อขอการกำหนดค่าและนำไปใช้ หากคุณเคยทำงานกับ Ansible พวกเขาจะใช้โมเดลพุชอื่น: ผู้ดูแลระบบเริ่มกระบวนการใช้การกำหนดค่า ลูกค้าเองก็จะไม่ใช้สิ่งใดเลย
ในระหว่างการสื่อสารผ่านเครือข่าย จะใช้การเข้ารหัส TLS แบบสองทาง: เซิร์ฟเวอร์และไคลเอนต์มีคีย์ส่วนตัวและใบรับรองที่เกี่ยวข้อง โดยทั่วไปแล้วเซิร์ฟเวอร์จะออกใบรับรองให้ไคลเอ็นต์ แต่โดยหลักการแล้ว คุณสามารถใช้ CA ภายนอกได้
ความรู้เบื้องต้นเกี่ยวกับแถลงการณ์
ในศัพท์เฉพาะของหุ่นเชิด ไปยังเซิร์ฟเวอร์หุ่นเชิด เชื่อมต่อ โหนด (โหนด) มีการเขียนการกำหนดค่าสำหรับโหนด ในแถลงการณ์ ในภาษาโปรแกรมพิเศษ - Puppet DSL
Puppet DSL เป็นภาษาประกาศ อธิบายสถานะที่ต้องการของโหนดในรูปแบบของการประกาศทรัพยากรแต่ละรายการ เช่น:
- ไฟล์นี้มีอยู่และมีเนื้อหาเฉพาะ
- ติดตั้งแพ็คเกจแล้ว
- การบริการได้เริ่มต้นขึ้นแล้ว
ทรัพยากรสามารถเชื่อมโยงถึงกัน:
- มีการขึ้นต่อกันซึ่งส่งผลต่อลำดับการใช้ทรัพยากร
ตัวอย่างเช่น “ก่อนอื่นให้ติดตั้งแพ็คเกจ จากนั้นแก้ไขไฟล์การกำหนดค่า จากนั้นเริ่มบริการ” - มีการแจ้งเตือน - หากทรัพยากรมีการเปลี่ยนแปลง ทรัพยากรจะส่งการแจ้งเตือนไปยังทรัพยากรที่สมัครเป็นสมาชิก
ตัวอย่างเช่น หากไฟล์การกำหนดค่าเปลี่ยนแปลง คุณสามารถเริ่มบริการใหม่ได้โดยอัตโนมัติ
นอกจากนี้ Puppet DSL ยังมีฟังก์ชันและตัวแปร ตลอดจนคำสั่งและตัวเลือกแบบมีเงื่อนไข รองรับกลไกการสร้างเทมเพลตต่างๆ - EPP และ ERB
Puppet เขียนด้วยภาษา Ruby ดังนั้นโครงสร้างและคำศัพท์จำนวนมากจึงถูกนำมาจากที่นั่น Ruby ช่วยให้คุณสามารถขยาย Puppet - เพิ่มตรรกะที่ซับซ้อน ทรัพยากรประเภทใหม่ และฟังก์ชันต่างๆ
ขณะที่ Puppet กำลังทำงานอยู่ รายการสำหรับแต่ละโหนดเฉพาะบนเซิร์ฟเวอร์จะถูกคอมไพล์ลงในไดเร็กทอรี ไดเรกทอรี คือรายการทรัพยากรและความสัมพันธ์หลังจากคำนวณค่าฟังก์ชัน ตัวแปร และการขยายคำสั่งแบบมีเงื่อนไข
ไวยากรณ์และสไตล์โค้ด
ต่อไปนี้คือส่วนของเอกสารอย่างเป็นทางการที่จะช่วยให้คุณเข้าใจไวยากรณ์หากตัวอย่างที่ให้ไว้ยังไม่เพียงพอ:
ต่อไปนี้คือตัวอย่างลักษณะที่ปรากฏของรายการ:
# Комментарии пишутся, как и много где, после решётки.
#
# Описание конфигурации ноды начинается с ключевого слова node,
# за которым следует селектор ноды — хостнейм (с доменом или без)
# или регулярное выражение для хостнеймов, или ключевое слово default.
#
# После этого в фигурных скобках описывается собственно конфигурация ноды.
#
# Одна и та же нода может попасть под несколько селекторов. Про приоритет
# селекторов написано в статье про синтаксис описания нод.
node 'hostname', 'f.q.d.n', /regexp/ {
# Конфигурация по сути является перечислением ресурсов и их параметров.
#
# У каждого ресурса есть тип и название.
#
# Внимание: не может быть двух ресурсов одного типа с одинаковыми названиями!
#
# Описание ресурса начинается с его типа. Тип пишется в нижнем регистре.
# Про разные типы ресурсов написано ниже.
#
# После типа в фигурных скобках пишется название ресурса, потом двоеточие,
# дальше идёт опциональное перечисление параметров ресурса и их значений.
# Значения параметров указываются через т.н. hash rocket (=>).
resource { 'title':
param1 => value1,
param2 => value2,
param3 => value3,
}
}
การเยื้องและการขึ้นบรรทัดใหม่ไม่ใช่ส่วนที่จำเป็นของไฟล์ Manifest แต่แนะนำให้ทำ
- การเยื้องสองช่อง ไม่ได้ใช้แท็บ
- เครื่องหมายปีกกาคั่นด้วยช่องว่าง ส่วนเครื่องหมายทวิภาคจะไม่คั่นด้วยช่องว่าง
- เครื่องหมายจุลภาคหลังแต่ละพารามิเตอร์ รวมถึงพารามิเตอร์สุดท้าย พารามิเตอร์แต่ละตัวอยู่ในบรรทัดแยกกัน มีข้อยกเว้นสำหรับเคสที่ไม่มีพารามิเตอร์และหนึ่งพารามิเตอร์: คุณสามารถเขียนในหนึ่งบรรทัดและไม่มีเครื่องหมายจุลภาค (เช่น
resource { 'title': }
иresource { 'title': param => value }
). - ลูกศรบนพารามิเตอร์ควรอยู่ในระดับเดียวกัน
- ลูกศรความสัมพันธ์ของทรัพยากรจะถูกเขียนไว้ด้านหน้า
ตำแหน่งของไฟล์บน pappetserver
สำหรับคำอธิบายเพิ่มเติม ผมจะแนะนำแนวคิดของ "ไดเรกทอรีราก" ไดเร็กทอรีรากคือไดเร็กทอรีที่มีการกำหนดค่า Puppet สำหรับโหนดเฉพาะ
ไดเร็กทอรีรากจะแตกต่างกันไปขึ้นอยู่กับเวอร์ชันของ Puppet และสภาพแวดล้อมที่ใช้ สภาพแวดล้อมเป็นชุดการกำหนดค่าอิสระที่จัดเก็บไว้ในไดเร็กทอรีที่แยกจากกัน โดยปกติแล้วจะใช้ร่วมกับ git ซึ่งในกรณีนี้สภาพแวดล้อมจะถูกสร้างขึ้นจากสาขา git ดังนั้น แต่ละโหนดจึงอยู่ในสภาพแวดล้อมใดสภาพแวดล้อมหนึ่ง สามารถกำหนดค่านี้ได้บนโหนดเองหรือใน ENC ซึ่งฉันจะพูดถึงในบทความถัดไป
- ในเวอร์ชันที่สาม ("old Puppet") ไดเร็กทอรีพื้นฐานคือ
/etc/puppet
. การใช้สภาพแวดล้อมเป็นทางเลือก - ตัวอย่างเช่น เราจะไม่ใช้กับ Puppet แบบเก่า หากใช้สภาพแวดล้อม ก็มักจะจัดเก็บไว้ใน/etc/puppet/environments
ไดเร็กทอรีรากจะเป็นไดเร็กทอรีสภาพแวดล้อม หากไม่ได้ใช้สภาพแวดล้อม ไดเร็กทอรีรากจะเป็นไดเร็กทอรีฐาน - เริ่มตั้งแต่เวอร์ชันที่สี่ (“Puppet ใหม่”) การใช้สภาพแวดล้อมกลายเป็นข้อบังคับ และไดเร็กทอรีฐานถูกย้ายไปที่
/etc/puppetlabs/code
. ดังนั้นสภาพแวดล้อมจึงถูกจัดเก็บไว้ใน/etc/puppetlabs/code/environments
ไดเร็กทอรีรากคือไดเร็กทอรีสภาพแวดล้อม
จะต้องมีไดเร็กทอรีย่อยในไดเร็กทอรีราก manifests
ซึ่งมีหนึ่งรายการขึ้นไปที่อธิบายโหนด นอกจากนี้ควรมีไดเร็กทอรีย่อยด้วย modules
ซึ่งมีโมดูลต่างๆ ฉันจะบอกคุณในภายหลังว่าโมดูลใดบ้าง นอกจากนี้ Puppet แบบเก่าอาจมีไดเร็กทอรีย่อยด้วย files
ซึ่งมีไฟล์ต่างๆ ที่เราคัดลอกไปยังโหนด ใน Puppet ใหม่ ไฟล์ทั้งหมดจะอยู่ในโมดูล
ไฟล์ Manifest มีนามสกุล .pp
.
ตัวอย่างการต่อสู้สองสามอย่าง
คำอธิบายของโหนดและทรัพยากรบนโหนด
บนโหนด server1.testdomain
ต้องสร้างไฟล์ /etc/issue
มีเนื้อหา Debian GNU/Linux n l
. ไฟล์จะต้องเป็นของผู้ใช้และกลุ่ม root
สิทธิ์การเข้าถึงจะต้องเป็น 644
.
เราเขียนแถลงการณ์:
node 'server1.testdomain' { # блок конфигурации, относящийся к ноде server1.testdomain
file { '/etc/issue': # описываем файл /etc/issue
ensure => present, # этот файл должен существовать
content => 'Debian GNU/Linux n l', # у него должно быть такое содержимое
owner => root, # пользователь-владелец
group => root, # группа-владелец
mode => '0644', # права на файл. Они заданы в виде строки (в кавычках), потому что иначе число с 0 в начале будет воспринято как записанное в восьмеричной системе, и всё пойдёт не так, как задумано
}
}
ความสัมพันธ์ระหว่างทรัพยากรบนโหนด
บนโหนด server2.testdomain
จะต้องทำงาน nginx โดยทำงานกับการกำหนดค่าที่เตรียมไว้ก่อนหน้านี้
มาแยกแยะปัญหากันดีกว่า:
- จำเป็นต้องติดตั้งแพ็คเกจ
nginx
. - จำเป็นต้องคัดลอกไฟล์การกำหนดค่าจากเซิร์ฟเวอร์
- บริการจำเป็นต้องทำงาน
nginx
. - หากอัปเดตการกำหนดค่าแล้ว จะต้องเริ่มบริการใหม่
เราเขียนแถลงการณ์:
node 'server2.testdomain' { # блок конфигурации, относящийся к ноде server2.testdomain
package { 'nginx': # описываем пакет nginx
ensure => installed, # он должен быть установлен
}
# Прямая стрелка (->) говорит о том, что ресурс ниже должен
# создаваться после ресурса, описанного выше.
# Такие зависимости транзитивны.
-> file { '/etc/nginx': # описываем файл /etc/nginx
ensure => directory, # это должна быть директория
source => 'puppet:///modules/example/nginx-conf', # её содержимое нужно брать с паппет-сервера по указанному адресу
recurse => true, # копировать файлы рекурсивно
purge => true, # нужно удалять лишние файлы (те, которых нет в источнике)
force => true, # удалять лишние директории
}
# Волнистая стрелка (~>) говорит о том, что ресурс ниже должен
# подписаться на изменения ресурса, описанного выше.
# Волнистая стрелка включает в себя прямую (->).
~> service { 'nginx': # описываем сервис nginx
ensure => running, # он должен быть запущен
enable => true, # его нужно запускать автоматически при старте системы
}
# Когда ресурс типа service получает уведомление,
# соответствующий сервис перезапускается.
}
เพื่อให้ได้ผล คุณต้องมีตำแหน่งไฟล์โดยประมาณต่อไปนี้บนเซิร์ฟเวอร์หุ่น:
/etc/puppetlabs/code/environments/production/ # (это для нового Паппета, для старого корневой директорией будет /etc/puppet)
├── manifests/
│ └── site.pp
└── modules/
└── example/
└── files/
└── nginx-conf/
├── nginx.conf
├── mime.types
└── conf.d/
└── some.conf
ประเภททรัพยากร
สามารถดูรายการประเภททรัพยากรที่รองรับทั้งหมดได้ที่นี่
ไฟล์
จัดการไฟล์ ไดเร็กทอรี ลิงก์สัญลักษณ์ เนื้อหา และสิทธิ์การเข้าถึง
พารามิเตอร์:
- ชื่อทรัพยากร — เส้นทางไปยังไฟล์ (ไม่จำเป็น)
- เส้นทาง — เส้นทางไปยังไฟล์ (หากไม่ได้ระบุในชื่อ)
- ทำให้มั่นใจ - ประเภทไฟล์:
absent
- ลบไฟล์present
— จะต้องมีไฟล์ประเภทใด ๆ (หากไม่มีไฟล์จะสร้างไฟล์ปกติ)file
- ไฟล์ปกติdirectory
- ไดเรกทอรีlink
- ซิมลิงค์
- เนื้อหา — เนื้อหาไฟล์ (เหมาะสำหรับไฟล์ธรรมดาเท่านั้น ไม่สามารถใช้ร่วมกับไฟล์ได้ แหล่ง หรือ เป้า)
- แหล่ง — ลิงค์ไปยังเส้นทางที่คุณต้องการคัดลอกเนื้อหาของไฟล์ (ไม่สามารถใช้ร่วมกันได้ เนื้อหา หรือ เป้า). สามารถระบุเป็น URI ที่มีแบบแผนได้
puppet:
(จากนั้นจะใช้ไฟล์จากเซิร์ฟเวอร์หุ่นเชิด) และแบบแผนhttp:
(ฉันหวังว่าจะมีความชัดเจนว่าจะเกิดอะไรขึ้นในกรณีนี้) และแม้กระทั่งกับแผนภาพfile:
หรือเป็นเส้นทางที่แน่นอนโดยไม่มีสคีมา (จากนั้นจะใช้ไฟล์จาก FS ท้องถิ่นบนโหนด) - เป้า — ตำแหน่งที่ symlink ควรชี้ (ไม่สามารถใช้ร่วมกันได้ เนื้อหา หรือ แหล่ง)
- เจ้าของ — ผู้ใช้ที่ควรเป็นเจ้าของไฟล์
- กลุ่ม — กลุ่มที่ไฟล์ควรอยู่
- โหมด — การอนุญาตไฟล์ (เป็นสตริง)
- ฟื้น - เปิดใช้งานการประมวลผลไดเรกทอรีแบบเรียกซ้ำ
- ล้าง - เปิดใช้งานการลบไฟล์ที่ไม่ได้อธิบายไว้ใน Puppet
- บังคับให้ - เปิดใช้งานการลบไดเร็กทอรีที่ไม่ได้อธิบายไว้ใน Puppet
แพ็คเกจ
ติดตั้งและลบแพ็คเกจ สามารถจัดการการแจ้งเตือน - ติดตั้งแพ็คเกจใหม่หากระบุพารามิเตอร์ ติดตั้งใหม่_on_refresh.
พารามิเตอร์:
- ชื่อทรัพยากร — ชื่อแพ็คเกจ (ไม่บังคับ)
- ชื่อ — ชื่อแพ็คเกจ (หากไม่ได้ระบุในชื่อ)
- ผู้จัดหา — ตัวจัดการแพ็คเกจที่จะใช้
- ทำให้มั่นใจ — สถานะของแพ็คเกจที่ต้องการ:
present
,installed
- ติดตั้งได้ทุกเวอร์ชันlatest
- ติดตั้งเวอร์ชันล่าสุดแล้วabsent
- ลบแล้ว (apt-get remove
)purged
— ลบพร้อมกับไฟล์กำหนดค่า (apt-get purge
)held
- เวอร์ชั่นแพ็คเกจถูกล็อค (apt-mark hold
)любая другая строка
— มีการติดตั้งเวอร์ชันที่ระบุแล้ว
- ติดตั้งใหม่_on_refresh - ถ้า
true
จากนั้นเมื่อได้รับการแจ้งเตือน แพ็คเกจจะถูกติดตั้งใหม่ มีประโยชน์สำหรับการแจกแจงตามแหล่งที่มา โดยที่การสร้างแพ็กเกจใหม่อาจมีความจำเป็นเมื่อเปลี่ยนพารามิเตอร์บิลด์ ค่าเริ่มต้นfalse
.
บริการ
จัดการบริการ สามารถประมวลผลการแจ้งเตือน - เริ่มบริการใหม่ได้
พารามิเตอร์:
- ชื่อทรัพยากร — บริการที่จะจัดการ (ไม่จำเป็น)
- ชื่อ — บริการที่ต้องการจัดการ (หากไม่ได้ระบุในชื่อ)
- ทำให้มั่นใจ — สถานะการบริการที่ต้องการ:
running
- เปิดตัวstopped
- หยุดแล้ว
- ทำให้สามารถ — ควบคุมความสามารถในการเริ่มบริการ:
true
— เปิดใช้งานการทำงานอัตโนมัติ (systemctl enable
)mask
- ปลอมตัว (systemctl mask
)false
— การทำงานอัตโนมัติถูกปิดใช้งาน (systemctl disable
)
- การเริ่มต้นใหม่ - คำสั่งให้เริ่มบริการใหม่
- สถานะ — คำสั่งตรวจสอบสถานะการให้บริการ
- ได้เริ่มต้นใหม่แล้ว — ระบุว่า service initscript รองรับการรีสตาร์ทหรือไม่ ถ้า
false
และระบุพารามิเตอร์แล้ว การเริ่มต้นใหม่ — ใช้ค่าของพารามิเตอร์นี้ ถ้าfalse
และพารามิเตอร์ การเริ่มต้นใหม่ ไม่ได้ระบุ - บริการหยุดและเริ่มรีสตาร์ท (แต่ systemd ใช้คำสั่งsystemctl restart
). - มีสถานะ — ระบุว่า service initscript รองรับคำสั่งหรือไม่
status
. ถ้าfalse
จากนั้นจะใช้ค่าพารามิเตอร์ สถานะ. ค่าเริ่มต้นtrue
.
exec
รันคำสั่งภายนอก หากคุณไม่ระบุพารามิเตอร์ สร้าง, เพียงแค่, เว้นแต่ หรือ สดชื่นคำสั่งจะถูกรันทุกครั้งที่รัน Puppet สามารถประมวลผลการแจ้งเตือน - รันคำสั่งได้
พารามิเตอร์:
- ชื่อทรัพยากร - คำสั่งที่จะดำเนินการ (ไม่จำเป็น)
- คำสั่ง — คำสั่งที่จะดำเนินการ (หากไม่ได้ระบุไว้ในชื่อ)
- เส้นทาง — เส้นทางที่ใช้ค้นหาไฟล์ปฏิบัติการ
- เพียงแค่ — หากคำสั่งที่ระบุในพารามิเตอร์นี้เสร็จสมบูรณ์โดยมีโค้ดส่งคืนเป็นศูนย์ คำสั่งหลักจะถูกดำเนินการ
- เว้นแต่ — หากคำสั่งที่ระบุในพารามิเตอร์นี้เสร็จสมบูรณ์ด้วยโค้ดส่งคืนที่ไม่เป็นศูนย์ คำสั่งหลักจะถูกดำเนินการ
- สร้าง — หากไม่มีไฟล์ที่ระบุในพารามิเตอร์นี้ คำสั่งหลักจะถูกดำเนินการ
- สดชื่น - ถ้า
true
จากนั้นคำสั่งจะถูกรันเมื่อผู้บริหารรายนี้ได้รับการแจ้งเตือนจากแหล่งข้อมูลอื่นเท่านั้น - ใบสั่ง — ไดเร็กทอรีที่ใช้รันคำสั่ง
- ผู้ใช้งาน — ผู้ใช้ที่จะรันคำสั่ง
- ผู้จัดหา - วิธีรันคำสั่ง:
- POSIX — กระบวนการลูกถูกสร้างขึ้นอย่างง่ายดาย อย่าลืมระบุ เส้นทาง
- เปลือก - คำสั่งถูกเรียกใช้ในเชลล์
/bin/sh
,ไม่อาจระบุได้ เส้นทางคุณสามารถใช้ทรงกลม ไพพ์ และคุณสมบัติเชลล์อื่นๆ ได้ มักจะตรวจพบโดยอัตโนมัติหากมีอักขระพิเศษใดๆ (|
,;
,&&
,||
เป็นต้น)
cron
ควบคุม cronjobs
พารามิเตอร์:
- ชื่อทรัพยากร - เป็นเพียงตัวระบุบางชนิด
- ทำให้มั่นใจ — สถานะคราวน์จ็อบ:
present
- สร้างหากไม่มีอยู่absent
- ลบถ้ามี
- คำสั่ง - ต้องรันคำสั่งอะไร
- สิ่งแวดล้อม — ในสภาพแวดล้อมใดที่จะรันคำสั่ง (รายการตัวแปรสภาพแวดล้อมและค่าผ่านทาง
=
) - ผู้ใช้งาน — ผู้ใช้คนไหนที่จะรันคำสั่ง
- นาที, ชั่วโมง, ในวันทำงาน, เดือน, วันเดือน — เมื่อใดจึงจะรัน cron หากไม่ได้ระบุแอตทริบิวต์เหล่านี้ ค่าใน crontab จะเป็นดังนี้
*
.
ในหุ่นกระบอก 6.0 cron เหมือนกับ
เกี่ยวกับทรัพยากรโดยทั่วไป
ข้อกำหนดสำหรับเอกลักษณ์ของทรัพยากร
ข้อผิดพลาดที่พบบ่อยที่สุดที่เราพบคือ ประกาศซ้ำ. ข้อผิดพลาดนี้เกิดขึ้นเมื่อทรัพยากรประเภทเดียวกันที่มีชื่อเดียวกันตั้งแต่สองรายการขึ้นไปปรากฏในไดเรกทอรี
ดังนั้นฉันจะเขียนอีกครั้ง: รายการสำหรับโหนดเดียวกันไม่ควรมีทรัพยากรประเภทเดียวกันและมีชื่อเดียวกัน!
บางครั้งจำเป็นต้องติดตั้งแพ็คเกจที่มีชื่อเดียวกัน แต่มีตัวจัดการแพ็คเกจต่างกัน ในกรณีนี้ คุณต้องใช้พารามิเตอร์ name
เพื่อหลีกเลี่ยงข้อผิดพลาด:
package { 'ruby-mysql':
ensure => installed,
name => 'mysql',
provider => 'gem',
}
package { 'python-mysql':
ensure => installed,
name => 'mysql',
provider => 'pip',
}
ทรัพยากรประเภทอื่นๆ มีตัวเลือกที่คล้ายกันเพื่อช่วยหลีกเลี่ยงการทำซ้ำ - name
у บริการ, command
у execและอื่นๆ
เมตาพารามิเตอร์
ทรัพยากรแต่ละประเภทมีพารามิเตอร์พิเศษ โดยไม่คำนึงถึงลักษณะของทรัพยากร
รายการพารามิเตอร์เมตาทั้งหมด
รายชื่อตัวเลือก:
- ต้องการ — พารามิเตอร์นี้ระบุว่าทรัพยากรใดขึ้นอยู่กับทรัพยากรนี้
- ก่อน - พารามิเตอร์นี้ระบุว่าทรัพยากรใดขึ้นอยู่กับทรัพยากรนี้
- สมัครเป็นสมาชิก — พารามิเตอร์นี้ระบุว่าทรัพยากรใดที่ทรัพยากรนี้รับการแจ้งเตือน
- แจ้ง — พารามิเตอร์นี้ระบุว่าทรัพยากรใดได้รับการแจ้งเตือนจากทรัพยากรนี้
พารามิเตอร์เมตาที่แสดงทั้งหมดยอมรับลิงก์ทรัพยากรเดียวหรืออาร์เรย์ของลิงก์ในวงเล็บเหลี่ยม
ลิงค์ไปยังแหล่งข้อมูล
ลิงก์ทรัพยากรเป็นเพียงการกล่าวถึงทรัพยากร ส่วนใหญ่จะใช้เพื่อระบุการพึ่งพา การอ้างอิงทรัพยากรที่ไม่มีอยู่จะทำให้เกิดข้อผิดพลาดในการคอมไพล์
ไวยากรณ์ของลิงก์มีดังนี้: ประเภททรัพยากรที่มีอักษรตัวพิมพ์ใหญ่ (หากชื่อประเภทมีเครื่องหมายทวิภาคคู่ ดังนั้นแต่ละส่วนของชื่อที่อยู่ระหว่างเครื่องหมายทวิภาคจะถูกใช้ตัวพิมพ์ใหญ่) จากนั้นชื่อทรัพยากรในวงเล็บเหลี่ยม (กรณีของชื่อ ไม่เปลี่ยนแปลง!). ไม่ควรมีช่องว่าง วงเล็บเหลี่ยมจะเขียนต่อจากชื่อประเภท
ตัวอย่าง:
file { '/file1': ensure => present }
file { '/file2':
ensure => directory,
before => File['/file1'],
}
file { '/file3': ensure => absent }
File['/file1'] -> File['/file3']
การพึ่งพาและการแจ้งเตือน
ตามที่ระบุไว้ก่อนหน้านี้ การพึ่งพาอย่างง่ายระหว่างทรัพยากรเป็นแบบสกรรมกริยา อย่างไรก็ตาม โปรดใช้ความระมัดระวังเมื่อเพิ่มการขึ้นต่อกัน - คุณสามารถสร้างการขึ้นต่อกันแบบวนรอบได้ ซึ่งจะทำให้เกิดข้อผิดพลาดในการคอมไพล์
ต่างจากการอ้างอิงตรงที่การแจ้งเตือนไม่ใช่แบบสกรรมกริยา กฎต่อไปนี้ใช้กับการแจ้งเตือน:
- หากทรัพยากรได้รับการแจ้งเตือน จะมีการอัปเดต การดำเนินการอัปเดตขึ้นอยู่กับประเภททรัพยากร - exec รันคำสั่ง บริการ เริ่มบริการใหม่ แพ็คเกจ ติดตั้งแพ็คเกจใหม่ หากทรัพยากรไม่ได้กำหนดการดำเนินการอัปเดตไว้ ก็จะไม่มีอะไรเกิดขึ้น
- ในระหว่างการเรียกใช้ Puppet หนึ่งครั้ง ทรัพยากรจะได้รับการอัปเดตไม่เกินหนึ่งครั้ง สิ่งนี้เป็นไปได้เนื่องจากการแจ้งเตือนรวมการขึ้นต่อกันและกราฟการขึ้นต่อกันไม่มีรอบ
- หาก Puppet เปลี่ยนสถานะของทรัพยากร ทรัพยากรจะส่งการแจ้งเตือนไปยังทรัพยากรทั้งหมดที่สมัครรับข้อมูล
- หากทรัพยากรได้รับการอัปเดต ระบบจะส่งการแจ้งเตือนไปยังทรัพยากรทั้งหมดที่สมัครเป็นสมาชิก
การจัดการพารามิเตอร์ที่ไม่ระบุ
ตามกฎแล้ว หากพารามิเตอร์ทรัพยากรบางตัวไม่มีค่าเริ่มต้นและไม่ได้ระบุพารามิเตอร์นี้ไว้ในรายการ Puppet จะไม่เปลี่ยนคุณสมบัตินี้สำหรับทรัพยากรที่เกี่ยวข้องบนโหนด เช่นหากเป็นทรัพยากรประเภท ไฟล์ ไม่ได้ระบุพารามิเตอร์ owner
จากนั้น Puppet จะไม่เปลี่ยนเจ้าของไฟล์ที่เกี่ยวข้อง
ความรู้เบื้องต้นเกี่ยวกับคลาส ตัวแปร และคำจำกัดความ
สมมติว่าเรามีหลายโหนดที่มีส่วนการกำหนดค่าเหมือนกัน แต่ก็มีความแตกต่างเช่นกัน ไม่เช่นนั้นเราสามารถอธิบายได้ทั้งหมดในบล็อกเดียว node {}
. แน่นอน คุณสามารถคัดลอกส่วนที่เหมือนกันของการกำหนดค่าได้ แต่โดยทั่วไปนี่เป็นวิธีแก้ปัญหาที่ไม่ดี - การกำหนดค่าเพิ่มขึ้น และหากคุณเปลี่ยนส่วนทั่วไปของการกำหนดค่า คุณจะต้องแก้ไขสิ่งเดียวกันในหลาย ๆ ที่ ในเวลาเดียวกัน มันง่ายที่จะทำผิดพลาด และโดยทั่วไปแล้ว หลักการ DRY (อย่าทำซ้ำตัวเอง) ได้รับการประดิษฐ์ขึ้นด้วยเหตุผลบางอย่าง
เพื่อแก้ปัญหานี้มีการออกแบบเช่น ชั้น.
ชั้นเรียน
ก่อนอื่นต้องอธิบายชั้นเรียนก่อน คำอธิบายไม่ได้เพิ่มทรัพยากรใดๆ เลย ชั้นเรียนอธิบายไว้ในรายการ:
# Описание класса начинается с ключевого слова class и его названия.
# Дальше идёт тело класса в фигурных скобках.
class example_class {
...
}
หลังจากนี้คุณสามารถใช้คลาสได้:
# первый вариант использования — в стиле ресурса с типом class
class { 'example_class': }
# второй вариант использования — с помощью функции include
include example_class
# про отличие этих двух вариантов будет рассказано дальше
ตัวอย่างจากงานก่อนหน้า - มาย้ายการติดตั้งและการกำหนดค่าของ nginx ไปยังคลาสกัน:
class nginx_example {
package { 'nginx':
ensure => installed,
}
-> file { '/etc/nginx':
ensure => directory,
source => 'puppet:///modules/example/nginx-conf',
recure => true,
purge => true,
force => true,
}
~> service { 'nginx':
ensure => running,
enable => true,
}
}
node 'server2.testdomain' {
include nginx_example
}
ตัวแปร
คลาสจากตัวอย่างก่อนหน้านี้ไม่ยืดหยุ่นเลย เนื่องจากมีการกำหนดค่า nginx เหมือนกันเสมอ เรามาสร้างพาธไปยังตัวแปรการกำหนดค่า จากนั้นคลาสนี้สามารถใช้เพื่อติดตั้ง nginx ด้วยการกำหนดค่าใดก็ได้
ก็ทำได้
ข้อควรสนใจ: ตัวแปรใน Puppet นั้นไม่เปลี่ยนรูป!
นอกจากนี้ ตัวแปรจะสามารถเข้าถึงได้หลังจากที่มีการประกาศเท่านั้น มิฉะนั้น ค่าของตัวแปรจะเป็น undef
.
ตัวอย่างการทำงานกับตัวแปร:
# создание переменных
$variable = 'value'
$var2 = 1
$var3 = true
$var4 = undef
# использование переменных
$var5 = $var6
file { '/tmp/text': content => $variable }
# интерполяция переменных — раскрытие значения переменных в строках. Работает только в двойных кавычках!
$var6 = "Variable with name variable has value ${variable}"
หุ่นก็มี เนมสเปซและตัวแปรก็มีตามนั้น พื้นที่การมองเห็น: ตัวแปรที่มีชื่อเดียวกันสามารถกำหนดได้ในเนมสเปซที่ต่างกัน เมื่อแก้ไขค่าของตัวแปร ตัวแปรจะถูกค้นหาในเนมสเปซปัจจุบัน จากนั้นในเนมสเปซที่ล้อมรอบ และอื่นๆ
ตัวอย่างเนมสเปซ:
- global - ตัวแปรนอกคลาสหรือคำอธิบายโหนดไปที่นั่น
- เนมสเปซโหนดในคำอธิบายโหนด
- เนมสเปซคลาสในคำอธิบายคลาส
เพื่อหลีกเลี่ยงความคลุมเครือเมื่อเข้าถึงตัวแปร คุณสามารถระบุเนมสเปซในชื่อตัวแปรได้:
# переменная без пространства имён
$var
# переменная в глобальном пространстве имён
$::var
# переменная в пространстве имён класса
$classname::var
$::classname::var
ยอมรับว่าเส้นทางไปยังการกำหนดค่า nginx อยู่ในตัวแปร $nginx_conf_source
. จากนั้นชั้นเรียนจะมีลักษณะดังนี้:
class nginx_example {
package { 'nginx':
ensure => installed,
}
-> file { '/etc/nginx':
ensure => directory,
source => $nginx_conf_source, # здесь используем переменную вместо фиксированной строки
recure => true,
purge => true,
force => true,
}
~> service { 'nginx':
ensure => running,
enable => true,
}
}
node 'server2.testdomain' {
$nginx_conf_source = 'puppet:///modules/example/nginx-conf'
include nginx_example
}
อย่างไรก็ตาม ตัวอย่างที่ให้มานั้นไม่ดีเนื่องจากมี "ความรู้ลับ" บางอย่างในคลาสที่มีการใช้ตัวแปรที่มีชื่อดังกล่าว การทำให้ความรู้นี้เป็นแบบทั่วไปนั้นถูกต้องมากกว่ามาก - คลาสสามารถมีพารามิเตอร์ได้
พารามิเตอร์คลาส เป็นตัวแปรในเนมสเปซของคลาส โดยระบุไว้ในส่วนหัวของคลาส และสามารถใช้ได้เหมือนกับตัวแปรปกติในเนื้อหาของคลาส ค่าพารามิเตอร์จะถูกระบุเมื่อใช้คลาสในรายการ
พารามิเตอร์สามารถตั้งค่าเป็นค่าเริ่มต้นได้ หากพารามิเตอร์ไม่มีค่าเริ่มต้นและไม่ได้ตั้งค่าเมื่อใช้ จะทำให้เกิดข้อผิดพลาดในการคอมไพล์
มากำหนดพารามิเตอร์คลาสจากตัวอย่างด้านบนและเพิ่มพารามิเตอร์สองตัว: ตัวแรกที่จำเป็นคือเส้นทางไปยังการกำหนดค่าและตัวที่สองเป็นทางเลือกคือชื่อของแพ็คเกจที่มี nginx (เช่นใน Debian มีแพ็คเกจต่างๆ nginx
, nginx-light
, nginx-full
).
# переменные описываются сразу после имени класса в круглых скобках
class nginx_example (
$conf_source,
$package_name = 'nginx-light', # параметр со значением по умолчанию
) {
package { $package_name:
ensure => installed,
}
-> file { '/etc/nginx':
ensure => directory,
source => $conf_source,
recurse => true,
purge => true,
force => true,
}
~> service { 'nginx':
ensure => running,
enable => true,
}
}
node 'server2.testdomain' {
# если мы хотим задать параметры класса, функция include не подойдёт* — нужно использовать resource-style declaration
# *на самом деле подойдёт, но про это расскажу в следующей серии. Ключевое слово "Hiera".
class { 'nginx_example':
conf_source => 'puppet:///modules/example/nginx-conf', # задаём параметры класса точно так же, как параметры для других ресурсов
}
}
ใน Puppet ตัวแปรจะถูกพิมพ์ กิน
ประเภทจะถูกเขียนทันทีก่อนชื่อพารามิเตอร์:
class example (
String $param1,
Integer $param2,
Array $param3,
Hash $param4,
Hash[String, String] $param5,
) {
...
}
คลาส: รวมชื่อคลาสเทียบกับคลาส {'classname':}
แต่ละชั้นเรียนเป็นทรัพยากรประเภท ชั้น. เช่นเดียวกับทรัพยากรประเภทอื่นๆ ไม่สามารถมีอินสแตนซ์ของคลาสเดียวกันสองอินสแตนซ์บนโหนดเดียวกันได้
หากคุณพยายามเพิ่มคลาสให้กับโหนดเดียวกันสองครั้งโดยใช้ class { 'classname':}
(ไม่มีความแตกต่าง โดยมีพารามิเตอร์ต่างกันหรือเหมือนกัน) จะเกิดข้อผิดพลาดในการคอมไพล์ แต่ถ้าคุณใช้คลาสในรูปแบบทรัพยากร คุณสามารถตั้งค่าพารามิเตอร์ทั้งหมดในไฟล์ Manifest ได้อย่างชัดเจนทันที
แต่ถ้าคุณใช้ include
จากนั้นคุณสามารถเพิ่มคลาสได้หลายครั้งตามต้องการ ความจริงก็คือว่า include
เป็นฟังก์ชัน idempotent ที่ตรวจสอบว่ามีการเพิ่มคลาสลงในไดเร็กทอรีหรือไม่ หากคลาสไม่อยู่ในไดเร็กทอรี คลาสก็จะเพิ่มเข้าไป และหากมีอยู่แล้วก็ไม่ทำอะไรเลย แต่กรณีใช้งาน include
คุณไม่สามารถตั้งค่าพารามิเตอร์คลาสในระหว่างการประกาศคลาส - พารามิเตอร์ที่จำเป็นทั้งหมดจะต้องตั้งค่าในแหล่งข้อมูลภายนอก - Hiera หรือ ENC เราจะพูดถึงพวกเขาในบทความถัดไป
กำหนด
ตามที่กล่าวไว้ในบล็อกที่แล้ว คลาสเดียวกันไม่สามารถปรากฏบนโหนดได้มากกว่าหนึ่งครั้ง อย่างไรก็ตาม ในบางกรณี คุณจะต้องสามารถใช้บล็อกโค้ดเดียวกันกับพารามิเตอร์ที่แตกต่างกันบนโหนดเดียวกันได้ กล่าวอีกนัยหนึ่ง มีความต้องการประเภททรัพยากรของตนเอง
ตัวอย่างเช่น ในการติดตั้งโมดูล PHP เราทำสิ่งต่อไปนี้ใน Avito:
- ติดตั้งแพ็คเกจด้วยโมดูลนี้
- มาสร้างไฟล์การกำหนดค่าสำหรับโมดูลนี้กันดีกว่า
- เราสร้าง symlink เพื่อกำหนดค่าสำหรับ php-fpm
- เราสร้าง symlink เพื่อกำหนดค่าสำหรับ php cli
ในกรณีดังกล่าว การออกแบบ เช่น $title
โดยที่ชื่อทรัพยากรจะอยู่เมื่อมีการประกาศ เช่นเดียวกับในกรณีของคลาส จะต้องอธิบายคำจำกัดความก่อน หลังจากนั้นจึงจะสามารถนำมาใช้ได้
ตัวอย่างง่ายๆ พร้อมโมดูลสำหรับ PHP:
define php74::module (
$php_module_name = $title,
$php_package_name = "php7.4-${title}",
$version = 'installed',
$priority = '20',
$data = "extension=${title}.son",
$php_module_path = '/etc/php/7.4/mods-available',
) {
package { $php_package_name:
ensure => $version,
install_options => ['-o', 'DPkg::NoTriggers=true'], # триггеры дебиановских php-пакетов сами создают симлинки и перезапускают сервис php-fpm - нам это не нужно, так как и симлинками, и сервисом мы управляем с помощью Puppet
}
-> file { "${php_module_path}/${php_module_name}.ini":
ensure => $ensure,
content => $data,
}
file { "/etc/php/7.4/cli/conf.d/${priority}-${php_module_name}.ini":
ensure => link,
target => "${php_module_path}/${php_module_name}.ini",
}
file { "/etc/php/7.4/fpm/conf.d/${priority}-${php_module_name}.ini":
ensure => link,
target => "${php_module_path}/${php_module_name}.ini",
}
}
node server3.testdomain {
php74::module { 'sqlite3': }
php74::module { 'amqp': php_package_name => 'php-amqp' }
php74::module { 'msgpack': priority => '10' }
}
วิธีที่ง่ายที่สุดในการตรวจจับข้อผิดพลาดการประกาศซ้ำคือใน Define สิ่งนี้จะเกิดขึ้นหากคำจำกัดความมีทรัพยากรที่มีชื่อคงที่ และมีสองอินสแตนซ์ขึ้นไปของคำจำกัดความนี้บนบางโหนด
เป็นเรื่องง่ายที่จะป้องกันตัวเองจากสิ่งนี้: ทรัพยากรทั้งหมดภายในคำจำกัดความจะต้องมีชื่อขึ้นอยู่กับ $title
. อีกทางเลือกหนึ่งคือการเพิ่มทรัพยากรแบบ idempotent ในกรณีที่ง่ายที่สุด ก็เพียงพอแล้วที่จะย้ายทรัพยากรทั่วไปสำหรับอินสแตนซ์ทั้งหมดของคำจำกัดความไปยังคลาสที่แยกจากกัน และรวมคลาสนี้ไว้ในคำจำกัดความ - ฟังก์ชัน include
idempotent
มีวิธีอื่นในการบรรลุค่า idempotency เมื่อเพิ่มทรัพยากร ได้แก่ การใช้ฟังก์ชัน defined
и ensure_resources
แต่ฉันจะเล่าให้ฟังในตอนต่อไป
การขึ้นต่อกันและการแจ้งเตือนสำหรับคลาสและคำจำกัดความ
คลาสและคำจำกัดความเพิ่มกฎต่อไปนี้ในการจัดการการขึ้นต่อกันและการแจ้งเตือน:
- การพึ่งพาคลาส/กำหนด เพิ่มการพึ่งพาทรัพยากรทั้งหมดของคลาส/กำหนด
- การพึ่งพาคลาส/กำหนดจะเพิ่มการพึ่งพาให้กับทรัพยากรคลาส/กำหนดทั้งหมด
- การแจ้งเตือนคลาส/กำหนด แจ้งเตือนทรัพยากรทั้งหมดของคลาส/กำหนด;
- การสมัครสมาชิก class/define สมัครสมาชิกทรัพยากรทั้งหมดของคลาส/define
คำสั่งแบบมีเงื่อนไขและตัวเลือก
if
มันง่ายที่นี่:
if ВЫРАЖЕНИЕ1 {
...
} elsif ВЫРАЖЕНИЕ2 {
...
} else {
...
}
เว้นแต่
ยกเว้นในกรณีที่ตรงกันข้าม: บล็อกของโค้ดจะถูกดำเนินการหากนิพจน์เป็นเท็จ
unless ВЫРАЖЕНИЕ {
...
}
กรณี
ไม่มีอะไรซับซ้อนที่นี่เช่นกัน คุณสามารถใช้ค่าปกติ (สตริง ตัวเลข ฯลฯ) นิพจน์ทั่วไป และประเภทข้อมูลเป็นค่าได้
case ВЫРАЖЕНИЕ {
ЗНАЧЕНИЕ1: { ... }
ЗНАЧЕНИЕ2, ЗНАЧЕНИЕ3: { ... }
default: { ... }
}
ตัวเลือก
ตัวเลือกคือโครงสร้างภาษาที่คล้ายกับ case
แต่แทนที่จะดำเนินการบล็อกโค้ด กลับคืนค่า
$var = $othervar ? { 'val1' => 1, 'val2' => 2, default => 3 }
โมดูล
เมื่อการกำหนดค่ามีขนาดเล็ก ก็สามารถเก็บไว้ในรายการเดียวได้อย่างง่ายดาย แต่ยิ่งเราอธิบายการกำหนดค่ามากเท่าใด คลาสและโหนดในไฟล์ Manifest ก็ยิ่งมากขึ้นเท่านั้น มันก็จะขยายใหญ่ขึ้น และไม่สะดวกในการทำงานด้วย
นอกจากนี้ ยังมีปัญหาในการใช้โค้ดซ้ำอีกด้วย เมื่อโค้ดทั้งหมดอยู่ในรายการเดียว การแบ่งปันโค้ดนี้กับผู้อื่นเป็นเรื่องยาก เพื่อแก้ปัญหาทั้งสองนี้ Puppet มีเอนทิตีที่เรียกว่าโมดูล
โมดูล - คือชุดของคลาส คำจำกัดความ และเอนทิตี Puppet อื่นๆ ที่อยู่ในไดเร็กทอรีแยกต่างหาก กล่าวอีกนัยหนึ่ง โมดูลคือชิ้นส่วนอิสระของตรรกะ Puppet ตัวอย่างเช่น อาจมีโมดูลสำหรับทำงานกับ nginx และจะมีเฉพาะสิ่งที่จำเป็นสำหรับการทำงานกับ nginx หรืออาจมีโมดูลสำหรับทำงานกับ PHP เป็นต้น
โมดูลต่างๆ ได้รับการกำหนดเวอร์ชันแล้ว และรองรับการขึ้นต่อกันของโมดูลซึ่งกันและกันด้วย มีที่เก็บโมดูลแบบเปิด -
บนเซิร์ฟเวอร์หุ่น โมดูลจะอยู่ในไดเร็กทอรีย่อย modules ของไดเร็กทอรีราก ภายในแต่ละโมดูลจะมีไดเร็กทอรีไดเร็กทอรีมาตรฐาน - รายการ, ไฟล์, เทมเพลต, lib และอื่นๆ
โครงสร้างไฟล์ในโมดูล
รูทของโมดูลอาจมีไดเร็กทอรีต่อไปนี้พร้อมชื่อที่สื่อความหมาย:
manifests
- มันมีแถลงการณ์files
- มันมีไฟล์templates
- มันมีเทมเพลตlib
— มันมีรหัส Ruby
นี่ไม่ใช่รายการไดเร็กทอรีและไฟล์ทั้งหมด แต่เพียงพอสำหรับบทความนี้ในตอนนี้
ชื่อของทรัพยากรและชื่อของไฟล์ในโมดูล
ทรัพยากร (คลาส คำจำกัดความ) ในโมดูลไม่สามารถตั้งชื่อสิ่งที่คุณต้องการได้ นอกจากนี้ยังมีการติดต่อโดยตรงระหว่างชื่อของทรัพยากรและชื่อของไฟล์ที่ Puppet จะค้นหาคำอธิบายของทรัพยากรนั้น หากคุณละเมิดกฎการตั้งชื่อ Puppet ก็จะไม่พบคำอธิบายทรัพยากร และคุณจะได้รับข้อผิดพลาดในการคอมไพล์
กฎมีความเรียบง่าย:
- ทรัพยากรทั้งหมดในโมดูลต้องอยู่ในเนมสเปซของโมดูล หากโมดูลถูกเรียก
foo
จากนั้นควรตั้งชื่อทรัพยากรทั้งหมดในนั้นfoo::<anything>
หรือเพียงแค่foo
. - ทรัพยากรที่มีชื่อของโมดูลจะต้องอยู่ในไฟล์
init.pp
. - สำหรับทรัพยากรอื่นๆ รูปแบบการตั้งชื่อไฟล์จะเป็นดังนี้:
- คำนำหน้าที่มีชื่อโมดูลจะถูกยกเลิก
- ทวิภาคคู่ทั้งหมด ถ้ามี จะถูกแทนที่ด้วยเครื่องหมายทับ
- เพิ่มส่วนขยายแล้ว
.pp
ฉันจะสาธิตด้วยตัวอย่าง สมมติว่าฉันกำลังเขียนโมดูล nginx
. ประกอบด้วยทรัพยากรดังต่อไปนี้:
- ชั้น
nginx
อธิบายไว้ในรายการinit.pp
; - ชั้น
nginx::service
อธิบายไว้ในรายการservice.pp
; - กำหนด
nginx::server
อธิบายไว้ในรายการserver.pp
; - กำหนด
nginx::server::location
อธิบายไว้ในรายการserver/location.pp
.
แม่แบบ
คุณเองก็รู้ว่าเทมเพลตคืออะไร ฉันจะไม่อธิบายรายละเอียดที่นี่ แต่ฉันจะทิ้งมันไว้เผื่อไว้
วิธีใช้เทมเพลต: สามารถขยายความหมายของเทมเพลตได้โดยใช้ฟังก์ชัน template
ซึ่งส่งผ่านเส้นทางไปยังเทมเพลต สำหรับทรัพยากรประเภท ไฟล์ ใช้ร่วมกับพารามิเตอร์ content
. ตัวอย่างเช่นเช่นนี้:
file { '/tmp/example': content => template('modulename/templatename.erb')
ดูเส้นทาง <modulename>/<filename>
หมายถึงไฟล์ <rootdir>/modules/<modulename>/templates/<filename>
.
นอกจากนี้ยังมีฟังก์ชั่น inline_template
— รับข้อความเทมเพลตเป็นอินพุต ไม่ใช่ชื่อไฟล์
ภายในเทมเพลต คุณสามารถใช้ตัวแปร Puppet ทั้งหมดในขอบเขตปัจจุบันได้
Puppet รองรับเทมเพลตในรูปแบบ ERB และ EPP:
สั้น ๆ เกี่ยวกับ ERB
โครงสร้างการควบคุม:
<%= ВЫРАЖЕНИЕ %>
— ใส่ค่าของนิพจน์<% ВЫРАЖЕНИЕ %>
— คำนวณค่าของนิพจน์ (โดยไม่ต้องแทรก) คำสั่งแบบมีเงื่อนไข (ถ้า) และลูป (แต่ละรายการ) มักจะอยู่ที่นี่<%# КОММЕНТАРИЙ %>
นิพจน์ใน ERB เขียนด้วยภาษา Ruby (จริงๆ แล้ว ERB คือ Embedded Ruby)
หากต้องการเข้าถึงตัวแปรจากรายการ คุณต้องเพิ่ม @
ไปที่ชื่อตัวแปร หากต้องการลบตัวแบ่งบรรทัดที่ปรากฏหลังโครงสร้างการควบคุม คุณจำเป็นต้องใช้แท็กปิด -%>
.
ตัวอย่างการใช้เทมเพลต
สมมติว่าฉันกำลังเขียนโมดูลเพื่อควบคุม ZooKeeper คลาสที่รับผิดชอบในการสร้างการกำหนดค่ามีลักษณะดังนี้:
class zookeeper::configure (
Array[String] $nodes,
Integer $port_client,
Integer $port_quorum,
Integer $port_leader,
Hash[String, Any] $properties,
String $datadir,
) {
file { '/etc/zookeeper/conf/zoo.cfg':
ensure => present,
content => template('zookeeper/zoo.cfg.erb'),
}
}
และเทมเพลตที่สอดคล้องกัน zoo.cfg.erb
- ดังนั้น:
<% if @nodes.length > 0 -%>
<% @nodes.each do |node, id| -%>
server.<%= id %>=<%= node %>:<%= @port_leader %>:<%= @port_quorum %>;<%= @port_client %>
<% end -%>
<% end -%>
dataDir=<%= @datadir %>
<% @properties.each do |k, v| -%>
<%= k %>=<%= v %>
<% end -%>
ข้อเท็จจริงและตัวแปรบิวท์อิน
บ่อยครั้งที่ส่วนเฉพาะของการกำหนดค่าขึ้นอยู่กับสิ่งที่เกิดขึ้นบนโหนดในปัจจุบัน ตัวอย่างเช่น คุณต้องติดตั้งแพ็คเกจเวอร์ชันใดเวอร์ชันหนึ่งหรือเวอร์ชันอื่น ขึ้นอยู่กับว่า Debian release คืออะไร คุณสามารถตรวจสอบทั้งหมดนี้ได้ด้วยตนเอง โดยเขียนรายการใหม่หากโหนดมีการเปลี่ยนแปลง แต่นี่ไม่ใช่แนวทางที่จริงจัง ระบบอัตโนมัติดีกว่ามาก
ในการรับข้อมูลเกี่ยวกับโหนด Puppet มีกลไกที่เรียกว่าข้อเท็จจริง ข้อมูล - นี่คือข้อมูลเกี่ยวกับโหนดที่มีอยู่ในรายการในรูปแบบของตัวแปรสามัญในเนมสเปซส่วนกลาง ตัวอย่างเช่น ชื่อโฮสต์ เวอร์ชันระบบปฏิบัติการ สถาปัตยกรรมโปรเซสเซอร์ รายชื่อผู้ใช้ รายชื่ออินเทอร์เฟซเครือข่ายและที่อยู่ และอื่นๆ อีกมากมาย ข้อเท็จจริงมีอยู่ในรายการและเทมเพลตเป็นตัวแปรปกติ
ตัวอย่างการทำงานกับข้อเท็จจริง:
notify { "Running OS ${facts['os']['name']} version ${facts['os']['release']['full']}": }
# ресурс типа notify просто выводит сообщение в лог
พูดอย่างเป็นทางการ ข้อเท็จจริงมีชื่อ (สตริง) และค่า (มีหลายประเภท: สตริง อาร์เรย์ พจนานุกรม) กิน
ในระหว่างการดำเนินการ เจ้าหน้าที่หุ่นเชิดจะคัดลอกผู้รวบรวมข้อเท็จจริงที่มีอยู่ทั้งหมดจากเซิร์ฟเวอร์ pappet ไปยังโหนดก่อน จากนั้นจึงเปิดใช้งานและส่งข้อเท็จจริงที่รวบรวมไปยังเซิร์ฟเวอร์ หลังจากนี้ เซิร์ฟเวอร์จะเริ่มรวบรวมแค็ตตาล็อก
ข้อเท็จจริงในรูปแบบของไฟล์ปฏิบัติการ
ข้อเท็จจริงดังกล่าวจะอยู่ในโมดูลในไดเร็กทอรี facts.d
. แน่นอนว่าไฟล์ต่างๆ จะต้องสามารถเรียกใช้งานได้ เมื่อรัน จะต้องส่งข้อมูลเอาต์พุตไปยังเอาต์พุตมาตรฐานในรูปแบบ YAML หรือคีย์=ค่า
อย่าลืมว่าข้อเท็จจริงมีผลกับโหนดทั้งหมดที่ควบคุมโดยเซิร์ฟเวอร์ poppet ที่โมดูลของคุณใช้งานอยู่ ดังนั้นในสคริปต์ควรระมัดระวังในการตรวจสอบว่าระบบมีโปรแกรมและไฟล์ทั้งหมดที่จำเป็นสำหรับการทำงานจริงของคุณ
#!/bin/sh
echo "testfact=success"
#!/bin/sh
echo '{"testyamlfact":"success"}'
ข้อเท็จจริงทับทิม
ข้อเท็จจริงดังกล่าวจะอยู่ในโมดูลในไดเร็กทอรี lib/facter
.
# всё начинается с вызова функции Facter.add с именем факта и блоком кода
Facter.add('ladvd') do
# в блоках confine описываются условия применимости факта — код внутри блока должен вернуть true, иначе значение факта не вычисляется и не возвращается
confine do
Facter::Core::Execution.which('ladvdc') # проверим, что в PATH есть такой исполняемый файл
end
confine do
File.socket?('/var/run/ladvd.sock') # проверим, что есть такой UNIX-domain socket
end
# в блоке setcode происходит собственно вычисление значения факта
setcode do
hash = {}
if (out = Facter::Core::Execution.execute('ladvdc -b'))
out.split.each do |l|
line = l.split('=')
next if line.length != 2
name, value = line
hash[name.strip.downcase.tr(' ', '_')] = value.strip.chomp(''').reverse.chomp(''').reverse
end
end
hash # значение последнего выражения в блоке setcode является значением факта
end
end
ข้อความข้อเท็จจริง
ข้อเท็จจริงดังกล่าวจะถูกวางไว้บนโหนดในไดเร็กทอรี /etc/facter/facts.d
ในหุ่นเชิดเก่าหรือ /etc/puppetlabs/facts.d
ในหุ่นกระบอกใหม่
examplefact=examplevalue
---
examplefact2: examplevalue2
anotherfact: anothervalue
เข้าถึงข้อเท็จจริง
มีสองวิธีในการเข้าถึงข้อเท็จจริง:
- ผ่านพจนานุกรม
$facts
:$facts['fqdn']
; - โดยใช้ชื่อข้อเท็จจริงเป็นชื่อตัวแปร:
$fqdn
.
ทางที่ดีควรใช้พจนานุกรม $facts
หรือดีกว่านั้น ให้ระบุเนมสเปซส่วนกลาง ($::facts
).
ตัวแปรในตัว
นอกจากข้อเท็จจริงแล้วยังมี
- ข้อเท็จจริงที่เชื่อถือได้ — ตัวแปรที่นำมาจากใบรับรองของลูกค้า (เนื่องจากโดยปกติแล้วใบรับรองจะออกบนเซิร์ฟเวอร์ poppet เอเจนต์จึงไม่สามารถรับและเปลี่ยนใบรับรองได้ ดังนั้นตัวแปรจึง "เชื่อถือได้"): ชื่อของใบรับรอง ชื่อของ โฮสต์และโดเมน ส่วนขยายจากใบรับรอง
- ข้อเท็จจริงของเซิร์ฟเวอร์ —ตัวแปรที่เกี่ยวข้องกับข้อมูลเกี่ยวกับเซิร์ฟเวอร์—เวอร์ชัน ชื่อ ที่อยู่ IP ของเซิร์ฟเวอร์ สภาพแวดล้อม
- ข้อเท็จจริงของตัวแทน — ตัวแปรที่เพิ่มโดยตรงโดย puppet-agent และไม่ใช่ตามปัจจัย — ชื่อใบรับรอง เวอร์ชันตัวแทน เวอร์ชันหุ่น
- ตัวแปรหลัก - ตัวแปร Pappetmaster (sic!) มันเกือบจะเหมือนกับใน ข้อเท็จจริงของเซิร์ฟเวอร์บวกกับค่าพารามิเตอร์การกำหนดค่าที่มีอยู่
- ตัวแปรคอมไพเลอร์ — ตัวแปรคอมไพเลอร์ที่แตกต่างกันในแต่ละขอบเขต: ชื่อของโมดูลปัจจุบันและชื่อของโมดูลที่เข้าถึงวัตถุปัจจุบัน สามารถใช้เพื่อตรวจสอบว่าคลาสส่วนตัวของคุณไม่ได้ถูกใช้โดยตรงจากโมดูลอื่น
เพิ่มเติม 1: จะรันและแก้ไขข้อบกพร่องทั้งหมดนี้ได้อย่างไร
บทความนี้มีตัวอย่างโค้ดหุ่นกระบอกมากมาย แต่ไม่ได้บอกเราเลยว่าจะเรียกใช้โค้ดนี้อย่างไร ฉันกำลังแก้ไขตัวเอง
ตัวแทนก็เพียงพอที่จะเรียกใช้ Puppet แต่ในกรณีส่วนใหญ่ คุณจะต้องมีเซิร์ฟเวอร์ด้วย
ตัวแทน
อย่างน้อยตั้งแต่เวอร์ชัน XNUMX แพ็คเกจ puppet-agent จาก
ในกรณีที่ง่ายที่สุด หากต้องการใช้การกำหนดค่าหุ่นเชิด ก็เพียงพอที่จะเปิดใช้งานเอเจนต์ในโหมดไร้เซิร์ฟเวอร์: โดยมีเงื่อนไขว่าโค้ดหุ่นจะถูกคัดลอกไปยังโหนด จากนั้นเปิดใช้งาน puppet apply <путь к манифесту>
:
atikhonov@atikhonov ~/puppet-test $ cat helloworld.pp
node default {
notify { 'Hello world!': }
}
atikhonov@atikhonov ~/puppet-test $ puppet apply helloworld.pp
Notice: Compiled catalog for atikhonov.localdomain in environment production in 0.01 seconds
Notice: Hello world!
Notice: /Stage[main]/Main/Node[default]/Notify[Hello world!]/message: defined 'message' as 'Hello world!'
Notice: Applied catalog in 0.01 seconds
แน่นอนว่าจะดีกว่าถ้าตั้งค่าเซิร์ฟเวอร์และเรียกใช้เอเจนต์บนโหนดในโหมด daemon จากนั้นพวกเขาจะใช้การกำหนดค่าที่ดาวน์โหลดจากเซิร์ฟเวอร์ทุกๆ ครึ่งชั่วโมง
คุณสามารถเลียนแบบรูปแบบการทำงานแบบพุชได้ - ไปที่โหนดที่คุณสนใจและเริ่มต้น sudo puppet agent -t
. สำคัญ -t
(--test
) จริงๆ แล้วมีหลายตัวเลือกที่สามารถเปิดใช้งานทีละรายการได้ ตัวเลือกเหล่านี้มีดังต่อไปนี้:
- ห้ามทำงานในโหมด daemon (โดยค่าเริ่มต้น เอเจนต์จะเริ่มทำงานในโหมด daemon)
- ปิดตัวลงหลังจากใช้แค็ตตาล็อก (โดยค่าเริ่มต้น เจ้าหน้าที่จะทำงานต่อไปและใช้การกำหนดค่าทุกๆ ครึ่งชั่วโมง)
- เขียนบันทึกการทำงานโดยละเอียด
- แสดงการเปลี่ยนแปลงในไฟล์
เอเจนต์มีโหมดการทำงานที่ไม่มีการเปลี่ยนแปลง - คุณสามารถใช้ได้เมื่อคุณไม่แน่ใจว่าคุณได้เขียนการกำหนดค่าที่ถูกต้อง และต้องการตรวจสอบว่าเอเจนต์จะเปลี่ยนแปลงอะไรบ้างระหว่างการดำเนินการ โหมดนี้เปิดใช้งานโดยพารามิเตอร์ --noop
บนบรรทัดคำสั่ง: sudo puppet agent -t --noop
.
นอกจากนี้ คุณสามารถเปิดใช้งานบันทึกการดีบักของงานได้ - ในนั้นหุ่นเชิดจะเขียนเกี่ยวกับการกระทำทั้งหมดที่ดำเนินการ: เกี่ยวกับทรัพยากรที่กำลังประมวลผลอยู่เกี่ยวกับพารามิเตอร์ของทรัพยากรนี้เกี่ยวกับโปรแกรมที่เปิดตัว แน่นอนว่านี่คือพารามิเตอร์ --debug
.
เซิร์ฟเวอร์
ฉันจะไม่พิจารณาการตั้งค่า pappetserver แบบเต็มและการปรับใช้โค้ดในบทความนี้ ฉันจะบอกว่านอกกรอบมีเซิร์ฟเวอร์เวอร์ชันที่ทำงานได้อย่างสมบูรณ์ซึ่งไม่ต้องการการกำหนดค่าเพิ่มเติมเพื่อทำงานกับจำนวนเล็กน้อย โหนด (พูดมากถึงร้อย) ต้องมีการปรับแต่งโหนดจำนวนมากขึ้น - โดยค่าเริ่มต้น puppetserver จะเปิดตัวคนงานไม่เกินสี่คนเพื่อประสิทธิภาพที่มากขึ้นคุณต้องเพิ่มจำนวนและอย่าลืมเพิ่มขีด จำกัด หน่วยความจำมิฉะนั้นเซิร์ฟเวอร์จะรวบรวมขยะเกือบตลอดเวลา
การปรับใช้โค้ด - หากคุณต้องการอย่างรวดเร็วและง่ายดาย ลองดู (ที่ r10k)[
ภาคผนวก 2: แนวทางการเขียนโค้ด
- วางตรรกะทั้งหมดไว้ในคลาสและคำจำกัดความ
- เก็บคลาสและคำจำกัดความไว้ในโมดูล ไม่ใช่ในรายการที่อธิบายโหนด
- ใช้ข้อเท็จจริง.
- อย่าสร้าง ifs ตามชื่อโฮสต์
- อย่าลังเลที่จะเพิ่มพารามิเตอร์สำหรับคลาสและคำจำกัดความ - ซึ่งดีกว่าตรรกะโดยนัยที่ซ่อนอยู่ในเนื้อหาของคลาส/กำหนด
ฉันจะอธิบายว่าทำไมฉันถึงแนะนำให้ทำเช่นนี้ในบทความถัดไป
ข้อสรุป
มาจบการแนะนำตัวกันดีกว่า ในบทความถัดไป ฉันจะบอกคุณเกี่ยวกับ Hiera, ENC และ PuppetDB
เฉพาะผู้ใช้ที่ลงทะเบียนเท่านั้นที่สามารถเข้าร่วมในการสำรวจได้
อันที่จริงยังมีเนื้อหาอีกมากมาย - ฉันสามารถเขียนบทความในหัวข้อต่อไปนี้ โหวตสิ่งที่คุณสนใจอ่านเกี่ยวกับ:
- ลด 59,1%โครงสร้างหุ่นขั้นสูง - สิ่งไร้สาระในระดับถัดไป: ลูป การทำแผนที่และนิพจน์แลมบ์ดาอื่น ๆ ตัวรวบรวมทรัพยากร ทรัพยากรที่ส่งออก และการสื่อสารระหว่างโฮสต์ผ่านหุ่นกระบอก แท็ก ผู้ให้บริการ ประเภทข้อมูลนามธรรม13
- ลด 31,8%“ฉันเป็นผู้ดูแลระบบของแม่” หรือวิธีที่เราใน Avito ผูกมิตรกับเซิร์ฟเวอร์ poppet หลายเวอร์ชันในเวอร์ชันต่างๆ และโดยหลักการแล้ว ในส่วนที่เกี่ยวข้องกับการจัดการเซิร์ฟเวอร์ poppet7
- ลด 81,8%วิธีที่เราเขียนโค้ดหุ่นเชิด: เครื่องมือวัด เอกสาร การทดสอบ CI/CD.18
ผู้ใช้ 22 คนโหวต ผู้ใช้ 9 รายงดออกเสียง
ที่มา: will.com