tomcat 修改部署web项目的路径

tomcat 发布自己项目操作实际很简单,修改路径是为了方便我们发布项目,不需要频繁操作。

1. 首先当然是去tomcat官网选择你需要的版本下载

https://link.juejin.im/?target=http%3A%2F%2Ftomcat.apache.org%2Fdownload-70.cgi

2. 下载需要的JDK

https://link.juejin.im/?target=https%3A%2F%2Fwww.oracle.com%2Ftechnetwork%2Fjava%2Fjavase%2Fdownloads%2Findex.html

安装之后设置环境变量, JAVA_HOME = “你安装的java地址”

例如: JAVA_HOME="C:Program FilesJavajdk-10.0.1"

3. 解压tomcat,并且配置环境变量

CATALINA_HOME = “安装的tomcat地址”

例如: CATALINA_HOME="F:Program Files (x86)apache-tomcat-9.0.14"

4. 找到 tomcat下面的conf文件夹下面的server.xml

找到节点里面添加配置

<Context docBase="G:webapptest" reloadable="true" path="/test" />

docBase:  你电脑指定的web项目的路径

reloadable=”true” 相关文件改变的时候会重新加载web app

path: 虚拟文件目录,可以为空,配置的话访问地址就要加上它再访问

5. 找到 tomcat 下面的 bin 文件夹里面的 startup.bat 启动tomcat服务

输入 localhost:8080/test 即可看到你的web项目

6. 修改tomcat的端口号

找到server.xml 中的 节点的port=8080修改成你需要的端口,记得不要冲突

例:<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
  • netstat -ano 命令,列出所有端口情况,查看是否有冲突的端口被占用
  • netstat -aon|findstr “8080” 具体被占用8080端口对应的PID
  • tasklist|findstr “端口占用的PID” 就知道哪个进程占用了8080端口

python Web开发之 WSGI & uwsgi & uWSGI

未分类

未分类

首先弄清下面几个概念:

WSGI

全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server如何与web application通信的规范。server和application的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Bottle, Flask, Django。

WSGI是Web 服务器(uWSGI)与 Web 应用程序或应用框架(Django)之间的一种低级别的接口

wsgi server (比如uWSGI) 要和 wsgi application(比如django )交互,uwsgi需要将过来的请求转给django 处理,那么uWSGI 和 django的交互和调用就需要一个统一的规范,这个规范就是WSGI WSGI(Web Server Gateway Interface)

WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。自从 WSGI 被开发出来以后,许多其它语言中也出现了类似接口。

WSGI 的官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。

WSGI 是作为 Web 服务器与 Web 应用程序或应用框架之间的一种低级别的接口,以提升可移植 Web 应用开发的共同点。WSGI 是基于现存的 CGI 标准而设计的。

uwsgi

与WSGI一样是一种通信协议,是uWSGI服务器的独占协议,用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型的描述,与WSGI协议是两种东西,据说该协议是fcgi协议的10倍快。

uWSGI

是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等。

uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。

WSGI协议主要包括server和application两部分

  • WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端;

  • WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。

WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。例如uWSGI和Gunicorn都是实现了WSGI server协议的服务器,Django,Flask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。

未分类

像Django,Flask框架都有自己实现的简单的WSGI server,一般用于服务器调试,生产环境下建议用其他WSGI server。

FastCgi协议, uwsgi协议, http协议有什么用?

nginx 和 uWSGI交互就必须使用同一个协议,而上面说了uwsgi支持fastcgi,uwsgi,http协议,这些都是nginx支持的协议,只要大家沟通好使用哪个协议,就可以正常运行了。

uwsgi是服务器和服务端应用程序的通信协议,规定了怎么把请求转发给应用程序和返回

# WSGI和uwsgi
https://www.jianshu.com/p/679dee0a4193
# uWSGI,WSGI和uwsgi
https://www.cnblogs.com/wspblog/p/8575101.html

配置一个nginx+php-fpm的web服务器

一、基本信息

  • 系统(L):CentOS 6.9 #下载地址:http://mirrors.sohu.com
  • Web服务器(N):NGINX 1.14.0 #下载地址:http://nginx.org/en/download.html
  • 数据库服务器(M):MySQL 5.6.40 #下载地址:https://dev.mysql.com/downloads/mysql
  • PHP-FPM服务器(P):php-5.6.8.tar.gz #下载地址:http://mirrors.sohu.com/php/
  • OPENSSL:openssl-1.0.2o.tar.gz #下载地址:https://www.openssl.org/source/

指定服务安装的通用位置

mkdir /usr/local/services
SERVICE_PATH=/usr/local/services

创建服务运行的账户

useradd -r -M -s /sbin/nologin www

安装所需依赖包

yum -y install pcre pcre-devel 
gperftools gcc zlib-devel 
libxml2 libxml2-devel 
bzip2 bzip2-devel 
curl curl-devel 
libjpeg-devel libjpeg 
libpng-devel libpng 
freetype freetype-devel 
libmcrypt libmcrypt-devel 
openssl-devel

二、软件安装配置

1、NGINX+OPENSSL安装

下载解压NGINX+OPENSSL

NGINX_URL="http://nginx.org/download/nginx-1.14.0.tar.gz"
OPENSSL_URL="https://www.openssl.org/source/openssl-1.1.0h.tar.gz"

wget -P ${SERVICE_PATH} ${NGINX_URL} && tar -zxvf ${SERVICE_PATH}/nginx*.tar.gz -C ${SERVICE_PATH}
wget -P ${SERVICE_PATH} ${OPENSSL_URL} && tar -zxvf ${SERVICE_PATH}/openssl*.gz -C ${SERVICE_PATH}

编译安装NGINX

cd ${SERVICE_PATH}/nginx-*;./configure 
--prefix=${SERVICE_PATH}/nginx 
--user=www --group=www 
--with-http_stub_status_module 
--with-http_ssl_module 
--with-http_flv_module 
--with-pcre 
--with-http_gzip_static_module 
--with-openssl=${SERVICE_PATH}/openssl* 
--with-http_realip_module 
--with-google_perftools_module 
--without-select_module 
--without-mail_pop3_module 
--without-mail_imap_module 
--without-mail_smtp_module 
--without-poll_module 
--without-http_autoindex_module 
--without-http_geo_module 
--without-http_uwsgi_module 
--without-http_scgi_module 
--without-http_memcached_module 
--with-cc-opt='-O2' && cd ${SERVICE_PATH}/nginx-*;make && make install

NGINX+OPENSSL安装完成后的清理与其他配置

ln -sv ${SERVICE_PATH}/nginx /usr/local/
rm -rf ${SERVICE_PATH}/nginx/conf/*.default
cd ${SERVICE_PATH} ; rm -rf nginx*.tar.gz openssl*.tar.gz

写入主配置文件nginx.conf(配置已优化)

cat << EOF >/usr/local/nginx/conf/nginx.conf
user www;
worker_processes WORKERNUMBER;
worker_cpu_affinity auto;
worker_rlimit_nofile 655350;

error_log /var/log/nginx_error.log;
pid /tmp/nginx.pid;

google_perftools_profiles /tmp/tcmalloc;

events {
use epoll;
worker_connections 655350;
multi_accept on;
}

http {
charset utf-8;
include mime.types;
default_type text/html;

log_format main '"$remote_addr" - [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" '
'"$sent_http_server_name $upstream_response_time" '
'$request_time '
'$args';


sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 120;
client_body_buffer_size 512k;
client_header_buffer_size 64k;
large_client_header_buffers 4 32k;
client_max_body_size 300M;
client_header_timeout 15s;
client_body_timeout 50s;
open_file_cache max=102400 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 1;
server_names_hash_max_size 2048;
server_names_hash_bucket_size 256;
server_tokens off;
gzip on;
gzip_proxied any;
gzip_min_length 1024;
gzip_buffers 4 8k;
gzip_comp_level 9;
gzip_disable "MSIE [1-6].";
gzip_types application/json test/html text/plain text/css application/font-woff application/pdf application/octet-stream application/x-javascript application/javascript application/xml text/javascript;
fastcgi_cache_path /dev/shm/ levels=1:2 keys_zone=fastcgicache:512m inactive=10m max_size=3g;
fastcgi_cache_lock on;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
fastcgi_send_timeout 300;
fastcgi_connect_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 256k;
fastcgi_buffers 4 128k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;


include vhost/*.conf;
}
EOF

NGINX worker进程数配置,指定为逻辑CPU数量的2倍

THREAD=`expr $(grep process /proc/cpuinfo |wc -l) * 2`
sed -i s"/WORKERNUMBER/$THREAD/" ${SERVICE_PATH}/nginx/conf/nginx.conf

2、PHP-FPM安装

下载并解压PHP-FPM软件

FPM_URL="http://mirrors.sohu.com/php/php-5.6.8.tar.gz"
wget -P ${SERVICE_PATH} ${FPM_URL} && tar -zxvf ${SERVICE_PATH}/php*.tar.gz -C ${SERVICE_PATH}

编译安装PHP-FPM

cd ${SERVICE_PATH}/php-*;./configure 
--prefix=${SERVICE_PATH}/php 
--with-gd 
--with-mcrypt 
--with-mysql=mysqlnd 
--with-mysqli=mysqlnd 
--with-pdo-mysql=mysqlnd 
--enable-maintainer-zts 
--enable-ftp 
--enable-zip 
--with-bz2 
-with-iconv-dir 
--with-freetype-dir 
--with-jpeg-dir 
--with-png-dir 
--with-config-file-path=${SERVICE_PATH}/php 
--enable-mbstring 
--enable-fpm 
--with-fpm-user=www 
--with-fpm-group=www 
--disable-debug 
--enable-opcache 
--enable-soap 
--with-zlib 
--with-libxml-dir=/usr 
--enable-xml 
--disable-rpath 
--enable-bcmath 
--enable-shmop 
--enable-sysvsem 
--enable-inline-optimization 
--with-curl 
--enable-mbregex 
--enable-gd-native-ttf 
--with-openssl 
--with-mhash 
--enable-pcntl 
--enable-sockets 
--with-xmlrpc 
--with-pear 
--with-gettext 
--disable-fileinfo && cd ${SERVICE_PATH}/php-*;make && make install

若FPM程序有插件需求,如mongo或redis连接插件,则可通过pecl安装php相关插件

${SERVICE_PATH}/php/bin/pecl install mongo || exit

${SERVICE_PATH}/php/bin/pecl install redis || exit

安装完成后的配置清理

ln -sv ${SERVICE_PATH}/php /usr/local/

php.ini配置文件写入(配置已优化)

cat << EOF >${SERVICE_PATH}/php/php.ini 
[PHP]
engine = On
short_open_tag = Off
asp_tags = Off
precision = 14
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = 17
disable_functions = shell_exec,phpinfo,exec
disable_classes =
zend.enable_gc = On
expose_php = Off
max_execution_time = 60
max_input_time = 60
memory_limit = 128M
error_reporting = E_WARING & ERROR
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 2048
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
html_errors = Off
error_log = /var/log/php_errors.log
variables_order = "GPCS"
request_order = "GP"
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 8M
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
default_charset = "UTF-8"
doc_root =
user_dir =
enable_dl = Off
cgi.fix_pathinfo=0
file_uploads = On
upload_max_filesize = 2M
max_file_uploads = 20
allow_url_fopen = Off
allow_url_include = Off
default_socket_timeout = 60
[CLI Server]
cli_server.color = On
[Date]
[filter]
[iconv]
[intl]
[sqlite]
[sqlite3]
[Pcre]
[Pdo]
[Pdo_mysql]
pdo_mysql.cache_size = 2000
pdo_mysql.default_socket=
[Phar]
[mail function]
SMTP = localhost
smtp_port = 25
mail.add_x_header = On
[SQL]
sql.safe_mode = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[Interbase]
ibase.allow_persistent = 1
ibase.max_persistent = -1
ibase.max_links = -1
ibase.timestampformat = "%Y-%m-%d %H:%M:%S"
ibase.dateformat = "%Y-%m-%d"
ibase.timeformat = "%H:%M:%S"
[MySQL]
mysql.allow_local_infile = On
mysql.allow_persistent = On
mysql.cache_size = 2000
mysql.max_persistent = -1
mysql.max_links = -1
mysql.default_port =
mysql.default_socket =
mysql.default_host =
mysql.default_user =
mysql.default_password =
mysql.connect_timeout = 60
mysql.trace_mode = Off
[MySQLi]
mysqli.max_persistent = -1
mysqli.allow_persistent = On
mysqli.max_links = -1
mysqli.cache_size = 2000
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[mysqlnd]
mysqlnd.collect_statistics = On
mysqlnd.collect_memory_statistics = Off
[OCI8]
[PostgreSQL]
pgsql.allow_persistent = On
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice = 0
[Sybase-CT]
sybct.allow_persistent = On
sybct.max_persistent = -1
sybct.max_links = -1
sybct.min_server_severity = 10
sybct.min_client_severity = 10
[bcmath]
bcmath.scale = 0
[browscap]
[Session]
session.save_handler = files
session.save_path = "/tmp"
session.use_strict_mode = 0
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.referer_check =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.hash_function = 0
session.hash_bits_per_character = 5
url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
[MSSQL]
mssql.allow_persistent = On
mssql.max_persistent = -1
mssql.max_links = -1
mssql.min_error_severity = 10
mssql.min_message_severity = 10
mssql.compatibility_mode = Off
mssql.secure_connection = Off
[Assertion]
[COM]
[mbstring]
[gd]
gd.jpeg_ignore_warning = 0
[exif]
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
soap.wsdl_cache_limit = 5
[sysvshm]
[ldap]
ldap.max_links = -1
[mcrypt]
[dba]
[opcache]
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.validate_timestamps=1
opcache.revalidate_freq=30
opcache.fast_shutdown=1
opcache.enable_file_override=1
[curl]
[openssl]
extension_dir='${SERVICE_PATH}/php/lib/php/extensions/'
;extension=mongo.so
;extension=redis.so
EOF

php-fpm.conf配置文件写入(配置已优化)

cat << EOF >${SERVICE_PATH}/php/etc/php-fpm.conf
[global]
error_log = /var/log/php-fpm-error.log
log_level = warning
process_control_timeout = 10
rlimit_files = 655350
events.mechanism = epoll
[www]
user = www
group = www
listen = /dev/shm/php-fpm.sock
listen.backlog = 2048
listen.owner = www
listen.group = www
listen.mode = 0660
pm = dynamic
pm.max_children = 200
pm.start_servers = 105
pm.min_spare_servers = 10
pm.max_spare_servers = 200
pm.process_idle_timeout = 10s;
pm.max_requests = 1000
pm.status_path = /fpmstatus
ping.path = /ping
ping.response = pong
slowlog = /var/log/php-slow-$pool.log
request_slowlog_timeout = 10
request_terminate_timeout = 0
rlimit_files = 655350
security.limit_extensions = .php
EOF

三、基于以上配置PHP网站

mkdir /usr/local/nginx/conf/vhost
cat << EOF > /usr/local/nginx/conf/vhost/erbiao.ex.com.conf
server
{
listen 80 backlog=1024;
server_name erbiao.ex.com;
index index.php index.html ;
root /www/web/;
access_log off;
add_header Server-Name WEBerbiaoEX;

location ~ .php {
fastcgi_pass unix:/dev/shm/php-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
set $real_script_name $fastcgi_script_name;
if ($fastcgi_script_name ~ "^(.+?.php)(/.+)$") {
set $real_script_name $1;
set $path_info $2;
}
fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
fastcgi_param SCRIPT_NAME $real_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;


location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
}

location ~ .*.(js|css)?$
{
expires 12h;
}

}

}
EOF

若在同一服务器运行nginx和php-fpm,并发量不超过1000,选择unix socket,如此可避免一些检查操作(路由等),因此更快,更轻。若是高并发业务,则选择使用更可靠的tcp socket,以负载均衡、内核优化等运维手段维持效率

四、启动服务

启动nginx和php-fpm

/usr/local/nginx/sbin/nginx
/usr/local/php-fpm/sbin/php-fpm

命令其他选项

nginx
├── -s选项,向主进程发送信号
|   ├── reload参数,重新加载配置文件
|   ├── stop参数,快速停止nginx
|   ├── reopen参数,重新打开日志文件
|   ├── quit参数,Nginx在退出前完成已经接受的连接请求
├── -t选项,检查配置文件是否正确
├── -c选项,用于指定特定的配置文件并启动nginx
├── -V选项(大写),显示nginx编译选项与版本信息

php-fpm
├── -t选项,检查配置文件是否正确
├── -m选项,显示所有已安装模块
├── -i选项,显示PHP详细信息
├── -v选项,显示版本信息

Tornado Web服务器多进程启动的2个方法

一、Tornado简介

Tornado 是 FriendFeed 的 Web 服务器及其常用工具的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对epoll的运用,Tornado 每秒可以处理数以千计的连接,因此 Tornado 是实时 Web 服务的一个理想框架。

二、多进程启动方法

正常启动方法:

server = HTTPServer(app)
server.listen(8888)
IOLoop.instance().start()

多进程、方案1:

server = HTTPServer(app)
server.bind(8888)
server.start(0)  # Forks multiple sub-processes
IOLoop.instance().start()

多进程、方案2:

sockets = tornado.netutil.bind_sockets(8888)
tornado.process.fork_processes(0)
server = HTTPServer(app)
server.add_sockets(sockets)
IOLoop.instance().start()

Python Web 腾讯云部署:flask+fabric+gunicorn+nginx+supervisor

最近看了《Flask Web开发–基于Python的Web应用开发实战》,还有廖雪峰老师的Python教程,前者是基于Web框架flask进行开发,后者是自己搭建了一个Web框架。Web框架致力于如何生成HTML代码,而Web服务器用于处理和响应HTTP请求。Web框架和Web服务器之间的通信,需要一套双方都遵守的接口协议。WSGI协议就是用来统一这两者的接口的。

为了部署一个完整的环境,本文将使用flask作为Web框架,Gunicorn作为WSGI容器,Nginx作为Web服务器,同时使用Supervisor进行进程管理,并使用Fabric实现自动化部署。

前期准备

申请一个腾讯云服务器Ubuntu Server 16.04.1 LTS 64位:

账户:ubuntu
密码:**********
内网ip:172.16.0.17
外网ip:139.199.184.183

使用《Flask Web开发》源码:

$ mkdir web
$ cd web
~/web$ git clone https://github.com/miguelgrinberg/flasky.git

设置并进入虚拟环境:

~/web$ cd flasky
~/web/flasky$ virtualenv venv
~/web/flasky$ source venv/bin/activate

安装对应的库:

(venv) ~/web/flasky$ cd requirements
(venv) ~/flasky/requirements$ pip3 install -r common.txt
(venv) ~/flasky/requirements$ pip3 install -r dev.txt
(venv) ~/flasky/requirements$ pip3 install -r heroku.txt
(venv) ~/flasky/requirements$ pip3 install -r prod.txt

创建数据库:

(venv) ~/flasky/requirements$ cd ..
(venv) ~/flasky/$ python3 manage.py deploy

Fabric

Fabric入门看这里https://www.jianshu.com/p/0be28f6c4607

新开一个终端,远程登陆服务器:

$ ssh ubuntu@139.199.184.183

在服务器端定义好部署结构:

ubuntu@VM $ mkdir web
ubuntu@VM $ mkdir web/flasky
ubuntu@VM $ mkdir web/log

即:

  • web/    Web App根目录
    • flasky/  存放python文件
    • log/    存放日志文件

我们使用Fabric将代码上传到服务器,首先,安装Fabric:

(venv) ~/web$ sudo apt-get install fabric

在web目录下创建fabfile.py(必须这个名字),写入:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os, re
from datetime import datetime

# 导入Fabric API:
from fabric.api import *

# 服务器登录用户名:
env.user = 'ubuntu'
# sudo用户为root:
env.sudo_user = 'root'
# 服务器地址,可以有多个,依次部署:
env.hosts = ['139.199.184.183']

_TAR_FILE = 'dist-flasky.tar.gz'

def build():
    includes = ['app', 'migrations', 'requirements', 'tests', 'venv', *.py', '*.sqlite']
    excludes = ['.*', '*.pyc', '*.pyo']
    local('rm -f dist/%s' % _TAR_FILE)
    with lcd(os.path.join(os.path.abspath('.'), 'flasky')):
        cmd = ['tar', '--dereference', '-czvf', '../dist/%s' % _TAR_FILE]
        cmd.extend(['--exclude='%s'' % ex for ex in excludes])
        cmd.extend(includes)
        local(' '.join(cmd))


_REMOTE_TMP_TAR = '/tmp/%s' % _TAR_FILE
_REMOTE_BASE_DIR = '/home/ubuntu/web/flasky'

def deploy():
    newdir = 'flask-%s' % datetime.now().strftime('%y-%m-%d_%H.%M.%S')
    # 删除已有的tar文件:
    run('rm -f %s' % _REMOTE_TMP_TAR)
    # 上传新的tar文件:
    put('dist/%s' % _TAR_FILE, _REMOTE_TMP_TAR)
    # 创建新目录:
    with cd(_REMOTE_BASE_DIR):
        sudo('mkdir %s' % newdir)
    # 解压到新目录:
    with cd('%s/%s' % (_REMOTE_BASE_DIR, newdir)):
        sudo('tar -xzvf %s' % _REMOTE_TMP_TAR)
    # 重置软链接:
    with cd(_REMOTE_BASE_DIR):
        sudo('rm -f flask')
        sudo('ln -s %s flask' % newdir)
        sudo('chown ubuntu:ubuntu flask')
        sudo('chown -R ubuntu:ubuntu %s' % newdir)
    # 重启Python服务和nginx服务器:
    #with settings(warn_only=True):
    #    sudo('supervisorctl stop flask')
    #    sudo('supervisorctl start flask')
    #    sudo('/etc/init.d/nginx reload')

使用fab命令将代码打包:

(venv) ~/web$ mkdir dist
(venv) ~/web$ fab build

将代码传上服务器:

(venv) ~/web$ fab deploy

Gunicorn部署

gunicorn入门看这里https://www.jianshu.com/p/260f18aa5462

代码上传给服务器后,可以直接运行manage.py文件,就可以访问网站了。可是实际情景中需要处理高并发访问的情况,我们使用Gunicorn来解决这个问题。

在服务器上进入flask所在目录,激活虚拟环境,并安装gunicorn:

ubuntu@VM ~/web/flasky/flask$ source venv/bin/activate
(venv) ubuntu@VM ~/web/flasky/flask$  sudo apt-get install gunicorn

使用gunicorn运行web应用(支持4个并发请求):

(venv) ubuntu@VM ~/web/flasky/flask$ gunicorn -w 4 -b 172.16.0.17:5000 manage:app

其中172.16.0.17是服务器内网ip,这样设置后就可以通过外网ip访问网站了。在浏览器地址栏中输入139.199.184.183:5000,就可以看到网站了。

Nginx

Nginx入门看这里

使用gunicorn的话,需要将端口暴露给外界,这不利于服务器的安全问题。Nginx是一个高性能的HTTP和反向代理服务器,通过配置一个代理服务器,可以实现反向代理的功能。所谓反向代理,是指一个请求,从互联网过来,先进入代理服务器,再由代理服务器转发给局域网的目标服务器。

先安装nginx:

(venv) ubuntu@VM ~$ sudo apt-get install nginx

设置反向代理服务器:在服务器,进入目录/etc/nginx/sites-available/,在目录下的default文件写入一个server。

首先,先备份default文件:

$ cd /etc/nginx/sites-available/
$ sudo cp default default-backup

在default文件中写入server:

server {
    listen 80;
    root /home/ubuntu/web/flasky/flask/;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
    }
}

配置完成后,重启nginx:

$ sudo nginx -s reload

此时,该反向代理服务器就开始监听80端口。80端口是为HTTP开放的端口,浏览网页服务默认的端口号都是80 (8080端口跟80端口一样)。只要访问服务器,代理服务器就会将请求转发到127.0.0.1:5000.

回到目录/home/ubuntu/web/flasky/flask/下,再次使用gunicorn运行web app:

(venv) ubuntu@VM ~/web/flasky/flask$ gunicorn -w 4 -b 127.0.0.1:5000 manage:app

用的ip地址是127.0.0.1

而外界要访问网站只需要在浏览器地址栏输入139.199.184.183,使用默认端口80。这样web app实际使用的5000就不用暴露了。

Supervisor

supervisor入门看这里https://www.jianshu.com/p/0226b7c59ae2

如果你需要进程一直执行,若该进程因各种原因中断,也会自动重启的话,supervisor是一个很好的选择。

supervisor管理进程,是通过fork/exec的方式将这些被管理的进程当作supervisor的子进程来启动,所以我们只需要将要管理进程的可执行文件的路径添加到supervisor的配置文件中就好了。此时被管理进程被视为supervisor的子进程,若该子进程异常终端,则父进程可以准确的获取子进程异常终端的信息,通过在配置文件中设置autostart=true,可以实现对异常中断的子进程的自动重启。

安装supervisor:

(venv) ubuntu@VM ~$ sudo apt-get install supervisor

在目录/etc/supervisor/conf.d/下创建flask.conf,并加入:

;/etc/supervisor/conf.d/flask.conf

[program:flask]

command     = /home/ubuntu/.local/bin/gunicorn -w 4 -b 127.0.0.1:5000 manage:app
;这里/home/ubuntu/.local/bin/是我服务器上gunicorn的安装目录
directory   = /home/ubuntu/web/flasky/flask
user        = ubuntu
startsecs   = 3
autostart   = true

redirect_stderr         = true
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups  = 10
stdout_logfile          = /home/ubuntu/web/flasky/log/app.log

配置完后,进入目录/home/ubuntu/web/flasky/, 创建log目录:

(venv) ubuntu@VM ~/web/flasky$ mkdir log

启动supervisor:

(venv) ubuntu@VM /etc/supervisor$ sudo supervisord -c supervisor.conf                 

执行服务(运行app.py):

(venv) ubuntu@VM $ sudo supervisorctl start flask

此时,进程flask运行,执行command后边的语句,即/home/ubuntu/.local/bin/gunicorn -w 4 -b 127.0.0.1:5000 manage:app。

在浏览器地址栏输入139.199.184.183,使用默认端口80,即可访问网站。

如果supervisor遇到错误,可以在/var/log/supervisor/supervisord.log中查看日志;
如果app运行出现问题,可以在 /home/ubuntu/web/flasky/log/app.log中查看日志。

自动化部署

为了实现自动化部署,将上边fabfile.py文件的最后四个语句的注释取消,就能在将修改后的代码传上服务器后,用supervisor自动重载配置并重启flask进程。之后便可以直接访问网站。
因此,每次更改代码后,只需要执行以下两个语句,无需登陆服务器,便可以启动网站:

$ fab build
$ fab deploy

gunicorn配置文件

gunicorn的配置除了可以在命令行中设置,也可以使用配置文件实现更复杂的配置:

$ gunicorn -c gunicorn.py manage:app

gunicorn.py文件如下:

# gunicorn.py
import logging
import logging.handlers
from logging.handlers import WatchedFileHandler
import os
import multiprocessing
bind = '127.0.0.1:5000'       #绑定ip和端口号
backlog = 512                 #监听队列
chdir = '/home/test/server/bin'   #gunicorn要切换到的目的工作目录
timeout = 30      #超时
worker_class = 'gevent' #使用gevent模式,还可以使用sync 模式,默认的是sync模式
workers = multiprocessing.cpu_count() * 2 + 1    #进程数

threads = 2 #指定每个进程开启的线程数

loglevel = 'info' #日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'     #设置gunicorn访问日志格式,错误日志无法设置
"""
其每个选项的含义如下
        h           remote address
        l           '-'
        u           currently '-', may be user name in future releases
        t           date of the request
        r           status line (e.g. ``GET / HTTP/1.1``)
        s           status
        b           response length or '-'
        f           referer
        a           user agent
        T           request time in seconds
        D           request time in microseconds
        L           request time in decimal seconds
        p           process ID
        {Header}i   request header
        {Header}o   response header
"""
accesslog = "/home/ubutnu/web/flasky/log/gunicorn_access.log"      #访问日志文件
errorlog = "/home/ubutnu/web/flasky/log/gunicorn_error.log"        #错误日志文件

在本地将gunicorn.py放在/home/ubutnu/web/flasky/flask/目录下,在服务器端将/etc/supervisor/conf.d/flask.conf中的command改为:

command     = /home/ubuntu/.local/bin/gunicorn -c /home/ubutnu/web/flasky/flask/gunicorn.py manage:app

然后,在本地执行:

$ fab build
$ fab deploy

便可以访问网站了。

未分类

使用nginx后如何在web应用中获取用户ip及原理解释

问题背景

在实际应用中,我们可能需要获取用户的ip地址,比如做异地登陆的判断,或者统计ip访问次数等,通常情况下我们使用request.getRemoteAddr()就可以获取到客户端ip,但是当我们使用了nginx作为反向代理后,使用request.getRemoteAddr()获取到的就一直是nginx服务器的ip的地址,那这时应该怎么办?

part1:解决方案

我在查阅资料时,有一本名叫《实战nginx》的书,作者张晏,这本书上有这么一段话“经过反向代理后,由于在客户端和web服务器之间增加了中间层,因此web服务器无法直接拿到客户端的ip,通过$remote_addr变量拿到的将是反向代理服务器的ip地址”。这句话的意思是说,当你使用了nginx反向服务器后,在web端使用request.getRemoteAddr()(本质上就是获取$remote_addr),取得的是nginx的地址,即$remote_addr变量中封装的是nginx的地址,当然是没法获得用户的真实ip的,但是,nginx是可以获得用户的真实ip的,也就是说nginx使用$remote_addr变量时获得的是用户的真实ip,如果我们想要在web端获得用户的真实ip,就必须在nginx这里作一个赋值操作,如下:
proxy_set_header X-real-ip $remote_addr;
其中这个X-real-ip是一个自定义的变量名,名字可以随意取,这样做完之后,用户的真实ip就被放在X-real-ip这个变量里了,然后,在web端可以这样获取:
request.getAttribute("X-real-ip")
这样就明白了吧。

part2:原理介绍

这里我们将nginx里的相关变量解释一下,通常我们会看到有这样一些配置

server {
        listen       88;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        location /{
            root   html;
            index  index.html index.htm;
                            proxy_pass                  http://backend; 
           proxy_redirect              off;
           proxy_set_header            Host $host;
           proxy_set_header            X-real-ip $remote_addr;
           proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
                     # proxy_set_header            X-Forwarded-For $http_x_forwarded_for;
        }

我们来一条条的看

1. proxy_set_header X-real-ip $remote_addr;
这句话之前已经解释过,有了这句就可以在web服务器端获得用户的真实ip
但是,实际上要获得用户的真实ip,不是只有这一个方法,下面我们继续看。

2. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
我们先看看这里有个X-Forwarded-For变量,这是一个squid开发的,用于识别通过HTTP代理或负载平衡器原始IP一个连接到Web服务器的客户机地址的非rfc标准,如果有做X-Forwarded-For设置的话,每次经过proxy转发都会有记录,格式就是client1, proxy1, proxy2,以逗号隔开各个地址,由于他是非rfc标准,所以默认是没有的,需要强制添加,在默认情况下经过proxy转发的请求,在后端看来远程地址都是proxy端的ip 。也就是说在默认情况下我们使用request.getAttribute(“X-Forwarded-For”)获取不到用户的ip,如果我们想要通过这个变量获得用户的ip,我们需要自己在nginx添加如下配置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
意思是增加一个$proxy_add_x_forwarded_for到X-Forwarded-For里去,注意是增加,而不是覆盖,当然由于默认的X-Forwarded-For值是空的,所以我们总感觉X-Forwarded-For的值就等于$proxy_add_x_forwarded_for的值,实际上当你搭建两台nginx在不同的ip上,并且都使用了这段配置,那你会发现在web服务器端通过request.getAttribute(“X-Forwarded-For”)获得的将会是客户端ip和第一台nginx的ip。

那么$proxy_add_x_forwarded_for又是什么?
$proxy_add_x_forwarded_for变量包含客户端请求头中的”X-Forwarded-For”,与$remote_addr两部分,他们之间用逗号分开。
举个例子,有一个web应用,在它之前通过了两个nginx转发,即用户访问该web通过两台nginx。
在第一台nginx中,使用
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量的”X-Forwarded-For”部分是空的,所以只有$remote_addr,而$remote_addr的值是用户的ip,于是赋值以后,X-Forwarded-For变量的值就是用户的真实的ip地址了。

到了第二台nginx,使用
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for变量,X-Forwarded-For部分包含的是用户的真实ip,$remote_addr部分的值是上一台nginx的ip地址,于是通过这个赋值以后现在的X-Forwarded-For的值就变成了“用户的真实ip,第一台nginx的ip”,这样就清楚了吧。

最后我们看到还有一个$http_x_forwarded_for变量,这个变量就是X-Forwarded-For,由于之前我们说了,默认的这个X-Forwarded-For是为空的,所以当我们直接使用proxy_set_header X-Forwarded-For $http_x_forwarded_for时会发现,web服务器端使用request.getAttribute(“X-Forwarded-For”)获得的值是null。如果想要通过request.getAttribute(“X-Forwarded-For”)获得用户ip,就必须先使用proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;这样就可以获得用户真实ip。

ps:变量名太长,自己感觉看着好晕,打字打的我眼睛都花了,希望解释清楚了,如果有疑问可以和我交流,共同学习。

Docker下的web开发和Tomcat部署

本期实践的主要目标是开发一个简单的web应用,打包部署到Docker的tomcat容器中去; 第一期为了快速上手,获取docker是从国内的daocloud获取的,本期开始,为了更好的熟悉和了解Docker技术,我们的查找,pull和push都改为在Docker Hub上进行,即网站:hub.docker.com,建议各位去上面注册一个账号,这样就有自己的仓库可以保存镜像了。

在hub.docker.com上搜索tomcat,搜索结果的第一个就是官方镜像,如下图:

未分类

点击Detail按钮,进入详情页,可以发现有好多个tag,例如7.0.75这个,就是tomcat7.0.75版本:

未分类

这么多版本,究竟选哪个呢?我们还是先看看几个具体版本的差异吧,打开tomcat官网下的这个链接:tomcat.apache.org/whichversio…

可以看到具体的差异:

未分类

可以看到,tomcat7 支持servlet3.0,可以满足我们的要求了,所以就用它吧,执行如下命令行即可下载镜像:

docker pull tomcat:7.0.75

命令执行有可能执行失败,多重试几次才行,pull成功后用docker images命令可以看到镜像:

未分类

来快速体验一下镜像的效果,执行命令:

docker run -it --rm -p 8888:8080 tomcat:7.0.75

–rm参数表示container结束时,Docker会自动清理其所产生的数据。

可以看到tomcat启动的日志全部打印在终端了,

未分类

因为我们用-p 8888:8080将容器的8080端口映射到当前电脑的8888端口,所以打开当前电脑的浏览器,输入:localhost:8888,可以看到熟悉的大猫:

未分类

接下来我们开发一个最简单的spring mvc应用,然后部署到docker的tomcat容器中试试,这我用的是IntelliJ IDEA CE,创建maven工程:

未分类

GAV信息如下:

未分类

如下图所示,通过这里增加一个mvn命令:

未分类

增加mvn命令:

未分类

配置命令如下:

未分类

接下来我们给web工程添加spring mvc支持,首先是web.xml文件,替换成下面这样:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1" metadata-complete="true">

  <servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

完整的pom文件内容如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.bolingcavalry</groupId>
  <artifactId>helloworldwebapp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>helloworldwebapp Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.3.4.RELEASE</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>helloworldwebapp</finalName>
  </build>
</project>

在编译的时候遇到了一点小问题需要在此提一下,在工程上点击右键查看module属性,如下图:

未分类

此时看到的信息如下图所示:

未分类

注意在上图的绿色框框位置,如果你的工程中没有看到绿色框框中的内容,就用鼠标右键点击红色框框位置,在弹出的菜单中点击”Sources”,这样就把java目录加入到工程的编译目录中去了。

这时候去执行mvn命令依然无法编译java文件,在工程上点击右键,执行mvn的reimport命令,如下图,执行完毕后就可以用mvn命令编译java文件了:

未分类

现在开始添加测试代码,先增加一个view目录,里面放个jsp文件,文件结构和jsp文件的内容如下:

未分类

再增加一个java文件,文件路径如下:

未分类

该文件的源码:

package com.bolingcavalry.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class FirstController {

    @RequestMapping(value = "firstview", method = RequestMethod.GET)
    public String index() {
        return "firstview";
    }
}

最后在webapp/WEB-INF目录下增加spring-servlet.xml文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd 
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 配置扫描的包 -->
    <context:component-scan base-package="com.bolingcavalry.*" />

    <!-- 注册HandlerMapper、HandlerAdapter两个映射类 -->
    <mvc:annotation-driven />

    <!-- 访问静态资源 -->
    <mvc:default-servlet-handler />

    <!-- 视图解析器 -->
    <bean
        class-og="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/view/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

</beans>

好了,代码已经写完,可以打包了,执行我们刚才配置好的命令,如下图:

未分类

命令执行后,可以在target目录下看到war包:

未分类

现在我们把文件部署到tomcat上去,先建一个目录,例如我建了这个目录:/Users/bolingcavalry/temp/201703/10/share,然后把helloworldwebapp.war文件复制到这个目录下,再在控制台执行以下命令:

docker run --name helloworldwebapp -p 8888:8080 -d -v /Users/bolingcavalry/temp/201703/10/share:/usr/Downloads tomcat:7.0.75

这样就启动了一个容器,执行以下命令进入容器

docker exec -it helloworldwebapp /bin/bash

进入容器后再执行以下命令将war包复制到tomcat容器目录下:

cp /usr/Downloads/helloworldwebapp.war /usr/local/tomcat/webapps/

这时候再打开浏览器,输入http://localhost:8888/helloworldwebapp/firstview试试吧,如下图,符合预期:

未分类

此时,我们今天测试tomcat部署的目的已经达到了,接下来再试试提交镜像,在容器中输入exit 退出容器,再执行”docker stop helloworldwebapp”停止容器,然后执行以下命令把容器作为镜像保存在本地:

docker commit -a "bolingcavalry" -m "from tomcat 7.0.75,with a demo webapp"  helloworldwebapp bolingcavalry/helloworldwebapp:0.0.1

-a : 作者
-m :提交时的说明文字
0.0.1:tag

执行完毕后,输入docker images,可以看到新增的镜像:

未分类

接下来我们试着把本地镜像提交到hub.docker.com去(前提是已经在这个网站上注册过),输入命令docker login,接下来按照提示输入用户名和密码,执行一下命令提交镜像:

docker push bolingcavalry/helloworldwebapp:0.0.1

有点费时,需要等待:

未分类

等上传成功后,再去hub.docker.com上看看吧,自己的仓库下面已经可以看到刚刚提交的镜像了:

未分类

CentOS7下搭建postfix邮箱服务器并实现extmail的web访问

闲来无事想着尝试使用postfix搭建一个邮箱服务器,我是边搭建边写这个笔记,搭建过程中遇到坑也会一并记录,使用的系统版本如下:

[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)

本示例基于LNMP环境。

1. 准备工作

关闭selinux

[root@localhost ~]# setenforce 0
[root@localhost ~]# getenforce 
Permissive
[root@localhost ~]#

关闭firewalld防火墙,并清空iptables规则:

[root@localhost ~]# systemctl stop firewalld
[root@localhost ~]# iptables -F
[root@localhost ~]# iptables -X
[root@localhost ~]# iptables -nvL
Chain INPUT (policy ACCEPT 38 packets, 7291 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 12 packets, 1208 bytes)
 pkts bytes target     prot opt in     out     source               destination         
[root@localhost ~]#

由于CentOS7默认安装的是MariaDB,所以要添加MySQL的yum源,有些编译需要的devel包只有epel扩展源有,所以我们需要把epel源也一并添加。因为是通过wget命令从下载地址中下载,但是最小化安装的CentOS7不自带wget命令,还需要先安装这个命令:

yum install -y wget
wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
rpm -ivh mysql-community-release-el7-5.noarch.rpm
wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm

2. 安装postfix

首先需要安装编译环境及其他所需要的包,免得一会编译过程中老报缺少包的错误,因为需要安装的包有点多,所以这个过程有点慢:

yum install nginx vim gcc gcc-c++ openssl openssl-devel db4-devel ntpdate mysql mysql-devel mysql-server bzip2 php-mysql cyrus-sasl-md5 perl-GD perl-DBD-MySQL perl-GD perl-CPAN perl-CGI perl-CGI-Session cyrus-sasl-lib cyrus-sasl-plain cyrus-sasl cyrus-sasl-devel libtool-ltdl-devel telnet mail libicu-devel  -y

安装完以上所需的包后,开始编译安装postfix:

1)首先卸载系统自带的postfix,并删除postfix用户,重新指定uid、gid创建新用户postfix,postdrop,嫌一条条命令去执行有点麻烦就写成脚本文件去执行:

yum remove postfix -y
userdel postfix
groupdel postdrop
groupadd -g 2525 postfix
useradd -g postfix -u 2525 -s /sbin/nologin -M postfix
groupadd -g 2526 postdrop
useradd -g postdrop -u 2526 -s /sbin/nologin -M postdrop

2)下载源码包并解压编译(如果下载地址失效就到官网去找下载连接):

cd /usr/local/src/
wget http://cdn.postfix.johnriley.me/mirrors/postfix-release/official/postfix-3.0.11.tar.gz
tar -zxvf postfix-3.0.11.tar.gz
cd postfix-3.0.11
make makefiles 'CCARGS=-DHAS_MYSQL -I/usr/include/mysql -DUSE_SASL_AUTH -DUSE_CYRUS_SASL -I/usr/include/sasl -DUSE_TLS ' 'AUXLIBS=-L/usr/lib64/mysql -lmysqlclient -lz -lrt -lm -L/usr/lib64/sasl2 -lsasl2   -lssl -lcrypto'
make && make install
echo $?

在make install环节的时候会有个交互式的界面,可以自定义一些目录,我这里只更改了第二项临时文件目录,其他的都选择了默认目录:

Please specify the prefix for installed file names. Specify this ONLY
if you are building ready-to-install packages for distribution to OTHER
machines. See PACKAGE_README for instructions.
install_root: [/] 

Please specify a directory for scratch files while installing Postfix. You
must have write permission in this directory.
tempdir: [/usr/local/src/postfix-3.0.11] /tmp/extmail     // 就只更改这一项为tmp目录,其他的全部默认

Please specify the final destination directory for installed Postfix
configuration files.
config_directory: [/etc/postfix] 

Please specify the final destination directory for installed Postfix
administrative commands. This directory should be in the command search
path of adminstrative users.
command_directory: [/usr/sbin] 

Please specify the final destination directory for installed Postfix
daemon programs. This directory should not be in the command search path
of any users.
daemon_directory: [/usr/libexec/postfix] 

Please specify the final destination directory for Postfix-writable
data files such as caches or random numbers. This directory should not
be shared with non-Postfix software.
data_directory: [/var/lib/postfix] 

Please specify the final destination directory for the Postfix HTML
files. Specify "no" if you do not want to install these files.
html_directory: [no] 

Please specify the owner of the Postfix queue. Specify an account with
numerical user ID and group ID values that are not used by any other
accounts on the system.
mail_owner: [postfix] 

Please specify the final destination pathname for the installed Postfix
mailq command. This is the Sendmail-compatible mail queue listing command.
mailq_path: [/usr/bin/mailq] 

Please specify the final destination directory for the Postfix on-line
manual pages. You can no longer specify "no" here.
manpage_directory: [/usr/local/man] 

Please specify the final destination pathname for the installed Postfix
newaliases command. This is the Sendmail-compatible command to build
alias databases for the Postfix local delivery agent.
newaliases_path: [/usr/bin/newaliases] 

Please specify the final destination directory for Postfix queues.
queue_directory: [/var/spool/postfix] 

Please specify the final destination directory for the Postfix README
files. Specify "no" if you do not want to install these files.
readme_directory: [no]

Please specify the final destination pathname for the installed Postfix
sendmail command. This is the Sendmail-compatible mail posting interface.
sendmail_path: [/usr/sbin/sendmail] 

Please specify the group for mail submission and for queue management
commands. Specify a group name with a numerical group ID that is
not shared with other accounts, not even with the Postfix mail_owner
account. You can no longer specify "no" here.
setgid_group: [postdrop] 

Please specify the final destination directory for Postfix shared-library
files.
shlib_directory: [no]

3)更改目录的属主和属组:

chown -R postfix:postdrop /var/spool/postfix
chown -R postfix:postdrop /var/lib/postfix/
chown root /var/spool/postfix
chown -R root /var/spool/postfix/pid

4)修改postfix的配置文件:

[root@localhost ~]# vim /etc/postfix/main.cf
myhostname = mail.everyoo.com        //设置主机名
mydomain = everyoo.com        //指定域名
myorigin = $mydomain        //指明发件人所在的域名
inet_interfaces =         //all指定postfix系统监听的网络接口
mydestination = $myhostname, localhost.$mydomain, localhost,$mydomain        //指定postfix接收邮件时收件人的域名 [使用虚拟域需要禁用]
mynetworks_style = host        //指定信任网段类型
mynetworks = 192.168.77.1/24, 127.0.0.0/8        //指定信任的客户端
relay_domains = $mydestination        //指定允许中转邮件的域名
alias_maps = hash:/etc/aliases        //设置邮件的别名

5)然后需要在/etc/init.d/目录下提供一个脚本来管理postfix的启动与停止:

[root@localhost /var/www/extsuite/extman]# vim /etc/init.d/postfix

把下面的内容放在/etc/init.d/postfix里面:

#!/bin/bash
#
# postfix      Postfix Mail Transfer Agent
#
# chkconfig: 2345 80 30
# description: Postfix is a Mail Transport Agent, which is the program 
#              that moves mail from one machine to another.
# processname: master
# pidfile: /var/spool/postfix/pid/master.pid
# config: /etc/postfix/main.cf
# config: /etc/postfix/master.cf

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ $NETWORKING = "no" ] && exit 3

[ -x /usr/sbin/postfix ] || exit 4
[ -d /etc/postfix ] || exit 5
[ -d /var/spool/postfix ] || exit 6

RETVAL=0
prog="postfix"

start() {
     # Start daemons.
     echo -n $"Starting postfix: "
        /usr/bin/newaliases >/dev/null 2>&1
     /usr/sbin/postfix start 2>/dev/null 1>&2 && success || failure $"$prog start"
     RETVAL=$?
     [ $RETVAL -eq 0 ] && touch /var/lock/subsys/postfix
        echo
     return $RETVAL
}

stop() {
  # Stop daemons.
     echo -n $"Shutting down postfix: "
     /usr/sbin/postfix stop 2>/dev/null 1>&2 && success || failure $"$prog stop"
     RETVAL=$?
     [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/postfix
     echo
     return $RETVAL
}

reload() {
     echo -n $"Reloading postfix: "
     /usr/sbin/postfix reload 2>/dev/null 1>&2 && success || failure $"$prog reload"
     RETVAL=$?
     echo
     return $RETVAL
}

abort() {
     /usr/sbin/postfix abort 2>/dev/null 1>&2 && success || failure $"$prog abort"
     return $?
}

flush() {
     /usr/sbin/postfix flush 2>/dev/null 1>&2 && success || failure $"$prog flush"
     return $?
}

check() {
     /usr/sbin/postfix check 2>/dev/null 1>&2 && success || failure $"$prog check"
     return $?
}

restart() {
     stop
     start
}

# See how we were called.
case "$1" in
  start)
     start
     ;;
  stop)
     stop
     ;;
  restart)
     stop
     start
     ;;
  reload)
     reload
     ;;
  abort)
     abort
     ;;
  flush)
     flush
     ;;
  check)
     check
     ;;
  status)
       status master
     ;;
  condrestart)
     [ -f /var/lock/subsys/postfix ] && restart || :
     ;;
  *)
     echo $"Usage: $0 {start|stop|restart|reload|abort|flush|check|status|condrestart}"
     exit 1
esac

exit $?

为脚本添加执行权限,并将服务添加到开机启动项中:

[root@localhost /var/www/extsuite/extman]# chmod +x /etc/init.d/postfix
[root@localhost /var/www/extsuite/extman]# chkconfig --add postfix
[root@localhost /var/www/extsuite/extman]# chkconfig postfix on
[root@localhost /var/www/extsuite/extman]# chown postfix.postfix -R /var/lib/postfix/
[root@localhost /var/www/extsuite/extman]# chown postfix.postfix /var/spool/ -R

3. 安装dovecot

yum安装:

[root@localhost ~]# yum install -y dovecot dovecot-mysql

配置dovecot:

[root@localhost ~]# cd /etc/dovecot/
[root@localhost dovecot]# vim dovecot.conf     //直接在配置文件最后添加即可
protocols = imap pop3
!include conf.d/*.conf
listen = *
base_dir = /var/run/dovecot/
[root@localhost dovecot]# cd conf.d/
[root@localhost conf.d]# vim 10-auth.conf
disable_plaintext_auth = no
[root@localhost conf.d]# vim 10-mail.conf
mail_location = maildir:~/Maildir
mail_location = maildir:/var/mailbox/%d/%n/Maildir
mail_privileged_group = mail
[root@localhost conf.d]# vim 10-ssl.conf
ssl = no
[root@localhost conf.d]# vim 10-logging.conf 
log_path = /var/log/dovecot.log
info_log_path = /var/log/dovecot.info
log_timestamp = "%Y-%m-%d %H:%M:%S "
[root@localhost conf.d]# cp auth-sql.conf.ext auth-sql.conf
[root@localhost conf.d]# vim auth-sql.conf
passdb {  
    driver = sql        

    # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext  
    args = /etc/dovecot/dovecot-sql.conf.ext
}

userdb {  
    driver = sql  
    args = /etc/dovecot/dovecot-sql.conf.ext
}

编辑dovecot通过mysql认证的配置文件:

[root@localhost conf.d]# vim /etc/dovecot-mysql.conf
driver = mysql
connect = host=localhost dbname=extmail user=extmail password=extmail
default_pass_scheme = CRYPT
password_query = SELECT username AS user,password AS password FROM mailbox WHERE username = '%u'
user_query = SELECT maildir, uidnumber AS uid, gidnumber AS gid FROM mailbox WHERE username = '%u'

4. 安装courier-authlib

下载解压并编译:

[root@localhost ~]# cd /usr/local/src/
[root@localhost /usr/local/src]#  wget https://sourceforge.net/projects/courier/files/authlib/0.66.2/courier-authlib-0.66.2.tar.bz2
[root@localhost /usr/local/src]# tar -jxvf courier-authlib-0.66.2.tar.bz2
[root@localhost /usr/local/src]# cd courier-authlib-0.66.2
[root@localhost /usr/local/src/courier-authlib-0.66.2]# ./configure --prefix=/usr/local/courier-authlib     --sysconfdir=/etc     --without-authpam     --without-authshadow     --without-authvchkpw     --without-authpgsql     --with-authmysql     --with-mysql-libs=/usr/lib64/mysql     --with-mysql-includes=/usr/include/mysql     --with-redhat     --with-authmysqlrc=/etc/authmysqlrc     --with-authdaemonrc=/etc/authdaemonrc     --with-mailuser=postfix
[root@localhost /usr/local/src/courier-authlib-0.66.2]# make && makeinstall

编译过程中发生了一个错误:

configure: error: The Courier Unicode Library 1.2 appears not to be installed. You may need to install a separate development subpackage, in addition to the main package

这是因为Courier Unicode Library没有安装,我们下载courier-unicode-1.2并编译安装:

[root@localhost ~]# wget https://sourceforge.net/projects/courier/files/courier-unicode/1.2/courier-unicode-1.2.tar.bz2
[root@localhost ~]# tar jxvf courier-unicode-1.2.tar.bz2 
[root@localhost ~]# cd courier-unicode-1.2
[root@localhost courier-unicode-1.2]# ./configure
[root@localhost courier-unicode-1.2]# make && make install

完成Courier Unicode Library的安装后,倒回去再次编译courier-authlib就没问题了

配置courier-authlib:

[root@localhost  courier-authlib-0.66.2]# chmod 755 /usr/local/courier-authlib/var/spool/authdaemon
[root@localhost  courier-authlib-0.66.2]# cp /etc/authdaemonrc.dist  /etc/authdaemonrc
[root@localhost  courier-authlib-0.66.2]# cp /etc/authmysqlrc.dist  /etc/authmysqlrc
[root@localhost  courier-authlib-0.66.2]# vim /etc/authdaemonrc      //配置文件里的验证方法比较多,我们这里只使用authmysql
authmodulelist="authmysql"
authmodulelistorig="authmysql"
[root@localhost  courier-authlib-0.66.2]# vim /etc/authmysqlrc     //直接添加到配置文件尾部,然后去上面将响应系统默认的注视掉,或者删除即可
MYSQL_SERVER            localhost
MYSQL_USERNAME          extmail
MYSQL_PASSWORD          extmail
MYSQL_SOCKET            /var/lib/mysql/mysql.sock
MYSQL_PORT               3306
MYSQL_DATABASE          extmail
MYSQL_USER_TABLE        mailbox
MYSQL_CRYPT_PWFIELD     password
DEFAULT_DOMAIN          test.com
MYSQL_UID_FIELD         '2525'
MYSQL_GID_FIELD         '2525'
MYSQL_LOGIN_FIELD       username
MYSQL_HOME_FIELD        concat('/var/mailbox/',homedir)
MYSQL_NAME_FIELD        name
MYSQL_MAILDIR_FIELD     concat('/var/mailbox/',maildir)

courier-authlib添加服务启动脚本及其他:

[root@localhost  courier-authlib-0.66.2]# cp courier-authlib.sysvinit /etc/init.d/courier-authlib
[root@localhost  courier-authlib-0.66.2]# chmod +x /etc/init.d/courier-authlib
[root@localhost  courier-authlib-0.66.2]# chkconfig --add courier-authlib
[root@localhost  courier-authlib-0.66.2]# chkconfig courier-authlib on
[root@localhost  courier-authlib-0.66.2]# echo "/usr/local/courier-authlib/lib/courier-authlib" >> /etc/ld.so.conf.d/courier-authlib.conf
[root@localhost  courier-authlib-0.66.1]# ldconfig
[root@localhost  courier-authlib-0.66.1]# service courier-authlib start
Starting Courier authentication services: authdaemond

smtp以及虚拟用户相关的设置:

[root@localhost ~]# vim /usr/lib64/sasl2/smtpd.conf    //文件不存在,要自己创建
pwcheck_method: authdaemond
log_level: 3
mech_list: PLAIN LOGIN
authdaemond_path:/usr/local/courier-authlib/var/spool/authdaemon/socket
[root@localhost ~]# vim /etc/postfix/main.cf
##postfix支持SMTP##
smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = ''
smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination
broken_sasl_auth_clients=yes
smtpd_client_restrictions = permit_sasl_authenticated
smtpd_sasl_security_options = noanonymous
##postfix支持虚拟用户##
virtual_mailbox_base = /var/mailbox
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf   //这里的配置文件需在后面extman
里复制过来
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf
virtual_alias_domains =
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf
virtual_uid_maps = static:2525
virtual_gid_maps = static:2525
virtual_transport = virtual

5. 安装extmail

下载extmail和extman:

[root@localhost ~]# cd /usr/local/src/
[root@localhost /usr/local/src]# wget http://7xivyw.com1.z0.glb.clouddn.com/extmail-1.2.tar.gz
[root@localhost /usr/local/src]# wget http://7xivyw.com1.z0.glb.clouddn.com/extman-1.1.tar.gz

创建站点目录并解压、重命名extmail包:

[root@localhost /usr/local/src]# mkdir -p /var/www/extsuite
[root@localhost /usr/local/src]# tar -zxvf extmail-1.2.tar.gz -C /var/www/extsuite/
[root@localhost /usr/local/src]# mv /var/www/extsuite/extmail-1.2/ /var/www/extsuite/extmail

更改extmail的配置文件:

[root@localhost ~]# cd /var/www/extsuite/extmail
[root@localhost extmail]# cp webmail.cf.default webmail.cf
[root@localhost extmail]# vim webmail.cf
SYS_SESS_DIR = /tmp/extmail
SYS_UPLOAD_TMPDIR = /tmp/extmail/upload
SYS_USER_LANG = zh_CN
SYS_MIN_PASS_LEN = 8
SYS_MAILDIR_BASE = /var/mailbox
SYS_MYSQL_USER = extmail
SYS_MYSQL_PASS = extmail
SYS_MYSQL_DB = extmail
SYS_MYSQL_HOST = localhost
SYS_MYSQL_SOCKET = /var/lib/mysql/mysql.sock
SYS_MYSQL_TABLE = mailbox
SYS_MYSQL_ATTR_USERNAME = username
SYS_MYSQL_ATTR_DOMAIN = domain
SYS_MYSQL_ATTR_PASSWD = password
SYS_AUTHLIB_SOCKET = /usr/local/courier-authlib/var/spool/authdaemon/socket

建立临时文件目录与session目录,并更改权限:

[root@localhost extmail]# mkdir -p /tmp/extmail/upload
[root@localhost extmail]# chown -R postfix.postfix /tmp/extmail/

6. 安装extman

回到extman的下载目录下,解压extman包:

[root@localhost ~]# cd /usr/local/src/
[root@localhost /usr/local/src]# tar -zxvf extman-1.1.tar.gz -C /var/www/extsuite/
[root@localhost /usr/local/src]# cd /var/www/extsuite/
[root@localhost /var/www/extsuite]# mv extman-1.1/ extman

拷贝extman的配置文件:

[root@localhost /var/www/extsuite]# cd extman/
[root@localhost /var/www/extsuite/extman]# cp webman.cf.default webman.cf

更改cgi目录的属主属组:

[root@localhost /var/www/extsuite/extman]# chown -R postfix.postfix /var/www/extsuite/extman/cgi/
[root@localhost /var/www/extsuite/extman]# chown -R postfix.postfix /var/www/extsuite/extmail/cgi/

导入数据库:

由于数据库不能识别TYPE=MyISAM,所以这里直接导入会出错,需要先编辑extmail.sql数据库文件,将文件中的TYPE=MyISAM更改为ENGINE=MyISAM,共有五处修改:

[root@localhost /var/www/extsuite/extman]# vim docs/extmail.sql
:% s/TYPE/ENGINE/g

我这里导入数据的时候发生了一个错误,提示找不到mysql.sock文件:

[root@localhost /var/www/extsuite/extman]# mysql -uroot < docs/extmail.sql
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
[root@localhost /var/www/extsuite/extman]# ls /var/lib/mysql/mysql.sock
ls: 无法访问/var/lib/mysql/mysql.sock: 没有那个文件或目录

解决:然后我去查看了一下/etc/my.cnf文件,发现没问题,socket参数指向的也是 /var/lib/mysql/mysql.sock 这个路径,于是我就重启了mysql服务,然后再尝试就没有报找不到mysql.sock文件的错误了,但是报了另一个错误:

[root@localhost /var/www/extsuite/extman]# mysql -uroot < docs/extmail.sql
ERROR 1364 (HY000) at line 31: Field 'ssl_cipher' doesn't have a default value
[root@localhost /var/www/extsuite/extman]# 

这错误的意思是:字段 ‘ssl密码’ 没有默认值

于是又得去查看一下/etc/my.cnf文件,然后把sql_mode参数给注释掉:

未分类

接着重启mysql服务后,继续导入数据,这次就没问题了:

[root@localhost /var/www/extsuite/extman]# !service
service mysqld restart
Redirecting to /bin/systemctl restart  mysqld.service
[root@localhost /var/www/extsuite/extman]# mysql -uroot < docs/extmail.sql
[root@localhost /var/www/extsuite/extman]# mysql -uroot < docs/init.sql

导入数据成功后再次修改/etc/my.cnf文件,把刚刚注释的那行给去掉注释,不去掉的话,mysql服务可能会出现不能启动的问题:

未分类

数据导入成功后,登录mysql,创建一个mysql数据库用户extmail并授予权限:

[root@localhost /var/www/extsuite/extman]# mysql -uroot
mysql> GRANT ALL ON extmail.* to extmail@'%' identified by 'extmail';      //我这里是直接授予全部权限在任意的IP地址上了,实际情况根据需求而定
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql>

复制之前提到的配置文件:

[root@localhost ~]# cd /var/www/extsuite/extman/docs/
[root@localhost /var/www/extsuite/extman/docs]# cp mysql_virtual_* /etc/postfix/

为extman创建临时目录:

[root@localhost /var/www/extsuite/extman/docs]# mkdir /tmp/extman
[root@localhost /var/www/extsuite/extman/docs]# chown -R postfix.postfix /tmp/extman/

启动postfix、dovecot、saslauthd服务,并查看进程是否正常:

[root@localhost /var/www/extsuite/extman]# service postfix start
Starting postfix (via systemctl):                          [  确定  ]
[root@localhost /var/www/extsuite/extman]# ps aux |grep postfix
root      63586  0.0  0.1  95392  2160 ?        Ss   01:29   0:00 /usr/libexec/postfix/master -w
postfix   63587  0.0  0.2  95448  3808 ?        S    01:29   0:00 pickup -l -t unix -u
postfix   63588  0.0  0.2  95496  3816 ?        S    01:29   0:00 qmgr -l -t unix -u
root      63592  0.0  0.0 112680   976 pts/0    S+   01:33   0:00 grep --color=auto postfix
[root@localhost /var/www/extsuite/extman]#  ss -tnluo | grep :25
tcp    LISTEN     0      100       *:25                    *:*             
[root@localhost /var/www/extsuite/extman]# service dovecot start
Redirecting to /bin/systemctl start  dovecot.service
[root@localhost /var/www/extsuite/extman]# ps aux |grep dovecot
root      63834  0.3  0.0  15652  1484 ?        Ss   02:15   0:00 /usr/sbin/dovecot -F
dovecot   63837  0.0  0.0   9320  1012 ?        S    02:15   0:00 dovecot/anvil
root      63838  0.0  0.0   9448  1164 ?        S    02:15   0:00 dovecot/log
root      63840  0.0  0.1  12464  2196 ?        S    02:15   0:00 dovecot/config
root      63842  0.0  0.0 112680   972 pts/0    S+   02:15   0:00 grep --color=auto dovecot    
[root@localhost /var/www/extsuite/extman]# systemctl start saslauthd
[root@localhost /var/www/extsuite/extman]# ps aux |grep saslauthd
root      63131  0.0  0.0  69648   916 ?        Ss   01:19   0:00 /usr/sbin/saslauthd -m /run/saslauthd -a pam
root      63132  0.0  0.0  69648   676 ?        S    01:19   0:00 /usr/sbin/saslauthd -m /run/saslauthd -a pam
root      63133  0.0  0.0  69648   676 ?        S    01:19   0:00 /usr/sbin/saslauthd -m /run/saslauthd -a pam
root      63134  0.0  0.0  69648   676 ?        S    01:19   0:00 /usr/sbin/saslauthd -m /run/saslauthd -a pam
root      63135  0.0  0.0  69648   676 ?        S    01:19   0:00 /usr/sbin/saslauthd -m /run/saslauthd -a pam
root      63144  0.0  0.0 112680   972 pts/0    S+   01:20   0:00 grep --color=auto saslauthd
[root@localhost /var/www/extsuite/extman]# ps aux |grep courier-authlib
root      61661  0.0  0.0   4316   444 ?        S    00:07   0:00 /usr/local/courier-authlib/sbin/courierlogger -pid=/usr/local/courier-authlib/var/spool/authdaemon/pid -start /usr/local/courier-authlib/libexec/courier-authlib/authdaemond
root      61662  0.0  0.0  35512  1796 ?        S    00:07   0:00 /usr/local/courier-authlib/libexec/courier-authlib/authdaemond
root      61663  0.0  0.0  35512   468 ?        S    00:07   0:00 /usr/local/courier-authlib/libexec/courier-authlib/authdaemond
root      61664  0.0  0.0  35512   468 ?        S    00:07   0:00 /usr/local/courier-authlib/libexec/courier-authlib/authdaemond
root      61665  0.0  0.0  35512   468 ?        S    00:07   0:00 /usr/local/courier-authlib/libexec/courier-authlib/authdaemond
root      61666  0.0  0.0  35512   468 ?        S    00:07   0:00 /usr/local/courier-authlib/libexec/courier-authlib/authdaemond
root      61667  0.0  0.0  35512   468 ?        S    00:07   0:00 /usr/local/courier-authlib/libexec/courier-authlib/authdaemond
root      63660  0.0  0.0 112680   980 pts/0    S+   02:00   0:00 grep --color=auto courier-authlib

7. 测试

测试虚拟用户:

[root@localhost courier-authlib-0.66.2]# /usr/local/courier-authlib/sbin/authtest -s login postmaster@extmail.org extmail
Authentication succeeded.                //显示这个表示成功,测试时使用的是postmaster@extmail.org,因为我们导入的数据库init.sql里面自带了这个。
Authenticated: postmaster@extmail.org  (uid 2525, gid 2525)
Home Directory: /var/mailbox/extmail.org/postmaster  //这里需要注意/var/mailbox这个目录现在我们还没有创建,后面web访问的时候如果没有会报错,所以提前创建。
                    Maildir: /var/mailbox/extmail.org/postmaster/Maildir/
                    Quota: (none)
            Encrypted Password: $1$phz1mRrj$3ok6BjeaoJYWDBsEPZb5C0
                Cleartext Password: extmail
                    Options: (none)
[root@localhost courier-authlib-0.66.2]# mkdir /var/mailbox
[root@localhost courier-authlib-0.66.2]# chown -R postfix.postfix /var/mailbox/

测试smtp发信:

[root@localhost ~]# printf   "postmaster@extmail.org" | openssl base64
cG9zdG1hc3RlckBleHRtYWlsLm9yZw==
[root@localhost ~]#  printf   "extmail" | openssl base64
ZXh0bWFpbA==
[root@localhost ~]# telnet localhost 25
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail.daen.com ESMTP Postfix
auth login
334 VXNlcm5hbWU6
cG9zdG1hc3RlckBleHRtYWlsLm9yZw==
334 UGFzc3dvcmQ6
ZXh0bWFpbA==
235 2.7.0 Authentication successful     //成功
quit
221 2.0.0 Bye
Connection closed by foreign host.

8. 启动nginx实现web访问

nginx本身并不能解析cgi,extmail自带了解析cgi的程序,但是有些地方需要修改下:

[root@localhost ~]# vim /var/www/extsuite/extmail/dispatch-init
SU_UID=postfix
SU_GID=postfix

启动dispatch-init:

[root@localhost ~]# /var/www/extsuite/extmail/dispatch-init start
Starting extmail FCGI server...
[root@localhost ~]# /var/www/extsuite/extman/daemon/cmdserver -v -d 
loaded ok

添加nginx虚拟主机:

vim /etc/nginx/conf.d/extmail.conf

文件内容如下:

server {
   listen       8080;
   server_name  mail.everyoo.com;
   index index.html index.htm index.php index.cgi;
   root  /var/www/extsuite/extmail/html/;
   location /extmail/cgi/ {
             fastcgi_pass          127.0.0.1:8888;
             fastcgi_index         index.cgi;
             fastcgi_param  SCRIPT_FILENAME   /var/www/extsuite/extmail/cgi/$fastcgi_script_name;
             include               fcgi.conf;
        }
        location  /extmail/  {
             alias  /var/www/extsuite/extmail/html/;
        }
        location /extman/cgi/ {
             fastcgi_pass          127.0.0.1:8888;
             fastcgi_index         index.cgi;
             fastcgi_param  SCRIPT_FILENAME   /var/www/extsuite/extman/cgi/$fastcgi_script_name;
             include            fcgi.conf;
        }
        location /extman/ {
             alias  /var/www/extsuite/extman/html/;
        }
      access_log  /var/log/extmail_access.log;
}

创建fcgi.conf文件:

vim /etc/nginx/fcgi.conf

文件内容如下:

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;
fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

安装Unix::Syslog:

[root@localhost ~]# cd /usr/local/src/
[root@localhost /usr/local/src]# wget http://www.cpan.org/authors/id/M/MH/MHARNISCH/Unix-Syslog-1.1.tar.gz
[root@localhost /usr/local/src]# tar zxvf Unix-Syslog-1.1.tar.gz 
[root@localhost /usr/local/src]# cd Unix-Syslog-1.1
[root@localhost /usr/local/src/Unix-Syslog-1.1]# perl Makefile.PL
[root@localhost /usr/local/src/Unix-Syslog-1.1]# make && make install

启动nginx,并检查进程和监听端口是否正常:

[root@localhost ~]# service nginx start
Redirecting to /bin/systemctl start  nginx.service
[root@localhost ~]# ps aux |grep nginx
root      72338  0.0  0.1 122892  2296 ?        Ss   03:22   0:00 nginx: master process /usr/sbin/nginx
nginx     72339  0.0  0.1 123336  3192 ?        S    03:22   0:00 nginx: worker process
nginx     72340  0.0  0.1 123336  3192 ?        S    03:22   0:00 nginx: worker process
nginx     72341  0.0  0.1 123336  3192 ?        S    03:22   0:00 nginx: worker process
nginx     72342  0.0  0.1 123336  3192 ?        S    03:22   0:00 nginx: worker process
root      72344  0.0  0.0 112680   976 pts/0    S+   03:22   0:00 grep --color=auto nginx
[root@localhost ~]# netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:110             0.0.0.0:*               LISTEN      63834/dovecot       
tcp        0      0 0.0.0.0:143             0.0.0.0:*               LISTEN      63834/dovecot       
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      72338/nginx: master 
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      72338/nginx: master 
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1482/sshd           
tcp        0      0 127.0.0.1:8888          0.0.0.0:*               LISTEN      64100/dispatch.fcgi 
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN      64328/master        
tcp6       0      0 :::3306                 :::*                    LISTEN      62442/mysqld        
tcp6       0      0 :::80                   :::*                    LISTEN      72338/nginx: master 
tcp6       0      0 :::22                   :::*                    LISTEN      1482/sshd           
[root@localhost ~]# 

然后到windows上访问你服务器IP的8080端口:

未分类

extman的登录账户为root@extmail.org密码为extmail123,首次使用需要先添加域,添加之后再修改域,改为可自由注册,再注册用户就可以登录发邮件了:

未分类

supervisor集中化管理web工具cesi

linux进程管理器supervisor是会经常被用到的,但服务器多了之后,每个服务器的进程也不方便管理。同时,supervisor自带的web界面比较简陋,所以尝试了一下官网推荐的一些第三方开源软件,推荐一下这个cesi。

最终效果如下:

未分类

未分类

1. 首先是关于supervisor

通过apt或pip安装都可以

apt-get install supervisor
#pip install supervisor
echo_supervisord_conf > /etc/supervisor/supervisord.conf

关于supervisor配置文件

[unix_http_server]
#这里配置是否用unix socket通信来让supervisor与supervisorctl做通信

[inet_http_server]
#这里是用的http的方式做通信

[supervisorctl]
#这里选择supervisorctl到底用以上两种中的哪种方式来与supervisor通信,选择一种即可,记得填写密码

[program:pro_name]
stdout_logfile = {path}
redirect_stderr = true  ;让stderr也写入stdout中

常用操作

supervisorctl reload #重启加载supervisor配置文件
supervisorctl update #只增加新增的配置文件

2. 关于cesi

项目地址:https://github.com/Gamegos/cesi

安装cesi

apt-get install sqlite3 python python-flask
git clone https://github.com/Gamegos/cesi
cd cesi
sqlite3 ./userinfo.db < userinfo.sql
cp cesi.conf /etc/cesi.conf

配置cesi.conf,我的配置如下,一看就懂了

[node:118]
username = ***
password = ***
host = 127.0.0.1
port = 9001

[node:calmkart]
username = ***
password = ***
host = 45.76.71.69
port = 9001

[node:121]
username = ***
password = ***
host = *.*.*.121
port = 9001

[environment:my_env]
members = 118,calmkart,121

[cesi]
database = /root/cesi/userinfo.db
activity_log = /var/log/cesi.log
host = 0.0.0.0

用supervisor运行cesi,配置文件如下

[program:cesi]
directory = /root/cesi/cesi/
command = python web.py
autostart = true
startsecs = 5
autorestart = true
startretries = 3
user = root
redirect_stderr = true
stdout_logfile = /var/log/cesi1.log

开启任务

supervisorctl start cesi

默认账号密码:admin,admin
端口需要改的自己去web.py里面改

you get it

本测试服地址:
http://cesi.calmkart.com

此外,其他常用的supervisor相关第三方工具还有

  • suponoff
  • gosuv

服务器上的WEB项目反复出现MySQL数据库连接失败解决办法

  • 一个原因是没有关闭MySQL的定时任务计划,每天凌晨MySQL会默认运行一个自动更新的定时任务计划,如果没有关闭,就会自动断开连接。 解决办法: 1、这是一个基本的权限问题。去MySQL安装目录下,右键单击MySQL文件夹,进入安全选项卡下,单击“编辑用户组”,在“组和用户”选择你的电脑的用户,选择允许的情况下所有的项,应用并关闭。 2、这是一个Windows的任务计划服务,删除即可,开始右键/计算机管理/任务计划程序/任务计划程序库/MySQL/Installer/ManifestUpdate,右键单击并选择“禁用”。

  • 另一个原因是MySQL数据库的数据库连接有生存期限制,如果在规定时间内没有操作数据库连接对象,连接就会被关闭。也就是常说的MySQL的8小时问题

  • MySQL服务器默认连接的“wait_timeout”是8小时,也就是说一个Connection空闲超过8个小时,MySQL将自动断开该 Connection。但是数据库连接池并不知道连接已经断开了,如果程序正巧使用到这个已经断开的连接,程序就会报错误。

先来了解一下数据库连接池:

  • 用JAVA代码操作数据库需要数据库连接对象,一个用户至少要用到一个连接。现在假设有成千上百万个用户,就要创建十分巨大数量的连接对象,这会使数据库承受极大的压力,为了解决这种现象,一种技术出现了,这就是数据库连接池。

  • 所谓数据库连接池,可以看作在用户和数据库之间创建一个“池”,这个池中有若干个连接对象,当用户想要连接数据库,就要先从连接池中获取连接对象,然后操作数据库。一旦连接池中的连接对象被拿光了,下一个想要操作数据库的用户必须等待,等待其他用户释放连接对象,把它放回连接池中,这时候等待的用户才能获取连接对象,从而操作数据库。

  • 以下是两个连接池的简单实现~ 代码来自:
    https://www.cnblogs.com/vmax-tam/p/4158802.html 代码来自:https://www.cnblogs.com/xiaotiaosi/p/6398371.html

代码我就不搬过来了,重点讲讲如何解决问题

方法一:修改数据库的等待时间

1、首先重启MySQL服务,开始→右键→计算机管理→服务,找到MySQL的服务,重新启动

2、设置MySQL最大等待时间,运行MySQL 5.7 Command Line Client,输入密码后,拷贝运行以下代码段:

show variables LIKE '%timeout%';
show global variables LIKE '%timeout%';
set global wait_timeout=2147483;
set wait_timeout=2147483;
set global interactive_timeout=31536000;
set interactive_timeout=31536000;

3、重启Tomcat

方法二:让自己编写的数据库连接池代码生成数据库连接对象时,增加一个自动刷新的功能,间隔时间自动生成一个新的数据库连接对象。也就是定期使用连接池内的连接,使得它们不会因为闲置超时而被 MySQL 断开。

方法三:连接数据库的时候加上autoReconnect=true这个参数:

jdbc:mysql://localhost:3306/accounant?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true

方法四:减少连接池内连接的生存周期,使之小于上述项中所设置的“wait_timeout”的值。

方法五:将“testOnBorrow”设置为false,而将“testWhileIdle”设置为true,再设置好“testBetweenEvictionRunsMillis”值(小于8小时)。那些被MySQL关闭的连接就不会被清除出去,避免“8小时问题”。 例:http://www.totcms.com/html/201602-29/20160229114145.htm

Apache官方文档给出的配置示例可参见:http://tomcat.apache.org/tomcat-9.0-doc/