บทนำสู่หุ่นเชิด

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 เหมือนกับ นำออกจากกล่อง ใน puppetserver ดังนั้นจึงไม่มีเอกสารเกี่ยวกับไซต์ทั่วไป แต่เขา อยู่ในกล่อง ใน puppet-agent ดังนั้นจึงไม่จำเป็นต้องติดตั้งแยกต่างหาก คุณสามารถดูเอกสารประกอบของมันได้ ในเอกสารประกอบสำหรับ Puppet เวอร์ชันที่ XNUMXหรือ บน GitHub.

เกี่ยวกับทรัพยากรโดยทั่วไป

ข้อกำหนดสำหรับเอกลักษณ์ของทรัพยากร

ข้อผิดพลาดที่พบบ่อยที่สุดที่เราพบคือ ประกาศซ้ำ. ข้อผิดพลาดนี้เกิดขึ้นเมื่อทรัพยากรประเภทเดียวกันที่มีชื่อเดียวกันตั้งแต่สองรายการขึ้นไปปรากฏในไดเรกทอรี

ดังนั้นฉันจะเขียนอีกครั้ง: รายการสำหรับโหนดเดียวกันไม่ควรมีทรัพยากรประเภทเดียวกันและมีชื่อเดียวกัน!

บางครั้งจำเป็นต้องติดตั้งแพ็คเกจที่มีชื่อเดียวกัน แต่มีตัวจัดการแพ็คเกจต่างกัน ในกรณีนี้ คุณต้องใช้พารามิเตอร์ 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 (อย่าทำซ้ำตัวเอง) ได้รับการประดิษฐ์ขึ้นด้วยเหตุผลบางอย่าง

เพื่อแก้ปัญหานี้มีการออกแบบเช่น ชั้น.

ชั้นเรียน

ชั้นเรียน คือบล็อกที่มีชื่อของรหัส poppet จำเป็นต้องมีคลาสเพื่อใช้โค้ดซ้ำ

ก่อนอื่นต้องอธิบายชั้นเรียนก่อน คำอธิบายไม่ได้เพิ่มทรัพยากรใดๆ เลย ชั้นเรียนอธิบายไว้ในรายการ:

# Описание класса начинается с ключевого слова 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:

  1. ติดตั้งแพ็คเกจด้วยโมดูลนี้
  2. มาสร้างไฟล์การกำหนดค่าสำหรับโมดูลนี้กันดีกว่า
  3. เราสร้าง symlink เพื่อกำหนดค่าสำหรับ php-fpm
  4. เราสร้าง symlink เพื่อกำหนดค่าสำหรับ php cli

ในกรณีดังกล่าว การออกแบบ เช่น กำหนด (กำหนด, ประเภทที่กำหนด, ประเภททรัพยากรที่กำหนด) Define คล้ายกับคลาส แต่มีความแตกต่าง ประการแรก แต่ละ Define จะเป็นประเภททรัพยากร ไม่ใช่ทรัพยากร ประการที่สอง แต่ละคำจำกัดความมีพารามิเตอร์โดยนัย $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 просто выводит сообщение в лог

พูดอย่างเป็นทางการ ข้อเท็จจริงมีชื่อ (สตริง) และค่า (มีหลายประเภท: สตริง อาร์เรย์ พจนานุกรม) กิน ชุดของข้อเท็จจริงในตัว. คุณยังสามารถเขียนของคุณเอง มีการอธิบายผู้รวบรวมข้อเท็จจริง เช่นเดียวกับฟังก์ชั่นใน Rubyหรืออย่างไร ไฟล์ปฏิบัติการ. สามารถนำเสนอข้อเท็จจริงในรูปแบบได้เช่นกัน ไฟล์ข้อความพร้อมข้อมูล บนโหนด

ในระหว่างการดำเนินการ เจ้าหน้าที่หุ่นเชิดจะคัดลอกผู้รวบรวมข้อเท็จจริงที่มีอยู่ทั้งหมดจากเซิร์ฟเวอร์ 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 จาก พื้นที่เก็บข้อมูล Puppetlabs อย่างเป็นทางการ มีการขึ้นต่อกันทั้งหมด (ruby และ gems ที่เกี่ยวข้อง) ดังนั้นจึงไม่มีปัญหาในการติดตั้ง (ฉันกำลังพูดถึงการแจกแจงแบบ Debian - เราไม่ได้ใช้การแจกแจงแบบ RPM)

ในกรณีที่ง่ายที่สุด หากต้องการใช้การกำหนดค่าหุ่นเชิด ก็เพียงพอที่จะเปิดใช้งานเอเจนต์ในโหมดไร้เซิร์ฟเวอร์: โดยมีเงื่อนไขว่าโค้ดหุ่นจะถูกคัดลอกไปยังโหนด จากนั้นเปิดใช้งาน 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)[https://github.com/puppetlabs/r10k] สำหรับการติดตั้งขนาดเล็กก็ควรจะเพียงพอแล้ว

ภาคผนวก 2: แนวทางการเขียนโค้ด

  1. วางตรรกะทั้งหมดไว้ในคลาสและคำจำกัดความ
  2. เก็บคลาสและคำจำกัดความไว้ในโมดูล ไม่ใช่ในรายการที่อธิบายโหนด
  3. ใช้ข้อเท็จจริง.
  4. อย่าสร้าง ifs ตามชื่อโฮสต์
  5. อย่าลังเลที่จะเพิ่มพารามิเตอร์สำหรับคลาสและคำจำกัดความ - ซึ่งดีกว่าตรรกะโดยนัยที่ซ่อนอยู่ในเนื้อหาของคลาส/กำหนด

ฉันจะอธิบายว่าทำไมฉันถึงแนะนำให้ทำเช่นนี้ในบทความถัดไป

ข้อสรุป

มาจบการแนะนำตัวกันดีกว่า ในบทความถัดไป ฉันจะบอกคุณเกี่ยวกับ Hiera, ENC และ PuppetDB

เฉพาะผู้ใช้ที่ลงทะเบียนเท่านั้นที่สามารถเข้าร่วมในการสำรวจได้ เข้าสู่ระบบ, โปรด.

อันที่จริงยังมีเนื้อหาอีกมากมาย - ฉันสามารถเขียนบทความในหัวข้อต่อไปนี้ โหวตสิ่งที่คุณสนใจอ่านเกี่ยวกับ:

  • ลด 59,1%โครงสร้างหุ่นขั้นสูง - สิ่งไร้สาระในระดับถัดไป: ลูป การทำแผนที่และนิพจน์แลมบ์ดาอื่น ๆ ตัวรวบรวมทรัพยากร ทรัพยากรที่ส่งออก และการสื่อสารระหว่างโฮสต์ผ่านหุ่นกระบอก แท็ก ผู้ให้บริการ ประเภทข้อมูลนามธรรม13
  • ลด 31,8%“ฉันเป็นผู้ดูแลระบบของแม่” หรือวิธีที่เราใน Avito ผูกมิตรกับเซิร์ฟเวอร์ poppet หลายเวอร์ชันในเวอร์ชันต่างๆ และโดยหลักการแล้ว ในส่วนที่เกี่ยวข้องกับการจัดการเซิร์ฟเวอร์ poppet7
  • ลด 81,8%วิธีที่เราเขียนโค้ดหุ่นเชิด: เครื่องมือวัด เอกสาร การทดสอบ CI/CD.18

ผู้ใช้ 22 คนโหวต ผู้ใช้ 9 รายงดออกเสียง

ที่มา: will.com