container 里面没有 shell

今天本地搭建了一个 etcd 的服务器, 然后接下来想用 etcdctl 去测试一些命令, 于是想去用 container 里面的这个命令, 然后执行 docker exec -it etcd sh, 竟然得到没有这个文件, 然后又试了 bash 也没有.

首先 google 一把, 发现确实有人遇到同样的问题:
https://stackoverflow.com/questions/39900369/how-could-i-get-files-from-a-docker-container-running-the-official-etcd-image-if

按照上面的答案, 我们一个个去试一下:

docker export 导出整个container 文件

$ docker export etcd > /tmp/etcd.tar
$ ls -lah /tmp/etcd.tar
-rw-rw-r-- 1 supra supra 193M Aug 27 23:51 etcd.tar
$ mkdir /tmp/etcd
$ tar xvf /tmp/etcd.tar -C /tmp/etcd
$ ls -lah /tmp/etcd/bin/
total 8.0K
drwxr-xr-x  2 supra supra 4.0K Apr  2 04:55 .
drwxrwxr-x 16 supra supra 4.0K Aug 27 23:53 ..

$ find /tmp/etcd/ -name etcdctl
/tmp/etcd/usr/local/bin/etcdctl

可以看到, /bin 目录啥都没有. 但是我们找到了 etcdctl 在哪里.

docker cp 复制出 /bin 目录

$ mkdir /tmp/tmpbin
$ docker cp etcd:/bin /tmp/tmpbin/
$ ls -lah /tmp/tmpbin/bin/

可以看到内部啥也没有.

gremlin 操作 janusgraph 的基本语句

核心概念:

  1. Vertex, Edge, Property.
  2. Graph, Directed.
  3. 图数据库即是内存密集型也是CPU密集型.
  4. 图数据库做为其它数据库的一个补充.

建立本地连接

:remote connect tinkerpop.server conf/remote.yaml session
:remote console timeout none

打印 schema

mgmt = graph.openManagement()
mgmt.printSchema()
mgmt.printVertexLabels()
mgmt.printEdgeLabels()
mgmt.printPropertyKeys()
mgmt.printIndexes()

create schema & index

mgmt = graph.openManagement()
batch = mgmt.makeVertexLabel('batch').make()
date = mgmt.makePropertyKey('date').dataType(String.class).make()
hour = mgmt.makePropertyKey('hour').dataType(Integer.class).make()
mgmt.addProperties(batch, date, hour)

mgmt.buildIndex('indexBatchByDateHour', Vertex.class).addKey(date).addKey(hour).buildCompositeIndex()

# 只在这个 Vertex 上的 index
mgmt.buildIndex('indexBatchByDateHour', Vertex.class).addKey(date).addKey(hour).indexOnly(batch).unique().buildCompositeIndex()

mgmt.commit()
g.tx().commit();

对已有数据建立 index

graph.tx().rollback()
mgmt = graph.openManagement()
key = mgmt.getPropertyKey('key')
myIndex = mgmt.buildIndex('byKey', Vertex.class).addKey(key).unique().buildCompositeIndex()
mgmt.commit()
graph.tx().commit();

//Wait for the index to become available
ManagementSystem.awaitGraphIndexStatus(graph, 'byKey').call()


//Reindex the existing data
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getGraphIndex("byKey"), SchemaAction.REINDEX).get()
mgmt.commit()

未结束的 transactions

graph.getOpenTransactions()
graph.getOpenTransactions().getAt(0).rollback()
graph.getOpenTransactions().getAt(0).commit()

查看 index

mgmt.getGraphIndexes(Vertex.class)
mgmt.getGraphIndexes(Edge.class)
mgmt.getGraphIndex("indexBatchByDateHour")

查看 console 信息

$> Gremlin.version()
$> :show imports

查看当前 Graph 支持的 features

$ graph.features()
$ graph.toString()

使用 tinkergraph

$ :plugin use tinkerpop.tinkergraph
$ graph = TinkerGraph.open()
$ g = graph.traversal()

注入 JavaScript on chrome 网页

为什么需要这个功能

有时候, 我们不能更改一个别人做的网页, 但是发现这个页面有些方面不是自己习惯的, 或者它给它添加某些功能, 那么我们可能需要通过页面 注入JavaScript的方式去修改或增强它.

如何做

你可以在每次打开这个页面的时候, 运行一段 JavaScript脚本. 但是这样需要每次都手工打开 Inspect 然后打开console 然后运行脚本.

如果我们可以让它每次打开这页面的时候, 自动执行这个脚本, 岂不是更棒.

所以, 我们可以通过写一个 chrome extension 的方式, 让它自动注入这些脚本.

并且我们可以定义很多自定义脚本, 每当打开某些网页的时候, 自动注入.

最简单的例子

一共4步, 每一步都很简单.

1. 创建一个文件夹

mkdir myext

2. 创建 manifest.json

在上面的文件夹中创建文件 manifest.json, 内容如下:

{
  "manifest_version": 3,
  "name": "My Extension",
  "version": "1.0",
  "permissions": [
    "tabs"
  ],
  "host_permissions": [
    "https://www.google.com/"
  ],
  "content_scripts": [
    {
      "matches": [
        "https://www.google.com/"
      ],
      "js": [
        "google.js"
      ]
    }
  ]
}

3. 创建需要注入的脚本

在上面的文件夹中创建需要注入的脚本文件, 这里跟上面 content_scripts.js里面的保持一致就好.

console.log("running on google.com");

document.querySelector("textarea").value = "llama";
document.querySelector("input[value='Google Search']").click();

4. 加载新插件

打开 chrome 的 extension 管理界面, 然后在页面右上角打开: 开发者模式, 然后选择 加载未打包 的extension, 加载完成后测试即可.

Kretprobes

使用Kretprobes 监控内涵函数运行时间和返回值.

一个简单的例子

文件名: myretprobe.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>
#include <linux/limits.h>
#include <linux/sched.h>

static char func_name[NAME_MAX] = "ksys_read";
module_param_string(func, func_name, NAME_MAX, S_IRUGO);
MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the"
            " function's execution time");

/* per-instance private data */
struct my_data {
    ktime_t entry_stamp;
};

/* Here we use the entry_hanlder to timestamp function entry */
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    struct my_data *data;

    if (!current->mm)
        return 1;    /* Skip kernel threads */

    data = (struct my_data *)ri->data;
    data->entry_stamp = ktime_get();
    return 0;
}

/*
 * Return-probe handler: Log the return value and duration. Duration may turn
 * out to be zero consistently, depending upon the granularity of time
 * accounting on the platform.
 */
static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    int retval = regs_return_value(regs);
    struct my_data *data = (struct my_data *)ri->data;
    s64 delta;
    ktime_t now;

    now = ktime_get();
    delta = ktime_to_ns(ktime_sub(now, data->entry_stamp));
    printk(KERN_INFO "%s returned %d and took %lld ns to execute\n",
            func_name, retval, (long long)delta);
    return 0;
}

static struct kretprobe my_kretprobe = {
    .handler        = ret_handler,
    .entry_handler        = entry_handler,
    .data_size        = sizeof(struct my_data),
    /* Probe up to 20 instances concurrently. */
    .maxactive        = 20,
};

static int __init kretprobe_init(void)
{
    int ret;

    my_kretprobe.kp.symbol_name = func_name;
    ret = register_kretprobe(&my_kretprobe);
    if (ret < 0) {
        printk(KERN_INFO "register_kretprobe failed, returned %d\n",
                ret);
        return -1;
    }
    printk(KERN_INFO "Planted return probe at %s: %p\n",
            my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
    return 0;
}

static void __exit kretprobe_exit(void)
{
    unregister_kretprobe(&my_kretprobe);
    printk(KERN_INFO "kretprobe at %p unregistered\n",
            my_kretprobe.kp.addr);

    /* nmissed > 0 suggests that maxactive was set too low. */
    printk(KERN_INFO "Missed probing %d instances of %s\n",
        my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);
}

module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL");

Makefile

obj-m += myretprobe.o

tag ?= `uname -r`
KDIR := /lib/modules/${tag}/build/

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

运行并查看结果

$ make all
$ sudo insmod myretprobe.ko

$ sudo rmmod myretprobe
$ tail -n 10 /var/log/syslog
Jul 13 00:27:54 supra kernel: [ 2367.855060] ksys_read returned -32 and took 234 ns to execute
Jul 13 00:27:54 supra kernel: [ 2367.855063] ksys_read returned -32 and took 190 ns to execute
Jul 13 00:27:54 supra kernel: [ 2367.855066] ksys_read returned -32 and took 191 ns to execute
Jul 13 00:27:54 supra kernel: [ 2367.855068] ksys_read returned -32 and took 189 ns to execute

统计返回值 histogram 的例子

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <linux/init.h>

#define MY_ARRAY_SIZE 10
static uint my_array[MY_ARRAY_SIZE];

static char func_name[NAME_MAX] = "ksys_read";
module_param_string(func, func_name, NAME_MAX, S_IRUGO);
MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the"
            " function's execution time");

static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    int retval = regs_return_value(regs);
    if (retval < 0) {
        my_array[0]++;
    } else if (0 == retval) {
        my_array[1]++;
    } else if (retval < 20) {
        my_array[2]++;
    } else if (retval < 40) {
        my_array[3]++;
    } else if (retval < 80) {
        my_array[4]++;
    } else if (retval < 160) {
        my_array[5]++;
    } else if (retval < 320) {
        my_array[6]++;
    } else if (retval < 640) {
        my_array[7]++;
    } else if (retval < 1280) {
        my_array[8]++;
    } else {
        my_array[9]++;
    }
    //printk(KERN_INFO "%s returned %d \n", func_name, retval);
    return 0;
}

static struct kretprobe my_kretprobe = {
    .handler        = ret_handler,
    .maxactive        = 3,
};

static int __init kretprobe_init(void)
{
    int ret;

    memset(my_array, 0, sizeof(uint) * MY_ARRAY_SIZE);
    my_kretprobe.kp.symbol_name = func_name;
    ret = register_kretprobe(&my_kretprobe);
    if (ret < 0) {
        printk(KERN_INFO "register_kretprobe failed, returned %d\n",
                ret);
        return -1;
    }
    printk(KERN_INFO "Planted return probe at %s: %p\n",
            my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
    return 0;
}

static void __exit kretprobe_exit(void)
{
    int i;
    unregister_kretprobe(&my_kretprobe);
    printk(KERN_INFO "kretprobe at %p unregistered\n",
            my_kretprobe.kp.addr);

    printk(KERN_INFO "my_array values:\n");
    for (i = 0; i < MY_ARRAY_SIZE; i++) {
        printk(KERN_INFO "my_array[%d]: %u\n", i, my_array[i]);
    }

    /* nmissed > 0 suggests that maxactive was set too low. */
    printk(KERN_INFO "Missed probing %d instances of %s\n",
        my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);
}

module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL");

使用Kretprobes 观测系统调用 read 字节数 并放到 proc 文件系统

下面使用 Kretprobes 观测系统调用 ksys_read() 的返回字节数, 并把这些数字做成 histogram 的形式放到 /proc/readpattern 去, 然后读这个文件.

源代码

文件名 dumpreadstat.c:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/proc_fs.h>

#define MY_ARRAY_SIZE 10
#define MAX_STRING_LENGTH 16
static uint my_array[MY_ARRAY_SIZE];
static char desc_arr[MY_ARRAY_SIZE][MAX_STRING_LENGTH] = {
    "< 0        ",
    "= 0        ",
    "0 -> 20    ",
    "20 -> 40   ",
    "40 -> 80   ",
    "80 -> 160  ",
    "160 -> 320 ",
    "320 -> 640 ",
    "640 -> 1280",
    " > 1280    "
};

static int flag = 1;
static struct proc_dir_entry *proc_file;

static char func_name[NAME_MAX] = "ksys_read";
module_param_string(func, func_name, NAME_MAX, S_IRUGO);
MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the"
            " function's execution time");

static int open_proc(struct inode *inode, struct file *file)
{
    printk(KERN_ALERT "open proc\n");
    return 0;
}

static int release_proc(struct inode *inode, struct file *file)
{
    printk(KERN_ALERT "release proc\n");
    return 0;
}

static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{
    int i;
    printk(KERN_ALERT "read proc\n");
    if (flag)
    {
        flag = 0;
    }
    else
    {
        flag = 1;
        return 0;
    }

    char output[1024];
    int offst = 15;
    int remaining = sizeof(output);
    snprintf(output, remaining, "bytes \t\t:count\n");
    remaining -= 15;
    for (i = 0; i < MY_ARRAY_SIZE; i++) {
        int ret;

        ret = snprintf(output + offst, remaining, "%s\t: %u\n", desc_arr[i], my_array[i]);
        if (ret < 0 || ret >= remaining) {
            printk(KERN_ERR "Failed to concatenate my_array values\n");
            return -EINVAL;
        }

        offst += ret;
        remaining -= ret;
    }

    printk(KERN_ALERT "%s", output);

    if (copy_to_user(buffer, output, offst))
    {
        printk(KERN_ERR "Data Send: Err!\n");
        return -EFAULT;
    }
    return strlen(output);
}

static ssize_t write_proc(struct file *filp, const char *buffer, size_t len, loff_t *off)
{
    printk(KERN_ALERT "write proc\n");
    return 0;
}

static const struct proc_ops proc_fops = {
    .proc_open = open_proc,
    .proc_read = read_proc,
    .proc_write = write_proc,
    .proc_release = release_proc,
};

static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    int retval = regs_return_value(regs);
    if (retval < 0) {
        my_array[0]++;
    } else if (0 == retval) {
        my_array[1]++;
    } else if (retval < 20) {
        my_array[2]++;
    } else if (retval < 40) {
        my_array[3]++;
    } else if (retval < 80) {
        my_array[4]++;
    } else if (retval < 160) {
        my_array[5]++;
    } else if (retval < 320) {
        my_array[6]++;
    } else if (retval < 640) {
        my_array[7]++;
    } else if (retval < 1280) {
        my_array[8]++;
    } else {
        my_array[9]++;
    }
    //printk(KERN_INFO "%s returned %d \n", func_name, retval);
    return 0;
}

static struct kretprobe my_kretprobe = {
    .handler        = ret_handler,
    .maxactive        = 3,
};

static int __init kretprobe_init(void)
{
    int ret;

    memset(my_array, 0, sizeof(uint) * MY_ARRAY_SIZE);
    my_kretprobe.kp.symbol_name = func_name;
    ret = register_kretprobe(&my_kretprobe);
    if (ret < 0) {
        printk(KERN_INFO "register_kretprobe failed, returned %d\n",
                ret);
        return -1;
    }
    printk(KERN_INFO "Planted return probe at %s: %p\n",
            my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);

    /* Create proc file under /proc/dumpprocmm */
    proc_file = proc_create("readpattern", 0666, NULL, &proc_fops);
    if (!proc_file) {
        printk(KERN_ERR "Failed to create proc file\n");
        return -ENOMEM;
    }

    return 0;
}

static void __exit kretprobe_exit(void)
{
    int i;
    unregister_kretprobe(&my_kretprobe);
    printk(KERN_INFO "kretprobe at %p unregistered\n",
            my_kretprobe.kp.addr);

    printk(KERN_INFO "my_array values:\n");
    for (i = 0; i < MY_ARRAY_SIZE; i++) {
        printk(KERN_INFO "my_array[%d]: %u\n", i, my_array[i]);
    }

    if (proc_file) {
        proc_remove(proc_file);
        printk(KERN_INFO "Removed /proc/%s file\n", "readpattern");
    }

    /* nmissed > 0 suggests that maxactive was set too low. */
    printk(KERN_INFO "Missed probing %d instances of %s\n",
        my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);
}

module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL");

Makefile

obj-m += dumpreadstat.o

tag ?= `uname -r`
KDIR := /lib/modules/${tag}/build/

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

执行并观测

$ make all
$ sudo insmod dumpreadstat.ko
$ cat /proc/readpattern
bytes         :count
< 0            : 1
= 0            : 27
0 -> 20        : 65
20 -> 40       : 24
40 -> 80       : 7
80 -> 160      : 8
160 -> 320     : 3
320 -> 640     : 2
640 -> 1280    : 11
 > 1280        : 10

# 卸载模块
$ sudo rmmod dumpreadstat

# 观测系统日志
$ tail -n 100 /var/log/syslog