作者归档:admin

基于Docker的Nginx + PHP-FPM + Phalcon镜像

上一篇简单介绍了Docker的安装,运行,这一篇来构建一个基于Nginx和PHP-FPM的Phalcon镜像。在官方找了以下,单独的Nginx和PHP镜像更加流行,混合的反倒不是很受欢迎。其实官方并不提倡在一个容器里面运行多个服务,最好是一个容器只对外提供一个服务:一个容器启动时仅仅运行一个命令(其实里面可以包含多个),也方便部署扩展升级。多个服务之间可以使用Docker Compose来管理。但是Docker并不阻止创建包含多个服务器的镜像,为了方便,所以我们仍然可以自己构建。
构建镜像可以有好几种方式,比如基于Alpine Linuxphusion/baseimage-docker构建,或者基于Ubuntu,CentOS等构建,又或者在PHP,Nginx的基础镜像上构建。注意:如果要采用Ubuntu或者CentOS构建,可能需要一些额外的工作,以便保持镜像轻量稳定运行。
这里采用已有的richarvey/nginx-php-fpm来构建,它是一个基于Nginx官方镜像来构建的。
Github上拉取相关文件从Dockerfile构建:

$ sudo git clone https://github.com/ngineered/nginx-php-fpm
$ sudo docker build -t nginx-php-fpm:latest .

关于Dockerfile的相关解释,可以参考这里。当然也可以直接拉取镜像使用

$ sudo docker pull richarvey/nginx-php-fpm
# 也可以直接运行,会自动拉取
#$ sudo docker run -d richarvey/nginx-php-fpm

查看本地的镜像,连单独的nginx也来了:

[email protected]:~# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx-php-fpm       latest              4fc9ac9f2945        7 hours ago         228.5 MB
nginx               mainline-alpine     00bc1e841a8f        5 days ago          54.21 MB

这里的mainline-alpine是指基于Alpine Linux构建的。Alpine Linux是一个仅有5M大小的linux系统,采用apk add/search来安装/查找相应软件,有许多镜像都是基于它构建的,官方PHP镜像也有基于它构建的Docker镜像。
然后运行nginx-php-fpm:

[email protected]:~# docker run --name web -d richarvey/nginx-php-fpm

docker inspect命令用来查看容器的相关信息,查看下分配的IP:

[email protected]:~# docker inspect web | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
                    "IPAddress": "172.17.0.2",

然后在浏览器里面访问:http://172.17.0.2/就可以看到phpinfo的页面。到这里,Nginx + PHP的web容器就已经运行起来了,对应的Nginx和PHP进程可以在宿主机器上直接查看:

[email protected]:~# ps aux | grep nginx
root     18167  0.0  0.0  13696  4300 pts/6    S    01:47   0:00 nginx: master process /usr/sbin/nginx
systemd+ 18168  0.0  0.0  14144  1868 pts/6    S    01:47   0:00 nginx: worker process
systemd+ 18169  0.0  0.0  14144  1868 pts/6    S    01:47   0:00 nginx: worker process
systemd+ 18170  0.0  0.0  14144  1868 pts/6    S    01:47   0:00 nginx: worker process
systemd+ 18171  0.0  0.0  14144  1868 pts/6    S    01:47   0:00 nginx: worker process
systemd+ 18172  0.0  0.0  14144  1868 pts/6    S    01:47   0:00 nginx: worker process
root     18190  0.0  0.0  21292  1012 pts/18   S+   01:47   0:00 grep --color=auto nginx
[email protected]:~# ps aux | grep php-fpm
root     18166  0.0  0.2 167880 23364 pts/6    S    01:47   0:00 php-fpm: master process (/etc/php5/php-fpm.conf)
systemd+ 18173  0.0  0.1 167880  8620 pts/6    S    01:47   0:00 php-fpm: pool www
systemd+ 18174  0.0  0.1 167880  8620 pts/6    S    01:47   0:00 php-fpm: pool www
systemd+ 18175  0.0  0.1 167880  8620 pts/6    S    01:47   0:00 php-fpm: pool www
root     18192  0.0  0.0  21292  1032 pts/18   S+   01:47   0:00 grep --color=auto php-fpm

接下来要为这个容器添加Phalcon扩展。首先要进入容器里面,使用docker attach命令进入:

[email protected]:~# docker attach web



结果在这里等了半天进不去。。。。查看下当前镜像入口程序:

[email protected]:~# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
94176348a939        nginx-php-fpm       "/start.sh"         6 seconds ago       Up 5 seconds        80/tcp, 443/tcp     web

这个容器启动的时候运行的是start.sh这个脚本,这个脚本运行了Supervisor工具。于是重新启动容器,运行/bin/bash

#终止容器运行
[email protected]:~# docker stop web
web
#删除容器
[email protected]:~# docker rm web
web
#重新运行
[email protected]:~# docker run --name web -d -t -i nginx-php-fpm /bin/bash
ea21e10df702644a83ed75930b30c7764a786c4feabdf17cd868f86640137c47
[email protected]:~# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
ea21e10df702        nginx-php-fpm       "/bin/bash"         6 seconds ago       Up 5 seconds        80/tcp, 443/tcp     web
[email protected]:~# docker attach web
#进来了
bash-4.3# ls
bin       etc       lib       media     proc      run       srv       sys       usr
dev       home      linuxrc   mnt       root      sbin      start.sh  tmp       var

就可以进去了。
先安装编译相关工具包:

bash-4.3# apk --no-cache add php5-dev
bash-4.3# apk --no-cache add gcc
bash-4.3# apk --no-cache add make
bash-4.3# apk --no-cache add autoconf
bash-4.3# apk --no-cache add libc-dev
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/community/x86_64/APKINDEX.tar.gz
(1/2) Installing musl-dev (1.1.14-r12)
(2/2) Installing libc-dev (0.7-r0)
OK: 334 MiB in 106 packages

编译安装Phalcon:

bash-4.3# cd /home
bash-4.3# git clone --depth=1 git://github.com/phalcon/cphalcon.git
bash-4.3# cd cphalcon/build
bash-4.3# ./install
bash-4.3# ls -la /usr/lib/php5/modules/ | grep phalcon
-rwxr-xr-x    1 root     root       5045264 Sep 28 17:34 phalcon.so

更改PHP扩展的配置:

bash-4.3# cd /etc/php5/conf.d/
bash-4.3# vi phalcon.ini
#添加以下内容
#extension=phalcon.so

#检查扩展是否加载成功
bash-4.3# php -i | grep phalcon
/etc/php5/conf.d/phalcon.ini,
phalcon
phalcon => enabled
phalcon.db.escape_identifiers => On => On
phalcon.db.force_casting => Off => Off
phalcon.orm.cast_on_hydrate => Off => Off
phalcon.orm.column_renaming => On => On
phalcon.orm.enable_implicit_joins => On => On
phalcon.orm.enable_literals => On => On
phalcon.orm.events => On => On
phalcon.orm.exception_on_failed_save => Off => Off
phalcon.orm.ignore_unknown_columns => Off => Off
phalcon.orm.late_state_binding => Off => Off
phalcon.orm.not_null_validations => On => On
phalcon.orm.virtual_foreign_keys => On => On
OLDPWD => /home/cphalcon/build
_SERVER["OLDPWD"] => /home/cphalcon/build
_ENV["OLDPWD"] => /home/cphalcon/build

加载成功了,需要保持本次镜像变更。首先退出容器:

bash-4.3# cd /home
#删除各种不必要的东西,比如gcc
bash-4.3# rm -rf cphalcon/
bash-4.3# exit
exit

然后查看版本并提交变更:

[email protected]:~# docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
ea21e10df702        nginx-php-fpm       "/bin/bash"         31 minutes ago      Exited (0) 6 seconds ago                       web
[email protected]:~# docker commit ea2 nginx-php-fpm:phalcon
sha256:bb388df328ecc33fac02dba69759d5c992a145f650a0e5b20ca29a4b122fa933

docker commit命令可以用来提交变更,ea2是container id的前三位,也可以写全;然后跟的是要提交的镜像。这里提交到phalcon这个标签下,以便与原来的区分开。查看所有镜像,发现有两个不同的标签:

[email protected]:~# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx-php-fpm       phalcon             bb388df328ec        11 seconds ago      364.4 MB
nginx-php-fpm       latest              4fc9ac9f2945        4 hours ago         228.5 MB

采用新镜像来运行,这里要讲程序运行入口改回/start.sh,以便能正常启动Nginx和PHP-FPM:

[email protected]:~# docker rm web
web
[email protected]:~# docker run --name web -d -t -i nginx-php-fpm:phalcon /start.sh
deecb19467cda2676b24248e3f55970a2481255c6022a80ffbf5087792ccb559
[email protected]:~# docker ps
CONTAINER ID        IMAGE                   COMMAND             CREATED             STATUS              PORTS               NAMES
deecb19467cd        nginx-php-fpm:phalcon   "/start.sh"         4 seconds ago       Up 3 seconds        80/tcp, 443/tcp     web

入口程序改变了,需要再提交一次变更:

[email protected]:~# docker stop web
web
[email protected]:~# docker ps -l
CONTAINER ID        IMAGE                   COMMAND             CREATED             STATUS                       PORTS               NAMES
c7600e62733d        nginx-php-fpm:phalcon   "/start.sh"         34 seconds ago      Exited (137) 8 seconds ago                       web
[email protected]:~# docker commit c76 nginx-php-fpm:phalcon
sha256:1c97ee169a551dd8441f42b40beafd102c71f3e887e2317dc11ce0ef136ceaf0

运行最终的镜像:

[email protected]:~# docker rm web
web
[email protected]:~# docker run --name web -d -t -i nginx-php-fpm:phalcon
cb5b0c9e55913a538539e46c53ac7905b21def84a05eb00ef81c4b500853576c
[email protected]:~# docker ps
CONTAINER ID        IMAGE                   COMMAND             CREATED             STATUS              PORTS               NAMES
cb5b0c9e5591        nginx-php-fpm:phalcon   "/start.sh"         4 seconds ago       Up 3 seconds        80/tcp, 443/tcp     web

访问http://172.17.0.2/,便可以在页面找到phalcon扩展。
通常我们会将程序和数据分开,挂载外部文件目录到容器里面去:

[email protected]:~# docker stop web
web
[email protected]:~# docker rm web
web
[email protected]:~# docker run --name web -d -t -i -v /home/docker/nginx-php-fpm/src:/var/www/html/ nginx-php-fpm:phalcon
ffd64793fe8e7a2a95b68f514e221b7ec3b6cadfe668c016f55a7bb6d48bc702

-v参数可以用来挂载目录或者文件,可以又多个-v参数。
刚才容器里面做的那些已经添加到Dockerfile里面去,你直接使用它来构建。
至此Nginx + PHP-FPM + Phalcon镜像构建完成,介绍绍了如何进入容器,提交变更,网络访问和文件挂载。

参考链接:
A minimal Ubuntu base image modified for Docker-friendliness
eboraas/phalcon
基于Docker的PHP开发环境
Docker for PHP Developers
Docker在PHP项目开发环境中的应用
使用 Supervisor 来管理进程
PHP C扩展框架Phalcon
Alpine Linux,一个只有5M的Docker镜像

Ubuntu 16.04 上安装Docker

Docker是一个轻量级的虚拟化容器技术,既可以为不同应用程序提供了隔离空间(内存,资源,网络),又可以将应用程序与环境一起打包,发布到其他机器上,避免环境的差异,这点像Vagrant。 但是,它又与传统的虚拟机不容(如VirtualBox),它的应用程序仍然是跑在宿主机上面的,并且镜像里面通常仅包含所必须的组件而非完整的系统,因此拥有极高的性能。这又有点像Python的Virtual Environment,不过Docker更加强大,本质是是个Linux,可以打包的不同语言的应用程序。
Docker要求linux内核版本大于3.10,可以在通过以下命令查看是否支持

$ uname -r
4.4.0-38-generic

需要先更新以下apt仓库

$ sudo apt-get update
$ sudo apt-get install apt-transport-https ca-certificates
$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
[/sell]
检查以下是否已经创建Docker软件仓库文件,如果没有需要手动创建,并添加以下内容

[email protected]:~# cat /etc/apt/sources.list.d/docker.list
deb https://apt.dockerproject.org/repo ubuntu-xenial main
[/sell]
确认是否配置成功

[email protected]:~# sudo apt-get update
[email protected]:~# apt-cache policy docker-engine
docker-engine:
  Installed: 1.12.1-0~xenial
  Candidate: 1.12.1-0~xenial
  Version table:
 *** 1.12.1-0~xenial 500
        500 https://apt.dockerproject.org/repo ubuntu-xenial/main amd64 Packages
        100 /var/lib/dpkg/status
     1.12.0-0~xenial 500
        500 https://apt.dockerproject.org/repo ubuntu-xenial/main amd64 Packages
     1.11.2-0~xenial 500
        500 https://apt.dockerproject.org/repo ubuntu-xenial/main amd64 Packages
     1.11.1-0~xenial 500
        500 https://apt.dockerproject.org/repo ubuntu-xenial/main amd64 Packages
     1.11.0-0~xenial 500
        500 https://apt.dockerproject.org/repo ubuntu-xenial/main amd64 Packages

Docker容器建立在Auf文件系统之上,以便支持将不同的目录挂载到同一虚拟文件系统下面,并设置不同的读写权限。需要安装linux-image-extra:

$ sudo apt-get install linux-image-extra-$(uname -r) linux-image-extra-virtual
[/sell]
现在可以开始安装Docker了:

$ sudo apt-get install docker-engine
$ sudo service docker start

试运行一下:

[email protected]:~# docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

如果你本地没有hello-world这个镜像,它是会自动去Docker官网上下载的。
查看最近运行过的容器,不加参数-l,是查看正在运行的容器

[email protected]:~# docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
1931765938dc        hello-world         "/hello"            4 minutes ago       Exited (0) 4 minutes ago                       suspicious_lichterman

运行镜像的时候也可以指定名称(而不是suspicious_lichterman)

[email protected]:~# docker run --name hello hello-world

Hello from Docker!
...

[email protected]:~# docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
e4773606e6cf        hello-world         "/hello"            7 seconds ago       Exited (0) 6 seconds ago                       hello

但是再次运行同样名称为hello的容器时会报错

[email protected]:~# docker run --name hello hello-world
docker: Error response from daemon: Conflict. The name "/hello" is already in use by container e4773606e6cf4222b786d5301ab38e4dddb90e11a0358f64d33109bf829f0b5a. You have to remove (or rename) that container to be able to reuse that name..
See 'docker run --help'.

这时候需要先把旧的给删掉,再运行就不会了

[email protected]:~# docker rm hello
hello

通过Docker stop停止运行指定容器。
查看本地的Docker镜像

[email protected]:~# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx-php-fpm       phalcon             1c97ee169a55        3 hours ago         364.5 MB
nginx-php-fpm       latest              4fc9ac9f2945        7 hours ago         228.5 MB
ubuntu              16.04               c73a085dc378        47 hours ago        127.1 MB
ubuntu              latest              c73a085dc378        47 hours ago        127.1 MB
nginx               mainline-alpine     00bc1e841a8f        5 days ago          54.21 MB
mysql               5.7                 4b3b6b994512        5 weeks ago         384.5 MB
hello-world         latest              c54a2cc56cbb        12 weeks ago        1.848 kB

这里先简单的介绍Docker的安装、运行,后续将介绍Docker命令行交互,网络,文件存储等。

参考链接:
Docker Overview
Docker:利用Linux容器实现可移植的应用部署
Installation
on Ubuntu

Docker:具备一致性的自动化软件部署
Docker — 从入门到实践
Docker简介与入门
剖析Docker文件系统:Aufs与Devicemapper
What is the runtime performance cost of a Docker container
Docker – The Linux Container
当流浪者(Vagrant)遇见码头工人(Docker): 实战

Centos 6.4 安装 Python 2.7

终于又开始学Python了,不过这次是在Centos 6.4 上面,也碰到了好多问题。Centos6.4
并不能通过yum安装Python 2.7,系统自带的yum等使用的都是Python2.6.6,将系统的Python软链接指向2.7版本会有各种问题,包括依赖库等等;或者只能创建新的可执行命令,如Python27。最终按照这篇文章的介绍成功安装了Python2.7。
首先安装相关的工具,要不然等下编译Python会报各种各样的错:

$sudo yum groupinstall "Development tools"
$sudo yum install zlib-devel
$sudo yum install bzip2-devel
$sudo yum install openssl-devel
$sudo yum install ncurses-devel
$sudo yum install sqlite-devel

然后下载Python并安装,注意这里是make altinstall而不是make install,参考这里

$ wget https://www.python.org/ftp/python/2.7.11/Python-2.7.11.tar.xz
$ tar xf Python-2.7.11.tar.xz
$ cd Python-2.7.11
$ ./configure --prefix=/usr/local
$ sudo make 
$ sudo make altinstall

检查一下是不是安装到了/usr/local/bin/python2.7下面去了,后面Python 2.7相关的库也将安装到这里

$ ls -ltr /usr/local/bin/python*

检查一下Python2.6是不是还在/usr/bin/下面

$ ls -ltr /usr/bin/python*

检查一下系统路径变量PATH,保证/usr/local/bin在/usr/bin之前,然后将Python的软链接指向2.7。

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
$ ln -s /usr/local/bin/python2.7 /usr/local/bin/python

这样子普通用户登录的时候就可以使用Python2.7了,而root用户(sudo)仍然使用Python2.6,yum等才不会出错。

$ which python
/usr/local/bin/python
$ python -V
Python 2.7.11

$ sudo -s
which python
#/usr/bin/python
python -V
#Python 2.6.6
exit

安装Python 2.7的包管理工具

$ wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
$ sudo /usr/local/bin/python2.7 ez_setup.py
$ sudo /usr/local/bin/easy_install-2.7 pip

检查一下是不是对了

$ which pip
/usr/local/bin/pip

$ which easy_install
/usr/local/bin/easy_install

在普通用户下面操作使用easy_install 就是安装到Python2.7的目录下面去了

$ easy_install requests

这时候会报错,因为相关的Python 2.7目录并没有写的权限

$ sudo chmod 664 /usr/local/bin

或者

sudo /usr/local/bin/easy_install-2.7 requests

注意:如果是在root用户或者sudo命令下,使用的仍然是Python 2.6,所以必须要指明使用那个版本的easy_install。
接下来就可以愉快的使用pip安装Python2.7相关的库了。

参考链接:
Installing python 2.7 on centos 6.3. Follow this sequence exactly for centos machine only
How To Set Up Python 2.7.6 and 3.3.3 on CentOS 6.4
How to install Python 2.7 and Python 3.3 on CentOS 6
Centos 6.4 python 2.6 升级到 2.7
CENTOS 6.5 安装 Python 2.7 总结
Difference in details between “make install” and “make altinstall”
Common Python Tools: Using virtualenv, Installing with Pip, and Managing Packages

分布式系统

好久以前写的了。在InfoQ上学习了分布式监控系统的设计与实现微信朋友圈技术之道,在架构设计的基础思想并不复杂。
首先是微信朋友圈技术之道,介绍了微信朋友圈团队的开发,团队仅4个人,因为他们站在巨人的肩膀上:
基础环境
全部才用C++开发
混合部署普通服务器
海量带宽

强大的基础设施
腾讯CDN,图片和视频上传、存储、分发
RPC框架
Key-Value(KV)存储系统
强大、方便、灵活的部署系统
强大的RPC框架
C++的框架
支持protobuf描述接口
支持进程/线程/协程多种模式:支持数十万的并发协程,方便编写和调试“同步”的网络调用和服务
从CGI到叶子服务器,全系统透明支持过载保护和QOS
为每次CGI调用自动生成全系统调用关系图
每个服务自动内建500多个监控项
高性能Key-value存储系统
三机一组,三机间支持数据一致性,容忍一台机器出错、自动切换
三机分布在一个数据中心的三个独立园区,在任意一个园区提供本区读写服务,容忍一个园区网络隔离

微信架构
接入层,维持长连接,避免重复连接开销;推送消息
逻辑层,注册/登录,消息,朋友圈,LBS;批处理,群聊,通知,好友推荐
存储代理层,账号,消息,关系链,朋友圈,群管理
存储层,Key-value存储
性能水平扩展(sharding)
相册,按照用户做水平扩展
发表,按照发表key做水平扩展
评论,按照评论key做水平扩展
时间线,按照用户做水平扩展

发表流程
上传图片到CDN,查找最近节点
调用朋友圈CGI
添加新发表
在相册增加新发表索引
添加时间线更新任务,批处理,给好友时间线添加新索引
返回发表成功

浏览流程
调用朋友圈CGI
拉取时间线
拉取新发表元数据
拉取CDN上的图片

数据单副本、索引写扩散、检查更新单读取
数据(发表)单副本:减少内存开销
索引写扩散、检查更新单读取:减少检查更新时的读扩散
写比较慢,失败可以重试;读不可等待

赞、评论与浏览
往赞、评论表插入发表索引和对应赞、评论

微信部署、接入与容灾
1地3园区对等部署,对等接入(电信、移动、联通);数据对等分布于同步;对等服务
容灾,任何两个园区都可以提供全量无损服务
数据中心分布:上海、深圳、香港、加拿大
通信优先专线服务;其次公网(加密)
各数据中心可独立提供服务
各数据中心通过idc queue异步同步写入(自动重试)

核心数据读写分析
相册与发表
本地idc写入,单向同步到其他idc
key的全局唯一性保证无冲突(根据idc预先生成)
时间线
本地idc只保存本地用户时间线的key,无需同步

跨洋同步的挑战和应对
挑战:大带宽延迟、丢包、乱序、低可靠性(专心中断)
应对:延迟、丢包、乱序达到一定程度自动从tcp切换到udp;专线中断自动切换到公网(AES加密)

评论、赞写冲突
因果一致性(不同数据中心保证key不重复,比如不同idc取模)

参考链接:
分布式监控系统的设计与实现
微信朋友圈技术之道