UDNZ

  • 首页

  • 标签28

  • 归档15

  • 搜索

设置 Windows 10 子系统通过代理上网

发表于 2019-05-07

安装 Windows 10 Ubuntu 子系统

(网上很多教程,略)

设置代理上网

如果 Windows 10 本身就通过代理上网,则子系统默认是无法上网的,仍然需要设置代理。

设置 apt 的代理

1
echo 'Acquire::http::Proxy "http://myproxy.com:8080";' | sudo tee /etc/apt/apt.conf.d/my-proxy.conf

设置其他代理

1
echo "export http_proxy='myproxy.com:8080'" | sudo tee -a ~/.bashrc

Protobuf & gRPC & gRPC-Gateway

发表于 2019-01-25

Protobuf & gRPC & gRPC-Gateway

安装

  1. 安装 protoc,下载地址:https://github.com/protocolbuffers/protobuf/releases

    根据自己的系统下载相应的 protoc,windows 用户统一下载 win32 版本。

  2. 配置 protoc 到系统的环境变量中,执行如下命令查看是否安装成功:

    1
    2
    $ protoc --version
    libprotoc 3.6.1
  3. 安装 ProtoBuf 相关的 golang 依赖库

    1
    2
    # 用于根据 protobuf 生成 golang 代码,语法 protoc --go_out=. *.proto
    $ go get -u github.com/golang/protobuf/protoc-gen-go
  4. 安装语法支持、代码高亮和代码格式化插件

    安装 VSC 插件:

    • vscode-proto3
    • Clang-Format

    安装命令行工具

    • clang-format:npm install -g clang-format

Protobuf

  1. 编写 protobuf 代码 ./pb/user/profile-service.proto。(语法参考)

    详细代码示例见下文。

  2. 生成 *.pb.go 代码。

    1
    2
    # 生成 message
    $ protoc --go_out=. ./pb/**/*.proto

    详细命令参数见:https://github.com/golang/protobuf

    通过以上命令把./pb/user/profile-service.proto生成了./api/user/profile-service.pb.go文件。至此,我们生成了 go 代码,其中生成的 struct 和相关的方法都可以在 go 工程里正常使用。

gRPC

  1. 生成 *.pb.go 代码。

    您可能已经发现了,上面生成的代码中找不到我们定义的 service ProfileService,其中的两个方法也找不到。这是为什么呢?是因为我们执行 protoc 命令时,没有指定支持 grpc 的插件,指定支持 grpc 的插件之后,即会生成服务相关代码:

    1
    2
    # 生成 message 和 service(支持gRPC)
    $ protoc --go_out=plugins=grpc:. ./pb/**/*.proto

    执行上述命令之后,您会发现生成的 go 文件里,多出了与 ProfileServiceServer、ProfileServiceClient 相关的 struct 和 interface,也多出了我们 GetProfile、Logout 两个方法相关的代码。

  2. 编写服务端和客户端代码

    服务端代码:./grpc/server/server.go,客户端代码:./grpc/grpc-client/client.go,详细代码见下文。

  3. 运行效果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 运行 gRPC 服务
    $ go run ./grpc/server/server.go

    # 运行 gRPC 客户端,调用服务端
    $ go run ./grpc/grpc-client/client.go
    Login: true
    Greeting: austinluo

    # 运行 gRPC 客户端,调用服务端
    $ go run ./grpc/grpc-client/client.go admin
    Login: true
    Greeting: admin

至此,基于 gRPC 通信的服务端和客户端均已经建立起来。

gRPC Gateway

如果我们的 gRPC 服务希望通过 HTTP 协议公开出来,以供现有的其他服务调用,那么我们就需要用到 gRPC Gateway。它是 Google 官方提供的一个反向代理,核心功能是提供 HTTP 接口,将接收到的 JSON 请求解码再编码为 pb 二进制格式,然后通过 gRPC 调用服务端,服务端返回了 pb 二进制的响应之后,它再次解码编码为 HTTP 响应体返回到客户端。

如下为实现的过程示例。

  1. 安装:https://github.com/grpc-ecosystem/grpc-gateway
  2. 在 proto 文件中添加google.api.http相关描述
  3. 执行 protoc 命令

    1
    2
    3
    4
    5
    6
    protoc \
    -I. \
    -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
    --go_out=plugins=grpc:. \
    --grpc-gateway_out=logtostderr=true:. \
    ./pb/**/*.proto

    至此,除了生成了上文所说的./api/user/profile-service.pb.go,也生成了./api/user/profile-service.pb.gw.go 文件,该文件即可用于创建 HTTP 反向代理。

  4. 创建 HTTP 反向代理服务 ./grpc/grpc-gateway/http_proxy.go。详细代码见下文。

  5. 运行效果

    1
    2
    3
    4
    5
    # 运行 gRPC 服务
    $ go run ./grpc/server/server.go

    # 运行 gRPC-gateway 反向代理
    $ go run ./grpc/grpc-gateway/http_proxy.go

    发起请求,执行 Login:

    1
    2
    3
    4
    5
    6
    7
    POST /v1/login
    Host: localhost:8080
    Content-Type: application/json
    accept-encoding: gzip, deflate
    content-length: 46

    { "name": "austinluo", "password": "12345" }
    1
    2
    3
    4
    5
    6
    7
    8
    HTTP/1.1 200
    status: 200
    Content-Type: application/json
    Grpc-Metadata-Content-Type: application/grpc
    Date: Fri, 25 Jan 2019 06:35:27 GMT
    Content-Length: 11

    {"ok":true}

    发起请求,执行 GetProfile:

    1
    2
    3
    GET /v1/profile
    Host: localhost:8080
    accept-encoding: gzip, deflate
    1
    2
    3
    4
    5
    6
    7
    8
    HTTP/1.1 200
    status: 200
    Content-Type: application/json
    Grpc-Metadata-Content-Type: application/grpc
    Date: Fri, 25 Jan 2019 06:41:19 GMT
    Content-Length: 66

    {"profile":{"user":{"staff_id":"61050","staff_name":"austinluo"}}}

    至此,支持 HTTP 请求的 gRPC Gateway 搭建成功,可以通过它访问 gRPC 服务了。

附:完整代码:

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
// ./pb/user/profile-service.proto

syntax = "proto3";
package api.user;

option go_package = "api/user";

import "google/api/annotations.proto";

service ProfileService {
rpc GetProfile(GetProfileReq) returns (GetProfileRes) {
option (google.api.http) = {
get : "/v1/profile"
};
}
rpc Login(LoginReq) returns (LoginRes) {
option (google.api.http) = {
post : "/v1/login",
body : "*"
};
}
}

message GetProfileReq {}
message GetProfileRes { Profile profile = 1; }

message LoginReq {
string name = 1;
string password = 2;
}
message LoginRes { bool ok = 1; }

// Note that the generated Go field names always use camel-case naming, even if
// the field name in the .proto file uses lower-case with underscores (as it should).
// https://developers.google.com/protocol-buffers/docs/reference/go-generated
message Staff {
string staff_id = 1;
string staff_name = 2;
}

message Profile {
Staff user = 1;
repeated string permissions = 2;
}
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
53
54
55
56
57
58
59
60
// ./grpc/server/server.go

package main

import (
"context"
"errors"
"log"
"net"

pb "git.code.oa.com/fip-team/rasse/api/user"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)

const (
port = ":50051"
)

// HACK: the logged in user name
// it should be kept in a state management system with session.
var loggedinUser string

// HACK: the password for users
const password = "12345"

type server struct{}

func (s *server) GetProfile(ctx context.Context, in *pb.GetProfileReq) (*pb.GetProfileRes, error) {
user := &pb.Staff{StaffId: "61050", StaffName: loggedinUser}
permissions := []string{}
return &pb.GetProfileRes{
Profile: &pb.Profile{
User: user,
Permissions: permissions,
}}, nil
}

func (s *server) Login(ctx context.Context, in *pb.LoginReq) (*pb.LoginRes, error) {
log.Printf("Received: %v", in.String())
if in.Password == password {
loggedinUser = in.Name
return &pb.LoginRes{Ok: true}, nil
}
return nil, errors.New("password error")
}

func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterProfileServiceServer(s, &server{})
// Register reflection service on gRPC server.
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// ./grpc/grpc-client/client.go

package main

import (
"context"
"log"
"os"
"time"

pb "git.code.oa.com/fip-team/rasse/api/user"
"google.golang.org/grpc"
)

const (
address = "localhost:50051"
defaultName = "austinluo"
defaultPassword = "12345"
)

func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewProfileServiceClient(conn)

// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

resLogin, err := c.Login(ctx, &pb.LoginReq{Name: name, Password: defaultPassword})
if err != nil {
log.Fatalf("could not login: %v", err)
}
log.Printf("Login: %v", resLogin.Ok)

resProfile, err := c.GetProfile(ctx, &pb.GetProfileReq{})
if err != nil {
log.Fatalf("could not get profile: %v", err)
}
log.Printf("Greeting: %s", resProfile.Profile.User.StaffName)
}
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
// ./grpc/grpc-gateway/http_proxy.go

package main

import (
"flag"
"net/http"

"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"

gw "git.code.oa.com/fip-team/rasse/api/user"
)

var (
echoEndpoint = flag.String("echo_endpoint", "localhost:50051", "endpoint of YourService")
)

func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()

mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := gw.RegisterProfileServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
if err != nil {
return err
}

return http.ListenAndServe(":8080", mux)
}

func main() {
flag.Parse()
defer glog.Flush()

if err := run(); err != nil {
glog.Fatal(err)
}
}

参考

  • gRPC Go Quick Start: https://grpc.io/docs/quickstart/go.html
  • Go support for Protocol Buffers: https://github.com/golang/protobuf
  • Language Guide (proto3): https://developers.google.com/protocol-buffers/docs/proto3
  • grpc-gateway: https://github.com/grpc-ecosystem/grpc-gateway
  • gRPC in 3 minutes (Go): https://github.com/grpc/grpc-go/tree/master/examples
  • grpc examples of gin-gonic/gin: https://github.com/gin-gonic/gin/tree/master/examples/grpc
  • Package google.api(google.api.http): https://cloud.google.com/service-infrastructure/docs/service-management/reference/rpc/google.api#google.api.HttpRule

Using portable commands like zip/tar on Windows

发表于 2019-01-23

在 Windows 上使用 Mac/Linux 上的 zip/tar 等命令

开发时时而在 MAC、Linux 上,时而在 Windows 上,脚本执行环境无法统一是个大问题。所幸我们使用的 Git 中带有 MINGW32(或安装 CYGWIN),这里面已经有很多的非 Windows 平台命令了。

但是在使用过程中,还是有一些命令是缺失的,比如 zip,此时我们可以自行下载他们(下载地址),下载之后,将可执行的 exe 文件和相关依赖文件,放到 MING32/CYGWIN 的 bin 目录中即可。

附:

  • Git 的 MINGW32 默认安装目录:C:\Program Files (x86)\Git\mingw32\bin

将代码从 SVN 迁移到 GIT 并保留提交记录

发表于 2018-12-04

第一步:用户映射

创建用户映射 (例如 users.txt) ,将 SVN 用户和 Git 用户对应起来,为保留提交记录做准备。

进入 SVN 的目录,执行如下命令,注意其中 $1\@your-company.com 部分应当替换为你实际的映射关系。

1
2
3
4
5
6
7
8
$ svn log --xml | grep -P "^<author" | sort -u | \
perl -pe 's/<author>(.*?)<\/author>/$1 = $1 <$1\@your-company.com>/' > users.txt

# 或

$ svn log -q svn+ssh://your/svn/path | \
awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2"@your-company.com>"}' | \
sort -u > users.txt

注意,生成的 users.txt 文件应当以 ANSI 编码保存,并且使用 CRLF 换行,您可以使用记事本另存为的功能选择该编码。

第二步:克隆代码

创建一个用于存放 Git 本地仓库的文件夹,如:E:/git,进入该文件夹,执行 clone 命令。

1
2
cd /e/git
git svn clone svn+ssh://your/svn/path -r 2000:HEAD --no-metadata --authors-file=users.txt --trunk=.

该步骤除了 clone 代码之外,还将导入所有的提交记录,执行较慢,需要耐心等待。中途出现找不到作者的情况时,可以修改 users.txt 补充该作者,重新再执行 git svn clone 命令。

详细命令参数,请参考 git svn help clone。

执行完成之后,会在 E:/git 目录下创建以你 SVN path 最后一节为名称的子目录,可以进入该目录下的 .git 文件夹,执行 git log 命令查看提交记录。

第三步:提交到 GIT

这个步骤和常规地创建 GIT 仓库没有任何区别,直接添加远程仓库地址,push 即可:

1
2
git remote add origin http://your/remote/git/path.git
git push -u origin master

至此,代码及提交记录从 SVN 迁移到 GIT 的工作全部完成。

参考链接:

  • https://gitee.com/progit/8-Git-%E4%B8%8E%E5%85%B6%E4%BB%96%E7%B3%BB%E7%BB%9F.html#8.2-%E8%BF%81%E7%A7%BB%E5%88%B0-Git
  • https://www.lovelucy.info/codebase-from-svn-to-git-migration-keep-commit-history.html

【译】Go 语言实践:编写可维护的程序的建议

发表于 2018-10-27 | 更新于 2018-11-25

译者注

本文为 QCon 2018 上海站主题演讲嘉宾、Heptio 资深工程师、著名 Go 语言专家 David Cheney 关于 Go 语言实践的英文分享。为方便大家阅读,在此由 Austin Luo 翻译为中文,在文中难以理解之处,也特别增加了译者的理解说明。翻译水平有限,如有偏颇之处,烦请联系我(uonun@163.com)更正。转载请注明出处,保留本节译者注。


目录

  • 指导原则
    • 简单性
    • 可读性
    • 生产率
  • 标识符
    • 选择清晰的名称,而不是简洁的名称
    • 标识符长度
    • 命名中不要包含所属类型的名称
    • 使用一致的命名风格
    • 使用一致的声明风格
    • 成为团队合作者
  • 代码注释
    • 变量和常量上的注释应当描述它的内容,而非目的
    • 总是为公开符号写文档说明
  • 包的设计
    • 一个好的包从它的名称开始
    • 避免将包命名为 base、common、util
    • 快速返回,而不是深层嵌套
    • 让零值变得有意义
    • 避免包级别的状态
  • 项目结构
    • 考虑更少,更大的包
    • 确保 main 包越小越好
  • API 设计
    • 设计难以被误用的 API
    • 针对默认用例设计 API
    • 让函数自身定义它所需的行为
  • 错误处理
    • 通过消除错误来消除错误处理
    • 错误只处理一次
  • 并发
    • 保持自己忙碌,否则自己做
    • 将并发留给调用者
    • 不要启动一个永不停止的协程

引言

接下来这两场我将给大家一些编写 Go 代码的最佳实践。

今天这是一个研讨会风格的演讲,我会摒弃那些绚丽的 PPT,而是使用您们可以直接带走的文档。

您可以在这里找到这个演讲最新的在线版本:
https://dave.cheney.net/practical-go/presentations/qcon-china.html

阅读全文 »

在 Visual Studio Code 中使用 MINGW32 作为终端

发表于 2018-10-08

打开 Visual Studio Code 的设置,按如下配置即可:

1
2
3
4
5
{
// 找到您的 git 目录
"terminal.integrated.shell.windows": "C:\\Program Files (x86)\\Git\\bin\\bash.exe",
"terminal.integrated.shellArgs.windows": ["--login", "-i"]
}

Linux 环境一键打包发布脚本

发表于 2018-10-06

以下脚本作为工作笔记沉淀,记录一下。

阅读全文 »

通过 SSH 隧道链接远程 MySQL

发表于 2018-10-01

场景:

  • SSH 跳板机:10.0.0.1
  • MySQL 数据库:10.0.0.2:3306

MySQL 部署的机器不允许 SSH 连接,并且数据库端口只允许 localhost 或 10.0.0.1 这台跳板机访问,此时本机可以通过 ssh 连上跳板机,通过 ssh 建立的隧道端口转发连接上 MySQL 数据库。

阅读全文 »

Understanding Architecture

发表于 2018-09-30

架构是这样定义的

  • 每个系统都有一个架构
  • 架构由架构元素以及相互之间的关系构成
  • 系统是为了满足 利益相关者(stakeholder) 的需求而构建的
  • 利益相关者都有自己的关注点(concerns)
  • 架构由架构文档描述
  • 架构文档描述了一系列的架构视角
  • 每个视角都解决并且对应到利益相关者的关注点。

架构师的首要任务是尽最大可能找出所有利益相关者,业务方,产品经理,客户/用户,开发经理,工程师,项目经理,测试人员,运维人员,产品运营人员等等都有可能是利益相关者,架构师要充分和利益相关者沟通,深入理解他们的关注点和痛点,并出架构解决这些关注点。架构师常犯错误是漏掉重要的利益相关者,沟通不充分,都会造成架构有欠缺,不能满足利益相关者的需求。利益相关者的关注点是有可能冲突的,比如管理层(可管理性)vs技术方(性能),业务方(多快好省)vs 技术方(可靠稳定),这需要架构师去灵活平衡,如何平衡体现了架构师的水平和价值。

阅读全文 »

Kubernetes 环境搭建

发表于 2018-09-30 | 更新于 2019-05-22

Kubernetes 环境安装

面向生产:kubeadmin

环境准备

IP 主机名 用途
192.168.1.1 k8s-master master、etcd
192.168.1.2 k8s-node-1 node1
192.168.1.3 k8s-node-2 node2

准备工作

设置三台机器的主机名

1
2
3
4
5
6
# 在 master 上执行
hostnamectl --static set-hostname k8s-master
# 在 node1 上执行
hostnamectl --static set-hostname k8s-node-1
# 在 node2 上执行
hostnamectl --static set-hostname k8s-node-2

设置 hosts

在三台机器上设置 hosts,均执行如下命令:

1
2
3
4
echo '192.168.1.1    k8s-master
192.168.1.1 etcd
192.168.1.2 k8s-node-1
192.168.1.3 k8s-node-2' >> /etc/hosts

关闭 selinux

1
2
setenforce 0
sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux

网络配置

master 机

1
2
3
4
5
6
7
8
9
10
firewall-cmd --permanent --add-port=6443/tcp
firewall-cmd --permanent --add-port=2379-2380/tcp
firewall-cmd --permanent --add-port=10250/tcp
firewall-cmd --permanent --add-port=10251/tcp
firewall-cmd --permanent --add-port=10252/tcp
firewall-cmd --permanent --add-port=10255/tcp
firewall-cmd --reload
modprobe br_netfilter
echo '1' > /proc/sys/net/bridge/bridge-nf-call-iptables
sysctl -w net.ipv4.ip_forward=1

node 机

1
2
3
4
5
6
7
firewall-cmd --permanent --add-port=10250/tcp
firewall-cmd --permanent --add-port=10255/tcp
firewall-cmd --permanent --add-port=30000-32767/tcp
firewall-cmd --permanent --add-port=6783/tcp
firewall-cmd --reload
echo '1' > /proc/sys/net/bridge/bridge-nf-call-iptables
sysctl -w net.ipv4.ip_forward=1

安装 kubelet kubeadm kubectl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kube*
EOF

# 将 SELinux 设置为 permissive 模式(将其禁用)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

systemctl enable kubelet && systemctl start kubelet

参考:https://kubernetes.io/zh/docs/setup/independent/install-kubeadm/#%E5%AE%89%E8%A3%85-kubeadm-kubelet-%E5%92%8C-kubectl

安装 etcd 服务

1
yum install -y etcd

更改配置

1
vi /etc/etcd/etcd.conf

创建集群

面向试用:minikube

指引:https://kubernetes.io/docs/tasks/tools/install-kubectl/

安装 VM 驱动

腾讯云 CVM 未能启用驱动启动 minikube,安装驱动的过程可忽略。

- VirtualBox

CentOS:虚拟机不支持内部 VM,未在物理机上尝试

1
2
3
4
5
6
7
8
9
cd /etc/yum.repos.d/
wget http://download.virtualbox.org/virtualbox/rpm/rhel/virtualbox.repo
yum update

rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum install -y binutils gcc make patch libgomp glibc-headers glibc-devel kernel-headers kernel-devel dkms
yum install -y VirtualBox-5.2

service vboxdrv start
  • https://www.if-not-true-then-false.com/2010/install-virtualbox-with-yum-on-fedora-centos-red-hat-rhel/
  • https://www.virtualbox.org/wiki/Linux_Downloads
  • https://www.cnblogs.com/harry-h/p/6405433.html

MAC 上可以直接下载安装:https://www.virtualbox.org/wiki/Downloads

- KVM2

安装成功,但 minikube start 时未能尝试成功

https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm2-driver

1
2
3
4
5
6
yum -y install libvirt-daemon-kvm qemu-kvm

curl -Lo docker-machine-driver-kvm2 https://storage.googleapis.com/minikube/releases/latest/docker-machine-driver-kvm2 \
&& chmod +x docker-machine-driver-kvm2 \
&& sudo cp docker-machine-driver-kvm2 /usr/local/bin/ \
&& rm docker-machine-driver-kvm2

附:查看虚拟机网络以及使用不同的网络启动 minikube

1
2
3
4
5
yum -y install libvirt virt-install bridge-utils
# 查看虚拟机网络
virsh net-list --all
# 使用不同的虚拟机网络(minikube-net)
minikube start --vm-driver kvm2 --kvm-network minikube-net

- 其他驱动

指引:https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm-driver

安装 kubectl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# CentOS
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
yum install -y kubectl

# MAC: 方式一
brew install kubernetes-cli
# MAC:方式二
curl -Lo kubectl http://storage.googleapis.com/kubernetes-release/release/v1.5.1/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/

# 安装校验
kubectl version

# 确保服务已启动
systemctl enable kubelet.service
systemctl start kubelet.service

安装 minikube

指引:https://github.com/kubernetes/minikube/releases

1
2
3
4
5
6
7
8
# CentOS
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.29.0/minikube-linux-amd64 \
&& chmod +x minikube \
&& sudo cp minikube /usr/local/bin/ \
&& rm minikube

# MAC
brew cask install minikube

运行

(使用 KVM2 驱动)运行之前

1
2
3
4
5
6
7
8
9
10
11
# 启动服务
systemctl enable libvirtd && systemctl start libvirtd

# 查看进程文件
ls -l /var/run/libvirt/libvirt-sock

# 若进程文件不存在,则修改配置:
vi /etc/libvirt/libvirtd.conf

# 取消行前注释,重新启动服务
#unix_sock_dir = "/var/run/libvirt"

拉取 k8s 相关镜像

原本在 minikube start 命令中会自动拉取镜像,但是因为众所周知的原因,我们无法成功拉取到镜像。这里可以使用阿里 docker 容器镜像服务:https://dev.aliyun.com/search.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 进入虚拟机拉取,而非本机
minikube ssh

# 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/k8sth/kube-apiserver-amd64:v1.10.0
docker pull registry.cn-hangzhou.aliyuncs.com/k8sth/kube-controller-manager-amd64:v1.10.0
docker pull registry.cn-hangzhou.aliyuncs.com/k8sth/kube-scheduler-amd64:v1.10.0
docker pull registry.cn-hangzhou.aliyuncs.com/k8sth/etcd-amd64:3.1.12
docker pull registry.cn-shenzhen.aliyuncs.com/kubernetes_google/kubernetes-dashboard-amd64:v1.10.0

# 修改 tag
docker tag registry.cn-hangzhou.aliyuncs.com/k8sth/kube-apiserver-amd64:v1.10.0 k8s.gcr.io/kube-apiserver-amd64:v1.10.0
docker tag registry.cn-hangzhou.aliyuncs.com/k8sth/kube-controller-manager-amd64:v1.10.0 k8s.gcr.io/kube-controller-manager-amd64:v1.10.0
docker tag registry.cn-hangzhou.aliyuncs.com/k8sth/kube-scheduler-amd64:v1.10.0 k8s.gcr.io/kube-scheduler-amd64:v1.10.0
docker tag registry.cn-hangzhou.aliyuncs.com/k8sth/etcd-amd64:3.1.12 k8s.gcr.io/etcd-amd64:3.1.12
docker tag registry.cn-shenzhen.aliyuncs.com/kubernetes_google/kubernetes-dashboard-amd64:v1.10.0 k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.0

启动 Kubenetes

指引:https://kubernetes.io/docs/setup/minikube/#quickstart

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
# 在代理下运行时,需要指定正确的代理,否则 minikube 在内部访问网络会有问题
# 注意,代理地址应当是在虚拟机中能够访问的地址,不能使用 127.0.0.1、localhost 这种
https_proxy=http://myproxy.com:8080
minikube start \
--docker-env http_proxy=http://myproxy.com:8080 \
--docker-env https_proxy=http://myproxy.com:8080 \
--docker-env no_proxy=192.168.99.0/24
--vm-driver=none

# 安装和启动过程中可能产生错误,通过该命令查看日志
minikube logs -f

# 启动 k8s( CentOS 上不使用任何 vm 驱动,MAC 上默认可使用 VirtualBox)
minikube start \
--network-plugin=cni \
--container-runtime=containerd \
--bootstrapper=kubeadm
--vm-driver=none

# Dashboard
# https://github.com/kubernetes/dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
kubectl proxy

# 部署及运行镜像
kubectl run hello-minikube --image=registry.cn-hangzhou.aliyuncs.com/acs/echoserver:1.4 --port=8080

# 导出服务
kubectl expose deployment hello-minikube --type=NodePort

# 查询 Pod
kubectl get pod
kubectl get pods --all-namespaces

# 查看状态
kubectl describe --namespace=kube-system po kubernetes-dashboard-6f4cfc5d87-d647l
kubectl logs kubernetes-dashboard-6f4cfc5d87-x976v --namespace=kube-system
kubectl cluster-info

# 调用服务
curl $(minikube service hello-minikube --url)

# 删除服务
kubectl delete services hello-minikube

# 删除部署
kubectl delete deployment hello-minikube

# 停止 k8s
minikube stop

向 kubernetes 添加账号

https://github.com/kubernetes/dashboard/wiki/Creating-sample-user

相关问题

failed to create kubelet: misconfiguration: kubelet cgroup driver: “cgroupfs” is different from docker cgr…driver: “systemd”

该问题因为 docker 的配置和 kubelet 的配置不一致导致。

使用 docker info 打印 docker 信息:

1
2
3
4
5
6
[root@VM_0_16_centos kubelet.service.d]# docker info | grep Driver
WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.
WARNING: You're not using the default seccomp profile
Storage Driver: devicemapper
Logging Driver: journald
Cgroup Driver: systemd

而查看 kubelet 服务的启动参数(--cgroup-driver),其设置为 cgroupfs:

1
2
3
4
5
6
7
8
9
10
11
[root@VM_0_16_centos kubelet.service.d]# more /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

[Unit]
Wants=docker.socket

[Service]
ExecStart=
ExecStart=/usr/bin/kubelet --hostname-override=minikube --cluster-domain=cluster.local --cgroup-driver=cgroupfs --authorization-mode=Webhook --client-ca-file=/var/lib/minikube/certs/ca.crt --fail-swap-on=false --kubeconfig=/etc/kubernetes/ku
belet.conf --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true --cluster-dns=10.96.0.10 --cadvisor-port=0

[Install]

此时,修改 docker 的服务参数(vi /usr/lib/systemd/system/docker.service),将其中的 --exec-opt native.cgroupdriver 参数值改为 cgroupfs 。

然后,重启 docker,重启 kubelet

1
systemctl daemon-reload && systemctl restart docker && systemctl restart kubelet

[ERROR FileContent–proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1

解决方案:

1
2
echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
echo 1 > /proc/sys/net/bridge/bridge-nf-call-ip6tables

Unable to update cni config: No networks found in /etc/cni/net.d

1
yum install -y kubernetes-cni

参考

https://www.jianshu.com/p/a42eeb66a19c

12
Austin Luo

Austin Luo

架构师,腾讯
专注微服务和软件基础设施建设

15 日志
25 标签
RSS
GitHub Bitbucket QQ 群
© 2015 – 2019 udnz.com
- 思考 / 一丝不苟 / 精益求精 / 永不满足 / 怀疑一切 / 认知未来 -