Fork me on GitHub
Jay Chang's Blog

我的技术博客


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

Ubuntu Linux下安装MySQL5.7

发表于 2019-07-27 |
字数统计: 1,540

MySQL通用Linux二进制安装包用于在Linux系统上安装MySQL。可以根据我们的需求,定制安装MySQL,比如可以设置数据存储目录,日志存储目录等。

准备

如果系统之前安装过mysql,版本可能是比较旧的,需要在安装之前确认下。可以使用如apt,yum包管理工具,将mysql卸载。并注意/etc/my.cnf,mysql数据存放目录,将数据备份好,然后再删除这些文件或目录。

mysql需要依赖libaio库,在安装mysql前确认已经安装好libaio库

  • centos:

    1
    2
    yum search libaio  # search for info
    yum install libaio # install library
  • ubuntu:

    1
    2
    apt-cache search libaio # search for info
    apt-get install libaio1 # install library

下载MySQL二进制安装包

下载64位mysql5.7.27 linux通用二进制安装包

1
wget --no-check-certificate https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.27-linux-glibc2.12-x86_64.tar.gz

二进制安装包清单

目录 目录中的文件
bin mysqld服务端程序,mysql客户端程序,等其他工具程序
docs mysql文档
man man手册
include header头文件
share 用于数据库安装的错误消息、字典和SQ
support-files 各种支持文件

创建用户组、用户

1
2
groupadd mysql
useradd -r -g mysql -s /bin/false mysql

此用户仅仅用于mysql服务,而不用于系统登录,所以使用useradd -r和-s /bin/false命令选项来创建没有登录权限的用户。

解压MySQL安装包

解压mysql安装包到/user/local目录下,并设置软连接,使得mysql的basedir为/usr/local/mysql

1
2
3
tar zxvf /path/to/mysql-5.7.27-linux-glibc2.12-x86_64.tar.gz -C /usr/local
cd /usr/local
ln -s mysql-5.7.26-linux-glibc2.12-x86_64 mysql

/path/to改为mysql安装包所在目录

将bin目录添加到PATH环境变量中

1
2
3
4
export PATH=$PATH:/usr/local/mysql/bin
echo "export PATH=$PATH:/usr/local/mysql/bin" >> ~/.bashrc
echo "export PATH=$PATH:/usr/local/mysql/bin" >> /etc/profile
source /etc/profile

mysql目录、文件规划

名称 目录或文件 软链 实际目录
数据目录datadir /data/mysql/data 否
参数配置my.cnf /usr/local/mysql/etc/my.cnf 否
日志log目录 /usr/local/mysql/log 是 /data/mysql/log
错误日志log-error /usr/local/mysql/log/mysql_error.log 否
慢查询日志slow_query_log_file /usr/local/mysql/log/mysql_slow_query.log 否
二进制日志log-bin /usr/local/mysql/binlogs/mysql-bin 是 /data/mysql/binlogs/mysql-bin
套接字socket文件 /usr/local/mysql/run/mysql.sock 否
pid文件(进程id) /usr/local/mysql/run/mysql.pid 否

默认情况下socket文件, pid文件是存在datadir的,建议还是分开一个目录,取名run目录好一点

创建所需目录并设置权限

1
2
3
4
mkdir -p /usr/local/mysql/{etc,run}
mkdir -p /data/mysql/{data,binlogs,log}
chown -R mysql.mysql /data/mysql
chown -R mysql.mysql /usr/local/mysql/{etc,run}

配置my.cnf

文件放在/usr/local/mysql/etc/my.cnf,放此目录,会自动加载该配置文件的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

[client]
port = 3306
socket = /usr/local/mysql/run/mysql.sock

[mysqld]

##########################
#GENERAL
##########################
user = mysql
port = 3306
server_id = 1
skip-name-resolve
bind-address= 0.0.0.0
default_storage_engine = InnoDB
socket = /usr/local/mysql/run/mysql.sock
pid-file = /usr/local/mysql/run/mysqld.pid
max_allowed_packet=32M
max_connections = 1024
open_files_limit = 65535
#default-time-zone='+08:00'
explicit_defaults_for_timestamp=true

##########################
# character set
##########################
character_set_server = utf8mb4
collation-server = utf8mb4_general_ci
init_connect='SET NAMES utf8mb4'

##########################
# DATA STORAGE
##########################
basedir= /usr/local/mysql
datadir = /data/mysql/data

##########################
# INNODB
##########################
innodb_file_per_table = 1
innodb_buffer_pool_size = 512M
innodb_log_buffer_size = 16M
innodb_log_file_size = 128M
innodb_tmpdir= /data/mysql/tmp

##########################
# MyISAM
##########################
key_buffer_size = 64M

##########################
# Logging
##########################
# mysql log timezone use system timezone
log_timestamps=SYSTEM
log_error = /data/mysql/log/mysql_error.log

##########################
# log bin
##########################
log-bin = /data/mysql/binlogs/mysql-bin
sync-binlog = 1
binlog_format = row
binlog_row_image = FULL
expire_logs_days = 15
master_info_repository = TABLE
relay_log_info_repository = TABLE


##########################
# log relay
##########################
relay-log = /data/mysql/binlogs/relay-bin
relay_log_recovery = on
max_relay_log_size = 1G
log_slave_updates = 1

##########################
# gtid
##########################
gtid_mode = on
enforce_gtid_consistency = on
replicate_ignore_db=mysql
replicate_ignore_db=information_schema
replicate_ignore_db=performation_schema
replicate_ignore_db=sys

##########################
# General logs (only enable for debugging – it use too much I/O)
##########################
#general-log = on
#general-log-file = /data/mysql/log/general-query.log

##########################
# Slow query logs (optional)
##########################
slow_query_log = on
long_query_time= 3
slow_query_log_file = /data/mysql/log/slow-query.log

初始化mysql server

查看初始化参数

1
mysqld --verbose --help |more

  • 初始化数据库
    1
    2
    3
    4
    5
    cd /user/local/mysql
    mkdir mysql-files
    chown mysql:mysql mysql-files
    chmod 750 mysql-files
    bin/mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql/data

在日志文件理会提示一个临时密码,记录这个密码

1
grep 'temporary password' /data/mysql/log/mysql_error.log

upload successful

  • 生成ssl
1
mysql_ssl_rsa_setup --basedir=/usr/local/mysql --datadir=/data/mysql/data

启动mysql服务

1
mysqld_safe --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql/data --defaults-file=/usr/local/mysql/etc/my.cnf &

或

1
mysqld_safe --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql/data &

可以不用指定–defaults-file,会加载到$MYSQL_HOME/etc/my.cnf这个配置文件

修改密码

mysql -uroot -p
输入上一步查到的临时密码

1
2
3
set password=password('123456');
grant all privileges on *.* to root@'%' identified by '123456' with grant option;
flush privileges;

mysql作为系统服务

非systemctrl管理(不推荐)

1
2
#Next command is optional
cp support-files/mysql.server /etc/init.d/mysqld

修改/etc/init.d/mysqld的basedir,datadir等配置
chomod 755 /etc/init.d/mysqld

下面介绍使用 systemctrl来管理mysql服务

systemctl管理(强烈推荐)

创建 /etc/systemd/system/mysqld.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=MySQL Server
Documentation=man:mysqld(5.7)
Documentation=https://dev.mysql.com/doc/refman/5.7/en/using-systemd.html
After=network.target
After=syslog.target

[Install]
WantedBy=multi-user.target

[Service]
User=mysql
Group=mysql
#PIDFile=/usr/local/mysql/run/mysqld.pid
#ExecStart=
ExecStart=/usr/local/mysql/bin/mysqld_safe --defaults-file=/usr/local/mysql/etc/my.cnf
LimitNOFILE=65535

设置开机启动

1
2
systemctl daemon-reload
systemctl enable mysqld

启动、停止、查看日志

1
2
3
systemctrl start mysqld
systemctrl stop mysqld
journalctl -u mysqld

附录

单机多实例的安装配置见https://www.jb51.net/article/103843.htm,需要注意版本差异

参考

https://lalitvc.wordpress.com/2019/03/04/mysql-5-7-binary-install-on-linux/
https://dev.mysql.com/doc/refman/5.7/en/binary-installation.html
https://dev.mysql.com/doc/refman/5.7/en/postinstallation.html
https://dev.mysql.com/doc/refman/5.7/en/using-systemd.html#systemd-mysql-configuration
https://dev.mysql.com/doc/refman/5.7/en/mysqld-safe.html#option_mysqld_safe_malloc-lib
https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html
https://blog.csdn.net/zml3721/article/details/79090983
https://www.jianshu.com/p/0d628b2f7476
https://www.jb51.net/article/103843.htm
https://blog.pythian.com/manage-multiple-mysql-binary-installations-with-systemd/
https://www.linuxidc.com/Linux/2017-10/147829.htm
https://blog.csdn.net/l1028386804/article/details/87996449

Ubuntu18.04 Nginx+Lua+GraphicsMagick图片缩放

发表于 2019-07-07 | 分类于 Linux Server |
字数统计: 3,311

背景

自己搭建的图片服务器,有图片缩放的需求,大致思路是可以使用nginx调用lua,使用GraphicMagick的命令来做图片缩放

说明

文件夹规划

lua.jaychang.cn(如/var/filebase)

1
2
3
4
5
6
7
8
9
10
11
12
13
jaychang@nginx:~$ tree /var/filebase/
/var/filebase/
├── avatar.png
├── cache
│   └── thumb
│   ├── avatar.png_100x100.png
│   └── upload
│   ├── 1.png_100x100.png #固定高和宽
│   ├── 1.png_400-.png # 定高
│   └── 1.png_800-.png # 定宽
└── upload
├── 1.png
4 directories, 8 files

其中img.xyz.com为图片站点根目录
cache/thumb为缩略图存放目录
upload目录存放上传的图片

链接地址对应关系

原图访问地址:http://img.xyz.com/upload/1.png
缩略图访问地址:http://img.xyz.com/upload/1.png_100x100.png 即为宽100,高100
自动宽地址: http://img.xyz.com/upload/1.png_-400.png 用”-“表示自动,即为高400,宽自动
自动高地址: http://img.xyz.com/upload/1.png_800-.jpg 用”-“表示自动,即为宽800,高自动

访问流程

首先判断缩略图是否存在,如存在则直接显示缩略图;
缩略图不存在,则判断原图是否存在,如原图存在则拼接graphicsmagick(gm)命令,生成并显示缩略图,否则返回404

所需软件

  • lua-5.3.5.tar.gz
  • LuaJIT-2.0.5.tar.gz
  • nginx-1.14.2.tar.gz
  • nginx模块:lua-nginx-module-0.10.15.tar.gz

    lua-nginx-module 依赖于 LuaJIT 和 ngx_devel_kit。LuaJIT 需要安装,ngx_devel_kit 只需下载源码包,在 Nginx 编译时指定 ngx_devel_kit 目录

  • nginx模块:nginx-http-concat
  • nginx模块:ngx_devel_kit
  • nginx模块:nginx-http-concat
1
2
3
4
5
6
7
curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
curl -R -O http://luajit.org/download/LuaJIT-2.0.5.tar.gz
curl -R -O http://nginx.org/download/nginx-1.14.2.tar.gz
curl -R -O https://github.com/openresty/lua-nginx-module/archive/v0.10.15.tar.gz
curl -R -O https://github.com/openresty/lua-resty-core/archive/v0.1.17.tar.gz
curl -R -O https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
git clone git@github.com:alibaba/nginx-http-concat.git

安装依赖

1
2
3
apt-get install -y gcc g++ make

apt-get install libreadline-dev libpcre3 libpcre3-dev openssl libssl-dev zlib1g zlib1g-dev libgeoip-dev -y

编译安装Lua LuaJIT

编译安装Lua

1
2
3
4
curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
tar zxf lua-5.3.5.tar.gz
cd lua-5.3.5
make linux test

编译安装LuaJIT

1
2
3
4
5
6
7
8
curl -R -O http://luajit.org/download/LuaJIT-2.0.5.tar.gz
tar -zxvf LuaJIT-2.0.5.tar.gz
cd LuaJIT-2.0.5
make -j2 && make install
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0
echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf
ldconfig

安装GraphicsMagick

1
apt install -y graphicsmagick

直接用apt来安装的话,可以免去安装 jpg,png 等图片库依赖

创建用户及相应目录

1
2
3
4
groupadd nginx && useradd nginx -g nginx -s /sbin/nologin -M

mkdir -p /var/tmp/nginx/client_body_temp
mkdir -p /var/tmp/nginx/uwsgi_temp

创建用户也可以用以下命令:

1
groupadd nginx && useradd nginx -g nginx -s /bin/false -M

编译安装nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
./configure --prefix=/usr/local/nginx \
--user=nginx --group=nginx \
--sbin-path=/usr/sbin/nginx \
--with-pcre \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-http_stub_status_module \
--with-http_v2_module \
--with-http_flv_module \
--with-http_ssl_module \
--with-http_addition_module \
--with-http_geoip_module \
--add-module=/usr/local/src/lua-nginx-module-0.10.15 \
--add-module=/usr/local/src/ngx_devel_kit-0.3.0 \
--add-module=/usr/local/src/nginx-http-concat \
--http-scgi-temp-path=/var/tmp/nginx/cgi_temp \
--http-client-body-temp-path=/var/tmp/nginx/client_body_temp \
--http-proxy-temp-path=/var/tmp/nginx/proxy_temp \
--http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp \
--http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log
1
make -j 2 && make install

注意:动态加载模块,Nginx官方的load_module指令,详细文档参考1
和 参考2
还有–with-http_spdy_module 已经改为–with-http_v2_module了
,如果不用geo的话,编译的时候可以不加–with-http_geoip_module,可以不安装libgeoip-dev

测试nginx

1
nginx -t

如果出现以下错误(没有报错就不用做以下操作了)

1
2
root@ubuntu:/usr/local/src/nginx-1.14.2# nginx -t
nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory

执行以下命令即可

1
2
echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf
ldconfig

修改nginx.conf配置文件

将nginx.conf配置文件中的server{}段配置注释掉

加一行配置,以便读取/usr/local/tengine/conf.d目录下所有后缀为.conf的配置文件

1
include ../conf.d/*.conf;

可以参考如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
user nginx;
worker_processes 4;
worker_cpu_affinity 1000 0100 0010 0001;
error_log /var/log/nginx/error.log error;
pid /usr/local/nginx/logs/nginx.pid;
worker_rlimit_nofile 65535;
events
{
use epoll;
worker_connections 65535;
}
http
{
lua_load_resty_core off;
limit_conn_zone $binary_remote_addr zone=one:10m;
limit_conn_zone $server_name zone=perserver:10m;
include mime.types;
include fastcgi.conf;
default_type application/octet-stream;
charset utf-8;
server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 4 64k;
sendfile on;
autoindex off;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 120;

fastcgi_connect_timeout 60;
fastcgi_send_timeout 60;
fastcgi_read_timeout 60;
fastcgi_buffer_size 128k;
fastcgi_buffers 8 128k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';

client_max_body_size 10m;

include ../conf.d/*.conf;
}

注意:lua_load_resty_core off;如果不加会有以下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html)
nginx: [error] lua_load_resty_core failed to load the resty.core module from https://github.com/openresty/lua-resty-core; ensure you are using an OpenResty release from https://openresty.org/en/download.html (rc: 2, reason: module 'resty.core' not found:
no field package.preload['resty.core']
no file ' /usr/local/lib/lua-resty-core-0.1.17/lib/resty/core.lua'
no file './resty/core.lua'
no file '/usr/local/share/luajit-2.0.5/resty/core.lua'
no file '/usr/local/share/lua/5.1/resty/core.lua'
no file '/usr/local/share/lua/5.1/resty/core/init.lua'
no file './resty/core.so'
no file '/usr/local/lib/lua/5.1/resty/core.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
no file './resty.so'
no file '/usr/local/lib/lua/5.1/resty.so'
no file '/usr/local/lib/lua/5.1/loadall.so')

创建放图片的目录

目录规划

  • /var/www 放网页,css,js等资源
  • /var/fielbase 放图片
  • /var/filebase/upload 上传的图片放这里
1
2
3
mkdir -p /var/filebase/upload 
mkdir -p /var/filebase/cache/thumb
chown -R nginx:nginx /var/filebase

配置站点配置文件

在/usr/local/tengine/conf.d目录下创建demo.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 定义lua缩略图支持的图片尺寸及开关
init_by_lua '
image_sizes_check = true
image_sizes = {"800x800", "400x400","200x200","100x100", "-800", "-400", "-200","-100", "800-", "400-", "200-","100-"}
';

server {
listen 80;
servername img.xyz.com;
index index.php index.html index.htm;

set $root_path '/var/www';
root $root_path;

# /lua仅用于测试,可去掉
location /lua {
default_type 'text/plain';
content_by_lua 'ngx.say("hello, ttlsa lua")';
}

# 这里也是需要根据实际情况修改的,这里的是用了rewrite
location / {
try_files $uri $uri/ /index.php?$args;

# add support for img which has query params,
# like: xxx.jpg?a=b&c=d_750x750.jpg
if ($args ~* "^([^_]+)_(\d+)+x(\d+)+\.(jpg|jpeg|gif|png)$") {
set $w $2;
set $h $3;
set $img_ext $4;

# rewrite ^\?(.*)$ _${w}x${h}.$img_ext? last;
rewrite ([^.]*).(jpg|jpeg|png|gif)$ $1.$2_${w}x${h}.$img_ext? permanent;
}
}

# set var for thumb pic
set $upload_path /var/filebase;
set $img_original_root $upload_path;# original root;
set $img_thumbnail_root $upload_path/cache/thumb;
set $img_file $img_thumbnail_root$uri;

# like:/xx/xx/xx.jpg_100-.jpg or /xx/xx/xx.jpg_-100.jpg
location ~* ^(.+\.(jpg|jpeg|gif|png))_((\d+\-)|(\-\d+))\.(jpg|jpeg|gif|png)$ {
root $img_thumbnail_root; # root path for croped img
set $img_size $3;

if (!-f $img_file) { # if file not exists
add_header X-Powered-By 'Nginx+Lua+GraphicsMagick By Botao'; # header for test
add_header file-path $request_filename; # header for test
set $request_filepath $img_original_root$1; # origin_img full path:/document_root/1.gif
set $img_size $3; # img width or height size depends on uri
set $img_ext $2; # file ext
content_by_lua_file /usr/local/nginx/lua/autoSize.lua; # load lua
}
}

# like: /xx/xx/xx.jpg_100x100.jpg
location ~* ^(.+\.(jpg|jpeg|gif|png))_(\d+)+x(\d+)+\.(jpg|jpeg|gif|png)$ {
root $img_thumbnail_root; # root path for croped img

if (!-f $img_file) { # if file not exists
add_header X-Powered-By 'Nginx+Lua+GraphicsMagick By Botao'; # header for test
add_header file-path $request_filename; # header for test
set $request_filepath $img_original_root$1; # origin_img file path
set $img_width $3; # img width
set $img_height $4; # height
set $img_ext $5; # file ext
content_by_lua_file /usr/local/nginx/lua/cropSize.lua; # load lua
}
}

# if need (all go there)
location ~* /upload {
root $img_original_root;
}


location ~ /\.ht {
deny all;
}
}

lua脚本

  • /usr/local/tengine/lua/autoSize.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
-- 根据输入长或宽的尺寸自动裁切图片大小

-- 检测路径是否目录
local function is_dir(sPath)
if type(sPath) ~= "string" then return false end

local response = os.execute("cd " .. sPath)
if response == 0 then
return true
end
return false
end

-- 文件是否存在
function file_exists(name)
local f = io.open(name, "r")
if f ~= nil then io.close(f) return true else return false end
end

-- 获取文件路径
function getFileDir(filename)
return string.match(filename, "(.+)/[^/]*%.%w+$") --*nix system
end

-- 获取文件名
function strippath(filename)
return string.match(filename, ".+/([^/]*%.%w+)$") -- *nix system
end

--去除扩展名
function stripextension(filename)
local idx = filename:match(".+()%.%w+$")
if (idx) then
return filename:sub(1, idx - 1)
else
return filename
end
end

--获取扩展名
function getExtension(filename)
return filename:match(".+%.(%w+)$")
end

function getImgSize(img)

end

-- 判断尺寸是否合法
-- check image size
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end

if image_sizes_check
then
if not table.contains(image_sizes, ngx.var.img_size)
then
ngx.exit(404);
end
end
-- check image end

-- 开始执行
-- ngx.log(ngx.ERR, getFileDir(ngx.var.img_file));

local gm_path = 'gm'

-- check image dir
if not is_dir(getFileDir(ngx.var.img_file)) then
os.execute("mkdir -p " .. getFileDir(ngx.var.img_file))
end

-- 获取高宽 100!或!100模式
local uri = ngx.var.img_size
local width = string.sub(uri,1,1)
local height = 0

if width == "-" then
width = 0
height = string.sub(uri,2,string.len(uri))
else
width = string.sub(uri,1,string.len(uri)-1)
height = 0
end
-- ngx.log(ngx.ERR,uri)
-- ngx.log(ngx.ERR,width)
-- ngx.log(ngx.ERR,height)
-- ngx.log(ngx.ERR,ngx.var.img_file);
-- ngx.log(ngx.ERR,ngx.var.request_filepath);
-- 裁剪后保证等比缩图 (缺点:裁剪了图片的一部分)
-- 如: gm convert autoSize.jpg -resize x200 -quality 100 +profile "*" autoSize.jpg_-200.jpg
if (file_exists(ngx.var.request_filepath)) then
local cmd = gm_path .. ' convert -auto-orient ' .. ngx.var.request_filepath
if height == 0 then
cmd = cmd .. " -resize " .. width .. "x" .. ""
else
cmd = cmd .. " -resize " .. "x" .. height .. ""
end

-- 由于压缩后比较模糊,默认图片质量为100,请根据自己情况修改quality
cmd = cmd .. " -quality 100"
cmd = cmd .. " +profile \"*\" " .. ngx.var.img_file;
ngx.log(ngx.ERR, cmd);
os.execute(cmd);
ngx.exec(ngx.var.uri);
else
ngx.exit(ngx.HTTP_NOT_FOUND);
end
  • /usr/local/tengine/lua/cropSize.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
-- 根据输入长和宽的尺寸裁切图片

-- 检测路径是否目录
local function is_dir(sPath)
if type(sPath) ~= "string" then return false end

local response = os.execute("cd " .. sPath)
if response == 0 then
return true
end
return false
end

-- 文件是否存在
function file_exists(name)
local f = io.open(name, "r")
if f ~= nil then io.close(f) return true else return false end
end

-- 获取文件路径
function getFileDir(filename)
return string.match(filename, "(.+)/[^/]*%.%w+$") --*nix system
end

-- 获取文件名
function strippath(filename)
return string.match(filename, ".+/([^/]*%.%w+)$") -- *nix system
end

--去除扩展名
function stripextension(filename)
local idx = filename:match(".+()%.%w+$")
if (idx) then
return filename:sub(1, idx - 1)
else
return filename
end
end

--获取扩展名
function getExtension(filename)
return filename:match(".+%.(%w+)$")
end

-- 判断尺寸是否合法
-- 待切割的图片尺寸
local img_width_height = ngx.var.img_width .. "x" .. ngx.var.img_height;
-- check image size
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end

if image_sizes_check
then
if not table.contains(image_sizes, img_width_height)
then
ngx.exit(404);
end
end
-- check image end

-- 开始执行
-- ngx.log(ngx.ERR, getFileDir(ngx.var.img_file));

local gm_path = 'gm'

-- check image dir
if not is_dir(getFileDir(ngx.var.img_file)) then
os.execute("mkdir -p " .. getFileDir(ngx.var.img_file))
end

-- ngx.log(ngx.ERR,ngx.var.img_file);
-- ngx.log(ngx.ERR,ngx.var.request_filepath);

-- 裁剪后保证等比缩图 (缺点:裁剪了图片的一部分)
-- gm convert cropSize.jpg -thumbnail 300x300^ -gravity center -extent 300x300 -quality 100 +profile "*" cropSize.jpg_300x300.jpg
if (file_exists(ngx.var.request_filepath)) then
local cmd = gm_path .. ' convert -auto-orient ' .. ngx.var.request_filepath
cmd = cmd .. " -thumbnail " .. ngx.var.img_width .. "x" .. ngx.var.img_height .. "^"
cmd = cmd .. " -gravity center -extent " .. ngx.var.img_width .. "x" .. ngx.var.img_height

-- 由于压缩后比较模糊,默认图片质量为100,请根据自己情况修改quality
cmd = cmd .. " -quality 100"
cmd = cmd .. " +profile \"*\" " .. ngx.var.img_file;
-- ngx.log(ngx.ERR, cmd);
os.execute(cmd);
ngx.exec(ngx.var.uri);
else
ngx.exit(ngx.HTTP_NOT_FOUND);
end

然后 nginx -s reload下

测试效果

在/var/filebase/upload目录,放1张图片

upload successful
原图

upload successful
100x100

upload successful
由于我们开启了image_sizes_check,不支持的尺寸会返回404

upload successful
固定定宽高自适应

upload successful
查看服务器上生成的文件

PS:这里有个问题,就是图片缩放后,图片的方向跟原先不一致的问题,后来笔者查了下GraphicsMagick资料,关于图片缩放后改变图片方向的问题,可以用-auto-orient参数可以来解决的,可以写成”gm convert -auto-orient “

参考资料

https://github.com/botaozhao/nginx-lua-GraphicsMagick
https://www.fanhaobai.com/2017/09/lua-in-nginx.html
https://github.com/openresty/lua-nginx-module/issues/1533
https://github.com/openresty/lua-nginx-module/pull/1501#issuecomment-486123650
https://github.com/openresty/lua-resty-core/issues/248
https://segmentfault.com/a/1190000011093243
https://github.com/yanue/nginx-lua-GraphicsMagick/blob/master/nginx-install.md
http://www.icode9.com/content-3-77987.html
https://my.oschina.net/ranhai/blog/1797454
https://www.twblogs.net/a/5bafdb372b7177781a0f64d7/zh-cn

Ubuntu18.04 Tengine+Lua+GraphicsMagick图片缩放

发表于 2019-06-10 | 分类于 Linux Server |
字数统计: 2,952

背景

自己搭建的图片服务器,有图片缩放的需求,大致思路是可以使用nginx调用lua,使用GraphicMagick的命令来做图片缩放

说明

文件夹规划

lua.jaychang.cn(如/var/filebase)

1
2
3
4
5
6
7
8
9
10
11
12
13
jaychang@tengine:~$ tree /var/filebase/
/var/filebase/
├── avatar.png
├── cache
│   └── thumb
│   ├── avatar.png_100x100.png
│   └── upload
│   ├── 1.png_100x100.png #固定高和宽
│   ├── 1.png_400-.png # 定高
│   └── 1.png_800-.png # 定宽
└── upload
├── 1.png
4 directories, 8 files

其中img.xyz.com为图片站点根目录
cache/thumb为缩略图存放目录
upload目录存放上传的图片

链接地址对应关系

原图访问地址:http://img.xyz.com/upload/1.png
缩略图访问地址:http://img.xyz.com/upload/1.png_100x100.png 即为宽100,高100
自动宽地址: http://img.xyz.com/upload/1.png_-400.png 用”-“表示自动,即为高400,宽自动
自动高地址: http://img.xyz.com/upload/1.png_800-.jpg 用”-“表示自动,即为宽800,高自动

访问流程

首先判断缩略图是否存在,如存在则直接显示缩略图;
缩略图不存在,则判断原图是否存在,如原图存在则拼接graphicsmagick(gm)命令,生成并显示缩略图,否则返回404

所需软件

  • lua-5.3.5.tar.gz
  • LuaJIT-2.0.5.tar.gz
  • tengine-2.3.0.tar.gz

安装依赖

1
2
3
apt-get install -y gcc g++ make

apt-get install libreadline-dev libpcre3 libpcre3-dev openssl libssl-dev zlib1g zlib1g-dev libgeoip-dev -y

编译安装Lua LuaJIT

编译安装Lua

1
2
3
4
curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
tar zxf lua-5.3.5.tar.gz
cd lua-5.3.5
make linux test

编译安装LuaJIT

1
2
3
4
5
6
7
curl -R -O http://luajit.org/download/LuaJIT-2.0.5.tar.gz
tar -zxvf LuaJIT-2.0.5.tar.gz
cd LuaJIT-2.0.5
make -j2
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0
ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

安装GraphicsMagick

1
2
3
4
5
wget ftp://ftp.graphicsmagick.org/pub/GraphicsMagick/1.3/GraphicsMagick-1.3.31.tar.gz
tar xzvf GraphicsMagick-1.3.31.tar.gz
cd GraphicsMagick-1.3.31
./configure --prefix=/usr/local/GraphicsMagick --enable-shared
make && make install

创建用户及相应目录

1
2
3
4
groupadd nginx && useradd nginx -g nginx -s /sbin/nologin -M

mkdir -p /var/tmp/tengine/client_body_temp
mkdir -p /var/tmp/tengine/uwsgi_temp

创建用户也可以用以下命令:

1
groupadd nginx && useradd nginx -g nginx -s /bin/false -M

编译安装tengine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
./configure --prefix=/usr/local/tengine \
--user=nginx --group=nginx \
--sbin-path=/usr/sbin/nginx \
--with-pcre \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-http_stub_status_module \
--with-http_v2_module \
--with-http_lua_module \
--with-http_flv_module \
--with-http_ssl_module \
--with-http_addition_module \
--with-http_geoip_module \
--http-scgi-temp-path=/var/tmp/tengine/cgi_temp \
--http-client-body-temp-path=/var/tmp/tengine/client_body_temp \
--http-proxy-temp-path=/var/tmp/tengine/proxy_temp \
--http-uwsgi-temp-path=/var/tmp/tengine/uwsgi_temp \
--http-fastcgi-temp-path=/var/tmp/tengine/fastcgi_temp \
--http-log-path=/var/log/tengine/access.log \
--error-log-path=/var/log/tengine/error.log
1
make -j 2 && make install

注意:在Tengine-2.3.0版本后废弃Tengine的dso_tool工具以及dso配置指令,若之前有使用Tengine的dso功能、则可以切换到Nginx官方的load_module指令,详细文档参考1
和 参考2
还有–with-http_spdy_module 已经改为–with-http_v2_module了
,如果不用geo的话,编译的时候可以不加–with-http_geoip_module,可以不安装libgeoip-dev

测试tengine

1
2
cd /usr/local/tengine
./sbin/nginx -t

如果出现以下错误(没有报错就不用做以下操作了)

1
2
jaychang@tengine:/usr/local/tengine/sbin$ ./nginx -t
./nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory

查找下位置

1
2
3

jaychang@tengine:/usr/local/tengine/sbin$ sudo whereis libluajit-5.1.so.2
libluajit-5.1.so: /usr/local/lib/libluajit-5.1.so /usr/local/lib/libluajit-5.1.so.2

修改/etc/ld.so.conf,添加“/usr/local/lib”
vi /etc/ld.so.conf

1
2
include /etc/ld.so.conf.d/*.conf
/usr/local/lib

然后执行下ldconfig

1
ldconfig

修改nginx.conf配置文件

将nginx.conf配置文件中的server{}段配置注释掉

加一行配置,以便读取/usr/local/tengine/conf.d目录下所有后缀为.conf的配置文件

1
include ../conf.d/*.conf;

可以参考如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
user nginx;
worker_processes 4;
worker_cpu_affinity 1000 0100 0010 0001;
error_log /var/log/tengine/error.log error;
pid /usr/local/tengine/logs/nginx.pid;
worker_rlimit_nofile 65535;
events
{
use epoll;
worker_connections 65535;
}
http
{
limit_conn_zone $binary_remote_addr zone=one:10m;
limit_conn_zone $server_name zone=perserver:10m;
include mime.types;
include fastcgi.conf;
default_type application/octet-stream;
charset utf-8;
server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 4 64k;
sendfile on;
autoindex off;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 120;

fastcgi_connect_timeout 60;
fastcgi_send_timeout 60;
fastcgi_read_timeout 60;
fastcgi_buffer_size 128k;
fastcgi_buffers 8 128k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';

client_max_body_size 10m;

include ../conf.d/*.conf;
}

创建放图片的目录

目录规划

  • /var/www 放网页,css,js等资源
  • /var/fielbase 放图片
  • /var/filebase/upload 上传的图片放这里
1
2
3
mkdir -p /var/filebase/upload 
mkdir -p /var/filebase/cache/thumb
chown -R nginx:nginx /var/filebase

配置站点配置文件

在/usr/local/tengine/conf.d目录下创建demo.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 定义lua缩略图支持的图片尺寸及开关
init_by_lua '
image_sizes_check = true
image_sizes = {"800x800", "400x400","200x200","100x100", "-800", "-400", "-200","-100", "800-", "400-", "200-","100-"}
';

server {
listen 80;
servername img.xyz.com;
index index.php index.html index.htm;

set $root_path '/var/www';
root $root_path;

# /lua仅用于测试,可去掉
location /lua {
default_type 'text/plain';
content_by_lua 'ngx.say("hello, ttlsa lua")';
}

# 这里也是需要根据实际情况修改的,这里的是用了rewrite
location / {
try_files $uri $uri/ /index.php?$args;

# add support for img which has query params,
# like: xxx.jpg?a=b&c=d_750x750.jpg
if ($args ~* "^([^_]+)_(\d+)+x(\d+)+\.(jpg|jpeg|gif|png)$") {
set $w $2;
set $h $3;
set $img_ext $4;

# rewrite ^\?(.*)$ _${w}x${h}.$img_ext? last;
rewrite ([^.]*).(jpg|jpeg|png|gif)$ $1.$2_${w}x${h}.$img_ext? permanent;
}
}

# set var for thumb pic
set $upload_path /var/filebase;
set $img_original_root $upload_path;# original root;
set $img_thumbnail_root $upload_path/cache/thumb;
set $img_file $img_thumbnail_root$uri;

# like:/xx/xx/xx.jpg_100-.jpg or /xx/xx/xx.jpg_-100.jpg
location ~* ^(.+\.(jpg|jpeg|gif|png))_((\d+\-)|(\-\d+))\.(jpg|jpeg|gif|png)$ {
root $img_thumbnail_root; # root path for croped img
set $img_size $3;

if (!-f $img_file) { # if file not exists
add_header X-Powered-By 'Nginx+Lua+GraphicsMagick By Botao'; # header for test
add_header file-path $request_filename; # header for test
set $request_filepath $img_original_root$1; # origin_img full path:/document_root/1.gif
set $img_size $3; # img width or height size depends on uri
set $img_ext $2; # file ext
content_by_lua_file /usr/local/tengine/lua/autoSize.lua; # load lua
}
}

# like: /xx/xx/xx.jpg_100x100.jpg
location ~* ^(.+\.(jpg|jpeg|gif|png))_(\d+)+x(\d+)+\.(jpg|jpeg|gif|png)$ {
root $img_thumbnail_root; # root path for croped img

if (!-f $img_file) { # if file not exists
add_header X-Powered-By 'Nginx+Lua+GraphicsMagick By Botao'; # header for test
add_header file-path $request_filename; # header for test
set $request_filepath $img_original_root$1; # origin_img file path
set $img_width $3; # img width
set $img_height $4; # height
set $img_ext $5; # file ext
content_by_lua_file /usr/local/tengine/lua/cropSize.lua; # load lua
}
}

# if need (all go there)
location ~* /upload {
root $img_original_root;
}


location ~ /\.ht {
deny all;
}
}

lua脚本

  • /usr/local/tengine/lua/autoSize.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
-- 根据输入长或宽的尺寸自动裁切图片大小

-- 检测路径是否目录
local function is_dir(sPath)
if type(sPath) ~= "string" then return false end

local response = os.execute("cd " .. sPath)
if response == 0 then
return true
end
return false
end

-- 文件是否存在
function file_exists(name)
local f = io.open(name, "r")
if f ~= nil then io.close(f) return true else return false end
end

-- 获取文件路径
function getFileDir(filename)
return string.match(filename, "(.+)/[^/]*%.%w+$") --*nix system
end

-- 获取文件名
function strippath(filename)
return string.match(filename, ".+/([^/]*%.%w+)$") -- *nix system
end

--去除扩展名
function stripextension(filename)
local idx = filename:match(".+()%.%w+$")
if (idx) then
return filename:sub(1, idx - 1)
else
return filename
end
end

--获取扩展名
function getExtension(filename)
return filename:match(".+%.(%w+)$")
end

function getImgSize(img)

end

-- 判断尺寸是否合法
-- check image size
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end

if image_sizes_check
then
if not table.contains(image_sizes, ngx.var.img_size)
then
ngx.exit(404);
end
end
-- check image end

-- 开始执行
-- ngx.log(ngx.ERR, getFileDir(ngx.var.img_file));

local gm_path = 'gm'

-- check image dir
if not is_dir(getFileDir(ngx.var.img_file)) then
os.execute("mkdir -p " .. getFileDir(ngx.var.img_file))
end

-- 获取高宽 100!或!100模式
local uri = ngx.var.img_size
local width = string.sub(uri,1,1)
local height = 0

if width == "-" then
width = 0
height = string.sub(uri,2,string.len(uri))
else
width = string.sub(uri,1,string.len(uri)-1)
height = 0
end
-- ngx.log(ngx.ERR,uri)
-- ngx.log(ngx.ERR,width)
-- ngx.log(ngx.ERR,height)
-- ngx.log(ngx.ERR,ngx.var.img_file);
-- ngx.log(ngx.ERR,ngx.var.request_filepath);
-- 裁剪后保证等比缩图 (缺点:裁剪了图片的一部分)
-- 如: gm convert autoSize.jpg -resize x200 -quality 100 +profile "*" autoSize.jpg_-200.jpg
if (file_exists(ngx.var.request_filepath)) then
local cmd = gm_path .. ' convert -auto-orient ' .. ngx.var.request_filepath
if height == 0 then
cmd = cmd .. " -resize " .. width .. "x" .. ""
else
cmd = cmd .. " -resize " .. "x" .. height .. ""
end

-- 由于压缩后比较模糊,默认图片质量为100,请根据自己情况修改quality
cmd = cmd .. " -quality 100"
cmd = cmd .. " +profile \"*\" " .. ngx.var.img_file;
ngx.log(ngx.ERR, cmd);
os.execute(cmd);
ngx.exec(ngx.var.uri);
else
ngx.exit(ngx.HTTP_NOT_FOUND);
end
  • /usr/local/tengine/lua/cropSize.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
-- 根据输入长和宽的尺寸裁切图片

-- 检测路径是否目录
local function is_dir(sPath)
if type(sPath) ~= "string" then return false end

local response = os.execute("cd " .. sPath)
if response == 0 then
return true
end
return false
end

-- 文件是否存在
function file_exists(name)
local f = io.open(name, "r")
if f ~= nil then io.close(f) return true else return false end
end

-- 获取文件路径
function getFileDir(filename)
return string.match(filename, "(.+)/[^/]*%.%w+$") --*nix system
end

-- 获取文件名
function strippath(filename)
return string.match(filename, ".+/([^/]*%.%w+)$") -- *nix system
end

--去除扩展名
function stripextension(filename)
local idx = filename:match(".+()%.%w+$")
if (idx) then
return filename:sub(1, idx - 1)
else
return filename
end
end

--获取扩展名
function getExtension(filename)
return filename:match(".+%.(%w+)$")
end

-- 判断尺寸是否合法
-- 待切割的图片尺寸
local img_width_height = ngx.var.img_width .. "x" .. ngx.var.img_height;
-- check image size
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end

if image_sizes_check
then
if not table.contains(image_sizes, img_width_height)
then
ngx.exit(404);
end
end
-- check image end

-- 开始执行
-- ngx.log(ngx.ERR, getFileDir(ngx.var.img_file));

local gm_path = 'gm'

-- check image dir
if not is_dir(getFileDir(ngx.var.img_file)) then
os.execute("mkdir -p " .. getFileDir(ngx.var.img_file))
end

-- ngx.log(ngx.ERR,ngx.var.img_file);
-- ngx.log(ngx.ERR,ngx.var.request_filepath);

-- 裁剪后保证等比缩图 (缺点:裁剪了图片的一部分)
-- gm convert cropSize.jpg -thumbnail 300x300^ -gravity center -extent 300x300 -quality 100 +profile "*" cropSize.jpg_300x300.jpg
if (file_exists(ngx.var.request_filepath)) then
local cmd = gm_path .. ' convert -auto-orient ' .. ngx.var.request_filepath
cmd = cmd .. " -thumbnail " .. ngx.var.img_width .. "x" .. ngx.var.img_height .. "^"
cmd = cmd .. " -gravity center -extent " .. ngx.var.img_width .. "x" .. ngx.var.img_height

-- 由于压缩后比较模糊,默认图片质量为100,请根据自己情况修改quality
cmd = cmd .. " -quality 100"
cmd = cmd .. " +profile \"*\" " .. ngx.var.img_file;
-- ngx.log(ngx.ERR, cmd);
os.execute(cmd);
ngx.exec(ngx.var.uri);
else
ngx.exit(ngx.HTTP_NOT_FOUND);
end

然后 nginx -s reload下

测试效果

在/var/filebase/upload目录,放1张图片

upload successful
原图

upload successful
100x100

upload successful
由于我们开启了image_sizes_check,不支持的尺寸会返回404

upload successful
固定定宽高自适应

upload successful
查看服务器上生成的文件

PS:这里有个问题,就是图片缩放后,图片的方向跟原先不一致的问题,后来笔者查了下GraphicsMagick资料,关于图片缩放后改变图片方向的问题,可以用-auto-orient参数可以来解决的,可以写成”gm convert -auto-orient “

参考资料

https://github.com/botaozhao/nginx-lua-GraphicsMagick
https://github.com/yanue/nginx-lua-GraphicsMagick/blob/master/nginx-install.md
http://www.icode9.com/content-3-77987.html
https://my.oschina.net/ranhai/blog/1797454
https://www.twblogs.net/a/5bafdb372b7177781a0f64d7/zh-cn

Ubuntu18.04 Swarm集群搭建

发表于 2019-05-15 | 分类于 Docker&Kubernetes |
字数统计: 2,828

Swarm简介

Swarm 是 Docker 官方提供的一款集群管理工具,用来管理 Docker 集群,它将若干台 Docker 主机抽象为一个整体,并且通过一个入口统一管理这些 Docker 主机上的各种 Docker 资源。

Swarm 只是一个调度器(Scheduler)加路由器 (Router),Swarm 自己不运行容器,它只是接受 Docker 客户端发送过来的请求,调度适合的节点来运行容器,这意味着,即使 Swarm 由于某些原因挂掉了,集群中的节点也会照常运行,当 Swarm 重新恢复运行之后,它会收集重建集群信息。

Docker Engine 从 V1.12.0 版本开始,原生集成了 Docker Swarm,所以只要在每台机器上安装 Docker,就可以直接使用 Docker Swarm。

Swarm 和 Kubernetes 比较类似,但是更加轻,具有的功能也较 Kubernetes 更少一些。

Swarm会提供网络管理、负载均衡、基于DNS容器发现、集群状态管理、扩容缩容等功能,在DevOps流程层面,可以组合成发布、回滚应用、查看服务的状态、日志、终端,更改发布配置(例如环境变量、默认副本数等)等功能。这个层面Portainer这个开源项目,提供了更好用集群管理的UI界面和类似Kubernetes Apiserver设计的Docker集群API接口服务。

upload successful

Docker Swarm的资源抽象比较简单,在没有容器编排的情况下,我们主要使用Services,这个和Kubernetes上的Services有些不一样,可以理解为Services和Deployment的合体,用来管理和定义容器的调度和扩容等,也可以直接做端口映射,在容器集群里任意IP都可以访问。

集群规划

因为Manager节点只负责调度集群的Task,配置要求不高,可用3台低配置机器作为Manager节点,根据Raft算法,3台可以容许1台机器出问题,5台可以允许2台机器出问题,大家可以按需求部署。Worker节点的作用是运行业务容器。同时,在Manager和Worker节点上,会以容器方式部署机器监控和日志收集,上面还需要运行网络和存储插件。

IP地址 Swarm节点类型 主机名
192.168.56.111 manager m01
192.168.56.112 manager m02
192.168.56.113 manager m03
192.168.56.114 worker n01
192.168.56.115 worker n02

集群节点配置

笔者是用VirtualBox虚拟5台机器,作为运行Docker的宿主机。本节以下内容均是在m01上操作的。其他节点配置不再赘述,参考此m01节点的配置即可。

网络配置

每台宿主机需要启用仅主机(Host-Only)网络及网络地址转换(NAT)两张网卡

以m01为例,/etc/netplan/01-netcfg.yaml网络配置如下:

1
2
3
4
5
6
7
8
9
10
11
network:
version: 2
renderer: networkd
ethernets:
enp0s3:
dhcp4: no
dhcp6: no
addresses: [192.168.56.111/24]
enp0s8:
dhcp4: yes
dhcp6: no

注意这里enp0s3就是仅主机(Host-Only)网络这个网卡,此网卡用于ssh连接我们的虚拟机,由于这里是virtualbox虚拟机无需配置网关,enp0s8就是网络地址转换(NAT)这个网卡,此网卡用于访问外网。

主机名及hosts

设置主机名 sudo vi /etc/hostname

1
m01

如果遇到重启机器后主机名被重置,可以再用sudo hostnamectl set-hostname xxx 设置

通过sudo vi /etc/host以添加以下内容

1
2
3
4
5
192.168.56.111 m01
192.168.56.112 m02
192.168.56.113 m03
192.168.56.114 n01
192.168.56.115 n02

设置国内软件源

备份sources.list,sudo cp /etc/apt/sources.list /etc/apt/sources.list.default

批量修改sources.list

1
2
3
sudo sed -i "s#us.archive.ubuntu.com/#mirrors.aliyun.com/#g" /etc/apt/sources.list

sudo sed -i "s#security.ubuntu.com/#mirrors.aliyun.com/#g" /etc/apt/sources.list

安装Docker

安装GPG证书

1
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

写入软件源信息

1
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

更新并安装Docker-CE

1
2
sudo apt-get -y update
sudo apt-get -y install docker-ce

若想要安装指定版本Docker,参照如下操作:

  • Step 1: 查找Docker-CE的版本:
1
2

sudo apt-cache madison docker-ce
  • Step 2: 安装指定版本的Docker-CE
1
sudo apt-get -y install docker-ce=[VERSION]

注意上面命令中的[VERSION]需要替换成你想要安装的docker版本,
比如这里我将[VERSION]替换为5:18.09.4~3-0~ubuntu-bionic,所以最终的安装命令是sudo apt-get -y install docker-ce=5:18.09.6~3-0~ubuntu-bionic

其他可选的Docker软件源还有清华大学Docker软件源

配置Docker镜像加速

1
2
3
4
sudo vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}

注意 insecure-registries是配置非https的镜像中心,比如我们自己搭建的一个docker私有镜像中心,是用http的话,就可以用insecure-registries指定。

修改docker.service配置

如果碰到Unable to proxy the request via the Docker socket,这个错误,建议可以在任意worker节点,docker服务配置,
upload successful

这里只要改集群中一个节点就可以了(如果是用portainer agent方式添加的endpoint)主要是要改ExecStart,暴露2375,笔者改的是n01节点上的/lib/systemd/system/docker.service

sudo vi /lib/systemd/system/docker.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
BindsTo=containerd.service
After=network-online.target firewalld.service containerd.service
Wants=network-online.target
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// -H 0.0.0.0:2375 --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this option.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers


内容过多省略...

还可以改成

1
2
# for containers run by docker
ExecStart=/usr/bin/dockerd -H unix:// -H 0.0.0.0:2375 --containerd=/run/containerd/containerd.sock

或

1
2
# for containers run by docker
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H 0.0.0.0:2375 --containerd=/run/containerd/containerd.sock

sudo systemctl daemon-reload
sudo systemctl restart docker

重启docker服务

1
sudo systemctl restart docker

开始搭建Swarm集群

上述都是在做准备工作,接下来就正式开始搭建Swarm集群。

初始化Swarm集群

1
sudo docker swarm init --advertise-addr 192.168.56.111

初始化swarm集群

注意:上图显示的token仅作参考,具体需看实际情况,也请记下来

如果遗失可以通过下面两个命令,分别找回manager、worker的join token

1
2
3
sudo docker swarm join-token manager

sudo docker swarm join-token worker

集群添加manager节点

获取manager的join token

根据上一节给出的提示,在192.168.56.111(m01)上执行

1
sudo docker swarm join-token manager

集群添加manager节点

根据提示,若要将节点作为manager节点加入swarm集群,需执行如下命令:

1
docker swarm join --token SWMTKN-1-65q3om1lmrjrsvnbqug0qccf00ertneorskvmv86g05qzicc8w-1kuf57afgawuuz24h7gx4hycb 192.168.56.111:2377

添加manager节点到集群

根据我们的规划,我们要将m02、m03作为manager节点加入到swarm集群,所以我们在m02,m03上执行:

1
sudo docker swarm join --token SWMTKN-1-65q3om1lmrjrsvnbqug0qccf00ertneorskvmv86g05qzicc8w-1kuf57afgawuuz24h7gx4hycb 192.168.56.111:2377

注意:请根据实际情况进行修改,不要一模一样复制!

upload successful

upload successful

添加worker节点到集群

分别在n01,n02上执行如下命令:

1
docker swarm join --token SWMTKN-1-65q3om1lmrjrsvnbqug0qccf00ertneorskvmv86g05qzicc8w-978befgfbbuqzqzv2zbvcg8fk 192.168.56.111:2377

注意:请根据实际情况进行修改,不要一模一样复制!

upload successful

upload successful

查看节点状态

在任意manager节点上执行

1
sudo docker node ls

upload successful

从上图可以看到集群中有3个manager节点,2个worker

Swarm集群管理UI

建议每次都在固定的节点上运行,所以这里指定了node.hostname == m01

1
sudo docker service create --name portainer --publish 9000:9000 --replicas=1 --constraint 'node.hostname == m01' --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock --mount type=volume,src=portainer_data,dst=/data portainer/portainer

upload successful

Add Endpoints

添加一个Local Endpoint,由于portainer是运行在m01上的,所以就是添加m01

upload successful

upload successful

部署Portainer Agent

打开App Template,点击Poirtainer Agent
upload successful

给stack取个名字,然后单击deploy

upload successful

部署完成后,每个节点会起一个

upload successful

部署Portainer Agent的另外一个途径是在任意manager节点上,执行
以下命令

1
curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent

添加其他Endpoint

这里以添加m02为例,Endpoint的名称我们就填m02这个主机名好了,然后就是要填写Endpoint URL,就填m02节点的IP地址以及Portainer Agent的端口号,即192.168.56.112:9001

upload successful

所有Endpoint添加完毕

upload successful

这里的m01也可以删掉后,用Agent来添加

镜像中心设置

当镜像中心是需要身份认证的,那么可以在registries里添加一个镜像中心,输入镜像中心的用户名,密码即可,这样集群中的所有节点都能从该镜像中心拉取镜像了。由于篇幅,这里不再赘述跟截图了,读者可以自行尝试。

测试集群

  • 部署一个nginx service
1
sudo docker service create --name mynginx --replicas 3 -p 8088:80 nginx:alpine

upload successful

我们发现通过任意节点的8088端口都能访问,那是因为Ingress的特性使得请求可以转发到任何一台Worker或者Manager节点上,然后通过内置的routing mesh的Load Balancer通过LVS转发请求到Service的其中一个副本,一个值得注意的点是,就算这台Node上没有运行这个Service的容器。Load Balancer也会找到其他机器上存在的副本,这个主要是通过内置的DNS加LVS转发实现。

  • 扩容、缩容
1
sudo docker service scale mynginx=2
1
sudo docker service scale mynginx=3

附录

删除节点

删除manager节点

manager节点不能直接删除,需要先降级到worker节点再删除。

比如,这里我误把n02作为manager加入到集群,我想把n02改成worker节点。

upload successful

1
2
sudo docker node demote cu8f
sudo docker node rm cu8f

cu8f为节点ID的前缀

删除worker节点

1
sudo docker node rm [ID或HOSTNAME]

工作节点离开集群

在工作节点上执行

1
sudo docker swarm leave

工作节点离开集群后,节点状态就会变为Down,这样此节点就可以加入到另外一个swarm集群了。(原先的swarm集群manager节点上记得用sudo docker node rm xxx删除此节点)

创建overlay网络

1
docker network create --subnet=192.168.10.0/24 --attachable -d overlay my-net

QA

如何删除docker

通过sudo apt-get purge docker-ce,然后将/var/lib/docker目录删掉

参考资料

http://dockone.io/article/8808 房多多容器化和容器云实践
https://www.cnblogs.com/bigberg/p/8656963.html Docker Macvlan
https://cizixs.com/2017/02/14/network-virtualization-macvlan/ linux 网络虚拟化: macvlan
https://opsx.alibaba.com/guide?lang=zh-CN&document=69a2341e-801e-11e8-8b5a-00163e04cdbb ubuntu 18.04 (bionic) 配置 opsx 安装源

CentOS6.6内核升级,安装docker17.06.2-ce

发表于 2018-05-24 | 分类于 Docker&Kubernetes |
字数统计: 5,474

前言


由于笔者所在公司无论是开发、测试环境,还是准生产环境、生产环境,使用的都是CentOS6.6。要想按照新版本的Docker就必须升级Linux内核。因此,此文讲解如何升级Linux内核,以及在升级完内核后,如何安装Docker17.06.2-ce。其他版本的docker笔者没有尝试,应该也是可行的。毕竟内核升级到4.4.132-1了

阅读全文 »

Docker下Zookeeper集群搭建

发表于 2018-05-05 | 分类于 Docker&Kubernetes |
字数统计: 6,110

前言

自己了解一些Docker容器技术,想在自己机器上搭建一个zookeeper集群,以前在虚拟机上有过实践经验,这次就想着在Docker下也搞一个Zookeeper集群。Docker的相关介绍这里就不多讲了,大家可以通过搜索引擎查阅相关资料。

阅读全文 »

数据结构-线性表的链式表示与实现

发表于 2018-02-06 | 分类于 数据结构与算法 |
字数统计: 2,535

线性表链式存储结构定义

线性表的链式存储结构的特点是用一组任务的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是非连续的。这就意味着,这些数据元素可以存储在内存未被占用的任意位置。

阅读全文 »

数据结构-线性表的顺序表示与实现

发表于 2018-01-22 | 分类于 数据结构与算法 |
字数统计: 1,571

线性表的抽象数据类型定义

ADT 线性表(List)

Data

​ 线性表的数据对象集合为{a1,a2,…,ai,ai+1,…,an-1,an},每个元素的类型为DataType。其中,除第一个元素a1外,每个元素都有一个直接前驱元素,除最后一个元素an外,每个元素都有一个直接后继元素,数据元素之间的关系是一对一的

阅读全文 »

【Spring Cloud微服务全家桶】03之Eureka控制台的General Info(unavailable-replicas available-replicas显示不正确问题)

发表于 2017-09-01 | 分类于 Spring Cloud |
字数统计: 325

前言

之前【Spring Cloud微服务全家桶】之高可用服务注册中心 文章介绍了Eureka Server高可用

阅读全文 »

【Spring Cloud微服务全家桶】02之高可用服务注册中心

发表于 2017-09-01 | 分类于 Spring Cloud |
字数统计: 2,172

前言

在Spring Cloud微服务全家桶之服务注册、服务发现(Eureka、Consul作为服务注册中心)文中,使用的是单点eureka注册中心。在开发测试环境是可以的,但生产环境强烈不建议用单点eureka注册中心。

阅读全文 »
123
Jay Chang

Jay Chang

当你的才华还撑不起你的野心时,你就应该静下心来学习。

21 日志
5 分类
13 标签
RSS
GitHub Twitter Facebook Google E-Mail
Links
  • 美团点评
  • 有赞技术
  • 网易乐得
  • 你假笨
  • 占小狼
  • 斩秋
  • 芋道源码
  • 伍翀Jark
  • 郭俊Jason
  • Chenssy
  • 龙谭斋
  • Jerry Qu
  • MoreWindows
  • Philipp Hauer
© 2017 — 2023 Jay Chang 浙ICP备19027095号
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.4