docker 搭建lnmp开发环境

docker学习心得

前言

耗时一周零三天,终于用docker搭建起自己的开发环境。
详细过程:请参考https://segmentfault.com/a/1190000011912956

未分类

下面说说我的心路历程:(从一个系统说起deepin)
Ubuntu16.04用的好好地,突然看见17.10发布了,界面还挺好看。果断升级为17.10。然后发现,界面看着漂亮,用的时候很难受,快捷键和16.04也不太一样,装的软件各种打不开。经过深思熟虑我就换成deepin,各种常见的软件都能装,比如QQ,微信,搜狗等等,美滋滋。来搭建一下环境lnmp,然后,桌面没了。查了一下资料,说deepin桌面版不能装。心想,弄个虚拟机吧,可以各种折腾,折腾坏了重建就行了。想到最近docker很火,果断入坑。

作者经历的各种阶段:(各位可以参考,避免浪费时间)

第一阶段:找教程
极客学院这个不错,各种概念讲的很清楚,建议新学者直接通读一遍再动手。还有,不要全看,如果只是想我一样想在本地搭建docker环境。我给你总结几点:

  1. 了解docker三个概念,镜像,容器,仓库
  2. 掌握这么几个命令和对应命令的常用参数:docker run/ps/rm/rmi/start/stop/exec (创建并运行容器/查看运行的容器/删除容器/删除镜像/启动容器/停止容器/容器外进入容器)
  3. 知道Dockerfile是什么?能做什么?我刚开始,想着只用centos官方镜像通过Dockerfile创建出自己的lnmp,想着在Dockerfile里面完成php,mysql,nginx的编译安装,后面发现还不是照着网络上的编译过程各种复制粘贴,还各种报错,自己不会解决,何必自己坑自己。
  4. 知道docker-compose是什么?能做什么?(自己看教程)

第二阶段:安装docker
安装我就不说了,着重说一下docker加速器阿里云加速器

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://4qqg0972.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

第三阶段:学习命令
第四阶段:找别人搭建的lnmp ( https://www.awaimai.com/2120.html )
第五阶段:看完之后满脑袋为什么?开始研究,最后质疑别人搭建的
第六阶段:还是用别人搭建好的(自己搭建不出来)
第七阶段:发现和自己的需求不一致,配置文件对不上
第八阶段:自己搭
第九阶段:觉得自己搭建的很好,在这写个教程,哈哈

总结:这些阶段是作者真实经历过的,可能还比这个多。作为过来人,我只想说,一定要只做一件事,不要过多的去研究,我们只是想搭建本地运行环境。

自己搭建docker lnmp 过程:

第一步:docker pull centos # 拉取官方centos镜像
第二步:docker run -it –name ‘lnmp-self’ centos /bin/bash # 创建并运行容器
第三步:使用lnmp一键安装包
第四步:docker commit -a ‘amor’ -m ‘lnmp’ b7515f3e6a82 lnmp:1.4 # 基于已有镜像的容器创建新镜像
第五步:创建Dockerfile进行微调,可能需要安装新软件等
第六步:通过docker-compose 进行管理,开放端口,挂载数据卷(挂载配置,项目目录)

这样做有以下几点好处:

  1. 避免编译安装,降低Dockerfile复杂度,避免学习使用大量的linux命令,避免使用自己不熟悉的镜像
  2. 易理解,易管理,易扩展(都是自己弄得嘛,哈哈)

下面附上自己的Dockerfile,docker-compose内容,请在第五步和第六步之后参考
Dockerfile

FROM lnmp:1.4
MAINTAINER amor ([email protected])
# ssh
RUN yum install openssh-server -y
RUN mkdir -p /data/website/
CMD ["lnmp", "start"]

docker-compose

lnmp:
  build: .
  ports:
    - "80:80"
    - "443:443"
    - "22:22"
  volumes:
    # nginx 配置文件夹
    - ./conf/nginx/:/usr/local/nginx/conf/
    # mysql 配置文件
    - ./conf/mysql/my.cnf:/etc/my.cnf:rw
    # php配置文件
    - ./conf/php/:/usr/local/php/etc/
    # 项目目录
    - /data/Nutcloud/Ubuntu/website/:/data/website/
  tty: true

目录结构(仅供参考)

.
├── conf
│   ├── mysql
│   ├── nginx
│   └── php
├── docker-compose.yml
└── Dockerfile

apt方式安装LNMP环境教程(ubuntu17.10|PHP7.1)

1. 简要说明

安装环境是阿里云ubuntu17.10,这个教程里我把域名都写成hostname.com, ip都写成192.168.1.1,你可以根据自己的需要更换。另外如果不是root账户的话,最好切换到root账户。

sudo su

2. 安装MYSQL 5.7

我们使用apt-get方式安装MySQL:

apt-get -y install mysql-server mysql-client

安装的时候会要求你输入MySQL的root密码,建议此时输入,比较方便。
输入以下命令,让MySQL更安全:

mysql_secure_installation

我们将会被问这些问题:

root@root:~# mysql_secure_installationSecuring the MySQL server deployment.Enter password for user root: 这里输入MySQL密码VALIDATE PASSWORD PLUGIN can be used to test passwordsand improve security. It checks the strength of passwordand allows the users to set only those passwords which aresecure enough. Would you like to setup VALIDATE PASSWORD plugin?Press y|Y for Yes, any other key for No: 想要让MySQL验证密码强度可以填Y,但个人建议N。otherwise.Using existing password for root.Change the password for root ? ((Press y|Y for Yes, any other key for No) : 建议NoBy default, a MySQL installation has an anonymous user,allowing anyone to log into MySQL without having to havea user account created for them. This is intended only fortesting, and to make the installation go a bit smoother.You should remove them before moving into a productionenvironment.Remove anonymous users? (Press y|Y for Yes, any other key for No) : 这个要填YSuccess.Normally, root should only be allowed to connect from'localhost'. This ensures that someone cannot guess atthe root password from the network.Disallow root login remotely? (Press y|Y for Yes, any other key for No) : 是否允许root远程登录?想要更安全选Y,但我建议选N,可以避免很多问题。Success.By default, MySQL comes with a database named 'test' thatanyone can access. This is also intended only for testing,and should be removed before moving into a productionenvironment.Remove test database and access to it? (Press y|Y for Yes, any other key for No) : 这个选Y,移除没用的test表。- Dropping test database...Success.- Removing privileges on test database...Success.Reloading the privilege tables will ensure that all changesmade so far will take effect immediately.Reload privilege tables now? (Press y|Y for Yes, any other key for No) : 选YSuccess.All done!

MySQL就装好了。

3. 安装Nginx

apt-get install nginx
apt-get install nginx

然后你打开你的IP地址或域名,就能看见nginx默认页面了。

未分类

nginx默认的路径在/var/www/html

4. 安装PHP7.1

我们搭建wordpress博客主要要用到php-fpm组件,我们apt-get它,ubuntu会自动安装必要的php程序。

apt-get -y install php7.1-fpm

5. 配置nginx

编辑nginx的默认站点配置文件

vim /etc/nginx/sites-available/default
server { listen 80 default_server; listen [::]:80 default_server; # SSL configuration # # listen 443 ssl default_server; # listen [::]:443 ssl default_server; # # Note: You should disable gzip for SSL traffic. # See: https://bugs.debian.org/773332 # # Read up on ssl_ciphers to ensure a secure configuration. # See: https://bugs.debian.org/765782 # # Self signed certs generated by the ssl-cert package # Don't use them in a production server! # # include snippets/snakeoil.conf;root /var/www/html;# Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html;server_name _;location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; }# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ .php$ { include snippets/fastcgi-php.conf; # With php7.0-cgi alone: fastcgi_pass 127.0.0.1:9000; # With php7.0-fpm: # fastcgi_pass unix:/run/php/php7.0-fpm.sock; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # location ~ /.ht { deny all; }}

另外需要注意,我们是要安装wordpress的,所以又一个index的地方(# Add index.php to the list),在index后面要增加index.php

PHP-FPM默认是通过socket连接的,我们要改成用TCP链接。

vim /etc/php/7.0/fpm/pool.d/www.conf

修改listen:

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

像上面那样修改后,重启nginx:

service nginx restart

然后打开:

vim /etc/php/7.1/fpm/php.ini

设置cgi.fix_pathinfo=0

; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting; of zero causes PHP to behave as before. Default is 1. You should fix your scripts; to use SCRIPT_FILENAME rather than PATH_TRANSLATED.; http://php.net/cgi.fix-pathinfocgi.fix_pathinfo=0

reload php-fpm:

service php7.0-fpm reload

新建一个文件:

vim /var/www/html/info.php

写入

<?php
phpinfo();
?>

然后我们可以打开浏览器输入域名,例如 192.168.1.1/info.php(你的显示应该是php7.1)

未分类

你可以看到PHP已经工作了,包括现在支持的一些模块。

6. 使PHP7.1支持MySQL

我们可以先看一下有哪些PHP7.1的模块:

apt-cache search php7.1

可以选一些你喜欢的模块安装(以下是我自己安装的):

apt-get -y install php7.1-mysql php7.1-gd php7.1-curl php7.1-intl php7.1-mcrypt

reload PHP-FPM:

service php7.1-fpm reload

刷新一下192.168.1.1/info.php,看一下自己的模块是否都安装好了。

这样就安装配置好了,接下来就可以在/var/www/html里上传wordpress文件了。

Ansible实战之Nginx高可用代理LNMP-wordpress

实验环境:前端使用Nginx做代理服务器,静态资源经由缓存服务器,连接后端web集群,动态资源直接连接后端集群,可由Nginx代理或Varnish实现动静分离,web服务端连接PHP服务,从而更好的提供动态资源,将动态资源数据保存在Mysql关系型数据库上,且Mysql数据库使用主从复制的技术。为验证整体架构的准确性,故将wordpress应用搭建在web服务端,来验证构架的有效性。为了防止单点故障,前端的Nginx代理还使用了keepqlive技术来实现高可用从而达到增加网络的安全性能的目的。

实验拓展:为了增加可用性,可将web集群分为动静两类web 集群组,从来实现动静分离的效果,Varnish集群来为静态资源提供缓存,从而使网络访问速度更快。前端代理也可使用HAProxy及LVS等技术来替代。后端Mysql数据库也可以增加数据备份的案例。

varnish的分离分离参考 http://www.cnblogs.com/JevonWei/p/7499417.html

网络拓扑图
未分类

主机环境

Ansible         172.16.252.82
Nginx_A 代理  172.16.252.207  
Nginx_B 代理  172.16.252.103
Keepalived_A    172.16.252.207  
Keepalived_B    172.16.252.103
Nginx+PHP_A     172.16.252.184  
Nginx+PHP_B     172.16.252.67
Mysql_Master    172.16.252.184  
Mysql_Slave     172.16.252.67

受添加限制
    Nginx_A和Keepalived_A为Nginx1.danran.com上
    Nginx_B和Keepalived_B为Nginx2.danran.com上
    Nginx+PHP_A和Mysql_Mstart在web1.danran.com主机上
    Nginx+PHP_B和Mysql_Slave在web2.danran.com主机上

实验准备

  • 各节点需保持时间同步
  • 确保主机名可以通信
  • 节点间使用秘钥连接

时间同步

[root@ansible ~]# ntpdate 172.16.0.1

节点主机名通信

编辑/etc/hosts主机解析文件或使用DNS解析亦可

[root@ansible ~]# vim /etc/hosts
172.16.252.184  web1.danran.com
172.16.252.67   web2.danran.com
172.16.252.82   ansible.danran.com
172.16.252.103  nginx2.danran.com
172.16.252.82   Ansible.danran.com
[root@ansible ~]# scp /etc/hosts nginx1.danran.com:/etc/
[root@ansible ~]# scp /etc/hosts nginx2.danran.com:/etc/
[root@ansible ~]# scp /etc/hosts web1.danran.com:/etc/
[root@ansible ~]# scp /etc/hosts web2.danran.com:/etc/

节点秘钥连接

[root@ansible ~]# ssh-keygen -t rsa -P ""
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
8e:bb:44:d7:25:df:1b:3e:9b:fa:22:15:b5:6b:e4:19 root@ansible
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|              .  |
|          . .. . |
|         . +..E  |
|      . S . .+o+ |
|     . +    ..=o |
|      o .  . .+  |
|     . .  . .  + |
|      o.   ..++  |
+-----------------+
[root@ansible ~]# ssh-copy-id -i .ssh/id_rsa.pub [email protected]
[root@ansible ~]# ssh-copy-id -i .ssh/id_rsa.pub [email protected]
[root@ansible ~]# ssh-copy-id -i .ssh/id_rsa.pub [email protected]
[root@ansible ~]# ssh-copy-id -i .ssh/id_rsa.pub [email protected]

Ansible配置文件

[root@ansible ~]# vim ansible.yml 
- hosts: websrvs
  remote_user: root
  roles:
  - nginx_web
- hosts: proxy
  remote_user: root
  roles:
  - nginx_proxy
- hosts: keepalive
  remote_user: root
  roles:
  - keepalive
- hosts: varnish
  remote_user: root
  roles:
  - varnish
- hosts: php-fpm
  remote_user: root
  roles:
  - php-fpm
- hosts: mysql
  remote_user: root
  roles:
  - mariadb
- hosts: websrvs
  remote_user: root
  roles:
  - wordpress 

Ansible主机清单文件

[root@ansible ~]# vim /etc/ansible/hosts 
[websrvs]
172.16.252.184
172.16.252.67

[proxy]
172.16.252.207
172.16.252.103

[keepalive]
172.16.252.207  start1=MASTER start2=BACKUP priority1=100 priority2=90
172.16.252.103  start1=BACKUP start2=MASTER priority1=90 priority2=100

[varnish]
172.16.252.207
172.16.252.103

[php-fpm]
172.16.252.184
172.16.252.67

[mysql]
172.16.252.184 serverid=1  log="log_bin = master-log"
172.16.252.67  serverid=2  log="relay-log = master-log"

定义角色

keepalive

[root@ansible ~]# cd /etc/ansible/roles/
[root@ansible ~]# mkdir keepalived/{files,templates,tasks,handlers,vars,meta,default} -pv

[root@ansible roles]# vim keepalive/tasks/main.yml 
- name: install keepalived
  yum: name=keepalived state=latest
- name: install conf
  template: src=keepalived.j2 dest=/etc/keepalived/keepalived.conf
  tags: conf
  notify: restart keepalived
- name: start keepalived
  service: name=keepalived state=started

[root@ansible roles]# vim keepalive/handlers/main.yml 
- name: restart keepalived
  service: name=keepalived state=restarted

[root@ansible roles]# vim keepalive/templates/keepalived.j2 
global_defs {
    notification_email {
        [email protected]
    }
    notification_email_from [email protected]
    smtp_server 127.0.0.1
    smtp_connect_timeout 30
    router_id keepaliveA
    vrrp_mcast_group4 224.103.5.5
}
vrrp_instance VI_A {
    state {{ start1 }}
    interface {{ ansible_default_ipv4.alias }}
    virtual_router_id 51
    priority {{ priority1 }}
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass qr8hQHuL
    }

    virtual_ipaddress {
    172.16.252.100/32
    }
}
vrrp_instance VI_B {
    state {{ start2 }}
    interface {{ ansible_default_ipv4.alias }}
    virtual_router_id 52
    priority {{ priority2 }}
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass eHTQgK0n
    }
    virtual_ipaddress {
       172.16.252.10/32
    }
}

nginx_web

[root@ansible ~]# cd /etc/ansible/roles/
[root@ansible ~]# mkdir nginx_web/{files,templates,tasks,handlers,vars,meta,default} -pv

[root@ansible roles]# vim nginx_web/tasks/main.yml 
- name: install nginx
  yum: name=nginx state=latest
  when: ansible_os_family == "RedHat"
- name: install conf
  template: src=vhost1.conf.j2 dest=/etc/nginx/conf.d/vhost1.conf
  tags: conf
  notify: restart nginx
- name: install site home directory
  file: path={{ ngxroot }} state=directory
- name: install index page
  copy: src=index.html dest={{ ngxroot }}/
- name: start nginx
  service: name=nginx state=started

[root@ansible roles]# vim nginx_web/handlers/main.yml 
- name: restart nginx
  service: name=nginx state=restarted

[root@ansible roles]# vim nginx_web/vars/main.yml 
ngxroot: /blog

[root@ansible roles]# vim nginx_web/templates/vhost1.conf.j2 
server {
    listen 8080;
    root "/blog/wordpress";
    index index.php index.html;
    location ~ .*.(php|php5)?$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        include fastcgi.conf;
    }
}

nginx_proxy

[root@ansible ~]# cd /etc/ansible/roles/
[root@ansible ~]# mkdir nginx_proxy/{files,templates,tasks,handlers,vars,meta,default} -pv

[root@ansible roles]# vim nginx_proxy/tasks/main.yml 
- name: install nginx
  yum: name=nginx state=latest
  when: ansible_os_family == "RedHat"
- name: install conf
  template: src=proxy.conf.j2 dest=/etc/nginx/conf.d/vhost1.conf
  tags: conf
  notify: restart nginx
- name: install nginx.conf
  copy: src=nginx.conf  dest=/etc/nginx/nginx.conf
- name: start nginx
  service: name=nginx state=started

[root@ansible roles]# vim nginx_proxy/handlers/main.yml 
- name: restart nginx
  service: name=nginx state=restarted

[root@ansible roles]# vim nginx_proxy/templates/proxy.conf.j2 
upstream websrv {
    server 172.16.252.207:6081;
    server 172.16.252.103:6081;
}

server {
    listen 80 default_server;
    server_name www.jevon.com;
    location / {
        proxy_pass http://websrv/;
        proxy_set_header Host $host;
        proxy_set_header X-Forward-For $remote_addr;
    }
}

[root@ansible roles]# vim nginx_proxy/files/nginx.conf  \取消nginx自带默认web主机,将新定义的web虚拟主机作为默认主机
server {
    listen       80 ;
}

varnish

[root@ansible ~]# cd /etc/ansible/roles/
[root@ansible ~]# mkdir varnish/{files,templates,tasks,handlers,vars,meta,default} -pv

[root@ansible roles]# vim varnish/tasks/main.yml 
- name: install varnish
  yum: name=varnish state=latest
- name: install conf
  copy: src=default.vcl dest=/etc/varnish/
  tags: varconf
  notify: restart varnish
- name: start varnish
  service: name=varnish state=started

[root@ansible roles]# vim varnish/handlers/main.yml 
- name: restart varnish
  service: name=varnish  state=restarted

[root@ansible roles]# vim varnish/files/default.vcl 
vcl 4.0;
import directors;
backend web1 {
.host = "172.16.252.184";
.port = "8080";
}
backend web2 {
    .host = "172.16.252.67";
    .port = "8080";
}
sub vcl_init {
    new websrv = directors.round_robin();
    websrv.add_backend(web1);
    websrv.add_backend(web2);
}

sub vcl_purge {
    return (synth(200,"Pruge Fishished"));
}
acl purges {
    "172.16.252.110";
    "127.0.0.0"/8;
}
sub vcl_recv {
    if (req.method == "PURGE") {
        if (client.ip !~ purges) {
            return(synth(403,"Purging not allowed for" + client.ip));
    }
    return(purge);
}
    if (req.url ~ "(?i).(jpg|jpeg|png|gif)$") {
        set req.backend_hint = websrv.backend();
     }else {
        set req.backend_hint = websrv.backend();
    }
    if (req.restarts == 0) {
        if (req.http.X-Forwarded-For) {
            set req.http.X-Forwarded-For = req.http.X-Forwarded-For + "," + client.ip;
        } else {
                set req.http.X-Forwarded-For = client.ip;
        }
    }
}
sub vcl_backend_response {
    unset beresp.http.X-Powered-By;
    if (bereq.url ~ ".(css|js|png|gif|jp(e?)g|swf|ico|txt|eot|svg|woff)") {
    unset beresp.http.cookie;
    set beresp.http.cache-control = "public, max-age=3600";
    }
    if ( beresp.status != 200 && beresp.status != 404 ) {
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
        return (deliver);
    }
    set beresp.ttl = 1h;
    set beresp.grace = 30s;
    return (deliver);
}
sub vcl_deliver {
    if (obj.hits>0) {
        set resp.http.X-Cache = "Hit Via " + server.ip;
    } else {
        set resp.http.X-Cache = "Miss from " + server.ip;
    }
}

php-fpm

[root@ansible ~]# cd /etc/ansible/roles/
[root@ansible ~]# mkdir php-fpm/{files,templates,tasks,handlers,vars,meta,default} -pv

[root@ansible roles]# vim php-fpm/tasks/main.yml 
- name: install {{ item }} package
  yum: name={{ item }} state=latest
  with_items:
  - php-fpm
  - php-mysql
- name: start php-fpm
  service: name=php-fpm  state=started  enabled=yes

mariadb

[root@ansible ~]# cd /etc/ansible/roles/
[root@ansible ~]# mkdir mariadb/{files,templates,tasks,handlers,vars,meta,default} -pv

[root@ansible roles]# vim mariadb/tasks/main.yml 
- name: install mariadb
  yum: name=mariadb-server   state=latest
- name: install conf
  template: src=server.j2 dest=/etc/my.cnf.d/server.cnf
  tags: conf
  notify: restart mariadb
- name: start mariadb
  service: name=mariadb  state=started  enabled=yes
- name: command master
  shell: /usr/bin/mysql -e "GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repluser'@'172.16.%.%' IDENTIFIED BY 'replpass';"
  shell: /usr/bin/mysql -e "flush privileges;"
  when: ansible_hostname == "web1"
- name: command slave
  shell: /usr/bin/mysql -e "CHANGE MASTER TO MASTER_HOST='172.16.252.184', MASTER_USER='repluser', MASTER_PASSWORD='replpass', MASTER_LOG_FILE='master-log.000003', MASTER_LOG_POS=245;"
  shell: /usr/bin/mysql -e "start slave;"
  when: ansible_hostname == "web2"
- name: wordpress command
  shell: /usr/bin/mysql -e "create database blog;"
  shell: /usr/bin/mysql -e "grant all on blog.* to 'blog'@'localhost' identified by 'blog';"

[root@ansible roles]# vim mariadb/handlers/main.yml 
- name: restart mariadb
  service: name=mariadb state=restarted

[root@ansible roles]# vim mariadb/templates/server.j2 
[mysqld]

server-id = {{ serverid }}
{{ log }}
innodb_file_per_table = ON
skip_name_resolve = ON

wordpress

[root@ansible ~]# cd /etc/ansible/roles/
[root@ansible ~]# mkdir wordpress/{files,templates,tasks,handlers,vars,meta,default} -pv

[root@ansible roles]# vim wordpress/tasks/main.yml 
- name: install unzip
  yum: name=unzip state=latest
- name: copy file
  copy: src=wordpress-4.8.1-zh_CN.zip dest=/blog
- name: command unzip
  command: /usr/bin/unzip -o  /blog/wordpress-4.8.1-zh_CN.zip -d /blog
- name: copy conf
  copy: src=wp-config.php dest=/blog/wordpress/
- name: mv conf
  command: mv /blog/wordpress/wp-config-sample.php /blog/wordpress/wp-config.php
  command: sed -ri 's/database_name_here/blog/' /blog/wordpress/wp-config.php
  command: sed -ri 's/username_here/blog/' /blog/wordpress/wp-config.php
  command: sed -ri 's/password_here/blog/' /blog/wordpress/wp-config.php

[root@ansible roles]# ls wordpress/files/
wordpress-4.8.1-zh_CN.zip

运行yml样本

[root@ansible ~]# ansible-playbook ansible.yml 
    .....
    .....
PLAY RECAP *********************************************************************
172.16.252.103             : ok=15   changed=4    unreachable=0    failed=0   
172.16.252.184             : ok=20   changed=3    unreachable=0    failed=0   
172.16.252.207             : ok=14   changed=2    unreachable=0    failed=0   
172.16.252.67              : ok=20   changed=3    unreachable=0    failed=0  

访问测试

未分类

利用docker-compose安装lnmp(Nginx mariadb php7.0 )

对于Docker来说,最大的便利就是能快速的搭建起一个个的容器,容器之间可以通过网络和文件来进行通信。

之前我已经将自己的博客使用docker搭建起来了,这里简单记录一下docker-compose文件内容。

我的博客的架构为lnmp,依赖的容器有:

  • Nginx(Port:80)

  • mariadb(Port:3306)

  • wordpress+php7.0-fpm(Port:9000)

  • phpmyadmin(Port:8009)

docker-compose.yml文件内容如下

nginx:
    image: nginx:latest
    ports:
        - '80:80'
    volumes:
        - ./nginx:/etc/nginx/conf.d
        - ./logs/nginx:/var/log/nginx
        - ./jialeens:/var/www/html
    links:
        - wordpress
    restart: always

mysql:
    image: mariadb
    ports:
        - '3306:3306'
    volumes:
        - ./db-data:/var/lib/mysql
    environment:
        - MYSQL_ROOT_PASSWORD=******
    restart: always

wordpress:
    image: wordpress:4.8.0-php7.0-fpm
    ports:
        - '9000:9000'
    volumes:
        - ./jialeens:/var/www/html
    environment:
        - WORDPRESS_DB_NAME=***
        - WORDPRESS_TABLE_PREFIX=wp_
        - WORDPRESS_DB_HOST=mysql
        - WORDPRESS_DB_PASSWORD=*****
    links:
        - mysql
    restart: always
phpmyadmin:
  image: phpmyadmin/phpmyadmin
  links:
    - mysql
  environment:
    PMA_HOST: mysql
    PMA_PORT: 3306
  ports:
    - '8009:80'

Nginx配置文件:

jialeens.com.conf

server {
    listen 80;
    server_name jialeens.com www.jialeens.com;

    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 128k;
    client_max_body_size 100m;
    root /var/www/html;
    index index.php;

    access_log /var/log/nginx/jialeens-access-http.log;
    error_log /var/log/nginx/jialeens-error-http.log;

    if ($host = 'jialeens.com') {
        return 301 http://www.jialeens.com$request_uri;
    }
    location ~* ^.+.(js|ico|gif|jpg|jpeg|png|html|htm)$ {
       log_not_found off;
       access_log off;
       expires 7d;
    }
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ .php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PHP_VALUE "upload_max_filesize=128M n post_max_size=128M";
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

因为流量不大,所以没做fastcgi的缓存,以后有空再弄吧。

Docker部署WordPress LNMP(Nginx PHP MySQL)环境实践

Docker基于LXC实现了把软件封装到一个完整的文件系统,可以在docker容器中运行所需的一切代码,运行环境,系统工具和系统库。由于docker使用独立于主机的文件系统,可以确保软件在不同的主机环境中仍然保持运行环境不变。docker与主机共用一个操作系统内核,使用docker容器具有轻量级的特点,能占用更少的内存快速启动容器。

下面我们学习使用docker来部署目前非常流行的博客系统wordpress的运行环境nginx php mysql(作者wordpress博客devops.webres.wang正是运行在docker容器中)。那么docker部署wordpress的运行环境与我们传统上直接在主机配置环境有什么区别?我们从开发和运维人员角度来说明。运维使用docker制作好wordpress容器,分发给开发人员,开发人员随即只需一个命令就可以部署好完全一样的运行环境,从此只需要关注代码本身,而不再需要把时间浪费在配置环境上。而同时,docker容器确保了开发环境与生产环境的一致性,极大减少由于开发环境与生产环境不一致出现的各种问题。而由于docker容器可以快速部署的特点,运维人员可以很轻松的对服务进行伸缩和扩展。

那么如何使用docker部署wordpress的运行环境?大概步骤是分别编写nginx php mysql的Dockerfile文件,从这些Dockerfile文件中生成各自的镜像,然后使用docker-compose工具来统一管理nginx php mysql。为了能只使用docker-compose.yml一个文件就能快速部署wordpress环境,我们把Dockerfile及环境的相关配置保存到阿里云的Kelude(git代码托管code.aliyun.com),然后使用阿里云的Docker镜像仓库(cr.console.aliyun.com)从Kelude拉取Dockerfile自动构建镜像。国外此类服务有hub.docker.com和github.com,使用阿里云的是因为可以免费设置私有git仓库和私有镜像,因为我们可能需要保存一些不便公开的私密信息(如网站证书,密码)。当然你也可以不使用这类服务,直接把镜像保存到本地环境中。下面开始一步步介绍。

准备工作

使用阿里云Kelude

到https://code.aliyun.com/创建一个项目,如Dockerfile。之后我们把wordpress环境的所有相关Dockerfile及配置文件放置到centosbz目录。

使用阿里云镜像仓库

阿里云docker镜像仓库地址为https://cr.console.aliyun.com,用来存放docker镜像,可以在本地push镜像上去,也可以从Kelude拉取Dockerfile自动构建镜像。我们先登录,然后新建一个namespace,如centos-bz,之后所有的nginx,php,mysql镜像将存放在这个namespace下。

安装docker-compose

需要在运行docker容器的主机上安装docker-compose,可以参照官方文档手动安装,也可以使用ezhttp的一键安装工具(推荐)安装。如:

wget webres.wang/ezhttp.zip
unzip ezhttp.zip
cd ezhttp-master
./start.sh

之后会弹出一个菜单,输入2选择Some Useful Tools,然后输入18选择安装docker和compose。

编写Dockerfile

clone以上在阿里云Kelude创建的Dockerfile镜像到本地,在此项目中创建webres.wang,然后在webres.wang目录分别创建mysql,nginx,php目录,用于存放它们各自Dockerfile及配置文件。
这里我们还约定以下目录:

  • /home/docker/nginx/logs/webres.wang:存放devops.webres.wang网站的日志
  • /home/docker/nginx/www/webres.wang: 存放devops.webres.wang网站的文件
  • /home/docker/php: 存放php-fpm的日志
  • /home/docker/mysql:mysql data目录
  • nginx Dockerfile

    在nginx目录创建Dockerfile文件,写入如下内容:

    # 从debian:jessie镜像基础上安装nginx
    FROM debian:jessie
    
    # 声明此Dockerfile维护者的邮箱,有什么问题可以发到此邮件寻问
    LABEL maintainer "[email protected]"
    
    # 定义软件版本及编译工具变量
    ENV NGINX_VERSION 1.10.3
    ENV OPENSSL_VERSION 1.0.2h
    ENV ZLIB_VERSION 1.2.11
    ENV PCRE_VERSION 8.40
    ENV CONCAT_VERSION 1.2.2
    ENV BUILD_TOOLS wget gcc make g++
    ENV SRC_DIR /opt/nginx
    
    # 切换到工作目录
    WORKDIR ${SRC_DIR}
    
    # 开始编译nginx,我们这里使用编译安装nginx而不是使用官方提供的nginx镜像是因为这里使用到了第三方的concat模块,只能编译了。
    # 把所有的安装命令都写在一个RUN指令中是因为这样可以减小镜像层数,缩减镜像大小。推荐使用反斜杠和&&把所有的安装命令放置到一行中。
    RUN apt-get update 
        && apt-get -y --no-install-recommends install ca-certificates ${BUILD_TOOLS} 
        && wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz  
        && wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz  
        && wget http://www.zlib.net/zlib-${ZLIB_VERSION}.tar.gz  
        && wget https://ftp.pcre.org/pub/pcre/pcre-${PCRE_VERSION}.tar.gz  
        && wget https://github.com/alibaba/nginx-http-concat/archive/${CONCAT_VERSION}.tar.gz -O nginx-http-concat-${CONCAT_VERSION}.tar.gz  
        && tar xf nginx-${NGINX_VERSION}.tar.gz  
        && tar xf openssl-${OPENSSL_VERSION}.tar.gz  
        && tar xf zlib-${ZLIB_VERSION}.tar.gz  
        && tar xf pcre-${PCRE_VERSION}.tar.gz  
        && tar xf nginx-http-concat-${CONCAT_VERSION}.tar.gz  
        && cd nginx-${NGINX_VERSION}  
        && ./configure --prefix=/usr/local/nginx --with-pcre=../pcre-${PCRE_VERSION} 
                    --with-zlib=../zlib-${ZLIB_VERSION} 
                    --with-http_ssl_module 
                    --with-openssl=../openssl-${OPENSSL_VERSION} 
                    --add-module=../nginx-http-concat-${CONCAT_VERSION}  
        && make -j$(nproc) 
        && make install 
        && rm -rf ${SRC_DIR} 
        && apt-get purge -y --auto-remove ${BUILD_TOOLS} 
        && rm -rf /var/lib/apt/lists/* 
    
    # 把构建上下文目录conf,即Dockerfile/webres.wang/nginx/conf目录下的文件复制到容器的/usr/local/nginx/conf目录。
    COPY conf/ /usr/local/nginx/conf/
    
    # 定义启动容器时运行的命令
    ENTRYPOINT ["/usr/local/nginx/sbin/nginx"]
    
    EXPOSE 80 443
    

    对于conf目录下的nginx配置文件,需要把日志,网站目录更改为以下约定的目录位置。

    php-fpm Dockerfile

    创建Dockerfile/webres.wang/php-fpm目录,在此目录下创建Dockerfile文件,内容如下:

    FROM debian:jessie
    LABEL maintainer "[email protected]"
    
    # 定义软件版本,编译工具,依赖等变量
    ENV PHP_VERSION 5.6.30
    ENV BUILD_TOOLS m4 
                   autoconf 
                   autoconf2.13 
                   openssl 
                   wget 
                   gcc 
                   make
    
    ENV BUILD_DEPS libcurl4-gnutls-dev 
                   libxml2-dev 
                   zlib1g-dev 
                   libpcre3-dev 
                   libjpeg-dev 
                   libpng12-dev 
                   libfreetype6-dev 
                   libmhash-dev 
                   libmcrypt-dev 
                   libssl-dev 
                   libtool
    
    ENV PHP_LOCATION /usr/local/php
    ENV BUILD_ARG   --prefix=${PHP_LOCATION} 
                    --with-config-file-path=${PHP_LOCATION}/etc 
                    --enable-fpm 
                    --enable-bcmath 
                    --with-pdo_sqlite 
                    --with-gettext 
                    --with-iconv 
                    --enable-ftp 
                    --with-sqlite3 
                    --enable-mbstring 
                    --enable-sockets 
                    --enable-zip 
                    --enable-soap 
                    --with-openssl 
                    --with-zlib 
                    --with-curl 
                    --with-gd 
                    --with-jpeg-dir 
                    --with-png-dir 
                    --with-freetype-dir 
                    --with-mcrypt 
                    --with-mhash 
                    --with-mysql=mysqlnd 
                    --with-mysqli=mysqlnd 
                    --with-pdo-mysql=mysqlnd 
                    --without-pear 
                    --with-libdir=lib64 
                    --enable-opcache 
                    --disable-cgi
    
    ENV SRC_DIR /opt/php
    
    WORKDIR ${SRC_DIR}
    
    # 开始编译安装php
    RUN apt-get update 
        && apt-get -y --no-install-recommends install ${BUILD_DEPS} ${BUILD_TOOLS} 
        && wget http://php.net/distributions/php-${PHP_VERSION}.tar.gz 
        && tar xf php-${PHP_VERSION}.tar.gz 
        && cd php-${PHP_VERSION} 
        && ln -s /usr/lib/x86_64-linux-gnu/libssl.so /usr/lib/libssl.so 
        && ln -s /usr/lib /usr/lib64 
        && ./configure ${BUILD_ARG} 
        && make -j$(nproc) 
        && make install 
        && cp php.ini-production ${PHP_LOCATION}/etc/php.ini 
        && cp ${PHP_LOCATION}/etc/php-fpm.conf.default ${PHP_LOCATION}/etc/php-fpm.conf 
        && rm -rf ${SRC_DIR} 
        && apt-get purge -y --auto-remove ${BUILD_TOOLS} 
        && rm -rf /var/lib/apt/lists/* 
    
    
    WORKDIR ${PHP_LOCATION}/etc/
    
    # 配置php-fpm,即使用sed工具编辑php-fpm.conf和php.ini文件,这里的php-fpm相关配置命令不与上面的编译命令合在一起来减小层数是因为
    # 配置文件可能会改动比较多,这样分开当配置文件更改时可以直接使用缓存跳过编译步骤,加快构建速度。
    RUN set_php_variable(){ 
            local key=$1; 
            local value=$2; 
            if grep -q -E "^$keys*=" php.ini;then 
                sed -i -r "s#^$keys*=.*#$key=$value#" php.ini; 
            else 
                sed -i -r "s#;s*$keys*=.*#$key=$value#" php.ini; 
            fi; 
            if ! grep -q -E "^$keys*=" php.ini;then 
                echo "$key=$value" >> php.ini; 
            fi; 
        } 
        && BASE_DIR=/home/docker/php 
        && set_php_variable disable_functions "dl,eval,assert,exec,popen,system,passthru,shell_exec,escapeshellarg,escapeshellcmd,proc_close,proc_open" 
        && set_php_variable expose_php Off 
        && set_php_variable error_log ${BASE_DIR}/php_errors.log 
        && set_php_variable request_order  "CGP" 
        && set_php_variable cgi.fix_pathinfo 0 
        && set_php_variable short_open_tag on 
        && set_php_variable date.timezone Asia/Chongqing 
        && sed -i 's/^user =.*/user = www-data/' php-fpm.conf 
        && sed -i 's/^group =.*/group = www-data/' php-fpm.conf 
        && sed -i "s#;slowlog = log/$pool.log.slow#slowlog = ${BASE_DIR}/$pool.log.slow#" php-fpm.conf 
        && sed -i 's/;request_slowlog_timeout = 0/request_slowlog_timeout = 5/' php-fpm.conf 
        && sed -i 's/^pm.max_children.*/pm.max_children =20/' php-fpm.conf 
        && sed -i 's/^pm.start_servers.*/pm.start_servers =5/' php-fpm.conf 
        && sed -i 's/^pm.min_spare_servers.*/pm.min_spare_servers =3/' php-fpm.conf 
        && sed -i 's/^pm.max_spare_servers.*/pm.max_spare_servers =8/' php-fpm.conf 
        && sed -i '/[global]/adaemonize =no' php-fpm.conf 
        && sed -i 's/^listen.*/listen =0.0.0.0:9000/' php-fpm.conf 
        && echo "[opcache]n 
                zend_extension=opcache.son 
                opcache.memory_consumption=128n 
                opcache.interned_strings_buffer=8n 
                opcache.max_accelerated_files=4000n 
                opcache.revalidate_freq=60n 
                opcache.fast_shutdown=1 n" >> php.ini
    
    ENTRYPOINT ["/usr/local/php/sbin/php-fpm"]
    
    EXPOSE 9000    
    

    mysql Dockerfile

    创建Dockerfile/webres.wang/mysql/Dockerfile文件,内容如下:

    FROM mysql:5.6
    LABEL maintainer "[email protected]"
    COPY my.cnf /etc/mysql/my.cnf
    

    这个Dockerfile非常简单,直接使用了官方的mysql镜像,唯一区别是我们使用自己定义的my.cnf配置文件。
    对于my.cnf配置文件,需要把日志,data目录指向/home/docker/mysql,一个my.cnf示例文件如下:

    # Generated by EZHTTP at 2016-02-03 01:05:29
    
    [mysql]
    
    # CLIENT #
    port                           = 3306
    socket                         = /home/docker/mysql/mysql.sock
    
    [mysqld]
    
    # GENERAL #
    port                           = 3306
    user                           = mysql
    default-storage-engine         = InnoDB
    socket                         = /home/docker/mysql/mysql.sock
    pid-file                       = /home/docker/mysql/mysql.pid
    skip-name-resolve
    
    # MyISAM #
    key-buffer-size                = 32M
    
    # INNODB #
    #innodb-flush-method            = O_DIRECT
    innodb-log-files-in-group      = 2
    innodb-log-file-size           = 64M
    innodb-flush-log-at-trx-commit = 2
    innodb-file-per-table          = 1
    innodb-buffer-pool-size        = 1G
    
    # CACHES AND LIMITS #
    tmp-table-size                 = 32M
    max-heap-table-size            = 32M
    query-cache-type               = 0
    query-cache-size               = 0
    max-connections                = 300
    thread-cache-size              = 50
    open-files-limit               = 1024
    table-definition-cache         = 100
    table-open-cache               = 400
    
    
    # SAFETY #
    max-allowed-packet             = 16M
    max-connect-errors             = 1000000
    
    # DATA STORAGE #
    datadir                        = /home/docker/mysql
    
    # LOGGING #
    log-error                      = /home/docker/mysql/mysql-error.log
    log-queries-not-using-indexes  = 1
    slow-query-log                 = 1
    slow-query-log-file            = /home/docker/mysql/mysql-slow.log
    
    # BINARY LOGGING #
    log-bin = /home/docker/mysql/mysql-bin
    server-id = 1
    expire-logs-days = 14
    sync-binlog = 1
    

    构建镜像

    把上一步创建的文件推送到阿里云的Kelude。然后我们登录阿里云的docker镜像仓库cr.console.aliyun.com。这里以设置自动构建nginx镜像为例,php和mysql镜像构建设置类似。
    1.点击左侧“镜像列表”,在右侧点击仓库镜像,如图:
    虚拟化技术
    2.在仓库镜像创建对话框中,说明如下:
    地域:选择离部署docker主机最近的位置,国内的话选择华东1或华东2。
    Namespace和仓库名称:这里选择centos-bz,nginx。
    设置代码源:我们这里选择阿里云code。
    构建设置:勾选代码变更时自动构建镜像,海外机器构建(因为国内主机apt-get安装软件时较慢),Dockerfile路径填/webres.wang/nginx
    完成后点击创建仓库按钮。
    如图:
    虚拟化技术
    3.回到镜像列表,找到nginx镜像,点击管理。
    4.左侧点击“构建”,右侧点击“立即构建”开始首次构建,之后我们更改Dockerfile及配置文件到Kelude之后就会自动构建了。
    5.查看日志,查看构建进程。
    然后继续完成php,mysql的镜像构建设置。

    启动环境

    为了方便统一管理nginx,php,mysql的启动,我们使用docker-compose工具。我们只需要编写一个docker-compose.yml文件,然后使用docker-compose工具就可以快速启动docker容器了。之后把docker-compose.yml传输到任意一台支持docker环境的主机中就可以快速配置wordpress的运行环境。

    docker-compose.yml

    把docker-compose.yml文件放置在/home/docker目录下。

    version: '3'
    # 定义三个服务nginx,php,mysql
    services:
        nginx:
            # 依赖php服务,意味着在启动nginx之前先启动php
            depends_on:
                - php
            # nginx镜像的路径
            image: registry.cn-hangzhou.aliyuncs.com/centos-bz/nginx
            # 容器的/home/docker/nginx目录挂载主机中的/home/docker/nginx目录,
            # 这样使nginx容器把网站文件和目录存放到主机目录中,持久化和方便管理
            volumes:
                - /home/docker/nginx:/home/docker/nginx
            # nginx意外退出时自动重启
            restart: always
    
            # 映射80和443端口
            ports:
                - "80:80"
                - "443:443"
    
            # 容器名称
            container_name: nginx    
        php:
            depends_on:
                - mysql
            image: registry.cn-hangzhou.aliyuncs.com/centos-bz/php-fpm
            restart: always
            volumes:
                - /home/docker/nginx/www:/home/docker/nginx/www
                - /home/docker/php:/home/docker/php
            container_name: php   
    
        mysql:
            image: registry.cn-hangzhou.aliyuncs.com/centos-bz/mysql
            volumes:
                - /home/docker/mysql:/home/docker/mysql
            restart: always
            # 设置MYSQL_ROOT_PASSWORD环境变量,这里是设置mysql的root密码。这里为root。
            environment:
                MYSQL_ROOT_PASSWORD: root
            container_name: mysql
    

    启动环境

    在/home/docker目录执行:

    docker-compose up
    

    查看nginx,php,mysql是否正常启动,如果正常,ctrl-c停止,再执行:

    docker-compose up -d
    

    这里compose命令就在后台启动了。
    执行docker ps查看容器运行状态。

    连接问题

    容器之间可以通过容器名称来连接,如nginx配置文件中连接php的代码fastcgi_pass php:9000,网站数据库配置文件使用mysql:3306。

    日常运维

    迁移

    比如A主机迁移到B主机。只需要三步。

  • 1.打包A主机的/home/docker目录,传输到B主机相同位置
  • 2.配置B主机docker环境
  • 3.在B主机的/home/docker目录下执行docker-compose up -d
  • 导出导入数据库

    把centos.sql.gz数据库文件导入到centos数据库:

    gunzip < centos.sql.gz | docker exec -i mysql mysql -uroot -proot centos
    

    把centos数据库导出到centos.sql.gz

    docker exec -i mysql mysqldump -uroot -proot centos | gzip > centos.sql.gz
    

    备份

    推荐使用ezhttp一键备份设置:

    wget webres.wang/ezhttp.zip
    unzip ezhttp.zip
    cd ezhttp-master
    ./start.sh
    

    之后会弹出一个菜单,输入2选择Some Useful Tools,然后输入14选择备份设置。需要注意的是在设置mysql使用mysqldump备份时,在提示输入mysql bin directory时,输入docker exec /usr/bin/。

    CentOS-5 yum安装nginx php53 mysql55 lnmp环境

    对于lnmp的使用,devops.webres.wang已经提供有lnmp安装包,但如果想快速配置环境,则推荐yum安装了。

    1、导入外部软件库

    centos-5 32位:

    1. rpm -Uvh http://dl.iuscommunity.org/pub/ius/stable/Redhat/5/i386/epel-release-5-4.noarch.rpm
    2. rpm -Uvh http://dl.iuscommunity.org/pub/ius/stable/Redhat/5/i386/ius-release-1.0-10.ius.el5.noarch.rpm
    3. rpm -Uvh http://nginx.org/packages/centos/5/noarch/RPMS/nginx-release-centos-5-0.el5.ngx.noarch.rpm

    centos-5 64位:

    1. rpm -Uvh http://dl.iuscommunity.org/pub/ius/stable/Redhat/5/x86_64/epel-release-5-4.noarch.rpm
    2. rpm -Uvh http://dl.iuscommunity.org/pub/ius/stable/Redhat/5/x86_64/ius-release-1.0-10.ius.el5.noarch.rpm
    3. rpm -Uvh http://nginx.org/packages/centos/5/noarch/RPMS/nginx-release-centos-5-0.el5.ngx.noarch.rpm

    2、yum安装lnmp

    1. yum install mysql55-server mysql55 php53u-fpm php53u-mysql php53u-gd php53u-mbstring php53u-mcrypt nginx

    3、配置lnmp

    1. vi /etc/nginx/conf.d/default.conf

    修改为:

    1. location ~ .php$ {
    2.         root           html;
    3.         fastcgi_pass   127.0.0.1:9000;
    4.         fastcgi_index  index.php;
    5.         fastcgi_param  SCRIPT_FILENAME  /usr/share/nginx/html$fastcgi_script_name;
    6.         include        fastcgi_params;
    7.     }

    4、启动lnmp

    1. chkconfig php-fpm on
    2. chkconfig nginx on
    3. chkconfig mysqld on
    4. service php-fpm start
    5. service nginx start
    6. service mysqld start

    双机热备+负载均衡线上方案(Heartbeat+DRBD+NFS+Keepalived+Lnmp)

    我们下面来实现一个架构,heartbeat+drbd+nfs实现mysql和网站数据的同步,keepalived实现nginx的高可用,而用nginx和dns轮询实现负载均衡。

    架构说明

    目录规划

    /usr/local/src/lnmp:用来存放源码工具等等
    /data:用来存放所有数据和NFS以及DRBD的挂载
    /data/shell:用来存放所有管理脚本
    /data/mysql:用来挂载DRBD的mysql资源,以供mysql存放数据库
    /data/wwwnfs:用来挂载DRBD生成的www资源,以供两个节点挂载到各个节点的/data/www目录,以供论坛等程序数据使用
    /data/www:用来挂载NFS资源,用来存放论坛(网站)等程序数据

    拓扑工作原理

    内网:
    1,DRBD网络存储创建出两个资源,一个mysql给mysql数据库同步用,一个www给web(论坛)数据NFS共享挂载用,虚拟出两个虚拟IP,一个是 192.168.1.100,用来连接数据库,一个是192.168.1.200,用来给节点挂载NFS
    注意:NFS底下挂载了三次:DRBD挂载一次,文件系统挂载一次,客户端挂载一次
    2,Heartbeat来实现DRBD的HA,同时虚拟出两个内网IP,并管理NFS,MySQL的启动和关闭

    外网:
    1,两个节点都用Nginx做均衡器,通过内网调度负载两个节点,实现内部均衡
    2,DNS配置双IP对应一个域名的方式来实现DNS轮询,实现外网均衡
    3,Keepalived使用双主(master)配置虚拟出两个虚拟IP:节点一 12.12.12.100和节点二 12.12.12.200,同时共外网访问,两个节点互为主从关系,当某个节点挂掉的时候,另外一个节点将同时是两个资源的master,同时拥有两个虚拟IP,实现资源转移。

    我们知道DNS的缺点就是生效慢,分配资源不合理,理论上有可能把所有的请求都发送给同一节点,导致均衡不合理导致所有资源不可用,这里我们由于有了NGINX内部负载,就不怕DNS轮询不均衡了,因为NGINX内部有严谨的调度方式,不管那台请求有多少,在内部都能实现理想的调度,这样就能把DNS负载均衡和NGINX完美结合,是硬件资源得到合理的利用,然后利用keepalive保证了每个节点的可靠性,几乎完美!
    拓扑图如下:
    高可用/集群

    架构实现

    LNMP架构配置

    配置LNMp架构需要注意两点:
    注意一:这里MYSQL都不要初始化,不要启动!后面有专门的配置的
    注意二:nginx所有端口都改成 8080,因为一会还要安装nginx来做均衡器并对外提供服务,所以不要用默认的80
    注意三、nginx和php-fpm运行的用户都是www。

    安装配置NFS

    1、安装NFS

    1. yum install nfs-utils nfs4-acl-tools portmap

    2、配置/etc/exports

    1. /data/wwwnfs 192.168.1.0/24(rw,,no_root_squash,sync,anonuid=502,anongid=502)

    注意:
    /data/wwwnfs:就是给两个节点挂载的目录,所有网站程序都放在这里,实现论坛程序等数据的共享(同步)
    anonuid=502,anongid=502:这个表示客户端上任何用户进入到挂载目录都以uid=502和gid=502身份,我这里这个代表的是www用户
    3、启动

    1. service portmap start
    2. service nfs start

    切忌,必须先启动portmap

    1. chkconfig  nfs off
    2. chkconfig  portmap on

    注意:portmap服务器必须常驻,且不收heartbeat管理;而nfs这必须要用heartbeat来管理他的启动和关闭,所以这里要关闭nfs开机自动启动

    同时要启动锁机制,因为同时有两个节点要使用同一份数据,所以需要有总裁,这个尤其是在NFS给mysql用的时候是必须要用的,对于论坛或网站,要看情况,如果存在对同一文件同时修改的时候必须要启动NFS锁机制,如果没有这种情况,那么建议不要启动,启动了会降低NFS的性能:

    1. /sbin/rpc.lockd
    2. echo "/sbin/rpc.lockd" >>/etc/rc.local

    4、开机自动挂载

    1. echo "sleep 20" >>/etc/rc.local
    2. echo "/bin/mount -t nfs 192.168.1.200:/data/wwwnfs /data/www" >>/etc/rc.local

    为什么为延迟20秒再挂载nfs?因为如果不等待立即挂载,会发现挂载不上,这是由于heartbeat启动用的vip还没设置好的原因。
    立即挂载:

    1. mount -a

    安装配置DRBD

    安装方法见:http://devops.webres.wang/2012/02/drbd-compile-install-deploy/

    配置文件

    DRBD有三种配置文件:
    /usr/local/drbd/etc/drbd.conf
    /usr/local/drbd/etc/drbd.d/global_common.conf
    /usr/local/drbd/etc/drbd.d/*.res
    1、drbd.conf

    1. include "drbd.d/global_common.conf";
    2. include "drbd.d/*.res";

    2、global_common.conf

    1. global {
    2.   usage-count yes;
    3. }
    4. common {
    5.   net {
    6.     protocol C;
    7.   }
    8. }

    3、mysql.res和www.res
    mysql.res:

    1. vi /usr/local/drbd/etc/drbd.d/mysql.res
    1. #资源组的名称
    2. resource mysql{
    3.  
    4. #定义主服务器资源
    5.         on node1{
    6. #建立块设备文件
    7.         device /dev/drbd1;
    8. #要用于复制的分区
    9.         disk /dev/sdb1;
    10. #定义侦听IP和端口
    11.         address 192.168.1.10:7788;
    12. #meta data信息存放的方式,这里为内部存储,即和真实数据放在一起存储
    13.         meta-disk internal;
    14.                    }
    15.  
    16. #定义备服务器资源
    17.         on node2{
    18.         device /dev/drbd1;
    19.         disk /dev/sdb1;
    20.         address 192.168.1.20:7788;
    21.         meta-disk internal;
    22.                    }
    23.  
    24.                 }

    www.res:

    1. vi /usr/local/drbd/etc/drbd.d/www.res
    1. #资源组的名称
    2. resource www{
    3.  
    4. #定义主服务器资源
    5.         on node2{
    6. #建立块设备文件
    7.         device /dev/drbd2;
    8. #要用于复制的分区
    9.         disk /dev/sdb2;
    10. #定义侦听IP和端口
    11.         address 192.168.1.20:7789;
    12. #meta data信息存放的方式,这里为内部存储,即和真实数据放在一起存储
    13.         meta-disk internal;
    14.                    }
    15.  
    16. #定义备服务器资源
    17.         on node1{
    18.         device /dev/drbd2;
    19.         disk /dev/sdb2;
    20.         address 192.168.1.10:7789;
    21.         meta-disk internal;
    22.                    }
    23.  
    24.                 }

    最后复制这些文件到node2。

    初始化DRBD资源

    1)在各个节点启用资源mysql和www

    1. modprobe drbd
    2. dd if=/dev/zero of=/dev/sdb1 bs=1M count=10
    3. dd if=/dev/zero of=/dev/sdb2 bs=1M count=10
    4. drbdadm create-md mysql
    5. drbdadm create-md www
    6. drbdadm up mysql
    7. drbdadm up www

    2),提升各个节点上的主
    在node1上:

    1. drbdadm primary –force mysql

    在node2上:

    1. drbdadm primary –force www

    3)格式化drbd块设备
    在node1上

    1. mkfs.ext3 /dev/drbd1

    在node2上

    1. mkfs.ext3 /dev/drbd2

    4)挂载分区
    在node1上

    1. mount /dev/drbd1 /data/mysql

    在node2上

    1. mount /dev/drbd2 /data/wwwnfs

    安装配置heartbeat

    1、安装heartbeat

    1. yum install heartbeat

    安装完后会自动建立用户hacluster和组haclient
    确保两个节点上hacluster用户的的UID和GID相同
    2、同步两台节点的时间

    1. rm -rf /etc/localtime
    2. cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
    3. yum install -y ntp
    4. ntpdate -d cn.pool.ntp.org

    3、配置/etc/ha.d/ha.cf

    1. debugfile /var/log/ha-debug                             #打开错误日志报告
    2. keepalive 2                                             #两秒检测一次心跳线连接
    3. deadtime 10                                             #10 秒测试不到主服务器心跳线为有问题出现
    4. warntime 6                                              #警告时间(最好在 2 ~ 10 之间)
    5. initdead 120                                            #初始化启动时 120 秒无连接视为正常,或指定heartbeat
    6.                                                         #在启动时,需要等待120秒才去启动任何资源。
    7.  
    8. udpport 694                                             #用 udp 的 694 端口连接
    9. ucast eth0 192.168.1.20                                #单播方式连接(主从都写对方的 ip 进行连接)
    10. node   node1                                           #声明主服(注意是主机名uname -n不是域名)
    11. node   node2                                           #声明备服(注意是主机名uname -n不是域名)
    12. auto_failback on                                        #自动切换(主服恢复后可自动切换回来)这个不要开启
    13. respawn hacluster /usr/lib/heartbeat/ipfail           #监控ipfail进程是否挂掉,如果挂掉就重启它

    4、/etc/ha.d/authkeys

    1. auth 1
    2. 1 crc

    5、/etc/ha.d/haresources

    1. node1 IPaddr::192.168.1.100/24/eth0 drbddisk::mysql Filesystem::/dev/drbd1::/data/mysql::ext3 mysqld portmap
    2. node2 IPaddr::192.168.1.200/24/eth0 drbddisk::www Filesystem::/dev/drbd2::/data/wwwnfs::ext3 portmap nfs

    6、创建nfs管理脚本

    1. vi /etc/ha.d/resource.d/nfs

    写入:

    1. #!/bin/bash
    2.  
    3. NFSD=/etc/rc.d/init.d/nfs
    4. NFSDPID=`/sbin/pidof nfsd`
    5. case $1 in
    6. start)
    7. $NFSD start;
    8. ;;
    9. stop)
    10. $NFSD stop;
    11.         if [ "$NFSDPID" != " " ];then
    12.                 for NFSPID in $NFSDPID
    13.                 do /bin/kill -9 $NFSPID;
    14.                 done
    15.         fi
    16. ;;
    17. *)
    18. echo "Syntax incorrect. You need one of {start|stop }"
    19. ;;
    20. esac

    先启动node1的heartbeat,再启动node2的heartbeat
    启动成功后,这里有几项需要检查
    node1:
    1、执行ip a,检查是否已经设置有虚拟ip 192.168.1.100
    2、执行cat /proc/drbd检查状态是否正常
    3、执行df -h查看/dev/drbd1是否已经挂载到/data/mysql
    4、执行service mysqld status查看mysql是否已经启动
    node2:
    1、执行ip a查看是否已经设置虚拟ip 192.168.1.200
    2、执行cat /proc/drbd检查状态是否正常
    3、执行df -h查看/dev/drbd2是否已经挂载到/data/wwwnfs和192.168.1.200:/data/wwwnfs是否已经挂载到/data/www

    nginx均衡器配置

    1. user  www;
    2. worker_processes  1;
    3.  
    4. error_log  /var/log/nginx/error.log warn;
    5. pid        /var/run/nginx.pid;
    6.  
    7.  
    8. events {
    9.     worker_connections  1024;
    10. }
    11.  
    12.  
    13. http {
    14.     include       /etc/nginx/mime.types;
    15.     default_type  application/octet-stream;
    16.  
    17.     log_format  main  ‘$remote_addr – $remote_user [$time_local] "$request" ‘
    18.                       ‘$status $body_bytes_sent "$http_referer" ‘
    19.                       ‘"$http_user_agent" "$http_x_forwarded_for"’;
    20.  
    21.     access_log  /var/log/nginx/access.log  main;
    22.  
    23.     sendfile        on;
    24.     #tcp_nopush     on;
    25.  
    26.     keepalive_timeout  65;
    27.  
    28.     #gzip  on;
    29.  upstream devops.webres.wang_server
    30.   {
    31.   server 192.168.1.10:8080 weight=3 max_fails=2 fail_timeout=30s;
    32.   server 192.168.1.20:8080 weight=9 max_fails=2 fail_timeout=30s;
    33.   }
    34.   server
    35.   {
    36.     listen       80;
    37.     server_name  devops.webres.wang;
    38.     location / {
    39.     root /data/www/devops.webres.wang;
    40.     index index.php index.htm index.html;
    41.     proxy_redirect off;
    42.     proxy_set_header Host $host;
    43.     proxy_set_header X-Real-IP $remote_addr;
    44.     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    45.     proxy_pass http://devops.webres.wang_server;
    46.     }
    47.     access_log  off;
    48.   }
    49. server
    50.   {
    51.     listen       8080;
    52.     server_name  devops.webres.wang;
    53.     index index.html index.htm index.php;
    54.     root  /data/www/devops.webres.wang;
    55.     #limit_conn   crawler  20;
    56.  location ~ .php$ {
    57.         root           /data/www/devops.webres.wang;
    58.         fastcgi_pass   127.0.0.1:9000;
    59.         fastcgi_index  index.php;
    60.         fastcgi_param  SCRIPT_FILENAME  /data/www/devops.webres.wang/$fastcgi_script_name;
    61.         include        fastcgi_params;
    62.     }
    63.     location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$
    64.     {
    65.       expires      30d;
    66.     }
    67.     location ~ .*.(js|css)?$
    68.     {
    69.       expires      1h;
    70.     }
    71.     access_log  off;
    72.       }
    73. }

    这里定义了两台用于负载均衡的机子,分别是192.168.1.10:8080和192.168.1.20:8080,通过proxy_pass http://devops.webres.wang_server代理循询转发到这两台机,达到负载均衡的作用。
    你可以建立index.php,里面写入:

    1. <?php
    2. echo $_SERVER[‘SERVER_ADDR’];
    3. ?>

    如果连续刷新几次,得到不同的IP,证明已经均衡负载到不同的服务器。

    Keepalived实现nginx和php的HA

    1、keepalived安装
    安装方法见:http://devops.webres.wang/2012/02/nginx-keepalived-high-availability/
    2、配置
    节点一node1配置如下:

    1. global_defs {
    2.    notification_email {
    3.      [email protected]
    4.    }
    5.    notification_email_from [email protected]
    6.    smtp_server 127.0.0.1
    7.    smtp_connect_timeout 30
    8.    router_id LVS_DEVEL
    9. }
    10. vrrp_instance VI_1 {
    11.     state MASTER        ############ 辅机为 BACKUP
    12.     interface eth0
    13.     virtual_router_id 100
    14.     mcast_src_ip 192.168.1.10  ########### 本机IP
    15.     priority 102                  ########### 权值要比 back 高
    16.     advert_int 1
    17.     authentication {
    18.         auth_type PASS
    19.         auth_pass 1111
    20.     }
    21.     virtual_ipaddress {
    22.        12.12.12.100
    23.     }
    24. }
    25.  
    26. vrrp_instance VI_1 {
    27.     state BACKUP
    28.     interface eth0
    29.     virtual_router_id 200
    30.     mcast_src_ip 192.168.1.101 ########### 本机IP
    31.     priority 101              ##########权值 要比 master 低。。
    32.     advert_int 1
    33.     authentication {
    34.         auth_type PASS
    35.         auth_pass 1111
    36.     }
    37.     virtual_ipaddress {
    38.        12.12.12.200
    39.     }
    40. }

    节点二配置:

    1. global_defs {
    2.    notification_email {
    3.      [email protected]
    4.    }
    5.    notification_email_from [email protected]
    6.    smtp_server 127.0.0.1
    7.    smtp_connect_timeout 30
    8.    router_id LVS_DEVEL
    9. }
    10. vrrp_instance VI_1 {
    11.     state BACKUP
    12.     interface eth0
    13.     virtual_router_id 100
    14.     mcast_src_ip 192.168.1.20 ########### 本机IP
    15.     priority 101              ##########权值 要比 master 低。。
    16.     advert_int 1
    17.     authentication {
    18.         auth_type PASS
    19.         auth_pass 1111
    20.     }
    21.     virtual_ipaddress {
    22.        12.12.12.100
    23.     }
    24. }
    25.  
    26. vrrp_instance VI_1 {
    27.     state MASTER        ############ 辅机为 BACKUP
    28.     interface eth0
    29.     virtual_router_id 200
    30.     mcast_src_ip 192.168.1.103  ########### 本机IP
    31.     priority 102                  ########### 权值要比 back 高
    32.     advert_int 1
    33.     authentication {
    34.         auth_type PASS
    35.         auth_pass 1111
    36.     }
    37.     virtual_ipaddress {
    38.        12.12.12.200
    39.     }
    40. }

    3、创建监控脚本
    node1监控脚本:

    1. vi /opt/check.sh
    1. #!/bin/bash
    2. while  :
    3. do
    4. mysqlcheck=`/usr/bin/mysqladmin -uroot ping 2>&1`
    5. mysqlcode=`echo $?`
    6. heartbeat=`ps -C heartbeat –no-header | wc -l`
    7. if [ $mysqlcode -ne 0 ] ;then
    8.  if [ $heartbeat-ne 0 ];then
    9. service heartbeat stop
    10. fi
    11. fi
    12. phpcheck=`ps -C php-fpm –no-header | wc -l`
    13. nginxcheck=`ps -C nginx –no-header | wc -l`
    14. keepalivedcheck=`ps -C keepalived –no-header | wc -l`
    15. if [ $nginxcheck -eq 0 ]|| [ $phpcheck -eq 0 ];then
    16.                 if [ $keepalivedcheck -ne 0 ];then
    17.                    killall -TERM keepalived
    18.                 else
    19.                    echo "keepalived is stoped"
    20.                 fi
    21.         else
    22.                 if [ $keepalivedcheck -eq 0 ];then
    23.                    /etc/init.d/keepalived start
    24.                 else
    25.                    echo "keepalived is running"
    26.                 fi
    27. fi
    28. sleep 5
    29. done

    node2监控脚本:

    1. #!/bin/bash
    2. while  :
    3. do
    4. phpcheck=`ps -C php-cgi –no-header | wc -l`
    5. nginxcheck=`ps -C nginx –no-header | wc -l`
    6. keepalivedcheck=`ps -C keepalived –no-header | wc -l`
    7. if [ $nginxcheck -eq 0 ]|| [ $phpcheck -eq 0 ];then
    8.                 if [ $keepalivedcheck -ne 0 ];then
    9.                    killall -TERM keepalived
    10.                 else
    11.                    echo "keepalived is stoped"
    12.                 fi
    13.         else
    14.                 if [ $keepalivedcheck -eq 0 ];then
    15.                    /etc/init.d/keepalived start
    16.                 else
    17.                    echo "keepalived is running"
    18.                 fi
    19. fi
    20. sleep 5
    21. done

    这个监控代码实现了mysql,nginx,php-fpm的HA。
    加上权限,并执行

    1. chmod +x /opt/check.sh
    2. nohup sh /opt/check.sh &

    设置开机启动:
    echo “nohup sh /opt/check.sh &” >> /etc/rc.local

    4、测试keepalived
    分别启动keepalived

    1. service keepalived start

    1)执行ip a检查node1和node2是否已经存在vip:12.12.12.100和12.12.12.200
    2)测试nginx和php-fpm的HA。在node1执行service nginx stop或者service php-fpm stop停止nginx或php-fpm,过几秒钟后你会发现node2已经接管了vip 12.12.12.100,并且使用vip 12.12.12.100或12.12.12.200浏览nginx网页你会发现网页显示的IP一直是192.168.1.20,表明keepalived已经成功接管node1的vip和nginx或php-fpm服务。
    3)测试mysql HA。在node1执行service mysqld stop停止mysql服务,几秒后在node2查看,发现node2已经接管vip 192.168.1.100,并且已经启动mysql服务。
    注意:在恢复mysql或nginx,php-fpm时,先停止监控脚本,要不heartbeat或keepalived还没实现接管又被停止。
    参考:http://bbs.ywlm.net/thread-965-1-1.html

    如何卸载lnmp

    1. killall nginx
    2. /etc/init.d/mysql stop
    3. killall mysqld
    4. /usr/local/php/sbin/php-fpm stop
    5. killall php-cgi
    6. rm -rf /usr/local/php
    7. rm -rf /usr/local/nginx
    8. rm -rf /usr/local/mysql
    9. rm -rf /usr/local/zend
    10. rm /etc/my.cnf
    11. rm /etc/init.d/mysql
    12. rm /root/vhost.sh
    13. rm /root/lnmp

    使用webmin管理LNMP生产环境及Linux系统

    Webmin简介

    Webmin是功能非常强大的Unix系统管理面板。管理员通过任何一款浏览器,就能添加用户帐号,管理Apache,DNS,文件共享系统,甚至更多。Webmin允许你DIY模块,你只需要到模块管理页面,增加你需要的功能,删除你认为不实用的功能。如果你熟悉perl,你甚至可以自己开发模块来增加Webmin功能。使用Webmin管理Linux服务器,你将可以可视化管理你的服务器,完成脱离了命令行模式的管理方法。下面我们来介绍如何使用Webmin(下面的所有操作都是在CentOS中进行)。
    继续阅读使用webmin管理LNMP生产环境及Linux系统