header image

在Ubuntu Server 16.04 LTS上安装PHP7

之前我们说过在Ubuntu Server 14.04 LTS上安装PHP7的事宜。现在新的LTS已经出了,当然也要说一说。

安装

很简单(以FPM为例,需要Apache2模块的自行研究):

sudo apt-get install php7.0-fpm php7.0-mysql php7.0-curl php7.0-mcrypt php7.0-json php7.0-gd

基本上这样就能运行大多数PHP程序了。还有其他一些包需要的请自行安装。

配置

配置文件和过去有点变化,在/etc/php/7.0/fpm下。和过去一样,修改php.ini等相关文件就行了。

默认的监听池的路径也有点变化,在/var/run/php/php7.0-fpm.sock,所以NginxApache2的相关设置就需要改一下。

因为16.04使用了SystemD,和过去不同,重启服务的方式如下:

sudo systemctl restart php7.0-fpm

总结

就这么简单。

添加代码高亮功能

技术类的博客,不来点代码高亮总觉得逼格不够。下面就说说如何加入这个功能吧,所有主题通用(其他类型网页也可以举一反三,方法都是类似的)。

准备

按照效率来说,使用PHP的方案自然是最好的。但鉴于使用PHP会造成需要修改的地方比较多比较麻烦,所以就不考虑了。这次就用一个比较常见JavaScript库来解决——highlight.js。这个库简单易用,而且自带很多种样式选择,总有一种适合你。

选择样式

这里有很多种预设样式可以选择。左边的Language categories就是选择语言的分类,Styles就是选择样式,右边就可以查看效果。确定好要使用的样式之后,到这里找到对应的css文件,点开后把代码复制下来保存到主题目录就行了。比如我就保存到主题下的css/highlight.css文件中。

然后就要把样式引入主题中,最简单的方法就是修改主题的头部文件header.php(视主题而定,一般是这个文件)的</head>前添加这样一行:

<link rel="stylesheet" href="<?php echo esc_url( get_template_directory_uri() ); ?>/css/highlight.css">

<?php echo esc_url( get_template_directory_uri() ); ?>是输出当前主题的网络路径。可以根据需要修改。

关于CDN

highlight.js我们可以通过网上的CDN来引入,也可以放到自己的网站上。比如使用百度的CDN,就上百度静态资源公共库搜索hightlight,然后就会得到结果了。如果放到自己的网站上,你就得考虑相对路径、绝对路径等等,这里不做讨论(有问题请留言)。下面会以path/to/highlight.js来统一指代。

题外话:关于HTTPS和HTTP的引用

有的网站既有HTTP访问又有HTTPS访问,这时网页中的资源引用就会出现问题。如果从HTTPS页面引用HTTP的资源就会被浏览器拒绝。

这时可以使用一种无协议的引用方式,当然前提是被引用资源要两种都能访问。比如:

<script src="//example.com/code.js"></script>

这样在HTTP页面上,浏览器就会引用http://example.com/code.js,在HTTPS页面上就会引用https://example.com/code.js。对于CSS也可以使用这种方式引用。

最简单的方法:让highlight自动处理

这种方法是让highlight自动处理相关的代码块,只要在主题脚部文件footer.php(视主题而定,一般是这个文件)的</body>前加入如下代码:

<script src="path/to/highlight.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

第一行是引用highlight.js的文件,第二行是执行初始化。

highlight.js会自动寻找结构为<pre><code>...</code></pre>的代码块,并自动判断语言。也可以通过<code>元素的类来指定语言或者不要高亮。支持的语言名称和别名可以在这里查到(右边那一列就是)。语言前也可以加上language-或者lang-。不要高亮则设置为nohighlight。比如:

<pre><code class="php">PHP代码</code></pre>
<pre><code class="lang-html">HTML代码</code></pre>
<pre><code class="language-js">JavaScript代码</code></pre>
<pro><code class="nohighlight">不要高亮</code></pre>

如果装过之前介绍的Markdown插件的,还可以更方便。可以用三个或更多`来代表代码块,并在后面加上直接加上类名。比如:

```php
PHP代码
```

```language-html
HTML代码
```

```lang-js
JS代码
```

```nohighlight
不要高亮
```

高级方法:无法保证代码块的结构时,或是代码块动态生成时

上面的方法要求代码块的结构是<pre><code>...</code></pre>这样的,如果不能保证这样的结构我们就需要额外的处理了。另外,如果代码块是动态加载的,也需要额外处理。

加载JS的代码是一样的,只不过不用初始化了:

<script src="path/to/highlight.js"></script>

我们要把元素传递给highlight.js的hljs.highlightBlock接口处理,这个接口接受一个参数,就是需要处理的DOM元素。和上面类似,我们可以用类属性来指定语言种类。

如果代码不是在pre或者设置过CSS属性white-space保留换行的元素之中的话我们需要用<br>元素来换行。这种情况我们要先设置一下让highlight使用<br>来换行(以下为JavaScript代码,需要放到<script>元素中,或者放到外部文件中通过<script>元素的src属性引用):

hljs.configure({useBR: true});

然后我们就要选择所有的包含代码的元素然后传递给highlight.js处理。比如我们的代码块结构是<div><code>...</code></div>这样的,那么就要用如下代码:

code_elms = document.querySelectorAll('div code');
// code_elms = Array.prototype.slice.call(code_elms);
for(var i=0; i <= code_elms.length; i++) {
    code_elm = code_elms[i];
    hljs.highlightBlock(code_elm);
}

document.querySelectorAll是受到主流浏览器的支持的通过类CSS选择器来选择元素的接口,和jQuery的选择器很类似,返回的是一个类数组对象。但实际上并不是数组,其中的成员会随着DOM的变化而变化,所以如果页面上的元素会动态发生变化,最好取消第二行的注释以将其转换为真正的数组。

如果对ECMAScript6感兴趣,那么这个还可以更简化

Array.from( document.querySelectorAll('div code') ).forEach( code => hljs.highlightBlock(code) );

这里用到了ES6的Array.from来转换为数组元素,然后用forEach方法来遍历每个成员。然后用箭头函数来定义了forEach的回调函数。鉴于浏览器支持的原因,不建议这样使用。

还可以用jQuery,这个在highlight.js的官网上就给出例子了:

$('div.code').each(function(i, block) {
  hljs.highlightBlock(block);
});

高级方法:使用Web Workers

什么是Web Workers,简单点说就是在后台执行JavaScript代码的。使用Web Worker可以避免在执行耗时代码时当前页面假死。highlight.js在处理量非常大的代码时有可能会假死,所以放到Web Workers中执行是很好的解决方法。

使用Web Workers就不需要在页面中引用highlight.js,我们将在Worker中引用。

首先我们需要修改header.php文件,在</head>之前加入:

<script>
    addEventListener('load', function() {
        var codes = document.querySelectorAll('pre code');
        if(codes.length > 0) {
            for(var i=0; i<codes.length;i++) {
                var code=codes[i];
                var msg = {
                    lang: Array.prototype.slice.call(code.classList),
                    content: code.textContent
                };
                var worker = new Worker('<?php echo esc_url( get_template_directory_uri() ) ?>/js/highlight-worker.js');
                worker.onmessage = function(c) {
                    return function(event) { c.innerHTML = event.data; }
                }(code);
                worker.postMessage(msg);
            }
        }
    });
</script>

第10行中因为我们引用了外部文件,所以用<?php echo esc_url( get_template_directory_uri() ) ?>输出了主题目录的网络路径。这段代码的意思就是,在窗口的load事件上绑定了行为:选择所有符合pre code选择器的元素,然后遍历每一个元素,把元素的内容和类属性通过msg对象传递给Worker,然后把Worker返回的数据替换元素的内容。Worker是使用一个叫做highlight-worker.js的文件建立的。

根据上面的代码可以看出,highlight-worker.js我放到了主题目录下的js/highlight-worker.js,其内容如下:

onmessage = function(event) {
    importScripts('path/to/highlight.js');
    var result = event.data.lang.length==0 ? self.hljs.highlightAuto(event.data.content) : self.hljs.highlightAuto(event.data.content, event.data.lang);
    postMessage(result.value);
}

这段代码是在Worker的message事件上绑定行为:先导入highlight.js文件,然后根据传入的数据(也就是前面的msg对象)的lang属性的有无,调用hljs.highlightAuto接口来处理传入的数据的content属性(也就是代码内容)。然后把处理结果返回。

这样就行了。

最后

如果想查看highlight.js的完整API列表,可以到这里。还可以研究一下自己修改样式。虽然这个方案并不是最佳的,比如效率问题,比如高亮还是不如一些代码编辑器,比如不会加行号等等,但对于代码的可读性已经是很不错的提升了,更复杂的功能以后再研究吧。

使用Atom替代Sublime Text

Sublime Text虽好,但是在Ubuntu下有个蛋疼的问题——不支持中文输入,只能从别处复制进来,或者用一个更蛋疼的InputHelper插件(实际上就是弹出个输入框让你输入,然后在自动完成复制粘贴的动作)。

AtomGitHub推出的一款编辑器,使用了GitHub自家的Electron框架(一个使用Web技术——也就是HTML、JavaScript、CSS——来开发桌面应用的框架,微软的Visual Studio Code也是使用这个框架的)。才推出的时候我就试用了一下,发现非常不好用。但是随着这两年的不断打磨,Atom已经渐入佳境了。大量的扩展相比Sublime Text也丝毫不落下风。

想尝试的可以上atom.io下载。

当然,为了更好用,下面几个扩展是需要装以下的

  1. convert-to-utf8
    虽然现在UTF-8还无疑问是最佳选择了,但是不可避免的会有很多非UTF-8编码的文件。这个扩展可以识别大部分编码,并自动转换,可以避免乱码。
  2. simplified-chinese-menu
    如果不想看英文菜单,可以装这个扩展,虽然还是有少部分没有汉化。
  3. minimap
    一个来自Sublime Text的功能,在文档右边显示一个缩略图,可以快速定位,比较实用。

其他的linter、snippets类的扩展,有需要的肯定不会陌生,就不多说了。

简单粗暴禁用root

Ubuntu桌面版的root默认是禁用的,你无法登录。但是服务器版不行,服务器版默认的用户就是root

在建立好自己的用户之后,root基本就没啥用了(可以sudo)除了一些远程传输文件需要root之外,登录是基本不会用的了。

于是开始找禁止root用密码登录但是可以用密钥登录的方法。显示在这里找到利用PAM来禁止root用密码登录的方法,虽然行之有效,但是和Ubuntu桌面版的效果还差点意思。

不过,Ubuntu自己的社区文档里有一篇介绍root和sudo的文章里面就提供了一种简单粗暴的方法(见4.3.2节Re-disabling your root account):

sudo passwd -dl root

查看passwd命令的帮助就可以知道,参数-d是删除密码,-l是锁定密码。所以root就这样被干掉了。

经过测试,用密钥并不受影响,但是密码已经没法用了。当然,破解的方法也很简单,就是用passwd命令给root重新设置密码。

折腾香橙派

这里说的香橙派并不是食物,而是树莓派的国产模仿版。都属于ARM平台开发版,当然,装上Linux之后就是一台小电脑了,可以发挥的地方很多,就看你怎么利用了。

我是由于Xbox One网络实在太糟糕,而它又不支持VPN,所以必须要通过代理网关来转发,我又不想买新的路由器,所以便宜小巧的香橙派就成了我的首选了。

香橙派比树莓派价格更便宜,配置也更给力,就是不知道耐不耐用了。官方网站上都有详细介绍,我购买的是Orange Pi PC这一款,使用后发现系统在Micro SD卡上还是太慢(即使是UHS-I,当然,很可能香橙派根本不支持UHS总线,只能用更慢的总线模式读取),所以如果对速度有要求的场合(比如说做电视盒子)还是买Orange Pi PlusOrange Pi 2这些板载eMMC Flash ROM的版本。

没玩过树莓派,不知道树莓派的系统支持怎么样,但是香橙派的系统是非常丰富的,官网上可以看到AndroidLubuntuUbuntu with MATEFedoraDebianRaspbianOpenSUSEKali LinuxArch Linux等等系统,进论坛还能找到GentooOpenWRT等系统,总有一款适合你。最终我在中文论坛上找了一款Ubuntu 15.10 with MATEMATE是一个轻量级的桌面,自然没有Ubuntu官配的Unity那样的体验了,不过我要的是丰富软件包,并不用桌面。

然而在一切开始之前,却遇到了一个问题,居然不能用USB OTG那个接口供电,只好又重新买了变压器,这才用上。

下面我的操作都是在Ubuntu上进行的,Windows下的话工具有所不同,在官网和论坛上都有说了,就不另外说了。

系统安装

在树莓派官网和论坛上都有详细的步骤,这里就不细说了。就是把下载得到的系统包先解压出来,得到类似ubuntu_wily3.4.39_CN.img这样的磁盘镜像文件,然后写入到SD卡上。注意Ubuntu会自动挂载移动磁盘,所以必须先卸载(不能直接在文件管理器里卸载,这样会直接把设备都卸载掉了,必须用命令,这里我的SD卡是/dev/sdc):

sudo umount /dev/sdc*

然后就是写入系统:

sudo dd if=ubuntu_wilu3.4.39_CN.img of=/dev/sdc oflag=direct bs=4M

如果4M的块无法启动,要改成1M重新写入。

然后就会在文件管理器里见到两个新的分区了,挂载BOOT分区,然后根据需要把对应的屏幕分辨率的文件重命名为script.binuImage是内核,因为我用的是Orange Pi PC所以不用改了,如果是Orange Pi Plus要把uImage-OPI-PLUS重命名为uImageOrange Pi 2也不用改)。我选择的是1080p60的分辨率:

mv script.bin.OPI-PC_1080p60 script.bin

如果无法启动,在另一个linux分区下的boot目录里还有更多的内核文件,根据需要移动到BOOT分区并重命名就行了。

启动并配置

插上HDMI、鼠标键盘、网线和电源,然后按下开机按钮,香橙派就这么启动了。进到桌面后,登录名和密码都是orangepi,后面可以自己改。我下的系统SSH服务器默认是启用的,所以也可以不登录桌面,直接SSH过去操作。

首先要重新设定分区的大小,否则就没法利用SD卡的所有空间了:

sudo fs_resize

重启之后就可以了。

配置本来没啥好说的,虽然桌面环境不同,但本质都是Ubuntu,所以算是大同小异。但还是有一样得说一说,就是软件源。国内的镜像基本都没有ARM的源,包括官方的中国镜像(实际是指向阿里云的镜像)。不过最终还是发现中国科技大学的镜像有ARM的源。参考电脑上的source.list,把/etc/apt/source.list改为如下内容:

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily main restricted
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily main restricted

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-updates main restricted
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-updates main restricted

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily universe
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily universe
deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-updates universe
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-updates universe

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily multiverse
deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-updates multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-updates multiverse

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-backports main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-backports main restricted universe multiverse

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-security main restricted
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-security main restricted
deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-security universe
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-security universe
deb http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-security multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ wily-security multiverse

deb http://archive.canonical.com/ubuntu wily partner
deb-src http://archive.canonical.com/ubuntu wily partner

然后就是照例的更新升级:

sudo apt-get update
sudo apt-get dist-upgrade

然后就是装各种需要的软件了,这个就不细说了。

编译安装shadowsocks-libev

这才是今日的重点,shadowsocks-libev是一个C语言版本的shadowsocks,依赖少功能强,比起原版的Python版本的更好用。

在它的README.md中已经详细介绍了安装方法了,这里就简单说一下(GitHub页面上显示的就是README.md格式化后的内容)。

工具和依赖

直接从软件源安装(在服务器上安装时发现还需要fakeroot这个包,不过我下载的这个系统已经有了):

sudo apt-get install git build-essential autoconf libtool libssl-dev gawk debhelper dh-systemd init-system-helpers pkg-config

下载源码

直接从GitHub仓库克隆就行了:

git clone https://github.com/shadowsocks/shadowsocks-libev.git

编译成软件包

shadowsocks-libev的开发者非常贴心,根本不用做复杂的配置工作,直接进入源码目录后一条命令就编译打包成deb了:

cd shadowsocks-libev
dpkg-buildpackage -us -uc -i

安装

deb打包在源码的上级目录,出来安装就行了:

cd ..
sudo dpkg -i shadowsocks-libev*.deb

配置shadowsocks-libev

安装好后默认启用的是服务器端ss-server,我们需要的是作为代理的客户端ss-local和作为网关的ss-redir。所以还需要一些工作。

配置文件

装好后应该在/etc/shadowsocks-libev/下有个config.json的配置文件,我们不管它,我们来分别建立用于ss-localss-redir的配置文件,分别命名为local.jsonredir.json

cd /etc/shadowsocks-libev
sudo touch local.json redir.json

然后根据需要修改如下内容保存到两个文件里:

{
  "server": "1.2.3.4",
  "server_port": 8388,
  "local_address": "0.0.0.0",
  "local_port": 1080,
  "password": "givemefive",
  "method": "rc4-md5"
}

修改说明:

  1. server:是服务器的IP地址,把1.2.3.4改成实际的值。
  2. server_port:是服务器监听的端口,把8388改成实际的值。
  3. local_address:这里可以不用改,0.0.0.0表示监听香橙派的所有IP,其他场合配置中没有这项是因为那些只需要监听本机的IP(也就是127.0.0.1),而我的香橙派的不是给它自己用的,是提供给局域网里的其他主机用的,所以要监听所有IP。redir.json中可以配置为127.0.0.1或者删除此项。
  4. local_port:这里是要监听的香橙派的端口,注意local.jsonredir.json要设置不同的端口以免冲突,我设置的分别是108023232
  5. password:服务器的密码。
  6. method:加密方法,根据需要修改。

配置服务

以下所有服务的配置都是基于systemd,只适用于Ubuntu 15.04以后版本(其他系统的支持情况请参见维基百科systemd)。

首先要停止默认的ss-server

sudo systemctl disable shadowsocks-libev

然后配置ss-local使用local.json

sudo systemctl enable shadowsocks-libev-local@local

同样地配置ss-redir

sudo systemctl enable shadowsocks-libev-redir@redir

@后面的字符就是配置文件的文件名.json前的部分。由此可见我们可以建立多个配置文件同时连接多个不同服务器,只要监听的本地端口不冲突就行了。这只有systemd才能做到,过去的System V就必须建立多个初始化脚本不同的才行。

当然别忘了启动服务:

sudo systemctl start shadowsocks-libev-local@local
sudo systemctl start shadowsocks-libev-redir@redir

设置转发

ss-local现在已经可以使用了,比如我监听的是1080的端口,我只要在Firefox中使用Autoproxy插件设置SOCKS5代理的为香橙派的IP和1080端口,然后订阅gfwlist就可以自动根据访问的网站使用或者不使用代理了。

但我的终极目标是给Xbox One用,所以还需要设置转发才行。这里就要用到Linux内置的iptables功能了(可能需要安装iptables软件包)。

感谢好心人,这里已经有了一份配置方法了。因为给Xbox One用不需要考虑过滤国内流量,所以直接参照最开始的部分配置就行了。

下面所有的命令都需要root权限,所以先切换为root:

sudo su

首先要新建一个链SHADOWSOCKS:

iptables -t nat -N SHADOWSOCKS

然后添加shadowsocks服务器的IP到不转发的规则里(将1.2.3.4修改为实际的值):

iptables -t nat -A SHADOWSOCKS -d 1.2.3.4 -j RETURN

然后添加为私有网络保留的IP地址段到不转发规则里:

iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN

转发到ss-redir监听的23232端口:

iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 23232
iptables -t nat -A SHADOWSOCKS -p udp -j REDIRECT --to-ports 23232

还要应用转发规则到PREROUTING链:

iptables -t nat -A PREROUTING -p tcp -j SHADOWSOCKS
iptables -t nat -A PREROUTING -p udp -j SHADOWSOCKS

因为iptables规则重启后会丢失,所以我们还要保存下来让他开机自动应用,不用麻烦,安装一个软件包就解决了:

sudo apt-get install iptables-persistent

安装过程中会询问是否保存当前规则,确定就行了。

规则保存在/etc/iptables/rule.v4(因为我们用的是IPv4的iptables)。如果以后修改了规则,需要手动保存,用下面的命令就行了。注意这条命令必须先切换到root(sudo su)之后才能运行,在命令前加sudo是不行的。

iptables-save > /etc/iptables/rule.v4

Xbox One以及其他设置

设置好了转发之后,我们只要在Xbox One或者其他需要转发的设备上修改网络设置就行了。先查看通过DHCP自动获取的网络参数,然后手动设置参数,保持其他参数不变,把网关修改成香橙派的IP就行了。

比如:

  • IP地址(IP address):192.168.11.36(不变)
  • 子网掩码(netmask):255.255.255.0(不变)
  • 网关(gateway):192.168.11.1(改成香橙派IP
  • 主要DNS(primary DNS):192.168.11.1(不变)
  • 次要DNS(secondary DNS):8.8.8.8(不变)

总结

由于shadowsocks的特性,连接是按需建立的,并不是VPNSSH tunnel那样需要持续保持连接的,所以可以让它一直运行而不用担心连接断开。

如果更进一步,还可以在香橙派上建立DNS服务和DHCP服务,代替无线路由器的功能,给不同的设备分配不同的网络参数(比如给电脑就分配路由器为网关,给Xbox One就分配香橙派为网关)。或者给香橙派插上无线网卡(2和Plus的话自带无线网卡),作为AP使用,让连上香橙派AP的使用转发,连上无线路由器的不转发(本来我是想这样搞的,无奈香橙派的内核不支持我的USB无线网卡)。玩法是多种多样的,就看你能不能想到了。

总之,妈妈再也不用担心我玩Xbox One无法下载更新了。