PHP程序配置文件(最佳?)实践

最原始的方式

写php程序时会直接把数据库、缓存的连接信息放在config.php文件里。这样做有两个弊端,1.在开发调试时必须先把连接信息改成本地的,要提交代码时再改回远程的,麻烦,也容易遗漏。2.开发人员可以直接看到线上数据库的连接地址、账号、密码,不安全。

PHP程序配置文件(最佳?)实践

怎么办?

线上运行PHP程序时通常需要一个Http服务器,例如apache、nginx。以nginx为例,在配置文件中可以通过fastcgi_param指令来给PHP传递变量

http://leo108.com/pid-2184.asp

fastcgi_param  DB_HOST  "192.168.1.1";

这样就可以在PHP代码里通过$_SERVER[‘DB_HOST’]来获取到对应的值。PHP程序配置文件(最佳?)实践

所以开发环境和线上环境只要配好nginx的配置,就可以实现不改代码执行程序了。PHP

更进一步,可以传递一个标示当前环境的变量,例如leo108's blog

fastcgi_param  CODE_ENV  "production";

然后在程序里可以根据$_SERVER[‘CODE_ENV’]的不同来执行一些不同的逻辑(比如开发环境会打印所有错误信息而线上环境不显示)http://leo108.com/pid-2184.asp

这样完美了吗?

并没有,在实际应用中发现有两个问题:1.如果这个PHP程序不仅仅提供web服务,还提供了cli工具(例如数据库升级脚本),这个时候nginx传递的变量就过不来了。2.貌似不支持传递数组变量。配置

解决思路

要同时支持web和cli,通过nginx配置传变量已经行不通了,那能不能走php自己的配置呢?

于是在php.ini的末尾加上如下配置:

[userconf]
userconf.db_host=127.0.0.1
userconf.db_name=test

重启php-fpm之后,发现通过ini_get(‘userconf.db_host’)取到的数据是空,于是详细查看了php的文档,发现对于自定义的配置项,需要通过get_cfg_var()函数来获得。经过测试,在web和cli模式下,通过get_cfg_var(‘userconf.db_host’)可以拿到正确的值。

再优化

但这个方案还是有弊端:1.修改配置需要重启php-fpm。2.虽然php.ini里面支持数组的数据,但是还是不够灵活,最好是能直接用php配置。

http://leo108.com

所以我们可以在php.ini里面只配置一个配置目录的路径,这个目录下放置各个程序的配置文件,php程序先从php.ini获取到这个目录的路径,再从这个目录下读取php格式的配置文件。示例代码:配置

$path = get_cfg_var('userconf.dir');
$conf = include($path . '/test.php');

这样配置文件是每次访问都会重新读取,变更时不需要重启php-fpm;而且不同的站点只要选择不同的配置文件名,就可以在一台服务器上共存。

但这样还是有一个问题没有解决,那就是无法在同一台服务器上部署两个相同的站点,不过这个场景也不多,不解决也没关系。

vagrant centos升级内核版本、升级VBoxGuestAdditions版本

在vagrant的centos中,如果直接执行yum install kernel-devel,会提示”No matches found for: kernel-devel”,仔细观察了一下yum的输出,发现加载了一个versionlock的插件,于是猜测与这个插件有关,禁用了内核版本的更新,所以把这个插件禁用掉即可。

centos

编辑/etc/yum/pluginconf.d/versionlock.conf文件,将enable的值改成0。然后再执行yum update kernel就可以将内核更新到最新版本。centos

但是这个时候如果重启了vagrant虚拟机,会发现vagrant报错

推酷是个无耻的网站

Failed to mount folders in Linux guest. This is usually because
the “vboxsf” file system is not available. Please verify that
the guest additions are properly installed in the guest and
can work properly.vagrant centos升级内核版本、升级VBoxGuestAdditions版本

搜索了下,发现可以通过执行/etc/init.d/vboxadd setup重新安装VBoxGuestAdditions来解决。

===============================================

但是centos自带的VBoxGuestAdditions版本比较低,在启动vagrant的时候也会提示

The guest additions on this VM do not match the installed version of VirtualBox! In most cases this is fine, but in rare cases it can prevent things such as shared folders from working properly. If you see shared folder errors, please make sure the guest additions within the virtual machine match the version of VirtualBox you have installed on your host and reload your VM.推酷是个无耻的网站

所以可以更新一下VBoxGuestAdditions版本。访问http://download.virtualbox.org/virtualbox/,找到你当前virtualbox版本的目录,进去之后可以找到对应版本的VBoxGuestAdditions的iso文件,将这个iso文件下载到vagrant虚拟机中,然后执行以下命令:

mount VBoxGuestAdditions_5.0.10.iso -o loop /mnt
cd /mnt/
sh VBoxLinuxAdditions.run --nox11

执行完毕之后退出虚拟机,再次执行vagrant reload即可virtualbox

将git源码打包成rpm安装包(centos)

centos6系统里通过yum安装的git版本只有1.7.1,好多新特性都没有。而使用编译安装的话,每台服务器都要编译一遍太麻烦,以后更新起来也麻烦,所以决定自己打一个rpm包。

首先需要安装rpm-build

yum install -y rpm-build

然后创建一个rpmbuild目录:git

cd ~ && rpmdev-setuptree

https://github.com/git/git/releases下载git源码包,一定要下载.tar.gz版本的,rpmbuild需要这种格式。

http://leo108.com

先下载到home目录下

将git源码打包成rpm安装包(centos)

wget https://github.com/git/git/archive/v2.6.4.tar.gz -O ~/git-2.6.4.tar.gz

复制git-2.6.4.tag.gz到rpmbuild目录下的SOURCES目录http://leo108.com/pid-2172.asp

cp ~/git-2.6.4.tar.gz ~/rpmbuild/SOURCES

解压tar包后,在git-2.6.4目录中执行yum

make git.spec && rpmbuild -ba git.spec

如果在rpmbuild过程中提示错误,通常是因为编译依赖的原因,这个时候只需要执行yum install 依赖包,然后再次执行rpmbuild -ba git.spec。

rpmbuild

等到编译打包完成后,rpm包位于~/rpmbuild/RPMS/x86_64目录下,通常只需要git-2.6.4-1.el6.x86_64.rpm和perl-Git-2.6.4-1.el6.x86_64.rpm两个包。将git源码打包成rpm安装包(centos)

在需要安装新版git的服务器上,先通过yum卸载旧包

推酷是个无耻的网站

yum remove -y perl-Git git

然后将那两个rpm包复制过来,再通过yum安装:

yum localinstall git-2.6.4-1.el6.x86_64.rpm perl-Git-2.6.4-1.el6.x86_64.rpm

这个时候执行git –version就可以看到系统里已经是新版本的git了。

将git源码打包成rpm安装包(centos)

===================12月10日补充===================centos

这种方法适用于大多数程序,例如ganglia就在源码目录下提供了对应的spec文件。git

参考资料:centos

  1. https://gist.github.com/fernandoaleman/1376973
  2. http://stackoverflow.com/a/33439452/2013307
  3. 制作php的RPM包:https://blog.linuxeye.com/431.html

如何在linux的终端输出二维码

脑子突然间蹦出来的想法,就迫不及待试试看能否实现。http://leo108.com/pid-2161.asp

需求很简单,就是在linux的终端中输入一个字符串(可以是以命令行参数形式,也可以是通过交互式输入),然后就会输出对应的二维码。

终端

首先PHP已经有现成的QrCode类库phpqrcode,可以将一个字符串转成PNG格式的图片,但是PNG图片是没法在终端里展示的,于是仔细翻看文档和demo,发现该类库也可以输出0和1组成的矩阵(实际上该方法返回的是一个PHP的二维数组)。

http://leo108.com/pid-2161.asp

已经有了0和1的矩阵,接下来要做的就是输出黑白色块,为了操作方便,我引入了symfony项目中的console组件。通过console组件可以非常方便的创建一个Cli命令,而且内置了大量输入和输出方法。

终端

根据console的文档,我们可以新建两个OutputFormatStyle:

如何在linux的终端输出二维码

$black = new OutputFormatterStyle('black', 'black');
$output->getFormatter()->setStyle('blackc', $black);
$white = new OutputFormatterStyle('white', 'white');
$output->getFormatter()->setStyle('whitec', $white);

定义了文字颜色和背景颜色分别是白色和黑色的两个样式。http://leo108.com/pid-2161.asp

这样就可以输出白色和黑色的色块了:

$output->writeln('<whitec>  </whitec><blackc>  </blackc><whitec>  </whitec>');

上面的代码就会输出两个白色块中间隔着一个黑色块。终端

黑白色块输出搞定之后,只需要根据二维码的0-1矩阵输出对应色块就行。

所以核心代码如下:

QrCode

protected function execute(InputInterface $input, OutputInterface $output)
{
    $lrPadding = 1;
    $udPadding = 1;
    $text = 'http://leo108.com';
    $map = array(
        0 => '<whitec>  </whitec>',
        1 => '<blackc>  </blackc>',
    );
    $this->initStyle($output);
    $text   = QRcode::text($text);
    $length = strlen($text[0]);

    $paddingLine = str_repeat($map[0], $length + $lrPadding * 2) . "\n";
    $after = $before = str_repeat($paddingLine, $udPadding);
    $output->write($before);
    foreach ($text as $line) {
        $output->write(str_repeat($map[0], $lrPadding));
        for ($i = 0; $i < $length; $i++) {
            $type = substr($line, $i, 1);
            $output->write($map[$type]);
        }
        $output->writeln(str_repeat($map[0], $lrPadding));
    }
    $output->write($after);
}

其中$lrPadding和$udPadding分别用来配置左右和上下白边的长度。

最终代码已托管githubQrCode

最后来张效果图:

QrCode

如何在linux的终端输出二维码

openldap主从配置

在公司服务器上搭建了一个LDAP服务,为了避免出现单点,需要给LDAP做主从。在网上查了一下,基本上都是在sldap.conf里使用replica配置。但是在重启ldap服务时提示:

ldap

<replogfile> keyword is obsolete (ignored)
<replica> keyword is obsolete (ignored)

http://leo108.com/pid-2158.asp

说明这两个配置已经是被废弃的了,于是上openldap官网上查了一下openldap的复制功能http://leo108.com

openldap支持5种复制方式,分别是

leo108's blog

Syncrepl:slave服务器从master上拉取数据,缺点是拉取的最小粒度是单条记录
Delta-syncrepl:与上一条相似,但拉取的最小粒度是属性
N-Way Multi-Master:多主,支持2个及以上的master
MirrorMode:双主镜像,不支持3个及以上的master,但可以有slave
Syncrepl Proxy:代理模式

推酷是个无耻的网站

按目前的需求只要配置成MirrorMode即可,编辑/etc/openldap/sldap.conf,openldap主从配置

找到“moduleload syncprov.la”,将前面的#号去掉。

ldap

在最后加入

index entryCSN,entryUUID eq
overlay syncprov
syncprov-checkpoint 100 10
syncprov-sessionlog 100

serverID 1
syncrepl rid=123
    provider=ldap://node2:389
    type=refreshAndPersist
    searchbase="dc=example,dc=com"
    schemachecking=off
    bindmethod=simple
    binddn="cn=Manager,dc=example,dc=com"
    credentials=secret
    retry="60 +"
mirrormode on

其中serverID是节点ID,唯一。rid是复制ID,两台服务器必须一致。

http://leo108.com/pid-2158.asp

修改完毕之后将两个服务器上的ldap都重启下即可。

leo108's blog

参考:http://www.ttlsa.com/database/openldap-mirrormode-cluster/

推酷是个无耻的网站

CentOS7 与 CentOS6 主要不同点

根据RedHat的升级指南整理,这里只记录影响比较大的部分。CentOS7 与 CentOS6 主要不同点

  1. systemd替代SysV。systemd与SysV基本上兼容,service命令会直接映射到systemd。systemd只支持start、stop和status 3种动作。
  2. 磁盘挂载。默认情况下如果/etc/fstab中配置的磁盘挂载失败就会导致系统启动失败。除非加入nofail参数。如
    /dev/optional     /optional   xfs   defaults,nofail   1  2
  3. 文件系统布局。/bin, /sbin, /lib 和 /lib64 目录现在位于 /usr 目录中,但为了兼容,系统创建了软链(即/bin指向了/usr/bin)。
  4. /tmp目录成为tmpfs挂载点。简单来说/tmp目录变成一个内存存储目录,也就是重启机器后目录中的文件就丢失。可以通过systemctl enable/disable tmp.mount来启用/关闭此功能。对于需要永久存储或者体积特别大的文件,可以放在/var/tmp目录中。
  5. 系统语言设置。从原本的/etc/sysconfig/i18n改为/etc/locale.conf 和 /etc/vconsole.conf。
  6. hostname设置。从原本的/etc/sysconfig/network改为/etc/hostname。
  7. 默认文件系统改成xfs。
  8. 通过firewalld后台进程配置iptables,firewalld可以动态修改配置而不会影响到现有的网络连接。配置文件位于/usr/lib/firewalld和/etc/firewalld。
  9. 用户id基数从500调整为1000。

gitlab升级新版后显示500错误

今天gitlab从7.x升级到8.0.1,发现有部分页面出现了http500错误,比如管理后台的setting页面、ci系统、各个项目的hook页面。

通过查看gitlab的日志,所有页面出错的原因基本是都是某个方法找不到(undefined method `enable_ssl_verification=’ 或者 undefined method `import_sources’),通过搜索,发现gitlab上有类似的问题

按照里面的步骤先执行

gitlab-rake gitlab:check

大多数地点都是OK,有个地方error

http://leo108.com/pid-2150.asp

All migrations up? … no
Try fixing it:
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
Please fix the error above and rerun the checks.

http://leo108.com/pid-2150.asp

看起来像是数据库没有被更新,但是不要按照提示的操作,会提示bundle命令不存在,按照刚刚那个帖子里的提示执行

gitlab-rake db:migrate

刷新页面发现有问题的页面已经正常。

git子树合并

公司的站点用的都是内部开发的一个框架,之前全部都是通过submodule的方式引入到具体项目中,但在使用过程中发现submodule十分不方便,从远端拉取更新时,并不会主动更新submodule的目录(除非加上–recurse-submodules参数),还得再手动执行一次git submodule update,如果忘记更新submodule,之后提交变更又会把submodule给退回旧版本,很容易出问题。

git

之前在参与progit翻译的时候看到git还有一个子树合并的功能,也能够实现在一个git仓库中引入另一个git仓库。leo108's blog

于是参照progit中的步骤,首次将框架的仓库引入到项目仓库中是OK的,但是从框架仓库拉取更新时报错

git子树合并

Automatic merge failed; fix conflicts and then commit the result.

于是搜索了下,发现stackoverflow上也有人遇到相同的问题,应该是progit的错误(也可能是git新版本修改了子树合并的操作),按照这个步骤操作就可以了。

git

有一点需要注意的是,如果git配置了pull.rebase=true,在pull子树更新时需要加上参数–rebase=false,否则会出错。(没有具体了解是什么原因,有兴趣的同学自己探索一下)

git子树合并

专注于技术,切不可沉湎于技术