Docker安装部署MySQL5.7

1、进入Linux后,使用Docker命令下载MySQL,命令如:

docker pull mysql:5.7

运行该命令后,则会显示以下日志:

[root@localhost ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
ad74af05f5a2: Pull complete 
0639788facc8: Pull complete 
de70fa77eb2b: Pull complete 
724179e94999: Pull complete 
50c77fb16ba6: Pull complete 
d51f459239fb: Pull complete 
937bbdd4305a: Pull complete 
35369f9634e1: Pull complete 
f6016aab25f1: Pull complete 
5f1901e920da: Pull complete 
fdf808213c5b: Pull complete 
Digest: sha256:96edf37370df96d2a4ee1715cc5c7820a0ec6286551a927981ed50f0273d9b43
Status: Downloaded newer image for mysql:5.7

2、先查看本机都有哪些镜像,命令如下:

docker images
[root@localhost ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mysql               5.7                 c73c7527c03a        8 days ago          412MB
hello-world         latest              1815c82652c0        7 weeks ago         1.84kB
java                latest              d23bdf5b1b1b        6 months ago        643MB
learn/tutorial      latest              a7876479f1aa        4 years ago         128MB

3.然后启动我们的mysql的docker容器,命令如下:

docker run --name mysql5.7 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7

注意,这里的容器名字叫:mysql5.7,mysql的root用户密码是:123456,映射宿主机子的端口3306到容器的端口3306,仓库名mysql和标签(tag)唯一确定了要指定的镜像,其实如果这里只有一个mysql也有必要要tag,执行该命令返回的结果是:

9238d9feb10a0c553d950451add144727b659a0972ccf04d7c59c3bfa198ed20

4.查看已经运行的的所有容器,命令如:docker ps

[root@localhost ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
9238d9feb10a        mysql:5.7           "docker-entrypoint..."   12 seconds ago      Up 9 seconds        0.0.0.0:3306->3306/tcp   mysql5.7
[root@localhost ~]#

5.使用mysql的工具,比如navicat连接成功如下:

未分类

docker导出镜像及导入镜像并启动容器

备份镜像方法

docker commit -p 30b8f18f20b4 container-backup

备份到本地方法

docker save -o ~/container-backup.tar container-backup

导入本地镜像

docker load -i ~/container-backup.tar

启动本地镜像

docker load -i ~/container-backup.tar

启动容易方法方法

docker run -d -p 812:80 -p 2222:22 -p 3308:3306 --name centos2lnmp -v /Users/haha/wwwroot/docker:/home/wwwroot/default/ centos6

进入容器方法

docker exec -it centos6 /bin/bash

用awk实现按列求和及求平均值

平常的统计中肯定需要用上对取出来的一列数据进行求和求平均值。使用awk很容易实现。具体演示的命令如下:

[root@123 build]# ll
total 336
-rw-rw-r-- 1 1000 1000   3330 Mar 14 21:17 ax_check_compile_flag.m4
-rw-rw-r-- 1 1000 1000   2200 Mar 14 21:17 build2.mk
-rwxrwxr-x 1 1000 1000   2165 Mar 14 21:17 buildcheck.sh
-rw-rw-r-- 1 1000 1000   2510 Mar 14 21:17 build.mk
-rwxrwxr-x 1 1000 1000    381 Mar 14 21:17 config-stubs
-rw-rw-r-- 1 1000 1000    700 Mar 14 21:17 genif.sh
-rw-rw-r-- 1 1000 1000 230377 Mar 14 21:17 libtool.m4
-rw-rw-r-- 1 1000 1000   2225 Mar 14 21:17 mkdep.awk
-rw-rw-r-- 1 1000 1000   1556 Mar 14 21:17 order_by_dep.awk
-rw-rw-r-- 1 1000 1000    122 Mar 14 21:17 print_include.awk
-rw-rw-r-- 1 1000 1000    410 Mar 14 21:17 scan_makefile_in.awk
-rwxrwxr-x 1 1000 1000  65761 Mar 14 21:17 shtool
[root@123 build]# ll | awk '{print $5}'
3330
2200
2165
2510
381
700
230377
2225
1556
122
410
65761
[root@123 build]# ll | awk '{a=a+$5;b++;}END{print a,b,a/b}'
311737 13 23979.8
[root@123 build]# ll | awk '/build/ {print $9,$5}'                  
build2.mk 2200
buildcheck.sh 2165
build.mk 2510
[root@123 build]# ll | awk '/build/ {a=a+$5;b++}END{print a,b,a/b}' 
6875 3 2291.67
[root@123 build]# 

Linux awk命令详解

awk :适用程序,一种unix工具

就是一个强大的文本分析工具,相对于grep查找、sed的编辑,awk在对数据分析并生成报告的时候,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种处理。

  • awk是用来操作数据和产生报表的一种编程语言。数据可能来自标准输入、一个或多个文件或一个进程的输出等。awk可以用在命令行里进行简单操作,也可应用到较大的应用程序中。
  • awk是一门编程语法,作为unix工具来使用简化了很多,但是仍然有许多编程语言的特性,可以对目标进行一系列的处理。

  • 如果抛出awk的BEGIN和END,对文件的每行,awk都分两个阶段处理:

1、读取该行内容,分配临时寄存器,分配域名等操作;
2、对域做各种处理并输出;

  • awk基本用途

1、简单输出,如 awk ‘{print 1,NF}’ — print的规则
2、作为分隔符使用

单字符分隔符:打印系统中用户名和其他使用shell类型
单字符分隔符,管道连续使用awk:打印nginx日志中的访问目录。
多字符分隔符:抓取apache详细版本
多字符多个分隔符:截取ip地址
正则分隔符:截取ip地址

[root@admin test]# ls -l
total 16
-rw-r--r--. 1 root root    0 Jun 15 11:03 aa.jpg
-rw-r--r--. 1 root root   12 Jun 15 10:48 ls.jpg
-rw-r--r--. 1 root root  200 Jun 15 11:31 result.jpg
-rw-r--r--. 1 root root    0 Jun 15 11:04 right.jpg
drwxr-xr-x. 2 root root 4096 Jun 20 06:39 sd.ex
-rw-r--r--. 1 root root   49 Jun 15 11:04 wrong.jpg

通过awk打印出第一列:

[root@admin test]# ls -l | awk '{print $1}'
total
-rw-r--r--.
-rw-r--r--.
-rw-r--r--.
-rw-r--r--.
drwxr-xr-x.
-rw-r--r--.

打印前两列,也就是前两个域:

[root@admin test]# ls -l | awk '{print $1,$2}'
total 16
-rw-r--r--. 1
-rw-r--r--. 1
-rw-r--r--. 1
-rw-r--r--. 1
drwxr-xr-x. 2
-rw-r--r--. 1

在awk中,抛开BEGIN和END不看,’{}’ 这是一个固定写法。

$ : 符号表示域,域之间通过默认的分隔符 空格 分开,如果有多个空格就会变成一个空格,第一个域为$1,第二个为$2等…

打印最后一列:

[root@admin test]# ls -l | awk '{print $NF}'
16
aa.jpg
ls.jpg
result.jpg
right.jpg
sd.ex
wrong.jpg

$NF : 就代表最后一列的意思。
如果想打印倒数第二列,则使用$(NF -1)。

[root@admin test]# ls -l | awk '{print $(NF - 1)}'
total
11:03
10:48
11:31
11:04
06:39
11:04

-F :参数-F是改变awk的默认分隔符,可以支持正则表达式。

[root@admin test]# ls -l
total 16
-rw-r--r--. 1 root root    0 Jun 15 11:03 aa.jpg
-rw-r--r--. 1 root root   12 Jun 15 10:48 ls.jpg
-rw-r--r--. 1 root root  200 Jun 15 11:31 result.jpg
-rw-r--r--. 1 root root    0 Jun 15 11:04 right.jpg
drwxr-xr-x. 2 root root 4096 Jun 20 06:39 sd.ex
-rw-r--r--. 1 root root   49 Jun 15 11:04 wrong.jpg

awk默认的分隔符是 空格,空格前一列就是$1,后面是$2,$3…..
当我们改变默认分隔符为 分号 “:”时,打印一下结果:

[root@admin test]# ls -l | awk -F":" '{print $NF}'
total 16
03 aa.jpg
48 ls.jpg
31 result.jpg
04 right.jpg
39 sd.ex
04 wrong.jpg

通过-F 参数改变默认分隔符,以及其支持正则表达式的特性,精确的抽出ifconfig 文件中的ip地址。

#精确的抽取ip地址
[root@admin test]# ifconfig | grep "inet addr" | awk -F "addr:|  *" '{print $4}'
192.168.1.6
127.0.0.1

分析:

grep "inet addr" 表示先抽取出含有ip的那行,然后通过管道交给awk去处理

-F "addr:|  *" :表示该变默认分隔符为addr:或者连续多个空格

$4 :表示改变默认分隔符以后,ip地址在第4个域。

用grep实现抽取ip :

[root@admin home]# ifconfig | egrep -o "[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]*"
192.168.1.6
192.168.1.255
255.255.255.0
127.0.0.1
255.0.0.0

然后在用head和tail抽取想要的ip即可。

使用sed实现抽取ip

[root@admin home]# ifconfig |grep "inet addr" | sed 's/inet addr://' | sed 's/Bcast.*//' | head -1
192.168.1.6
或者:
[root@admin home]# ifconfig |grep "inet addr" | sed 's/^.*addr://;s/  *.*//' | head -1
192.168.1.6

awk匹配打印

注:需要匹配的内容是写在{}外面的,通过//来体现匹配。
匹配打印实际上是文件每行读取的时候做的处理。

1、整行中匹配内容

awk '/sth/{print $1}' ---打印匹配sth的行的第一个域,这是对整行进行操作
awk '!/sth/{print $1}'  ---打印不匹配sth的行的第一个域

如匹配打印sd关键字:

[root@admin test]# ls
aa.jpg  ls.jpg  result.jpg  right.jpg  sd.ex  wrong.jpg

[root@admin test]# ls | awk '/sd/{print $1}'
sd.ex

2、域匹配内容

匹配某个文件中以默认分隔符分隔的$1域中含有关键字sth的行,然后打印出第一个域,命令如下:

awk '$1~/sth/{print $1}' 

3、改变awk中的默认分隔符

如打印出passwd文件中,以冒号 : 分隔的第一个域中含有关键字wcx行的第一个域:

[root@admin etc]# cat passwd | awk -F":" '$1~/wcx/{print $1}'
wcx

awk判断打印

判断打印实际上是文件每行读取后做的处理。
格式例如:

awk '{if($1=="wcx")print $1}'

if 判断必须是进入某一行以后才能做的,所以必须是在花括号{} 里面。而模式匹配是先匹配到内容的行才能进行下一步操作,所以模式匹配实在花括号{}外边。

[root@admin etc]# cat ./passwd | awk -F ":" '{if($1 == "wcx")print}
wcx:x:501:501::/home/wcx:/bin/bash

上面命令中,如果passwd文件中,以冒号分隔的第一个域==”wcx”,那么就打印出来,这是等于判断,所以只有wcx的行能出来。

BEGIN和END

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

awk的数组

有一个awk.txt文件,其内容如下:

[root@admin home]# cat awk.txt
kk kekea
jj d32
jame 23
sr
sr
kk
jame
wcx
jame 

统计一下awk.txt文件中每个人名出现的次数:

[root@admin home]# cat awk.txt | awk '{a[$1]++}END{for (i in a)print i,a[i]}'
wcx 1
jj 1
kk 2
sr 2
jame 3

解题思路分析:

  • {a[$1]++} :awk开始扫描管道传过来的内容,但是么有存储功能,现在我们需要计算每个人名出现的额次数,所以需要把扫描到的相同的人名分别存储起来,这时候就必须用数组,并且这里的数组都是hash数组,就是键值对的数组,这里的键—对应的就是对应域中的人名,如kk,值 — 对应的就是kk这个人名出现的次数。数组a中的下标$1代表的是第一个域中列名,如wcx。awk开始逐行扫描,每扫到一个相同的人名,对应的hash数组里的值就+1,如a[kk]++,直到扫到文件的结尾。(注:数组的默认值为空,如果要做++操作了,就会把里的值变成0)

  • END : END之后列出的操作将在Unix awk开始扫描完全部的输入之后执行。上面扫描完以后,就开始执行END后面的操作了。

  • {for (i in a)print i,a[i]} :利用for循环去遍历刚刚产生的hash数组,然后打印出数组下标所代表的值,也就是人名以及对数组中的值,也就是对应人名出现的次数。这里的i,就代表数组的下标,也就是域中的人名,如kk。

awk输出分隔符

awk默认的输出分隔符也是空格,如果想改变默认输出分隔符怎么做?

[root@admin home]# cat awk.txt | awk '{print $1,$2}'
kk kekea
jj d32
jame 23
sr 234
sr edr
kk 23
jame rt
wcx 88
jame 34

运行结果看出,打印时,$1和$2间加了逗号以后,输出的文本中域间就都是用空格隔开的。

如果我不加逗号,改为加一串别的字符时,输出后就以加入的字符作为分隔符了。如:

[root@admin home]# cat awk.txt | awk '{print $1"--"$2"BB"}'
kk--kekeaBB
jj--d32BB
jame--23BB
sr--234BB
sr--edrBB
kk--23BB
jame--rtBB
wcx--88BB
jame--34BB

通过awk批量修改扩展名

[root@admin test]# ls
aa.jpg  ls.jpg  result.jpg  right.jpg  wrong.jpg

将test目录下的以.jpg结尾的文件改成.txt结尾的文件:

[root@admin test]# ls | awk -F "." '{print "mv "$1"."$2" "$1".txt"}'|sh
[root@admin test]# ls
aa.txt  ls.txt  result.txt  right.txt  wrong.txt

或者:
[root@admin test]# ls | awk -F "." '{print "mv "$0" "$1".txt"}' | sh
#$0表示整行的内容

思路分析,同样改名也是需要构造mv a.txt a.jpg这样类似的语句的,这就用到了上面讲的输出时修改默认分隔符的操作了。构造出的mv语句再传给sh去执行就可以实现改名的操作。

awk内置变量使用介绍

未分类

我们将逐渐揭开 awk 功能的神秘面纱,在本节中,我们将介绍 awk 内置built-in变量的概念。你可以在 awk 中使用两种类型的变量,它们是:用户自定义user-defined变量和内置变量。awk 内置变量已经有预先定义的值了,但我们也可以谨慎地修改这些值.

awk 内置变量包括

  • FILENAME : 当前输入文件名称
  • NR : 当前输入行编号(是指输入行 1,2,3……等)
  • NF : 当前输入行的字段编号
  • OFS : 输出字段分隔符
  • FS : 输入字段分隔符
  • ORS : 输出记录分隔符
  • RS : 输入记录分隔符

FILENAME

让我们继续演示一些使用上述 awk 内置变量的方法,想要读取当前输入文件的名称,你可以使用 FILENAME 内置变量,如下:

$ awk ' { print FILENAME } ' ~/domains.txt

未分类

你会看到,每一行都会对应输出一次文件名,那是你使用 FILENAME 内置变量时 awk 默认的行为。我们可以使用 NR 来统计一个输入文件的行数(记录),谨记:它也会计算空行,正如我们将要在下面的例子中看到的那样。 输出文件内容 当我们使用 cat 命令查看文件 domains.txt 时,会发现它有 14 行文本和 2 个空行:

$ cat ~/domains.txt

未分类

awk 统计行数

$ awk ' END { print "Number of records in file is: ", NR } ' ~/domains.txt

未分类

awk 统计文件中的字段数

$ awk '{ "Record:",NR,"has",NF,"fields" ; }' ~/names.txt

未分类

FS 内置变量

你也可以使用 FS 内置变量指定一个输入文件分隔符,它会定义 awk 如何将输入行划分成字段。FS 默认值为“空格”和“制表符”,但我们也能将 FS 值修改为任何字符来让 awk 根据情况切分输入行。有两种方法可以达到目的:第一种方法是使用 FS 内置变量;第二种方法是使用 awk 的 -F 选项。来看 Linux 系统上的 /etc/passwd 文件,该文件中的各字段是使用 冒号(:) 分隔的,因此,当我们想要过滤出某些字段时,可以将冒号(:) 指定为新的输入字段分隔符, awk 过滤密码文件中的各字段 . 我们可以使用 -F 选项,如下:

$ awk -F':' '{ print $1, $4 ;}' /etc/passwd

未分类

此外,我们也可以利用 FS 内置变量,如下:

$ awk ' BEGIN { FS=“:” ; } { print $1, $4 ; } ' /etc/passwd

未分类

使用 OFS 内置变量

使用 OFS 内置变量来指定一个用于输出的字段分隔符,它会定义如何使用指定的字符分隔输出字 段 使用 awk输出的分隔符:

$ awk -F':' ' BEGIN { OFS="==>" ;} { print $1, $4 ;}' /etc/passwd

未分类

在本节中,我们已经学习了使用含有预定义值的 awk 内置变量的理念。但我们也能够修改这些值,虽然并不推荐这样做,除非你明白自己在做什么,并且充分理解(这些变量值)。

Ubuntu下设置APT的http代理

在公司的网络中,系统有时候需要设置了网络代理之后,才能正常的访问外网,那么下面将会介绍如何在ubuntu linux系统中设置网络代理。

1、编辑/etc/apt/apt.conf 配置文件(如果/etc/apt/目录下没有apt.conf文件,那么需要手动创建)

2、按照下面的格式,将网络代理配置信息加入到apt.conf文件里。

Acquire::http::proxy “http://user:passwd@proxyserver:port”;

例如: Acquire::http::Proxy “http://192.168.0.1:80“;

3、保存退出当前配置文件

4、运行 sudo apt-get update 命令,来检测ubuntu系统是否能够正常更新。

ubuntutest@ubuntutest:~$ sudo apt-get update
[sudo] password for ubuntutest:
命中:1 http://archive.ubuntu.com/ubuntu xenial InRelease
获取:2 http://security.ubuntu.com/ubuntu xenial-security InRelease [102 kB]
获取:3 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
获取:4 http://archive.ubuntu.com/ubuntu xenial-backports InRelease [102 kB]
已下载 306 kB,耗时 1秒 (250 kB/s)
正在读取软件包列表... 完成

CentOS 7.0安装LAMP服务器(PHP+MariaDB+Apache)

1、关闭firewall:

systemctl stop firew
alld.service #停止firewall
systemctl disable firewalld.service #禁止firewall开机启动

2、安装iptables防火墙(#可不安装)

yum install iptables-services #安装
vi /etc/sysconfig/iptables #编辑防火墙配置文件
//配置文件:
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
//:wq! #保存退出

关闭 SELINUX

vi /etc/selinux/config
#SELINUX=enforcing #注释掉
#SELINUXTYPE=targeted #注释掉
SELINUX=disabled #增加
:wq! #保存退出
setenforce 0 #使配置立即生效

一、Apache安装

yum install httpd #根据提示,输入Y安装即可成功安装
systemctl start httpd.service #启动apache
systemctl stop httpd.service #停止apache
systemctl restart httpd.service #重启apache
systemctl enable httpd.service #设置apache开机启动

二、安装MariaDB

yum install mariadb mariadb-server 
//#询问是否要安装,输入Y即可自动安装,直到安装完成
systemctl start mariadb.service #启动MariaDB
systemctl stop mariadb.service #停止MariaDB
systemctl restart mariadb.service #重启MariaDB
systemctl enable mariadb.service #设置开机启动
cp /usr/share/mysql/my-huge.cnf /etc/my.cnf 
//拷贝配置文件(注意:如果/etc目录下面默认有一个my.cnf,直接覆盖即可)

设置密码

mysql_secure_installation
systemctl restart mariadb.service

三、安装PHP

//主程序
yum install php
//安装模块
yum install php-mysql php-gd libjpeg* php-ldap php-odbc php-pear php-xml php-xmlrpc php-mbstring php-bcmath php-mhash
systemctl restart mariadb.service #重启MariaDB
systemctl restart httpd.service #重启apache

四、安装phpMyAdmin

//主程序
sudo yum install phpmyadmin php-mcrypt
//修改配置文件
vi /etc/httpd/conf.d/phpMyAdmin.conf 

<Directory /usr/share/phpMyAdmin/>
  AddDefaultCharset UTF-8

  <IfModule mod_authz_core.c>
   # Apache 2.4
   <RequireAny>
    #Require ip 127.0.0.1
    #Require ip ::1
    Require all granted
   </RequireAny>
  </IfModule>
  <IfModule !mod_authz_core.c>
   Order Deny,Allow
   Deny from All
   Allow from 127.0.0.1
   Allow from ::1
  </IfModule>
</Directory>

<Directory /usr/share/phpMyAdmin/setup/>
  <IfModule mod_authz_core.c>
   # Apache 2.4
   <RequireAny>
    #Require ip 127.0.0.1
    #Require ip ::1
    Require all granted
   </RequireAny>
  </IfModule>
  <IfModule !mod_authz_core.c>
   Order Deny,Allow
   Deny from All
   Allow from 127.0.0.1
   Allow from ::1
  </IfModule>
</Directory>

systemctl restart httpd #重启httpd

Apache如何安装配置证书?

格式说明

首先 Apache 使用的证书是 .pem 格式的。什么是 .pem 格式,就是以 —–BEGIN xxx—– 开头的文件,如:

-----BEGIN CERTIFICATE-----
MIID6jCCAtKgAwIBAgIBFDANBgkqhkiG9w0BAQsFADB7MQswCQYDVQQGEwJDTjER  
MA8GA1UECAwIU2hhbmdoYWkxDzANBgNVBAoMBmRlZXB6ejEPMA0GA1UECwwGZGVl  
cHp6MRMwEQYDVQQDDApkZWVwenouY29tMSIwIAYJKoZIhvcNAQkBFhNkZWVwenou  
... ...
-----END CERTIFICATE-----

当然现在 openssl 新版签发的证书,在 —–BEGIN xxx—– 之前还有一段信息,我们这里就不举例了。

所以,当我门从证书服务商那里下载证书的时候需要注意证书的格式问题。选择 Apache 进行下载。

一般的,证书服务商会提供三个文件:

example.com.ca.crt  
example.com.crt  
example.com.key  
  • example.com.ca.crt,CA 中间证书。
  • example.com.crt,你的服务器证书文件。
  • example.com.key,你的私钥。

还有可能是如下文件:

example.com.crt  
example.com.key  
  • example.com.key 你的私钥文。
  • example.com.crt 是你的证书文件或证书链文件。用文本编辑器打开,如果看到两张上面 pem 内容,即站点证书+CA中间证书。你需要将它们分别存为两个文件,如 example.com.ca.crt 和 example.com.crt。如果不是,请联系证书服务商或到 这里 补全证书链。

安装证书

首先,确保 ssl 模块已经加载进 apache 了:

$ a2enmod ssl

如果你看到了 Module ssl already enabled 这样的信息就说明你成功了。如果你看到了 Enabling module ssl,那么你还需要用下面的命令重启apache:

$ service apache2 restart

最后像下面这样修改你的虚拟主机文件(通常在/etc/apache2/sites-enabled 下):

DocumentRoot /var/www/html/  
ServerName example.com  
SSLEngine on  
SSLCertificateFile /usr/local/ssl/crt/example.com.crt  
SSLCertificateKeyFile /usr/local/ssl/example.com.key  
SSLCACertificateFile /usr/local/ssl/example.com.ca.crt  

你现在应该可以用 https://example.com(注意使用 https 而不是 http)来访问你的网站了,并可以看到SSL的进度条了(通常在你浏览器中用一把锁来表示)。

使用zabbix监控apache性能

原理

监控原理跟之前写的监控nginx差不多,都是利用web服务器自身提供的状态信息页获取运行状态信息。apache的监控状态信息如下:

Total Accesses: 252523

Total kBytes: 2154830

CPULoad: 2.72004

Uptime: 16624

ReqPerSec: 15.1903

BytesPerSec: 132733

BytesPerReq: 8738

BusyWorkers: 1

IdleWorkers: 9

Scoreboard: 

我们一般只需要这四个数据:ReqPerSec、BytesPerSec、BusyWorkers、IdleWorkers

开启apache status

创建状态页配置文件/etc/httpd/conf.d/status.conf

Listen 89
<VirtualHost *:89>
    CustomLog /dev/null common
    ErrorLog /dev/null
    <Location "/server-status">
        SetHandler server-status
        Require ip 192.168.7.227
    </Location>
</VirtualHost>

CustomLog 和 ErrorLog在这里的作用是将日志写入/dev/null,即关闭咋apache状态页面日志记录。但是CustomLog和ErrorLog指令不能放到Location里,所以就新建一个VirtualHost。

访问http://192.168.7.227:89/server-status?auto即可得到上面的状态信息。我这里测试用的就是zabbix-server安装时自带的apache,所以限定可以访问的IP和apache服务器的IP是同一个。

创建zabbix模板

/etc/zabbix/zabbix_agentd.d/apache_status.sh

#!/bin/bash

URL="http://192.168.7.227:89/server-status?auto"

function ReqPerSec(){
/usr/bin/curl -s $URL |grep ReqPerSec|awk '{print $2}'
}

function BytesPerSec(){
/usr/bin/curl -s $URL |grep BytesPerSec|awk '{print $2}'
}

function BusyWorkers(){
/usr/bin/curl -s $URL |grep BusyWorkers|awk '{print $2}'
}

function IdleWorkers(){
/usr/bin/curl -s $URL |grep IdleWorkers|awk '{print $2}'
}

function ping(){
/usr/sbin/pidof httpd|wc -l
}

#根据脚本参数执行对应函数
$1

/etc/zabbix/zabbix_agentd.d/apache_status.conf
Bash

UserParameter=apache.status[*],/etc/zabbix/zabbix_agentd.d/apache_status.sh $1

重启zabbix-agent后就可以创建apache监控模板了。

items配置都和nginx监控大同小异,特别看下这个带单位的:单位我这里写的Bps,Bytes每秒的意思,但在zabbix中,它会自动帮你换算成KBps:

未分类

并不是说zabbix很聪明知道你这个单位什么意思,它只是简单的除以1000然后加上一个大写“K”在前面而已,加入你的单位是”obe”,那么就显示成了Kobe,哈哈^_^

未分类

再添加3个Graphs,模板就算做完了:

未分类

最后附上版本文件,适用于zabbix-3.2.7-1:https://github.com/dmli30/shell/blob/master/zabbix/apache_status_templates.xml

Golang如何安全使用协程

什么是协程?

协程(Coroutine)是在1963年由Melvin E. Conway USAF, Bedford, MA等人提出的一个概念。而且协程的概念是早于线程(Thread)提出的。但是由于协程是非抢占式的调度,无法实现公平的任务调用。也无法直接利用多核优势。因此,我们不能武断地说协程是比线程更高级的技术。

尽管,在任务调度上,协程是弱于线程的。但是在资源消耗上,协程则是极低的。一个线程的内存在MB级别,而协程只需要KB级别。而且线程的调度需要内核态与用户的频繁切入切出,资源消耗也不小。

我们把协程的基本特点归纳为:

  • 协程调度机制无法实现公平调度
  • 协程的资源开销是非常低的,一台普通的服务器就可以支持百万协程。

那么,近几年为何协程的概念可以大热。我认为一个特殊的场景使得协程能够广泛的发挥其优势,并且屏蔽掉了劣势 — 网络编程。

与一般的计算机程序相比,网络编程有其独有的特点。

  • 高并发(每秒钟上千数万的单机访问量)
  • Request/Response。程序生命期端(毫秒,秒级)
  • 高IO,低计算(连接数据库,请求API)。

最开始的网络程序其实就是一个线程一个请求设计的(Apache)。后来,随着网络的普及,诞生了C10K问题。Nginx 通过单线程异步IO把网络程序的执行流程进行了乱序化,通过IO事件机制最大化的保证了CPU的利用率。

至此,现代网络程序的架构已经形成。基于IO事件调度的异步编程。其代表作恐怕就属NodeJS了吧。

异步编程的槽点

异步编程为了追求程序的性能,强行的将线性的程序打乱,程序变得非常的混乱与复杂。对程序状态的管理也变得异常困难。写过Nginx C Module的同学应该知道我说的是什么。

我们开始吐槽NodeJS 那恶心的层层Callback。

Golang

再我们疯狂被NodeJS的层层回调恶心到的时候,Golang 作为名门之后开始走入我们的视野。并且迅速的在Web后端极速的跑马圈地。其代表者Docker 以及围绕这Docker展开的整个容器生态圈欣欣向荣起来。其最大的卖点 – 协程 开始真正的流行与讨论起来。

我们开始向写PHP一样来写全异步IO的程序。看上去美好极了,仿佛世界就是这样了。

在网络编程中,我们可以理解为Golang 的协程本质上其实就是对IO事件的封装,并且通过语言级的支持让异步的代码看上去像同步执行的一样。

安全正确的使用Golang协程

我们在使用Golang的时候不一定就要非常深入的理解与学习协程的种种技术细节。但我们得时时刻刻的警惕起来。因为无论我们的代码写得是多么的同步化(看上去所有的IO好像都是阻塞的),但其本质还是异步。在协程里面会进行各种各种的中断与调度。

  • 谨慎的使用全局变量

当我们在定义一个全局变量的时候,我们就是在埋下一颗炸弹。它可能会以你想象不到的姿势点燃并爆炸。最容易出错的就是协程竞争与数据污染。

  • 协程竞争

协程竞争指的是不同的协程同时对一个对象(尤其是原生的map)进行数据读写操作。golang 在出现这种竞争的时候将会抛出fatal错误,并且程序退出。

无论是操作struct 、int、 string map 、 slice 都请做好资源锁。

我建议的全局变量应该是只读的,最进行一次初始化。通常在main函数或package的init函数中执行。

  • 数据污染

数据污染指的是程序经过一段运行时间之后,全局变量里面的值变得与期望值不一样。

  • 协程饿死

由于协程是非抢占的式的调度,那么就有可能某一个协程长时间的运行一个协程,导致其他协程无法被调度,直到饿死。

这样的情况,通常发生再大规模的处理程序中。比如数据迭代处理,大型多媒体资源处理等生命期长的协程中。

在Golang中,提供了runtime.Gosched() 函数来主动挂起当前协程,把CPU出让,让调度器有机会调度其他协程。

func BigJob() {

    for i := 0;i < 100000000;i ++ {
        //do something ...
        //每执行1W个任务出让CPU
        if i % 10000 == 0 {
            runtime.Gosched()
        }
    }
}

总结

Golang 再语言级别提供了协程支持。让异步编程变得更加轻松,但是它并没有解决存在于并发环境(多线程)中的种种问题。我们需要掌握并发环境中编程的技术知识用来规避和解决问题。

我们要非常了解协程的特点与调度模型。并且通过仔细的检验来保证协程尽可能的被公平调度(至少在web系统中,这是一个非常重要的特性)。