Jim

Talk is cheap. Show me the code.


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

  • 搜索

使用 kubeadm 搭建 kubernetes 集群

发表于 2019-03-19 | 分类于 Kubenetes

kubeadm 简介

kubeadm 是 k8s 官方提供的用于快速部署 k8s 集群的命令行工具,也是官方推荐的最小化部署 k8s 集群的最佳实践,比起直接用二进制部署能省去很多工作,因为该方式部署的集群的各个组件以 docker 容器的方式启动,而各个容器的启动都是通过该工具配自动化启动起来的。

kubeadm 不仅仅能部署 k8s 集群,还能很方便的管理集群,比如集群的升级、降级、集群初始化配置等管理操作。

kubeadm 的设计初衷是为新用户提供一种便捷的方式来首次试用 Kubernetes, 同时也方便老用户搭建集群测试他们的应用。

kubeadm 的使用案例:

  • 新用户可以从 kubeadm 开始来试用 Kubernetes。
  • 熟悉 Kubernetes 的用户可以使用 kubeadm 快速搭建集群并测试他们的应用。
  • 大型的项目可以将 kubeadm 和其他的安装工具一起形成一个比较复杂的系统。

安装环境要求

  • 一台或多台运行着下列系统的机器:
    • Ubuntu 16.04+
    • Debian 9
    • CentOS 7
    • RHEL 7
    • Fedora 25/26
    • HypriotOS v1.0.1+
    • Container Linux (针对1800.6.0 版本测试)
  • 每台机器 2 GB 或更多的 RAM (如果少于这个数字将会影响您应用的运行内存)
  • 2 CPU 核心或更多(节点少于 2 核的话新版本 kubeadm 会报错)
  • 集群中的所有机器的网络彼此均能相互连接(公网和内网都可以)
  • 禁用 Swap 交换分区。(Swap 分区必须要禁掉,否则安装会报错)

准备环境

本文使用 kubeadm 部署一个 3 节点的 k8s 集群:1 个 master 节点,2 个 node 节点。各节点详细信息如下:

Hostname IP OS 发行版 内存(GB) CPU(核)
k8s-master 192.168.10.100 Centos7 2 2
k8s-node-1 192.168.10.101 Centos7 2 2
k8s-node-2 192.168.10.102 Centos7 2 2

kubeadm 安装 k8s 集群完整流程

  • 使用 Vagrant 启动 3 台符合上述要求的虚拟机
  • 调整每台虚拟机的服务器参数
  • 各节点安装 docker、kubeadm、kubelet、kubectl 工具
  • 使用 kubeadm 部署 master 节点
  • 安装 Pod 网络插件(CNI)
  • 使用 kubeadm 部署 node 节点

接下来我们依次介绍每步的具体细节:

使用 Vagrant 启动 3 台符合上述要求的虚拟机

Vagrant 的使用在这里不具体介绍了,如需了解请点击这里。
本文用到的 Vagrantfile:

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
# -*- mode: ruby -*-
# vi: set ft=ruby :
# author: qhh0205

$num_nodes = 2

Vagrant.configure("2") do |config|
# k8s 主节点定义及初始化配置
config.vm.define "k8s-master" do | k8s_master |
k8s_master.vm.box = "Centos7"
k8s_master.vm.hostname = "k8s-master"
k8s_master.vm.network "private_network", ip: "192.168.10.100"
k8s_master.vm.provider "virtualbox" do | v |
v.name = "k8s-master"
v.memory = "2048"
v.cpus = 2
end
end

# k8s node 节点定义及初始化配置
(1..$num_nodes).each do |i|
config.vm.define "k8s-node-#{i}" do |node|
node.vm.box = "Centos7"
node.vm.hostname = "k8s-node-#{i}"
node.vm.network "private_network", ip: "192.168.10.#{i+100}"
node.vm.provider "virtualbox" do |v|
v.name = "k8s-node-#{i}"
v.memory = "2048"
v.cpus = 2
end
end
end
end

进入 Vagrantfile 文件所在目录,执行如下命令启动上述定义的 3 台虚拟机:

1
vagrant up

调整每台虚拟机的服务器参数

  1. 禁用 swap 分区:
    临时禁用:swapoff -a
    永久禁用:sed -i '/swap/s/^/#/g' /etc/fstab
    swap 分区必须禁止掉,否则 kubadm init 自检时会报如下错误:

    1
    [ERROR Swap]: running with swap on is not supported. Please disable swap
  2. 将桥接的 IPv4 流量传递到 iptables 的链:

    1
    2
    3
    4
    5
    $ cat > /etc/sysctl.d/k8s.conf << EOF
    net.bridge.bridge-nf-call-ip6tables = 1
    net.bridge.bridge-nf-call-iptables = 1
    EOF
    $ sysctl --system

    如果不进行这一步的设置,kubadm init 自检时会报如下错误:

    1
    [ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1
  3. 关闭网络防火墙:

    1
    2
    systemctl stop firewalld
    systemctl disable firewalld
  4. 禁用 SELinux:
    临时关闭 selinux(不需要重启主机): setenforce 0
    永久关闭 selinux(需要重启主机才能生效):sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config

各节点安装 docker、kubeadm、kubelet、kubectl 工具

安装 Docker

配置 Docker yum 源(阿里 yum 源):

1
curl -sS -o /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装 docker:

1
2
3
yum install --nogpgcheck -y yum-utils device-mapper-persistent-data lvm2
yum install --nogpgcheck -y docker-ce
systemctl enable docker && systemctl start docker

安装 kubeadm、kubelet、kubectl 工具

配置相关工具 yum 源(阿里 yum 源):

1
2
3
4
5
6
7
8
9
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

安装 kubeadm、kubelet、kubectl:
其实 kubeadm、kubelet、kubectl 这三个工具的版本命名是一致的(和 k8s 版本命名一致),我们可以指定安装特定的版本,即安装指定版本的 k8s 集群。

查看哪些版本可以安装:
yum --showduplicates list kubeadm|kubelet|kubectl

在这里我们安装 1.13.2 版本:

1
2
3
4
5
yum install -y kubeadm-1.13.2 kubelet-1.13.2 kubectl-1.13.2
# 设置 kubelet 开机自启动: kubelet 特别重要,如果服务器重启后 kubelet
# 没有启动,那么 k8s 相关组件的容器就无法启动。在这里不需要把 kubelet 启动
# 起来,因为现在还启动不起来,后续执行的 kubeadm 命令会自动把 kubelet 拉起来。
systemctl enable kubelet

使用 kubeadm 部署 master 节点

登陆 master 节点执行如下命令:

1
kubeadm init --kubernetes-version v1.13.2 --image-repository registry.aliyuncs.com/google_containers --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.10.100

参数说明:
--kubernetes-version: 安装指定版本的 k8s 版本,该参数和 kubeadm 的版本有关,特定版本的 kubeadm 并不能安装所有版本的 k8s,最好还是 kubeadm 的版本和该参数指定的版本一致。

--image-repository: 该参数仅在高版本(具体哪个版本没仔细查,反正在 1.13.x 中是支持的)的 kubeadm 中支持,用来设置 kubeadm 拉取 k8s 各组件镜像的地址,默认拉取的地址是:k8s.gcr.io。众所周知 k8s.gcr.io 国内是无法访问的,所以在这里改为阿里云镜像仓库。

--pod-network-cidr: 设置 pod ip 的网段 ,网段之所以是 10.244.0.0/16,是因为后面安装 flannel 网络插件时,yaml 文件里面的 ip 段也是这个,两个保持一致,不然可能会使得 Node 间 Cluster IP 不通。这个参数必须得指定,如果这里不设置的话后面安装 flannel 网络插件时会报如下错误:

1
E0317 17:02:15.077598       1 main.go:289] Error registering network: failed to acquire lease: node "k8s-master" pod cidr not assigned

--apiserver-advertise-address: API server 用来告知集群中其它成员的地址,这个参数也必须得设置,否则 api-server 容器启动不起来,该参数的值为 master 节点所在的本地 ip 地址。


题外话:像之前没有 --image-repository 这个参数时,大家为了通过 kubeadm 安装 k8s 都是采用”曲线救国”的方式:先从别的地方把同样的镜像拉到本地(当然镜像的 tag 肯定不是 k8s.gcr.io/xxxx),然后将拉下来的镜像重新打个 tag,tag 命名成和执行 kubeadm init 时真正拉取镜像的名称一致(比如:k8s.gcr.io/kube-controller-manager-amd64:v1.13.2)。这么做显然做了很多不必要的工作,幸好现在有了 --image-repository 这个参数能自定义 kubeadm 拉取 k8s 相关组件的镜像地址了。


执行上面命令后,如果出现如下输出(截取了部分),则表示 master 节点安装成功了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

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

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

kubeadm join 192.168.10.100:6443 --token jm6o42.9ystvjarc6u09pjp --discovery-token-ca-cert-hash sha256:64405f3a90597e0ebf1f33134649196047ce74df575cb1a7b38c4ed1e2f94421

根据上面输出知道:要开始使用集群普通用户执行下面命令:

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

现在就可以使用 kubectl 访问集群了:

1
2
3
[vagrant@k8s-master ~]$ kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master NotReady master 13m v1.13.2

可以看出现在 master 节点还是 NotReady 状态,这是因为默认情况下,为了保证 master 的安全,master 是不会被分配工作负载的。你可以取消这个限制通过输入(不建议这样做,我们后面会向集群中添加两 node 工作节点):

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

安装 Pod 网络插件(CNI)

Pod 网络插件有很多种,具体见这里:https://kubernetes.io/docs/concepts/cluster-administration/addons/,我们选择部署 Flannel 网络插件:

1
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

使用 kubeadm 部署 node 节点

前面已经将 master 节点部署完了,接下来部署 node 节点就很简单了,在 node节点执行如下命令将自己加到 k8s 集群中(复制 master 节点安装完后的输出):

1
kubeadm join 192.168.10.100:6443 --token jm6o42.9ystvjarc6u09pjp --discovery-token-ca-cert-hash sha256:64405f3a90597e0ebf1f33134649196047ce74df575cb1a7b38c4ed1e2f94421

出现如下输出(截取了部分)表示成功将 node 添加到了集群:

1
2
3
4
5
6
...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.

在 master 节点查看 node 状态,如果都为 Ready,则表示集群搭建完成:

1
2
3
4
[vagrant@k8s-master ~]$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 14m v1.13.2
k8s-node-1 Ready <none> 3m v1.13.2

用同样的方法把另一个节点也加入到集群中。

相关资料

https://purewhite.io/2017/12/17/use-kubeadm-setup-k8s/
https://kubernetes.io/zh/docs/setup/independent/install-kubeadm/
https://k8smeetup.github.io/docs/admin/kubeadm/

.dockerignore 文件从入门到实践

发表于 2019-02-24 | 分类于 Docker

简介

.dockerignore 文件的作用类似于 git 工程中的 .gitignore 。不同的是 .dockerignore 应用于 docker 镜像的构建,它存在于 docker 构建上下文的根目录,用来排除不需要上传到 docker 服务端的文件或目录。

docker 在构建镜像时首先从构建上下文找有没有 .dockerignore 文件,如果有的话则在上传上下文到 docker 服务端时忽略掉 .dockerignore 里面的文件列表。这么做显然带来的好处是:

  • 构建镜像时能避免不需要的大文件上传到服务端,从而拖慢构建的速度、网络带宽的消耗;
  • 可以避免构建镜像时将一些敏感文件及其他不需要的文件打包到镜像中,从而提高镜像的安全性;

.dockerignore 文件编写方法

.dockerignore 文件的写法和 .gitignore 类似,支持正则和通配符,具体规则如下:

  • 每行为一个条目;
  • 以 # 开头的行为注释;
  • 空行被忽略;
  • 构建上下文路径为所有文件的根路径;

文件匹配规则具体语法如下:

规则 行为
*/temp* 匹配根路径下一级目录下所有以 temp 开头的文件或目录
*/*/temp* 匹配根路径下两级目录下所有以 temp 开头的文件或目录
temp? 匹配根路径下以 temp 开头,任意一个字符结尾的文件或目录
**/*.go 匹配所有路径下以 .go 结尾的文件或目录,即递归搜索所有路径
*.md
!README.md
匹配根路径下所有以 .md 结尾的文件或目录,但 README.md 除外

⚠️注意事项:
如果两个匹配语法规则有包含或者重叠关系,那么以后面的匹配规则为准,比如:

1
2
3
*.md
!README*.md
README-secret.md

这么写的意思是将根路径下所有以 .md 结尾的文件排除,以 README 开头 .md 结尾的文件保留,但是 README-secret.md 文件排除。

再来看看下面这种写法(同上面那种写法只是对换了后面两行的位置):

1
2
3
*.md
README-secret.md
!README*.md

这么写的意思是将根路径下所有以 .md 结尾和名称为 README-secret.md 的文件排除,但所有以 README 开头 .md 结尾的文件保留。这样的话 README-secret.md 依旧会被保留,并不会被排除,因为 README-secret.md 符合 !README*.md 规则。

使用案例

前段时间帮前端同学写了一个 Dockerfile,Dockerfile 放在 git 仓库根路径下,发现 git 工程中有很多真正应用跑起来用不到的文件,如果直接在 Dockerfile 中使用 COPY 或 ADD 指令拷贝文件,那么很显然会把很多不需要的文件拷贝到镜像中,从而会拖慢构建镜像的过程,产生的镜像也比较臃肿。解决方法就是编写 .dockerignore 文件,忽略掉不需要的文件,然后放到 docker 构建上下文的根路径下。.dockerignore 及 Dockerfile 文件内容如下:
.dockerignore:

1
2
3
4
5
6
.git
_mockData
deleted
email-templates
script
static

Dockerfile:

1
2
3
4
5
6
7
8
9
FROM node:8-alpine

COPY . /app/node
WORKDIR /app/node
RUN yarn install

EXPOSE 8026

CMD ["yarn", "run", "tool-dev"]

使用 .dockerignore 前后上传到 docker 服务端的构建上下文大小对比:
使用前(73.36MB):

1
2
3
[vagrant@docker]$ docker build -t tool:5.0 -f Dockerfile-frontend-tool .
Sending build context to Docker daemon 73.36MB
Step 1/6 : FROM node:8-alpine

使用后(11.38MB):

1
2
3
[vagrant@docker]$ docker build -t tool:6.0 -f Dockerfile-frontend-tool .
Sending build context to Docker daemon 11.38MB
Step 1/6 : FROM node:8-alpine

参考资料

https://docs.docker.com/engine/reference/builder/#dockerignore-file

深入理解 Docker 构建上下文

发表于 2019-02-17 | 分类于 Docker

本文通过具体实践深入解读 Docker 构建上下文的含义,解惑或者纠正很大一部分人对 Docker 构建上下文的理解误区。本文主要讨论如下主题:

  • 对 Docker 构建上下文的理解误区
  • 理解 Docker 的架构
  • 理解 docker build 的工作原理
  • 正确理解 Docker 构建上下文

对 Docker 构建上下文的理解误区

我们都知道,构建一个 Docker 镜像非常简单,大家一般都会这么做(当然这么做是完全正确的):

  1. 跳到 Dockerfile 所在目录;
  2. 执行 docker build 构建命令:
    1
    docker build -t <imageName:imageTag> .

通过上面的工作流,很容易形成这样的理解误区:

  • docker build 后面的 . 为 Dockerfile 所在的目录;
  • Dockerfile 文件名 必须为 Dockerfile;

其实上面这种理解是错误的,要想准确理解其含义,首先我们需要先了解下 Docker 的架构和 docker build 的工作原理。

理解 Docker 的架构

Docker 是一个典型的 C/S 架构的应用,分为 Docker 客户端(即平时敲的 docker 命令) Docker 服务端(dockerd 守护进程)。

Docker 客户端通过 REST API 和服务端进行交互,docker 客户端每发送一条指令,底层都会转化成 REST API 调用的形式发送给服务端,服务端处理客户端发送的请求并给出响应。

Docker 镜像的构建、容器创建、容器运行等工作都是 Docker 服务端来完成的,Docker 客户端只是承担发送指令的角色。

Docker 客户端和服务端可以在同一个宿主机,也可以在不同的宿主机,如果在同一个宿主机的话,Docker 客户端默认通过 UNIX 套接字(/var/run/docker.sock)和服务端通信。

理解 docker build 的工作原理

理解了 Docker 的架构就很容易理解 docker build 构建镜像的工作原理了。docker build 构建镜像的流程大概如下:

  • 执行 docker build -t <imageName:imageTag> . ;
  • Docker 客户端会将构建命令后面指定的路径(.)下的所有文件打包成一个 tar 包,发送给 Docker 服务端;
  • Docker 服务端收到客户端发送的 tar 包,然后解压,根据 Dockerfile 里面的指令进行镜像的分层构建;

正确理解 Docker 构建上下文

了解了 Docker 的架构和镜像构建的工作原理后,Docker 构建上下文也就容易理解了。Docker 构建上下文就是 Docker 客户端上传给服务端的 tar 文件解压后的内容,也即 docker build 命令行后面指定路径下的文件。

Docker 镜像的构建是在远程服务端进行的,所以客户端需要把构建所需要的文件传输给服务端。服务端以客户端发送的文件为上下文,也就是说 Dockerfile 中指令的工作目录就是服务端解压客户端传输的 tar 包的路径。

关于 docker build 指令的几点重要的说明:

  1. 如果构建镜像时没有明确指定 Dockerfile,那么 Docker 客户端默认在构建镜像时指定的上下文路径下找名字为 Dockerfile 的构建文件;
  2. Dockerfile 可以不在构建上下文路径下,此时需要构建时通过 -f 参数明确指定使用哪个构建文件,并且名称可以自己任意命名。

下面通过具体的实例来理解下:

首先创建一个简单的 demo 工程,工程结构如下:

1
2
3
4
5
6
7
helloworld-app
├── Dockerfile
└── docker
├── app-1.0-SNAPSHOT.jar
├── hello.txt
└── html
└── index.html

Dockerfile 内容:

1
2
3
FROM busybox
COPY hello.txt .
COPY html/index.html .

实践1:直接进入 helloworld-app 目录进行镜像构建,以 docker 目录为构建上下文:

1
2
$ docker build -t hello-app:1.0 docker
unable to prepare context: unable to evaluate symlinks in Dockerfile path: lstat /Users/haohao/opensource/helloworld-app/docker/Dockerfile: no such file or directory

可以看出默认 docker 客户端从 docker 构建上下文路径下找名字为 Dockerfile 的构建文件。

实践2:明确指定 Dockerfile 文件进行镜像构建,还是以 docker 目录为构建上下文:

1
2
3
4
5
6
7
8
9
10
$ docker build -f Dockerfile -t hello-app:1.0 docker                                                                                 
Sending build context to Docker daemon 96.61MB
Step 1/3 : FROM busybox
---> d8233ab899d4
Step 2/3 : COPY hello.txt .
---> 3305fc373120
Step 3/3 : COPY html/index.html .
---> efdefc4e6eb2
Successfully built efdefc4e6eb2
Successfully tagged hello-app:1.0

从输出结果可以得知:

  • 构建镜像时客户端会先给服务端发送构建上下路径下的内容(即 docker 目录下的文件);
  • Dockerfile 可以不在构建上下文路径下;
  • Dockerfile 中指令的工作目录是服务端解压客户端传输的 tar 包的路径;

实践3:以当前目录为构建上下文路径:

1
2
3
4
5
6
7
8
$ ls
Dockerfile docker
$ docker build -t hello-app:2.0 .
Sending build context to Docker daemon 96.62MB
Step 1/3 : FROM busybox
---> d8233ab899d4
Step 2/3 : COPY hello.txt .
COPY failed: stat /var/lib/docker/tmp/docker-builder375982663/hello.txt: no such file or directory

可以看出:

  • 镜像构建上下文路径并不是 Dockerfile 文件所在的路径;
  • Dockerfile 中指令的工作目录是服务端解压客户端传输的 tar 包的路径,因为 COPY 指令失败了,意味着当前目录并没有 hello.txt 文件;

相关资料

https://docs.docker.com/engine/reference/commandline/build/
https://yeasy.gitbooks.io/docker_practice/content/image/build.html

Linux 进程树查看工具 pstree

发表于 2019-02-16 | 分类于 Linux

简介

pstree 是 Linux 下的一个用于展示进程树结构的工具,类似于 tree 展示目录树一样,可视化地查看进程的继承关系。pstree 工具其实是 PSmisc 工具集的成员之一,PSmisc 工具集由 4 个实用的 Linux 进程管理工具(通过 Linux 的 /proc 文件系统实现)组成:

  • fuser - identifies what processes are using files.
  • killall - kills a process by its name, similar to a pkill found in some other Unices.
  • pstree - Shows currently running processes in a tree format.
  • peekfd - Peek at file descriptors of running processes.

pstree 带来的方便之处:
一条命令就可以很轻松地追溯某个进程的继承关系,再也不需要通过多次执行 ps -ef 一级一级的查看进程的继承关系。

安装

On Fedora/Red Hat/CentOS

1
sudo yum install -y psmisc

On Mac OS

1
brew install pstree

On Ubuntu/Debian APT

1
sudo apt-get install psmisc

使用

语法

pstree [选项]

选项

-a:显示每个程序的完整指令,包含路径,参数或是常驻服务的标示;
-c:不使用精简标示法;
-G:使用VT100终端机的列绘图字符;
-h:列出树状图时,特别标明现在执行的程序;
-H<程序识别码>:此参数的效果和指定”-h”参数类似,但特别标明指定的程序;
-l:采用长列格式显示树状图;
-n:用程序识别码排序。预设是以程序名称来排序;
-p:显示程序识别码;
-u:显示用户名称;
-U:使用UTF-8列绘图字符;
-V:显示版本信息。

示例

  1. 显示 PID 为 2858 进程的进程树;

    1
    2
    3
    [vagrant@docker ~]$ pstree 2858
    dockerd─┬─2*[docker-proxy───4*[{docker-proxy}]]
    └─9*[{dockerd}]
  2. 显示 PID 为 2858 进程的进程树,同时列出每个进程的 pid;
    注意:可以观察出,大括号括起来的为线程!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [vagrant@docker ~]$ pstree -p 2858
    dockerd(2858)─┬─docker-proxy(4378)─┬─{docker-proxy}(4379)
    │ ├─{docker-proxy}(4380)
    │ ├─{docker-proxy}(4381)
    │ └─{docker-proxy}(4382)
    ├─docker-proxy(6582)─┬─{docker-proxy}(6583)
    │ ├─{docker-proxy}(6585)
    │ ├─{docker-proxy}(6586)
    │ └─{docker-proxy}(6587)
    ├─{dockerd}(2997)
    ├─{dockerd}(2998)
    ├─{dockerd}(2999)
    ├─{dockerd}(3000)
    ├─{dockerd}(3222)
    ├─{dockerd}(3223)
    ├─{dockerd}(3224)
    ├─{dockerd}(4480)
    └─{dockerd}(4493)
  3. 显示 PID 为 2858 进程的进程树,同时列出每个进程的 pid 和启动进程的命令行;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    [vagrant@docker ~]$ pstree -p 2858 -a
    dockerd,2858 -H fd://
    ├─docker-proxy,4378 -proto tcp -host-ip 0.0.0.0 -host-port 3306 -container-ip 172.17.0.2 -container-port 3306
    │ ├─{docker-proxy},4379
    │ ├─{docker-proxy},4380
    │ ├─{docker-proxy},4381
    │ └─{docker-proxy},4382
    ├─docker-proxy,6582 -proto tcp -host-ip 0.0.0.0 -host-port 8080 -container-ip 172.17.0.3 -container-port 80
    │ ├─{docker-proxy},6583
    │ ├─{docker-proxy},6585
    │ ├─{docker-proxy},6586
    │ └─{docker-proxy},6587
    ├─{dockerd},2997
    ├─{dockerd},2998
    ├─{dockerd},2999
    ├─{dockerd},3000
    ├─{dockerd},3222
    ├─{dockerd},3223
    ├─{dockerd},3224
    ├─{dockerd},4480
    └─{dockerd},4493
  4. 直接执行 pstree 默认列出整个系统的进程树;

相关资料

http://man.linuxde.net/pstree
http://psmisc.sourceforge.net
https://www.wikiwand.com/en/Pstree

Docker 启动 MySQL 最佳实践

发表于 2019-01-27 | 分类于 MySQL

本文主要介绍使用 Docker 启动 MySQL 服务的最佳实践,Docker 镜像来自 docker 官方镜像。

启动一个 MySql 5.7 实例

关于版本的选择,修改镜像 tag 即可,支持的 tag 在 docker hub 仓库 有说明。

1
2
docker run --name mysql5.7 --restart always -p 3306:3306 -e MYSQL_ROOT_PASSWORD=12345 \
-v /home/vagrant/mysql5.7/data:/var/lib/mysql -d mysql:5.7

参数说明

  • --name mysql5.7: 指定运行容器名称
  • --restart always: 容器意外退出后自动重启
  • -p 3306:3306: 映射主机 3306 端口到容器 3306 端口
  • -e MYSQL_ROOT_PASSWORD=12345: 指定 msyql root 密码,该参数是为必须的
  • -v /home/vagrant/mysql5.7/data:/var/lib/mysql: mysql 数据持久化,主机 /home/vagrant/mysql5.7/data 目录挂载到容器 /var/lib/mysql 目录

连接 MySql

mysql 容器连接服务端:

1
docker run -it --rm mysql:5.7 mysql -hxxx -uxxx -p***

注意:如果在 mysql server 端所在的主机连接,-h 参数不能是 localhost,应该为主机所在的内网 ip。

Redis 常用命令总结

发表于 2019-01-19 | 分类于 Redis

Redis 常用命令总结

redis-cli

redis-cli 是 redis 的客户端工具,有很多实用的参数。

redis-benchmark

redis-benchmark 为 redis 提供的性能测试工具,对 redis 各种数据的操作进行测试,并给出测试结果。如下为 GET 操作的测试报告样例:

1
2
3
4
5
6
7
8
9
10
11
====== GET ======
20000 requests completed in 0.36 seconds
100 parallel clients
3 bytes payload
keep alive: 1

62.01% <= 1 milliseconds
97.57% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 3 milliseconds
55865.92 requests per second

基于 Docker Compose 容器化搭建 Wordpress

发表于 2019-01-14 | 分类于 Wordpress

最近由于业务需求帮公司搞了几个 Wordpress 作为官网,中间也是踩了不少坑,倒不是搭建 wordpress 难,主要是 wordpress 本身坑就挺多的,比如迁移、使用过程中文件上传大小的限制问题、迁移后域名无法变更问题等等。

接下来演示如何基于 Docker Compose 来容器化搭建一个可靠、易维护的 Wordpress 网站,可靠指的是服务挂了会自愈(当然是 docker 本身的功能了),易维护指的是即使后面做服务的迁移也是非常方便的,只是简单的文件拷贝,然后 docker compose 启动,没有任何其他的维护成本。

架构:非容器化 nginx 反向代理 + Docker Compose ( Wordpress + MySql)

Docker Compose 工程

Wordpress Docker Compose 工程目录结构:

1
2
3
4
5
wordpress
├── db_data # mysql 数据目录
├── docker-compose.yaml # docker-compose 文件
├── upload.ini # php 文件上传相关配置
└── wp_site # wordpress 静态资源存储目录

docker-compose.yaml:

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
version: '3.3'

services:
db:
image: mysql:5.7
volumes:
- ./db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: example

wordpress:
depends_on:
- db
image: wordpress:5.0.3
volumes:
- ./wp_site:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
ports:
- "9000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: example

uploads.ini:

1
2
3
4
5
file_uploads = On
memory_limit = 128M
upload_max_filesize = 512M
post_max_size = 128M
max_execution_time = 600

外部 Nginx 配置文件

https_server.conf(网站配置文件):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 80;
server_name example.com;
rewrite ^(.*)$ https://$host$1 permanent;
}

server {
listen 443;
ssl on;
ssl_certificate crts/example/example_com.crt;
ssl_certificate_key crts/example/example_com.key;
server_name example.com;
location / {
proxy_pass http://localhost:9002;
include conf.d/common.cfg;
proxy_set_header X-Forwarded-Proto https;
}
}

common.cfg(Nginx 相关配置项):

1
2
3
4
5
6
7
8
9
10
11
proxy_set_header   Host             $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 128m;
client_body_buffer_size 10m;
client_body_temp_path /tmp/client_body_temp;
proxy_connect_timeout 40;
proxy_send_timeout 20;
proxy_read_timeout 20;
proxy_buffer_size 256k;
proxy_buffers 32 64k;

启动服务

进入 docker compose 工程目录执行:

1
docker-compose up -d

Tips

相关 docker compose 指令:
docker-compose stop: 停止已启动的服务,停止后容器还在,只是退出了;
docker-compose start: 启动已停止的服务;
docker-compose down: 停止并清理掉启动的 Docker 容器、卷、网络等相关资源;
docker-compose logs -f: 实时查看日志

构建 Docker 镜像上传到 docker hub

发表于 2019-01-13 | 分类于 Docker

本文演示如何将自己构建的 Docker 镜像推送到 docker hub来实现镜像的共享。

  1. 注册一个 docker hub 账号
    举例:账号名为 qhh0205
  2. 写一个 Dockerfile
    举例:该 Dockerfile 安装了指定版本的 ant 和 jmeter,GitHub 仓库地址:https://github.com/qhh0205/docker-ant-jmeter

    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
    FROM openjdk:8
    MAINTAINER qhh0205 <qhh0205@gmail.com>

    # ant default version: 1.10.5
    # jmeter default version: 5.0
    # Specify version by docker build --build-arg <varname>=<value> ...

    ARG ANT_VERSION=1.10.5
    ENV ANT_HOME=/opt/ant

    ARG JMETER_VERSION=5.0
    ENV JMETER_HOME /opt/jmeter

    RUN apt-get -y update && \
    apt-get -y install wget

    # Installs Ant
    RUN wget --no-check-certificate --no-cookies http://archive.apache.org/dist//ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz \
    && tar -zvxf apache-ant-${ANT_VERSION}-bin.tar.gz -C /opt/ \
    && ln -s /opt/apache-ant-${ANT_VERSION} /opt/ant \
    && rm -f apache-ant-${ANT_VERSION}-bin.tar.gz

    ENV PATH ${PATH}:/opt/ant/bin

    # Installs Jmeter
    RUN wget --no-check-certificate --no-cookies https://archive.apache.org/dist//jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz \
    && tar -zvxf apache-jmeter-${JMETER_VERSION}.tgz -C /opt/ \
    && ln -s /opt/apache-jmeter-${JMETER_VERSION} /opt/jmeter \
    && rm -f apache-jmeter-${JMETER_VERSION}.tgz

    ENV PATH $PATH:/opt/jmeter/bin
  3. 构建镜像
    进入 Dockerfile 所在目录,执行构建命令:

    1
    docker build -t qhh0205/ant-jmeter:1.10.5-5.0 .

    参数说明:
    qhh0205/ant-jmeter:1.10.5-5.0: docker 镜像 tag 名称
    qhh0205: docker hub 账号名
    ant-jmeter: dcoker hub 仓库名
    1.10.5-5.0: 镜像 tag

  4. 登陆 docker hub 账号

    1
    docker login
  5. 上传镜像

    1
    docker push qhh0205/ant-jmeter:1.10.5-5.0

使用 Ansible 统计服务器资源利用率

发表于 2019-01-10 | 分类于 Ansible

分享一个 ansible playbook,统计服务器 CPU、内存、磁盘利用率,3 条 shell 脚本实现统计:

CPU 利用率统计:

1
top -bn1 | grep load | awk '{printf "CPU Load: %.2f\n", $(NF-2)}'

内存利用率统计:

1
free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'

磁盘利用率统计(列出每块磁盘利用率):

1
df -h -t ext2 -t ext4 | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print "Disk Usage:"" " $1 " " $3"/"$2" ""("$5")"}'

Ansible playbook: server-cpu-mem-disk-usage.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
- name: Statistics CPU Memory Disk Utilization
hosts: "{{ hosts }}"
become: no
remote_user: "{{ user }}"
gather_facts: no
tasks:
- name: "Statistics CPU Memory Disk Utilization..."
shell: |
free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'
df -h -t ext2 -t ext4 | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print "Disk Usage:"" " $1 " " $3"/"$2" ""("$5")"}'
top -bn1 | grep load | awk '{printf "CPU Load: %.2f\n", $(NF-2)}'
register: out
- debug: var=out.stdout_lines

输出结果样例:

1
2
3
4
5
6
7
8
9
ok: [gke-test-standard-pool] => {
"out.stdout_lines": [
"Memory Usage: 8766/16052MB (54.61%)",
"Disk Usage: /dev/root 449M/1.2G (37%)",
"Disk Usage: /dev/sda8 28K/12M (1%)",
"Disk Usage: /dev/sda1 61G/95G (64%)",
"CPU Load: 0.92"
]
}

基于谷歌云 gcp 的动态 Ansible inventory 实践

发表于 2019-01-09 | 分类于 Ansible

关于 Ansible inventory 说明

ansible inventory 文件可以分为如下两类:

  1. 静态 inventory:主机信息写死到文件,这种情况一般适用于管理少量主机,对于成百上千规模的主机人工管理主机清单文件显然是不合理的;
  2. 动态 inventory:ansible 根据脚本动态获取云提供商的主机清单文件,这样可以省去人工维护静态清单文件的繁琐工作,对于大批量主机管理也是非常可靠的;

Ansible 动态获取云提供商主机 inventory 原理

ansible 通过 -i 参数指定动态 inventory 目录,该目录底下放置获取云提供商主机清单的脚本(ansible 社区提供的一般是 Python 脚本),ansible 在执行时该脚本会自动执行并将结果保存到内存中。

那么上面说的获取云提供商主机清单的可执行脚本在哪里获取呢?在 这里 (ansible 官方源码仓库:社区提供的脚本)获取,这里有各个云提供商对应的主机清单脚本(*.py)及配置文件(*.ini),比如谷歌的 gce.py 和 gce.ini,Aws 的 ec2.py 和 ec2.ini 等等。

基于 gcp 的动态 inventory 使用

下面是配置使用谷歌云动态 ansible inventory 的详细步骤

  1. 相关软件包安装;

    1
    pip install apache-libcloud pycrypto
  2. 谷歌云控制台创建一个服务账号(需要有 gce 的访问权限),获取 json 认证文件;

  3. 从 ansible 官方仓库 下载 gce.py 和 gce.ini 文件;

    1
    2
    3
    4
    mkdir -p inventories/gcp-dynamic-inventory
    cd inventories/gcp-dynamic-inventory
    wget https://github.com/ansible/ansible/blob/devel/contrib/inventory/gce.py
    wget https://github.com/ansible/ansible/blob/devel/contrib/inventory/gce.ini
  4. 编辑 gce.ini 配置文件

    1
    2
    3
    4
    5
    6
    [gce]
    libcloud_secrets =
    gce_service_account_email_address = <服务账号邮箱:在第 2 步的 json 认证文件里面可以找到>
    gce_service_account_pem_file_path = <第 2 步中 json 认证文件路径:绝对路径>
    gce_project_id = <gcp 项目 id>
    gce_zone =
  5. 测试配置的正确性

    1
    2
    # 如果输出一个很长的 json 串表示没问题
    ./gce.py --list
  6. 执行 ansible 任务

    1
    2
    3
    4
    5
    ansible -i inventories/gcp-dynamic-inventory <pattern> -m <module_name> -a 'module_args'

    Ex:
    # 查看 asia-east1-a 区域的所有主机时间
    ansible -i inventories/gcp-dynamic-inventory asia-east1-a -m shell -a 'date'

    参数说明:
    inventories/gcp-dynamic-inventory: gce.py 脚本所在的目录,ansible 运行时会自动在该目录下执行该脚本获取主机清单;

    pattern:./gce.py –list 执行结果的 json 顶级节点都可以作为 ansible 的目标主机;

    最佳实践:可以给 gce 主机添加 tag,然后通过 tag 对主机分组;

  7. gce.ini 文件位置
    gce.ini 文件没必要必须和 gce.py 在一个目录,可以设置环境变量放到系统其他目录,这样就可以将配置和脚本分离,避免敏感配置放到代码仓库。设置方法:~/.bashrc 文件添加如下内容:

    1
    [[ -s "$HOME/.ansible/gce.ini" ]] && export GCE_INI_PATH="$HOME/.ansible/gce.ini"

Tips

ansible 执行时可以通过 --list-host 参数先测试下本次操作影响到哪些主机,不会真正执行 task;

参考文档

https://temikus.net/ansible-gcp-dynamic-inventory-bootstrap

https://medium.com/vimeo-engineering-blog/orchestrating-gce-instances-with-ansible-d825a33793cd

1…678…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
访问人数 总访问量 次