扇贝单词的一些缺点

最近一段时间,我成为了扇贝单词的重度用户。过去的60天,我给扇贝提了20条建议。

但,客观来讲扇贝还是有很多缺点,所以记在这儿,供各位参考。

  1. Web和app功能不一致
    1. 比如app有听词模式,web没有
    2. 比如web可以编辑释义,app不能
    3. 比如web可以添加例句,app不能
  2. Web的释义和app释义不一致,官方回复是app是新版,而web正在改版
  3. 释义不准确,比如有的缺少释义、有的没有释义(我很好奇,没有释义的单词是怎么录入的?)
  4. 单词读音展示问题
    1. 有的单词是美音和英音读音不一致,然后我点击美音,它把两个读音都读出来了……
    2. 有的单词是,不同读音对应不同含义,扇贝根本没有标记出来读音和含义的对应关系
  5. 网页背单词,在Firefox上,快捷键没法使用

我当前在背考研单词。等当前词本结束之后,我会尝试下其他背单词软件的。

Firefox不应该管理DNS(和证书库)

Firefox已经越俎代庖,开始管理证书了,而且还想染指DNS。

最近Firefox在nightly版本中添加了DNS overt HTTPS的支持,第一眼就觉得这个事情不是系统来做吗?比如Android支持DNS overt HTTPS才是比较合理的事情嘛。

看了Reddit上面的评论,发现其他人也有类似的感觉,不止一个人觉得这事应该由系统来做。

另外,这也可能会导致hosts文件配置失效;而在web开发中,修改hosts文件是常用的操作。

而且,Firefox中没有清除DNS缓存的地方,所以很多时候,对于hosts的修改,不能立即在Firefox中看到结果。

总而言之,让浏览器(比如Firefox和Chrome)来管理DNS,不是一个好主意。

说到这儿,Firefox之前也做过类似的事情:

Firefox不知道什么时候,开始自己搞了一套独立的证书信任系统,和系统内置的证书系统是独立的(至少在Android上是这样的)。

比如,我要使用charles抓https的包,需要将charles的tls证书倒入到Andorid系统的证书库中,但是回头一看,Firefox还是不认,所以还得自己将证书添加到Firefox的证书库中。

另外一个更加智障的问题是,我在charles抓包结束后,需要删除这个证书,我可以在Android系统的证书库中删除掉。但是对于添加到Firefox中的那一份,我是没有办法删除的,只能卸载并重新安装Firefox

Firefox想取代系统的证书库,但是这个功能都没有做到合格就上线了。

Continue reading

cherry-pick到底应该怎么翻译?

git当中的cherry-pick命令,翻译成中文叫什么名字呢?

GitLab给出了答案

// gitlab.po
msgid "ChangeTypeAction|Cherry-pick"
msgstr "优选"

这个是不是听起来怪怪的?

cherry-pick的中文意思是“择优挑选”,比如

They seem to cherry-pick the loss leaders and discount items in the supermarkets and discount stores, relying on smaller retailers for everything else.

因为他们会选择超市和打折店里的特价商品和和打折商品,而其他所有的东西都是从小商贩那里获得。

当然,也要注意的是,写作论文的时候,只挑选那些好的数据写在文章中,这种行为也可以叫cherry-pick,比如:

Be careful not to ‘cherry-pick‘ data: don’t choose just what you like, or what supports your objective.
注意别“优选”数据:别选择一些只是您喜欢的,或者支持您的目标的。

所以从常用语义上来讲,优选这个翻译也不算太差。

但由于“优选”这个词在中国被用烂了,多用在电商等领域,如果我们开发的时候来一句“请将aaa提交优选到B分支”,完全不能理解啊。

个人觉得,翻译成“拾取”会比较好,比如“请将aaa提交拾取到B分支”就顺口好多。


另外,也可以看看GitLab对其他git术语的翻译:

Revert 还原,Issue 议题等。

PHP/Composer是如何加载一个类的

PHP/composer开发中,我们只需要require ‘vendor/autoload.php’,然后就可以直接使用各种类了。那么这些类是如何加载的呢?其中有没有什么可以优化的点呢?

概览

PHP/composer下,类的加载主要到如下部分(还没有包括各个部分的初始化逻辑):

PHP中zend_lookup_class_ex
    |-> EG(class_table)
    |-> spl_autoload_call
        |-> Composer\Autoload\ClassLoader::loadClass
            |-> findFile
                |-> class map lookup
                |-> PSR-4 lookup
                |-> PSR-0 lookup

PHP的类加载

首先,PHP在运行的时候,需要一个类,是通过zend_lookup_class_ex来找到这个类的相关信息的。

zend_lookup_class_ex查找类的主要逻辑如下(假设类名字放到变量lc_name中):

ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, const zval *key, int use_autoload) /* {{{ */
{
  // 1. 类名字转化为小写
    if (ZSTR_VAL(name)[0] == '\\') {
      lc_name = zend_string_alloc(ZSTR_LEN(name) - 1, 0);
      zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1);
    } else {
      lc_name = zend_string_tolower(name);
    }

  // 2. 直接在class_table中查找
  ce = zend_hash_find_ptr(EG(class_table), lc_name);
  if (ce) {
    if (!key) {
      zend_string_release(lc_name);
    }
    return ce;
  }
  // 3. 如果没有autoload_func,则注册默认的__autoload
  if (!EG(autoload_func)) {
    zend_function *func = zend_hash_str_find_ptr(EG(function_table), ZEND_AUTOLOAD_FUNC_NAME, sizeof(ZEND_AUTOLOAD_FUNC_NAME) - 1);
    if (func) {
      EG(autoload_func) = func;
    } else {
      if (!key) {
        zend_string_release(lc_name);
      }
      return NULL;
    }

  }

  // 4. 加载ACLASS的过程中,又加载ACLASS,递归加载,直接找不到类
  if (zend_hash_add_empty_element(EG(in_autoload), lc_name) == NULL) {
    if (!key) {
      zend_string_release(lc_name);
    }
    return NULL;
  }

  // 5. 调用autoload_func
  ZVAL_STR_COPY(&fcall_info.function_name, EG(autoload_func)->common.function_name);
  fcall_info.symbol_table = NULL;

  zend_exception_save();
  if ((zend_call_function(&fcall_info, &fcall_cache) == SUCCESS) && !EG(exception)) {
    ce = zend_hash_find_ptr(EG(class_table), lc_name);
  }
  zend_exception_restore();

  if (!key) {
    zend_string_release(lc_name);
  }
  return ce;
}
  1. lc_name转化成小写(这说明PHP中类名字不区分大小写)
  2. 然后在EG(class_table)找,如果找到,直接返回(我们自己注册的类,扩展注册的类都是这样找到的)
  3. 然后查看EG(autoload_func) ,如果没有则将__autoload注册上(值得注意的是,如果注册了EG(autoload_func),则不会走__autoload)
  4. 通过EG(in_autoload)判断是否递归加载了(EG(in_autoload)是一个栈,记载了那些类正在被autoload加载)
  5. 然后调用EG(autoload_func),并返回类信息

SPL扩展注册

刚刚可以看到,PHP只会调用EG(autoload_func),根本没有什么SPL的事情,那么SPL是如何让PHP调用自己的类加机制的呢?

首先,我去找SPL扩展的MINIT过程,结果发现其中并没有相关的逻辑。

出乎我的意料,这个注册过程在spl_autoload_register中完成:

PHP_FUNCTION(spl_autoload_register)
{
    // 已经将SPL注册到PHP了,且当前用户要注册到spl的autoload函数已经注册,则跳过
    if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), lc_name)) {
      if (!Z_ISUNDEF(alfi.closure)) {
        Z_DELREF_P(&alfi.closure);
      }
      goto skip;
    }

    // 如果必要的话,初始化SPL_G(autoload_functions)
    if (!SPL_G(autoload_functions)) {
      ALLOC_HASHTABLE(SPL_G(autoload_functions));
      zend_hash_init(SPL_G(autoload_functions), 1, NULL, autoload_func_info_dtor, 0);
    }

    // 如果之前已经注册了spl_autoload,那就将spl_autoload转移到autoload_functions中
    spl_func_ptr = zend_hash_str_find_ptr(EG(function_table), "spl_autoload", sizeof("spl_autoload") - 1);
    if (EG(autoload_func) == spl_func_ptr) { /* registered already, so we insert that first */
      autoload_func_info spl_alfi;

      spl_alfi.func_ptr = spl_func_ptr;
      ZVAL_UNDEF(&spl_alfi.obj);
      ZVAL_UNDEF(&spl_alfi.closure);
      spl_alfi.ce = NULL;
      zend_hash_str_add_mem(SPL_G(autoload_functions), "spl_autoload", sizeof("spl_autoload") - 1,
          &spl_alfi, sizeof(autoload_func_info));
      if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
        /* Move the newly created element to the head of the hashtable */
        HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
      }
    }

    // 将用户要注册的函数,即lc_name,放到autoload_functions中
    if (zend_hash_add_mem(SPL_G(autoload_functions), lc_name, &alfi, sizeof(autoload_func_info)) == NULL) {
      if (obj_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) {
        Z_DELREF(alfi.obj);
      }
      if (!Z_ISUNDEF(alfi.closure)) {
        Z_DELREF(alfi.closure);
      }
      if (UNEXPECTED(alfi.func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
        zend_string_release(alfi.func_ptr->common.function_name);
        zend_free_trampoline(alfi.func_ptr);
      }
    }
    if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
      /* Move the newly created element to the head of the hashtable */
      HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
    }
skip:
    zend_string_release(lc_name);
  }

  // 根据autoload_functions的值,决定向PHP注册spl_autoload_call还是spl_autoload
  if (SPL_G(autoload_functions)) {
    EG(autoload_func) = zend_hash_str_find_ptr(EG(function_table), "spl_autoload_call", sizeof("spl_autoload_call") - 1);
  } else {
    EG(autoload_func) =	zend_hash_str_find_ptr(EG(function_table), "spl_autoload", sizeof("spl_autoload") - 1);
  }

  RETURN_TRUE;
}

在composer环境下,这个函数的功能就是,将用户的autoload函数放到SPL_G(autoload_functions)中,且将spl_autoload_call注册到PHP中。

这样,PHP在找一个类的时候,就会调用spl_autoload_call了。

spl_autoload_call逻辑

spl_autoload_call的逻辑很简单:

PHP_FUNCTION(spl_autoload_call)
{
  if (SPL_G(autoload_functions)) {
    HashPosition pos;
    zend_ulong num_idx;
    int l_autoload_running = SPL_G(autoload_running);
    SPL_G(autoload_running) = 1;
    lc_name = zend_string_alloc(Z_STRLEN_P(class_name), 0);
    zend_str_tolower_copy(ZSTR_VAL(lc_name), Z_STRVAL_P(class_name), Z_STRLEN_P(class_name));
    zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &pos);
    // 遍历之前注册的autoload_functions
    while (zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &num_idx, &pos) == HASH_KEY_IS_STRING) {
      alfi = zend_hash_get_current_data_ptr_ex(SPL_G(autoload_functions), &pos);
      if (UNEXPECTED(alfi->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
        zend_function *copy = emalloc(sizeof(zend_op_array));

        memcpy(copy, alfi->func_ptr, sizeof(zend_op_array));
        copy->op_array.function_name = zend_string_copy(alfi->func_ptr->op_array.function_name);
        // 调用autoload_function
        zend_call_method(Z_ISUNDEF(alfi->obj)? NULL : &alfi->obj, alfi->ce, &copy, ZSTR_VAL(func_name), ZSTR_LEN(func_name), retval, 1, class_name, NULL);
      } else {
        zend_call_method(Z_ISUNDEF(alfi->obj)? NULL : &alfi->obj, alfi->ce, &alfi->func_ptr, ZSTR_VAL(func_name), ZSTR_LEN(func_name), retval, 1, class_name, NULL);
      }
      zend_exception_save();
      if (retval) {
        zval_ptr_dtor(retval);
        retval = NULL;
      }
      // 如果调用结束之后,能在class_table找到类,则返回
      if (zend_hash_exists(EG(class_table), lc_name)) {
        break;
      }
      zend_hash_move_forward_ex(SPL_G(autoload_functions), &pos);
    }
    zend_exception_restore();
    zend_string_free(lc_name);
    SPL_G(autoload_running) = l_autoload_running;
  } else {
    /* do not use or overwrite &EG(autoload_func) here */
    zend_call_method_with_1_params(NULL, NULL, NULL, "spl_autoload", NULL, class_name);
  }
}
  1. 判断SPL_G(autoload_functions)存在
  2. 依次调用autoload_functions
  3. 如果调用完成后,这个类存在了,那就返回

至此,SPL的部分已经讲完了。我们来看看composer做了什么。

composer注册autoload

composer的autoload注册在 ‘vendor/autoload.php’ 中完成,这个文件完成了两件事:

  1. include vendor/composer/autoload_real.php
  2. 调用ComposerAutoloaderInit<rand_id>::getLoader()

vendor/composer/autoload_real.php仅仅定义了ComposerAutoloaderInit<rand_id>类和composerRequire<rand_id>函数。

<rand_id>是类似id一样的东西,确保要加载多个composer的autoload的时候不会冲突。composerRequire<rand_id>则是为了避免ComposerAutoloader require文件的时候,文件修改了ComposerAutoloader的东西。

接下来我们关注下ComposerAutoloaderInit<rand_id>::getLoader()做了哪些事情。

这个类的loader只会初始化一次,第二次是直接返回已经存在的loader了:

if (null !== self::$loader) {
    return self::$loader;
}

如果是第一次调用,先注册['ComposerAutoloaderInit<rand_id>', 'loadClassLoader'],然后new一个\Composer\Autoload\ClassLoader 作为$loader,然后立马取消注册loadClassLoader

也就是说['ComposerAutoloaderInit<rand_id>', 'loadClassLoader']的唯一作用就是加载\Composer\Autoload\ClassLoader

接下来就是在ComposerAutoloaderInit<rand_id>::getLoader()初始刚刚拿到的$loader了:

// autoload_namespaces.php里面放的是PSR-0
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
    $loader->set($namespace, $path);
}
// autoload_psr4.php里面放的是PSR-4注册的
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
    $loader->setPsr4($namespace, $path);
}
// autoload_classmap.php放的是classmap注册的
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
    $loader->addClassMap($classMap);
}
// ……
// 将[$loader, 'loadClass']注册到spl中
$loader->register(true);
// ……
// autoload_files.php是file声明的autoload
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $fileIdentifier => $file) {
    composerRequire32715bcfade9cdfcb6edf37194a34c36($fileIdentifier, $file);
}
return $loader;
  1. autoload_namespaces.php返回的是各个包里面声明的PSR-0加载规则,是一个数组。key为namespace,有可能为空字符串;value为路径的数组。
  2. $loader->set,如果$namespace/$prefix为空,直接放到$loader->fallbackDirsPsr0数组中。如果不为空,则放到$loader->prefixesPsr0[$prefix[0]][$prefix]中(这可能是为了减少PHP内部的hash表冲突,加快查找速度)。
  3. autoload_psr4.php返回的是各个包里面声明的PSR-4加载规则,是一个数组。key为namespace,有可能为空字符串;value为路径的数组。
  4. $loader->setPsr4,如果$namespace/$prefix为空,直接放到$loader->fallbackDirsPsr4数组中。如果不为空,则将$namespace/$prefix的长度放到$loader->prefixLengthsPsr4[$prefix[0]][$prefix]中,将路径放到$loader->prefixDirsPsr4[$prefix]中。
  5. autoload_classmap.php返回的是各个包里面声明的classmap加载规则,是一个数组。key为class全名,value为文件路径。(这个信息是composer扫描全部文件得到的)
  6. $loader->addClassMap,则将这些信息array_merge到$loader->classMap中。
  7. autoload_files.php返回的是各个包里面声明的file加载规则,是一个数组。key为每个文件的id/hash,value是每个文件的路径。
  8. 注意,autoload_files.php里面的文件,在getLoader中就已经被include了。

到这儿,我们的$loader已经初始化好了,而且也已经注册到SPL中了

composer加载类

我们之前是将[$loader, ‘loadClass’]注册到了SPL中,那就看看它的逻辑吧:

public function loadClass($class)
{
    if ($file = $this->findFile($class)) {
        includeFile($file);
        // 根据我们刚刚的分析,此处返回值是根本没有用
        return true;
    }
}

所以看下来,重点在findFile函数里面:

public function findFile($class)
{
    // 通过classmap找这个类
    if (isset($this->classMap[$class])) {
        return $this->classMap[$class];
    }
    // 这里涉及到一个composer的性能优化:
    // https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-a-authoritative-class-maps
    if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
        return false;
    }
    // 这里同样也涉及到性能优化:
    // https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-b-apcu-cache
    if (null !== $this->apcuPrefix) {
        $file = apcu_fetch($this->apcuPrefix.$class, $hit);
        if ($hit) {
            return $file;
        }
    }
    // 这个函数处理了PSR-0和PSR-4的加载规则
    $file = $this->findFileWithExtension($class, '.php');

    // ……
    return $file;
}

如果是classmap的加载规则,那就会在这儿加载成功。如果是PSR-0或者PSR-4,则需要看看findFileWithExtension的逻辑了:

private function findFileWithExtension($class, $ext)
{
    // PSR-4 lookup
    $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

    // $prefix不为空的PSR-4加载规则
    $first = $class[0];
    if (isset($this->prefixLengthsPsr4[$first])) {
        $subPath = $class;
        while (false !== $lastPos = strrpos($subPath, '\\')) {
            $subPath = substr($subPath, 0, $lastPos);
            $search = $subPath.'\\';
            if (isset($this->prefixDirsPsr4[$search])) {
                $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
                foreach ($this->prefixDirsPsr4[$search] as $dir) {
                    if (file_exists($file = $dir . $pathEnd)) {
                        return $file;
                    }
                }
            }
        }
    }

    // $prefix为空的PSR-4加载规则
    foreach ($this->fallbackDirsPsr4 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
            return $file;
        }
    }

    // PSR-0 lookup
    if (false !== $pos = strrpos($class, '\\')) {
        // namespaced class name
        $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
            . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
    } else {
        // PEAR-like class name
        $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
    }

    // $prefix不为空的PSR-0加载规则
    if (isset($this->prefixesPsr0[$first])) {
        foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
            if (0 === strpos($class, $prefix)) {
                foreach ($dirs as $dir) {
                    if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
                        return $file;
                    }
                }
            }
        }
    }

    // $prefix为空的PSR-0加载规则
    foreach ($this->fallbackDirsPsr0 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
            return $file;
        }
    }

    // 从include path中找文件
    if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
        return $file;
    }

    return false;
}
  1. $prefix不为空的PSR-4加载规则:
    1. 比如类A\B\C,先找A\B\对应目录下面的C.php;再找A\对应目录下面的B\C.php;以此类推
  2. $prefix为空的PSR-4加载规则
    1. 如果找不到,那就在fallbackDirsPsr4下找A\B\C.php文件
  3. $prefix不为空的PSR-0加载规则
    1. PSR-0支持namespace和下划线分隔的类(PEAR-like class name);这点对一些需要向namespace迁移的旧仓库很有用
    2. 对于类A\B\C或者A_B_C,先找A\B\对应目录下面的C.php;再找A\对应目录下面的B\C.php;以此类推
  4. $prefix为空的PSR-0加载规则
    1. 如果找不到,直接在prefixesPsr0中找A\B\C.php文件
  5. 如果还没有找到,在条件允许的状态下,可以到include path中找A\B\C.php文件

这样,composer就找到了这个类对应的文件,并且include了。

Docker for Mac with Kubernetes初次尝试

首先,2018-01-09日Docker公司宣布了Docker for Mac支持Kubernetes

后来陆续尝试了几次,今天终于成功了,所以记录下。

安装Docker for Mac with Kubernetes

首先,安装Docker for Mac Edge版本:

brew cask install docker-edge

设置代理(我用的是https://github.com/netheril96/MEOW):

开启Kubernetes:

 

等待安装:

OK了:

kubectl version也能看到客户端版本和服务端版本了:

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.2", GitCommit:"5fa2db2bd46ac79e5e00a4e6ed24191080aa463b", GitTreeState:"clean", BuildDate:"2018-01-18T10:09:24Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.2", GitCommit:"5fa2db2bd46ac79e5e00a4e6ed24191080aa463b", GitTreeState:"clean", BuildDate:"2018-01-18T09:42:01Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}

创建kubernetes-dashboard 服务

$ kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
secret "kubernetes-dashboard-certs" created
serviceaccount "kubernetes-dashboard" created
role "kubernetes-dashboard-minimal" created
rolebinding "kubernetes-dashboard-minimal" created
deployment "kubernetes-dashboard" created
service "kubernetes-dashboard" created

等一会就可以看到创建好了:

$ kubectl get pods --namespace kube-system
NAME READY STATUS RESTARTS AGE
etcd-docker-for-desktop 1/1 Running 0 6m
kube-apiserver-docker-for-desktop 1/1 Running 2 6m
kube-controller-manager-docker-for-desktop 1/1 Running 0 6m
kube-dns-6f4fd4bdf-zl9dh 3/3 Running 0 7m
kube-proxy-xsx8n 1/1 Running 0 7m
kube-scheduler-docker-for-desktop 1/1 Running 0 5m
kubernetes-dashboard-5bd6f767c7-6szsl 1/1 Running 0 1m
$ kubectl get deployments --namespace kube-system
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kube-dns 1 1 1 1 7m
kubernetes-dashboard 1 1 1 1 1m

先运行kubectl proxy:

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

然后访问:

http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/

即可看到dashboard:

来一个Wordpress的例子吧

clone仓库:

git clone https://github.com/kubernetes/examples.git
cd examples/mysql-wordpress-pd/

然后基本上按照官方教程来就好了

创建MySQL密码

创建密码

$ kubectl create secret generic mysql-pass --from-literal=password=YOUR_PASSWORD
secret "mysql-pass" created

使用kubectl get secrets 可以看到有一个名字为mysql-pass 的secret

$ kubectl get secrets
NAME TYPE DATA AGE
default-token-z7lmq kubernetes.io/service-account-token 3 15m
mysql-pass Opaque 1 3s

部署MySQL

开始部署MySQL服务:

$ kubectl create -f mysql-deployment.yaml
service "wordpress-mysql" created
persistentvolumeclaim "mysql-pv-claim" created
deployment "wordpress-mysql" created

看下pvc创建好了没有:

$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pv-claim Bound pvc-32bcbb1a-1143-11e8-a423-025000000001 20Gi RWO hostpath 1m

看下MySQL的pod创建好了没有:

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
wordpress-mysql-58cf8dc9f9-5grjs 1/1 Running 0 1m

部署Wordpress

$ kubectl create -f wordpress-deployment.yaml
service "wordpress" created
persistentvolumeclaim "wp-pv-claim" created
deployment "wordpress" created

检查下pvc和service的状态:

$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pv-claim Bound pvc-32bcbb1a-1143-11e8-a423-025000000001 20Gi RWO hostpath 3m
wp-pv-claim Bound pvc-a5f97603-1143-11e8-a423-025000000001 20Gi RWO hostpath 11s
$ kubectl get services wordpress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
wordpress LoadBalancer 10.96.2.58 <pending> 80:30734/TCP 32s

此处EXTERNAL-IP为<pending>,这个我也不清楚为何。

等一会之后,访问http://127.0.0.1/ ,就可以看到熟悉的WordPress安装界面了:

清理Wordpress

kubectl delete secret mysql-pass
kubectl delete deployment -l app=wordpress
kubectl delete service -l app=wordpress
kubectl delete pvc -l app=wordpress

 

如何在macOS上运行GTK 3(hello, world)

首先,安装 Homebrew

然后,brew install gtk+3

随后,查阅GTK的官方文档,写Hello, world:

// hello.c
#include <gtk/gtk.h>

static void
activate (GtkApplication* app,
          gpointer        user_data)
{
  GtkWidget *window;

  window = gtk_application_window_new (app);
  gtk_window_set_title (GTK_WINDOW (window), "Window");
  gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
  gtk_widget_show_all (window);
}

int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}

然后编译:

gcc `pkg-config --cflags gtk+-3.0` -o hello hello.c `pkg-config --libs gtk+-3.0`

当前目录下就有了hello二进制可执行文件,执行即可:

Use pty as ansible connection

Well, sometimes, we can only use pty to access machines.

For example, Jump Server only allowed use ssh pty to type command, get the result.

But, how can I deploy application via ansible?


There are two good news:

  1. pexpect can interactive with the ssh session.
  2. ansible can use custom connection plugin.

Let’s try it

Let’s start at directory ansible-pty

Firstly, we should create a connection plugin.

Put those code to ansible-pty/plugins/pty.:

import base64
import re

import pexpect
from ansible.plugins.connection import ConnectionBase
from ansible.utils.display import Display

display = Display()


class Connection(ConnectionBase):
    transport = 'pty'
    _mark = "=" * 10

    def __init__(self, *args, **kwargs):
        super(Connection, self).__init__(*args, **kwargs)

        self.host = self._play_context.remote_addr
        self.user = self._play_context.remote_user

    #
    # Main public methods
    #
    def exec_command(self, cmd, in_data=None, sudoable=True):
        ''' run a command on the remote host '''

        super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)

        display.vvv(u"ESTABLISH SSH CONNECTION FOR USER: {0}".format(self._play_context.remote_user),
                    host=self._play_context.remote_addr)

        self._process.write(cmd)
        self._process.write(" ; echo $? && echo ")
        self._process.write(self._mark)
        self._process.write('\n')
        self._process.readline()

        lines = b''
        while True:
            line = self._process.readline()
            if line.strip() == self._mark.encode():
                break
            lines += line
        regex_pattern = r"((?P<output>(?:.|\n)*)(?:\r|\n)+)?(?P<retcode>\d+)(?:\r|\n)+"
        matches = re.match(regex_pattern, lines.decode(), re.MULTILINE)

        stdout = matches.group('output')
        if not stdout:
            stdout = ''
        returncode = matches.group('retcode')
        returncode = int(returncode)
        stderr = ''

        self._eat_prompt(self._process)

        return returncode, stdout, stderr

    def put_file(self, in_path, out_path):
        ''' transfer a file from local to remote '''

        super(Connection, self).put_file(in_path, out_path)

        display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self.host)
        self._process.write("base64 -d > " + out_path)
        self._process.write(' && printf "' + self._mark + '\n"')
        self._process.write('\n')
        with open(in_path, 'rb') as fd:
            while True:
                raw_content = fd.read(512)
                if len(raw_content) == 0:
                    break
                encoded_content = base64.b64encode(raw_content)
                self._process.write(encoded_content.decode())
                self._process.write('\n')
        self._process.write('\n')
        self._process.sendcontrol('d')

        self._process.readline()
        while True:
            line = self._process.readline()
            if line.strip() == self._mark.encode():
                break

        self._eat_prompt(self._process)

    def fetch_file(self, in_path, out_path):
        ''' fetch a file from remote to local '''

        raise NotImplemented()

    def _connect(self):
        process = pexpect.spawn('ssh {user}@{host}'.format(user=self.user, host=self.host))
        process.setwinsize(40, 160)

        self._eat_prompt(process)

        self._connected = True
        self._process = process

    def _eat_prompt(self, process):
        # after logged in
        # there is some prompt like `[[email protected] ~]# `
        process.expect('\~\](?:\#|\$) '.encode())

    def close(self):
        self._connected = False

And make ansible can load this plugin. Put code to ansible.cfg:

[defaults]
connection_plugins = connection_plugins:./plugins/

Try pty connection with inventory files:

# inventory
vultr ansible_connection=pty ansible_user=root

Run:

ansible -i inventory -m setup all

You can get the machine information:

vultr | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "45.76.53.225"
        ],
        "ansible_all_ipv6_addresses": [
            "2001:19f0:7001:1836:5400:ff:fe7c:400c",
            "fe80::5400:ff:fe7c:400c"
        ],
        "ansible_apparmor": {
            "status": "disabled"
        },
......

Other thought

  • Not implement fetch_file
  • And, some times the command output will confilct with _mark
  • Stderr is redriect to stdout

You also can upload, download file via pty.

So the jump server, and pty audit is almost useless. You can alway find way to hide what are you do.

[评论]systemd @ Facebook — a year later[All Systems Go! 2017]

今天看了All Systems Go! 2017上,systemd @ Facebook — a year later这个talk。记录下自己的感想。

首先,FaceBook软件更新还是比较及时的,CentOS 7 和 systemd 都上了:

可怜我司还在Ubuntu 14.04上

另外,很多基础组件都是和上游有良好的沟通的:

长远来看,紧跟上游对公司的技术实力、技术影响力都有好处。当然,短期来看,我的系统工作的很好,不跟进上游没毛病啊。国内,大部分是后者吧……

当然,大公司都有自己的repo:

FaceBook居然都已经接入了Meson,很让我吃惊。

资源管理

资源管理用的是cgroup2:

结合systemd中的slice:

在docker的潮流之下,FaceBook还是比较 old school style 的。不过,slice确实很够解决这个问题,后面可以看看

服务监控

systemd也提供了监控接口,通过dbus暴露出来:

一个daemon收集数据,上报:

使用Cython包装的sd-bus来获取数据,IPython写原型:

案例学习

一般都是直接关闭rpm里面重启的脚本,FaceBook还做的一个工具。

journalctl可以看单机日志,不知道分布式日志有没有用journald。

嗯,一个小坑

systemd的这个转义很反人类,但是他们并没有打算改!

FaceBook的建议

文档要贴近用户,积极参与上游,技术分享!

评论

Reddit上,很多人都不喜欢systemd这种经常改API,不考虑兼容性的做法。

当然,也有人讨论视频、文字交流的效率……


实际上,之前有一个系统需要部署。

我考察过很多daemonize的方式,从最经典的init.d,到中间的supervisord、upstrat,再到激进的systemd。

综合来看,systemd比较灵活,而且支持user-service,非常适合初期服务的部署。

但是,由于我司用的是Ubuntu 14.04,故只能切回到supervisord。服务的reload,对于supervisord是一个大问题。

个人而言,还是很期待systemd普及的。

另外,关于最后的建议。国内公司倒是经常tech talks,但是很多时候,并不会将自己的东西放开,也不会推向上游,这对长期的技术发展很不利。

如何退出无响应的 SSH 连接

经常出现ssh连接由于网络问题,“卡住”了。不能进行任何操作,也不能退出。

ssh文档里面也有写通过~.来断开ssh连接:

$ man ssh
...
ESCAPE CHARACTERS
     ...

     The supported escapes (assuming the default `~') are:

     ~.      Disconnect.

     ~?      Display a list of escape characters.

但是,实际使用过程中发现,按了没有反应。

后来看了其他人的做法,发现两个技巧:

  1. ssh转义字符,只有在新行生效。所以最保险的按键方式是:回车 -> Shift+` -> .
  2. 由于输入两个~会导致ssh直接发送~。比如我们按~~~. 会关闭第二层ssh,而不是当前失去响应的ssh会话。

所以为了保险起见,我的操作是这样子的:

回车~?,直到出现ssh的转义字符帮助(没有出现就继续按回车~?

然后按 ~.

退出ssh


~~~. 会关闭第二层ssh,对吗?

  1. ~~是给第一层ssh会话发送~字符
  2. ~~~是给第二层ssh会话发送~字符
  3. ~~~.是给第二层ssh会话发送~.字符,即关闭第二层ssh会话

参考资料:

如何调试iptables [CentOS 7]

首先,来一张非常有用的图:

基础知识

iptables可以jump到TRACE目标,开启包追踪(packet tracing)选项。

iptables有一个raw表,基本上是iptables最先处理包的地方。

 开始调试

在最入口的位置给符合条件的包开启包追踪,比如对所有进入的icmp包:

iptables -t raw -I PREROUTING -p icmp -j TRACE

有的时候,还需要加载需要的模块(CentOS 7下):

modprobe nf_log_ipv4

然后你就可以在/var/log/messages看到包追踪信息了。

关闭

删除掉TRACE规则即可:

iptables -t raw -D PREROUTING 1

这是一次对机器ping产生的trace信息:

 可以看到,firewalld强行加戏:)

Oct 21 15:34:50 vultr kernel: TRACE: raw:PREROUTING:policy:3 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PREROUTING:rule:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PREROUTING_direct:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PREROUTING:rule:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PREROUTING_ZONES_SOURCE:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PREROUTING:rule:3 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PREROUTING_ZONES:rule:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PRE_public:rule:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PRE_public_log:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PRE_public:rule:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PRE_public_deny:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PRE_public:rule:3 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PRE_public_allow:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PRE_public:return:4 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:PREROUTING:policy:4 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PREROUTING:rule:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PREROUTING_direct:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PREROUTING:rule:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PREROUTING_ZONES_SOURCE:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PREROUTING:rule:3 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PREROUTING_ZONES:rule:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PRE_public:rule:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PRE_public_log:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PRE_public:rule:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PRE_public_deny:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PRE_public:rule:3 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PRE_public_allow:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PRE_public:return:4 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PREROUTING:rule:4 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:DOCKER:return:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:PREROUTING:policy:5 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:INPUT:rule:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:INPUT_direct:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: mangle:INPUT:policy:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:INPUT:rule:3 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:INPUT_direct:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:INPUT:rule:4 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:INPUT_ZONES_SOURCE:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:INPUT:rule:5 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:INPUT_ZONES:rule:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:IN_public:rule:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:IN_public_log:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:IN_public:rule:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:IN_public_deny:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:IN_public:rule:3 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:IN_public_allow:return:13 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: filter:IN_public:rule:4 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: security:INPUT:rule:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: security:INPUT_direct:return:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: security:INPUT:policy:2 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0
Oct 21 15:34:50 vultr kernel: TRACE: nat:INPUT:policy:1 IN=eth0 OUT= MAC=56:00:00:7c:40:0c:fe:00:00:7c:40:0c:08:00 SRC=183.192.89.160 DST=1.2.53.225 LEN=84 TOS=0x04 PREC=0x00 TTL=43 ID=32583 PROTO=ICMP TYPE=8 CODE=0 ID=8765 SEQ=0

参考资料