all articles

run laravel on aws with nginx and php-fpm

2016-11-07 @sunderls

nginx php-fpm laravel

话说,我其实是个程序员。但不是科班出身,大学时期开始自己没事儿捣鼓捣鼓,就学了lamp和js前端技术。后来陆陆续续搞自己的网站,muzimen.cn(已放弃),直到现在定稿为colla.me,都用的很土的XAMPP。嗯,最开始用的linode,感觉太贵,就换到了digitalocean,但是太慢,后来就转到了AWS,因为AWS提供1年的free tier,结果1年到期后发现换来换去太复杂,就懒得搞了,现在每个月30刀的服务器费用,嘛,感觉还能接受。

直到最近,想要重启2011年的一个旧项目,发现php代码实在是太烂,想重构。目前用的CodeIgniter,原来的作者早就不维护了,死在了2.x,后来社区的人接受,出现了CI3.x, 4.x,但是要求PHP版本更新,而xampp上面的php版本是打包好的,不太好搞,直接装最新版的XAMPP又会搞死,所以就不知道怎么搞了。

经过研究发现,终于想到了一个解决办法,nginx + php-fpm + laravel,花了一个周末搞了搞,总算把collamark.com以外的部分重构了,问题不大,这里介绍一下经验。

1. 首先在aws上,安装PHP7

参考: http://qiita.com/Ikumi/items/8f76d074134b7080d102#cmid=73966

1.1 remi

rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm

1.2 libwebp

yum -y install libwebp --disablerepo=amzn-main --enablerepo=epel

1.3 amzn-main

yum -y install libmcrypt libtool-ltdl libtidy libXpm libtiff gd-last autoconf automake

1.4 php7

yum -y install --disablerepo=amzn-main --enablerepo=remi-php70 php php-opcache php-devel php-mbstring php-mcrypt php-phpseclib-crypt-blowfish php-pecl-apc php-gd php-mysqlnd php-xml php-fpm

2. 安装nginx

sudo yum install -y nginx

默认安装就好

3. 安装laravel

参考: https://laravel.com/docs/5.3/installation

3.1 首先安装composer

参考: https://getcomposer.org/download/

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === 'aa96f26c2b67226a324c27919f1eb05f21c248b987e6195cad9690d5c1ff713d53020a02ac8c217dbf90a7eacc9d141d') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"

3.2 创建laravel 新项目(项目名暂定为colla)

composer create-project --prefer-dist laravel/laravel colla

如果project是从git地方过来的话,库文件是没有的,composer install就好。 这样请求可以通过nginx定位到/path/to/colla/public,然后跑其中的index.php了。

3.3 更改storage文件夹权限

chmod 777 storage/ -R

laravel运行起来后,会在storage中管理session和页面缓存,log之类的,但是app文件下的user是ec2-user,会和nginx冲突。这里偷懒,直接777好了。

4. php-fpm 设置

参考: http://qiita.com/kotarella1110/items/634f6fafeb33ae0f51dc

4.1 更改conf中的user

sudo vim /etc/php-fpm.d/www.conf

把 user, group, listen.user, listen.group都改为nginx, 因为nginx处理请求。

; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server.
; Default Values: user and group are set as the running user
;                 mode is set to 0660
listen.owner = nginx
listen.group = nginx
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
; RPM: apache Choosed to be able to access some dir as httpd
user = nginx
; RPM: Keep a group allowed to write in log dir.
group = nginx

注意nginx, php-fpm, laravel之间会出现用户权限问题,我这里为了简单,就设置为nginx。 如果改为了ec2-user之类的话,nginx内部的临时文件权限会有问题。当然laravel的文件也有问题,就取影响最小的了。

4.2 把端口式改为unix socket

; listen = 127.0.0.1:9000
listen = /var/run/php-fpm.sock

默认是9000,改为socket过后,据说性能会好一些,嘛,感觉上也是这样,php-fpm.sock的名字可以自己取

4.3 修改 post_max_size

php_admin_value[post_max_size] = 32M

没有这个调整的话,post数据一大就会出错。

4.4 重启 php-fpm

sudo service php-fpm restart

这样,php-fpm 服务就跑起来了,ngxin可以把请求导到php-fpm,其中的代码就可以被执行。

5 nginx 设置

sudo vim /etc/nginx/nginx.conf

5.1 添加 index.php

include /etc/nginx/conf.d/*.conf;

index   index.php index.html;

5.2 创建 server

server {
    listen       80;
    listen       [::]:80;
    server_name  colla.me;
    root         /var/www/colla/public/;

    client_max_body_size 300M;
    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm.sock;
        include fastcgi.conf;
    }
}

try_files把请求都导向index.php,然后php的处理会路由到php-fpm的unix socket。

5.3 reload

sudo nginx -s reload

6 完成

这样 aws + nginx + php-fpm + laravel的程序就跑起来啦。

7 laravel的多语言处理

colla.me的url第一个字段是语言代码,比如colla.me/ja/, colla.me/zh/。如果url中没有ja|zh|en,服务器需要根据用户请求中的语言偏好,direct到合适的语言代码,默认为英语。如何实现呢?

7.1 首先在app/config/app.php中添加支持的语言

[
'locale' => 'en',
'locale_avail' => ['zh','en','ja'],
]

locale本来就有,locale_avail表示只支持3种语言。

7.2 routes/web.php中,对请求中的字段进行解析

$locale = '';
if (in_array(Request::segment(1), config('app.locale_avail'))) {
    config(['app.locale' => Request::segment(1)]);
    $locale = Request::segment(1);
} else if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])){
    $languages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
    $defaultLang = 'en';
    foreach($languages as $alang) {
        if(in_array($alang, config('app.locale_avail'))){
            $defaultLang = $alang;
            break;
        }
    }

    $host = Request::root();
    $path = Request::path();
    if (Request::header('X-Forwarded-Host')){
        $host = Request::header('X-Forwarded-Host');
        $pos = strpos($path,'/');
        if ($pos){
            $path = substr($path, strpos($path,'/') + 1);
        } else {
            $path = '';
        }
        header('Location: http://'.$host.'/'.$defaultLang.'/'.$path);
        exit();
    }  else {
        Redirect::to('/'.$defaultLang.'/'.$path)->send();
    }
}

注意如果是nginx代理过来的请求,需要从X-Forwarded-Host获得原来的host。

7.3 locale 字段通过Route::group处理。

Route::group(['prefix' => $locale], function(){
     Route::get('/blog', 'BlogController@index');
     //....
});

总结一下,在route中,动态计算一下locale,然后再注册,就可以实现多语言支持了。