迁移wordpress到docker容器

这几天抽了个时间,终于把自己阿里云ecs的os升级到了centos7,所以也打算把博客wordpress也升级下,同时还要使用现在比较火的docker技术。

下面把相关wordpress迁移到docker中的相关步骤记录下。

PS:强烈建议OS使用3.0以上内核。

一、备份wordpress数据

在正式迁移wordpress之前,我们需要备份wordpress的相关数据,包括数据库、图片以及主题。

有关wordpress数据库的备份,就是个仁者见仁智者见智的事情了,你通过什么方法都可以备份的。我这边的做法是在本地的一个环境上,使用navicat这个mysql数据库管理工具,把数据传输到本地的环境上。

wordpress图片和主题都在wp-content这个目录下,我们只需要备份这个目录即可。

二、安装docker及其相关软件

wordpress相关数据备份完毕后,我们现在开始安装docker及其相关软件。

2.1 安装docker

docker的安装我们可以分为使用脚本快速安装和配置yum源安装,下面分别介绍下。

2.1.1 脚本快速安装

docker的安装比较简单,我们可以直接使用官方提供的脚本快速安装命令进行安装,如下:

curl -fsSL https://get.docker.com/ | sh

未分类

当然我们也可以使用国内提供的脚本快速安装命令,如下:

curl -sSL https://get.daocloud.io/docker | sh

2.1.2 配置yum源安装

通过配置yum源方式安装docker就比较简单,只需要在本地配置yum仓库配置即可。在此我们使用的中科大的docker仓库,配置如下:

vim /etc/yum.repos.d/docker.repo

[dockerrepo]

name=Docker Repository

baseurl=https://yum.dockerproject.org/repo/main/centos/7

enabled=1

gpgcheck=1

gpgkey=https://yum.dockerproject.org/gpg

未分类

yum仓库配置完毕后,我们现在开始安装docker,如下:

yum -y install docker-engine

未分类

2.1.3 普通用户添加docker权限

如果我们想让普通用户也具有使用docker权限,只需要把该用户添加到docker用户组即可。现在以ilanni这个用户为例,如下:

cat /etc/group |grep docker

sudo usermod -aG docker ilanni

su – ilanni

docker ps

未分类

2.1.4 docker开机启动

默认情况下docker是没有开机启动的,使用下面命令使docker开机启动,如下:

systemctl start docker && systemctl enable docker

未分类

2.2 安装docker-compose

因为wordpress需要使用多个docker镜像,所以在此我们使用docker-compose编排工具,进行管理。

安装docker-compose,使用如下命令:

curl -L https://github.com/docker/compose/releases/download/1.13.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose –version

未分类

2.3 安装iptables

centos7默认情况使用的是firewalld作为防火墙,但是对firewalld很是不熟悉,所以还是打算使用iptables。

下面安装iptables,使用如下命令:

yum -y install iptables-services iptables

未分类

把iptables加入到系统服务,使用如下命令:

cp /usr/libexec/iptables/iptables.init /etc/init.d/iptables

/etc/init.d/iptables start

/etc/init.d/iptables status

未分类

把iptables设置为开机启动,使用如下命令:

systemctl enable iptables

systemctl list-unit-files | grep iptables

未分类

关闭firewalld服务,使用如下命令:

systemctl disable firewalld.service

systemctl status firewalld.service

未分类

PS:docker及其相关软件安装完毕后,强烈建议重启服务器。

三、下载镜像

在本次wordpress迁移到docker中,我们需要两个镜像mysql镜像和wordpress镜像。其中wordpress镜像中包含apache、php和wordpress,而mysql镜像提供数据库服务。

PS:无论是wordress镜像还是mysql镜像都提供了多个版本,比如:wordpress镜像中有的只提供wordpress功能,没有提供php功能等等。

但是这次为了迁移的方便,我们只使用wordpress提供的全部功能。

除此之外,考虑到众所周知的原因,我们使用国内网易的蜂巢docker。

3.1 下载mysql5.5镜像

因为我现在wordpress的数据库使用的mysql5.5版本,所以我们也要下载mysql5.5的docker镜像,使用如下命令:

docker pull hub.c.163.com/library/mysql:5.5

未分类

未分类

如果要使用官方提供的mysql5.5镜像,使用如下命令:

docker pull mysql:5.5

未分类

3.2 下载wordpress镜像

wordpress镜像我们使用最新的版本即可,下载最新版本的镜像命令如下:

docker pull hub.c.163.com/library/wordpress

未分类

如果要使用官方提供的wordpress镜像,使用如下命令:

docker pull wordpress

未分类

对于wordpress提供的多个版本,我们使用的是包含有apache、php、php-fpm的latest版本。

对于只提供wordpress功能的版本,我们会在以后的文章进行相关讲解。

四、编写docker-compose.yml文件

为了管理容器的方便在此我们使用的是docker-compose来进行的,当然你也可以对每个docker镜像单独来运行。

但是在这为了迁移的方便,我们在这直接使用docker-compose来进行管理。

对于docker-compose,我们只需要编写docker-compose.yml文件,即可。示例如下:

vim docker-compose.yml

version: '2'

services:

mysqldb:

image: hub.c.163.com/library/mysql:5.5

container_name: ilanni_mysql

ports:

- "33033:3306"

volumes:

- ./data:/var/lib/mysql

restart: always

environment:

MYSQL_ROOT_PASSWORD: 123456

MYSQL_DATABASE: ilanni

MYSQL_USER: wwwilanni

MYSQL_PASSWORD: ilanni123

wordpress:

depends_on:

- mysqldb

image: hub.c.163.com/library/wordpress

container_name: ilanni_wordpress

ports:

- "80:80"

restart: always

environment:

WORDPRESS_DB_HOST: ilanni_mysql:3306

WORDPRESS_DB_NAME: ilanni

WORDPRESS_DB_USER: wwwilanni

WORDPRESS_DB_PASSWORD: ilanni123

volumes:

- ./wp-content/themes/xiu_ilanni:/var/www/html/wp-content/themes/xiu_ilanni

- ./wp-content/plugins:/var/www/html/wp-content/plugins

- ./wp-content/uploads:/var/www/html/wp-content/uploads

- ./favicon.ico:/var/www/html/favicon.ico

- ./alivv.txt:/var/www/html/alivv.txt

未分类

未分类

上述的docker-compose.yml文件中,我们创建了两个容器ilanni_mysql和ilanni_wordpress,其中- ./data:/var/lib/mysql的意思是把容器中的/var/lib/mysql目录映射到本地的data目录下。

  • ./alivv.txt:/var/www/html/alivv.txt意思是把本地的alivv.txt文件挂载到容器为/var/www/html/alivv.txt文件。

  • “33033:3306″意思是把容器中的3306端口映射为本地33033端口。

depends_on意思是一个容器依赖与另外一个容器。

docker-compose.yml文件编写完毕后,我们就可以启动容器了。使用如下命令:

docker-compose up -d

docker ps

未分类

通过上图我们可以很明显的看出,ilanni_mysql和ilanni_wordpress这个容器已经创建完毕。

PS:上述docker-compose.yml文件相关的环境变量中,我们都可以在对应docker镜像中看到对应的函数。

mysql镜像的对应变量,可以通过如下连接进行查看:

https://hub.docker.com/r/library/mysql/

未分类

wordpress镜像的对应变量,可以通过如下连接进行查看:

https://hub.docker.com/r/library/wordpress/

未分类

五、导入wordpress备份数据

通过上述章节,我们可以看到ilanni_mysql容器已经正常启动了。

现在我们把wordpress备份的数据导入到新的数据库中,使用navicat进行数据传输如下:

未分类

未分类

未分类

通过上图,我们可以看到wordpress数据已经被恢复到新的数据库ilanni_mysql中了。

其他的备份数据,我们只需要复制到对应的目录下即可。

六、启动容器

其实我们在前面已经启动了容器,之所以我们再次说要启动容器,是因为我们在第五章节中刚刚把原来备份的数据恢复到新的环境中。

现在我们只需要重启容器即可,使用如下命令:

docker-compose restart

未分类

现在我们来访问下刚刚恢复数据后的wordpress,如下:

未分类

未分类

通过上图,我们可以看出wordpress已经全部迁移过去了。

七、配置iptables规则

因为没有启用firewalld服务,所以这个牵涉到有关iptables规则的配置。

下面是正确配置的iptables规则,如下:

cat /etc/sysconfig/iptables

*nat

:PREROUTING ACCEPT [263:15384]

:INPUT ACCEPT [135:7704]

:OUTPUT ACCEPT [104:6272]

:POSTROUTING ACCEPT [232:13952]

:DOCKER - [0:0]

-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER

-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER

-A POSTROUTING -s 172.17.0.1/16 ! -o docker0 -j MASQUERADE

COMMIT

*filter

:INPUT ACCEPT [0:0]

:FORWARD ACCEPT [0:0]

:OUTPUT ACCEPT [0:0]

:DOCKER - [0:0]

-A INPUT -i br-eaa791e079d2 -j ACCEPT

-A FORWARD -o br-eaa791e079d2 -j DOCKER

-A FORWARD -o br-eaa791e079d2 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

-A FORWARD -i br-eaa791e079d2 ! -o br-eaa791e079d2 -j ACCEPT

-A FORWARD -i br-eaa791e079d2 -o br-eaa791e079d2 -j ACCEPT

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

-A INPUT -p icmp -j ACCEPT

-A INPUT -i lo -j ACCEPT

-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT

-A INPUT -p tcp -m state --state NEW -m tcp --dport 22022 -j ACCEPT

-A INPUT -j REJECT --reject-with icmp-host-prohibited

-A FORWARD -j REJECT --reject-with icmp-host-prohibited

COMMIT

未分类

上述iptables规则中,我们需要关注nat规则和filter规则,对于nat规则中我们只需要关注POSTROUTING中ip地址即可。

该ip地址就是docrer0网卡的ip地址,我们可以通过ip a命令进行查看,如下:

ip add

未分类

对于filter规则中,需要我们关注如下规则:

:DOCKER - [0:0]

-A FORWARD -o br-d63b827b6fc9 -j DOCKER

-A FORWARD -o br-d63b827b6fc9 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

-A FORWARD -i br-d63b827b6fc9 ! -o br-d63b827b6fc9 -j ACCEPT

-A FORWARD -i br-d63b827b6fc9 -o br-d63b827b6fc9 -j ACCEPT

其中br-d63b827b6fc9为docker所在机器的桥接网卡地址,如果docker所在机器有多块网卡的话,我们可以通过登录进入docker容器查看ip地址,就可以得到该容器运行时所使用的宿主机的网卡。

示例如下:

docker exec -it ilanni_mysql /bin/bash

ip a    

未分类

八、配置wordpress上传图片权限

按照上述步骤迁移完毕后,在发布文章时,你会发现wordpress提示你没有上传图片的权限。

这个应该是wordpress镜像的一个bug,需要我们修改wordpress镜像的upload目录的权限,如下:

docker exec -it ilanni_wordpress /bin/bash

chown www-data:www-data -R wp-content/uploads/

未分类

安装配置docker私有仓库

局域网私有仓库

如上,搭建一个只有本机访问的私有镜像非常简单,不用对已有的docker服务进行任何配置。如果要搭建一个能在局域网内访问的docker仓库,就需要对docker服务进行一些简单的配置。

首先,在主机A上:

1. 安装registry镜像(同上)

拉取registry镜像:

docker pull registry

Using default tag: latest 
latest: Pulling from library/registry709515475419: 
Pull completedf6e278d8f96: 
Pull complete4b0b08c1b8f7: 
Pull complete80119f43a01e: 
Pull completeacf34ba23c50: 
Pull completeDigest:

sha256:412e3b6494f623a9f03f7f9f8b8118844deaecfea19e3a5f1ce54eed4f400296Status: Downloaded newer

imageforregistry:latest

启动registry镜像

docker run-d-p 5000:5000 --restart=always --name registry  -v

/your/path/to/registry-images:/var/lib/registry  registry:latest

2. 将某个image导入registry,这里使用主机A的IP而不是localhost

docker tag jdeathe/centos-ssh:centos-7 192.168.0.111:5000/ssh:7

镜像推到仓库

docker push 192.168.0.111:5000/ssh:7

3.修改主机A的docker配置(/etc/sysconfig/docker文件不存在则直接创建)

other_args="--insecure-registry192.168.0.111:5000"

4.重启docker服务

service docker restart

在同一局域网中的另一台主机B上

4.1 修改docker配置(/etc/docker/daemon.json文件不存在则直接创建),使docker daemon能连上私有的registry

{

"registry-mirrors": ["192.168.0.111:5000"],

}

4.2 修改主机B的docker配置(/etc/sysconfig/docker文件不存在则直接创建)

other_args="--insecure-registry192.168.0.111:5000"

4.3 重启docker服务

service

docker restart

4.4 下载主机A中仓库的镜像,在局域网中速度非常快,以后开发过程中做部署测试就非常方便了。

docker pull 192.168.0.111:5000/ssh:7

CentOS6安装Docker

未分类

之前写过一篇关于在Mac上面使用并安装Docker的文章《Mac上Docker的安装和使用初探》,介绍了在Macos上面安装Docker的步骤。近期由于需要在一台CentOS 6.5的服务器上面部署一些服务,考虑到使用Docker来做这些事情,记录一下处理的步骤。

1、检查内核版本

uname -r

如果输出的信息为2.6.32-431.el6.centos.plus.x86_64,表示当前的内核版本是2.6.32。docker需要的内核版本是3.10,所以需要升级Linux的内核,升级的步骤如下:

(1) 导入public key

rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org

(2) 安装ELRepo

rpm -Uvh http://www.elrepo.org/elrepo-release-6-6.el6.elrepo.noarch.rpm

(3) 安装内核

yum --enablerepo=elrepo-kernel install kernel-lt -y

目前在ELRepo源中存在如下几个版本的内核,参考地址http://elrepo.org/linux/kernel/el6/x86_64/RPMS/,long-term表示长期稳定版本

  • 1) ml(main-line): 4.6

  • 2) lt(long-term): 3.10

(4) 修改Grub引导顺序

vim /etc/grub.conf
修改default=0

(5) 重启

shutdown -r now

2、安装Docker

(1) 更新yum包

sudo yum update

(2) 下载rmp包

curl -O -sSL https://get.docker.com/rpm/1.7.0/centos-6/RPMS/x86_64/docker-engine-1.7.0-1.el6.x86_64.rpm

(3) 安装rmp包

sudo yum localinstall --nogpgcheck docker-engine-1.7.0-1.el6.x86_64.rpm

(4) 启动docker服务

sudo service docker start

(5) 验证Docker

sudo docker run hello-world

如果安装启动成功,控制台输出的结果如下所示:

未分类

(6) 设置开机启动

sudo chkconfig docker on

如何卸载

yum list installed | grep docker
sudo yum -y remove docker-engine.x86_64 

linux awk命令使用详解

当你第一次拿起双手在电脑上使用 awk 命令处理一个或者多个文件的时候,它会依次读取文件的每一行内容, 然后对其进行处理,awk 命令默认从 stdio 标准输入获取文件内容, awk 使用一对单引号来表示 一些可执行的脚本代码,在可执行脚本代码里面,使用一对花括号来表示一段可执行代码块,可以同时存在多个代码块。 awk 的每个花括号内同时又可以有多个指令,每一个指令用分号分隔,awk 其实就是一个脚本编程语言。说了这么多,你肯定还是一脸的懵逼。先别急,往下看。。。

1、awk 命令的基本格式

awk [options] 'program' file

options 这个表示一些可选的参数选项,反正就是你爱用不用,不用可以拉到。。。 program 这个表示 awk 的可执行脚本代码,这个是必须要有的。 file 这个表示 awk 需要处理的文件,注意是纯文本文件,不是你的 mp3,也不是 mp4 啥的。。

2、先来一个 awk 的使用例子热热身

$ awk '{print $0}' /etc/passwd

awk 命令的可执行脚本代码使用单引号括起来,紧接着里面是一对花括号,记住是 “花括号” 不是 “花姑娘”,然后花括号里面就是一些可执行的脚本代码段,当 awk 每读取一行之后,它会依次执行双引号里面的每个脚本代码段,在上面这个例子中, $0 表示当前行。当你执行了上面的命令之后,它会依次将 /etc/passwd 文件的每一行内容打印输出。

3、awk 自定义分隔符

awk 默认的分割符为空格和制表符,我们可以使用 -F 参数来指定分隔符

$ awk -F ':' '{print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator
games
ftp
nobody

上面的命令将 /etc/passwd 文件中的每一行用冒号 : 分割成多个字段,然后用 print 将第 1 列字段的内容打印输出

4、如何在 awk 中同时指定多个分隔符

比如现在有这样一个文件 some.log 文件内容如下

Grape(100g)1980
raisins(500g)1990
plum(240g)1997
apricot(180g)2005
nectarine(200g)2008

现在我们想将上面的 some.log 文件中按照 “水果名称(重量)年份” 来进行分割

$ awk -F '[()]' '{print $1, $2, $3}' some.log
Grape 100g 1980
raisins 500g 1990
plum 240g 1997
apricot 180g 2005
nectarine 200g 2008

在 -F 参数中使用一对方括号来指定多个分隔符,awk 处理 some.log 文件时就会使用 “(” 和 “)” 来对文件的每一行进行分割。

5、awk 内置变量的使用

  • $0 这个表示文本处理时的当前行
  • $1 表示文本行被分隔后的第 1 个字段列
  • $2 表示文本行被分割后的第 2 个字段列
  • $3 表示文本行被分割后的第 3 个字段列
  • $n 表示文本行被分割后的第 n 个字段列
  • NR 表示文件中的行号,表示当前是第几行
  • NF 表示文件中的当前行列的个数,类似于 mysql 数据表里面每一条记录有多少个字段
  • FS 表示 awk 的输入分隔符,默认分隔符为空格和制表符,你可以对其进行自定义设置
  • OFS 表示 awk 的输出分隔符,默认为空格,你也可以对其进行自定义设置
  • FILENAME 表示当前文件的文件名称,如果同时处理多个文件,它也表示当前文件名称

比如我们有这么一个文本文件 fruit.txt 内容如下,我将用它来向你演示如何使用 awk 命令工具,顺便活跃一下此时尴尬的气氛。。

peach 100 Mar 1997 China
Lemon 150 Jan 1986 America
Pear 240 Mar 1990 Janpan
avocado 120 Feb 2008 china

我们来瞧一瞧下面这些简单到爆炸的例子,这个表示打印输出文件的每一整行的内容

$ awk '{print $0}' fruit.txt
peach 100 Mar 1997 China
Lemon 150 Jan 1986 America
Pear 240 Mar 1990 Janpan
avocado 120 Feb 2008 china

下面这个表示打印输出文件的每一行的第 1 列内容

$ awk '{print $1}' fruit.txt
peach
Lemon
Pear
avocado

下面面这个表示打印输出文件的每一行的第 1 列、第 2 列和第 3 列内容

$ awk '{print $1, $2, $3}' fruit.txt
peach 100 Mar
Lemon 150 Jan
Pear 240 Mar
avocado 120 Feb

其中加入的逗号表示插入输出分隔符,也就是默认的空格

文件的每一行的每一列的内容除了可以用 print 命令打印输出以外,还可以对其进行赋值

$ awk '{$2 = "***"; print $0}' fruit.txt
peach *** Mar 1997 China
Lemon *** Jan 1986 America
Pear *** Mar 1990 Janpan
avocado *** Feb 2008 china

上面的例子就是表示通过对 $2 变量进行重新赋值,来隐藏每一行的第 2 列内容,并且用星号 * 来代替其输出

在参数列表中加入一些字符串或者转义字符之类的东东

$ awk '{print $1 "t" $2 "t" $3}' fruit.txt
peach 100 Mar
Lemon 150 Jan
Pear 240 Mar
avocado 120 Feb

像上面这样,你可以在 print的参数列表中加入一些字符串或者转义字符之类的东东,让输出的内容格式更漂亮,但一定要记住要使用双引号。

awk 内置 NR 变量表示每一行的行号

$ awk '{print NR "t" $0}' fruit.txt
1 peach  100 Mar 1997 China
2 Lemon  150 Jan 1986 America
3 Pear  240 Mar 1990 Janpan
4 avocado 120 Feb 2008 china

awk 内置 NF 变量表示每一行的列数

$ awk '{print NF "t" $0}' fruit.txt
5 peach  100 Mar 1997 China
5 Lemon  150 Jan 1986 America
5 Pear  240 Mar 1990 Janpan
5 avocado 120 Feb 2008 china

6、awk 中 $NF 变量的使用

$ awk '{print $NF}' fruit.txt
China
America
Janpan
china

上面这个 $NF 就表示每一行的最后一列,因为 NF 表示一行的总列数,在这个文件里表示有 5 列,然后在其前面加上 $ 符号,就变成了 $5 ,表示第 5 列

$ awk '{print $(NF - 1)}' fruit.txt
1997
1986
1990
2008

上面 $(NF-1) 表示倒数第 2 列, $(NF-2) 表示倒数第 3 列,依次类推。

现在除了刚才说的有一个 fruit.txt 文件之外,我们又多了一个新文件叫 company.txt 内容如下

yahoo 100 4500
google 150 7500
apple 180 8000
twitter 120 5000

我们用 fruit.txt 和 company.txt 两个文件来向你演示 awk 同时处理多个文件的时候有什么效果

$ awk '{print FILENAME "t" $0}' fruit.txt company.txt
fruit.txt  peach 100 Mar 1997 China
fruit.txt  Lemon 150 Jan 1986 America
fruit.txt  Pear 240 Mar 1990 Janpan
fruit.txt  avocado 120 Feb 2008 china
company.txt yahoo 100 4500
company.txt google 150 7500
company.txt apple 180 8000
company.txt twitter 120 5000

当你使用 awk 同时处理多个文件的时候,它会将多个文件合并处理,变量 FILENAME 就表示当前文本行所在的文件名称。

看到这里是不是感觉 awk 命令的使用方法真的是简单到爆炸,现在不要太高兴,请举起你的双手跟我一起摇摆。。。哦,不对!请拿起你的双手在电脑上试一试上面这些例子。

7、BEGIN 关键字的使用

在脚本代码段前面使用 BEGIN 关键字时,它会在开始读取一个文件之前,运行一次 BEGIN 关键字后面的脚本代码段, BEGIN 后面的脚本代码段只会执行一次,执行完之后 awk 程序就会退出

$ awk 'BEGIN {print "Start read file"}' /etc/passwd
Start read file

awk 脚本中可以用多个花括号来执行多个脚本代码,就像下面这样

$ awk 'BEGIN {print "Start read file"} {print $0}'
 /etc/passwd
Start read file
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin

8、END 关键字使用方法

awk 的 END 指令和 BEGIN 恰好相反,在 awk 读取并且处理完文件的所有内容行之后,才会执行 END 后面的脚本代码段

$ awk 'END {print "End file"}' /etc/passwd
End file

一定要多动手在电脑上敲一敲这些命令,对身体好。脑子是个好东西,要多用。。。

$ awk 'BEGIN {print "Start read file"} {print $0} END
 {print "End file"}' /etc/passwd
Start read file
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
End file

9、在 awk 中使用变量

可以在 awk 脚本中声明和使用变量

$ awk '{msg="hello world"; print msg}' /etc/passwd
hello world
hello world
hello world
hello world
hello world

awk 声明的变量可以在任何多个花括号脚本中使用

$ awk 'BEGIN {msg="hello world"} {print msg}' 
/etc/passwd
hello world
hello world
hello world
hello world
hello world

10、在 awk 中使用数学运算

在 awk 中,像其他编程语言一样,它也支持一些基本的数学运算操作

$ awk '{a = 12; b = 24; print a + b}' company.txt
36
36
36
36

上面这段脚本表示,先声明两个变量 a = 12 和 b = 24,然后用 print 打印出 a 加上 b 的结果。

看到上面的输出结果,你很可能又是一脸的懵逼,为什么会重复输出 4 次同样的计算结果。所以说小时不学好,长大做IT。 知识这东西真到了要用的时候,能亮瞎别人的双眼,好了,不废话。请记住 awk 是针对文件的每一行来执行一次单引号 里面的脚本代码,每读取到一行就会执行一次,文件里面有多少行就会执行多少次,但 BEGIN 和 END 关键字后面的 脚本代码除外,如果被处理的文件中什么都没有,那 awk 就一次都不会执行。。。

11、awk 还支持其他的数学运算符

+ 加法运算符
- 减法运算符
* 乘法运算符
/ 除法运算符
% 取余运算符

12、在 awk 中使用条件判断

比如有一个文件 company.txt 内容如下

yahoo 100 4500
google 150 7500
apple 180 8000
twitter 120 5000

我们要判断文件的第 3 列数据,也就是平均工资小于 5500 的公司,然后将其打印输出

$ awk '$3 < 5500 {print $0}' company.txt
yahoo 100 4500
twitter 120 5000

上面的命令结果就是平均工资小于 5500 的公司名单, $3 < 5500 表示当第 3 列字段的内容小于 5500 的时候才会执行后面的 {print $0} 代码块

$ awk '$1 == "yahoo" {print $0}' company.txt
yahoo 100 4500

13、awk 的一些其他的条件操作符

未分类

使用 if 指令判断来实现上面同样的效果

$ awk '{if ($3 < 5500) print $0}' company.txt
yahoo 100 4500
twitter 120 5000

上面表示如果第 3 列字段小于 5500 的时候就会执行后面的 print $0,很像 C 语言和 PHP 的语法对不对。

14、在 awk 中使用正则表达式

在 awk 中支持正则表达式的使用,如果你还对正则表达式不是很了解,请先停下来,上 google 去搜一下。

比如现在我们有这么一个文件 poetry.txt 里面都是我写的诗,不要问我为什么那么的有才华。内容如下:

This above all: to thine self be true
There is nothing either good or bad, but thinking
 makes it so
There’s a special providence in the fall of
 a sparrow
No matter how dark long, may eventually in the
 day arrival

使用正则表达式匹配字符串 “There” ,将包含这个字符串的行打印并输出

$ awk '/There/{print $0}' poetry.txt
There is nothing either good or bad, but
 thinking makes it so
There’s a special providence in the fall
 of a sparrow

使用正则表达式配一个包含字母 t 和字母 e ,并且 t 和 e 中间只能有任意单个字符的行

$ awk '/t.e/{print $0}' poetry.txt
There is nothing either good or bad, but
 thinking makes it so
There’s a special providence in the fall
 of a sparrow
No matter how dark long, may eventually in
 the day arrival

如果只想匹配单纯的字符串 “t.e”, 那正则表达式就是这样的 /t.e/ ,用反斜杠来转义 . 符号 因为 . 在正则表达式里面表示任意单个字符。

使用正则表达式来匹配所有以 “The” 字符串开头的行

$ awk '/^The/{print $0}' poetry.txt
There is nothing either good or bad, but
 thinking makes it so
There’s a special providence in the fall
 of a sparrow

在正则表达式中 ^ 表示以某某字符或者字符串开头。

使用正则表达式来匹配所有以 “true” 字符串结尾的行

$ awk '/true$/{print $0}' poetry.txt
This above all: to thine self be true

在正则表达式中 $ 表示以某某字符或者字符串结尾。

又一个正则表达式例子如下

$ awk '/m[a]t/{print $0}' poetry.txt
No matter how dark long, may eventually in
 the day arrival

上面这个正则表达式 /m[a]t/ 表示匹配包含字符 m ,然后接着后面还要包含中间方括号中表示的单个字符 a ,最后还要包含字符 t 的行,输出结果中只有单词 “matter” 符合这个正则表达式的匹配。因为正则表达式 [a] 方括号中表示匹配里面的任意单个字符。

继续上面的一个新例子如下

$ awk '/^Th[ie]/{print $0}' poetry.txt
This above all: to thine self be true
There is nothing either good or bad,
 but thinking makes it so
There’s a special providence in the fall
 of a sparrow

这个例子中的正则表达式 /^Th[ie]/表示匹配以字符串 “Thi” 或者 “The” 开头的行,正则表达式方括号中表示匹配其中的任意单个字符。

再继续上面的新的用法

$ awk '/s[a-z]/{print $0}' poetry.txt
This above all: to thine self be true
There is nothing either good or bad,
 but thinking makes it so
There’s a special providence in the
 fall of a sparrow

正则表达式 /s[a-z]/ 表示匹配包含字符 s 然后后面跟着任意 a 到 z 之间的单个字符的字符串,比如 "se", "so", "sp" 等等。

正则表达式 [] 方括号中还有一些其他用法比如下面这些

  • [a-zA-Z] 表示匹配小写的 a 到 z 之间的单个字符,
    或者大写的 A 到 Z 之间的单个字符

  • [^a-z] 符号 ^ 在方括号里面表示取反,
    也就是非的意思,
    表示匹配任何非 a 到 z 之间的单个字符

正则表达式中的星号 * 和加号 + 的使用方法

$ awk '/go*d/{print $0}' poetry.txt
There is nothing either good or bad,
 but thinking makes it so

上面这个表示匹配包含字符串 "god",并且中间的字母 "o" 可以出现 0 次或者多次,比如单词 "good" 就符合这个要求。 正则表达式中的 + 和星号原理差不多,只是加号表示任意 1 个或者 1 个以上,也就是必须至少要出现一次。

正则表达式问号 ? 的使用方法

$ awk '/ba?d/{print $0}' poetry.txt
There is nothing either good or bad,
 but thinking makes it so

正则表达式中的问号 ? 表示它前面的字符只能出现 0 次 或者 1 次,也就是可以不出现,也可以出现,但如果有出现也只能出现 1 次。

正则表达式中的 {} 花括号用法

$ awk '/go{2}d/{print $0}' poetry.txt
There is nothing either good or bad,
but thinking makes it so

花括号 {} 表示规定它前面的字符必须出现的次数,像上面这个 /go{2}d/ 就表示只匹配字符串 "good",也就是中间的字母 "o" 必须要出现 2 次。

正则表达式中的花括号还有一些其他的用法如下

  • /go{2,3}d/ 表示字母 "o" 只能可以出现 2 次或者 3 次
  • /go{2,10}d/ 表示字母 "o" 只能可以出现 2次,
    3次,4次,5次,6次 … 一直到 10 次
  • /go{2,}d/ 表示字母 "o" 必须至少出现 2 次
    或着 2 次以上

正则表达式中的圆括号的用法

$ awk '/th(in){1}king/{print $0}' poetry.txt
There is nothing either good or bad,
 but thinking makes it so

正则表达式中的圆括号表示将多个字符当成一个完整的对象来看待。比如 /th(in){1}king/ 就表示其中字符串 "in" 必须出现 1 次。而如果不加圆括号就变成了 /thin{1}king/ 这个就表示其中字符 "n" 必须出现 1 次。

看到这里,如果你对 poetry.txt 件中写的诗比较熟悉,你就会发现。。。我去!这诗根本就不是我写的。所以论多读书是多么的重要。我有幸借用莎士比亚的诗来向你讲解如何在 awk 中使用正则表达式。

15、使用awk的一些总结

因为 awk 算起来也是一种编程语言,它的功能远远不止我们上面讲的这些,awk 还有一些其他比较复杂的功能。但一般我们不建议将 awk 用的太过于复杂。通常面对一些比较复杂的场景我们还是要使用其他的一些工具,比如 shell 脚本,Lua 等等

Awk获取shell外部变量值

这里提到awk,相信写shell的朋友都会接触到。AWK 是一种用于处理文本的编程语言工具。AWK 提供了极其强大的功能:

  • 可以进行正则表达式的匹配
  • 样式装入
  • 流控制
  • 数学运算符
  • 进程控制语句
  • 内置的变量和函数

可以把awk看作一门完全的程序设计语言,它处理文本的速度是快得惊人的。现在很多基于shell 日志分析工具都可以用它完成。设计简单,速度表现很好。 涉及到以上六个方面内容,我会在以后文章中加以介绍。 这次主要说下,怎么样把外部变量传入到awk执行语句中。

一、基础:

awk [ -F re] [parameter...] ['pattern {action}' ] [-f progfile][in_file...]

awk一般语法如上面所说。

如:

[chengmo@ localhost ~]$ echo 'awk code' | awk 'BEGIN{print "startn============="}{print $0}END{print "=========nend"}'
start
=============
awk code
=========
end

在 awk中两个特别的表达式,BEGIN和END,这两者都可用于pattern中(参考前面的awk语法),提供BEGIN和END的作用是给程序赋予初始状态和在程序结束之后执行一些扫尾的工作。任何在BEGIN之后列出的操作(在{}内)将在awk开始扫描输入之前执行,而END之后列出的操作将在扫描完全部的输入之后执行。因此,通常使用BEGIN来显示变量和预置(初始化)变量,使用END来输出最终结果。

二、获得外部变量方法

1、获得普通外部变量

[chengmo@ localhost ~]$ test='awk code'
[chengmo@ localhost ~]$ echo | awk '{print test}' test="$test"
awk code
[chengmo@ localhost ~]$ echo | awk test="$test" '{print test}'
awk: cmd. line:1: fatal: cannot open file `{print test}' for reading (No such file or directory)

格式如:awk ‘{action}’ 变量名=变量值 ,这样传入变量,可以在action中获得值。 注意:变量名与值放到’{action}’后面。

[chengmo@ localhost ~]$ echo | awk ‘BEGIN{print test}’ test=”$test”

这种变量在:BEGIN的action不能获得。

2、BEGIN程序块中变量

[chengmo@ localhost ~]$ test='awk code'
[chengmo@ localhost ~]$ echo | awk -v test="$test" 'BEGIN{print test}'
awk code
[chengmo@ localhost ~]$ echo | awk -v test="$test" '{print test}'
awk code

格式如:awk –v 变量名=变量值 [–v 变量2=值2 …] ‘BEGIN{action}’ 注意:用-v 传入变量可以在3中类型的action 中都可以获得到,但顺序在 action前面。

3、获得环境变量

[chengmo@ localhost ~]$ awk 'BEGIN{for (i in ENVIRON) {print i"="ENVIRON[i];}}'
AWKPATH=.:/usr/share/awk
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SELINUX_LEVEL_REQUESTED=
SELINUX_ROLE_REQUESTED=
LANG=en_US.UTF-8
.......

只需要调用:awk内置变量 ENVIRON,就可以直接获得环境变量。它是一个字典数组。环境变量名 就是它的键值。

使用awk获取每行的最大值

需求

  • 有一个数字文本,每行都是数字,以空格分开;现在需要将每行中最大值取出来

文本如下:

[root@localhost ~]#cat urfile
1   1     2
1   2     1
1   3     1
  • 使用awk解决
[root@localhost ~]#awk 'BEGIN{n=1;max=0;nu=0}{for(n=1;n=max)max=a[$n]}{print max}{max=0}}' urfile
2
2
3

解释说明

使用数组将每个数字记录,然后将每个值与max变量比较,若比max大,则将内容赋予max;使用nu记录行号,每行执行最后将max置0

awk用法与示例介绍

awk:模式扫描和数据处理语言

描述: awk是一种编程语言,用于Linux/unix下对文本和数据进行扫描与处理,数据可以来自标准输入、文件、管道。工作流程是:逐行扫描文件,寻找特定匹配模式的行,并进行相应的处理动作。awk读取文件文件内容每一行时,将对比该行是否与给定的模式相匹配,如果匹配,则执行相应处理动作,否则不对该行进行处理。如果没有指定的处理脚本,则把匹配的行显示到标准输出(默认print动作),如果没有指定模式匹配,则默认匹配所有数据

语法:

awk  [ POSIX or GNU style options ] -f program-file [ --  ] file ...
awk [ POSIX or GNU style options ] [ --  ]  program-text file ...

选项:

  • -F fs 使用fs作为输入行的分隔符(默认是空格或者制表符)

  • -v var=val 在处理过程之前,给var设置一个变量val

  • -f program-file 从文件中读取awk的处理内容

内置变量:

  • ARGC 命令行参数个数

  • ARGV 命令行参数的一个排列,索引从0到ARGC-1

  • ARGIND ARGV最近处理文件的索引

  • FILENAME 当前输入文档的名称

  • FNR 当前输入文档的记录编号

  • NR 输入流的当前记录编号(行号)

  • FS 字段分隔符

  • NF 当前记录的字段个数

  • OFS 输出字段分隔符,默认为空格

  • RS 输出记录分隔符默认是换行符n

  • ORS 输出记录分隔符,默认是换行符n

AWK patterns may be one of the following:

BEGIN
END
/regular expression/
relational expression
pattern && pattern
pattern || pattern
pattern ? pattern : pattern
(pattern)
! pattern
pattern1, pattern2

例子:

1、-F 指定分隔符

[root@python ~]# cat test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol  
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject
[root@python ~]# awk '{print $2}' test.txt
48049/tcp
48128/udp
48129/tcp
48129/udp
48556/udp
48619/tcp
[root@python ~]# awk -F'/' '{print $2}'             test.txt
tcp               # 3GPP Cell Broadcast         Service Protocol
udp               # Image Systems Network       Services
tcp               # Bloomberg locator
udp               # Bloomberg locator
udp               # com-bardac-dw
tcp               # iqobject

#指定空格或者/作为分分隔符。+代表重复前面的字符一次或者多次
[root@python ~]# awk -F'[ /]+' '{print $2}'         test.txt
48049
48128
48129
48129
48556
48619

2、-v 变量赋值

[root@python ~]# awk -v a=2 '{print $a}' test.txt
48049/tcp
48128/udp
48129/tcp
48129/udp
48556/udp
48619/tcp
[root@python ~]# awk -v a=342 'BEGIN{print a}'
342

3、-f 从文件中读取awk的内容

#编辑awk脚本文件
[root@python ~]# cat a.txt 
/^$/ {print "BLANK LINE"}
#有几个空行就打印多少行的BLANK LINE
[root@python ~]# awk -f a.txt /etc/ssh/ssh_config
BLANK LINE
BLANK LINE
BLANK LINE
BLANK LINE

4、记录和字段

#$0表示将匹配的内容完全输出
[root@python ~]# echo "I am a bird"|awk '{print $0}'
I am a bird
[root@python ~]# echo "I am a bird"|awk '{print $1,$2}'
I am

#NF表达总的字段的个数(可以理解为列数)
[root@python ~]# echo "I am a bird"|awk '{print NF}'
4

#$NF表示最后一个字段
[root@python ~]# echo "I am a bird"|awk '{print $NF}'
bird

#NR表示输入流的当前记录编号,可以理解为(匹配的行编号)
[root@python ~]# echo "I am a bird"|awk '{print NR}'
1
[root@python ~]# echo -e "I am a birdnhello"|awk '{print NR}'
1
2

5、OFS指定输出的分隔符

echo "I am a bird"|awk 'BEGIN{OFS="#"}{print $1,$2,$3,$4}'
I#am#a#bird

6、正则匹配

#匹配含有tcp的行
[root@python ~]# awk '/tcp/{print $0}' test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
blp5            48129/tcp               # Bloomberg locator
iqobject        48619/tcp               # iqobject

#匹配以blp5开头的行
[root@python ~]# awk '/^blp5/{print $0}' test.txt
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator

#匹配以tor结束的行
[root@python ~]# awk '/tor$/{print $0}' test.txt
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator

#逻辑或||
[root@python ~]# awk '/blp5/||/3gpp/{print $0}' test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator

#逻辑与&&
[root@python ~]# awk '/blp5/&&/tcp/{print $0}' test.txt
blp5            48129/tcp               # Bloomberg locator

#逻辑非
[root@python ~]# awk '!/blp5/&& !/3gpp/{print $0}' test.txt
isnetserv       48128/udp               # Image Systems Network Services
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject

7、匹配范围

[root@python ~]# awk '/3gpp/,/blp5/{print $0}' test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               #       Bloomberg locator

8、BEGIN和END格式(打印标签)

#打印行首
[root@python ~]# awk 'BEGIN{print "SERVICEttPORTtttDESCRIPION"}{print $0}' test.txt
SERVICE         PORT                    DESCRIPION
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject

#打印行尾
[root@python ~]# awk '{print $0}END{print "The ending..."}' test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject
The ending...

#行首行尾都打印
[root@python ~]# awk 'BEGIN{print "SERVICEttPORTtttDESCRIPION"}{print $0}END{print "The ending..."}' test.txt
SERVICE         PORT                    DESCRIPION
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject
The ending...

9、表达式与操作符:如果在awk定义的变量中没有初始化,则初始值为空字符串或者0,字符操作一定要加引号(a=”How are you”)。

#统计所有的空白行
[root@python ~]# awk '/^$/{print x+=1}' /etc/ssh/ssh_config
1
2
3
4

#打印出空白行的总个数
[root@python ~]# awk '/^$/{x+=1}END{print x}' /etc/ssh/ssh_config
4

#~(匹配) 、!~(不匹配).打印出root的ID号。匹配第一个字段为root的行,打印出其UID
[root@python ~]# awk -F':' '$1~/root/{print $3}' /etc/passwd
0

#打印出UID大于400的用户
[root@python ~]# awk -F':' '$3>400{print $1}' /etc/passwd
saslauth
mysql
dianel
[root@python ~]# awk -F':' '$3>400{x+=1}END{print x}' /etc/passwd
3

10、awk的高级应用

if条件判断

#判断boot分区可用容量小于20M时报警,否则就显示OK
[root@python ~]# df|grep 'boot'|awk '{if($4<20000)print"alart";else print "ok"}'
ok

[root@python ~]# seq 5|awk '{if($0==3)print $0}'
3
[root@python ~]# seq 5|awk '{if($0==3)print $0;else print "no"}'
no
no
3
no
no

while循环:语法格式

while (condition) statement
do statement while (condition)

两种格式:

#因为i和total都初始化,默认为0
[root@python ~]# awk 'BEGIN{do {i++;total+=i}while(i<100)print total}' 
5050

[root@python ~]# awk 'BEGIN{while(i<100){i++;total+=i}print total}' 
5050

for循环:语法格式

1.for (expr1; expr2; expr3) statement
2.for (var in array) statement

[root@python ~]# awk 'BEGIN{for(i=0;i<101;i++){total+=i} print total}'
5050

break和continue

  • break:跳出循环

  • continue:终止当前循环

打印IP:

[root@python ~]# ifconfig eth0|awk '/Bcast/'|awk -F'[ :]+' '{print $4}'
192.168.1.13

配置apt upgrade禁止更新ubuntu系统内核

Ubuntu系统下的apt工具用来管理软件包很是方便,就连Linux系统内核都可以使用apt来更新。

而apt upgrade命令比较不够灵活,这一个命令下去,系统中所有可以升级的软件包都会被升级,包括系统内核,这也是Linux系统的一个软件包。有的时候,有些软件包我们不想升级,特别是系统内核。

可以用下面的方法来避免使用apt upgrade命令时,升级某个软件包,麦新杰就用系统内核包来举例说明。

首先,使用命令uname -r查看系统使用的内核名称;

使用dpkg –get-selections linux-image*命令查询已经安装内核的列表:

xinlin@ubuntu:~$
xinlin@ubuntu:~$ dpkg --get-selections linux-image*
linux-image-4.10.0-27-generic install
linux-image-4.4.0-31-generic install
linux-image-4.4.0-83-generic install
linux-image-extra-4.4.0-31-generic install
linux-image-extra-4.4.0-83-generic install
linux-image-generic install
xinlin@ubuntu:~$
xinlin@ubuntu:~$ uname -r
4.10.0-27-generic
xinlin@ubuntu:~$

可以看出系统使用的内核是linux-image-4.10.0-27-generic。

将正在使用的内核软件包设置为hold状态,这个命令只能是root用户执行,不建议修改dpkg工具相关文件的属性。(位置:/usr/lib/dpkg)

xinlin@ubuntu:~$
xinlin@ubuntu:~$ su
Password:
root@ubuntu:/home/xinlin# cd
root@ubuntu:~# echo "linux-image-4.10.0-27-generic hold" | dpkg --set-selections
root@ubuntu:~#
root@ubuntu:~# exit
exit
xinlin@ubuntu:~$
xinlin@ubuntu:~$ dpkg --get-selections linux-image-4.10.0-27-generic
linux-image-4.10.0-27-generic hold
xinlin@ubuntu:~$

这样,就实现了使用apt upgrade命令,不升级hold软件包的功能。

注意:设置为hold状态的软件包,对于apt upgrade命令,不会升级,但是依然可以通过apt install命令将其升级并将状态切回install。因此,从安全性角度来看,这样做其实效果不大,仅仅只是避免了偶然的操作失误。本人还是推荐执行apt install来进行“定点升级”。

解决Apache获取客户端真实IP的问题

起因:

服务器端部署apache+php应用,使用$_server[‘remote_addr’]获取的ip为127.0.0.1。虽然说可以通过其他方案来获取正确的ip,但强迫症发作,觉得这是不可接受的。

解决方案:

作为反代服务器的NGINX配置:

确保server中包含以下内容:

server{
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

作为后端服务器的Apache配置:

1、启用mod_remoteip模块

a2enmod remoteip

或(debian系统有效):

sudo ln -s /etc/apache2/mods-available/remoteip.load /etc/apache2/mods-enabled/remoteip.load

2、修改配置文件(以debian为例)

方案一:在使用Cloudflare后,客户端的真实ip会被放在HTTP_CF_CONNECTING_IP中,直接使用即可

在apache2.conf中或虚拟主机的配置文件中添加以下内容

<IfModule remoteip_module>
RemoteIPHeader CF-Connecting-IP
RemoteIPInternalProxy 127.0.0.1/24
</IfModule>

修改apache access log格式(在/etc/apache2/site-enable/*.conf中添加或修改)

LogFormat "%{CF-Connecting-IP}i %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" proxied
CustomLog ${APACHE_LOG_DIR}/access-example.com.log proxied

方案二:手动剔除Cloudflare所有的ip

Cloudflare使用的ip可以在https://www.cloudflare.com/ips/查询

<IfModule remoteip_module>
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1/24
#CloudFlare IP Ranges
RemoteIPInternalProxy 103.21.244.0/22
RemoteIPInternalProxy 103.22.200.0/22
RemoteIPInternalProxy 103.31.4.0/22
RemoteIPInternalProxy 104.16.0.0/12
RemoteIPInternalProxy 108.162.192.0/18
RemoteIPInternalProxy 131.0.72.0/22
RemoteIPInternalProxy 141.101.64.0/18
RemoteIPInternalProxy 162.158.0.0/15
RemoteIPInternalProxy 172.64.0.0/13
RemoteIPInternalProxy 173.245.48.0/20
RemoteIPInternalProxy 188.114.96.0/20
RemoteIPInternalProxy 190.93.240.0/20
RemoteIPInternalProxy 197.234.240.0/22
RemoteIPInternalProxy 198.41.128.0/17
</IfModule>

其他:若未使用cdn:

<IfModule remoteip_module>
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1/24
</IfModule>

3、补充:若后端服务器为NGINX

在配置文件中添加:

set_real_ip_from 127.0.0.1/24;
real_ip_header X-Forwarded-For;

Ubuntu系统的APACHE HTTPS安装和配置

Ubuntu下HTTPS配置非常简单,对大部分用户而言,使用普通的自签名证书,只需按照步骤进行就可以了,无需了解密钥、证书的更多知识,更深的背景知识还有RSA算法、DES算法、X509规范、CA机构…等等,随便哪个方向都够学习一阵子的,所幸的是有了OpenSSL、OpenSSH等这些开源免费的软件,把很多底层的算法、规范都集成了,对上层应用而言,只需一二三操作即可,至多到官网去查查一些特殊的命令集。

一、安装Apache

$sudo apt-get install apache2

使用此方式安装的APACHE,配置比较分散,一般如下:

  • 默认站点在 /var/www/
  • 配置文件在 /etc/apache2/
  • 日志在 /var/log/apache/
  • 启动脚本是 /usr/bin/apache2ctl 或者 /etc/init.d/apache2

二、安装openssl

Ubuntu默认已经安装了OPENSSL,如果没安装,

$sudo apt-get install openssl

三、开启SSL模块

$sudo a2enmod ssl

这条命令相当于

sudo ln -s /etc/apache2/mods-available/ssl.load /etc/apache2/mods-enabled
sudo ln -s /etc/apache2/mods-available/ssl.conf /etc/apache2/mods-enabled

如果没有a2enmod指令,也可直接在apache2.conf中设置SSL模块加载:

LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so

四、创建证书

创建证书有两种:一种是自签名证书,另外一种是第三方CA机构签名证书。第一种随便使用,只是没有经过官方认可的机构认证而已,后一种则是正规的签名证书,有发证机构签名。其实很多所谓的大网站上使用的SSL证书,一样都是自签名的,主要是因为这个证书只做为在线验证使用,保证传输数据安全即可,不过使用这种证书,对常规浏览器和一些软件而言,一般均会弹出警告,让你确认这个签名证书的有效性。正规签名证书也不过只是多了一重保障而已,而且浏览器、软件等可以自己鉴别。

1、自签名证书

可使用apache内置的工具创建默认的自签名证书,通过-days参数指定有效期。

$sudo apache2-ssl-certificate

注意:上述命令可能在最新的apache中默认未安装,如果使用的是LAMP套件,一般会有这个模块。

不过我们还可以使用openssl命令(如果openssl是自编译安装的,没有注册该命令的话,可以使用绝对路径,比如类似/usr/local/openssl/openssl这样)创建:

$sudo openssl req -x509 -newkey rsa:1024 -keyout apache.pem -out apache.pem -nodes -days 999

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:SH
Locality Name (eg, city) []:SH
Organization Name (eg, company) [Internet Widgits Pty Ltd]:ABC
Organizational Unit Name (eg, section) []:RD
Common Name (eg, YOUR name) []:myservername
Email Address []:[email protected]

注意:在要求输入Common Name (eg, YOUR name) 时,输入你的主机名(授权主机)。

创建完成后,当前目录下有个apache.pem文件,已经包含密钥和证书。可以把这个证书拷贝到/etc/apache2/下创建一个ssl目录然后拷贝到:

/etc/apache2/ssl/apache.pem

2、第三方CA机构签署证书

生成此证书,需要向第三方提交一个“生成证书请求文件(CSR)”,生成这个CSR需要两步:

  • 生成私钥KEY
  • 生成请求CSR

A、运行如下命令生成私钥:

[root@localhost tmp]#  openssl genrsa -des3 1024 -out server.key
Generating RSA private key, 1024 bit long modulus
...++++++
.......................................................++++++
e is 65537 (0x10001)
Enter pass phrase:
Verifying - Enter pass phrase:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,32BDD10A4F977F7E

A7FOhETnGpQnRcCoe1VtOtb8yq12xi6ljq/6wYB6MsGGdGjhftF1FxnSMd9Jx4o2
gjyUJNQs4zYkrtAaY6WYdOMiswymxiSYlKnX3l3uA6d6NqUpoyFxN7mgxqvbrdjq
EaGSLo1d63B6THIq9mOGNm3l1xKtiUZVwHqVaHdb1F/RD6YshwE9yE5bAXjKJKat
sTewoVPxj0bjEDBz49K4m+epUrh7UQ7ZyjMiefvCgg0OxFB3H8zdG0SHa1sV4fG4
9R+4PPoIIlLty4oyAYRwNVUWV47qGV4Jub11s50azVwtS9CV31HZQt48zkcUZ9WF
2PBRQ3c4AMkewzfvjEUIF7bfHcBMl9ugu4fZfJTUxJbA4vHvoVczXhvcTaf6awHn
4YpEX/T3xWE8ObyOjvVh5Utl39INOqzxVKGoZF1ogLFm60SokYx0r+Y19jrz2084
Nri4mHlYNymY+tviTFUUIHZ+8FRnkq0vnW68ejiSzG0Xyr2DDzc7pi6J58bqQ3yR
eNJuK8KVjXxkRn1HXtGL/C6ZpJ4qs6VVzX291vPrr7luWrXGsAcRudLWRFZDSoUB
lFw1CY9ol2TOX2mvt6JryhoH08x3s2prlIen10N35sVELB/nb1/8kkpztCbPHiA/
IH2A3g/WexCAatmCMuqlxW8Cwe98AUZduuZsAZeDoyXk7VxD7YhPKZmKPKOx3gZv
2S1ZpzsNgZh9HhpXsRxjwbYnyICtcUY+dQZXk1w//BP2syjcffXqqhp2FPK3SG7l
PsvHIWQGeTUe5uILP7S/Bbi/KrFAkDguGJHge0mmHgL9gi8RvODtKQ==
-----END RSA PRIVATE KEY-----
[root@localhost tmp]#

这里使用了-des3参数,将会需要输入一个密码对私钥进行加密,每次使用此私钥也需要输入此密码,如不需对私钥加密请不要使用-des3选项。输入两次密码后,将会生成server.key私钥文件。

B、生成请求文件

运行如下命令生成证书请求文件(CSR)

openssl req -new -key server.key –out server.csr

把这个CSR文件传给CA机构,然后他们会使用此请求文件生成证书。

五、编辑HTTPS(SSL)配置

A、添加监听端口

编辑Apache端口配置(/etc/apache2/ports.conf),加入443端口(SSL缺省使用):

Listen 80

Listen 443

B、设置site-enabled

上文安装完后,会在/etc/apache2/sites-available/目录下生成一个缺省的default-ssl文件。缺省的网页目录仍然是/var/www/。我们可以创建一个链接到site-enabled目录。

ln -s /etc/apache2/sites-available/default-ssl /etc/apache2/sites-enabled/001-ssl

C、修改配置文件

确认HTTP监听端口改为80

#vi /etc/apache2/sites-enabled/000-default
NameVirtualHost *:80

ServerAdmin webmaster@localhost
#[......]

HTTPS监听端口缺省443:

把端口改为443,在下加入SSL认证配置,其它的根据需要自己定制 与普通配置无异:

#vi /etc/apache2/sites-enabled/001-ssl
NameVirtualHost *:443

<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/apache.pem
ServerAdmin webmaster@localhost
DocumentRoot /var/www
ServerName myServerName
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/>
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
# This directive allows us to have apache2's default start page
# in /apache2-default/, but still have / go to the right place
#RedirectMatch ^/$ /apache2-default/
</Directory>

六、重启APACHE

# service httpd restart

或,

# /etc/init.d/apache2 restart

测试https://localhost/,IE会弹出security alert(安全警告),如下图:

未分类

Firefox会提示是否添加Exception信息,如下图:

未分类