Jim

Talk is cheap. Show me the code.


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

  • 搜索

Vagrant 多网卡环境下 flannel 网络插件导致 DNS 无法解析

发表于 2019-08-08 | 分类于 Kubernetes

之前写过一篇 k8s 集群自动化部署的文章:「Kubeadm 结合 Vagrant 自动化部署最新版 Kubernetes 集群」,发现集群启动后 DNS 无法解析,公网和集群内部都无法解析,具体问题表现是:进入 pod 执行 ping service 名称或者公网域名都是无法解析 Unknow host。

经过网上搜索一番找到了问题并得以解决,主要原因是 Vagrant 在多主机模式下有多个网卡,eth0 网卡用于 nat 转发访问公网,而 eth1 网卡才是主机真正的 IP,在这种情况下直接部署 k8s flannel 插件会导致 CoreDNS 无法工作。解决方法很简单,调整下 flannel 的启动参数,加上 - --iface=eth1 网卡参数:
vim https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
- --kube-subnet-mgr 后面加一行参数:- --iface=eth1

1
2
3
4
5
6
7
8
9
10
11
......
containers:
- name: kube-flannel
image: quay.io/coreos/flannel:v0.11.0-amd64
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
- --iface=eth1
......

参考文档:
https://blog.frognew.com/2019/07/kubeadm-install-kubernetes-1.15.html#2-3-%E5%AE%89%E8%A3%85pod-network | 使用kubeadm安装Kubernetes 1.1
https://github.com/kubernetes/kubeadm/issues/1056 | github issue 讨论

helm 部署 kubernetes-dashboard

发表于 2019-08-08 | 分类于 Kubernetes

kubernetes-dashboard 是 k8s 官方提供的集群 Web UI,可以查看集群详细的信息,比如集群的 api 资源,pod 日志,工作负载,节点资源利用率等等。在部署 kubernetes-dashboard 前需要先安装 heapster ,heapster 用于收集数据,而 dashboard 是展示数据的界面。关于 heapster 的安装见之前文章「helm 部署 heapster 组件」。如果没有部署 heapster 组件,kubernetes-dashboard 的 pod 会报错:

2019/08/06 10:30:06 Metric client health check failed: the server could not find the requested resource (get services heapster). Retrying in 30 seconds.


使用官方提供的 Chart:https://github.com/helm/charts/tree/master/stable/kubernetes-dashboard
对 values 文件进行一些定制:

  • docker 镜像地址改为阿里云镜像地址,国内访问不了默认的镜像地址;
  • service 类型改为 NodePort,并指定 nodePort 端口为 30000;

定制后的 values 文件:https://raw.githubusercontent.com/qhh0205/helm-charts/master/kube-component-values/kube-dashboard.yml

helm 部署:

1
helm install stable/kubernetes-dashboard  --name kubernetes-dashboard -f  https://raw.githubusercontent.com/qhh0205/helm-charts/master/kube-component-values/kube-dashboard.yml --namespace kube-system

创建集群服务账号:admin

1
kubectl create -f https://raw.githubusercontent.com/qhh0205/helm-charts/master/some-apiserver-rs/admin-sa.yml

获取 dashboard 访问 token:

1
kubectl get secret `kubectl get secret -n kube-system | grep admin-token | awk '{print $1}'` -o jsonpath={.data.token} -n kube-system | base64 -d

helm 部署 heapster 组件

发表于 2019-08-08 | 分类于 Kubernetes

之前工作用的 k8s 集群(GKE)都是支持 kubectl top node 查看节点资源使用情况的,最近自己本地新搭的集群发现用不了该命令。网上搜索了下发现是由于缺少集群指标收集组件导致,目前常用的集群指标收集组件是 heapster 和 metrics-server,看官方介绍 heapster 要逐渐被淘汰了,更推荐 metrics-server。但是为了适配后续要安装的 kubernetes-dashboard,先使用 heapster 组件。

heapster 组件是 Kubernetes 官方支持的容器集群监控组件,主要用于收集集群指标数据并存储,收集的数据可以对接可视化图表展示分析,比如 grafana、kubernetes-dashboard。

除了 kubectl top node 依赖于 heapster 收集的指标,kubernetes-dashboard 也需要 heapster,本文使用 helm 来一键部署 heapster 组件。

heapster Chart 使用 helm 官方提供的:https://github.com/helm/charts/tree/master/stable/heapster
对 values 文件做一些定制:

  • 使用阿里云镜像仓库,Chart 默认使用 k8s.gcr.io,国内是拉不下来镜像的;
  • rbac 服务账号使用 admin,使用默认的 default 账号权限太小,收集指标时报错。
  • heapster 启动参数 –source 调整成:kubernetes:https://kubernetes.default:443?useServiceAccount=true&kubeletHttps=true&kubeletPort=10250&insecure=true,否则 heapster 会报错:

    E0806 22:11:05.017143 1 manager.go:101] Error in scraping containers from kubelet_summary:192.168.26.10:10255: Get http://192.168.26.10:10255/stats/summary/: dial tcp 192.168.26.10:10255: getsockopt: connection refused

    报错主要原因是 k8s 1.12.0 以后已经取消了 kubelet 10255 端口:

    1
    2
    3
    4
    5
     --read-only-port int32    
    The read-only port for the Kubelet to serve on with no authentication/authorization
    (set to 0 to disable) (default 10255) (DEPRECATED:
    This parameter should be set via the config file specified by the Kubelet's --config flag.
    See https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/ for more information.)

    详情见:https://sealyun.com/post/heapster-error/

定制后的 values 文件在这里:https://raw.githubusercontent.com/qhh0205/helm-charts/master/kube-component-values/heapster-values.yml

创建集群服务账号:admin,heapster 使用 admin 服务账号,确保有足够权限

1
kubectl create -f https://raw.githubusercontent.com/qhh0205/helm-charts/master/some-apiserver-rs/admin-sa.yml

接下来使用 helm 安装部署 heapster:

1
helm install stable/heapster --name heapster -f https://raw.githubusercontent.com/qhh0205/helm-charts/master/kube-component-values/heapster-values.yml --namespace kube-system

heapster 安装完后就可以正常使用 kubectl top node 了:

1
2
3
4
[root@master ~]# kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master 141m 7% 1096Mi 63%
node1 45m 2% 821Mi 47%

如果 heapster pod 有问题一般会报下面错误:

1
2
[root@master ~]# kubectl top node
error: metrics not available yet

Helm 安装使用

发表于 2019-08-08 | 分类于 Kubernetes

其实 Helm 的安装很简单,之所以单独写这篇文章主要是因为国内网络原因导致 helm 使用存在障碍(防火墙对 google 不友好),本文重点说如何解决这一问题。

helm 安装

官方提供了一件安装脚本,安装最新版:https://helm.sh/docs/using_helm/#installing-helm

1
curl -L https://git.io/get_helm.sh | bash

创建服务账号和角色绑定

rbac-config.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system

1
kubectl create -f rbac-config.yaml

helm init 初始化

国内无法访问 gcr.io 仓库,指定阿里云镜像仓库,同时指定前面创建的服务账号:

1
helm init --service-account tiller -i registry.aliyuncs.com/google_containers/tiller:v2.14.3

国内也无法访问 helm 默认的 Chart 仓库,所以也改成阿里云 Chart 镜像仓库:

1
2
helm repo remove stable
helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

Kubeadm 结合 Vagrant 自动化部署最新版 Kubernetes 集群

发表于 2019-08-06 | 分类于 Kubernetes

之前写过一篇搭建 k8s 集群的教程:「使用 kubeadm 搭建 kubernetes 集群」,教程中用到了 kubeadm 和 vagrant,但是整个过程还是手动一步一步完成:创建节点--> 节点配置、相关软件安装 --> 初始化 master 节点 --> node 节点加入 master 节点。其实这个过程完全可以通过 Vagrant 的配置器自动化来实现,达到的目的是启动一个 k8s 只需在 Vagrant 工程目录执行:vagrant up 即可一键完成集群的创建。

本文主要介绍如何使用 Kubeadm 结合 Vagrant 自动化 k8s 集群的创建,在了解了 kubeadm 手动搭建 kubernetes 集群的过程后,自动化就简单了,如果不了解请参见之前的文章:「使用 kubeadm 搭建 kubernetes 集群」,梳理下整个过程,在此不做过多介绍。下面大概介绍下自动化流程:

  1. 首先抽象出来每个节点需要执行的通用脚本,完成一些常用软件的安装、docker 安装、kubectl 和 kubeadm 安装,还有一些节点的系统级配置:
    具体实现脚本见:https://github.com/qhh0205/kubeadm-vagrant/blob/master/install-centos.sh

  2. 编写 Vagrantfile,完成主节点的初始化安装和 node 节点加入主节点。但是有个地方和之前手动安装不太一样,为了自动化,我们必须在 kubeadm 初始化 master 节点之前生成 TOKEN(使用其他任意主机的 kubeadm 工具生成 TOKEN 即可),然后自动化脚本统一用这个 TOKEN 初始化主节点和从节点加入。Vagrantfile 具体实现见:https://github.com/qhh0205/kubeadm-vagrant/blob/master/Vagrantfile

完整的 Vagrant 工程在这里:https://github.com/qhh0205/kubeadm-vagrant
使用 kubeadm + vagrant 自动化部署 k8s 集群,基于 Centos7 操作系统。该工程 fork 自 kubeadm-vagrant, 对已知问题进行了修复:节点设置正确的 IP 地址「set-k8s-node-ip.sh」。否则使用过程中会出现问题,具体问题见这里:「kubeadm + vagrant 部署多节点 k8s 的一个坑」。其他一些调整:节点初始化脚本更改、Vagrantfile 添加 Shell 脚本配置器,运行初始化脚本。

默认:1 个 master 节点,1 个 node 节点,可以根据需要修改 Vagrantfile 文件,具体见工程 README.md 说明。

kubeadm + vagrant 部署多节点 k8s 的一个坑

发表于 2019-08-06 | 分类于 Kubernetes

之前写过一篇「使用 kubeadm 搭建 kubernetes 集群」教程,教程里面使用 Vagrant 启动 3 个节点,1 个 master,2 个 node 节点,后来使用过程中才慢慢发现还是存在问题的。具体问题表现是:

  1. kubectl get node -o wide 查看到节点 IP 都是:10.0.2.15;

    1
    2
    3
    4
    [root@master ~]# kubectl get node -o wide
    NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
    master Ready master 5m11s v1.15.1 10.0.2.15 <none> CentOS Linux 7 (Core) 3.10.0-862.2.3.el7.x86_64 docker://19.3.1
    node1 Ready <none> 2m31s v1.15.1 10.0.2.15 <none> CentOS Linux 7 (Core) 3.10.0-862.2.3.el7.x86_64 docker://19.3.1
  2. kubect get pod 可以查看 pod,pod 也运行正常,但是无法查看 pod 日志,也无法 kubectl exec -it 进入 pod。具体报错如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [root@master ~]# kubectl get pod
    NAME READY STATUS RESTARTS AGE
    nginx-deployment-5754944d6c-gnqjg 1/1 Running 0 66s
    nginx-deployment-5754944d6c-mxgn7 1/1 Running 0 66s

    [root@master ~]# kubectl logs nginx-deployment-5754944d6c-gnqjg
    Error from server (NotFound): the server could not find the requested resource ( pods/log nginx-deployment-5754944d6c-gnqjg)

    [root@master ~]# kubectl exec -it nginx-deployment-5754944d6c-gnqjg sh
    error: unable to upgrade connection: pod does not exist

带着上面两个问题,于是网上搜索一番,找到了根因并得以解决:
https://github.com/kubernetes/kubernetes/issues/60835

主要原因

Vagrant 在多主机模式时每个主机的 eth0 网口 ip 都是 10.0.2.15,这个网口是所有主机访问公网的出口,用于 nat 转发。而 eth1才是主机真正的 IP。kubelet 在启动时默认读取的是 eth0 网卡的 IP,因此在集群部署完后 kubect get node -o wide 查看到节点的 IP 都是 10.0.2.15。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[vagrant@master ~]$ ip addr
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:c9:c7:04 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0
valid_lft 85708sec preferred_lft 85708sec
inet6 fe80::5054:ff:fec9:c704/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 08:00:27:25:1b:45 brd ff:ff:ff:ff:ff:ff
inet 192.168.26.10/24 brd 192.168.26.255 scope global noprefixroute eth1
valid_lft forever preferred_lft forever
inet6 fe80::a00:27ff:fe25:1b45/64 scope link
valid_lft forever preferred_lft forever
...

解决方法

上面知道问题的根本原因是 k8s 节点 IP 获取不对导致访问节点出现问题,那么解决方法就是调整 kubelet 参数设置正确的
IP 地址:
编辑 /etc/sysconfig/kubelet 文件,KUBELET_EXTRA_ARGS 环境变量添加 --node-ip 参数:

1
KUBELET_EXTRA_ARGS="--node-ip=<eth1 网口 IP>"

然后重启 kubelet:systemctl restart kubelet
执行 kubectl get node -o wide 发现节点 IP 已经改变成了KUBELET_EXTRA_ARGS 变量指定的 IP。

1
2
3
4
[root@master ~]# kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master Ready master 24m v1.15.1 192.168.26.10 <none> CentOS Linux 7 (Core) 3.10.0-862.2.3.el7.x86_64 docker://19.3.1
node1 Ready <none> 21m v1.15.1 10.0.2.15 <none> CentOS Linux 7 (Core) 3.10.0-862.2.3.el7.x86_64 docker://19.3.1

用同样的方法修改其他节点 IP 即可。
为了方便,这里提供了一个命令,自动化上面步骤:

1
2
echo KUBELET_EXTRA_ARGS=\"--node-ip=`ip addr show eth1 | grep inet | grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}/" | tr -d '/'`\" > /etc/sysconfig/kubelet
systemctl restart kubelet

Jenkins 集成 allure 测试报告工具

发表于 2019-07-25 | 分类于 DevOps

allure 基于已有的测试报告数据进行进一步的加工,美化等操作,相当于做了一次数据格式转换。allure 支持多种语言的多种测试框架,比如 Java 的 jUnit4、jUnit5、TestNg 等等。

本文主要介绍如何在 Jenkins 中集成 allure 测试报表工具,在每次项目自动化测试完成后,用 allure 生成经过加工后的测试报告。我们以 java 工程的 TestNg 测试为例,处理 TestNg 生成的测试报告。

  • Jenkins 安装 allure 插件
    全局工具配置:

  • Jenkinsfile 添加 allure 代码

    1
    2
    3
    script {
    allure jdk: '', report: "target/allure-report-unit", results: [[path: "target/surefire-reports"]]
    }

    target/allure-report-unit 参数:allure 报告生成路径;
    target/surefire-reports 参数:测试报告原始路径;

  • Jenkins 平台查看 allure 报告
    在 allure 成功集成到 Jenkins 后,allure 每次处理完成,在 Jenkins job 页面都可以看到 allure 的图标,点击图标即可查看报告详细信息:

遇到问题及解决方法

问题:
allure 在 Jenkins pipline 中生成报表时报目录权限问题:java.nio.file.AccessDeniedException

原因:
jenkins k8s pod 执行 job 时默认用户为 jenkins,但是 pipeline 中调用的容器生成的文件的属主是 root。
解决方法:
配置 jenkins k8s 插件模版,添加安全配置,运行用户设置为 root
https://groups.google.com/forum/#!topic/jenkinsci-users/GR0n8ZkCJ-E

pod 配置(spec 下):

1
2
3
securityContext:
runAsUser: 0
fsGroup: 0

参考文档

https://docs.qameta.io/allure/

使用 Go 开发命令行应用

发表于 2019-07-24 | 分类于 Go

作为一个程序员,命令行工具是我们再熟悉不过的了,我们每天或多或少都会用到命令行工具。比如项目构建、打包、启动等等。那么如何用 Go 语言编写类似的工具呢?调研了下,大概有下面三种方法:

  • os.Args 函数
    os.Args 功能类似于 Shell 脚本的 $@ 功能,获取到命令行输入,然后进行人工解析处理,这种方式对于编写简单的工具还行,对于复杂点的工具,光解析输入参数就是一场”灾难”了。
  • 使用 Go 标准 flag 包
    flag 包是 Golang 官方提供的命令行参数解析包,省去人工解析工作,对于构建一个一般功能的命令行应用足够了,但是对于复杂应用还是显得比较麻烦。
  • 使用第三方包 cli 或者 cobra
    使用第三方包就比较专业了,提供的功能更加丰富,使用起来也很顺手。目前对于 Golang 命令行应用开发业界比较主流的就是 cli 和 cobra 了。其实两者的流行度差不多,cobra 的学习成本稍微高点,而且更加专业,看文档介绍,Kubernetes、Moby、rkt、etcd 等都是基于 cobra 构建。我个人更推荐使用 cli,因为 cli 比较轻量级、容易学习,使用起来也更加得心应手,对于开发日常应用足够了。

本文主要介绍后面两种方式,即 flag 包和第三方包 cli 的使用,对于 cobra 的使用这里不做具体介绍。

使用 flag 包构建命令行应用

flag 包的使用很简单,能很方便地解析命令行输入,支持的命令行参数类型有 bool, int, uint, string, time.Duration, float 类型,另外还可以自定义类型。这里介绍下一般的使用方法,假如要开发一个命令行工具,使用方式:

1
./go-curl -v -X "GET" https://example.com

具体实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
// 定义一个 bool 类型的参数,默认值为 false,第三个参数为 Usage 说明
// 函数返回值为对应类型的指针
v := flag.Bool("v", false, "Makes curl verbose during the operation.")

// 定义一个 String 类型的参数,默认值为 GET,第三个参数为 Usage 说明
// 与上面那种不同的是函数第一个参数为变量的指针
var X string
flag.StringVar(&X, "X", "GET", "(HTTP) Specifies a custom request method to use when communicating with the HTTP server.")

// 在参数定义完后必须调用 flag.Parse() 完成命令行参数的解析
flag.Parse()

// 返回其余参数的列表
args := flag.Args()
fmt.Printf("v: %t, X: %s, args: %v\n", *v, X, args)
os.Exit(0)
}

可以看出定义参数有两种方式:

  • 指针类型参数:调用函数为 flag.Type 形式,返回值为对应类型的指针;
  • 值类型参数:调用函数为 flag.TypeVar 形式,调用时传递变量的指针;

两种方法效果都是一样的,只不过一种解析返回的是指针,另一种是直接使用变量。

使用 cli 包构建命令行应用

使用 cli 包开发命令行工具能省很多事,而且写出来的代码结构非常清晰,很容易理解。具体使用见代码仓库 README.md 。在这里举一个例子,我最近写的服务部署命令行工具:https://github.com/qhh0205/deploy-kit 通过看官方文档结合这个例子能很容易掌握 cli 包的是使用技巧。下面为该部署工具的 --help 输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NAME:
deploy - deploy application

USAGE:
deploy [global options] command [command options] [arguments...]

VERSION:
v1.0

COMMANDS:
list, ls list all of services
app deploy microservice application
web deploy web application
lsbranch, lsb list the code branches of service
upload-cdn, upcdn upload file or directory to gcs bucket
help, h Shows a list of commands or help for one command

GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version

该工具基于 cli 包构建,cli 包的使用核心是通过 app.Commands = []cli.Command{} 定义一些列命令、选项,并且通过 Action 绑定对应选项参数的处理函数。

参考文档

https://github.com/urfave/cli | cli GitHub
https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter13/13.1.html | flag 包介绍
https://blog.yumaojun.net/2016/12/30/go-cobra/ | cobra 使用,如何使用golang编写漂亮的命令行工具
https://blog.rapid7.com/2016/08/04/build-a-simple-cli-tool-with-golang/ | Building a Simple CLI Tool with Golang
https://medium.com/what-i-talk-about-when-i-talk-about-technology/dealing-with-command-line-options-in-golang-flag-package-e5fb6ef1a79e | Dealing with Command Line Options in Golang: flag package

Golang 协程顺序打印

发表于 2019-07-23 | 分类于 Go

A、B 两个协程分别打印 1、2、3、4 和 A,B,C,D

实现:定义 A、B 两个 channal,开 A、B 两个协程,A 协程输出[1, 2, 3, 4]、B 协程输出[A, B, C, D],通过两个独立的 channal 控制顺序,交替输出。

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
func main() {
A := make(chan bool, 1)
B := make(chan bool)
Exit := make(chan bool)
go func() {
s := []int{1, 2, 3, 4}
for i := 0; i < len(s); i++ {
if ok := <-A; ok {
fmt.Println("A: ", s[i])
B <- true
}
}
}()
go func() {
defer func() {
close(Exit)
}()
s := []byte{'A', 'B', 'C', 'D'}
for i := 0; i < len(s); i++ {
if ok := <-B; ok {
fmt.Printf("B: %c\n", s[i])
A <- true
}
}
}()
A <- true
<-Exit
}

A、B 两个协程顺序打印 1~20

实现:与上面基本一样,定义 A、B 两个 channal,开 A、B 两个协程,A 协程输出奇数、B 协程输出偶数,通过两个独立的 channal 控制顺序,交替输出。

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
package main

import "fmt"

func main() {
A := make(chan bool, 1)
B := make(chan bool)
Exit := make(chan bool)

go func() {
for i := 1; i <= 10; i++ {
if ok := <-A; ok {
fmt.Println("A = ", 2*i-1)
B <- true
}
}
}()
go func() {
defer func() {
close(Exit)
}()
for i := 1; i <= 10; i++ {
if ok := <-B; ok {
fmt.Println("B : ", 2*i)
A <- true
}
}
}()

A <- true
<-Exit
}

Go 语言 exec 实时获取外部命令的执行输出

发表于 2019-07-20 | 分类于 Go

在 Go 语言中调用外部 Linux 命令可以通过标准的 os/exec 包实现,我们一般的使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
"os/exec"
)

func main() {
cmd := exec.Command("ls", "-al")
output, _ := cmd.CombinedOutput()
fmt.Println(string(output))
}

上面这种使用方式虽然能获取到外部命令的执行结果输出 output,但是必须得命令执行完成后才将获取到的结果一次性返回。很多时候我们是需要实时知道命令执行的输出,比如我们调用一个外部的服务构建命令: mvn build,这种情况下实时输出命令执行的结果对我们来说很重要。再比如我们 ping 远程 IP,需要知道实时输出,如果直接 ping 完在输出,在使用上来说体验不好。

要实现外部命令执行结果的实时输出,需要使用 Cmd 结构的 StdoutPipe() 方法创建一个管道连接到命令执行的输出,然后用 for 循环从管道中实时读取命令执行的输出并打印到终端。具体代码如下:

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
func RunCommand(name string, arg ...string) error {
cmd := exec.Command(name, arg...)
// 命令的错误输出和标准输出都连接到同一个管道
stdout, err := cmd.StdoutPipe()
cmd.Stderr = cmd.Stdout

if err != nil {
return err
}

if err = cmd.Start(); err != nil {
return err
}
// 从管道中实时获取输出并打印到终端
for {
tmp := make([]byte, 1024)
_, err := stdout.Read(tmp)
fmt.Print(string(tmp))
if err != nil {
break
}
}

if err = cmd.Wait(); err != nil {
return err
}
return nil
}

1…345…14

haohao

Talk is cheap. Show me the code.

134 日志
35 分类
43 标签
GitHub CSDN 开源中国 E-Mail
© 2017 — 2021 haohao
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.3
访问人数 总访问量 次