etcd 集群运维实践

时间:2022-09-22 23:43:47

etcd 是 Kubernetes 集群的数据核心,最严重的情况是,当 etcd 出问题彻底无法恢复的时候,解决问题的办法可能只有重新搭建一个环境。因此围绕 etcd 相关的运维知识就比较重要,etcd 可以容器化部署,也可以在宿主机自行搭建,以下内容是通用的。

集群的备份和恢复

添加备份

  1. #!/bin/bash

  2. IP=123.123.123.123

  3. BACKUP_DIR=/alauda/etcd_bak/

  4. mkdir -p $BACKUP_DIR

  5. export ETCDCTL_API=3

  6. etcdctl --endpoints=http://$IP:2379 snapshot save $BACKUP/snap-$(date +%Y%m%d%H%M).db

  7. # 备份一个节点的数据就可以恢复,实践中,为了防止定时任务配置的节点异常没有生成备份,建议多加几个

恢复集群

  1. #!/bin/bash

  2. # 使用 etcdctl snapshot restore 生成各个节点的数据

  3. # 比较关键的变量是

  4. # --data-dir 需要是实际 etcd 运行时的数据目录

  5. # --name  --initial-advertise-peer-urls  需要用各个节点的配置

  6. # --initial-cluster  initial-cluster-token 需要和原集群一致

  7. ETCD_1=10.1.0.5

  8. ETCD_2=10.1.0.6

  9. ETCD_3=10.1.0.7

  10. for i in ETCD_1 ETCD_2 ETCD_3

  11. do

  12. export ETCDCTL_API=3

  13. etcdctl snapshot restore snapshot.db \

  14. --data-dir=/var/lib/etcd \

  15. --name $i \

  16. --initial-cluster ${ETCD_1}=http://${ETCD_1}:2380,${ETCD_2}=http://${ETCD_2}:2380,${ETCD_3}=http://${ETCD_3}:2380 \

  17. --initial-cluster-token k8s_etcd_token \

  18. --initial-advertise-peer-urls http://$i:2380 && \

  19. mv /var/lib/etcd/ etcd_$i

  20. done

  21. # 把 etcd_10.1.0.5 复制到 10.1.0.5节点,覆盖/var/lib/etcd(同--data-dir路径)

  22. # 其他节点依次类推

用 etcd 自动创建的 SnapDb 恢复

  1. #!/bin/bash

  2. export ETCDCTL_API=3

  3. etcdctl snapshot restore snapshot.db \

  4. --skip-hash-check \

  5. --data-dir=/var/lib/etcd \

  6. --name 10.1.0.5 \

  7. --initial-cluster 10.1.0.5=http://10.1.0.5:2380,10.1.0.6=http://10.1.0.6:2380,10.1.0.7=http://10.1.0.7:2380 \

  8. --initial-cluster-token k8s_etcd_token \

  9. --initial-advertise-peer-urls http://10.1.0.5:2380

  10. # 也是所有节点都需要生成自己的数据目录,参考上一条

  11. # 和上一条命令唯一的差别是多了  --skip-hash-check  (跳过完整性校验)

  12. # 这种方式不能确保 100% 可恢复,建议还是自己加备份

  13. # 通常恢复后需要做一下数据压缩和碎片整理,可参考相应章节

踩过的坑
[ 3.0.14 版 etcd restore 功能不可用 ] https://github.com/etcd-io/etcd/issues/7533
使用更新的 etcd 即可。
总结:恢复就是要拿 DB 去把 etcd 的数据生成一份,用同一个节点的,可以保证除了 restore 时候指定的参数外,所有数据都一样。这就是用一份 DB,操作三次(或者5次)的原因。

集群的扩容——从 1 到 3

执行添加

  1. #!/bin/bash

  2. export ETCDCTL_API=2

  3. etcdctl --endpoints=http://10.1.0.6:2379 member add 10.1.0.6 http://10.1.0.6:2380

  4. etcdctl --endpoints=http://10.1.0.7:2379 member add 10.1.0.7 http://10.1.0.7:2380

  5. # ETCD_NAME="etcd_10.1.0.6"

  6. # ETCD_INITIAL_CLUSTER="10.1.0.6=http://10.1.0.6:2380,10.1.0.5=http://10.1.0.5:2380"

  7. # ETCD_INITIAL_CLUSTER_STATE="existing"

准备添加的节点 etcd 参数配置

  1. #!/bin/bash

  2. /usr/local/bin/etcd

  3. --data-dir=/data.etcd

  4. --name 10.1.0.6

  5. --initial-advertise-peer-urls http://10.1.0.6:2380

  6. --listen-peer-urls http://10.1.0.6:2380

  7. --advertise-client-urls http://10.1.0.6:2379

  8. --listen-client-urls http://10.1.0.6:2379

  9. --initial-cluster 10.1.0.6=http://10.1.0.6:2380,10.1.0.5=http://10.1.0.5:2380

  10. --initial-cluster-state exsiting

  11. --initial-cluster-token k8s_etcd_token

  12. # --initial-cluster 集群所有节点的 name=ip:peer_url

  13. # --initial-cluster-state exsiting 告诉 etcd 自己归属一个已存在的集群,不要自立门户

踩过的坑
从 1 到 3 期间,会经过集群是两节点的状态,这时候可能集群的表现就像挂了,endpoint status 这些命令都不能用,所以我们需要用 member add 先把集群扩到三节点,然后再依次启动 etcd 实例,这样做就能确保 etcd 就是健康的。
从 3 到更多,其实还是 member add 啦,就放心搞吧。

集群加证书

生成证书

  1. curl -s -L -o /usr/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64

  2. curl -s -L -o /usr/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64

  3. chmod +x /usr/bin/{cfssl,cfssljson}

  4. cd /etc/kubernetes/pki/etcd

  1. #  cat ca-config.json

  2. {

  3.  "signing": {

  4.    "default": {

  5.      "expiry": "100000h"

  6.    },

  7.    "profiles": {

  8.      "server": {

  9.        "usages": ["signing", "key encipherment", "server auth", "client auth"],

  10.        "expiry": "100000h"

  11.      },

  12.      "client": {

  13.        "usages": ["signing", "key encipherment", "server auth", "client auth"],

  14.        "expiry": "100000h"

  15.      }

  16.    }

  17.  }

  18. }

  1. #  cat ca-csr.json

  2. {

  3.  "CN": "etcd",

  4.  "key": {

  5.    "algo": "rsa",

  6.    "size": 4096

  7.  },

  8.  "names": [

  9.    {

  10.      "C": "CN",

  11.      "L": "Beijing",

  12.      "O": "Alauda",

  13.      "OU": "PaaS",

  14.      "ST": "Beijing"

  15.    }

  16.  ]

  17. }

  1. #  cat server-csr.json

  2. {

  3.  "CN": "etcd-server",

  4.  "hosts": [

  5.    "localhost",

  6.    "0.0.0.0",

  7.    "127.0.0.1",

  8.    "所有master 节点ip ",

  9.    "所有master 节点ip ",

  10.    "所有master 节点ip "

  11.  ],

  12.  "key": {

  13.    "algo": "rsa",

  14.    "size": 4096

  15.  },

  16.  "names": [

  17.    {

  18.      "C": "CN",

  19.      "L": "Beijing",

  20.      "O": "Alauda",

  21.      "OU": "PaaS",

  22.      "ST": "Beijing"

  23.    }

  24.  ]

  25. }

  1. # cat client-csr.json

  2. {

  3.  "CN": "etcd-client",

  4.  "hosts": [

  5.    ""

  6.  ],

  7.  "key": {

  8.    "algo": "rsa",

  9.    "size": 4096

  10.  },

  11.  "names": [

  12.    {

  13.      "C": "CN",

  14.      "L": "Beijing",

  15.      "O": "Alauda",

  16.      "OU": "PaaS",

  17.      "ST": "Beijing"

  18.    }

  19.  ]

  20. }

  1. cd /etc/kubernetes/pki/etcd

  2. cfssl gencert -initca ca-csr.json | cfssljson -bare ca

  3. cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server-csr.json | cfssljson -bare server

  4. cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client-csr.json | cfssljson -bare client

参考链接:https://lihaoquan.me/2017/3/29/etcd-https-setup.html
首先更新节点的peer-urls

  1. export ETCDCTL_API=3

  2. etcdctl --endpoints=http://x.x.x.x:2379 member list

  3.   #  1111111111  ..........

  4.   #  2222222222  ..........

  5.   #  3333333333  ..........

  6. etcdctl --endpoints=http://172.30.0.123:2379 member update 1111111111 --peer-urls=https://x.x.x.x:2380

  7.   # 执行三次把三个节点的peer-urls都改成https

修改配置

  1. #  vim /etc/kubernetes/main*/etcd.yaml

  2. #  etcd启动命令部分修改 http 为 https,启动状态改成 existing

  3.    - --advertise-client-urls=https://x.x.x.x:2379

  4.    - --initial-advertise-peer-urls=https://x.x.x.x:2380

  5.    - --initial-cluster=xxx=https://x.x.x.x:2380,xxx=https://x.x.x.x:2380,xxx=https://x.x.x.x:2380

  6.    - --listen-client-urls=https://x.x.x.x:2379

  7.    - --listen-peer-urls=https://x.x.x.x:2380

  8.    - --initial-cluster-state=existing

  9. #  etcd 启动命令部分插入

  10.    - --cert-file=/etc/kubernetes/pki/etcd/server.pem

  11.    - --key-file=/etc/kubernetes/pki/etcd/server-key.pem

  12.    - --peer-cert-file=/etc/kubernetes/pki/etcd/server.pem

  13.    - --peer-key-file=/etc/kubernetes/pki/etcd/server-key.pem

  14.    - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem

  15.    - --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem

  16.    - --peer-client-cert-auth=true

  17.    - --client-cert-auth=true

  18. #  检索hostPath在其后插入

  19.  - hostPath:

  20.      path: /etc/kubernetes/pki/etcd

  21.      type: DirectoryOrCreate

  22.    name: etcd-certs

  23. #  检索mountPath在其后插入

  24.    - mountPath: /etc/kubernetes/pki/etcd

  25.      name: etcd-certs

  1. #  vim /etc/kubernetes/main*/kube-apiserver.yaml

  2. #  apiserver 启动部分插入,修改 http 为https

  3.    - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.pem

  4.    - --etcd-certfile=/etc/kubernetes/pki/etcd/client.pem

  5.    - --etcd-keyfile=/etc/kubernetes/pki/etcd/client-key.pem

  6.    - --etcd-servers=https://x.x.x.x:2379,https://x.x.x.x:2379,https://x.x.x.x:2379

总结下就是,先准备一套证书。然后修改 etcd 内部通信地址为https,这时候etcd日志会报错(可以忽略),然后用etcd --带证书的参数启动,把所有链接etcd的地方都用上证书,即可。
遇到的坑
[ etcd 加证书后,apiserver 的健康检查还是 http 请求,etcd 会一直刷日志 ] https://github.com/etcd-io/etcd/issues/9285

  1. 2018-02-06 12:41:06.905234 I | embed: rejected connection from "127.0.0.1:35574" (error "EOF", ServerName "")

解决办法:直接去掉 apiserver 的健康检查,或者把默认的检查命令换成 curl(apiserver 的镜像里应该没有 curl,如果是刚需的话自己重新 build 一下吧)。

集群升级

已经是 v3 的的集群不需要太多的配置,保留数据目录,替换镜像(或者二进制)即可;
v2 到 v3 的升级需要一个 merge 的操作,我并没有实际的实践过,也不太推荐这样做。

集群状态检查

其实上述所有步骤都需要这些命令的辅助——

  1. #!/bin/bash

  2. # 如果证书的话,去掉--cert --key --cacert 即可

  3. # --endpoints= 需要写了几个节点的url,endpoint status就输出几条信息

  4. export ETCDCTL_API=3

  5. etcdctl \

  6. --endpoints=https://x.x.x.x:2379 \

  7. --cert=/etc/kubernetes/pki/etcd/client.pem \

  8. --key=/etc/kubernetes/pki/etcd/client-key.pem \

  9. --cacert=/etc/kubernetes/pki/etcd/ca.pem \

  10. endpoint status -w table

  11. etcdctl --endpoints=xxxx endpoint health

  12. etcdctl --endpoints=xxxx member list

  13. kubectl get cs

数据操作(删除、压缩、碎片整理)

删除

  1. ETCDCTL_API=2 etcdctl rm --recursive            # v2 的 api 可以这样删除一个“目录”

  2. ETCDCTL_API=3 etcdctl --endpoints=xxx del /xxxxx --prefix # v3 的版本

  3. # 带证书的话,参考上一条添加 --cert --key --cacert 即可

遇到的坑:在一个客户环境里发现 Kubernetes 集群里的 “事件” 超级多,就是 kubectl describe xxx 看到的 events 部分信息,数据太大导致 etcd 跑的很累,我们就用这样的方式删掉没用的这些数据。
碎片整理

  1. ETCDCTL_API=3 etcdctl --endpoints=xx:xx,xx:xx,xx:xx defrag

  2. ETCDCTL_API=3 etcdctl --endpoints=xx:xx,xx:xx,xx:xx endpoint status # 看数据量

压缩

  1. ETCDCTL_API=3 etcdctl --endpoints=xx:xx,xx:xx,xx:xx compact

  2. # 这个在只有 K8s 用的 etcd 集群里作用不太大,可能具体场景我没遇到

  3. # 可参考这个文档

  4. # https://www.cnblogs.com/davygeek/p/8524477.html

  5. # 不过跑一下不碍事

  6. etcd --auto-compaction-retention=1

  7. # 添加这个参数让 etcd 运行时自己去做压缩

常见问题

  1. etcd 对时间很依赖,所以集群里的节点时间一定要同步

  2. 磁盘空间不足,如果磁盘是被 etcd 自己吃完了,就需要考虑压缩和删数据啦

  3. 加证书后所有请求就都要带证书了,要不会提示 context deadline exceeded

  4. 做各个操作时 etcd 启动参数里标明节点状态的要小心,否则需要重新做一遍前面的步骤很麻烦

日志收集

etcd 的日志暂时只支持 syslog 和 stdout 两种——https://github.com/etcd-io/etcd/issues/7936。
etcd 的日志在排查故障时很有用,如果我们用宿主机来部署 etcd,日志可以通过 systemd 检索到,但 kubeadm 方式启动的 etcd 在容器重启后就会丢失所有历史。我们可以用以下的方案来做——
shell 的重定向

  1. etcd --xxxx --xxxx   >  /var/log/etcd.log

  2. # 配合 logratate 来做日志切割

  3. # 将日志通过 volume 挂载到宿主机

supervisor
supervisor 从容器刚开始流行时,就是保持服务持续运行很有效的工具。
Sidecar 容器(后续我在 GitHub 上补充一个例子,github.com/jing2uo)
Sidecar 可以简单理解为一个 Pod 里有多个容器(比如 kubedns)他们彼此可以看到对方的进程,因此我们可以用传统的 strace 来捕捉 etcd 进程的输出,然后在 Sidecar 这个容器里和 shell 重定向一样操作。

  1. strace  -e trace=write -s 200 -f -p 1

Kubeadm 1.13 部署的集群

最近我们测试 Kubernetes 1.13 集群时发现了一些有趣的改变,诈一看我们上面的命令就没法用了——
https://kubernetes.io/docs/setup/independent/ha-topology/
区分了 Stacked etcd topology 和 External etcd topology,官方的链接了这个图很形象—— 
etcd 集群运维实践

这种模式下的 etcd 集群,最明显的差别是容器内 etcd 的initial-cluster 启动参数只有自己的 IP,会有点懵挂了我这该怎么去恢复。其实基本原理没有变,Kubeadm 藏了个 ConfigMap,启动参数被放在了这里——

  1. kubectl get cm  etcdcfg -n kube-system -o yaml

  1.    etcd:

  2.      local:

  3.        serverCertSANs:

  4.        - "192.168.8.21"

  5.        peerCertSANs:

  6.        - "192.168.8.21"

  7.        extraArgs:

  8.          initial-cluster: 192.168.8.21=https://192.168.8.21:2380,192.168.8.22=https://192.168.8.22:2380,192.168.8.20=https://192.168.8.20:2380

  9.          initial-cluster-state: new

  10.          name: 192.168.8.21

  11.          listen-peer-urls: https://192.168.8.21:2380

  12.          listen-client-urls: https://192.168.8.21:2379

  13.          advertise-client-urls: https://192.168.8.21:2379

  14.          initial-advertise-peer-urls: https://192.168.8.21:2380

Q&A

Q:请问 etcd 监控和告警如何做的?告警项都有哪些?A:告警要看用的什么监控吧,和 Kubernetes 配套比较常见的是普罗米修思和 Grafana 了。告警项我没有具体配过,可以关注的点是:endpoint status -w table 里可以看到数据量,endpoints health 看到健康状态,还有内存使用这些,具体可以参考普罗米修思的 exporter 是怎么做的。
Q:使用 Kubeadm 部署高可用集群是不是相当于先部署三个独立的单点 Master,最后靠 etcd 添加节点操作把数据打通?A:不是,Kubeadm 部署会在最开始就先建一个 etcd 集群,apiserver 启动之前就需要准备好 etcd,否则 apiserver 起不了,集群之间就没法通信。可以尝试手动搭一下集群,不用 Kubeadm,一个个把组件开起来,之后对Kubernetes的组件关系会理解更好的。
Q:etcd 跨机房高可用如何保证呢?管理 etcd 有好的 UI 工具推荐么?A:etcd 对时间和网络要求很高,所以跨机房的网络不好的话性能很差,光在那边选请输入链接描述举去了。我分享忘了提一个 etcd 的 mirror,可以去参考下做法。跨机房的话,我觉得高速网络是个前提吧,不过还没做过。UI 工具没找过,都是命令行操作来着。
Q:Kubeadm 启动的集群内 etcd节点,kubectl 操作 etcd 的备份恢复有尝试过吗?A:没有用 kubectl 去处理过 etcd 的备份恢复。etcd 的恢复依赖用 SnapDb 生成数据目录,把 etcd 进程丢进容器里,类似的操作避免不了,还有启动的状态需要修改。kubeadm 启动的 etcd 可以通过 kubectl 查询和 exec,但是数据操作应该不可以,比如恢复 etcd ing 时,无法连接 etcd,kubectl 还怎么工作?
Q:kubeadm-ha 启动 3 个 Master,有 3 个 etcd 节点,怎么跟集群外的 3 个 etcd 做集群,做成 3 Master 6 etcd?A:可以参考文档里的扩容部分,只要保证 etcd 的参数正确,即使一个集群一部分容器化,一部分宿主机,都是可以的(当然不建议这么做)。可以先用 kubeadm 搭一个集群,然后用扩容的方式把其他三个节点加进来,或者在 kubeadm 操作之前,先搭一个 etcd 集群。然后 kubeadm 调用它就可以。
Q:有没有试过 Kubeadm 的滚动升级,etcd 版本变更,各 Master 机分别重启,数据同步是否有异常等等?A:做过。Kubeadm 的滚动升级公司内部有从 1.7 一步步升级到 1.11、1.12 的文档,或多或少有一点小坑,不过今天主题是 etcd 所以没提这部分。各个 Master 分别重启后数据的一致我们测试时没问题,还有比较极端的是直接把三 Master 停机一天,再启动后也能恢复。

阅读原文

etcd 集群运维实践的更多相关文章

  1. 灵雀云:etcd 集群运维实践

    [编者的话]etcd 是 Kubernetes 集群的数据核心,最严重的情况是,当 etcd 出问题彻底无法恢复的时候,解决问题的办法可能只有重新搭建一个环境.因此围绕 etcd 相关的运维知识就比较 ...

  2. 阿里巴巴大规模神龙裸金属 Kubernetes 集群运维实践

    作者 | 姚捷(喽哥)阿里云容器平台集群管理高级技术专家 本文节选自<不一样的 双11 技术:阿里巴巴经济体云原生实践>一书,点击即可完成下载. 导读:值得阿里巴巴技术人骄傲的是 2019 ...

  3. 彻底搞懂 etcd 系列文章(三):etcd 集群运维部署

    0 专辑概述 etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管.etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件 ...

  4. PB 级大规模 Elasticsearch 集群运维与调优实践

    PB 级大规模 Elasticsearch 集群运维与调优实践 https://mp.weixin.qq.com/s/PDyHT9IuRij20JBgbPTjFA | 导语 腾讯云 Elasticse ...

  5. 集群运维ansible

    ssh免密登录 集群运维 生成秘钥,一路enter cd ~/.ssh/ ssh-keygen -t rsa 讲id_rsa.pub文件追加到授权的key文件中 cat ~/.ssh/id_rsa.p ...

  6. PB级大规模Elasticsearch集群运维与调优实践

    导语 | 腾讯云Elasticsearch 被广泛应用于日志实时分析.结构化数据分析.全文检索等场景中,本文将以情景植入的方式,向大家介绍与腾讯云客户合作过程中遇到的各种典型问题,以及相应的解决思路与 ...

  7. PB级大规模Elasticsearch集群运维与调优实践【>>戳文章免费体验Elasticsearch服务30天】

    [活动]Elasticsearch Service免费体验馆>> Elasticsearch Service自建迁移特惠政策>>Elasticsearch Service新用户 ...

  8. 使用Chef管理windows集群 &vert; 运维自动化工具

    但凡服务器上了一定规模(百台以上),普通的ssh登录管理的模式就越来越举步维艰.试想Linux发布了一个高危漏洞的补丁,你要把手下成百上千台机器都更新该补丁,如果没有一种自动化方式,那么至少要耗上大半 ...

  9. 400&plus;节点的 Elasticsearch 集群运维

    本文首发于InfoQ https://www.infoq.cn/article/1sm0Mq5LyY_021HGuXer 作者:Anton Hägerstrand 翻译:杨振涛 目录: 数据量 版本 ...

随机推荐

  1. 单机多实例Tomcat部署

    单机单用户基础上, 如何运行多个tomcat实例. 首先是tomcat的目录结构 bin    – 包含所有运行tomcat的二进制和脚本文件 lib     – 包含tomcat使用的所有共享库 c ...

  2. Android Studio NDK 学习之接受Java传入的Int数组

    本博客是基于Android Studio 1.3 preview版本,且默认你已经安装了Android SDK, Android NDK. 用Android Studio新建一个工程叫AndroidJ ...

  3. NGINX的奇淫技巧 —— 5&period; NGINX实现金盾防火墙的功能&lpar;防CC&rpar;

    NGINX的奇淫技巧 —— 5. NGINX实现金盾防火墙的功能(防CC) ARGUS 1月13日 发布 推荐 0 推荐 收藏 2 收藏,1.1k 浏览 文章整理中...... 实现思路 当服务器接收 ...

  4. Java内存区域 - 深入Java虚拟机读后总结

    Java虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,有各自的创建时间和销毁时间,有的区域随着虚拟机进程的启动而存在,有的区域则是依赖用户线程的启动 ...

  5. C&num;ArrayList对象集合

    ArrayList alist = new ArrayList(); //集合对像 长度可以改变,类型不限 //添加单个元素可以Add() alist.Add("在在的"); al ...

  6. Python3 下实现 Tencent AI 调用

    1.背景 a.鹅厂近期发布了自己的AI api,包括身份证ocr.名片ocr.文本分析等一堆API,因为前期项目用到图形OCR,遂实现试用了一下,发现准确率还不错,放出来给大家共享一下. b.基于py ...

  7. Linux中jdk的安装和环境变量的配置

    我安装的linux系统版本是RedHat4 64位,jdk版本为jdk-6u10-linux-i586.bin 1.首先找到安装包的位置->进入此文件夹,此bin格式的文件可用./命令直接进行安 ...

  8. VSCode 必装的 10 个高效开发插件 --转

    https://www.cnblogs.com/parry/p/vscode_top_ten_plugins.html

  9. 力扣(LeetCode)1016&period; 子串能表示从 1 到 N 数字的二进制串

    给定一个二进制字符串 S(一个仅由若干 '0' 和 '1' 构成的字符串)和一个正整数 N,如果对于从 1 到 N 的每个整数 X,其二进制表示都是 S 的子串,就返回 true,否则返回 false ...

  10. ubuntu安装nodejs,npm live-server

    sudo apt-get install curl 先安装的是curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/in ...