CuriousY A world with wonder

K8S troubleshooting notes

| Comment
  1. 由于minikube使用的并不是本地安装的docker deamon,所以它并不会使用本地build好的image,而是从remote去pull。如果想当场build之后就在minikube中使用,可以在build image之前设置为使用minikube的docker:

    eval $(minikube docker-env)
    

    并且将对应的K8S的schema中的imagePullPolicy设为Never,这样就会使用刚build好的本地的image了。

  2. 在使用minikube的docker制作image是碰到过pip install failed的情况,排查下来的原因是minikube中的docker(cpu、内存)资源不足,导致安装某些包需要编译时失败了。通过给minikube分配更多的资源解决,比如:

    minikube start --memory 4096 --cpus 4
    
  3. 想要mount本地的文件、文件夹到某个pod的某个container中?首先你需要先把本地的路径mount到minikube创建的VM中:

    minikube start --mount --mount-string="$HOME/.minikube:/data/.minikube"
    

    通过minikube ssh可以登录minikube的VM中确认文件是否成功mount。

    然后,在K8S的schema中创建对应的volume并mount:

          containers:
            - name: nginx
              image: nginx
              volumeMounts:
                - mountPath: /opt/kube-cert/
                  name: kube-cert
          volumes:
            - name: kube-cert
              hostPath:
                path: /data/.minikube/
                type: Directory
    
  4. pod中的某个container启动失败了,可以通过一些命令来troubleshooting:

    # Get detailed info of the pod, xxx is the pod name
    kubectl get pod xxx --output=yaml
    # Print stdout of failed pod/container
    kubectl logs xxx
    # For container which can start up but got some issues, you can login to the box
    kubectl exec -it xxx bash
    
  5. K8S的service可以被其他deployment的pod内直接访问(kube-proxy会自动给service分配DNS name,就是service的name,另外也可以通过环境变量来获取所有service的地址(K8S会把当前的service注册到环境变量中,但之后注册的就没办法了));而同一个pod内部的container之间互相访问则访问localhost就可以了,比如pod内起了一个MongoDB的container以及一个Nginx的container,那么Nginx可以通过localhost:27017来连接到该MongoDB。

  6. 可以在一个yaml文件中定义多个service和deployment等的schema,这样通过kubectl apply -f xxx.yaml就可以同时都起起来。

  7. 从pod内部调用K8S的api可以参考Accessing the API from within a Pod,前提是该pod需要有相应的权限,这些权限可以通过创建并绑定对应的RBAC role来完成,比如:

    ---
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: jobs-create
    rules:
    - apiGroups: ["batch", "extensions"]
      resources: ["jobs"]
      verbs: ["create", "get", "list", "watch", "update", "patch", "delete"]
    ---
    # Bind to default service account in default namespace
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: jobs-create
    subjects:
    - kind: ServiceAccount
      name: default
      namespace: default
    roleRef:
      kind: ClusterRole
      name: jobs-create
      apiGroup: rbac.authorization.k8s.io
    
  8. K8S的job执行结束后不会自动销毁,K8S目前只有一个alpha的feature可以为job设置TTL,并在固定的周期清理过期并完成的job,所以暂时要么手动enable这个feature,要么就自己写一个service来清理job。

  9. K8S的job是没有rerun的概念的,所以如果有一个同名的job即使处于completed的状态,也无法再创建一个同样名字的job,必须先delete掉之前的job或者使用不同的名字。

Some notes for Golang

| Comment
  1. $GOPATH为工作目录,它有点类似于Python的site-packages,里面用于存放项目依赖的第三方代码。

    $GOPATH
    ├── pkg
    └── src
    		├── golang.org
    		└── Example-project
    
  2. 对于某一个项目,代码一般不要放在src目录下,而是按照功能放置代码文件(比如和web相关的就放在web文件夹内)。这一点和$GOPATH不能混淆(refer)。

  3. 使用go get安装依赖之前需要使用go mod init来初始化。

  4. 不同于JavaScript的NPM把依赖安装在各个项目目录下,Python可以通过virtualenv来隔离项目依赖,Go把所有的依赖都存放在工作目录下(这里只说Go官方支持的modules方式)。在每个项目下,使用go.modgo.sum文件来标识项目所需的依赖,对于同一个依赖,会根据其version和commit来放置到不同的文件夹。因此,同一个依赖在$GOPATH下可能会有很多份不同版本的代码。(这样虽然可能复用之前安装过的依赖,但如何有效地清除那些已经不再需要的依赖呢?)

  5. 对比Python:

    • Go没有set数据结构(原因么看这里),要实现一个集合可以这样:

      var mySet = map[string]bool {}
      mySet["a"] = true   // Add "a"
      _, ok = mySet["b"]  // Check if "b" exist
      
    • Go不支持方法定义时给参数默认值(by design,see here), 看到的比较好的替代方案是这样的:

      type Params struct {
        a, b, c int
      }
           
      func doIt(p Params) int {
        return p.a + p.b + p.c 
      }
           
      // you can call it without specifying all parameters
      doIt(Params{a: 1, c: 9})
      
  6. Go的module用init函数可以做一些初始化的工作,而struct的并不存在初始化的特殊函数,如果想要在struct生成时做些初始化操作,可以使用一个额外的工厂函数来生成该struct:

    type Person struct {
      name string
      age  int
    }
       
    func NewPerson(name string, age int) {
      // Do some init work
      return &Person{name, age}
    }
    
  7. Go标准库对xml或json的解析都需要提前定义好它的format,比如说对于xml:

    var body = `<response>
     <sessionKey>exuOpep4^t^oJRtf1JCAfnlIAhDK76ZlMTwP6uWZwXebJ_NRsh8qUBUpxM7LNtOi8Tt6J9DUvdcQdb1r9y8a^V</sessionKey>
      <messages>
        <msg code=""></msg>
      </messages>
    </response>
    `
    // Define the format
    var xmlBody struct {
      SessionKey string `xml:"sessionKey"`
    }
    if err := xml.Unmarshal([]byte(body), &xmlBody); err != nil {
      log.Fatal(err)
    }
    // Parsing result
    fmt.Println(xmlBody.SessionKey)
    
Read more

关于Typescript以及React

| Comment
  1. 使用Webstorm(或vscode)可以debug Typescript的代码,但需要在tsconfig.json中把sourceMap设为true。然后在Typescript代码中打上断点,再使用Node.js来debug编译过的JavaScript代码(编译过后应该同时会生成一个.map文件,根据这个文件来映射.js.ts进行debug)。

  2. tsconfig.json中可以设置outDir,即为编译后的.js文件会放到指定的目录下。

  3. tsconfig.json的compilerOptions中的lib如果包含了dom,则Typescript会认为这是一个前端的JS环境 ,所以一些保留字也会和前端JS的一致,这时候再用Node.js的话是有一些问题的,比如说下面的代码编译就没法通过:

    const fetch = require("node-fetch");
    

    错误信息为redeclared variable,因为它认为Global变量中已经有一个叫做fetch的变量了(但Node.js中并没有~)。

    比较好的解决方法是分别使用不同的tsconfig.jsontslint.json来维护前端代码和Node.js的后端代码。

  4. tslint默认配置要求interface的命名必须以I开头,但我看了许多人不建议这么命名,所以,可以把interface相关的rule修改下。

  5. 可以使用ReturnType来使用一个函数的返回值的类型定义新的类型:

    type yyy = ReturnType<typeof xxx>;
    
  6. create-react-app目前支持生成typscript的项目,但tslint目前要手动创建,并且在package.json中添加如下的命令:

    "lint": "tslint -c tslint.json src/**/*.{ts,tsx}"
    
  7. 可以安装额外的tslint package来扩充规则,对于React项目而言,比较有用的是tslint-react,贴下我的tslint.json作为参考:

    {
      "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
      "linterOptions": {
        "exclude": [
          "config/**/*.js",
          "node_modules/**/*.ts",
          "coverage/lcov-report/*.js"
        ]
      },
      "rules": {
        "no-console": false,
        "semicolon": false,
        "ordered-imports": false,
        "object-literal-sort-keys": false,
        "variable-name": [true, "allow-leading-underscore", "allow-pascal-case"],
        "interface-name": [true, "never-prefix"],
        "jsx-boolean-value": false,
        "max-classes-per-file": false
      }
    }
    
  8. 很多example可以参考https://github.com/piotrwitek/react-redux-typescript-guide

  9. React现在不建议在组件中使用匿名函数,原因是某些情况下会导致性能下降。所以,要获取组件中某个JSX的ref可以这样做:

    class MyComponent extends React.Component<Props, State> {
        private elem: React.RefObject<HTMLDivElement> = React.createRef<HTMLDivElement>();
        public render() {
            return (
                <div ref={this.elem}>
                    xxx
                <div/>
            );
        }
    }
    

Reference

About Deep Q Network

| Comment
  1. DQN是Q learning和Neural network结合的产物,而Q learning是强化学习经典的算法。Q learning的思路其实不复杂:假设在某一步骤时有N个选择,根据状态表(关于这个概念后面会再聊),选择了option A,进入到下一个步骤,继续根据状态表选择了option B,以此类推,直到最后一步的选择得到了结果,如果这个结果正是我们想得到的,则我们会给予一个奖励,怎么个奖励法?其实就是更新状态表,把之前的各个状态下对应的选择的权重进行提升。否则,如果结果不是我们想要的,则进行惩罚:当然也是更新状态表。所谓状态表,是这样一张表:它的每行都是一个状态,每列是对应该状态的各种选择,表中的值则是选择所对应的权重(也可以理解为做各个选择所期望得到的奖励):

      option A option B option C option D
    状态一 -0.12 0.28 0 0
    状态二 0.33 0 -0.21 0
    状态三 0 -0.02 0.31 0.54

    这里的状态根据所解决的问题不同而不同,比如说走迷宫的问题,那么走迷宫的人的坐标就是状态,而上下左右四个方向则是选项。需要注意的是,并不是每个状态下都会对应所有的选项的,比如在迷宫底部,向下这个选项就是不存在的(或者说它的权重无限低)。

  2. Q learning中的Q是Quality的缩写,就是状态表中的权重也可以认为是Q值(选择的质量)。

  3. Q learning如果仅仅在最后一步达到目标后再去更新状态表,其实是有一点不合适的,假设我们一共做了n次选择,我们当然可以给第n次选择奖励1,第n-1次选择奖励0.9,以此类推。但是这样往往最终得到的并不是最优解,可能我们可以少做几次选择也能达到目标,但由于这种奖励方式,导致多余的那几次选择都得到了奖励。所以,实际的Q learning是这样“发放奖励”的:

    • 如果当前的选择之后的状态即是我们所想要达到的目标状态,那么我们直接给予该选择一个固定的奖励;
    • 如果当前的选择之后的状态并非是最终的目标状态,那么我们使用该选择之后的状态下的所有选项中的最高权重(事实上这个拥有最高权重的选项就是下一步要做的选择),以一定比例给予当前的选择。

    所以,Q learning是每一次选择结束都会去更新状态表的。

  4. Q learning训练的过程就是一个不断去更新状态表的过程,做预测时,状态表就是决策表。对于复杂的问题,Q learning有这么些缺点:

    • 状态难以定义
    • 状态表存储成本高
    • “奖惩”力度难以把控

    并且,为了学习解决一个问题需要数以万次的尝试,这离人类的智能相差很远,这也是目前强化学习(不仅是Q learning)被人所病垢比较多的一点。

  5. 试想下,在Q learning的算法中,神经网络可以做些什么?首先,做选择可以交由神经网络来做,假设我们一共有N种选择,构造一个输出层含有N个神经元的网络即可,它的输入当然就是当前时刻的状态,这样我们就不用再创建上面那张状态表了;其次,“奖惩”力度也可以由神经网络来决定,它的输入为下一时刻的状态(即做出当前选择之后的状态),输出为预测的下一个状态下各个选择的权重(然后同样用预测的之后的所有选择的最高权重来给予奖励)。前者在DQN中称之为evaluate network,后者为target network。

Read more

About Recurrent Neural Network

| Comment
  1. RNN和普通的神经网络的区别在于,它中间隐层的状态不仅取决于输入值,还取决于隐层之前的状态值。即对于普通的神经网络,S = f(W * X + b),其中S为隐层的状态值(可以理解为隐层神经元的输出),X为输入向量,W为隐层神经元的权重,b为偏置,f为激活函数;而对于RNN,S1 = f(W * X + b + W_s * S0),其中,S1为隐层当前的状态值,S0为隐层上一个时刻的状态值,W_s为权重。

    RNNs are called recurrent because they perform the same task for every element of a sequence, with the output being depended on the previous computations.

  2. 因为RNN隐层上一时刻的状态值又包含了上上一时刻的状态值,所以理论上,某一时刻隐层的状态值是有其之前的所有时刻的状态值共同决定的。假设W_s是小于1的权重,那么越靠近当前时刻的状态对目前状态的影响越大,这也比较符合我们的经验认知。

    In theory RNNs can make use of information in arbitrarily long sequences, but in practice they are limited to looking back only a few steps (more on this later).

  3. 如果我们把RNN中循环的网络展开来,其实很像含有多个隐层的普通神经网络,只不过每个隐层都多了一个外部输入,且所有隐层共享了权重和偏置(这一点不禁联想到CNN)。在某种程度上来说,RNN和CNN都可以算是DNN的特例,所以也不要奇怪有些事情不光CNN能做,RNN也能实现(甚至DNN也可以做到)。比如说这个代码就实现了使用RNN来对MNIST手写数字进行分类,它把图像的每一行像素作为输入,输入到一个循环了N(N为图像高度)次的RNN中,最后用一个全连接层作为输出。 A recurrent neural network and the unfolding in time of the computation involved in its forward computation.

Read more
First | Page 1 of 23 |