Symfony2 deployment checklist

Symfony2 มีความยืดหยุ่นและความสามารถสูงมาก แต่ทำท่าว่าจะตกมาตายเพราะเรื่องความเร็ว .... แต่เดี๋ยวก่อน! ทีวีไดเร็ค... เอ้ย คุณต้องไม่เชื่อแน่ๆ ว่าในสภาพแวดล้อมที่ใช้งานจริง (prod environment) มันเร็วมากกว่าขั้นตอนการพัฒนาเยอะมาก แต่นั่นก็ยังไม่ดีพอ

ในขั้นตอนการใช้งานจริงของโปรเจคคุณ ขอแนะนำอย่างยิ่ง ให้คุณติดตั้ง PHP Accelerator เช่น APC ด้วย

สำหรับเครื่อง dedicated server

เครื่อง Linux

การติดตั้ง APC บน linux ตระกูล debian, ใช้คำสั่ง:

apt-get install php-apc

อาจจะต้องใช้คำสั่งที่ต่างไปแล้วแต่เครื่องที่คุณใช้.

เครื่อง Windows

แก้ไขไฟล์ php.ini โดยเปิดใช้งานส่วนนี้คือ:

extension=php_apc.dll

ทุกระบบ

หลังจากติดตั้งแล้ว, คุณต้องเปิดใช้งานมันด้วย โดยการเพิ่มบรรทัดตามด้านล่างนี้ในไฟล์ php.ini:

[APC]
apc.enabled=1

สำหรับเครื่อง shared hosting

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

ตรวจสอบให้แน่ใจด้วยว่าคุณเปลี่ยนรหัสลับของโปรเจ็คแล้วในขั้นตอนการใช้งานจริง. เข้าไปตรวจสอบที่ไฟล์ app/config/parameters.yml:

secret:  please_use_a_real_secret_here

รหัสลับที่ระบบใส่ไว้ให้ ไม่ ปลอดภัย, คุณ ต้อง เปลี่ยนมันเป็นอย่างอื่นซะ

เช็คเครื่อง Production Server ก่อนทำการติดตั้งระบบเพื่อใช้งานจริง

คุณมีวิธีตรวจสอบความพร้อมของเครื่อง Server สามวิธีดังนี้:

  1. Run คำสั่ง php app/check.php เพื่อตรวจเช็คระบบ (คุณต้องสามารถเข้าถึง shell ของเครื่องได้)
  2. เรียกไฟล์ config.php ผ่านทาง Browser;
  3. หรือคุณสามารถตรวจสอบความต้องการของระบบจากเอกสารได้ที่ documentation reference page

คุณคงไม่อยากให้โลโก้ของ Symfony โชว์อยู่บนไอค่อนของ browser เวลาที่มีคนเข้าชมเว็บไซต์คุณ นั่นเป็นเหตุผลว่าทำไมคุณต้องแก้ไข favicon เสียใหม่ให้เป็นไอค่อนของคุณเอง

แค่ทำการทับไฟล์เดิมที่มีอยู่คือ web/favicon.ico.

สำหรับสร้าง favicon ใหม่คุณสามารถใช้เครื่องมือเหล่านี้ช่วย:

  • เครื่องมือสร้างไอค่อนออนไลน์ favicon.cc สำหรับสร้างไฟล์ .ico
  • หรือจะใช้ไฟล์ PNG เป็นไอค่อนก็ได้ แต่นั่นแปลว่าคุณต้องแก้ไขแท็ก link ใน HTML เป็น: <link rel="icon" type="image/png" href="yourFavIcon.png">

การเก็บ log การทำงานของ Application เป็นเรื่องสำคัญอย่างยิ่ง Symfony2 ใช้ Monolog ในการทำหน้าที่นี้

การตั้งค่าพื้นฐานที่มีอยู่แล้วเหมาะสำหรับขั้นตอนในการพัฒนาเท่านั้น แต่ในการใช้งานจริง ​(​Production) ควรมีการตั้งค่าใหม่ ด้วยจุดประสงค์ 2 อย่างคือ:

  • ส่งข้อมูลความผิดพลาดของระบบทั้งหมดให้ผู้ดูแลระบบทราบทางอีเมล์ (logs of "error" level)
  • บันทึกข้อมูลเมื่อมีการใช้งานระบบความปลอดภัย เช่น การ Login เข้าระบบเป็นต้น (authentications) ซึ่งข้อมูลเหล่านี้โดยค่าพื้นฐานจะเก็บในระดับ info ที่ไม่มีการบันทึกเป็น log ไว้

การตั้งค่า Monolog สามารถจัดการได้ที่ไฟล์ config_prod.yml:

monolog:
    handlers:
        main:
            type:               fingers_crossed
            action_level:       error
            handler:            grouped
        grouped:
            type:               group
            members:            [streamed, swift]
        streamed:
            type:               stream
            path:               "%kernel.logs_dir%/%kernel.environment%.log"
            level:              debug
        swift:
            type:               swift_mailer
            from_email:         FQN@foo.com
            to_email:           webmaster@company.com
            subject:            "OOps"
            level:              debug
        login:
            type:               stream
            path:               "%kernel.logs_dir%/auth.log"
            level:              info
            channels:           security

ประมาณนี้แหละจ้า!

ใน production environment ขอแนะนำเป็นอย่างยิ่ง ให้ใช้งาน Doctrine cache ซึ่งมีอยู่ 2 แบบ

Query และ Metadata แคช

  • Query แคช ทำหน้าที่ในการเก็บข้อมูลการเปลี่ยนคำสั่ง DQL ไปเป็น SQL ใน production จะไม่ค่อยมีการเปลี่ยนแปลงอะไร นั่นเป็นเหตุผลที่เราควรทำแคชไว้
  • ส่วน Metadata แคช ใช้จัดเก็บ metadata จาก YAML, XML, Annotations เป็นต้น

การทำแคชใน production สามารถทำได้ด้วยการปรับค่าคอนฟิกเล็กน้อยในไฟล์ config_prod.yml:

doctrine:
    orm:
        auto_mapping: true
        query_cache_driver:    apc
        metadata_cache_driver: apc

Result Cache

ผลลัพภ์จากการ queries สามารถทำแคชได้ เพื่อที่จะได้ไม่ต้องติดต่อกับฐานข้อมูลโดยตรงซ้ำแล้วซ้ำอีก นี้เป็นอื่งอย่างหนึ่งที่คุณสามารถกำหนดค่าได้ครั้งเดียวใช้ได้ทุกส่วนใน Application:

doctrine:
    orm:
        auto_mapping: true
        result_cache_driver: apc

แต่ถ้าคุณไม่ต้องการใช้งานแคชผลลัพภ์นี้ทั้งหมดทั้ง Application คุณก็สามารถทำเฉพาะส่วนได้ ลองดูข้อมูลที่ Doctrine Result Cache documentation.

ตรวจสอบให้แน่ใจว่า Doctrine ได้ใช้งาน APC แคชอย่างถูกต้องแล้ว

เมื่อคุณทำการกำหนดค่า APC แคชให้กับ Doctrine เสร็จแล้ว ถ้าคุณไม่ได้เปิดใช้งาน apc.enable_cli = 1 ในไฟล์ php.ini ระบบ Dependency Injection Container จะไปใช้งาน FileCacheReader แทน ซึ่งนั่นไม่ถูกต้องตามที่เราต้องการ ลองตรวจสอบให้แน่ใจอีกครั้ง

ให้เข้าไปดูที่ไฟล์ app/cache/prod/appProdProjectContainer.php. และหาว่ามีข้อมูลตามนี้หรือไม่:

protected function getDoctrine_Orm_DefaultEntityManagerService()
{
    $a = $this->get('annotation_reader');
    $b = new \Doctrine\Common\Cache\ApcCache();
    $b->setNamespace('sf2orm_default_5cdc3404d84577b226d7772ca9818908');
    $c = new \Doctrine\Common\Cache\ApcCache();
    $c->setNamespace('sf2orm_default_5cdc3404d84577b226d7772ca9818908');

    // ...

    $g = new \Doctrine\ORM\Configuration();
    $g->setMetadataCacheImpl($b);
    $g->setQueryCacheImpl($c);

    // ...
}

ถ้าคุณไม่เจอ \Doctrine\Common\Cache\ApcCache แปลว่าคุณยังไม่ได้ใช้งาน APC

โดยพื้นฐานแล้ว ระบบ autoload ของ Composer ยังไม่ใช่สิ่งที่มีประสิทธิภาพที่สุด เพราะเมื่อคุณมีจำนวนคลาสเยอะมากๆ ระบบ autoload ก็กินเวลาพอสมควร

ใน production environment คุณต้องการให้ระบบ autoload ให้เร็วที่สุด Composer สามารถ dump และเพิ่มประสิทธิภาพในการโหลด ด้วยการรวมคลาสที่เป็นแบบ PSR-0 ไว้ในไฟล์ๆเดียวกันได้ ซึ่งจะทำให้มีประสิทธิภาพดีขั้น

วิธีการทำ เพียงแค่คุณเพิ่มคำสั่ง --optimize ต่อท้ายให้กับ dump-autoload ของ Composer:

php composer.phar dump-autoload --optimize

ขั้นตอนนี้อาจใช้เวลาซักหน่อย นั่นเป็นเหตุผลที่มันไม่ได้ทำให้อัตโนมัติตอนติดตั้งโปรเจ็คด้วย Composer คุณต้องทำสิ่งนี้เอง

ฟอร์มของ Symfony2 ฝังและตรวจสอบ CSRF ให้โดยอัตโมัติ

ตรวจสอบให้แน่ใจว่าคุณเปลี่ยนรหัสลับของโปรเจ็คแล้ว เข้าไปตรวจสอบได้ที่ไฟล์ app/config/parameters.yml:

secret:  please_use_a_real_secret_here

อย่างไรซะ การสร้างโทคเคนใหม่เสมอให้ทุกๆฟอร์มที่คุณใช้ก็จะเป็นเรื่องที่ดีกว่า คุณสามารถทำสิ่งนี้ได้ด้วยการเพิ่มอ๊อปชั่น intention ตอนที่คุณสร้างฟอร์ม ดังนี้:

namespace Acme\DemoBundle\Form;

use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class TaskType extends AbstractType
{
    // ...

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            // unique key สำหรับสร้างรหัสรับใหม่ในแต่ละฟอร์ม
            'intention'  => 'task_form',
        ));
    }

    // ...
}

ถ้าคุณใช้หน้า Error pages สำหรับ Production อันเดียวกับในขั้นตอนการ Develop นั่นจะเป็นอะไรที่ไม่ดีสำหรับคนใช้งานเว็บของคุณแน่ๆ คุณควรเปลี่ยนหน้า Error pages เหล่านั้นใหม่ เพื่อให้เว็บไซต์ของคุณดูดีขึ้นเวลาที่มี Error

อย่างที่คุณหวังไว้เลย, การปรับแต่งหน้า error pages ง่ายมากๆ หน้าเพจเหล่านั้นเป็นส่วนหนึ่งของ TwigBundle แปลว่าเราแค่ Override มันเท่านั้นเอง ไฟล์ที่เราต้องแก้ไขทั้งทั้งหมดถูกเก็บไว้ที่ Exception/errorXXX.html.twig โดยที่ XXX คือหมายเลข Error ต่างๆ ขอแนะนำเป็นอย่างยิ่งให้คุณแก้ไขหน้า errors 404 and 500

มีวิธีการอยู่ 2 วิธีให้คุณเลือก:

  1. สร้าง Bundle ขึ้นมาใหม่โดยทำการ extends TwigBundle และสร้างไฟล์เดียวกันไว้ที่ Resources/views/Exception/errorXXX.html.twig วิธีการนี้เหมาะมากถ้าคุณต้องการใช้ errors เพจเดียวกันนี้ในโปรเจ็คอื่นๆ ด้วย เพราะแค่ใช้ Bundle นี้ในโปรเจคอื่นเท่านั้นเอง
  2. หรือว่าคุณจะเขียนไฟล์เหล่านั้นไว้ที่ app/Resources/TwigBundle/views/Exception/errorXXX.html.twig ก็ได้ ถ้าคุณไม่อยากสร้าง Bundle ใหม่

* See Customize error pages on Symfony documentation