最近总是需要kubernetes通过ubuntu下载境外镜像,通过自己搭建的ss走代理
如何在linux像mac客户端一样通过终端代理起来,备忘一下

1
2
# apt-get install python-pip
# pip install shadowsocks

编辑文件/etc/shadowsocks.json,填写代理信息:

1
2
3
4
5
6
7
8
9
{ 
"server":"代理服务器ip”,
"server_port":代理服务器端口,
"password":"代理服务器访问密码”,
"local_address":"127.0.0.1”,
"local_port":1080,
"timeout":600,
"method":"aes-256-cfb" // 代理服务器访问数据加密方式,根据自己配置ss 服务端时的配置自行填写
}

开启 关闭

1
2
sudo sslocal -c /etc/shadowsocks.json -d start
sudo sslocal -c /etc/shadowsocks.json -d stop

本地代理跑起来后,我们来配置通过privoxy 8118走1080代理

1
sudo apt-get install privoxy

编辑配置文件/etc/privoxy/config,加入下面两行内容。

1
2
listen-address 127.0.0.1:8118
forward-socks5 / 127.0.0.1:1080 .

开启关闭重启

1
2
3
sudo service privoxy restart
sudo service privoxy stop
sudo service privoxy start

校验

1
2
3
export http_proxy="127.0.0.1:8118"
export https_proxy="127.0.0.1:8118"
$ curl ip.cip.cc


最近沉迷在 Kubernetes部署中不能自拔,作为初学者,基本上把可能踩的坑都踩了一遍,先分享一下怎么部署 Kubernetes 集群
首先,我们要知道 Kubernetes 是什么:

Kubernetes简称为k8s,它是 Google 开源的容器集群管理系统。在 Docker 技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性。

K8s 是一个完备的分布式系统支撑平台,具有完备的集群管理能力,多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和发现机制、內建负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制以及多粒度的资源配额管理能力。同时 K8s 提供完善的管理工具,涵盖了包括开发、部署测试、运维监控在内的各个环节。

安装

这个教程是对三台机器进行 k8s 部署,系统是Ubuntu 16.04.4 LTS。其中一台是 master ,其他两台是 worker。

安装 Docker

添加使用 HTTPS 传输的软件包以及 CA 证书

1
2
3
4
5
6
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common

添加软件源的 GPG 密钥

1
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

向 source.list 中添加 Docker 软件源

1
2
3
4
5
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
$ sudo apt-get update

安装指定版本

1
2
$ apt-cache madison docker-ce
$ sudo apt-get install docker-ce=<VERSION>

在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,Ubuntu 系统上可以使用这套脚本安装:

1
2
$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
安装 kubeadm, kubelet 和 kubectl

官方源

1
2
3
4
5
6
7
8
apt-get update && apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

因为某些你懂的原因,要更换阿里源,并安装kubelet kubeadm kubectl:

1
2
3
4
5
6
7
apt-get update && apt-get install -y apt-transport-https
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
1
2
关闭swap
$ sudo swapoff -a
使用kubeadm创建一个集群

使用 kubeadm 创建 k8s 集群其实还蛮简单,最大的困难是那堵墙,当我费了一整天把那堵墙问题解决后,发现 1.13.0 版本居然提供了中国特供的一个功能,所以把两种方法都写出来,供大家参考。

1.13.0 版本之前

查看kubeadm 会用到哪几个镜像:

1
$ kubeadm config images list

把得到的

1
2
3
4
5
6
7
k8s.gcr.io/kube-apiserver:v1.13.0
k8s.gcr.io/kube-controller-manager:v1.13.0
k8s.gcr.io/kube-scheduler:v1.13.0
k8s.gcr.io/kube-proxy:v1.13.0
k8s.gcr.io/pause:3.1
k8s.gcr.io/etcd:3.2.24
k8s.gcr.io/coredns:1.2.6

写个批量脚本获取替换成阿里云镜像地址拉取images并打回标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
images=(
kube-apiserver:v1.13.0
kube-controller-manager:v1.13.0
kube-scheduler:v1.13.0
kube-proxy:v1.13.0
pause:3.1
etcd:3.2.24
coredns:1.2.6
)
//pull镜像重新标记tag
for imageName in ${images[@]} ; do
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
done

拉取完后
kubeadm init
就可以完成 Kubernetes Master 的部署了

1.13.0版本之后

Kubenetes默认Registries地址是k8s.gcr.io,很明显,在国内并不能访问gcr.io,因此在kubeadm v1.13之前的版本,安装起来非常麻烦,但是在1.13版本中终于解决了国内的痛点,其增加了一个–image-repository参数,默认值是k8s.gcr.io,我们将其指定为国内镜像地址:registry.aliyuncs.com/google_containers,其它的就可以完全按照官方文档来愉快的玩耍了。

1
2
3
4
5
$ kubeadm init \
--image-repository registry.aliyuncs.com/google_containers \
--pod-network-cidr=192.168.0.0/16 \
--ignore-preflight-errors=cri \
--kubernetes-version=1.13.1

kubeadm 会生成一行指令:

1
kubeadm join 10.168.0.2:6443 --token 00bwbx.uvnaa2ewjflwu1ry --discovery-token-ca-cert-hash sha256:00eb62a2a6020f94132e3fe1ab721349bbcd3e9b94da9654cfe15f2985ebd711

并提示如下命令执行确保授权配置

1
2
3
4
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

安装完后,
kubectl get 命令来查看当前唯一一个节点的状态了

1
2
3
4
$ kubectl get nodes

NAME STATUS ROLES AGE VERSION
iz94t4csjq4z NotReady master 82m v1.13.0

其中STATUS 是NotReady
我们通过kubectl describe 来查看这个节点(Node)对象的详细信息、状态和事件(Event)信息

1
2
3
4
5
6
7
8
9
kubectl describe node {NAME}
//$ kubectl describe node iz94t4csjq4z

...
Conditions:
...

Ready False ... KubeletNotReady runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized

通过 kubectl describe 指令的输出,我们可以看到 NodeNotReady 的原因在于,我们尚未部署任何网络插件。
后面我们将专门讲关于部署插件。

另外,我们还可以通过 kubectl 检查这个节点上各个系统 Pod 的状态,其中,kube-system 是 Kubernetes
项目预留的系统 Pod 的工作空间(Namepsace,注意它并不是 Linux Namespace,它只是 Kubernetes 划分
不同工作空间的单位):

1
2
3
4
5
6
7
8
9
10
11
$ kubectl get pods -n kube-system

NAME READY STATUS RESTARTS AGE
coredns-78fcdf6894-j9s52 0/1 Pending 0 1h
coredns-78fcdf6894-jm4wf 0/1 Pending 0 1h
etcd-master 1/1 Running 0 2s
kube-apiserver-master 1/1 Running 0 1s
kube-controller-manager-master 0/1 Pending 0 1s
kube-proxy-xbd47 1/1 NodeLost 0 1h
kube-scheduler-master 1/1 Running 0 1s

可以看到,CoreDNS、kube-controller-manager 等依赖于网络的 Pod 都处于 Pending 状态,即调度失败。这当然是符合预期的:因为这个 Master 节点的网络尚未就绪。

命令

获取端部节点

1
2
3
4
$ kubectl get nodes

NAME STATUS ROLES AGE VERSION
iz94t4csjq4z Ready master 3h57m v1.13.0

查看node详细

1
$ kubectl describe node iz94t4csjq4z

查看全部pod

1
$ kubectl get pods --all-namespaces

通过namespaces获取pod

1
2
3
4
5
6
7
8
9
10
11
12
$ kubectl get pods -n kube-system

NAME READY STATUS RESTARTS AGE
coredns-86c58d9df4-qqddh 1/1 Running 0 3h58m
coredns-86c58d9df4-xxccw 1/1 Running 0 3h58m
etcd-iz94t4csjq4z 1/1 Running 0 3h57m
kube-apiserver-iz94t4csjq4z 1/1 Running 0 3h57m
kube-controller-manager-iz94t4csjq4z 1/1 Running 1 3h57m
kube-proxy-k4zkp 1/1 Running 0 3h58m
kube-scheduler-iz94t4csjq4z 1/1 Running 1 3h57m
kubernetes-dashboard-79ff88449c-dxbsb 1/1 Running 0 3h27m
weave-net-c7dvl 2/2 Running 0 3h49m

查看pod

1
$ kubectl describe pod -n kube-system

查看pod具体详情

1
$ kubectl describe pod kubernetes-dashboard-767dc7d4d-mg5gw -n kube-system
1
2
$ kubectl edit cm coredns -n kube-system
$ kubectl -n kube-system edit configmap coredns

单节点配置( Master 隔离)

默认情况下 Master 节点是不允许运行用户 Pod 的,而 Kubernetes 做到这一点,依靠的是 是 Kubernetes 的 Taint/Toleration 机制。一旦某个节点被加上了一个 Taint,即被“打上了污点”那么所有 Pod 就都不能在这个节点上运行,因为 Kubernetes 的 Pod 都有“洁癖”。
如果你通过 kubectl describe 检查一下 Master 节点的 Taint 字段,就会有所发现了:

1
2
3
4
5
6
$ kubectl describe node master

Name: master
Roles: master
Taints: node-role.kubernetes.io/master:NoSchedule

可以看到,Master 节点默认被加上了node-role.kubernetes.io/master:NoSchedule这样一个“污点”,其中“键”是node-role.kubernetes.io/master,而没有提供“值”。

如果你就是想要一个单节点的 Kubernetes,删除个 Taint 才是正确的选择:

1
$ kubectl taint nodes --all node-role.kubernetes.io/master-

如上所示,我们在“node-role.io/master”这个键后面加上了一个短横线“-”,这个格式就意味着移除所有以“node-role.kubernetes.io/master”为键的 Taint。

这个步骤的配置最终使Master节点能允许运行用户pod,也是确保下面插件部署能正确运行。

到了这一步,一个基本完整的 Kubernetes 集群就完毕了。

部署插件

部署 Dashboard 可视化插件

kubernetes-dashboard先从国内镜像拉下来:

1
2
3
$ docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kubernetes-dashboard-amd64:v1.10.0
$ docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kubernetes-dashboard-amd64:v1.10.0 k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.0
$ docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/kubernetes-dashboard-amd64:v1.10.0

打上kis.gcr.io的tag,这样执行Dashboard拉取的时候就直接本地拿pull下来的直接安装。

1
2
3
4
安装
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml
删除
$ kubectl delete -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml

安装后

1
$ kubectl describe pod kubernetes-dashboard -n kube-system 

可查看Dashboard状态

部署网络插件

部署网络插件非常简单
因为这里的镜像来源不是kis.gcr.io,所以我们就不先拉取镜像下来.

1
2
3
4
安装
$ kubectl apply -f https://git.io/weave-kube-1.6
删除
$ kubectl delete -f https://git.io/weave-kube-1.6

部署完成后,我们可以通过 kubectl get 重新检查Pod的状态:

1
2
3
4
5
6
7
8
9
10
11
12
$ kubectl get pods -n kube-system

NAME READY STATUS RESTARTS AGE
coredns-78fcdf6894-j9s52 1/1 Running 0 1d
coredns-78fcdf6894-jm4wf 1/1 Running 0 1d
etcd-master 1/1 Running 0 9s
kube-apiserver-master 1/1 Running 0 9s
kube-controller-manager-master 1/1 Running 0 9s
kube-proxy-xbd47 1/1 Running 0 1d
kube-scheduler-master 1/1 Running 0 9s
weave-net-cmk27 2/2 Running 0 19s

部署容器存储插件
1
2
3
4
5
6
7
8
9
10
11
12
安装
$ kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/operator.yaml
$ kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/cluster.yaml


删除
$ kubectl delete -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/operator.yaml
$ kubectl delete -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/cluster.yaml

查看安装情况
$ kubectl get pods -n rook-ceph-system
$ kubectl get pods -n rook-ceph

加入工作节点

SSH到其他机器上,成为 root 用户(如:sudo su -),安装 kubeadm, kubelet and kubectl。

然后复制上面的运行kubeadm init命令时最后一句输出,并运行它的:

1
$ kubeadm join --token <token> <master-ip>:<master-port> --discovery-token-ca-cert-hash sha256:<hash>

这时候回到master 节点服务器,运行下面命令查看节点状态:

1
$ kubectl get nodes

NAME STATUS ROLES AGE VERSION
izuf6e4bl8eavupeu7q9a0z Ready 98s v1.13.0
izuf6e4bl8eavupeu7q9a1z Ready master 75m v1.13.0
如果我们忘记了Master节点的加入token,可以使用如下命令来查看:

1
$ kubeadm token list

默认情况下,token的有效期是24小时,如果我们的token已经过期的话,可以使用以下命令重新生成:

1
$ kubeadm token create

如果我们也没有–discovery-token-ca-cert-hash的值,可以使用以下命令生成:

1
$ openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

至此一个简单的 k8s 集群部署就差不多了。

常见问题

Q1:

1
2
2018/11/05 04:04:18 [INFO] plugin/reload: Running configuration MD5 = f65c4821c8a9b7b5eb30fa4fbc167769
2018/11/05 04:04:24 [FATAL] plugin/loop: Seen "HINFO IN 6900627972087569316.7905576541070882081." more than twice, loop detected
1
2
3
4
5
6
7
8
9
配置:--resolv-conf
/etc/systemd/system/kubelet.service.d/10-kubeadm.conf
https://stackoverflow.com/questions/53075796/coredns-pods-have-crashloopbackoff-or-error-state/53414041#53414041
https://stackoverflow.com/questions/52645473/coredns-fails-to-run-in-kubernetes-cluster
https://www.jianshu.com/p/e4dcd56fad38

systemctl daemon-reload && systemctl restart kubelet


Q2:

1
Unable to connect to the server: dial tcp 192.168.1.169:6443: connect: network is unreachable

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

sudo swapoff -a

Q3:

1
2
3
4
5
 	[ERROR DirAvailable--etc-kubernetes-manifests]: /etc/kubernetes/manifests is not empty
[ERROR FileAvailable--etc-kubernetes-kubelet.conf]: /etc/kubernetes/kubelet.conf already exists
[ERROR Port-10250]: Port 10250 is in use
[ERROR FileAvailable--etc-kubernetes-pki-ca.crt]: /etc/kubernetes/pki/ca.crt already exists

首先我们需要了解什么是“MNIST”?

每当我们学习一门新的语言时,所有的入门教程官方都会提供一个典型的例子——“Hello World”。而在机器学习中,入门的例子称之为MNIST。

MNIST是一个简单的视觉计算数据集,它是像下面这样手写的数字图片:

每张图片还额外有一个标签记录了图片上数字是几,例如上面几张图的标签就是:5、0、4、1。

本文将会展现如何训练一个模型来识别这些图片,最终实现模型对图片上的数字进行预测。

首先要明确,我们的目标并不是要训练一个能在实际应用中使用的模型,而是通过这个过程了解如何使用TensorFlow完成整个机器学习的过程。我们会从一个非常简单的模型开始——Softmax回归。

然后要明白,例子对应的源代码非常简单,所有值得关注的信息仅仅在三行代码中。然而,这对于理解TensorFlow如何工作以及机器学习的核心概念非常重要,因此本文除了说明原理还会详细介绍每一行代码的功用。

模型创建

MINIST数据

MINIST数据集的官网是 Yann LeCun’s website。下面这2行代码的作用是从MINIST官网自动下载并读取数据:

1
2
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

MINIST的数据分为2个部分:55000份训练数据(mnist.train)和10000份测试数据(mnist.test)。这个划分有重要的象征意义,他展示了在机器学习中如何使用数据。在训练的过程中,我们必须单独保留一份没有用于机器训练的数据作为验证的数据,这才能确保训练的结果是可以在所有范围内推广的(可泛化)。

前面已经提到,每一份MINIST数据都由图片以及标签组成。我们将图片命名为“x”,将标记数字的标签命名为“y”。训练数据集和测试数据集都是同样的结构,例如:训练的图片名为 mnist.train.images 而训练的标签名为 mnist.train.labels

Kafka

概念名词

kafka [消息列队]
Kafka™ 是一个分布式流处理系统,这是什么意思呢?
我们认为一个流数据平台具有三个主要功能

1.它允许您发布和订阅流记录。在这方面,它类似于一个消息队列或企业消息传递系统。
2.它能让你以容错方式进行流数据的存储。
3.数据产生时你就可以进行流数据处理。

Kafka擅长哪些地方?

它被用于两大类别的应用程序

1.建立实时流数据通道,这个通道可以可靠的获取在系统或应用间的数据。

2.建立实时流媒体应用来转换流数据或对流数据做出反应

首先是几个概念:

kafka作为集群运行在一台或多台服务器。

Kafka群集存储流记录的类别称为主题(topics)

Kafka的每条记录包含一个键,一个值和一个时间戳。

Kafka 有个核心API:

Producer API 允许应用推送流记录到一个或多个Kafka主题上。

Consumer API 允许应用程序订阅一个或多个主题并且并处理产生的流记录

Streams API 允许应用程序作为一个流处理器,从一个或多个主题获取流数据,然后输出流数据到一个或多个主题,有效地将输入流转换为输出流。

Connector API 允许构建和运行可重用的生产者(Producer)
或消费者(Consumer)连接Kafka与现有应用程序或数据系统。例如,一个连接器(connector)在关系数据库中可能获取每个变化的表。

实践

配置
1
2
3
4
5
6
7
8
9
10
11
12
exports.kafka = {
host: 'localhost:9092',
producerConfig: {
// Partitioner type (default = 0, random = 1, cyclic = 2, keyed = 3, custom = 4), default 0
partitionerType: 1,
},
consumerTopics: [{
topic: 'test',
partition: 0,
},
],
};
订阅消费
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
'use strict'

const kafka = require('kafka-node')
module.exports = async app => {
app.beforeStart(async () => {
const ctx = app.createAnonymousContext()
const Producer = kafka.Producer
const client = new kafka.KafkaClient({
kafkaHost: app.config.kafka.host,
})
const producer = new Producer(client, app.config.kafka.producerConfig)
producer.on('error', function(err) {
console.error('ERROR: [Producer] ' + err)
})
producer.on('ready', function() {
app.logger.warn('【启动订阅者成功】')
})
app.producer = producer

const consumer = new kafka.Consumer(client, app.config.kafka.consumerTopics, {
autoCommit: true,
})
consumer.on('message', async function(message) {
app.logger.warn('【收到消息】:', message.value)
try {
// 处理接收到逻辑
// ctx.runInBackground(async () => {
// await ctx.service.log.insert(JSON.parse(message.value))
// })
consumer.commit(true, (err, data) => {
if (err) {
console.error('commit>>err:', err)
} else {
// console.log('commit>>data:', data)
}
})
} catch (error) {
console.error('ERROR: [GetMessage] ', message, error)
}
})
consumer.on('error', function(err) {
console.error('ERROR: [Consumer] ' + err)
})
})
}

发送消息
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
async sendKafka() {
const {
ctx,
app,
service,
} = this;
console.log('发送消息队列->');
const payloads = [{
topic: 'test',
messages: '2 ' + new Date(),
partition: 0,
},
{
topic: 'test',
messages: [ '3 ' + new Date(), '4 ' + new Date() ],
},
];
app.producer.send(payloads, function(err, data) {
console.log('data', data);
if (!err) {
ctx.helper.success({
ctx,
res: data,
});
}
});
}

基本命令

安装:
kafka依赖java环境,因此你可能需要先安装好java环境。

1
2
3
4
5
// mac 环境使用brew直接安装kafka
brew install kafka

// 卸载
brew uninstall kafka

mac 安装kafka过程中会自动的安装好zookeeper。

启动:

1
2
3
4
5
6
7
// 启动
brew services start kafka
brew services start zookeeper

// 重启
brew services restart kafka
brew services restart zookeeper

查看kafaka版本:

1
2
3
4
5
6
7
cd kafka

# 执行以下命令
find ./libs/ -name \*kafka_\* | head -1 | grep -o '\kafka[^\n]*'

drwxrwxr-x. 6 root root 117 May 18 2016 kafka_2.11-0.10.0.0
获得了版本为2.11-0.10.0.0。

前台启动Zookeeper 和kafka

1
2
3
bin/zookeeper-server-start.sh config/zookeeper.properties

bin/kafka-server-start.sh config/server.properties

后台启动Zookeeper 和kafka

1
2
3
4
5
6
7
bin/zookeeper-server-start.sh config/zookeeper.properties 1>/dev/null  2>&1  &

bin/kafka-server-start.sh config/server.properties 1>/dev/null 2>&1 &

bin/zookeeper-server-start.sh -daemon config/zookeeper.properties

bin/kafka-server-start.sh -daemon config/server.properties

其中1>/dev/null 2>&1 是将命令产生的输入和错误都输入到空设备,也就是不输出的意思。/dev/null代表空设备。

关闭命令

1
2
3
bin/zookeeper-server-stop.sh config/zookeeper.properties

bin/kafka-server-stop.sh config/server.properties

kafka配置说明:
配置文件server.properties

1
2
3
4
5
// mac 电脑配置文件地址
vim /usr/local/etc/kafka/server.properties

// Linux 配置文件地址
vim /usr/src/kafka/config/server.properties

备注: 尽量配置host.name, 例如本地的配置

1
2
host.name = 127.0.0.1
port=9092

常用基本命令:
创建一个主题(topic)

1
2
3
4
5
// mac
kafka-topics --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test

// linux
bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test

删除topic

1
2
3
4
5
// mac 
kafka-topics --delete --zookeeper localhost:2181 --topic 【topic name】

// linux
bin/kafka-topics.sh --delete --zookeeper localhost:2181 --topic 【topic name】

查看创建的topic list

1
2
3
4
5
// mac
kafka-topics --list --zookeeper localhost:2181

// linux
bin/kafka-topics.sh --list --zookeeper localhost:2181

查看详情

1
2
// linux
bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic

生产消息

1
2
3
4
5
// mac 
kafka-console-producer --broker-list localhost:9092 --topic test

// linux
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test

消费消息

1
2
3
4
5
// mac 
kafka-console-consumer --bootstrap-server localhost:9092 --topic test --from-beginning

// linux
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning

zookeeper

安装
1
sudo apt-get install zookeeper

默认信息

1
2
3
4
#安装路径
/usr/share/zookeeper
#配置文件
/etc/zookeeper/conf/zoo.cfg
卸载
1
2
3
4
sudo apt-get remove zookeeper
sudo apt-get remove --auto-remove zookeeper
sudo apt-get purge zookeeper
sudo apt-get purge --auto-remove zookeeper

安装

1
2
npm install xlsx --save
npm install file-saver --save
使用
1
2
import XLSX from 'xlsx';
import FileSaver from 'file-saver';

概念名词

workbook [excel文档]
首先是workbook,可以把它看作是一个excel文档。
我们要用xlsx导出excel,就是要按规范创建workbook
一个简单的workbook对象是这样的

1
2
3
4
const workbook = {
SheetNames: [], //工作表名数组
Sheets: {} //工作表对象 键名对应SheetNames的key
}

sheet [工作表]
从workbook对象下面延伸出来的是sheet对象,很容易理解,它对应这个excel文档下面的工作表
一个workbook可以有多个sheet,就像下面这个对象这样,等于是有三个工作表的excel文档
sheet的常用配置项:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const workbook = {
SheetNames: [ //工作表名数组
'Sheet1', 'Sheet2', 'Sheet2',
],
Sheets: { //工作表对象
Sheet1: { //工作表1
'!ref': 'A1:C2', //工作表的范围 如 必须 否则不显示
'!merges': [ //工作表单元格合并配置项 可选
{
s: { //s start 开始
c: 1,//cols 开始列
r: 0 //rows 开始行
},
e: {//e end 结束
c: 4,//cols 结束列
r: 0 //rows 结束行
}
}
],
'!cols': [ //工作表列宽配置项 可选
{
/* visibility */
hidden? : boolean, // if true, the column is hidden
/* column width is specified in one of the following ways: */
wpx? : number, // width in screen pixels
width? : number, // width in Excel's "Max Digit Width", width*256 is integral
wch? : number, // width in characters
/* other fields for preserving features from files */
MDW? : number, // Excel's "Max Digit Width" unit, always integral
}
],
'!rows': [ //工作表列高配置项 可选
{
/* visibility */
hidden? : boolean, // if true, the row is hidden
/* row height is specified in one of the following ways: */
hpx? : number, // height in screen pixels
hpt? : number, // height in points
level? : number, // 0-indexed outline / group level
}
],
},
Sheet2: {}, //工作表2
Sheet3: {} //工作表3
}
}

cell [单元格]
从sheet 对象下面延伸出来的是cell 对象,对应的是工作表下的单元格,cell 对象键名是A1,B1这样与excel一致的键名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const workbook = {
FileName: 'export.xlsx',
SheetNames: ['Sheet1'],
Sheets: {
Sheet1:{
'!ref': 'A1:B2',
A1:{},
A2:{},
B1:{},
B2:{}
}
}
}

js-xlsx提供了一个方法XLSX.utils.encode_cell来转换键名

1
2
3
4
XLSX.utils.encode_cell({
c: 0, //cols 列
r: 0 //rows 行
};

知道相应的行列值就能方便的转换成键名了,需要注意的是行列是从0开始不是从1开始

1
2
3
4
5
XLSX.utils.encode_cell({c: 0, r: 0});//A1
XLSX.utils.encode_cell({c: 0, r: 1});//A2
XLSX.utils.encode_cell({c: 1, r: 0});//B1
XLSX.utils.encode_cell({c: 1, r: 1});//B2
XLSX.utils.encode_cell({c: 100, r: 100});//CW101
cell单元格对象的配置项
键名 作用
v 初始值 (请参见数据类型t部分)
w 格式化文本 (如果适用)
t 单元格数据类型: b Boolean, n Number, e error, s String, d Date
f 单元格编码为A1样式的字符串(如果适用)
F 如果公式为数组公式,则包含数组的范围(如适用)
r 富文本编码(如适用)
h HTML 富文本呈现(如适用)
c 单元格注释
z 与单元格关联的数字格式字符串(如果请求)
l 单元格超链接对象(.Target holds link, .Tooltip is tooltip)
s 单元格的样式/主题(如果适用)
XLSX.write(workbook, options) 生成excel数据

第一个参数传入写好的workbook,第二个参数是配置项

write options 配置项
键名 默认值 描述
type 输出数据编码(见下表Output Type)
cellDates false 存储日期为d类型(默认为n)
bookSST false 生成共享字符串表
bookType “xlsx” 文档格式类型
sheet “” 单页格式的工作表名称
compression false 对基于ZIP的格式使用ZIP压缩
Props 重写工作簿时重写工作簿属性
themeXLSX Override theme XML when writing XLSX/XLSB/XLSM
Output Type
type output
“base64” string: Base64 encoding of the file
“binary” string: binary string (byte n is data.charCodeAt(n))
“string” string: JS string (characters interpreted as UTF8)
“buffer” nodejs Buffer
“file” string: path of file that will be created (nodejs only)

感觉上面学习的内容已经足够导出一个简单的excel了,我先来试下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const workbook = {
SheetNames: ['Sheet1'],
Sheets: {
Sheet1: {
'!ref': 'A1:C2',
A1: {v: '标题1', t: 's'},
B1: {v: '标题2', t: 's'},
A2: {v: '第2行第1列', t: 's'},
B2: {v: '第2行第2列', t: 's'},
}
}
};

const workbookOut = XLSX.write(workbook, {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
});

FileSaver.saveAs(new Blob([s2ab(workbookOut)], {
type: 'application/octet-stream'
}), 'export.xlsx')

运行 顺利生成export.xlsx

前提:

1.服务器:阿里云OSS
2.前端:vue或小程序
3.后端:egg

一、上传概述

场景:小程序+阿里云OSS
现在阿里云上传权限分两类 RAM和STS,所以针对两者的上传方式进行一次全解析。

二、上传文件的几种方式

2.1.客户端直传:

通过客户端直接签名的方式,进行直传,具体见例子: web前端直传,文档中已经讲的很详细了,这里就不赘述了,我们说一下它的缺点,缺点很明显:就是因为账户和秘钥都保存在js中,而用户可以通过前端debug(按F12)查阅到,秘钥完全暴露非常不安全。

2.2.上传到服务器再通过服务器直传

当然这种情况不在我们的考虑范围内,今天主要讲WEB直传的方式,但是基于node我们还是提一下。我在查询资料的时候,发现有人先把文件上传到服务器,再通过服务器后端直传到OSS系统,然后再把服务器上的文件删除。详见上传文件

这样的确会让上传安全很多,但是随之而来的问题就是会加大服务器的压力,每一次上传都要删除本地的文件。很占用带宽,如果上传量大的时候,容易出现问题。

2.3.服务器端签名后直传

这种情况相对于上一种,较安全,秘钥是保存在服务器端。通过服务器向阿里云请求获取签名(临时token)返回给前端,签名正确后直传OSS系统。这种情况下相较于前两种方式是比较好的。既可以保证安全性,又可以减轻服务器端的压力。

三、OSS实践

为什么要自动化部署,我这里就不多赘述了。

基于shipit-deploy的自动化部署,可以实现以下效果

一键部署多台服务器。
一键回滚多台服务器。
本地操作,不需要登录服务器。
方便定制,方便扩展,实现自动化。

使用

1.下载安装

1
2
npm install --save-dev shipit-cli
npm install --save-dev shipit-deploy

2.与服务器建立信任关系

1
ssh-copy-id USER@REMOTE_HOST

3.配置

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
module.exports = function (shipit) {
require('shipit-deploy')(shipit);
require('shipit-pm2')(shipit);
require('shipit-cnpmjs')(shipit);

shipit.initConfig({
default: {
workspace: '/tmp/github-monitor',
deployTo: '/tmp/deploy_to', //服务器的目标路径
repositoryUrl: 'https://github.com/user/repo.git', //git仓库地址
ignores: ['.git', 'node_modules'], //排除的文件
keepReleases: 2, //发布保留的版本数量
deleteOnRollback: false,
key: '/path/to/key',
shallowClone: true,
cnpm: {
remote: false
}
},
dev: { //开发服务器部署
servers: ['user@devServer1', 'user@devServer1'],
branch: 'dev' //需要发布的git分支,
pm2: {
json: 'pm2-dev-app.json' //开发环境的pm2启动配置
}
},
prod: { //生产服务器部署
servers: ['user@prodServer1', 'user@prodServer2'],
branch: 'master' //需要发布的git分支,
pm2: {
json: 'pm2-prod-app.json' //生产jam环境的pm2启动配置
}
}
});
};
pm2-prod-app.json 示例:

{
"apps": [
{
"name": "frontend_name",
"script": "app.js",
"args": "--env=production",
"instances": 1,
"cwd": "/tmp/production_path/current",
"env": {
"NODE_ENV": "production",
"PORT": "9001"
}
}
]
}

当然,我们也可以使用以下脚本来启动项目

1
pm2 startOrRestart pm2-prod-app.json

发布

1
2
3
4
5
shipit dev deploy //开发环境发布
shipit dev rollback //回滚

shipit prod deploy //生产环境发布
shipit prod rollback //回滚

1. 生成公钥和私钥

ssh-keygen -t rsa
按照提示输入完后,会在~/.ssh目录下生成id_rsa和id_rsa.pub这两个文件

2.与服务器建立联系

ssh-copy-id root@192.168.0.100 //示例ip
接下来会要求输入连接密码,验证成功后

3.无密码登陆服务器

尝试以下命令,看是不是直接登陆成功了:
ssh root@192.168.0.100
就是这么简单

include 不对parent造成过滤

1
2
3
4
5
6
7
8
9
10
11
12
include: [
{
attributes: ['id'],
model: models.Post,
where: {
isActive: 1
},
required: false
// 解决的代码就是这一行,加上 required: false.
//默认required=true,include 会去对 parent 造成过滤
}
]

findAllAndCount在有include的时候count条数不对


Sequelize查询的数据表只有10条数据,但查询结果是20,只要不加include条件就是正确的10,是带上了include中的model表的数据的10条

1
2
distinct:true 
//这一句可以去重,它返回的 count 不会把你的 include 的数量算进去

说明

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
31
const res = await ctx.model.Guarantee.findAll({
// where,
// where: {
// guarantee_num: '111',
// '$project.project_name$': '龙岗街道育贤小学教学楼等楼宇整治改造工程'
// },
where: {
guarantee_num: '111',
// '$project.project_name$': '龙岗街道育贤小学教学楼等楼宇整治改造工程'
},
limit,
offset,
include: [{
model: ctx.model.Project,
include: [{
//展示所有嵌套
all: true,
//深度递归嵌套,出来嵌套内容
// nested: true
}],
}],
// 用于返回唯一不同的值
distinct: true,
//如果 plain 是 TRUE ,则 sequelize 将只返回结果集的第一条记录。如果是 FALSE, 则是全部记录。
plain: false,
// 设置为 true,即可返回源数据
raw: false,
order: [
['created_at', 'DESC']
]
})

前提:

1.谷歌云
2.node环境、ubuntu

安装node环境

1
2
3
sudo apt install npm
sudo npm install n -g
sudo n stable

安装 shadowsocks

1
npm install -g shadowsocks

找到shadowsocks配置文件路径

linux环境下缺省shadowsocks配置文件路径
/usr/local/lib/node_modules/shadowsocks/config.json

修改配置config.json

1
2
3
4
5
6
7
8
{  
"server":"0.0.0.0",#更改服务器IP
"server_port":9000, #更改连接端口
"local_port":1080,
"password":"pass",#更改密码
"timeout":600,
"method":"aes-256-cfb"
}

启动shadowsocks

1
$ pm2 start ssserver
0%