Jim

Talk is cheap. Show me the code.


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

  • 搜索

自定义 Zabbix 监控指标及图表

发表于 2018-05-19 | 分类于 Zabbix

问题描述

有时候 Zabbix 监控系统的模版提供的监控指标并不能满足我们的需求,比如我们要监控服务器的线程数、TCP 连接数等,这些指标在 Zabbix 自带的模板中是没有的,这时候我们就需要自定义监控指标来实现可视化监控。本文以监控服务器的 TCP 连接数为例来说明如何自定义监控指标来实现可视化监控。

解决问题

总体思路是:
修改 Zabbix Agent 端的配置文件,添加监控指标的键值对 –> 重启 Zabbix Agent –> 在 Zabbix Server 端界面化控制台中的模板添加监控指标,指定配置文件中的键 –> 创建指标的可视化展示。
以下分步图文列出如何操作:

1. 修改 Zabbix Agent 端配置文件,添加监控指标的键值对

vim 打开 Zabbix Agent 端配置文件 /home/zabbix/zabbix/etc/zabbix_agentd.conf ,末尾添加如下内容:

1
2
UnsafeUserParameters=1
UserParameter=tcp.num,netstat -atunp | grep ESTABLISHED | wc -l

  • UnsafeUserParameters: 自定义指标必需要添加该行;
  • UserParameter: 自定义指标的参数;
  • tcp.num: 监控指标的键,在 Zabbix Server 端创建监控指标时会用到,可以随意命名,比如 tcp.count;
  • netstat -atunp | grep ESTABLISHED | wc -l:监控指标的值(注意:该值必须是数值类型,否则报错),获取服务器的 TCP 连接数,键和值之间通过英文逗号分隔;

2. 重启 Zabbix Agent 端

1
/home/zabbix/zabbix/sbin/zabbix_agentd -c /home/zabbix/zabbix/etc/zabbix_agentd.conf

3. 在 Zabbix Server 端界面化控制台创建监控指标

为了让所有监控机器都能生效,所以在这里选择 Zabbix Server 自带的系统模板 Template OS Linux 中添加指标:


4. 创建指标的可视化展示

在第三步中已经完成了监控指标的创建,即 Zabbix Server 已经开始收集 Agent 端的数据,但是我们还没有配置相应指标的可视化图表展示,无法看到该指标随时间的推移的变化趋势,接下来我们创建一个梯度图来可视化展示该指标的变化:




到这里该指标的可视化图表已创建完成,可以跳转到控制台首页查看相应 Zabbix Agent 的图表:

注意事项

由于 netstat 命令在非 root 用户下使用会有警告信息:

1
2
3
4
[zabbix@awsuw7-189 ~]$ netstat -atunp | grep ESTABLISHED | wc -l
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
329

这样 Zabbix Agent 配置文件中使用 netstat -atunp | grep ESTABLISHED | wc -l 获取到的 value 会是上面所有的输出(即字符串类型),而不是 wc -l 得到的数值类型,从而导致配置自定义监控指标后会报错:

解决方法其实比较简单,切换到 root 用户给 netstat 命令添加 s 权限即可解决:

1
[root@awsuw7-189 ~]# chmod u+s /bin/netstat

Vagrant Shell 配置器的使用

发表于 2018-05-06 | 分类于 Vagrant

Vagrant Shell 配置器的使用

摘要: 本文翻译自 Vagrant 官方文档 Shell Provisioner 部分,主要介绍了 Vagrant Shell 配置器的使用,如何使用内联脚本代码和外部脚本文件来配置虚拟机。

Provisioner name: “shell”

Vagrant Shell 配置器允许你向虚拟机上传脚本并执行。

对于想要快速启动和运行 Vagrant 的新手来说,Shell 配置器非常理想,并为不熟悉 Chef 或 Puppet 等配置管理系统的用户提供了选择。

对于类 POSIX 机器,shell 配置器使用 SSH 执行脚本。对于 Windows 机器,使用 WinRM 来执行脚本,shell 配置器通过 WinRM 执行 PowerShell 和 Batch (译者注:批处理) 脚本。

选项

shell 配置器有很多选项,其中 inline 或者 path 选项是必须的:

  • inline (string) - 指定在远程机器执行的 shell 内联命令。更多信息见下面 inline scripts 章节。
  • path (string) - 要上传并执行的 shell 脚本路径。该脚本可以是一个相对于 Vagrantfile 工程的文件或者是一个远程脚本(比如 gist)。

剩下的这些选项是可选的:

  • args (string or array) - 当以单一字符串执行脚本时,传递给脚本的参数。这些参数必须写得好像它们直接在命令行上键入一样,所以在需要时一定要避免字符、引号等。你也可以使用数组来传递参数。在这种情况下,Vagrant 将会为你处理引用。
  • env (string) - 传递给脚本作为环境变量的键值对。Vagrant 会处理环境变量值的引用,但是键保持不变。
  • binary (boolean) - Vagrant 会自动用 Unix 换行符来代替 Windows 换行符。如果设置为 false,不会替换。默认值是 “false”。如果 shell 配置器通过 WinRM 来交互,那么默认值是 “true”。
  • privileged (boolean) - 指定是否以超级用户执行脚本。默认是 “true”。Windows guest 虚拟机使用计划任务作为真正的管理员运行,而不受WinRM限
  • upload_path (string) - 上传脚本的远程路径。脚本将会通过 SCP 上传到 SSH 用户,因此该路径对于 SSH 用户必须是可写的。默认路径是 /tmp/vagrant-shell。在 Windows 下,这个默认路径是 C:\tmp\vagrant-shell。
  • keep_color (boolean) - Vagrant 根据终端输出是否是 stdout 或 stderr 自动地以绿色或者红色输出。如果设置为 true,Vagrant 不会输出颜色,而使用脚本的原生色彩输出。
  • name (string) - 该值将显示在终端输出中,以便在许多 shell 配置器存在时让用户识别更容易。
  • powershell_args (string) - 传递给 PowerShell 的额外参数,如果你在 Windows 使用 PowerShell。
  • powershell_elevated_interactive (boolean) - 在 Windows 交互式运行脚本。默认是 “false”。也必须享有特权。一定要启用 Windows 的自动登录,因为用户必须登录才能使用交互模式。
  • md5 (string) - 用于验证远程下载的 shell 文件的 MD5 值。
  • sha1 (string) - 用于验证远程下载的 shell 文件的 SHA1 值。
  • sensitive (boolean) - 将 env 选项中的 Hash 值标记为敏感数据,并将其从输出中隐藏起来。默认值是 “false”。

内联脚本

也许最简单的入门方式是使用内联脚本。内联脚本是直接在 Vagrantfile 文件中给定的脚本代码。例如:

1
2
3
4
Vagrant.configure("2") do |config|
config.vm.provision "shell",
inline: "echo Hello, World"
end

当配置器运行时,会在虚拟机中运行 echo Hello, World。

结合少量 Ruby 代码,很容易将 shell 脚本直接嵌入到 Vagrantfile 文件。举例如下:

1
2
3
4
5
6
7
8
$script = <<-SCRIPT
echo I am provisioning...
date > /etc/vagrant_provisioned_at
SCRIPT

Vagrant.configure("2") do |config|
config.vm.provision "shell", inline: $script
end

我知道如果你对 Ruby 不熟悉,那么上面的配置可能会看起来非常高级,但是不用害怕,它做的很简单:将 shell 脚本被赋 $shell 变量。这个全局变量包含一个字符串,然后作为内联脚本传递给 Vagrant 配置文件。

当然,如果在 Vagrantfile 中还有除了基本变量赋值之外的其他 Ruby 代码使你感到不舒服,那么您可以使用一个实际的脚本文件,在下一节中将对此进行说明。

对于 Windows 虚拟机,内联脚本必须是是 PowerShell。Batch(译者注:批处理) 脚本不能作为内联脚本。

外部脚本

shell 配置器还可以指定本地主机上的脚本的路径。Vagrant 会将该脚本上传到虚拟机并执行。例如:

1
2
3
Vagrant.configure("2") do |config|
config.vm.provision "shell", path: "script.sh"
end

上面的路径是相对于中 Vagrantfile 的路径。也可以使用绝对路径,以及 ~ (家目录)和 .. 等快捷方式。

如果你使用远程脚本作为配置器,你也可以将远程脚本的 URL 作为 path 的参数:

1
2
3
Vagrant.configure("2") do |config|
config.vm.provision "shell", path: "https://example.com/provisioner.sh"
end

如果你在 Windows 上运行 Batch(译者注:批处理) 或者 PowerShell 脚本,请确保外部路径具有适当的扩展名(”.bat” 或者 “.ps1”),由于 Windows 使用扩展名来决定所执行文件的类型。如果没有扩展名,那么脚本可能无法使用。

如果运行一个已经在虚拟机存在的脚本文件,你可以使用内联脚本来调用该远程虚拟机脚本:

1
2
3
4
Vagrant.configure("2") do |config|
config.vm.provision "shell",
inline: "/bin/sh /path/to/the/script/already/on/the/guest.sh"
end

脚本参数

您可以像任何普通的shell脚本一样参数化脚本。这些参数可以指定给 shell 配置器。应该将它们指定为字符串,因为它们将作为命令行的输入,因此确保正确地转义任何字符:

1
2
3
4
5
6
Vagrant.configure("2") do |config|
config.vm.provision "shell" do |s|
s.inline = "echo $1"
s.args = "'hello, world!'"
end
end

如果您不想担心引用,则还可以将参数指定为数组:

1
2
3
4
5
6
Vagrant.configure("2") do |config|
config.vm.provision "shell" do |s|
s.inline = "echo $1"
s.args = ["hello, world!"]
end
end

Vagrant 文件配置器的使用

发表于 2018-05-06 | 分类于 Vagrant

摘要: 本文翻译自 Vagrant 官方文档 File Provisioner 部分,主要介绍了文件配置器的使用,如何使用文件配置器将本地文件上拷贝到 Vagrant 所管理的虚拟机上面。

Provisioner name: “file”

Vagrant 文件配置器可以将本地主机的文件或者目录上传到虚拟机。

文件配置器可以轻松地将本地 ~/.gitconfig 复制到虚拟机的 vagrant 用户家目录,这样的话就不需要在每次配置一个新的虚拟机时运行 git config --global 。

1
2
3
4
5
Vagrant.configure("2") do |config|
# ... other configuration

config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
end

如果你想给虚拟机上传一个目录,可以使用下面的文件配置器来完成。复制时,本地文件夹会被复制到虚拟机的 newfolder 文件夹。注意,如果想在虚拟机保持和本机同样的文件夹名称,请确保目的路径名称和本地路径名称一致。

1
2
3
4
5
Vagrant.configure("2") do |config|
# ... other configuration

config.vm.provision "file", source: "~/path/to/host/folder", destination: "$HOME/remote/newfolder"
end

首先将 ~/path/to/host/folder 拷贝到虚拟机:

1
2
3
4
5
6
7
8
9
folder
├── script.sh
├── otherfolder
│ └── hello.sh
├── goodbye.sh
├── hello.sh
└── woot.sh

1 directory, 5 files

然后将 ~/path/to/host/folder 拷贝到虚拟机的 $HOME/remote/newfolder:

1
2
3
4
5
6
7
8
9
newfolder
├── script.sh
├── otherfolder
│ └── hello.sh
├── goodbye.sh
├── hello.sh
└── woot.sh

1 directory, 5 files

注意,文件上传不像文件目录同步,上传的文件或者文件夹不是保持同步的。还是以上面的例子来说,如果你更改了本地的 ~/.gitconfig,那么虚拟机上的同样的文件并不会更改。

文件配置器的文件上传是通过 SSH 或 PowerShell 用户来上传。通常情况下用户的权限是有限的,如果你想将文件上传到需要更高权限的位置,我们推荐将它们上传到临时位置,然后使用 shell 配置器将它们移动到目标的位置。

选项

文件配置器只有两个选项,并且是必须的:

  • source(string) - 要上传的本地文件或者文件夹。
  • destination(string) - 远程虚拟机路径。文件或者文件夹通过 SCP 来上传,因此该目的路径需要对 SSH 用户可写。SSH 用户可以通过 ssh-config 来查看,默认是 vagrant。

注意事项

尽管文件配置器支持尾部斜杠或 “globing”,但这会导致在本地和虚拟机之间复制文件时产生一些令人困惑的结果。例如,如下源路径没有尾部斜杠,而目的路径有尾部斜杠:

1
config.vm.provision "file", source: "~/pathfolder", destination: "/remote/newlocation/"

这意味着 vagrant 会将 ~/pathfolder 上传到远程目录 /remote/newlocation 底下,结果看起来会像下面这样:

1
2
3
4
5
newlocation
├── pathfolder
│ └── file.sh

1 directory, 2 files

同样的行为也可以通过如下配置来实现:

1
config.vm.provision "file", source: "~/pathfolder", destination: "/remote/newlocation/pathfolder"

另一个例子是使用 . 号,拷贝本地机器目录里面的文件,不包括上一层目录:

1
config.vm.provision "file", source: "~/otherfolder/.", destination: "/remote/otherlocation"

以上配置将 ~/otherfolder 目录下的所有文件拷贝到新的路径 /remote/otherlocation。这个也可以通过指定一个和源文件夹不同的远程文件夹来实现:

1
config.vm.provision "file", source: "/otherfolder", destination: "/remote/otherlocation"

用 Plumbum 开发 Python 命令行工具

发表于 2018-04-30 | 分类于 翻译

摘要:本文翻译自 Python Plumbum 开源库的官方文档 Plumbum CLI 部分,主要介绍如何使用 Plumbum CLI 工具包来开发 Python 命令行应用程序,这是一个非常 Pythonic、容易使用、功能强大的工具包,非常值得广大 Python 程序员掌握并使用。

译文:

轻松执行程序的另一方面是轻松编写 CLI 程序。Python 脚本一般使用 optparse 或者最新的 argparse 及其衍生品来开发命令行工具,但是所有这些表现力有限,而且非常不直观(甚至不够 Pythonic)。Plumbum 的 CLI 工具包提供了一个程序化的方法来构建命令行应用程序,不需要创建一个解析器对象,然后填充一系列“选项”,该 CLI 工具包使用内省机制将这些原语转义成 Pythonic 结构。

总体来看,Plumbum CLI 应用程序是一个继承自 plumbum.cli.Application 的类。这些类定义了一个 main() 方法,并且可选地公开出方法和属性来作为命令行的选项。这些选项可能需要参数,而任何剩余的位置参数会根据 main 函数的声明来将其赋予 main 方法。一个简单的 CLI 应用程序看起来像如下这样:

1
2
3
4
5
6
7
8
9
10
11
12
from plumbum import cli

class MyApp(cli.Application):
verbose = cli.Flag(["v", "verbose"], help = "If given, I will be very talkative")

def main(self, filename):
print("I will now read {0}".format(filename))
if self.verbose:
print("Yadda " * 200)

if __name__ == "__main__":
MyApp.run()

你可以运行该程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ python example.py foo
I will now read foo

$ python example.py --help
example.py v1.0

Usage: example.py [SWITCHES] filename
Meta-switches:
-h, --help Prints this help message and quits
--version Prints the program's version and quits

Switches:
-v, --verbose If given, I will be very talkative

到现在为止,你只看到了非常基本的使用。我们现在开始探索该库。

新版本 1.6.1: 你可以直接运行应用程序 MyApp(),不需要参数,也不需要调用 .main()。

应用程序

Application 类是你的应用程序的“容器”,该“容器”由一个你需要实现的main()方法和任何数量公开选项函数和属性。你的应用程序的入口是类方法 run,该方法实例化你的类、解析参数、调用所有的选项函数,然后使用给的位置参数来调用main()函数。为了从命令行运行你的应用程序,你所要做的是:

1
2
if __name__ == "__main__":
MyApp.run()

除了 run() 和 main(),Application 类还公开了两个内置的选项函数:help() 和 version(),分别用于显示帮助和程序的版本。默认情况下,--hep 和 -h 会调用 help(),--version 和 -v 会调用 version(),这些函数被调用后会显示相应的信息然后退出(没有处理任何其他选项)。

你可以通过定义类属性来自定义 help() 和 version() 显示的信息,比如 PROGNAME, VERSION 和 DESCRIPTION。举例:

1
2
3
class MyApp(cli.Application):
PROGNAME = "Foobar"
VERSION = "7.3"

颜色

新版本 1.6

该库也支持终端字符颜色控制。你可以直接将 PROGNAME, VERSION 和 DESCRIPTION 变为带颜色的字符串。如果你给 PROGNAME 设置了颜色,你会得到自定义的程序名字和颜色。使用方法字符串的颜色可以通过设置 COLOR_USAGE 来生效,不同选项组的颜色可以通过设置 COLOR_GROUPS 字典来生效。

举例如下:

1
2
3
4
5
class MyApp(cli.Application):
PROGNAME = colors.green
VERSION = colors.blue | "1.0.2"
COLOR_GROUPS = {"Meta-switches" : colors.bold & colors.yellow}
opts = cli.Flag("--ops", help=colors.magenta | "This is help")

1
2
3
4
5
6
7
8
9
10
11
12
SimpleColorCLI.py 1.0.2

Usage:
SimpleColorCLI.py [SWITCHES]

Meta-switches
-h, --help Prints this help message and quits
--help-all Print help messages of all subcommands and quit
-v, --version Prints the program's version and quits

Switches
--ops This is help

选项函数

switch 装饰器是该 CLI 开发工具包的“灵魂”,它会公开你的 CLI 应用程序的方法来作为 CLI 命令行选项,这些方法运行通过命令行来调用。我们测试下如下应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyApp(cli.Application):
_allow_root = False # provide a default

@cli.switch("--log-to-file", str)
def log_to_file(self, filename):
"""Sets the file into which logs will be emitted"""
logger.addHandler(FileHandle(filename))

@cli.switch(["-r", "--root"])
def allow_as_root(self):
"""If given, allow running as root"""
self._allow_root = True

def main(self):
if os.geteuid() == 0 and not self._allow_root:
raise ValueError("cannot run as root")

当程序运行时,选项函数通过相应的参数被调用。比如,$ ./myapp.py --log-to-file=/tmp/log 将被转化成调用 app.log_to_file("/tmp/log")。在选项函数被执行后,程序的控制权会被传递到 main 方法。

注意
方法的文档字符串和参数名字会被用来渲染帮助信息,尽量保持你的代码 DRY。

autoswitch 可以从函数名字中推断出选项的名称,举例如下:

1
2
3
@cli.autoswitch(str)
def log_to_file(self, filename):
pass

这会将选项函数和 --log-to-file 绑定。

选项参数

如上面例子所示,选项函数可能没有参数(不包括 self)或者有一个参数。如果选项函数接受一个参数,必须指明该参数的类型。如果你不需要特殊的验证,只需传递 str,否则,您可能会传递任何类型(或实际上可调用的任何类型),该类型将接收一个字符串并将其转换为有意义的对象。如果转换是不可行的,那么会抛出 TypeError 或者 ValueError 异常。

举例:

1
2
3
4
5
6
7
8
9
class MyApp(cli.Application):
_port = 8080

@cli.switch(["-p"], int)
def server_port(self, port):
self._port = port

def main(self):
print(self._port)

1
2
3
4
5
$ ./example.py -p 17
17
$ ./example.py -p foo
Argument of -p expected to be <type 'int'>, not 'foo':
ValueError("invalid literal for int() with base 10: 'foo'",)

工具包包含两个额外的”类型”(或者是是验证器):Range 和 Set。Range 指定一个最小值和最大值,限定一个整数在该范围内(闭区间)。Set 指定一组允许的值,并且期望参数匹配这些值中的一个。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyApp(cli.Application):
_port = 8080
_mode = "TCP"

@cli.switch("-p", cli.Range(1024,65535))
def server_port(self, port):
self._port = port

@cli.switch("-m", cli.Set("TCP", "UDP", case_sensitive = False))
def server_mode(self, mode):
self._mode = mode

def main(self):
print(self._port, self._mode)

1
2
3
4
5
6
$ ./example.py -p 17
Argument of -p expected to be [1024..65535], not '17':
ValueError('Not in range [1024..65535]',)
$ ./example.py -m foo
Argument of -m expected to be Set('udp', 'tcp'), not 'foo':
ValueError("Expected one of ['UDP', 'TCP']",)

注意
工具包中还有其他有用的验证器:ExistingFile(确保给定的参数是一个存在的文件),ExistingDirectory(确保给定的参数是一个存在的目录),NonexistentPath(确保给定的参数是一个不存在的路径)。所有这些将参数转换为本地路径。

可重复的选项

很多时候,你需要在同一个命令行中多次指定某个选项。比如,在 gcc 中,你可能使用 -I 参数来引入多个目录。默认情况下,选项只能指定一次,除非你给 switch 装饰器传递 list = True 参数。

1
2
3
4
5
6
7
8
9
class MyApp(cli.Application):
_dirs = []

@cli.switch("-I", str, list = True)
def include_dirs(self, dirs):
self._dirs = dirs

def main(self):
print(self._dirs)

1
2
$ ./example.py -I/foo/bar -I/usr/include
['/foo/bar', '/usr/include']

注意
选项函数只被调用一次,它的参数将会变成一个列表。

强制的选项

如果某个选项是必须的,你可以给 switch 装饰器传递 mandatory = True 来实现。这样的话,如果用户不指定该选项,那么程序是无法运行的。

选项依赖

很多时候,一个选项的出现依赖另一个选项,比如,如果不给定 -y 选项,那么 -x 选项是无法给定的。这种限制可以通过给 switch 装饰器传递 requires 参数来实现,该参数是一个当前选项所依赖的选项名称列表。如果不指定某个选项所依赖的其他选项,那么用户是无法运行程序的。

1
2
3
4
5
6
7
8
class MyApp(cli.Application):
@cli.switch("--log-to-file", str)
def log_to_file(self, filename):
logger.addHandler(logging.FileHandler(filename))

@cli.switch("--verbose", requires = ["--log-to-file"])
def verbose(self):
logger.setLevel(logging.DEBUG)

1
2
$ ./example --verbose
Given --verbose, the following are missing ['log-to-file']

警告
选项函数的调用顺序和命令行指定的选项的顺序是一致的。目前不支持在程序运行时计算选项函数调用的拓扑顺序,但是将来会改进。

选项互斥

有些选项依赖其他选项,但是有些选项是和其他选项互斥的。比如,--verbose 和 --terse 同时存在是不合理的。为此,你可以给 switch 装饰器指定 excludes 列表来实现。

1
2
3
4
5
6
7
8
9
10
11
12
class MyApp(cli.Application):
@cli.switch("--log-to-file", str)
def log_to_file(self, filename):
logger.addHandler(logging.FileHandler(filename))

@cli.switch("--verbose", requires = ["--log-to-file"], excludes = ["--terse"])
def verbose(self):
logger.setLevel(logging.DEBUG)

@cli.switch("--terse", requires = ["--log-to-file"], excludes = ["--verbose"])
def terse(self):
logger.setLevel(logging.WARNING)

1
2
$ ./example --log-to-file=log.txt --verbose --terse
Given --verbose, the following are invalid ['--terse']

选项分组

如果你希望在帮助信息中将某些选项组合在一起,你可以给 switch 装饰器指定 group = "Group Name", Group Name 可以是任意字符串。当显示帮助信息的时候,所有属于同一个组的选项会被聚合在一起。注意,分组不影响选项的处理,但是可以增强帮助信息的可读性。

选项属性

很多时候只需要将选项的参数存储到类的属性中,或者当某个属性给定后设置一个标志。为此,工具包提供了 SwitchAttr,这是一个数据描述符,用来存储参数。 该工具包还提供了两个额外的 SwitchAttr:Flag(如果选项给定后,会给其赋予默认值)和 CountOf (某个选项出现的次数)。

1
2
3
4
5
6
7
class MyApp(cli.Application):
log_file = cli.SwitchAttr("--log-file", str, default = None)
enable_logging = cli.Flag("--no-log", default = True)
verbosity_level = cli.CountOf("-v")

def main(self):
print(self.log_file, self.enable_logging, self.verbosity_level)

1
2
$ ./example.py -v --log-file=log.txt -v --no-log -vvv
log.txt False 5

环境变量

新版本 1.6

你可以使用 envname 参数将环境变量作为 SwitchAttr 的输入。举例如下:

1
2
3
4
5
class MyApp(cli.Application):
log_file = cli.SwitchAttr("--log-file", str, envname="MY_LOG_FILE")

def main(self):
print(self.log_file)

1
2
$ MY_LOG_FILE=this.log ./example.py
this.log

在命令行给定变量值会覆盖相同环境变量的值。

Main

一旦当所有命令行参数被处理后 ,main() 方法会获取程序的控制,并且可以有任意数量的位置参数,比如,在 cp -r /foo /bar 中, /foo 和 /bar 是位置参数。程序接受位置参数的数量依赖于 main() 函数的声明:如果 main 方法有 5 个参数,2 个是有默认值的,那么用户最少需要提供 3 个位置参数并且总数量不能多于 5 个。如果 main 方法的声明中使用的是可变参数(*args),那么位置参数的个数是没有限制的。

1
2
3
class MyApp(cli.Application):
def main(self, src, dst, mode = "normal"):
print(src, dst, mode)

1
2
3
4
5
6
7
8
$ ./example.py /foo /bar
/foo /bar normal
$ ./example.py /foo /bar spam
/foo /bar spam
$ ./example.py /foo
Expected at least 2 positional arguments, got ['/foo']
$ ./example.py /foo /bar spam bacon
Expected at most 3 positional arguments, got ['/foo', '/bar', 'spam', 'bacon']

注意
该方法的声明也用于生成帮助信息,例如:

1
Usage:  [SWITCHES] src dst [mode='normal']

使用可变参数:

1
2
3
class MyApp(cli.Application):
def main(self, src, dst, *eggs):
print(src, dst, eggs)

1
2
3
4
5
6
7
$ ./example.py a b c d
a b ('c', 'd')
$ ./example.py --help
Usage: [SWITCHES] src dst eggs...
Meta-switches:
-h, --help Prints this help message and quits
-v, --version Prints the program's version and quits

位置验证

新版本 1.6

你可以使用 cli.positional 装饰器提供的验证器来验证位置参数。只需在装饰器中传递与 main 函数中的相匹配的验证器即可。例如:

1
2
3
4
class MyApp(cli.Application):
@cli.positional(cli.ExistingFile, cli.NonexistentPath)
def main(self, infile, *outfiles):
"infile is a path, outfiles are a list of paths, proper errors are given"

如果你的程序只在 Python 3 中运行,你可以使用注解来指定验证器,例如:

1
2
3
class MyApp(cli.Application):
def main(self, infile : cli.ExistingFile, *outfiles : cli.NonexistentPath):
"Identical to above MyApp"

如果 positional 装饰器存在,那么注解会被忽略。

子命令

新版本 1.1

随着 CLI 应用程序的扩展,功能变的越来越多,一个通常的做法是将其逻辑分成多个子应用(或者子命令)。一个典型的例子是版本控制系统,比如 git,git 是根命令,在这之下的子命令比如 commit 或者 push 是嵌套的。git 甚至支持命令别名,这运行用户自己创建一些子命令。Plumbum 写类似这样的程序是很轻松的。

在我们开始了解代码之前,先强调两件事情:

  • 在 Plumbum中,每个子命令都是一个完整的 cli.Application 应用,你可以单独执行它,或者从所谓的根命令中分离出来。当应用程序单独执行是,它的父属性是 None,当以子命令运行时,它的父属性指向父应用程序。同样,当父应用使用子命令执行时,它的内嵌命令被设置成内嵌应用。
  • 每个子命令只负责它自己的选项参数(直到下一个子命令)。这允许应用在内嵌应用调用之前来处理它自己的选项和位置参数。例如 git --foo=bar spam push origin --tags:根应用 git 负责选项 --foo 和位置选项 spam ,内嵌应用 push 负责在它之后的参数。从理论上讲,你可以将多个子应用程序嵌套到另一个应用程序中,但在实践中,通常嵌套层级只有一层。

这是一个模仿版本控制系统的例子 geet。我们有一个根应用 Geet ,它有两个子命令 GeetCommit 和 GeetPush:这两个子命令通过 subcommand 装饰器来将其附加到根应用。

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
class Geet(cli.Application):
"""The l33t version control"""
VERSION = "1.7.2"

def main(self, *args):
if args:
print("Unknown command {0!r}".format(args[0]))
return 1 # error exit code
if not self.nested_command: # will be ``None`` if no sub-command follows
print("No command given")
return 1 # error exit code

@Geet.subcommand("commit") # attach 'geet commit'
class GeetCommit(cli.Application):
"""creates a new commit in the current branch"""

auto_add = cli.Flag("-a", help = "automatically add changed files")
message = cli.SwitchAttr("-m", str, mandatory = True, help = "sets the commit message")

def main(self):
print("doing the commit...")

@Geet.subcommand("push") # attach 'geet push'
class GeetPush(cli.Application):
"""pushes the current local branch to the remote one"""
def main(self, remote, branch = None):
print("doing the push...")

if __name__ == "__main__":
Geet.run()

注意

  • 由于 GeetCommit 也是一个 cli.Application,因此你可以直接调用 GeetCommit.run() (这在应用的上下文是合理的)
  • 你也可以不用装饰器而使用 subcommand 方法来附加子命令:Geet.subcommand("push", GeetPush)

以下是运行该应用程序的示例:

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
$ python geet.py --help
geet v1.7.2
The l33t version control

Usage: geet.py [SWITCHES] [SUBCOMMAND [SWITCHES]] args...
Meta-switches:
-h, --help Prints this help message and quits
-v, --version Prints the program's version and quits

Subcommands:
commit creates a new commit in the current branch; see
'geet commit --help' for more info
push pushes the current local branch to the remote
one; see 'geet push --help' for more info

$ python geet.py commit --help
geet commit v1.7.2
creates a new commit in the current branch

Usage: geet commit [SWITCHES]
Meta-switches:
-h, --help Prints this help message and quits
-v, --version Prints the program's version and quits

Switches:
-a automatically add changed files
-m VALUE:str sets the commit message; required

$ python geet.py commit -m "foo"
doing the commit...

配置解析器

应用程序的另一个常见的功能是配置文件解析器,解析后台 INI 配置文件:Config (或者 ConfigINI)。使用示例:

1
2
3
4
5
from plumbum import cli

with cli.Config('~/.myapp_rc') as conf:
one = conf.get('one', '1')
two = conf.get('two', '2')

如果配置文件不存在,那么将会以当前的 key 和默认的 value 来创建一个配置文件,在调用 .get 方法时会得到默认值,当上下文管理器存在时,文件会被创建。如果配置文件存在,那么该文件将会被读取并且没有任何改变。你也可以使用 [] 语法来强制设置一个值或者当变量不存在时获取到一个 ValueError。如果你想避免上下文管理器,你也可以使用 .read 和 .write。

ini 解析器默认使用 [DEFAULT] 段,就像 Python 的 ConfigParser。如果你想使用一个不同的段,只需要在 key 中通过 . 将段和标题分隔开。比如 conf['section.item'] 会将 item 放置在 [section] 下。所有存储在 ConfigINI 中的条目会被转化成 str,str 是经常返回的。

终端实用程序

在 plumbum.cli.terminal 中有多个终端实用程序,用来帮助制作终端应用程序。

get_terminal_size(default=(80,25)) 允许跨平台访问终端屏幕大小,返回值是一个元组 (width, height)。还有几个方法可以用来询问用户输入,比如 readline, ask, choose 和 prompt 都是可用的。

Progress(iterator) 可以使你快速地从迭代器来创建一个进度条。简单地打包一个 slow 迭代器并迭代就会生成一个不错的基于用户屏幕宽度的文本进度条,同时会显示剩余时间。如果你想给 fast 迭代器创建一个进度条,并且在循环中包含代码,那么请使用 Progress.wrap 或者 Progress.range。例如:

1
2
for i in Progress.range(10):
time.sleep(1)

如果在终端中有其他输出,但是仍然需要一个进度条,请传递 has_output=True 参数来禁止进度条清除掉历史输出。

在 plumbum.cli.image 中提供了一个命令行绘图器(Image)。它可以绘制一个类似 PIL 的图像:

1
Image().show_pil(im)

Image 构造函数接受一个可选的 size 参数(如果是 None,那么默认是当前终端大小)和一个字符比例,该比例来自当前字符的高度和宽度的度量,默认值是 2.45。如果设置为 None,ratio 将会被忽略,图像不再被限制成比例缩放。要直接绘制一个图像,show 需要一个文件名和一对参数。show_pil 和 show_pil_double 方法直接接受一个 PIL-like 对象。为了从命令行绘制图像,该模块可以直接被运行:python -m plumbum.cli.image myimage.png。

要获取帮助列表和更多的信息请参见 api docs。

请参阅

  • filecopy.py 示例
  • geet.py - 一个可运行的使用子命令的示例
  • RPyC 已经将基于 bash 的编译脚本换成了 Plumbum CLI。这是多么简短和具有可读性
  • 一篇博客,讲述 CLI 模块的理论

Vagrant 入门指南

发表于 2018-04-22 | 分类于 Vagrant

Vagrant 简介

Vagrant 是一个用来构建和管理虚拟机环境的工具。Vagrant 有着易于使用的工作流,并且专注于自动化,降低了开发者搭建环境的时间,提高了生产力。解决了“在我的机器上可以工作”的问题。

Vagrant 是为了方便的实现虚拟化环境而设计的,使用 Ruby 开发,基于 VirtualBox 等虚拟机管理软件的接口,提供了一个可配置、轻量级的便携式虚拟开发环境。使用 Vagrant 可以很方便的就建立起来一个虚拟环境,而且可以模拟多台虚拟机,这样我们平时还可以在开发机模拟分布式系统。

团队新员工加入,常常会遇到花一天甚至更多时间来从头搭建完整的开发环境,而有了Vagrant,只需要直接将已经打包好的 package(里面包括开发工具,代码库,配置好的服务器等)拿过来就可以工作了,这对于提升工作效率非常有帮助。

为什么选择 Vagrant

Vagrant 提供了一个易于配置,可重复使用,兼容的环境,通过一个单一的工作流程来控制,帮助你和团队最大化生产力和灵活性。
为了实现 Vagrant 的魔力,Vagrant 站在了巨人的肩膀上。虚拟机的配置基于 VirtualBox,VMware,AWS 或者其他提供商。然后一些配置工具,比如 shell 脚本,Chef 或者 Puppet 可以自动化地在虚拟机安装并配置软件。

对于开发者人员

如果你是一个开发者,Vagrant 将在一个一次性的、一致的环境中隔离依赖项及其配置,而不会影响你习惯使用的任何工具(编辑器、浏览器、调试器等)。一旦你或者其他人创建了一个 Vagrantfile,你只需要执行 vagrant up 所有的东西就自动安装和配置了。你团队中的其他成员使用同一个配置文件来创建开发环境,因此不管你工作在 Linux,MacOS X 还是 Windows, 所有团队的成员都可以在统一的环境环境中运行代码,这样就可以避免“在我的机器上可以工作”的问题。

对于运维人员

如果你是一个运维工程师或者 DevOps 工程师,Vagrant 给予你一个一次性的环境来开发和测试基础架构管理脚本。你可以使用本地虚拟机(比如 VirtualBox 或者 VMware)马上测试一些东西,比如 shell 脚本,Chef cookbooks,Puppet 模块等。然后,你可以用同样的配置在远程云上,比如 AWS 或者 RackSpace,来测试这些脚本。抛弃之前自定义脚本来回收 EC2 实例吧,停止使用 SSH 在各种机器之间跳来跳去,请开始使用 Vagrant 来给你的工作带来更多便利。

Vagrant 和 Terraform 的区别

Vagrant 和 Terraform 都出自同一个公司 HashiCorp,该公司主要做一些开源软件,相关的产品还有 Packer,Consul,Vault,Nomad 等。

Terraform 的主要用途是管理云提供商的远程资源,比如 AWS。Terraform 可以管理横跨多个云提供商巨量的基础设施。而 Vagrant 主要用来管理仅使用少量虚拟机的本地开发环境。

Vagrant 用于开发环境,Terraform 普遍用于基础设施管理。


VirtualBox 安装

VirtualBox 是 Oracle 开源的虚拟化系统,和 VMware 是同类产品,支持多个平台,可以到官方网站:https://www.virtualbox.org/wiki/Downloads 下载适合你平台的 VirtualBox 最新版本并安装。

提示:对于 Mac 用户,如果系统为 OSX 10.13.3(mac OS High Sierra) 或者更高版本,安装过程可能会失败,报错提示安装失败,安装器遇到了一个错误导致安装失败...,原因是新版本 Mac 系统的安全机制阻止外部内核扩展安装,导致安装失败。两种解决方法:

  1. 进入系统偏好设置>安全性与隐私>通用,然后手动允许;
  2. 在终端敲命令禁用此安全特性:sudo spctl --master-disable;

Vagrant 安装

到官方网站下载相应系统平台的安装包:http://www.vagrantup.com/downloads.html
直接根据向导进行操作即可完成安装,安装完后就可以在终端输入 vagrant 命令了。

提示:尽量下载最新的程序,因为VirtualBox经常升级,升级后有些接口会变化,老的Vagrant 可能无法使用。

Vagrant 启动第一台虚拟机

到此准备工作(VirtualBox 和 Vagrant 安装)基本上做完了,接下来就可以通过 Vagrant 来启动一台虚拟机了。

在启动虚拟机之前先简单介绍下 Vagrant box:box 是一个打包好的操作系统,是一个后缀名为 .box 的文件,其实是一个压缩包,里面包含了 Vagrant 的配置信息和 VirtualBox 的虚拟机镜像文件。vagrant up 启动虚拟机是基于 box 文件的,因此在启动虚拟机前必须得把 box 文件准备好。或者也可以在启动的时候指定远程 box 地址,在这里我把 box 文件下载下来,然后启动时指定该文件。

我使用网上分享的 ubuntu-server-16.04 这个 box,由于vagrant 官方 box 下载速度特别慢,所以在此提供一下该 box 的百度网盘下载地址,加速下载:https://pan.baidu.com/s/1wJCeWEyxKQLVPi1IH1IlYg

  1. 新建一个目录作为 Vagrant 的工程目录

    1
    2
    $haohao cd /Users/haohao
    $haohao mkdir vagrant
  2. 添加前面下载的 box
    添加 box 命令格式:vagrant box add <本地 box 名称> <box 文件>

    • 本地 box 名称:自定义名称,该名称是本地 vagrant 管理时使用的名称;
    • box 文件:前面下载的 vagrant box 文件或者远程 box url 地址;
      1
      2
      3
      4
      5
      $haohao vagrant box add ubuntu-server-16.04 ubuntu-server-16.04-amd64-vagrant.box
      ==> box: Box file was not detected as metadata. Adding it directly...
      ==> box: Adding box 'ubuntu-server-16.04' (v0) for provider:
      box: Unpacking necessary files from: file:///Users/haohao/vagrant/ubuntu-server-16.04-amd64-vagrant.box
      ==> box: Successfully added box 'ubuntu-server-16.04' (v0) for 'virtualbox'!
  3. 查看 box 是否添加成功
    查看当前 vagrant 中有哪些 box:vagrant box list

    1
    2
    $haohao vagrant box list
    ubuntu-server-16.04 (virtualbox, 0)
  4. 初始化上面添加的 box
    初始化命令格式:vagrant init <本地 box 名称>
    本地 box 名称:第 2 步中添加的 box 名称
    这里初始化前面添加的 box,初始化后会在当前目录生产一个 Vagrantfile 文件,里面包含了虚拟机的各种配置,关于具体每个配置项是什么意思,后面会介绍。

    1
    2
    3
    4
    5
    $haohao vagrant init 'ubuntu-server-16.04'
    A `Vagrantfile` has been placed in this directory. You are now
    ready to `vagrant up` your first virtual environment! Please read
    the comments in the Vagrantfile as well as documentation on
    `vagrantup.com` for more information on using Vagrant.
  5. 启动虚拟机
    虚拟机启动命令:vagrant up
    启动虚拟机时会自动将当前目录(即 Vagrantfile 文件所在目录),和虚拟机的 /vagrant 目录共享。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $haohao vagrant up
    Bringing machine 'default' up with 'virtualbox' provider...
    ==> default: Importing base box 'ubuntu-server-16.04'...
    ==> default: Matching MAC address for NAT networking...
    ==> default: Setting the name of the VM: vagrant_default_1524288099752_62326
    ==> default: Clearing any previously set network interfaces...
    .....
    ==> default: Mounting shared folders...
    default: /vagrant => /Users/haohao/vagrant
  6. 连接虚拟机
    命令格式:vagrant ssh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $haohao vagrant ssh
    Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-98-generic x86_64)

    * Documentation: https://help.ubuntu.com
    * Management: https://landscape.canonical.com
    * Support: https://ubuntu.com/advantage

    Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

    0 packages can be updated.
    0 updates are security updates.

    Last login: Sat Apr 21 05:28:37 2018 from 10.0.2.2
    ubuntu@ubuntu-xenial:~$
  7. 查看 Vagrant 共享目录
    进入虚拟机后执行 df -h 可以看到 Vagrant 默认把宿主机 Vagrantfile 所在的目录和虚拟机的 /vagrant 目录共享,可以通过 ls /vagrant/ 查看该目录内容,内容和宿主机对应目录一致。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ubuntu@ubuntu-xenial:~$ df -h
    Filesystem Size Used Avail Use% Mounted on
    udev 490M 0 490M 0% /dev
    tmpfs 100M 3.1M 97M 4% /run
    /dev/sda1 9.7G 857M 8.8G 9% /
    tmpfs 497M 0 497M 0% /dev/shm
    tmpfs 5.0M 0 5.0M 0% /run/lock
    tmpfs 497M 0 497M 0% /sys/fs/cgroup
    vagrant 234G 49G 185G 21% /vagrant
    tmpfs 100M 0 100M 0% /run/user/1000

    # ls 查看该共享目录内容和宿主机内容一致
    ubuntu@ubuntu-xenial:~$ ls /vagrant/
    ubuntu-xenial-16.04-cloudimg-console.log Vagrantfile

Vagrant 配置文件浅析

前面我们执行 vagrant init <本地 box 名称> 会在当前目录生成 Vagrantfile 文件,这个文件是非常重要的,一般给别人共享自己的环境时都是提供一个 Vagrantfile 和一个 box 文件,这样就可以很轻松地将环境共享给别人,别人能得到一模一样的统一的环境,这就是使用 Vagrant 的好处。

Vagrantfile 主要包括三个方面的配置,虚拟机的配置、SSH配置、Vagrant 的一些基础配置。Vagrant 是使用 Ruby 开发的,所以它的配置语法也是 Ruby 的,对于没有学过 Ruby 的朋友也没关系,根据例子模仿下就会了。

修改完配置后需要执行 vagrant reload 重启 VM 使其配置生效。

以下简单介绍下常用配置的配置项:

  1. box 名称设置
    config.vm.box = "base"
    上面这配置展示了 Vagrant 要去启用那个box作为系统,也就是前面我们输入 vagrant init <本地 box 名称>时所指定的 box,如果沒有输入 box 名称的话,那么默认就是 base。

  2. VM 相关配置
    VirtualBox 提供了 VBoxManage 这个命令行工具,可以让我们设定 VM,用modifyvm这个命令让我们可以设定 VM 的名称和内存大小等等,这里说的名称指的是在 VirtualBox 中显示的名称,我们也可以在 Vagrantfile 中进行设定,举例如下:

    调用 VBoxManage 的 modifyvm 的命令,设置 VM 的名称为 ubuntu,内存为 1024 MB。你可以类似的通过定制其它 VM 属性来定制你自己的 VM。

    1
    2
    3
    config.vm.provider "virtualbox" do |v|
    v.customize ["modifyvm", :id, "--name", "ubuntu", "--memory", "1024"]
    end
  3. 网络设置
    Vagrant 有两种方式来进行网络连接,一种是 host-only (主机模式),这种模式下所有的虚拟系统是可以互相通信的,但虚拟系统和真实的网络是被隔离开的,虚拟机和宿主机是可以互相通信的,相当于两台机器通过双绞线互联。另一种是Bridge(桥接模式),该模式下的 VM 就像是局域网中的一台独立的主机,可以和局域网中的任何一台机器通信,这种情况下需要手动给 VM 配 IP 地址,子网掩码等。我们一般选择 host-only 模式,配置如下:

    1
    config.vm.network :private_network, ip: "11.11.11.11"
  4. hostname 设置
    hostname 的设置非常简单:

    1
    config.vm.hostname = "kubernetes"
  5. 目录共享
    我们前面介绍过/vagrant目录默认就是当前的开发目录,这是在虚拟机开启的时候默认挂载同步的。我们还可以通过配置来设置额外的同步目录:

    1
    2
    # 第一个参数是主机的目录,第二个参数是虚拟机挂载的目录
    config.vm.synced_folder "/Users/haohao/data", "/vagrant_data"
  6. 端口转发
    对宿主机器上 8080 端口的访问请求 forward 到虚拟机的 80 端口的服务上:

    1
    config.vm.network :forwarded_port, guest: 80, host: 8080

Vagrant 常用命令清单

  • vagrant box add 添加box
  • vagrant init 初始化 box
  • vagrant up 启动虚拟机
  • vagrant ssh 登录虚拟机
  • vagrant box list 列出 Vagrant 当前 box 列表
  • vagrant box remove 删除相应的 box
  • vagrant destroy 停止当前正在运行的虚拟机并销毁所有创建的资源
  • vagrant halt 关机
  • vagrant package 把当前的运行的虚拟机环境进行打包为 box 文件
  • vagrant plugin 安装卸载插件
  • vagrant reload 重新启动虚拟机,重新载入配置文件
  • vagrant resume 恢复被挂起的状态
  • vagrant ssh-config 输出用于 ssh 连接的一些信息
  • vagrant status 获取当前虚拟机的状态
  • vagrant suspend 挂起当前的虚拟机
  • vagrant up 重启虚拟机

Vagrant 启动虚拟机集群

前面我们都是通过一个 Vagrantfile 配置启动单台机器,如果我们要启动一个集群,那么可以把需要的节点在一个 Vagrantfile 写好,然后直接就可以通过 vagrant up 同时启动多个 VM 组成一个集群。以下示例配置一个 web 节点和一个 db 节点,两个节点在同一个网段,并且使用同一个 box 启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Vagrant.configure("2") do |config|
config.vm.define :web do |web|
web.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--name", "web", "--memory", "512"]
end
web.vm.box = "ubuntu-server-16.04"
web.vm.hostname = "web"
web.vm.network :private_network, ip: "11.11.1.1"
end

config.vm.define :db do |db|
db.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--name", "db", "--memory", "512"]
end
db.vm.box = "ubuntu-server-16.04"
db.vm.hostname = "db"
db.vm.network :private_network, ip: "11.11.1.2"
end
end

相关连接

https://www.vagrantup.com/intro/index.html
https://blog.csdn.net/rickiyeat/article/details/55097687
https://github.com/astaxie/go-best-practice/blob/master/ebook/zh/01.0.md

Google Kubernetes Engine(GKE) 使用初探

发表于 2018-04-14 | 分类于 Google Cloud Platform

概述

Google 的 k8s 在 2017 年已经从容器编排领域的竞争中取得主导地位,从 Docker 之前的一度排挤到最终完全拥抱 k8s,显然 k8s 已经成了目前业界的标准。但是到目前为止能提供 k8s 完全托管服务的云服务商少之又少,即便是目前在云提供商有统治力的 AWS 也没有完全提供 k8s 托管服务,仅仅提供有限的定制服务,在这一方面并不成熟。然而 Google 的 k8s 托管服务,即 GKE,却将 k8s 托管服务做到了极致(至少目前看来),不仅提供了全套的 k8s 托管服务,更引人注目的是 Google 已然将 Autoscaler 和 k8s 集成,实现了 k8s 节点的自动伸缩机制,能根据 pod 的需求自动化添加或删除节点,当现有节点无法承载新的服务时会自动添加节点来满足需求,当现有节点足够空闲时会启用调节机制自动化收缩节点,从某种意义上来说这几乎做到了无服务器的理念。然而这也许只是冰山一角,更多强大的功能还需要进一步探索,本文只是一个入门指南,主要指导能快速开始上手基于 Google Cloud Platform 的 GKE 服务(k8s 托管服务)。

GKE 入门指南

接下来我们一步步指引如何使用 GKE 来部署服务,前提是对 k8s 有所了解,能简单使用 kubectl 命令。

  1. 安装并配置 Google Cloud SDK
    Google Cloud SDK 是 访问 GCP(Google Cloud Platform) 平台各种资源的命令行工具集,类似 aws 的 aws 命令行工具。
    安装和配置就不多说了,点击下面链接选择相应操作系统版本的 tar 包下载,然后解压,在 PATH 环境变量中添加 google-cloud-sdk/bin 即可:
    https://cloud.google.com/sdk/?hl=zh-cn
  2. 初始化 Google Cloud SDK
    初始化 Google Cloud SDK 是将 gcloud 命令和 Google 账号绑定起来并设置一些其他的默认值,比如区域,代理,账号,项目(Google 账号中新建的项目)之类的。在执行 gcloud init 初始化之前得先给 gcloud 配置 HTTP 代理(GFW 你懂得),具体配置见我之前这篇文章。然后执行 gcloud init 完成初始化,直接根据向导来即可。
  3. 到 Google Cloud Platform 控制台建一个 k8s 集群,记住名称
  4. 安装 gcloud kubectl 组件
    gcloud components install kubectl
  5. 获取群集的身份验证凭据
    创建群集后,您需要获取身份验证凭据以与群集进行交互。要为集群进行身份验证,请运行以下命令:
    gcloud container clusters get-credentials <上一步创建的集群名称>
  6. 接下来部署一个简单的 hello-server 服务到 GKE
    kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080

相关链接

https://cloud.google.com/kubernetes-engine/docs/quickstart
https://cloud.google.com/sdk/docs/quickstart-macos?hl=zh-cn

附录

gloud 常用命令

1
2
3
4
5
6
gcloud auth login --no-launch-browser # gcloud 登录认证
gcloud config set compute/zone [COMPUTE_ZONE] # 设置默认区域
gcloud components list # 列出可安装组件
gcloud components install [组件名称] # 安装组件
gcloud components update # 更新所有已安装组件
gcloud components remove [组件名称] # 卸载已安装组件

设置 gcloud http 代理

1
2
3
gcloud config set proxy/type http
gcloud config set proxy/address 127.0.0.1
gcloud config set proxy/port 1087

设置集群 docker 私服认证:

1
kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>

注意:设置 docker 私服后,要在 GKE 部署 k8s 服务,必须得在 k8s 资源文件(yaml 格式)中的 container
同一级指定 imagePullSecrets 键,要不然仍然无法拉取配置的私服的镜像,示例文件如下:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: private-reg
spec:
containers:
- name: private-reg-container
image: <your-private-image>
imagePullSecrets:
- name: regcred

查看集群 docker 私服配置:

1
2
kubectl get secret regcred --output=yaml      #base64 格式 显示
kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 -d # base64 解密后内容

配置 gcloud 使用 Shadowsocks HTTP 代理

发表于 2018-04-14 | 分类于 Google Cloud Platform

配置 gcloud 使用 Shadowsocks HTTP 代理

1. 问题描述

最近在使用 gcloud 访问 gcp(Google Cloud Platform) 的资源,但是由于 GFW 的原因必须得配个 HTTP 代理才能访问。虽然之前装个 Shadowsock-X 可以突破 GFW 用浏览器访问 Google,但是 Shadowsock-X 默认只开启 SOCKS 代理,并没有提供 HTTP 代理。为了让 shadowsocks 开启 HTTP 代理,必须得想一些办法了,要不然没法工作了。。。

2. 解决问题

经过一番 google,取而代之的是使用 Shadowsocks-NG 客户端,其实就是 Shadowsocks-X 的升级版,有了更多丰富的功能,最主要的是该客户端启动后默认就开启了 HTTP 代理,可以直接供 gcloud 等命令行工具使用。具体配置 gcloud 使用 Shadowsocks-NG http 代理的方法如下:

  1. 点击如下链接下载并安装 Shadowsocks-NG:
    https://github.com/shadowsocks/ShadowsocksX-NG/releases
  2. 启动 Shadowsocks-NG,填入 shadowsocks 服务端 ip,端口,加密方式等信息
    注意:不能勾选启用 OTA(被启用)复选框

  3. 获取 HTTP 代理的 IP 和端口
    点击偏好设置查看 HTTP 代理 IP 及端口:

  4. 设置 gcloud HTTP 代理
    使用如下命令设置上一步获取的 Shadowsocks-NG HTTP 代理:

    1
    2
    3
    gcloud config set proxy/type http
    gcloud config set proxy/address 127.0.0.1
    gcloud config set proxy/port 1087
  5. 接下来就可以畅通无阻地访问 google 云平台资源了

3. 相关链接

https://www.stefanwienert.de/blog/2018/01/21/shadowsocks-quick-guide-for-restricted-internet-environments/

hexo-next-主题配置gitalk评论-爬坑记

发表于 2018-03-25 | 分类于 Hexo

摘要

本文主要记录了我在配置 Hexo 博客 gitalk 评论功能时踩过的坑到最终爬出坑的过程,本教程献给打算给 Hexo 博客配置 gitalk 评论的小白朋友们(当然我也是 QAQ),避免再次踩同样的坑。本教程比较特殊,网上很多关于 gitalk 的教程,我就懒的再写一遍了,所以就将我在配置过程中提的 issue #115 直接导出成 pdf 并嵌入文章,跟着该 issue #115 做一遍即可完成 Hexo Next 主题 gitalk 评论功能的配置,教程如下:

致谢

感谢 Github 社区广大极客们的帮助,特别感谢 @iochen 这位大哥的帮助!

Python 两大环境管理神器:pyenv 和 virtualenv

发表于 2018-03-24 | 分类于 Python

简介


pyenv 是一个开源的 Python 版本管理工具,可以轻松地给系统安装任意 Python 版本,想玩哪个版本,瞬间就可以切换。有了 pyenv,我们不需要再为系统多版本 Python 共存问题而发愁,也不用为手动编译安装其他 Python 版本而浪费时间,只需要执行一条简单的命令就可以切换并使用任何其他版本,该工具真正地做到了开箱即用,简单实用。

virtualenv 是一个用来创建完全隔离的 Python 虚拟环境的工具,可以为每个项目工程创建一套独立的 Python 环境,从而可以解决不同工程对 Python 包,或者版本的依赖问题。假如有 A 和 B 两个工程,A 工程代码要跑起来需要 requests 1.18.4,而 B 工程跑起来需要 requests 2.18.4,这样在一个系统中就无法满足两个工程同时运行问题了。最好的解决办法是用 virtualenv 给每个工程创建一个完全隔离的 Python 虚拟环境,给每个虚拟环境安装相应版本的包,让程序使用对应的虚拟环境运行即可。这样既不影响系统 Python 环境,也能保证任何版本的 Python 程序可以在同一系统中运行。

最佳实践:使用 pyenv 安装任何版本的 Python,然后用 virtualenv 创建虚拟环境时指定需要的 Python 版本路径,这样就可以创建任何版本的虚拟环境,这样的实践真是极好的!

pyenv 的安装及使用


1. 安装

  • 将 pyenv 安装到 ~/.pyenv 目录(当然你可以安装到任意其他路径)

    1
    git clone https://github.com/yyuu/pyenv.git ~/.pyenv
  • 配置环境变量(我的 Shell 是 zsh,如果是 bash,请添加到 ~/.bashrc)

    1
    2
    echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
    echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
  • 添加 pyenv 初始化(我的 Shell 是 zsh,如果是 bash,请添加到 ~/.bashrc)

    1
    echo 'eval "$(pyenv init -)"' >> ~/.zshrc
  • 使当前 Shell 配置生效,完成安装

    1
    2
    exec $SHELL
    source ~/.zshrc

2. 使用

  • 查看有哪些 Python 版本可以安装

    1
    pyenv install --list
  • 安装某个 Python 版本

    1
    pyenv install -v 3.6.4
  • 查看当前 Python 版本情况(* 表示系统当前的 Python 版本,system表示系统初始版本)

    1
    2
    3
    4
    $ pyenv versions
    system
    2.6.7
    * 3.6.4 (set by /Users/haohao/.pyenv/version)
  • 切换 Python 版本(切换之后查看当前版本)

    1
    2
    3
    4
    5
    6
    $ pyenv global 3.6.4
    $ pyenv versions
    system
    * 3.6.4 (set by /Users/haohao/.pyenv/version)
    $ python -V
    Python 3.6.4
  • 卸载某个 Python 版本

    1
    pyenv uninstall 3.6.4

virtualenv 的安装及使用


1. 安装

1
sudo pip install virtualenv

2. 使用

下面我们使用 virtualenv 创建一个完全隔离的 Python 虚拟环境:

  1. 新建一个目录(一般用来用作工程路径)

    1
    $ mkdir myproject
  2. 进入目录创建一个完全独立干净的虚拟环境
    如果 virtualenv 后面不加任何参数,那么默认创建的虚拟环境的 Python 版本是系统当前版本,如果要创建其他版本,可以使用 -p 参数指定其他版本的 python 可执行文件路径。可执行文件可以在上一步安装的 pyenv 的 ~/.pyenv/versions 路径找到,该路径是 pyenv 管理的所有 Python 版本路径。

    1
    2
    3
    4
    5
    6
    7
    8
    # 使用系统当前的 Python 版本创建虚拟环境
    $ virtualenv venv
    New python executable in /Users/haohao/PycharmProjects/myproject/venv/bin/python
    # 创建虚拟环境时指定 Python 版本
    $ virtualenv -p ~/.pyenv/versions/2.6.7/bin/python venv
    Running virtualenv with interpreter /Users/haohao/.pyenv/versions/2.6.7/bin/python
    New python executable in /Users/haohao/PycharmProjects/myproject/venv/bin/python
    Installing setuptools<37, pip, wheel<0.30...done.
  3. 激活创建的虚拟环境并使用
    可以看出当前虚拟环境版本已经是 Python 2.6.7 了,而且所在路径确实是在上一步创建的虚拟环境路径。接下来使用 pip 安装的任何包都会安装在虚拟环境目录里面,不会安装在系统标准目录,从而保证当前环境是绝对干净的,对于系统是完全隔离的。

    1
    2
    3
    4
    5
    $ source venv/bin/activate
    $ which python
    /Users/haohao/PycharmProjects/myproject/venv/bin/python
    $ python -V
    Python 2.6.7
  4. 退出虚拟环境,回到系统版本

    1
    $ deactivate

Aws Ec2 实例外挂 EBS 卷详细步骤

发表于 2018-03-19 | 分类于 AWS
  1. 附加新建的卷到 ec2 实例(这一步在 aws 控制台进行);
  2. 对附加的卷分区(在这里分一个区):
    fdisk 设备名 (ex: /dev/xvdb)
    m
    n
    p
    w
  3. 给分区初始化文件系统(在这里写入 ext4 文件系统):
    mkfs.ext4 分区标识(ex: /dev/xvdb1)
    举例:mkfs.ext4 /dev/xvdb1
  4. 执行 mount 命令挂载分区到指定目录:
    mount 分区标识(ex: /dev/xvdb1) 挂载目录
    举例:mount /dev/xvdb1 /data
  5. 设置开机自动挂载:
    vim /etc/fstab # 文件追加如下一行内容
    分区标识(ex:/dev/xvdb1)挂载目录 文件系统类型 defaults 1 2
    举例:/dev/xvdb1 /data ext4 defaults 1 2
1…91011…14

haohao

Talk is cheap. Show me the code.

134 日志
35 分类
43 标签
GitHub CSDN 开源中国 E-Mail
© 2017 — 2021 haohao
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.3
访问人数 总访问量 次