Python 的类中有三种资源类型, 分别为字段, 方法和属性.

字段又分为静态字段和普通字段

方法分为静态方法, 普通方法和类方法

而字段和方法又能有私有字段和私有方法的形态

本篇文章就来介绍 Python 类中各个资源的访问规则

字段

公有静态字段

  • 在类的外部使用对象名访问
1
2
3
4
5
class A:
name = 'polarsnow'

a = A()
print(a.name)

执行结果为:

1
polarsnow

此种形态是使用实例化后的对象去访问的静态字段, 一般情况下, 最好用对象去访问对象自己的普通字段, 不要用对象来访问静态字段

  • 在类的外部使用类名访问
1
2
3
4
class A:
name = 'polarsnow'

print(A.name)

执行结果为:

1
polarsnow

此种形态是一般情况下, 正确访问类中静态字段的方式, 即使用类名访问

  • 在类的内部使用对象访问
1
2
3
4
5
6
7
8
class A:
name = 'polarsnow'

def f(self):
return self.name

a = A()
print(a.f())

此种情况是在类的内部, 通过一个方法来获取类中静态字段的值, 在方法中, 使用了对象的名字去调用获取了类中静态字段的值, 一般情况下, 上面也说了, 对于类中对静态字段的访问, 需要使用类名去实现, 不推荐使用对象名去调用

  • 在类的内部使用类名访问
1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
name = 'polarsnow'

def f(self):
return A.name

@classmethod
def f2(cls):
return cls.name

a = A()
print(a.f())
print(a.f2())

执行结果为:

1
2
polarsnow
polarsnow

此种情况下, 我通过两种方式, 用类名获取了静态字段的值. 如果使用普通方法的话, 默认方法内的形参只传递了对象的名字, 如果想显示地调用类中的静态字段的话, 需要明确地指定类名; 但是如果是用对象方法的话, 对象方法默认帮我们传递了对象的名字, 所以我们可以使用cls变量直接调用静态字段

私有静态字段

  • 在类的内部使用类方法访问
1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
__name = 'polarsnow'

def f(self):
return A.__name

@classmethod
def f2(cls):
return cls.__name

a = A()
print(a.f())
print(a.f2())

执行结果为:

1
2
polarsnow
polarsnow

私有的静态字段, 不能在外部使用对象或类名调用, 只能通过在类的内部访问. 上面的例子提供了两种方式来访问私有的静态字段. 一种是在普通方法内通过显示指定来访问, 一种是在类方法中, 通过形参, 隐式地通过cls类名来访问

方法

公有方法

  • 静态方法
  • 普通方法
  • 类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A:
name = 'polarsnow'

def __init__(self, name):
self.name = name

def f(self):
return self.name

@classmethod
def f2(cls):
return cls.name

@staticmethod
def f3():
return '123'

a = A('lvrui')
print(a.f())
print(a.f2())
print(a.f3())

执行结果为:

1
2
3
lvrui
polarsnow
123

在普通方法中, python 隐式传递了对象名; 在类方法总, python 隐式传递了类名; 而静态方法的本质既没有用到对象也没有用到类, 如果静态方法拿到外面来, 就是 python 中普通的函数而已, 之所以把这种函数也放到类中, 是为了方便类中相关操作的聚合, 同一类的操作放到同一个类下

私有方法

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
class A:
name = 'polarsnow'

def __init__(self, name):
self.name = name

def __f(self):
return self.name

def ff(self):
return self.__f()

@classmethod
def __f2(cls):
return cls.name

@classmethod
def ff2(cls):
return cls.__f2()

@staticmethod
def __f3():
return '123'

@staticmethod
def ff3():
return A.__f3()

a = A('lvrui')
print(a.ff())
print(a.ff2())
print(a.ff3())

执行结果为:

1
2
3
lvrui
polarsnow
123

每种类型的方法访问均可以访问自己的私有方法. 在共有静态方法调用私有静态方法时需要注意, 只能使用类名去访问私有静态方法

在实际使用中, 对于私有方法的访问, 也可以跨类型访问, 比如我可以用一个普通方法去调用一个私有的类方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A:
name = 'polarsnow'

def __init__(self, name):
self.name = name

def f(self):
return self.__f2()

@classmethod
def __f2(cls):
return cls.name


a = A('lvrui')
print(a.f())

执行结果为:

1
polarsnow

属性

私有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
class A:

@property
def __name(self):
return 'polarsnow'

@property
def n(self):
return self.__name


a = A()
print(a.n)

执行结果为:

1
polarsnow

1
2
3
4
5
6
7
8
9
10
[root@cord-gitlab-128-92 ~]# gitlab-rails console production
Loading production environment (Rails 4.2.7.1)
irb(main):001:0> u = User.where(id: 1).first
=> #<User id: 1, email: "admin@example.com", created_at: "2016-11-03 10:17:48", updated_at: "2017-07-11 03:21:27", name: "Administrator", admin: true, projects_limit: 10, skype: "", linkedin: "", twitter: "", authentication_token: "eTsFFy-5tb7qXAKLS1jf", theme_id: 2, bio: nil, username: "root", can_create_group: true, can_create_team: false, state: "active", color_scheme_id: 1, password_expires_at: nil, created_by_id: nil, last_credential_check_at: nil, avatar: nil, hide_no_ssh_key: false, website_url: "", notification_email: "admin@example.com", hide_no_password: false, password_automatically_set: false, location: nil, encrypted_otp_secret: nil, encrypted_otp_secret_iv: nil, encrypted_otp_secret_salt: nil, otp_required_for_login: false, otp_backup_codes: nil, public_email: "", dashboard: 0, project_view: 0, consumed_timestep: nil, layout: 0, hide_project_limit: false, otp_grace_period_started_at: nil, ldap_email: false, external: false, organization: nil>
irb(main):002:0> u.password = 'NewPassword2017'
=> "NewPassword2017"
irb(main):003:0> u.save!
Enqueued ActionMailer::DeliveryJob (Job ID: 6b4cf332-80c1-4b3e-a158-efb04e66940c) to Sidekiq(mailers) with arguments: "DeviseMailer", "password_change", "deliver_now", gid://gitlab/User/1
=> true
irb(main):004:0>

在 docker 的官方 Registry 中(store.docker.com) 流行的第三方应用在自己的页面中都提供了 dockerfile 的链接. 而很多 dockerfile 的 ENTRYPOINT 命令都是这么写的 ["docker-entrypoint.sh"] 本篇文章就扫盲下 docker-entrypoint.sh 的特殊用法和设计逻辑

MySQL

set -e

你写的每个脚本都应该在文件开头加上set -e, 这句语句告诉bash如果任何语句的执行结果不是true则应该退出. 这样的好处是防止错误像滚雪球般变大导致一个致命的错误, 而这些错误本应该在之前就被处理掉. 如果要增加可读性, 可以使用set -o errexit, 它的作用与set -e相同

set -o pipefail

设计用途同上, 就是希望在执行错误之后立即退出, 不要再向下执行了. 而 -o pipefail 的作用域是管道, 也就是说在 Linux 脚本中的管道, 如果前面的命令执行出了问题, 应该立即退出

shopt -s nullglob

在使用 Linux 中的通配符时 * ?等 如果没有匹配到任何文件, 不会报 No such file or directory 而是将命令后面的参数去掉执行

if [ “${1:0:1}” = ‘-‘ ]; then…

这是一个判断语句, 在官方文件中, 上一行已经给出了注释: if command starts with an option, prepend mysqld

这个判断语句是 ${1:0:1} 意思是判断 $1(调用该脚本的第一个参数), 偏移量0(不偏移), 取一个字符(取字符串的长度)

如果判断出来调用这个脚本后面所跟的参数第一个字符是-中横线的话, 就认为后面的所有字符串都是 mysqld 的启动参数

上面的这个操作类似于 Python 的字符串切片

set – mysqld “$@”

在上面判断完第一个参数是-开头之后, 紧接着就执行了 set -- mysqld "$@" 这个命令. 使用了 set -- 的用法. set —会将他后面所有以空格区分的字符串, 按顺序分别存储到$1, $2, $3 变量中, 其中新的$@set — 后面的全部内容

举例来说: bash docker-entrypoint.sh -f xxx.conf

在这种情况下, set -- mysqld "$@" 中的 $@ 的值为 -f xxx.conf

当执行完 set -- mysqld "$@" 这条命令后:

  • $1=mysqld
  • $2=-f
  • $3=xxx.conf
  • $@=mysqld -f xxx.conf

可以看到, 当执行 docker-entrypoint.sh脚本的时候后面加了 -x形式的参数之后, $@的值发生的改变, 在原有$@值的基础之上, 在前面又预添加了 mysqld 命令

exec “$@”

几乎在每个 docker-entrypoint.sh 脚本的最后一行, 执行的都是 exec "$@"命令

这个命令的意义在于你已经为你的镜像预想到了应该有的调用情况, 当实际使用镜像的人执行了你没有预料到的可执行命令时, 将会走到脚本的这最后一行, 去执行用户新的可执行命令

情况判断

上面直接说了脚本的最后一行, 在之前的脚本中, 需要充分的去考虑你自己的脚本可能会被调用的情况. 还是拿 MySQL 官方的 dockerfile 来说, 他判断以下情况:

  • 开头是 - , 认为是参数的情况
  • 开头是 mysqld, 且用户 id 为0 (root 用户) 的情况
  • 开头是 mysqld 的情况

判断完自己应用的所有调用形态之后, 最后应该加上exec "$@" 命令兜底

${mysql[@]}

Shell 中的数组, 直接执行 ${mysql[@]} 会把这个数组当做可执行程序来执行

1
2
3
4
5
6
7
8
9
10
11
➜  /tmp mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
➜ /tmp echo ${mysql[1]}
mysql
➜ /tmp echo ${mysql[2]}
--protocol=socket
➜ /tmp echo ${mysql[3]}
-uroot
➜ /tmp echo ${mysql[4]}
-hlocalhost
➜ /tmp echo ${mysql[@]}
mysql --protocol=socket -uroot -hlocalhost --socket=

exec gosu mysql “$BASH_SOURCE” “$@”

这里的 gosu 命令, 是 Linux 中 sudo 命令的轻量级”替代品”

gosu 是一个 golang 语言开发的工具, 用来取代 shell 中的 sudo 命令. su 和 sudo 命令有一些缺陷, 主要是会引起不确定的 TTY, 对信号量的转发也存在问题. 如果仅仅为了使用特定的用户运行程序, 使用 su 或 sudo 显得太重了, 为此 gosu 应运而生.

gosu 直接借用了 libcontainer 在容器中启动应用程序的原理, 使用 /etc/passwd 处理应用程序. gosu 首先找出指定的用户或用户组, 然后切换到该用户或用户组. 接下来, 使用 exec 启动应用程序. 到此为止, gosu 完成了它的工作, 不会参与到应用程序后面的声明周期中. 使用这种方式避免了 gosu 处理 TTY 和转发信号量的问题, 把这两个工作直接交给了应用程序去完成


参考文档:

MySQL5.7 字符集设置

  • character-set-client-handshake = FALSE
  • character-set-server = utf8mb4
  • collation-server = utf8mb4_unicode_ci
  • init_connect=’SET NAMES utf8mb4’

character-set-client-handshake

用来控制客户端声明使用字符集和服务端声明使用的字符集在不一致的情况下的兼容性.

1
2
character-set-client-handshake = false
# 设置为 False, 在客户端字符集和服务端字符集不同的时候将拒绝连接到服务端执行任何操作
1
2
3
# 默认为 true
character-set-client-handshake = true
# 设置为 True, 即使客户端字符集和服务端字符集不同, 也允许客户端连接

character-set-server

声明服务端的字符编码, 推荐使用utf8mb4 , 该字符虽然占用空间会比较大, 但是可以兼容 emoji 😈 表情的存储

1
character-set-server = utf8mb4

collation-server

声明服务端的字符集, 字符编码和字符集一一对应, 既然使用了utf8mb4的字符集, 就要声明使用对应的字符编码

1
collation-server = utf8mb4_unicode_ci

init_connect

init_connect 是用户登录到数据库上之后, 在执行第一次查询之前执行里面的内容. 如果 init_connect 的内容有语法错误, 导致执行失败, 会导致用户无法执行查询, 从mysql 退出

使用 init_connect 执行 SET NAMES utf8mb4 意为:

  • 声明自己(客户端)使用的是 utf8mb4 的字符编码
  • 希望服务器返回给自己 utf8mb4 的查询结果
1
init_connect = 'SET NAMES utf8mb4'

完整配置

1
2
3
4
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4'

参考文章:

MySQL5.7 在 5.6 版本的基础之上做了大量的优化, 本篇文章开篇将重点围绕经过优化的基于 GTID 的多线程复制和半同步复制的特性介绍, 后续会持续增加 MySQL5.7 的调优参数

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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
[client]
default-character-set = utf8mb4

[mysqld]

### 基本属性配置
port = 3306
datadir=/data/mysql
# 禁用主机名解析
skip-name-resolve
# 默认的数据库引擎
default-storage-engine = InnoDB

### 字符集配置
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'

### GTID
server_id = 59
# 为保证 GTID 复制的稳定, 行级日志
binlog_format = row
# 开启 gtid 功能
gtid_mode = on
# 保障 GTID 事务安全
# 当启用enforce_gtid_consistency功能的时候,
# MySQL只允许能够保障事务安全, 并且能够被日志记录的SQL语句被执行,
# 像create table ... select 和 create temporarytable语句,
# 以及同时更新事务表和非事务表的SQL语句或事务都不允许执行
enforce-gtid-consistency = true
# 以下两条配置为主从切换, 数据库高可用的必须配置
# 开启 binlog 日志功能
log_bin = on
# 开启从库更新 binlog 日志
log-slave-updates = on

### 慢查询日志
# 打开慢查询日志功能
slow_query_log = 1
# 超过2秒的查询记录下来
long_query_time = 2
# 记录下没有使用索引的查询
log_queries_not_using_indexes = 1

### 自动修复
# 记录 relay.info 到数据表中
relay_log_info_repository = TABLE
# 记录 master.info 到数据表中
master_info_repository = TABLE
# 启用 relaylog 的自动修复功能
relay_log_recovery = on
# 在 SQL 线程执行完一个 relaylog 后自动删除
relay_log_purge = 1


### 数据安全性配置
# 关闭 master 创建 function 的功能
log_bin_trust_function_creators = off
# 每执行一个事务都强制写入磁盘
sync_binlog = 1
# timestamp 列如果没有显式定义为 not null, 则支持null属性
# 设置 timestamp 的列值为 null, 不会被设置为 current timestamp
explicit_defaults_for_timestamp=true

### 优化配置
# 优化中文全文模糊索引
ft_min_word_len = 1
# 默认库名表名保存为小写, 不区分大小写
lower_case_table_names = 1
# 单条记录写入最大的大小限制
# 过小可能会导致写入(导入)数据失败
max_allowed_packet = 256M
# 半同步复制开启
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_slave_enabled = 1
# 半同步复制超时时间设置
rpl_semi_sync_master_timeout = 1000
# 复制模式(保持系统默认)
rpl_semi_sync_master_wait_point = AFTER_SYNC
# 后端只要有一台收到日志并写入 relaylog 就算成功
rpl_semi_sync_master_wait_slave_count = 1
# 多线程复制
slave_parallel_type = logical_clock
slave_parallel_workers = 4

### 连接数限制
max_connections = 1500
# 验证密码超过20次拒绝连接
max_connect_errors = 20
# back_log值指出在mysql暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中
# 也就是说,如果MySql的连接数达到max_connections时,新来的请求将会被存在堆栈中
# 以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log
# 将不被授予连接资源
back_log = 500
open_files_limit = 65535
# 服务器关闭交互式连接前等待活动的秒数
interactive_timeout = 3600
# 服务器关闭非交互连接之前等待活动的秒数
wait_timeout = 3600

### 内存分配
# 指定表高速缓存的大小。每当MySQL访问一个表时,如果在表缓冲区中还有空间
# 该表就被打开并放入其中,这样可以更快地访问表内容
table_open_cache = 1024
# 为每个session 分配的内存, 在事务过程中用来存储二进制日志的缓存
binlog_cache_size = 2M
# 在内存的临时表最大大小
tmp_table_size = 128M
# 创建内存表的最大大小(保持系统默认, 不允许创建过大的内存表)
# 如果有需求当做缓存来用, 可以适当调大此值
max_heap_table_size = 16M
# 顺序读, 读入缓冲区大小设置
# 全表扫描次数多的话, 可以调大此值
read_buffer_size = 1M
# 随机读, 读入缓冲区大小设置
read_rnd_buffer_size = 8M
# 高并发的情况下, 需要减小此值到64K-128K
sort_buffer_size = 1M
# 每个查询最大的缓存大小是1M, 最大缓存64M 数据
query_cache_size = 64M
query_cache_limit = 1M
# 提到 join 的效率
join_buffer_size = 16M
# 线程连接重复利用
thread_cache_size = 64

### InnoDB 优化
## 内存利用方面的设置
# 数据缓冲区
innodb_buffer_pool_size=2G
## 日志方面设置
# 事务日志大小
innodb_log_file_size = 256M
# 日志缓冲区大小
innodb_log_buffer_size = 4M
# 事务在内存中的缓冲
innodb_log_buffer_size = 3M
# 主库保持系统默认, 事务立即写入磁盘, 不会丢失任何一个事务
innodb_flush_log_at_trx_commit = 1
# mysql 的数据文件设置, 初始100, 以10M 自动扩展
innodb_data_file_path = ibdata1:100M:autoextend
# 为提高性能, MySQL可以以循环方式将日志文件写到多个文件
innodb_log_files_in_group = 3
##其他设置
# 如果库里的表特别多的情况,请增加此值
innodb_open_files = 800
# 为每个 InnoDB 表分配单独的表空间
innodb_file_per_table = 1
# InnoDB 使用后台线程处理数据页上写 I/O(输入)请求的数量
innodb_write_io_threads = 8
# InnoDB 使用后台线程处理数据页上读 I/O(输出)请求的数量
innodb_read_io_threads = 8
# 启用单独的线程来回收无用的数据
innodb_purge_threads = 1
# 脏数据刷入磁盘(先保持系统默认, swap 过多使用时, 调小此值, 调小后, 与磁盘交互增多, 性能降低)
# innodb_max_dirty_pages_pct = 90
# 事务等待获取资源等待的最长时间
innodb_lock_wait_timeout = 120
# 开启 InnoDB 严格检查模式, 不警告, 直接报错
innodb_strict_mode=1
# 允许列索引最大达到3072
innodb_large_prefix = on

[mysqldump]
# 开启快速导出
quick
default-character-set = utf8mb4
max_allowed_packet = 256M

[mysql]
# 开启 tab 补全
auto-rehash
default-character-set = utf8mb4

在 docker 的使用时, 经常会 pull 下来一些官方镜像或第三方的镜像, 毕竟绝大部分自己做的镜像也是基于官方镜像而来的, 其中一些对时间比较敏感的应用可能会因为时区的问题而导致异常, 本篇文章介绍两类系统下时区的修改

CentOS/RedHat/Fedora

1
➜  ~ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

执行命令后立即生效

Ubuntu

1
➜  ~ echo "Asia/shanghai" > /etc/timezone

执行命令后立即生效

#特别说明

在测试中发现, Debian 系统虽然应该和 Ubuntu 划分为一类, 但是却需要使用 CentOS 的做法来修改时区

到底需要使用哪种方式来修改时区可以使用以下的小技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 首先进入到容器中
docker exec -it 6170cc378e95 /bin/bash

# 不管是哪个系统版本, 先查看当前的时区配置文件

# 如果是出现一堆"乱码" 最后还带有时区标识的就使用上面 CentOS 的方式修改时区
cat /etc/localtime
xxxxxxxxxxxxxx
UTC0


# 如果是直接出现 xxx/yyy 这种地区字符的结果, 那么就使用上面 Ubuntu 的方式修改时区
cat /etc/timezone
Asia/Shanghai

  • Docker 管理命令
    • 容器管理
    • 镜像管理
    • 网络管理
    • 系统管理
    • 数据卷管理
    • 快照管理
    • 插件管理
    • 其他
    • swarmkit 管理
      • 模式管理
      • 节点管理
      • 秘钥管理
      • 服务管理
      • 服务栈管理

镜像管理 image

build

根据 dockerfile 构建镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@ubuntu:~# docker image build -t centos2 .
Sending build context to Docker daemon 19.46kB
Step 1/2 : FROM centos
---> a8493f5f50ff
Step 2/2 : LABEL author "lvrui"
---> Running in 9a996772de96
---> 61b7efba9133
Removing intermediate container 9a996772de96
Successfully built 61b7efba9133
root@ubuntu:~# cat dockerfile
FROM centos
LABEL author="lvrui"
root@ubuntu:~# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
centos2 latest 61b7efba9133 About a minute ago 192MB
nginx2 0.1 7cb5220c81d5 2 hours ago 204MB
centos latest a8493f5f50ff 37 hours ago 192MB
nginx latest 5766334bdaa0 41 hours ago 183MB
hello-world latest 48b5124b2768 2 months ago 1.84kB

-t 的作用是打标签, 可以直接打成 Registry 的形式, 方便直接 push 到 Registry

如果没有打赏 Registry 地址也没有关系,后期可以通过 tag 命令修改

history

显示镜像的历史构建信息

1
2
3
4
5
6
7
8
9
10
11
12
13
root@ubuntu:~# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
centos2 latest 61b7efba9133 2 minutes ago 192MB
nginx2 0.1 7cb5220c81d5 2 hours ago 204MB
centos latest a8493f5f50ff 37 hours ago 192MB
nginx latest 5766334bdaa0 41 hours ago 183MB
hello-world latest 48b5124b2768 2 months ago 1.84kB
root@ubuntu:~# docker image history centos2
IMAGE CREATED CREATED BY SIZE COMMENT
61b7efba9133 2 minutes ago /bin/sh -c #(nop) LABEL author=lvrui 0B
a8493f5f50ff 37 hours ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 37 hours ago /bin/sh -c #(nop) LABEL name=CentOS Base ... 0B
<missing> 37 hours ago /bin/sh -c #(nop) ADD file:807143da05d7013... 192MB

import/save/load

  • import 导入镜像, 导入 export 出来的镜像
  • save 导出镜像
  • load 导入镜像, 导入 save 出来的镜像

inspect

查看镜像详细信息

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
root@ubuntu:~# docker image inspect centos2
[
{
"Id": "sha256:61b7efba9133e4ceaf4046e76a2f455a1204a8a794f16920c7543010e2759323",
"RepoTags": [
"centos2:latest"
],
"RepoDigests": [],
"Parent": "sha256:a8493f5f50ffda70c2eeb2d09090debf7d39c8ffcd63b43ff81b111ece6f28bf",
"Comment": "",
"Created": "2017-04-08T09:12:50.750877048Z",
"Container": "9a996772de96183b48460aa1f02b907d1e8d97f8d322cc41aff909d2a8a74b61",
"ContainerConfig": {
"Hostname": "f85c553c1496",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"LABEL author=lvrui"
],
"Image": "sha256:a8493f5f50ffda70c2eeb2d09090debf7d39c8ffcd63b43ff81b111ece6f28bf",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": [],
"Labels": {
"author": "lvrui",
"build-date": "20170406",
"license": "GPLv2",
"name": "CentOS Base Image",
"vendor": "CentOS"
}
},
"DockerVersion": "17.04.0-ce",
"Author": "",
"Config": {
"Hostname": "f85c553c1496",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
"Image": "sha256:a8493f5f50ffda70c2eeb2d09090debf7d39c8ffcd63b43ff81b111ece6f28bf",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": [],
"Labels": {
"author": "lvrui",
"build-date": "20170406",
"license": "GPLv2",
"name": "CentOS Base Image",
"vendor": "CentOS"
}
},
"Architecture": "amd64",
"Os": "linux",
"Size": 192481139,
"VirtualSize": 192481139,
"GraphDriver": {
"Data": null,
"Name": "aufs"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:36018b5e978717a047892794aebab513ba6856dbe1bdfeb478ca1219df2c7e9c"
]
}
}
]

ls

作用等同于 docker images 查看镜像列表

1
2
3
4
5
6
7
root@ubuntu:~# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
centos2 latest 61b7efba9133 3 hours ago 192MB
nginx2 0.1 7cb5220c81d5 5 hours ago 204MB
centos latest a8493f5f50ff 40 hours ago 192MB
nginx latest 5766334bdaa0 44 hours ago 183MB
hello-world latest 48b5124b2768 2 months ago 1.84kB

prune

删除没有被引用的镜像

1
root@ubuntu:~# docker image prune

tag/pull/push

  • tag: 打标签
  • pull: 从远程仓库 Registry 拉取镜像
  • push: 从本地上传镜像

从registry中拉取镜像:

1
$ sudo docker pull registry.cn-beijing.aliyuncs.com/lvreg/webconsole:[镜像版本号]

将镜像推送到registry:

1
2
3
$ sudo docker login --username=lvrui03@126.com registry.cn-beijing.aliyuncs.com
$ sudo docker tag [ImageId] registry.cn-beijing.aliyuncs.com/lvreg/webconsole:[镜像版本号]
$ sudo docker push registry.cn-beijing.aliyuncs.com/lvreg/webconsole:[镜像版本号]

注意: 如果 Registry 有密码, 还需要先 login 到镜像仓库才可以上传镜像

rm

删除指定的镜像

1
root@ubuntu:~# docker image rm centos2:latest

在 rabbitmq 3.3.1 版本之后, 取消了 guest 登录的权限, 如果需要强制开启需要以下操作

1
2
➜  ~ echo "[{rabbit, [{loopback_users, []}]}]." > /etc/rabbitmq/rabbitmq.config 
➜ ~ systemctl restart rabbitmq-server

尝试登陆访问: http://rabbitmq-server-IP:15672

  • username: guest
  • password: guest

注意:

  • 在执行上面的重定向之前, 最好先检查你系统中有没有这个文件, 如果是新装的 rabbitmq, 默认是没有的, 可以直接执行上面的命令创建并写入内容
  • 上面写入的内容中一定不要忘记最后的 . 没有这个 . 重启 rabbitmq 会报错的

在日常的运维中, 经常有需要使用 nfs 挂载远程服务器目录的需求, 但是照我个人实际使用的经验来看, nfs 并不是”一劳永逸”, 故障率还是蛮高的, 其中最长遇到的就是由于网络原因/远程 nfs 服务器原因或其他原因导致的 nfs 客户端与 nfs 服务器失联. 失联并不可怕, 可怕的是它并不会自动重连. 这个时候需要我们手动介入到重连的操作中

nfs 客户端与服务端失去连接, 最直观的现象就是在 Linux 命令行中,执行 df -h 命令, 整个 terminal 终端就会卡在那里一动不动(挂起), 你可以参考以下方案解决该问题

新开一个 session

也许你当前的 session 已经被卡住了, 这时需要打开一个新的 session, 如果你是直接登录到 linux 系统界面的, 可以同时按下 “Ctrl+Alt+F2” 切换到第二个 terminal

查询之前挂载的路径

1
2
3
4
➜  ~ nfsstat -m
nfsstat -m
/webserver/page from 192.168.1.112:/webserver/page
Flags: rw,relatime,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.1.4,local_lock=none,addr=192.168.1.112

以上命令不仅仅会显示出当前的挂载点信息, 还会显示出详细的 nfs 属性信息

当然, 你也可以通过 mount 命令来查询 nfs 的挂载点, 前面写着 IP 的就是 nfs 的挂载信息

使用 umount -f PATH 卸载

1
2
➜  ~ cd
➜ ~ umount -f /webserver/page

先 cd 到家目录, 再强制卸载, 如果执行此命令后, 还是提示磁盘”busily” 就使用以下命令卸载

使用 umount -l PATH 卸载

1
2
➜  ~ cd
➜ ~ umount -l /webserver/page

注意: 这里是使用的小写的L

在 A 局域网环境中, 一台服务器使用 VPN 技术拨通了 B 局域网的一台服务器. 如果需要实现 A 局域网的所有服务器到 B 局域网通信, 那么首先需要实现 A 局域网中 VPN(隧道) 服务器的流量转发

防火墙操作

  • 保存规则
1
$ iptables-save > /etc/iptables-script
  • 恢复规则
1
$ iptables-restore < /etc/iptables-script

转发流量的防火墙规则

  • 查看规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ cat iptables.conf

# Generated by iptables-save v1.4.21 on Mon Nov 21 14:49:30 2016
*nat
:PREROUTING ACCEPT [75958158:4710517785]
:INPUT ACCEPT [11156433:697999260]
:OUTPUT ACCEPT [11270560:679018758]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -j MASQUERADE
COMMIT
# Completed on Mon Nov 21 14:49:30 2016
# Generated by iptables-save v1.4.21 on Mon Nov 21 14:49:30 2016
*filter
:INPUT ACCEPT [1937717187:1020106795408]
:FORWARD ACCEPT [2772458722:1915560771769]
:OUTPUT ACCEPT [2100892309:1998825790936]
COMMIT
# Completed on Mon Nov 21 14:49:30 2016
  • 导入规则
1
$ iptables-restore < iptables.conf

开启网卡转发

1
echo 1 > /proc/sys/net/ipv4/ip_forward