Autodeploy PHP with rocketeer

Bài ngày hôm nay mình sẽ giới thiệu với các bạn các từng bước để deploy PHP với rocketeer.

1. Giới thiệu

Rocketeer là một chương trình chạy với PHP. Nó lấy cảm hứng từ Laravel Framework nên sẽ nhanh chóng, thanh lịch và quan trọng nhất là dễ sử dụng.

Chức năng chính:

  • Đa năng, hỗ trợ nhiều kết nối, kết nối đa giao diện, nhiều stage trên mỗi máy chủ, v.v ...
  • Nhanh: queue task và chạy chúng song song trên tất cả các máy chủ và các stage
  • Có tính module: bạn không chỉ có thể thêm các tác vụ tùy chỉnh và các thành phần, các phần cốt lõi của Rocketeer cũng có thể được thay đổi, mở rộng
  • Cấu hình sẵn: nếu bạn mệt mỏi với việc thực hiện cùng một thói quen lặp đi lặp lại nhiều lần? Rocketeer được thực hiện cho sự xây dựng hiện đại và đi kèm với các mặc định thông minh và các tác vụ tích hợp như cài đặt các lib của ứng dụng
  • Mạnh mẽ: quản lý phiên bản release, kiểm tra máy chủ, rollbacks ... Tất cả các tính năng bạn mong đợi từ một công cụ auto deploy đều có sẵn.

Tại sao lại sử dụng Rocketeer? Mà không phải là Capistrano? Vâng. Ngay trên trang chủ của Rocketeer cũng có câu trả lời.

"Đó là một câu hỏi được hỏi cho tôi, tại sao không sử dụng Capistrano? Tôi đã sử dụng Capistrano trong quá khứ, nó làm mọi thứ bạn muốn, đó là một điều tuyệt vời.

Tuy nhiên, nó vẫn là một gói Ruby và tương thích chặt chẽ với Rails theo một số cách; Rocketeer làm cho nó để bạn không cần phải cài các package Ruby xung quanh ứng dụng của bạn. Bằng cách đó bạn cấu hình nó một lần và có thể sử dụng nó bất cứ nơi nào bạn muốn trong lĩnh vực ứng dụng của bạn, ngay cả bên ngoài của thói quen triển khai. Capistrano cũng rất dễ hiểu, dành cho những người dùng lần đầu hay người mới làm quen, Capistrano rất dễ dàng để tham gia cùng một lúc - Rocketeer hướng tới càng đơn giản càng tốt bằng cách cung cấp các mặc định thông minh và tăng thời gian giữa cài đặt và lần đầu tiên tấn công deploy.

Nó cũng được suy nghĩ nhiều hơn cho thế giới PHP - mặc dù bạn có thể cấu hình Capistrano để chạy Composer và PHPUnit, điều đó không phải là điều mong đợi, trong khi những công việc đó là một phần nhiệm vụ của mọi nhà phát triển PHP được tích hợp trong quá trình triển khai cốt lõi của Rocketeer."

2. Cài đặt

Vâng. cài đặt rocketeer rất đơn giản và dễ dàng. Bạn chỉ cần download file chạy về và phân quyền run. done!

$ wget http://rocketeer.autopergamene.eu/versions/rocketeer.phar
$ chmod +x rocketeer.phar
$ sudo mv rocketeer.phar /usr/local/bin/rocketeer

Đây là các câu lệnh có thể sử dụng với Rocketeer:

$ rocketeer list

Available commands:
  check            Check if the server is ready to receive the application
  cleanup          Clean up old releases from the server
  current          Display what the current release is
  deploy           Deploys the website
  flush            Flushes Rocketeer's cache of credentials
  help             Displays help for a command
  ignite           Creates Rocketeer's configuration
  list             Lists commands
  rollback         Rollback to the previous release, or to a specific one
  setup            Set up the remote server for deployment
  strategies       Lists the available options for each strategy
  teardown         Remove the remote applications and existing caches
  test             Run the tests on the server and displays the output
  tinker           Debug Rocketeer's environment
  update           Update the remote server without doing a new release
plugin
  plugin:config    Publishes the configuration of a plugin
  plugin:install   Install a plugin
  plugin:list      Lists the currently enabled plugins

3. Config Rocketeer

Chúng ta sẽ config Rocketeer = câu lệnh $ rocketeer ignite

No connections have been set, please create one: (production)develop

-> bạn phải định nghĩa môi trường để rocketeer làm việc. như ở đây mình dùng develop

No host is set for [develop/0], please provide one:10.0.1.101

-> chỉ định ip của host nào thuộc môi trường develop: 10.0.1.101

No username is set for [develop/0], please provide one:cuongtv

-> tên user để deploy: cuongtv

No password or SSH key is set for [develop/0], which would you use? (key) [key/password]password
No password is set for [develop/0], please provide one:

-> chọn cách thức ssh tới server (password hay dùng key): ở đây mình chọn password

No repository is set for [repository], please provide one:https://github.com/cuongtv2004/test-01.git

-> khai báo repo để deploy: https://github.com/cuongtv2004/test-01.git

No username is set for [repository], please provide one:
No password is set for [repository], please provide one:

-> khai báo user để login vào repo đó (nếu public thì không cần)

develop/0 | Ignite (Creates Rocketeer's configuration)
What is your application's name ? (test)test01
The Rocketeer configuration was created at test/.rocketeer

-> tên application: test01

Vậy là xong phần config. Chúng ta thử kiểm tra xem:

[email protected]:~/test$ ll
total 12
drwxrwxr-x  3 cuongtv cuongtv 4096 Oct  1 16:48 ./
drwxr-xr-x 11 cuongtv cuongtv 4096 Oct  1 16:42 ../
drwxrwxr-x  2 cuongtv cuongtv 4096 Oct  1 16:48 .rocketeer/

Vậy là rocketeer đã sinh ra thêm 1 folder .rocketeer. Tiếp theo chúng ta sẽ xem kĩ hơn các folder có trong đó nhé.

4. Cấu trúc thư mục:

$ tree .rocketeer/
.rocketeer/
├── config.php		
├── hooks.php		
├── paths.php		
├── remote.php		
├── scm.php			
├── stages.php		
└── strategies.php	
  • config.php Đây là nơi chứa file config chính. Các thông tin bạn vừa nhập khi cấu hình rocketeer sẽ lưu tại đây
...
    'default'          => ['develop'],

    // The various connections you defined
    // You can leave all of this empty or remove it entirely if you don't want
    // to track files with credentials : Rocketeer will prompt you for your credentials
    // and store them locally
    'connections'      => [
        'production' => [
            'host'      => '10.0.1.101',
            'username'  => 'cuongtv',
            'password'  => 'xxxxxxx',
            'key'       => '',
            'keyphrase' => '',
            'agent'     => '',
            'db_role'   => true,
        ],
    ],
...
  • hooks.php File này định nghĩa những task sẽ được chạy trên server trước và sau khi bạn thực hiện nhiệm vụ deploy. VD trước khi setup, mình cho cài nginx trên sv remote.
...
    'before' => [
        'setup'   => ['sudo apt-get install -y nginx',
                ],      
        'deploy'  => [],
        'cleanup' => [],
    ],
...
  • paths.php Bạn có thể định nghĩa đường dẫn của PHP và composer trong file này. Nếu không thì rocketer sẽ tự tìm đường dẫn mặc định của chúng.

  • remote.php Đây là file lưu mọi setting trên server được deploy. Thông thường, các bạn sẽ cần chú ý tới các dòng này:

    // The root directory where your applications will be deployed
    // This path *needs* to start at the root, ie. start with a /
    'root_directory' => '/opt/cuongtv/',

-> Đây là folder mà các bạn sẽ deploy

    // The folder the application will be cloned in
    // Leave empty to use `application_name` as your folder name
    'app_directory'  => '',

-> tên folder app các bạn muốn. Nếu không điền, rocketeer sẽ tự động nhận tên app khi các bạn cấu hình rocketeer.

    // A list of folders/file to be shared between releases
    // Use this to list folders that need to keep their state, like
    // user uploaded data, file-based databases, etc.
    'shared'         => [
        'storage/logs',
        'storage/sessions',
    ],

-> các bạn tạo folder/file share giữa các lần deploy (thường là file log, sesion và file env)

  • scm.php Đây là file lưu thông tin repo và branch deploy của các bạn. Các bạn có thể sử dụng với SVN hoặc với Git.
    // The SCM used (supported: "git", "svn")
    'scm'        => 'git',

    // The SSH/HTTPS address to your repository
    // Example: https://github.com/vendor/website.git
    'repository' => 'https://github.com/cuongtv2004/test-01.git',

    // The repository credentials : you can leave those empty
    // if you're using SSH or if your repository is public
    // In other cases you can leave this empty too, and you will
    // be prompted for the credentials on deploy. If you don't want
    // to be prompted (public repo, etc) set the values to null
    'username'   => '',
    'password'   => '',

    // The branch to deploy
    'branch'     => 'master',
  • stages.php Trường hợp trên một máy chủ có nhiều stage khác nhau (staging, testing, production) thì chúng ta sẽ định nghĩa tại đây.

  • strategies.php File cuối cùng cho phép bạn định cấu hình tác vụ nào để sử dụng để thực hiện các phần cốt lõi của luồng triển khai của bạn

...
    // Which strategy to use to check the server
    'check'        => 'Php',

    // Which strategy to use to create a new release
    'deploy'       => 'Clone',

    // Which strategy to use to test your application
    'test'         => 'Phpunit',

    // Which strategy to use to migrate your database
    'migrate'      => 'Artisan',

    // Which strategy to use to install your application's dependencies
    'dependencies' => 'Polyglot',
    
    // Execution hooks
    //////////////////////////////////////////////////////////////////////

    'composer'     => [
        'install' => function (Composer $composer, $task) {
            return $composer->install([], ['--no-interaction' => null, '--no-dev' => null, '--prefer-dist' => null]);
        },
        'update'  => function (Composer $composer) {
            return $composer->update();
        },
    ],
...

5. Deploy

Tất nhiên là để deploy được thì trên server các bạn nhớ phải cài php trước nhé 😄

  • Check kết nối tới server remote:
$ rocketeer check
| Check (Check if the server is ready to receive the application) [~0.51s]
PHP Warning:  unpack(): Type N: not enough input, need 4, have 1 in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Warning:  extract() expects parameter 1 to be array, boolean given in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Notice:  Undefined variable: length in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2827
|-- Check/Php (Checks if the server is ready to receive a PHP application)
|=> Checking presence of /usr/bin/git
$ /usr/bin/git --version
[[email protected]] (develop) git version 2.7.4
|=> Checking presence of Composer
|=> Checking PHP version
|=> Checking presence of required extensions
|=> Checking presence of required drivers
The Composer package manager could not be found
The tasks queue was canceled by task "Check"
Execution time: 0.4898s
Saved logs to /home/cuongtv/test/.rocketeer/logs/develop--20171001.log

Fail rồi. OK. không sao cả. ở đây là do trong folder deploy chưa tìm thấy composer thôi. Khởi tạo file composer nào.

$ composer init

                                            
  Welcome to the Composer config generator  
                                            


This command will guide you through creating your composer.json config.

Package name (<vendor>/<name>) [cuongtv/test]: 
Description []: 
Author [, n to skip]: n
Minimum Stability []: 
Package Type (e.g. library, project, metapackage, composer-plugin) []: 
License []: 

Define your dependencies.

Would you like to define your dependencies (require) interactively [yes]? 
Search for a package: 
Would you like to define your dev dependencies (require-dev) interactively [yes]? 
Search for a package: 

{
    "name": "cuongtv/test",
    "require": {}
}

Do you confirm generation [yes]?

OK rồi. thử check lại nhé:

$ rocketeer check
No username is set for [repository], please provide one:
No password is set for [repository], please provide one:
| Check (Check if the server is ready to receive the application)
PHP Warning:  unpack(): Type N: not enough input, need 4, have 1 in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Warning:  extract() expects parameter 1 to be array, boolean given in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Notice:  Undefined variable: length in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2827
|-- Check/Php (Checks if the server is ready to receive a PHP application)
|=> Checking presence of /usr/bin/git
$ /usr/bin/git --version
[[email protected]] (develop) git version 2.7.4
|=> Checking presence of Composer
|=> Checking PHP version
|=> Checking presence of required extensions
|=> Checking presence of required drivers
|=> Your server is ready to deploy
Execution time: 0.5114s
Saved logs to /home/cuongtv/test/.rocketeer/logs/develop--20171001.log

Vậy là đã kết nối thành công tới server rồi.

  • Setup
$ rocketeer setup
| Setup (Set up the remote server for deployment) [~0.4s]
|-- Closure (sudo apt-get install -y nginx) fired by setup.before [~0.32s]
PHP Warning:  unpack(): Type N: not enough input, need 4, have 1 in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Warning:  extract() expects parameter 1 to be array, boolean given in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Notice:  Undefined variable: length in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2827
$ cd /opt/cuongtv/test01/releases/
$ sudo apt-get install -y nginx
...
|-- Check (Check if the server is ready to receive the application) [~0.32s]
|---- Check/Php (Checks if the server is ready to receive a PHP application)
|===> Checking presence of /usr/bin/git
$ /usr/bin/git --version
[[email protected]] (develop) git version 2.7.4
|===> Checking presence of Composer
|===> Checking PHP version
|===> Checking presence of required extensions
|===> Checking presence of required drivers
|===> Your server is ready to deploy
$ mkdir /opt/cuongtv/test01/
[[email protected]] (develop) mkdir:
[[email protected]] (develop) cannot create directory ‘/opt/cuongtv/test01/’
[[email protected]] (develop) : File exists
[[email protected]] (develop) 
$ mkdir -p /opt/cuongtv/test01/releases
$ mkdir -p /opt/cuongtv/test01/current
$ mkdir -p /opt/cuongtv/test01/shared
|=> Getting some informations about the server
|=> Successfully setup "test01" at "/opt/cuongtv/test01"
Execution time: 9.1382s
Saved logs to /home/cuongtv/test/.rocketeer/logs/develop--20171001.log

vậy là đã setup xong. Sau bước setup, trên server remote sẽ có 3 folder:

├── current
├── releases
└── shared

trong đó: current là folder chứa code đang chạy, releases: folder chứa các bản release (chứa tối đa 4 lần theo file config) shared: folder chứa các file share để dùng chung cho các lần release

  • Deploy:
$ rocketeer deploy
No username is set for [repository], please provide one:
No password is set for [repository], please provide one:
| Deploy (Deploys the website)
PHP Warning:  unpack(): Type N: not enough input, need 4, have 1 in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Warning:  extract() expects parameter 1 to be array, boolean given in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Notice:  Undefined variable: length in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2827
|-- Primer (Run local checks to ensure deploy can proceed)
|-- CreateRelease (Creates a new release on the server)
|---- Deploy/Clone (Clones a fresh instance of the repository by SCM)
|===> Cloning repository in "/opt/cuongtv/test01/releases/20171001224901"
fatal: Not a git repository (or any of the parent directories): .git
$ /usr/bin/git clone "https://github.com/cuongtv2004/test-01.git" "/opt/cuongtv/test01/releases/20171001224901" --branch="master" --depth="1"
[[email protected]] (develop) Cloning into '/opt/cuongtv/test01/releases/20171001224901'...
|===> Initializing submodules if any
$ cd /opt/cuongtv/test01/releases/20171001224901
$ /usr/bin/git submodule update --init --recursive
|-- Dependencies (Installs or update the dependencies on server)
|---- Dependencies/Polyglot (Runs all of the above package managers if necessary)
|------ Dependencies/Composer (Installs dependencies with Composer)
|=> Sharing file /opt/cuongtv/test01/releases/20171001224901/storage/logs
|=> Sharing file /opt/cuongtv/test01/releases/20171001224901/storage/sessions
|=> Setting permissions for /opt/cuongtv/test01/releases/20171001224901/app/database/production.sqlite
$ cd /opt/cuongtv/test01/releases/20171001224901
$ chmod -R 755 /opt/cuongtv/test01/releases/20171001224901/app/database/production.sqlite
$ chmod -R g+s /opt/cuongtv/test01/releases/20171001224901/app/database/production.sqlite
$ chown -R www-data:www-data /opt/cuongtv/test01/releases/20171001224901/app/database/production.sqlite
[[email protected]] (develop) chmod:
[[email protected]] (develop) cannot access '/opt/cuongtv/test01/releases/20171001224901/app/database/production.sqlite'
[[email protected]] (develop) : No such file or directory
[[email protected]] (develop) 
|=> Setting permissions for /opt/cuongtv/test01/releases/20171001224901/storage
$ cd /opt/cuongtv/test01/releases/20171001224901
$ chmod -R 755 /opt/cuongtv/test01/releases/20171001224901/storage
$ chmod -R g+s /opt/cuongtv/test01/releases/20171001224901/storage
$ chown -R www-data:www-data /opt/cuongtv/test01/releases/20171001224901/storage
[[email protected]] (develop) chmod:
[[email protected]] (develop) cannot access '/opt/cuongtv/test01/releases/20171001224901/storage'
[[email protected]] (develop) : No such file or directory
[[email protected]] (develop) 
|=> Setting permissions for /opt/cuongtv/test01/releases/20171001224901/public
$ cd /opt/cuongtv/test01/releases/20171001224901
$ chmod -R 755 /opt/cuongtv/test01/releases/20171001224901/public
$ chmod -R g+s /opt/cuongtv/test01/releases/20171001224901/public
$ chown -R www-data:www-data /opt/cuongtv/test01/releases/20171001224901/public
[[email protected]] (develop) chmod:
[[email protected]] (develop) cannot access '/opt/cuongtv/test01/releases/20171001224901/public'
[[email protected]] (develop) : No such file or directory
[[email protected]] (develop) 
$ rm -rf /opt/cuongtv/test01/current
$ ln -s /opt/cuongtv/test01/releases/20171001224901 /opt/cuongtv/test01/current-temp
$ mv -Tf /opt/cuongtv/test01/current-temp /opt/cuongtv/test01/current
|=> Successfully deployed release 20171001224901
| Cleanup (Clean up old releases from the server)
|=> No releases to prune from the server
Execution time: 3.6339s
Saved logs to /home/cuongtv/test/.rocketeer/logs/develop--20171001.log

Nào, chúng ta thử kiểm tra nhé. Tạo file nginx trỏ tới /opt/cuongtv/test01/current nào. test01 test02

Khi bạn deploy 1 version bị lỗi. Yên tâm, chúng ta sẽ rollback lại ngay trong 1 nốt nhạc:

$ rocketeer rollback
| Rollback (Rollback to the previous release, or to a specific one) [~0.45s]
PHP Warning:  unpack(): Type N: not enough input, need 4, have 1 in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Warning:  extract() expects parameter 1 to be array, boolean given in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2826
PHP Notice:  Undefined variable: length in phar:///usr/local/bin/rocketeer/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php on line 2827
$ ln -s /opt/cuongtv/test01/releases/20171001230618 /opt/cuongtv/test01/current-temp
$ mv -Tf /opt/cuongtv/test01/current-temp /opt/cuongtv/test01/current
|=> Rolling back to release 20171001230618
Execution time: 0.4657s
Saved logs to /home/cuongtv/test/.rocketeer/logs/develop--20171001.log

Và đây là Kết quả