linux shell 中判断字符串为空的正确方法
2018-07-05 tech linux shell 1 mins 161 字

#!/bin/sh
STRING=
if [ -z "$STRING" ]; then
echo "STRING is empty"
fi
if [ -n "$STRING" ]; then
echo "STRING is not empty"
fi

#!/bin/sh
STRING=
if [ -z "$STRING" ]; then
echo "STRING is empty"
fi
if [ -n "$STRING" ]; then
echo "STRING is not empty"
fi

这篇文章记录在测试环境中部署 kubernetes 1.10 的 HA的过程。
非常感谢这个博主得文章,得以一览 k8s HA 的思路——《关于Kubernetes Master高可用的一些策略》
先看下 kubernetes 的架构:

为了实现没有单点故障的目标,在 kubernetes 内部,需要为以下几个组件建立高可用方案:
etcd 集群的数据中心,用于存放集群的配置以及状态信息,非常重要,如果数据丢失那么集群将无法恢复;因此高可用集群部署首先就是 etcd 是高可用集群。
如果你有3个节点,那么最多允许1个节点失效;当你有5个节点时,就可以允许有2个节点失效。
同时,增加节点还可以让etcd集群具有更好的读性能。因为etcd的节点都是实时同步的,每个节点上都存储了所有的信息,所以增加节点可以从整体上提升读的吞吐量。
kube-apiserver 集群核心,集群API接口、集群各个组件通信的中枢。由于 apiserver 是无状态的,每个master节点的apiserver都是active的,并处理来自Load Balance分配过来的流量;
kube-controller-manager 集群状态管理器 (内部自选举)。当集群状态与期望不同时,kcm会努力让集群恢复期望状态。默认kubeadm安装情况下–leader-elect参数已经设置为true,保证master集群中只有一个kube-controller-manager处于活跃状态;
kube-scheduler 集群Pod的调度中心(内部自选举);默认 kubeadm 安装情况下 –leader-elect 参数已经设置为 true,保证 master 集群中只有一个 kube-scheduler 处于活跃状态;
etcd 的高可用,在前篇的文章里我已经有介绍 ——《etcd ha》。本文介绍其他几个组件的高可用。
本文的操作系统环境为 CentOS Linux release 7.3.1611 (Core)
目前已在 10.19.0.55-57 上装好 etcd,并部署好 vip 10.19.0.230,接下来的目的是在55-57上安装 k8s master 作为ha。
为了保证未来安装时环境保持一致,使用 rpm 包安装 kubeadm kubectl kubelet 组件。
kubeadm-1.10.2-0.x86_64.rpm
kubectl-1.10.2-0.x86_64.rpm
kubelet-1.10.2-0.x86_64.rpm
kubernetes-cni-0.6.0-0.x86_64.rpm
yum install *
安装过程与先前安装 k8s集群相似——《kubernetes 安装入门(centos)》。
修改 kubeadm.conf
$ vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
# 修改 "cgroup-driver"值 由systemd变为cgroupfs
# 原因是 cgroup-driver参数要与docker的一致,否则就会出问题
Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=cgroupfs"
# 第九行增加swap-on=false
Environment="KUBELET_EXTRA_ARGS=--fail-swap-on=false"
启动Docker与kubelet服务
systemctl enable kubelet && systemctl start kubelet
使用 config.yaml 进行初始化配置
apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
etcd:
endpoints:
- https://10.19.0.55:2379
- https://10.19.0.56:2379
- https://10.19.0.57:2379
caFile: /app/allblue/etcd/ssl/ca.pem
certFile: /app/allblue/etcd/ssl/server.pem
keyFile: /app/allblue/etcd/ssl/server-key.pem
dataDir: /app/allblue/etcddata
networking:
podSubnet: 10.244.0.0/16
kubernetesVersion: 1.10.0
api:
advertiseAddress: "10.19.0.230"
token: "b99a00.a144ef80536d4344"
tokenTTL: "0s"
apiServerCertSANs:
- 10.19.0.55
- 10.19.0.56
- 10.19.0.57
- 10.19.0.230
featureGates:
CoreDNS: true
imageRepository: "10.18.1.230:80/k8s.gcr.io"
使用上述的配置文件,如下安装第一台master:
kubeadm init --config cfg/config.yaml --ignore-preflight-errors Swap
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> /etc/bash_profile
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> /etc/bashrc
由于是测试环境,机器内存较为紧张,将swap关闭容易导致系统宕机,故而不按照官方推荐的关闭 swap。
另外,在这里我们提前将安装需要的经镜像存储在本地Harbor镜像仓库中,也可以使用下面的脚本提前下载到本地:
images=(kube-proxy-amd64:v1.10.0
kube-scheduler-amd64:v1.10.0
kube-controller-manager-amd64:v1.10.0
kube-apiserver-amd64:v1.10.0
etcd-amd64:3.1.12 pause-amd64:3.1
kubernetes-dashboard-amd64:v1.8.3
k8s-dns-sidecar-amd64:1.14.8
k8s-dns-kube-dns-amd64:1.14.8
k8s-dns-dnsmasq-nanny-amd64:1.14.8
flannel:v0.9.1-amd64
heapster-amd64:v1.4.2)
for imageName in ${images[@]} ; do
docker pull 10.18.1.230:80/k8s.gcr.io/$imageName
docker tag 10.18.1.230:80/k8s.gcr.io/$imageName k8s.gcr.io/$imageName
docker rmi 10.18.1.230:80/k8s.gcr.io/$imageName
done
docker tag k8s.gcr.io/flannel:v0.9.1-amd64 flannel:v0.9.1-amd64
安装flannel网络
mkdir -p /etc/cni/net.d/
cat <<EOF> /etc/cni/net.d/10-flannel.conf
{
"name": "cbr0",
"type": "flannel",
"delegate": {
"isDefaultGateway": true
}
}
EOF
mkdir /usr/share/oci-umount/oci-umount.d -p
mkdir -p /run/flannel/
cat <<EOF> /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.1.0/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
EOF
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml
复制 master 1 证书到另外两台机器:
cp -r /etc/kubernetes/pki .
将master 1 中的证书导入到本地:
mkdir -p /etc/kubernetes/
cp -r pki /etc/kubernetes/
其它步骤参考第一台配置即可。
检测系统可用性。
echo "kubectl get node"
kubectl get node
echo ""
echo "kubectl get cs"
kubectl get cs
echo ""
echo "kubectl get po --all-namespaces"
kubectl get po --all-namespaces
在master节点查看节点添加命令。
kubeadm token create --print-join-command
为了避免故障,要将 kube-dns的replicas值设为2或者更多,并用anti-affinity将他们部署在不同的Node节点上。

我使用了traefik 作为 ingress 的实现,官网 https://docs.traefik.io/,以下按照 user-guide 进行安装:
# 创建角色和rbac绑定
$ kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-rbac.yaml
# ServiceAccount, DaemonSet(直接绑定主机端口),service
$ kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-ds.yaml
需要注意的是,官方最近修改的 traefik-ds.yaml 修改了两个地方,并不能正常使用,可以参考左侧进行修改。

运行几个ns,几个busybox,几个nginx,nslook试试dns问题。
# ns
apiVersion: v1
kind: Namespace
metadata:
name: demo1
labels:
nsname: demo1
---
apiVersion: v1
kind: Namespace
metadata:
name: demo2
labels:
nsname: demo2
busybox和nginx
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment
namespace: demo2
spec:
selector:
matchLabels:
app: busybox
replicas: 1
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:1.28.4
command: [ "/bin/sh" ]
tty: true
dnsConfig:
options:
- name: single-request-reopen
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-test
namespace: demo2
spec:
selector:
matchLabels:
app: nginx-test
replicas: 2
template:
metadata:
labels:
app: nginx-test
spec:
containers:
- name: nginx
image: nginx:latest
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 80
restartPolicy: Always
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment
namespace: demo1
spec:
selector:
matchLabels:
app: busybox
replicas: 1
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:1.28.4
command: [ "/bin/sh" ]
tty: true
dnsConfig:
options:
- name: single-request-reopen
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-test
namespace: demo1
spec:
selector:
matchLabels:
app: nginx-test
replicas: 2
template:
metadata:
labels:
app: nginx-test
spec:
containers:
- name: nginx
image: nginx:latest
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 80
restartPolicy: Always
service:
kind: Service
apiVersion: v1
metadata:
labels:
app: nginx-test
name: nginx-test
namespace: demo2
spec:
type: NodePort
ports:
- port: 8080
targetPort: 80
nodePort: 32004
selector:
app: nginx-test
---
kind: Service
apiVersion: v1
metadata:
labels:
app: nginx-test
name: nginx-test
namespace: demo1
spec:
type: NodePort
ports:
- port: 8080
targetPort: 80
nodePort: 32003
selector:
app: nginx-test

传统软件工程上来说,对成形软件的测试有着三个阶段:alpha、beta 和gamma,用来标识测试的阶段与范围。alpha 指的是内测,即现在说的 CB,即开发团队内部测试的版本或者有限用户的体验测试版本。beta 指的是公测,即针对所有用户公开的测试版本。而做过一些修改,成为正式发布的候选版本时(现在叫做 RC - Release Candidate),叫做 gamma。
对于互联网产品来说,一般会喜欢将“beta”期做久一点,比如上面有人说的http://weibo.com,还有http://google.com及网络游戏等。这样做的原因可能有所谓“降低被喷的风险”的作用,但是实际上根本原因还是互联网产品其本身特点,因为几乎所有互联网产品都要基于用户参与,如果用户量、交互量未达到产品预期目标,就是在“beta”阶段。这是合乎软件生命周期基本规律的。
开发期Pre-alpha
有时候软体会在Alpha或Beta版本前先释出Pre-alpha版本。一般而言相对于Alpha或Beta版本,Pre-alpha版本是一个功能不完整的版本。
alpha版:内部测试版。α是希腊字母的第一个,表示最早的版本,一般用户不要下载这个版本,这个版本包含很多BUG,功能也不全,主要是给开发人员和 测试人员测试和找BUG用的。
beta版:公开测试版。β是希腊字母的第二个,顾名思义,这个版本比alpha版发布得晚一些,主要是给“部落”用户和忠实用户测试用的,该版本任然存 在很多BUG,但是相对alpha版要稳定一些。这个阶段版本的软件还会不断增加新功能。如果你是发烧友,可以下载这个版本。
rc版:全写:Release Candidate(候选版本),该版本又较beta版更进一步了,该版本功能不再增加,和最终发布版功能一样。这个版本有点像最终发行版之前的一个类似 预览版,这个的发布就标明离最终发行版不远了。
多数开源软件会推出两个RC版本,最后的RC2则成为正式版本。闭源软件较少公开使用,微软在Windows 7 上用了此名称。苹果把在这阶段的产品称为“Golden Master”(简称GM),而最后的GM即成为正式版本。
stable版:稳定版。在开源软件中,都有stable版,这个就是开源软件的最终发行版。在符合需求规格的硬件与操作系统中运行不会造成严重的不相容或是硬件冲突,已受过某定量的测试无误。
RTM版:全称为Release to Manufacture。意思是:发放给生产商。改版程序已经固定,就差工厂包装、光盘印图案等工作了。
OEM版:厂商定制版。
EVAL版:评估版。就是有30或者60天等使用期限的版本。
RTL版:Retail.(零售版),这个版本就是真正发售的版本,有漂亮的包装、光盘、说明书等东西和高昂的价格。

这篇文章记录实践 etcd 的 3节点 HA的过程。分为三个步骤
关于etcd的介绍,请看下一篇文章。
SSL/TSL 认证分单向认证和双向认证两种方式。
官方推荐使用 cfssl 来自建 CA 签发证书,以下参考官方文档的步骤https://coreos.com/os/docs/latest/generate-self-signed-certificates.html.
证书类型介绍:
echo "下载 cfssl"
curl -o /usr/local/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
curl -o /usr/local/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x /usr/local/bin/cfssl*
mkdir -p /etc/etcd/ssl
cd /etc/etcd/ssl
cat >ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"server": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
},
"client": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"client auth"
]
},
"peer": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
EOF
cat >ca-csr.json <<EOF
{
"CN": "etcd",
"key": {
"algo": "rsa",
"size": 2048
}
}
EOF
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
请务必保证 ca-key.pem 文件的安全,*.csr 文件在整个过程中不会使用。
将会生成以下几个文件:
ca-key.pem ca.csr ca.pem
cat >config.json <<EOF
{
"CN": "etcd-0",
"hosts": [
"localhost",
"10.19.0.55",
"10.19.0.56",
"10.19.0.57"
],
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "US",
"L": "CA",
"ST": "San Francisco"
}
]
}
EOF
echo "生成服务器端证书 server.csr server.pem server-key.pem"
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server config.json | cfssljson -bare server
echo "生成peer端证书 peer.csr peer.pem peer-key.pem"
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer config.json | cfssljson -bare peer
至此,所有证书都已生成完毕。
安装etcd时有一个注意事项——要注意版本号。例如 kubernetes 不用的版本对 etcd 最低版本有要求。我是用 1.10 的 kubernetes,配合使用 3.1.18 的 etcd。
wget https://github.com/coreos/etcd/releases/download/v3.1.8/etcd-v3.1.8-linux-amd64.tar.gz
tar -zxvf etcd-v3.1.8-linux-amd64.tar.gz
ln -s /app/allblue/etcd/etcd-v3.1.18-linux-amd64/etcd /usr/bin/etcd
ln -s /app/allblue/etcd/etcd-v3.1.18-linux-amd64/etcdctl /usr/bin/etcdctl
初始化配置文件:
这个文件不用过多关注,因为后文启动配置时会将大部分内容重写覆盖。
vi /etc/etcd/etcd.conf
ETCD_NAME=node01
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="https://0.0.0.0:2379"
#[cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://etcd-0:2380"
ETCD_INITIAL_CLUSTER="node01=https://node01:2380,node02=https://node02:2380,node03=https://node03:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="nodecluster"
ETCD_ADVERTISE_CLIENT_URLS="https://node01:2379"
#ETCD_DISCOVERY=""
#ETCD_DISCOVERY_SRV=""
#ETCD_DISCOVERY_FALLBACK="proxy"
#ETCD_DISCOVERY_PROXY=""
#ETCD_STRICT_RECONFIG_CHECK="false"
#ETCD_AUTO_COMPACTION_RETENTION="0"
#
#[proxy]
#ETCD_PROXY="off"
#ETCD_PROXY_FAILURE_WAIT="5000"
#ETCD_PROXY_REFRESH_INTERVAL="30000"
#ETCD_PROXY_DIAL_TIMEOUT="1000"
#ETCD_PROXY_WRITE_TIMEOUT="5000"
#ETCD_PROXY_READ_TIMEOUT="0"
#
#[security]
ETCD_CERT_FILE="/etc/etcd/ssl/server.pem"
ETCD_KEY_FILE="/etc/etcd/ssl/server-key.pem"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_TRUSTED_CA_FILE="/etc/etcd/ssl/ca.pem"
ETCD_AUTO_TLS="true"
ETCD_PEER_CERT_FILE="/etc/etcd/ssl/peer.pem"
ETCD_PEER_KEY_FILE="/etc/etcd/ssl/peer-key.pem"
#ETCD_PEER_CLIENT_CERT_AUTH="false"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/ssl/ca.pem"
ETCD_PEER_AUTO_TLS="true"
#
#[logging]
#ETCD_DEBUG="false"
# examples for -log-package-levels etcdserver=WARNING,security=DEBUG
#ETCD_LOG_PACKAGE_LEVELS=""
#[profiling]
#ETCD_ENABLE_PPROF="false"
#ETCD_METRICS="basic"
然后对etcd的命令进行配置:
vi /etc/systemd/system/etcd.service
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos
[Service]
Type=notify
WorkingDirectory=/app/allblue/etcddata/
EnvironmentFile=/app/allblue/etcd/etcd.conf
User=root
ExecStart=/usr/bin/etcd \
--name node01 \
--cert-file=/app/allblue/etcd/ssl/server.pem \
--key-file=/app/allblue/etcd/ssl/server-key.pem \
--peer-cert-file=/app/allblue/etcd/ssl/peer.pem \
--peer-key-file=/app/allblue/etcd/ssl/peer-key.pem \
--trusted-ca-file=/app/allblue/etcd/ssl/ca.pem \
--peer-trusted-ca-file=/app/allblue/etcd/ssl/ca.pem \
--initial-advertise-peer-urls https://10.19.0.55:2380 \
--listen-peer-urls https://10.19.0.55:2380 \
--listen-client-urls https://10.19.0.55:2379,http://127.0.0.1:2379 \
--advertise-client-urls https://10.19.0.55:2379 \
--initial-cluster-token etcd-cluster-0 \
--initial-cluster node01=https://10.19.0.55:2380,node02=https://10.19.0.56:2380,node03=https://10.19.0.57:2380 \
--initial-cluster-state new \
--data-dir=/app/allblue/etcddata
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
在配置文件中需要关注[Service] 一栏的配置,包括数据存储目录、环境配置、启动命令等。有以下几个关注点:
/app/allblue/etcd/ssl/ 进行管理,方便不同 server 间的转移。/app/allblue/etcd/ 作为配置目录和ssl目录,以/app/allblue/etcddata 作为数据目录,便于管理。然后设置 etcd 为开机自动启动:
systemctl daemon-reload
systemctl enable etcd
systemctl start etcd
在其他机器上做好配置后,几台机器先后启动:
systemctl start etcd
vi check.sh
#!/usr/bin/env bash
etcdctl \
--ca-file=/app/allblue/etcd/ssl/ca.pem \
--cert-file=/app/allblue/etcd/ssl/peer.pem \
--key-file=/app/allblue/etcd/ssl/peer-key.pem \
--endpoints=https://10.19.0.55:2379,https://10.19.0.56:2379,https://10.19.0.57:2379 \
cluster-health
执行这个脚本,得到下列显示就是ok:
2018-07-02 16:12:01.988067 I | warning: ignoring ServerName for user-provided CA for backwards compatibility is deprecated
2018-07-02 16:12:01.988388 I | warning: ignoring ServerName for user-provided CA for backwards compatibility is deprecated
member 2e433331257e2668 is healthy: got healthy result from https://10.19.0.57:2379
member 884de43739502011 is healthy: got healthy result from https://10.19.0.56:2379
member fb3e4a0d91e719a6 is healthy: got healthy result from https://10.19.0.55:2379
cluster is healthy

有在用波兰 nazwa 家的服务器的同学们要注意一下,他们的 vps 重装系统后没有密码的,要跑进控制台自行设置。这篇文章说明一下 CentOS/Debian 的设置过程,其他系统大同小异。
首先登录到VPS服务器管理面板,转到实例列表并单击Console下拉列表。然后点击蓝色的QEMU行和SendCtrlAltDel重置按钮。
将光标设置为CentOS Linux,然后按e并转到编辑。

在编辑窗口中,转到以linux条目开头的行。删除参数root = UUID = e35103f0-80 …之后的信息并将其替换为以下条目:
rw init = /bin/bash

更改参数后,按CTRL + x或F10启动系统。
检查文件系统是否以读/写(rw)模式安装。
root @(none):/#mount | grep -w /
输入passwd命令并输入root帐户的新密码。
root @(none):/#passwd
创建autorelabel文件。
root @(none):/#touch /.autorelabel

重新启动系统。