Python–Virtualenv简明教程

  • virtualenv是一种工具来创建独立的Python环境。

  • virtualenv通过创建独立Python开发环境的工具, 来解决依赖、版本以及间接权限
    问题. 比如一个项目依赖Django1.3 而当前全局开发环境为Django1.7, 版本跨度过大, 导致不兼容使项目无法正在运行, 使用virtualenv可以解决这些问题.

  • virtualenv创建一个拥有自己安装目录的环境, 这个环境不与其他虚拟环境共享库, 能够方便的管理python版本和管理python库

1. 安装Virtualenv

使用pip安装Virtualenv, 使用过python的都应该知道pip包管理神器吧, 即使不知道, 网站也有大把的教程, 不过推荐查看官方安装指南

$ pip install virtualenv
//或者由于权限问题使用sudo临时提升权限
$ sudo pip install virtualenv

2. virtualenv基本使用

现在开始使用virtualenv管理python环境

➜  Test git:(master) ✗ virtualenv ENV  #创建一个名为ENV的目录, 并且安装了ENV/bin/python, 创建了lib,include,bin目录,安装了pip
New python executable in 
Installing setuptools, pip...done.
➜  Test git:(master) ✗ cd ENV
➜  ENV git:(master) ✗ ll
drwxr-xr-x  14 andrew_liu  staff  476 12  8 08:49 bin
drwxr-xr-x   3 andrew_liu  staff  102 12  8 08:49 include
drwxr-xr-x   3 andrew_liu  staff  102 12  8 08:49 lib
  • lib,所有安装的python库都会放在这个目录中的lib/pythonx.x/site-packages/下
  • bin,bin/python是在当前环境是使用的python解释器

如果在命令行中运行virtualenv –system-site-packages ENV, 会继承/usr/lib/python2.7/site-packages下的所有库, 最新版本virtualenv把把访问全局site-packages作为默认行为
default behavior.

2.1 激活virtualenv

#ENV目录下使用如下命令
➜  ENV git:(master) ✗ source ./bin/activate  #激活当前virtualenv
(ENV)➜  ENV git:(master) ✗ #注意终端发生了变化
#使用pip查看当前库
(ENV)➜  ENV git:(master) ✗ pip list
pip (1.5.6)
setuptools (3.6)
wsgiref (0.1.2) #发现在只有这三个
pip freeze  #显示所有依赖
pip freeze > requirement.txt  #生成requirement.txt文件
pip install -r requirement.txt  #根据requirement.txt生成相同的环境

2.2 关闭virtualenv

使用下面命令

$ deactivate

2.3 指定python版本

可以使用-p PYTHON_EXE选项在创建虚拟环境的时候指定python版本

#创建python2.7虚拟环境
➜  Test git:(master) ✗ virtualenv -p /usr/bin/python2.7 ENV2.7
Running virtualenv with interpreter /usr/bin/python2.7
New python executable in ENV2.7/bin/python
Installing setuptools, pip...done.
#创建python3.4虚拟环境
➜  Test git:(master) ✗ virtualenv -p /usr/local/bin/python3.4 ENV3.4
Running virtualenv with interpreter /usr/local/bin/python3.4
Using base prefix '/Library/Frameworks/Python.framework/Versions/3.4'
New python executable in ENV3.4/bin/python3.4
Also creating executable in ENV3.4/bin/python
Installing setuptools, pip...done.

到此已经可以解决python版本冲突问题和python库不同版本的问题

3. 其他

3.1 生成可打包环境

某些特殊需求下,可能没有网络, 我们期望直接打包一个ENV, 可以解压后直接使用, 这时候可以使用virtualenv -relocatable指令将ENV修改为可更改位置的ENV

#对当前已经创建的虚拟环境更改为可迁移
➜  ENV3.4 git:(master) ✗ virtualenv --relocatable ./
Making script ./bin/easy_install relative
Making script ./bin/easy_install-3.4 relative
Making script ./bin/pip relative
Making script ./bin/pip3 relative
Making script ./bin/pip3.4 relative

3.2 获得帮助

$ virtualenv -h

当前的ENV都被修改为相对路径, 可以打包当前目录, 上传到其他位置使用。

这并不能使虚拟环境跨平台使用。

python3.6使用sqlalchemy读取mysql中的数据并进行多进程并发处理

1. 介绍 SQLALChemy

SQLALChemy 是一个 python 的 ORM(Object Relational Mapper) 框架,开发人员可以快速开发操作数据库的程序,
它提供完整的数据库访问层,提供高性能的数据库访问能力。
它支持 SQLite、MySQL、Postgres、Oracle 等常用的数据库访问

2. 安装 SQLAlChemy

pip install sqlalchemy

2.1 创建测试数据库

# 建立数据库
CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;

2.2 用 SQLALChemy 创建数据库表

2.2.1 程序关键点

  • 创建操作数据库的 engine,使用 pymysql 库访问 mysql 数据库
  • 创建操作数据库的 session,绑定到 engine 上
  • 从 Base 继承定义 User,Article 类,对应 mapping 到数据库的 member,article 表
  • 使用 session.create_all 创建数据库表结构
  • session.add_all 新增数据到数据库
  • session.commit 提交所有变更到数据库,此时可以再数据库中查询插入的数据
  • 查询数据使用 session.query 方法,也可以在后面连接使用 filter 进行条件过滤
#!/usr/bin/env python
# coding: utf-8


from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker


# 创建数据库 engine 使用 utf8 编码
eng = create_engine('mysql+pymysql://root:1@localhost:3306/test?charset=utf8')
Base = declarative_base()

# 创建 session 类,绑定engine
Session = sessionmaker(bind=eng)
session = Session()


class User(Base):
    '''
    用户类,对应数据库的 member 表
    '''
    __tablename__ = 'member'

    # 定义表字段
    mid = Column(Integer, primary_key=True)
    nickname = Column(String(50))
    email = Column(String(128))

    def __repl__(self):
        return '<User(name={}, email={}, nickname{}>'.format(mid,
                                                             email,
                                                             nickname)


class Article(Base):
    '''
    文章类,对应数据库中的 article 表
    '''
    __tablename__ = 'article'

    # 定义表字段
    arid = Column(Integer, primary_key=True)
    tags = Column(String(128))
    description = Column(String(256))
    title = Column(String(256))


def create_table():
    '''
    创建数据库表结构,导入初始数据
    '''
    # 创建表
    Base.metadata.create_all(eng)

    # 插入数据
    session.add_all([
        User(mid=1, nickname='测试数据 test hello', email='[email protected]'),
        User(mid=2, nickname='测试数据 china hello', email='[email protected]'),
        User(mid=3, nickname='测试数据 上海 hello', email='[email protected]'),
        User(mid=4, nickname='测试数据 北京 hello', email='[email protected]'),
        User(mid=5, nickname='测试数据 上海 hello', email='[email protected]'),
        User(mid=6, nickname='测试数据 山东 hello', email='[email protected]'),
        User(mid=7, nickname='测试数据 武夷山 hello', email='[email protected]'),
        User(mid=8, nickname='测试数据 黄山 hello', email='[email protected]'),

        Article(arid=1, tags='测试数据 test hello', title='销售额度', description='测试 test ok'),
        Article(arid=2, tags='测试数据 china hello', title='成功转型', description='测试 test ok'),
        Article(arid=3, tags='测试数据 上海 hello', title='蓝蓝的天上白云飘', description='测试 test ok'),
        Article(arid=4, tags='测试数据 背景 hello', title='在水一方', description='测试 test ok'),
        Article(arid=5, tags='测试数据 上海 hello', title='晴天,阴天,雨天,大风天', description='测试 test ok'),
        Article(arid=6, tags='测试数据 山东 hello', title='每年365天,每天24小时', description='测试 test ok'),
        Article(arid=7, tags='测试数据 武夷山 hello', title='高效工作的秘密', description='测试 test ok'),
        Article(arid=8, tags='测试数据 黄山 hello', title='战狼2', description='测试 test ok'),
    ]
    )

    # 提交到数据库
    session.commit()


def modify_data():
    '''
    测试修改数据
    '''

    # 查询用户表
    users = session.query(User).all()
    print(users)

    # 查询文章表
    # articles = session.query(Article).all()
    articles = session.query(Article).filter(Article.arid==2)
    print(articles)

    # 修改文章表
    articles[0].description = '程度,修改描述可以成功'
    print(session.dirty)

    # 提交到数据库
    session.commit()


if __name__ == '__main__':
    create_engine()
    # modify_data()

3. 多进程搜索程序

3.1 程序关键点

  • 创建 DbMgr 类,封装数据库访问操作
  • 用 sqlalchemy 从数据库获取 member,article 表中的数据
  • 使用 automap 自动反射出 member,article 表对应的类
  • 创建 Searcher 类,提供进程调用函数,用来查询符合条件的结果,并且提供进程执行完的回调展示方法
  • 创建 10 个进程的进程池
  • 循环获取用户输入,创建 searcher 对象,多进程并发执行过滤
  • 多进程调用使用 python multiprocessing
#!/usr/bin/env python
# coding: utf-8


import os


from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.automap import automap_base
from multiprocessing import Pool


class DbMgr():
    '''
    连接数据库,从数据库获取 用户表,文章表数据
    '''

    def __init__(self):
        eng = create_engine('mysql+pymysql://root:1@localhost/test?charset=utf8')

        # 使用 automap 自动反射出 member,article 表对应的类
        meta = MetaData()
        meta.reflect(eng, only=['member', 'article'])
        Base = automap_base(metadata=meta)
        Base.prepare()

        self._Member = Base.classes.member
        self._Article = Base.classes.article

        # 获取操作数据库的 session
        Session = sessionmaker(eng, autocommit=True)
        self._ses = Session()

    def get_data(self):
        '''
        查询用户表,文章表
        '''
        self._users = self._ses.query('"user"', self._Member.mid,
                                      self._Member.email,
                                      self._Member.nickname).all()

        self._articles = self._ses.query(self._Article).all()
        self._articles = [('ar', i.arid, i.title, i.tags, i.description)
                          for i in self._articles]

        return list(self._users) + list(self._articles)


class Searcher():
    '''
    进城处理函数,查找符合条件的结果,找到后返回结果
    '''
    def __init__(self, keyword):
        self._keyword = keyword

    def run(self, data):
        '''
        查找字符串
        '''
        try:
            if self._keyword in str(data):
                return 'ret: ' + str(os.getpid()) + '->' + str(data)
            else:
                return None
        except Exception as e:
            return e

    def callback(self, data):
        '''
        全部执行完后回调函数,展示结果
        '''
        try:
            for i in data:
                if i:
                    print('match: {}'.format(i))
        except Exception as e:
            print(e)


def main():
    # 从数据库读取数据
    mgr = DbMgr()

    # 创建过滤进程池
    pool = Pool(4)

    # 创建搜索器
    while True:
        keyword = input('n输入搜索词:  ')
        if keyword == 'q':
            break

        searcher = Searcher(keyword)

        # 从数据库获取数据
        data = mgr.get_data()
        res = pool.map_async(searcher.run,
                             data,
                             10,
                             callback=searcher.callback)

        # 等待所有进程执行完成
        res.wait()
        print('all done', res.successful())


if __name__ == '__main__':
    main()

3.2 程序运行

(py36env) servadmin@debian:~/test # python mysql2es.py

输入搜索词:  test
match: ret: 10013->('user', 1, '[email protected]', '测试数据 test hello')
match: ret: 10013->('user', 2, '[email protected]', '测试数据 china hello')
match: ret: 10013->('user', 3, '[email protected]', '测试数据 上海 hello')
match: ret: 10013->('user', 4, '[email protected]', '测试数据 背景 hello')
match: ret: 10013->('user', 5, '[email protected]', '测试数据 上海 hello')
match: ret: 10013->('user', 6, '[email protected]', '测试数据 山东 hello')
match: ret: 10013->('user', 7, '[email protected]', '测试数据 武夷山 hello')
match: ret: 10013->('user', 8, '[email protected]', '测试数据 黄山 hello')
match: ret: 10013->('ar', 1, '销售额度', '测试数据 test hello', '程度,修改描述可以成功')
match: ret: 10013->('ar', 3, '蓝蓝的天上白云飘', '测试数据 上海 hello', '测试 test ok')
match: ret: 10013->('ar', 4, '在水一方', '测试数据 背景 hello', '测 test ok')
match: ret: 10013->('ar', 5, '晴天,阴天,雨天,大风天', '测试数据 上海 hello', '测试 test ok')
match: ret: 10013->('ar', 6, '每年365天,每天24小时', '测试数据 山东 hello', '测试 test ok')
match: ret: 10013->('ar', 7, '高效工作的秘密', '测试数据 武夷山 hello', '测试 test ok')
match: ret: 10013->('ar', 8, '战狼2', '测试数据 黄山 hello', '测试 test ok')
all done True

输入搜索词:  转型
match: ret: 10013->('ar', 2, '成功转型', '测试数据 china hello', '程度,修改描述可以成功')
all done True

输入搜索词:  test
match: ret: 10013->('user', 1, '[email protected]', '测试数据 test hello')
match: ret: 10013->('user', 2, '[email protected]', '测试数据 china hello')
match: ret: 10013->('user', 3, '[email protected]', '测试数据 上海 hello')
match: ret: 10013->('user', 4, '[email protected]', '测试数据 背景 hello')
match: ret: 10013->('user', 5, '[email protected]', '测试数据 上海 hello')
match: ret: 10013->('user', 6, '[email protected]', '测试数据 山东 hello')
match: ret: 10013->('user', 7, '[email protected]', '测试数据 武夷山 hello')
match: ret: 10013->('user', 8, '[email protected]', '测试数据 黄山 hello')
match: ret: 10013->('ar', 1, '销售额度', '测试数据 test hello', '程度,修改描述可以成功')
match: ret: 10013->('ar', 3, '蓝蓝的天上白云飘', '测试数据 上海 hello', '测试 test ok')
match: ret: 10013->('ar', 4, '在水一方', '测试数据 背景 hello', '测试 test ok')
match: ret: 10013->('ar', 5, '晴天,阴天,雨天,大风天', '测试数据 上海 hello', '测试 test ok')
match: ret: 10013->('ar', 6, '每年365天,每天24小时', '测试数据 山东 hello', '测试 test ok')
match: ret: 10013->('ar', 7, '高效工作的秘密', '测试数据 武夷山 hello', '测试 test ok')
match: ret: 10013->('ar', 8, '战狼2', '测试数据 黄山 hello', '测试 test ok')
all done True

输入搜索词:

解决使用Flask-SQLAlchemy中出现的1366报错

最近在按照这本书学Flask,学到通过ORM方式操作数据库时遇到一个很奇怪的问题:

会报下面这个1366的错

...default.py:470: Warning: (1366, "Incorrect string value: 'xD6xD0xB9xFAxB1xEA...' for column 'VARIABLE_VALUE' at row 479")

未分类

奇怪的地方在于我表格里的数据全部都是英语

因为看到UTF编码,首先就想到是不是编码的问题,于是

  • 检查了自己的MySQL的配置
    没发现配置有问题,都是UTF-8编码

  • 网上搜了下相关资料
    发现除了MySQL中的编码配置之外,Python的编码也要是UTF-8,检查没发现问题 (如下图)
    另外就是在字符串前加上u,变成u’string’的形式,当然这招也没用

未分类

到此我比较郁闷,遂求助于程序员好友,他看/听完描述,马上就找到了最可疑之处 – MySQL驱动

的确,书中在创建数据库连接时,并没提到相关概念,但我之前再根据廖雪峰网站学MySQL操作时,是有这个步骤的

然后根据这个思路进行操作

  • 安装MySQL驱动(我升级过Python,所以要再装一遍)
    本想安装MySQL官方驱动mysql-connector-python的,然而官方目前只支持到3.4
    我又懒,所以就用了另一个驱动mysql-connector,也不知道是谁开发的……

  • 修改代码,把

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:password@localhost/database'

改成

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqlconnector://root:password@localhost/database'

至此,上述1366报错信息消失!

我推测是因为SQLAlchemy使用了默认的数据库驱动(按官方文档,是mysql-python)有问题,才导致此问题。

还望看到此文章的大神能验证一下我的说法。

linux下python守护进程编写和原理理解

编写了一个服务端程序,打开终端能直接运行程序,但是这样终端就被限制住了。如果ctrl+c退出或者关闭终端,那么服务端程序就会退出。于是就想着让这个服务端程序成为守护进程,像httpd、vsftpd、mysqld一样,一直在后端运行,不会受终端的影响。

守护进程英文为daemon,像httpd、mysqld、vsftpd最后个字母d其实就是表示daemon的意思。

守护进程的编写步骤:

  • fork子进程,而后父进程退出,此时子进程会被init进程接管。
  • 修改子进程的工作目录、创建新进程组和新会话、修改umask。
  • 子进程再次fork一个进程,这个进程可以称为孙子进程,而后子进程退出。
  • 重定向孙子进程的标准输入流、标准输出流、标准错误流到/dev/null。

完成上面的4个步骤,那么最终的孙子进程就称为守护进程。先看下代码,后面再分析下每个步骤的原因。

#!/usr/bin/env python
#coding=utf8

import os, sys, time

#产生子进程,而后父进程退出
pid = os.fork()
if pid > 0:
    sys.exit(0)

#修改子进程工作目录
os.chdir("/")
#创建新的会话,子进程成为会话的首进程
os.setsid()
#修改工作目录的umask
os.umask(0)

#创建孙子进程,而后子进程退出
pid = os.fork()
if pid > 0:
    sys.exit(0)

#重定向标准输入流、标准输出流、标准错误
sys.stdout.flush()
sys.stderr.flush()
si = file("/dev/null", 'r')
so = file("/dev/null", 'a+')
se = file("/dev/null", 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())

#孙子进程的程序内容
while True:
    time.sleep(10)
    f = open('/home/test.txt', 'a')
    f.write('hello')

上面的程序没有任何错误处理,但是不影响原理分析。如果要应用到项目里,还需完善。下面笔者谈下自己对每个步骤的理解。

1、fork子进程,父进程退出

通常,我们执行服务端程序的时候都会通过终端连接到服务器,成功连接后会加载shell环境,终端和shell都是进程,shell进程是终端进程的子进程,通过ps命令可以很容易的查看到。在这个shell环境下一开始执行的程序都是shell进程的子进程,自然会受到shell进程的影响。在程序里fork子进程后,父进程退出,对了shell进程来说,这个父进程就算执行完了,而产生的子进程会被init进程接管,从而也就脱离了终端的控制。

2、修改子进程的工作目录

子进程在创建的时候会继承父进程的工作目录,如果执行的程序是在u盘里的,就会导致u盘不能卸载。

3、创建新会话

使用setsid后,子进程就会成为新会话的首进程(session leader);子进程会成为新进程组的组长进程;子进程没有控制终端。

4、修改umask

由于umask会屏蔽权限,所以设定为0,这样可以避免读写文件时碰到权限问题。

5、fork孙子进程,子进程退出

经过上面几个步骤后,子进程会成为新的进程组老大,可以重新申请打开终端,为了避免这个问题,fork孙子进程出来。

6、重定向孙子进程的标准输入流、标准输出流、标准错误流到/dev/null

因为是守护进程,本身已经脱离了终端,那么标准输入流、标准输出流、标准错误流就没有什么意义了。所以都转向到/dev/null,就是都丢弃的意思。

centos 6.4安装supervisor守护golang进程

最近在鼓捣golang守护进程的实现,无意发现了supervisor这个有意思的东西。supervisor是一个unix的系统进程管理软件,可以用它来管理apache、nginx等服务,若服务挂了可以让它们自动重启。当然也可以用来实现golang的守护进程,下面描述下具体实现。

安装supervisor

基于centos 6.4。

supervisor使用python编写的,可以用easy_install安装。centos上默认有python的运行环境,安装起来就非常简单了。

$ sudo yum install python-setuptools
$ sudo easy_install supervisor

如果没有看到什么报错,那么就安装成功了,可以使用echo_supervisord_conf查看配置详情,而后生成配置文件。

$ sudo echo_supervisord_conf > /etc/supervisord.conf

golang http服务

先整一个简单的golang http服务

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello world")
    })

    err := http.ListenAndServe(":9090", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

直接运行这个程序会占用住终端,下面看看如何用supervisor来跑这个程序。

supervisor配置golang

编辑/etc/supervisord.conf,在最后增加运行程序设置

[program:golang-http-server]
command=/home/golang/simple_http_server
autostart=true
autorestart=true
startsecs=10
stdout_logfile=/var/log/simple_http_server.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=10
stdout_capture_maxbytes=1MB
stderr_logfile=/var/log/simple_http_server.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=10
stderr_capture_maxbytes=1MB

几个配置说明:

  • command:表示运行的命令,填入完整的路径即可。
  • autostart:表示是否跟随supervisor一起启动。
  • autorestart:如果该程序挂了,是否重新启动。
  • stdout_logfile:终端标准输出重定向文件。
  • stderr_logfile:终端错误输出重定向文件。

其余配置说明可以查看官方文档。

启动supervisor

$ sudo /usr/bin/supervisord -c /etc/supervisord.conf

如果出现什么问题,可以查看日志进行分析,日志文件路径/tmp/supervisord.log

tips:如果修改了配置文件,可以用kill -HUP重新加载配置文件

$ cat /tmp/supervisord.pid | xargs sudo kill -HUP

查看supervisor运行状态

$ supervisorctl
golang-http-server RUNNING pid 23307, uptime 0:02:55
supervisor>

输入help可以查看帮助

supervisor> help
default commands (type help ):
=====================================
add clear fg open quit remove restart start stop update
avail exit maintail pid reload reread shutdown status tail version

supervisor运行原理

supervisor运行后本身是守护进程,通过自身来管理相应的子进程,通过观察相应的进程状态就很明了了。

$ ps -ef | grep supervisord
root 23306 1 0 07:30 ? 00:00:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf
root 23331 23222 0 07:41 pts/0 00:00:00 grep supervisord

$ ps -ef | grep simple_http_server
root 23307 23306 0 07:30 ? 00:00:00 /home/golang/simple_http_server
root 23333 23222 0 07:41 pts/0 00:00:00 grep simple_http_server

可以很直观的看出golang simple_http_server进程是supervisord的子进程。

supervisor是否靠谱

supervisor的诞生已经10年了,现在是3+版本,所以放心使用吧。

搭建nginx gunicorn mysql环境部署django应用

说实在的第一次用服务器来部署django确实有点不知所措,上网查了一些资料,准备部署一个nginx+gunicorn+django+mysql的一个博客系统。

用户环境

  • 服务器:阿里云服务器ECS
  • 镜像系统:ubuntu16.04 64位
  • 准备建立:nginx+gunicorn+django+mysql的博客系统

首先登陆到云服务器,也不知道先干什么,那么先更新一下吧

sudo apt-get update
sudo apt-get upgrade

中间可能会询问一些问题,输入y即可。

nginx

简单介绍一下,nginx是一个轻量级的高性能的web服务器,反向代理服务器以及邮件服务器。首先来配置一下nginx

sudo apt-get install nginx

一阵等待之后nginx就安装好了,首先来看一下nginx.conf配置

# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        ##
        # Gzip Settings
        ##

        gzip on;
        gzip_disable "msie6";

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

这一段代码是安装nginx后的nginx的默认配置,关于nginx的配置优化后续再讲,现在重要的是将服务器给搭建起来,首先简单的讲解一下

在默认配置中,nginx总共分为四个部分:

1、全局设置,主要用来设置nginx的相关配置,比如设定nginx运行的用户和用户组、运行的进程数、运行的文件等。该部分设置在{}之外。

2、events设置,从字面理解,events就是事件的意思,这里是设置事件的相关配置,如事件处理方式是epoll还是select、单个进程的最大连接数,网络IO模型等。

3、http设置,这里是http服务器的相关配置,从默认配置看,大致有五种,基本配置、SSL配置、Log配置、Gzip配置以及虚拟端口设置。这里我们先看虚拟端口这只,这里用include引入了两个文件,/etc/nginx/conf.d/.conf 和/etc/nginx/site-enabled/。我这里就直接将我自己的服务器配置放在conf.d/blog.conf文件中。

server {
    listen 80;
    server_name final-skynet.xin;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

这段代码的意思是nginx监听80端口,服务器的地址是final-skynet.xin,location是一个匹配规则,匹配到监听的url,具体的匹配规则这里就不详细赘述了。匹配成功后,转发到本地的http://127.0.0.1:8080地址,这是gunicorn监听的地址,然后设置其他需要转发的内容。

4、最后一个是mail设置,由于本项目现在还用不着,所以就将默认的代码注释删除了。nginx的基本配置就到这里了。

安装gunicorn和django

sudo pip install gunicorn
sudo apt-get install django

输入上述命令就可以安装好django和gunicorn了,这里先简单的配置一下django,创建一个简单的blog应用。

1、创建新项目

django-admin startproject SkyNet

在根目录输入该命令就可以创建一个新的django项目SkyNet,然后创建一个新的blog应用

进入SkyNet文件夹可以看到该文件夹的目录结构如下

/SkyNet
    manage.py
    /SkyNet
        __init__.py
        settings.py
        urls.py
        wsgi.py

然后在SkyNet目录下运行以下命令

python manage.py startapp blog

然后可以看见SkyNet目录下多了一个blog的文件夹,这个就是新创建的blog app

2、创建index页面

那先创建一个index页面来验证一下我们的配置。

  • 在urls.py中进行配置
from django.conf.urls import url
from django.contrib import admin
from blog.views import *

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$',index,name='index')
]
  • 创建index视图
from __future__ import unicode_literals

from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return HttpResponse('Hello , this is my first index')

现在需要在INSTALL_APPS配置创建的app和gunicorn

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'gunicorn',
    'blog',
]

下面需要通过gunicorn来启动django

gunicorn Skynet.wsgi:application -b 127.0.0.1:8080

最后在浏览器中输入网址进行验证

未分类

可能你需要重启一下你的nginx
讲到这个地方,云服务的环境部署应该是告一段落了

但是呢,在文章的开头,需要建立的是nginx+gunicorn+django+mysql的一个服务器,剩下的就是mysql的安装了

sudo apt-get install mysql-server

然后在安装的过程中提示需要设置密码,这个时候你可以选择设置密码,或者直接选择OK跳过。

我在这里是选择的跳过,安装完毕验证一下数据是否安装成功。由于我这里没有设置密码,故直接输入mysql就进入了mysql的命令行,设置密码的可以输入

mysql -u root -p

然后输入密码进入命令行。

在mysql创建数据库的时候经常出现编码问题,这里我先解决一下mysql的编码问题,在/etc/mysql/my.cnf中引用了conf.d中的文件,所以直接在/etc/mysql/conf.d/mysql.cnf进行修改。
首先查看一下mysql的编码,进入mysql命令行

mysql> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)
//或者
mysql> status
--------------
mysql  Ver 14.14 Distrib 5.7.19, for Linux (x86_64) using  EditLine wrapper

Connection id:      4
Current database:
Current user:       root@localhost
SSL:            Not in use
Current pager:      stdout
Using outfile:      ''
Using delimiter:    ;
Server version:     5.7.19-0ubuntu0.16.04.1 (Ubuntu)
Protocol version:   10
Connection:     Localhost via UNIX socket
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:        /var/run/mysqld/mysqld.sock
Uptime:         1 min 16 sec

可以看到db和server的characterset都默认为latin1,这里需要设置[mysqld]的character-set-server = utf8即可

//mysql.cnf
[mysqld]
character-set-server = utf8

然后重启mysql服务,在命令行输入

service mysql restart

然后重新查看,可以看到mysql的默认编码已经改过来了。

如果你不想这么麻烦,可以在创建数据库的时候设置编码为utf8即可
如下命令

CREATE DATABSE db_name DEFUALT CHARACTER SET utf8;

下面在django中配置mysql

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'blog',
        'USER': 'root',
        'PASSWORD': '',
        'HOST':'',
        'PORT':'',
    }
}

这里由于我的mysql用户没有设置密码所以,password为空,后续再进行设置
先需要在mysql中创建数据库。

CREATE DATABASE blog;

这个时候django还没有和mysql连接起来,需要安装mysqlclient或者MySQL-python,我在这里安装的是mysqlclient,在安装mysqlclient之前还需要安装libmysqlclient-dev

sudo apt-get install libmysqlclient-dev
pip install mysqlclient

这个时候就可以用django的数据迁移

python manage.py makemigrations
python manage.py migrate

这个时候进入mysql命令行,查看blog数据库中是否插入了django数据迁移来的数据库表

mysql> use blog
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+----------------------------+
| Tables_in_blog             |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
10 rows in set (0.00 sec)

看到数据库中的表就可以指导mysql已经配置完成了。
这样SkyNet的服务器配置基本上就完成了。后面需要解决一下代码上传和nginx等优化的问题。

nginx php-fpm的php.ini设置最大上传大小不生效

问题

虽然这种问题网上已经有很多人提出过,但我还是弄不明白。服务器为ubuntu,安装了nginx。
使用phpinfo()发现使用的配置文件是/etc/php/7.0/fpm/php.ini。
设置了如下属性:

upload_max_filesize = 256M
post_max_size = 256M

重启了nginx和php7.0-fpm进程,但是最大上传大小没有改变。
我使用的是wordpress,甚至我还安装了插件来提高最大上传大小,不过仍然没有用。
我也尝试在.htaccess文件增加了如下配置,也一样没有效果:

php_value post_max_size 256M
php_value uploads_max_filesize 256M

最佳答案

nginx默认的上传大小限制为1MB。要更改这个值的话需要设置client_max_body_size变量。可以在nginx.conf http区块设置:

http {
    #...
        client_max_body_size 100m;
        client_body_timeout 120s; # Default is 60, May need to be increased for very large uploads
    #...
}

如果你想上传一个非常大的文件,并且上传时间超过了60秒,这时候也需要提高client_body_timeout变量的值。
更新nginx配置文件后,不要忘记重启nginx。
需要重启nginx和php来重载配置。可以使用如下命令完成:

sudo service nginx restart
sudo service php7.0-fpm restart

注意:如果nginx上没有配置多个站点,可以直接在server区块加上:

server {
    client_max_body_size 8M;
}

Laravel Nginx出现404 Not Found错误

问题

刚刚开始用了几天Laravel,发现URL路由有点问题,http://localhost:8017/laravel能正常打开,不过http://localhost:8017/laravel/foo返回

404 Not Found

系统为Ubuntu 16.04,Laravel 5.4
routes/web.php

Route::get('/', function () {
    return view('welcome');    
});

// oedin
Route::get('foo', function () {
    return 'Hello World';
});

nginx配置文件:

server {
    listen 8017 default_server;
    listen [::]:8017 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;
    root /home/oedin/webdev;

    # Add index.php to the list if you are using PHP
    index index.php index.html index.htm;

    server_name localhost;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ .php$ {
        include snippets/fastcgi-php.conf;
        #try_files $uri /index.php =404;
        #fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
        #fastcgi_index index.php;
        #fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        #include fastcgi_params;

    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /.ht {
    #   deny all;
    #}
}

最佳答案

如果你想要你的应用与nginx使用,推荐新建一个nginx vhost配置文件,其中包含你的项目root目录。
不过可以直接执行php artisan serve命令来提供服务,这样不需要nginx。

部署在Nginx的wordpress全部页面都出现404错误

问题

虽然这个问题网上已经有很多人回答过了,但是还是不能解决我的问题。Nginx上的wordpress,除了首页外,其它页面全部出现404。
下面是nginx的配置:

server {
        listen 80 ;
        listen [::]:80;

        root /var/www/html/p/swear;
        index index.php index.html index.htm;

        server_name skinnybikiniswimwear.org;

        location / {
                try_files $uri /$uri/ /index.php?args =404;
        }


            location ~ .php$ {
                fastcgi_split_path_info ^(.+.php)(/.+)$;
                try_files $uri /index.php?args =404;
                fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
                fastcgi_index index.php;
                include fastcgi_params;
        }
}

从这个配置我找不出有什么问题。
wordpress安装在/var/www/html/p/swear。
多谢.

最佳答案

尝试如下配置:

    location / {
                # This is cool because no php is touched for static content.
                # include the "?$args" part so non-default permalinks doesn't break when using query string
                try_files $uri $uri/ /index.php?$is_args$args =404;
    }


    if (!-e $request_filename) {
            rewrite ^.*$ /index.php last;
    }

许多wordpress上配置nginx不成功,大多数是重写规则不对导致的404问题。

ubuntu 16.04 k8s(kubernetes) delve debug环境配置

环境说明

  • 系统:ubuntu 16.04
  • k8s版本:1.5.7
  • 内存:8GB

Docker安装

1. 卸载旧版本

sudo apt-get remove docker docker-engine docker.io

2. 配置仓库

apt-get update
apt-get -y install 
    apt-transport-https 
    ca-certificates 
    curl 
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
apt-key fingerprint 0EBFCD88
add-apt-repository 
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu 
   $(lsb_release -cs) 
   stable"

3. 安装docker ce

apt-get update
apt-cache madison docker-ce
apt-get -y install docker-ce=17.06.0~ce-0~ubuntu

etcd安装

1. 安装

ETCD_VER=v3.2.5
# choose either URL
GOOGLE_URL=https://storage.googleapis.com/etcd
GITHUB_URL=https://github.com/coreos/etcd/releases/download
DOWNLOAD_URL=${GOOGLE_URL}

rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
rm -rf /opt/etcd-${ETCD_VER} && mkdir -p /opt/etcd-${ETCD_VER}

curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /opt/etcd-${ETCD_VER} --strip-components=1
/opt/etcd-${ETCD_VER}/etcd --version

2. 环境变量

echo "export PATH=/opt/etcd-${ETCD_VER}:$PATH" >> /etc/profile

go安装

1. 下载go

cd /usr/local
wget https://storage.googleapis.com/golang/go1.9rc2.linux-amd64.tar.gz
tar xf go1.9rc2.linux-amd64.tar.gz
mkdir -p /home/go

2. 环境变量

echo 'export PATH=/usr/local/go/bin:$PATH' >> /etc/profile
echo "export GOPATH=/home/go" >> /etc/profile

OpenSSL安装

apt-get -y install openssl

安装k8s

安装

apt-get -y install unzip make gccgo
cd /home/go
wget https://github.com/kubernetes/kubernetes/archive/v1.5.7.zip
unzip v1.5.7.zip 
cd kubernetes-1.5.7
. /etc/profile
make GOGCFLAGS="-N -l"

启动

./hack/local-up-cluster.sh -O

配置

export KUBERNETES_PROVIDER=local
cluster/kubectl.sh config set-cluster local --server=https://localhost:6443 --certificate-authority=/var/run/kubernetes/apiserver.crt
cluster/kubectl.sh config set-credentials myself --username=admin --password=admin
cluster/kubectl.sh config set-context local --cluster=local --user=myself
cluster/kubectl.sh config use-context local
cluster/kubectl.sh

安装delve

go get github.com/derekparker/delve/cmd/dlv
cd /home/go/src/github.com/derekparker/delve
echo 'export PATH="$GOPATH/bin:$PATH' >> /etc/profile
. /etc/profile

dlv debug

启动

dlv exec /home/go/kubernetes-1.5.7/_output/bin/kubectl -- run my-nginx --image=nginx --replicas=2 --port=80

设置断点

(dlv) b cmd/run.go:138
Breakpoint 1 set at 0x1c3a45b for k8s.io/kubernetes/pkg/kubectl/cmd.Run() ./kubernetes-1.5.7/_output/local/go/src/k8s.io/kubernetes/pkg/kubectl/cmd/run.go:138

前进到断点

(dlv) c
> k8s.io/kubernetes/pkg/kubectl/cmd.Run() ./kubernetes-1.5.7/_output/local/go/src/k8s.io/kubernetes/pkg/kubectl/cmd/run.go:138 (hits goroutine(1):1 total:1) (PC: 0x1c3a45b)
   133:         cmd.Flags().String("service-overrides", "", "An inline JSON override for the generated service object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.  Only used if --expose is true.")
   134:         cmd.Flags().Bool("quiet", false, "If true, suppress prompt messages.")
   135:         cmd.Flags().String("schedule", "", "A schedule in the Cron format the job should be run with.")
   136: }
   137:
=> 138: func Run(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string, argsLenAtDash int) error {
   139:         if len(os.Args) > 1 && os.Args[1] == "run-container" {
   140:                 printDeprecationWarning("run", "run-container")
   141:         }
   142:
   143:         // Let kubectl run follow rules for `--`, see #13004 issue