快速安装containerd和nerdctl

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
# Download
# 版本灵活变动
export CONTAINERD_VERSION=1.7.13
export CNI_PLUGIN_VERSION=v1.4.0
export RUNC_VERSION=v1.1.11
export NERDCTL_VERSION=1.7.4

wget "https://github.com/containerd/containerd/releases/download/v$CONTAINERD_VERSION/containerd-v$CONTAINERD_VERSION-linux-amd64.tar.gz"
wget "https://github.com/containernetworking/plugins/releases/download/$CNI_PLUGIN_VERSION/cni-plugins-linux-amd64-$CNI_PLUGIN_VERSION.tgz"
wget "https://github.com/opencontainers/runc/releases/download/$RUNC_VERSION/runc.amd64"
wget "https://github.com/containerd/nerdctl/releases/download/v$NERDCTL_VERSION/nerdctl-$NERDCTL_VERSION-linux-amd64.tar.gz"

# Install
tar -Czvxf /usr/local/bin containerd-$CONTAINERD_VERSION-linux-amd64.tar.gz
mv /usr/local/bin/bin/* /usr/local/bin/ && rm -rf /usr/local/bin/bin
mkdir -p /opt/cni/bin && tar Czvxf /opt/cni/bin cni-plugins-linux-amd64-$CNI_PLUGIN_VERSION.tgz
chmod 755 runc.amd64 && mv runc.amd64 /usr/local/bin/runc
tar Czvxf /usr/local/bin nerdctl-$NERDCTL_VERSION-linux-amd64.tar.gz

# Config
mkdir /etc/containerd
containerd config default > /etc/containerd/config.toml
vim /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
endpoint = ["https://k8s-gcr.m.daocloud.io"]
...

# Enable
# cp https://github.com/containerd/containerd/blob/v$CONTAINERD_VERSION/containerd.service to /etc/systemd/system/containerd.service
systemctl daemon-reload
systemctl enable containerd --now

# Test
nerdctl info

CKA

记录刷题- -

Job:创建一个固定结束次数的并行 Job,共 2 个 pod,运行 45 completion,Job pod 打印“Beijing”,镜像自选。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: batch/v1
kind: Job
metadata:
name: job
spec:
parallelism: 2
completions: 45
restartPolicy: Never
template:
metadata:
name: job
spec:
containers:
- name: job
image: busybox:latest
command:
- 'sh'
- '-c'
- 'echo Beijing'

initContainer:pod 的 container 在启动时会检查 volume 中某个目录是否存在某个文件,若不存在则退出,若存在,则运行。要求修改 pod 的 yaml,通过 initContainer 创建该文件,使pod运行。

1

将名为 ek8s-node-1 的 node 设置为不可用,并重新调度该 node 上所有运行的 pods。

1
2
3
4
# cordon:将node设置为不可用,将阻止新pod调度到该节点之上,但不会影响任何已经在其上的 pod,这是重启节点或者执行其他维护操作之前的一个有用的准备步骤,DaemonSet创建的pod能够容忍节点的不可调度属性。
kubectl cordon ek8s-node-1
# drain:从节点安全的驱逐所有pods。
kubectl drain ek8s-node-1 --delete-local-data --ignore-daemonsets --force

在现有的 namespace app-team1 中创建一个名为 cicd-token 的新 ServiceAccount,创建一个 deployment-clusterrole 且只能够创建 Deployment、DaemonSet、StatefulSet 资源的 Cluster Role,并将新的 deployment-clusterrole 绑定到 cicd-token。

1
2
3
4
5
kubectl create sa cicd-token -n app-team1

kubectl create clusterrole deployment-clusterrole --verb=create --resource=Deployment,StatefulSet,DaemonSet

kubectl create rolebinding cicd-rolebinding --clusterrole=deployment-clusterrolr --serviceaccount=app-team1:cicd-token

k8s master 1.20.0 升级到 1.20.1,并升级 kubelet、kubectl。

1
2
3
4
5
6
7
8
9
10
# 将节点设为不可调度性
kubectl cordon mk8s-master-0
kubectl drain mk8s-master-0 --igonre-daemonsets
# 升级
apt-get update
apt-get -y install kubeadm=1.20.1-00
kubeadm update apply v1.20.1 --etcd-upgrade=false
apt-get -y install kubelet=1.20.1-00 kubectl=1.20.1-00
# 将不可调度性去除
kubectl uncordon mk8s-master-0

etcd 备份/恢复。

1
2
3
4
# 备份
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=<trusted-ca-file> --cert=<cert-file> --key=<key-file> snapshot save <backup-file-location>
# 恢复
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=<trusted-ca-file> --cert=<cert-file> --key=<key-file> snapshot restore <backup-file-location>

在 internal 的命名空间下创建一个名为 allow-port-from-namespace 的 NetworkPolicy,此 NetworkPolicy 允许 internal 中的 pod 访问 echo 命名空间中 9000 的端口。

1
2
3
# 先获取echo租户的labels
kubectl get ns echo --show-labels
echo-key: echo-value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-port-from-namespace
namespace: internal
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
echo-key: echo-value
ports:
- protocol: TCP
port: 9000

在 ing-internal 下创建一个 ingress 名为 ping,通过 5678 端口暴露 hello svc 下的 /hello。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ping
namespace: ing-internal
spec:
rules:
- http:
paths:
- path: /hello
backend:
service:
name: hello
port:
number: 5678

将 deployment webserver 扩展至 6 pods。

1
kubectl scale deployment webserver --replicas 6

创建一个名为 nginx-kusc00401 的 pod,镜像为 nginx,调度到 disk=ssd 的节点上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: nginx-kusc00401
spec:
containers:
- name: nginx
image: nginx:1.20.1
imagePullPolicy: Always
ports:
- name: http
containerPort: 80
NodeSelector:
disk=ssd

将一个现有的 pod 集成到 k8s 内置日志记录体系结构中(kubectl logs),使用 busybox 来将名为 sidecar 容器添加到名为 11-factor-app 的 pod 中,sidecar 需运行 /bin/sh -c tail -n+1 -f /var/log/11-factor-app.log ,使用安装在 /var/log 的 volume,使日志文件 11-factor-app.log 可用于 sidecar 容器。

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
apiVersion: v1
kind: Pod
metadata:
name: 11-factor-app
spec:
volumes:
- name: log
emptyDir: {}
containers:
- name: count
image: busybox
command:
- /bin/sh
- -c
- >
i=0;
while true;
do
echo "$i: $(date)" >> /var/log/11-factor-app.log;
i=$((i+1));
sleep 1;
done
volumeMounts:
- name: log
mountPath: /var/log
- name: sidecar
image: busybox
command:
- /bin/sh
- -c
- 'tail -n+1 -f /var/log/11-factor-app.log'
volumeMounts:
- name: log
mountPath: /var/log

通过 pod label name=cpu-utilizer 寻找 cpu 占用最多的 pod,并写入 /opt/tmp/tmp.txt

1
kubectl top pod -A -l name=cpu-utilizer --sort-by=cpu >> /opt/tmp/tmp.txt

Docker

一. Docker介绍

1.1 Docker是什么

Docker 是一个开源的应用容器引擎,基于 Go 语言并遵从 Apache2.0 协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

二. Docker的基本操作

2.1 Docker的安装

1
2
1.安装Docker依赖环境
yum -y install yum-utils device-mapper-persistent-data lvm2
1
2
2.下载Docker镜像源
yum-config-manager --add-repo https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo
1
2
3
3.安装Docker
yum makecache fast
yum -y install docker-ce
1
2
3
4.启动Docker并设为开机自启
systemctl start docker
systemctl enable docker
1
2
3
4
5
5.测试运行hello world
docker run hello-world
...
Hello from Docker!
...

2.2 Docker的中央仓库

1、Docker官方(hub.docker.com):镜像最全,下载最慢

2、国内镜像网站:网易蜂巢(163yun.com)

​ daocloud(hub.daocloud.io)

3、公司内部私服拉取镜像:

1
2
3
4
5
6
7
8
1.配置/etc/docker/daemon.json
{
"registry-mirrors":["https://registry.docker-cn.com"],
"insecure-registries":["ip:port"]
}
2.重启两个服务
systemctl daemon-reload
systemctl restart docker

2.3 Docker镜像的操作

1
2
3
4
1.拉取镜像到本地
docker pull 镜像名称[:tag]
举个栗子
docker pull daocloud.io/library/nginx:1.18.0
1
2
2.查看本地镜像
docker images
1
2
3.删除本地镜像
docker rmi 镜像id(image id)
1
2
3
4.本地镜像的导入导出
docker save -o 导出路径 镜像id
docker load -i 镜像文件
1
2
5.给镜像添加标签
docker tag 镜像id repository:tag

2.4 Docker容器的操作

1
2
3
4
5
6
1.运行容器
docker run 镜像id/镜像名称[:tag]
docker run -d -p 宿主机端口:容器端口 --name 容器名称 镜像id/镜像名称[:tag]
-d:代表后台运行容器
-p:为了映射linux端口和容器端口
--name:指定容器名称
1
2
3
4
2.查看容器
docker ps -qa
-a:查看所有容器,包括没有运行的
-q:只看容器id
1
2
3
3.查看容器日志
docker logs -f 容器id
-f:可以滚动查看容器日志的最后几行
1
2
4.进入容器内部
docker exec -it 容器id bash
1
2
3
4
5
5.停止/删除容器
docker stop 容器id
docker stop $(docker ps -qa)
docker rm 容器id
docker rm $(docker ps -qa)
1
2
6.启动容器
docker start 容器id

三. Docker数据卷

3.1 数据卷操作

数据卷就是将宿主机的一个目录映射到容器的一个目录中

可以在宿主机的目录中直接操作,容器的目录和文件也会跟着改变

创建好的数据卷会存放在一个默认的目录下:/var/lib/docker/volumes/

1
2
1.创建数据卷
docker volume create 数据卷名称
1
2
3
4
2.查看数据卷详细信息
docker volume inspect 数据卷名称
查看全部数据卷
docker volume ls
1
2
3.删除数据卷
docker volume rm 数据卷名称
1
2
3
4
5
6
7
8
9
10
11
4.应用数据卷
#如果映射数据卷不存在,docker会自动创建,会将容器内部的文件存储在数据卷中
docker run -v 数据卷名称:容器内部路径 镜像id
#直接指定一个路径作为存放路径,建议使用这种
docker run -v 数据卷路径:容器内部路径 镜像id
-v:映射数据卷

#可以在指定路径后面加上权限,一旦设置权限就不可以改了,举个栗子
docker run -v /root/nginx:/etc/nginx:rw nginx
ro:只读
rw:可读可写

3.2 实现容器之间数据同步

–volumes-from可以实现容器之间的数据同步,即使有一台容器挂了也不会影响到数据,别的容器仍然会有数据在。

--volumes-from

举个栗子

1
2
#启动容器一,并创建测试目录
docker run -d -v /root/docker/test1:/test1 -v /root/docker/test2:/test2 --name nginx01 镜像id
1
2
3
#启动容器二,挂载容器一的目录
docker run -d --name nginx02 --volumes-from nginx01 镜像id
docker exec -it 容器二id bash

进入容器二内部后可以看见容器一的test1和test2,这时候无论在容器一还是容器二生成或删除文件都会进行同步

数据共享一

用docker inspect 容器id可以看到两个容器映射的都是同一个目录

数据共享二

四. Dockerfile自定义镜像

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

4.1 创建自定义镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
1.创建一个Dockerfile文件,并指定自定义镜像信息
#Dockerfile中常用信息
FROM: 这个镜像的妈妈是谁?(指定基础镜像)
MAINTAINER: 告诉别人,谁负责养它?(指定维护者信息)
CMD: 你想让它干啥(指定容器run前要做什么,只有最后一个会生效)
ENTRYPOINT: 你想让它干啥(指定容器run前要做什么,可以追加命令)
RUN: 你想让它干啥(指定容器run后要做什么)
COPY: 给它点创业资金(拷贝文件到镜像内部)
ADD: 给它点创业资金(拷贝文件到镜像内部,如果是压缩包会解压了再就行拷贝)
WORKDIR: 我是cd(配置工作目录,栗子:如果WORKDIR配置了/home,那么COPY和ADD使用.作为拷贝路径的话都会拷贝到/home下)
VOLUME: 给一个存放行李的地方(设置数据卷,挂载到主机目录)
EXPOSE: 给一个门(指定对外开放的端口号)
ENV: 设置环境变量(environment,也就是参数-e)
1
2
2.在linux上通过docker指定镜像
docker build -t 镜像名称:[tag] .
1
2
3
4
3.举个nginx栗子
cat Dockerfile
FROM daocloud.io/library/nginx:1.18.0
COPY test.html /usr/share/nginx/html/test.html

4.2 CMD和ENTRYPOINT的区别

用CMD和ENTRYPOINT做同一个测试

CMD

1
2
1.创建Dockerfile文件
vim /root/docker/Dockerfile
1
2
3
2.编写
FROM daocloud.io/library/centos:latest
CMD ["ls","-a"]
1
2
3.生成镜像文件
docker build -f /root/docker/Dockerfile -t cmd_centos:1.0 .
1
2
4.启动容器,会发现执行了ls -a
docker run cmd_centos镜像id

cmd测试一

如果启动镜像的时候再加参数会报错,但完整的命令就可以

cmd测试二

cmd测试三

ENTRYPOINT

1
2
3
#除了Dockerfile中的CMD换成了ENTRYPOINT外其它都一样
FROM daocloud.io/library/centos:latest
ENTRYPOINT ["ls","-a"]

这时候我们再追加参数发现可以了,这就是ENTRYPOINT比起CMD有可以追加参数的不同

entrypoint测试一

4.3 制作Tomcat镜像

  1. 准备tomcat压缩包和jdk压缩包

    创建tomcat镜像一.png

  2. 编写Dockerfile文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM daocloud.io/library/centos:7
MAINTAINER cqm's diy tomcat<chenqiming13@qq.com>

ADD jdk-8u271-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.41.tar.gz /usr/local

RUN yum -y install vim

ENV MYPATH /usr/local
WORKDIR $MYPATH #进入容器后就会进入MYPATH目录
ENV JAVA_HOME /usr/local/jdk1.8.0_271
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.41
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.41
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

EXPOSE 8080

CMD /usr/local/apache-tomcat-9.0.41/bin/startup.sh && tailf /usr/local/apache-tomcat-9.0.41/bin/logs/catalina.out
  1. 生成tomcat镜像
1
docker build -t cqm_tomcat:1.0 .
  1. 启动容器
1
docker run -d -p 8080:8080 --name cqm_tomcat -v /root/tomcat/test:/usr/local/apache-tomcat-9.0.41/webapps/test -v /root/tomcat/logs:/usr/local/apache-tomcat-9.0.41/logs cqm_tomcat镜像id
  1. 访问测试

    创建tomcat镜像二

  2. 上传项目

    1
    2
    3
    cd /root/tomcat/test
    mkdir WEB-INF && cd WEB-INF
    vim web.xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
    <display-name>db</display-name>
    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
    </web-app>
    1
    vim ../index.hsp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <title>cqm's tomcat</title>
    </head>
    <body>
    this is cqm's tomcat web!!!<br/>
    <%
    System.out.println("-----this is cqm's tomcat web logs-----");
    %>
    </body>
    </html>
  3. 查看项目是否部署成功

创建tomcat镜像三

创建tomcat镜像四

4.4 上传自定义镜像到Docker Hub

1
2
3
4
5
6
7
1.登录docker账号
docker login -u docker用户名
...
Login Succeeded

#退出账号
docker logout
1
2
3
4
5
6
7
1.登录docker账号
docker login -u docker用户名
...
Login Succeeded

#退出账号
docker logout
1
2
2.给要上传的镜像添加标签
docker tag 镜像id docker用户名/镜像名称:版本号
1
2
3.上传镜像到Docker Hub
docker push 镜像id docker用户名/镜像名称:版本号

上传镜像到dockerhub

五. Docker网络

Docker使用桥接模式在Linux主机上添加一个docker0的网卡,而Docker每启动一个容器就会添加一个新的网卡,使用的是evth-pair技术。evth-pair就是一对虚拟设备接口,添加的网卡都是成对出现的,一段连接镜像,一段连接docker0,所以evth-pair就是一座桥梁,用来连接各种虚拟网络设备。

  1. 利用ip addr来查看Linux主机的网卡情况,可以看到docker0网卡

docker0

  1. 我们拉取一个镜像,并启动

docker网络一

  1. 我们在使用ip addr可以看到出现了对新的网卡

docker网络二

  1. 进入容器内部查看网卡可以看到是对应的

docker网络三

  1. 由此可知docker网络的架构

docker网络四

5.1创建自定义网络

  1. 创建自定义网络
1
2
3
4
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 cqmnet
--driver:网络类型,bridge为桥接
--subnet:配置子网
--gateway:配置网关地址

自定义网络一

  1. 启动两个tomcat容器并指定自定义网络
1
2
docker run -d -p 8081:8080 --net cqmnet --name tomcat01 镜像id
docker run -d -p 8082:8080 --net cqmnet --name tomcat02 镜像id

自定义网络二

  1. 测试两个容器之间是否连通

自定义网络二

5.2 实现docker0和自定义网络之间的连通

  1. 创建两个docker0网段的容器
1
2
docker run -d -p 8083:8080 --name tomcat03 镜像id
docker run -d -p 8084:8080 --name tomcat04 镜像id

自定义网络二

自定义网络二

  1. 利用coonnect进行连通
1
docker network connect cqmnet tomcat03
  1. 这时候再看自定义网络的信息,可以看到tomcat03被添加进来了

自定义网络六

  1. 测试

自定义网络六

  1. 结论,由此可知,connect是将一个网络中的容器添加到另一个网络中,通俗来讲就是该容器同时拥有两个网络的地址,就实现了两个网络之间的互通。

自定义网络六自定义网络九

5.3 不同主机之间容器的互联

方法一:静态路由

静态路由方法原理就是将容器的请求转发到指定的主机上,再由主机转发给容器。

  1. 修改 daemon.json 添加以下内容,使之每个主机的 docker 默认网段不同
1
2
3
4
# 主机一
"bip": "172.17.1.10/24"
# 主机二
"bip": "172.17.2.10/24"
  1. 添加路由规则
1
2
3
4
# 主机一,网关为主机二的IP地址,即访问172.17.2.0网段的数据包都会被转发到主机二上
route add -net 172.17.2.0 netmask 255.255.255.0 gw 192.168.88.130
# 主机二
route add -net 172.17.1.0 netmask 255.255.255.0 gw 192.168.88.135

方法二:Macvlan

Macvlan 原理是将一张物理网卡虚拟成多块虚拟网卡的技术,使得虚拟网卡与物理网卡的参数等都相同。

  1. 开启网卡的混杂模式
1
ip link set ens33 promisc [on/off] / ifconfig ens33 [-]promisc
  1. 创建 Macvlan 网络
1
2
# 子网和网关均和主机一致
docker network create --driver macvlan --subnet 192.168.88.0/24 --gateway 192.168.88.1 -o parent=ens33 [network_name]
  1. 创建容器需要指定网络
1
docker run -d -it --name centos --net [network_name] --ip 192.168.88.10 centos:7

5.4 Overlay

overlay 是一种在网络架构上进行叠加的虚拟化技术,即在原有的网络框架上叠加一层虚拟网络,从而实现应用在虚拟网络上承载,以及与其它网络业务分离。

在这个overlay网络模式里面,有一个类似于服务网关的地址,然后把这个包转发到物理服务器这个地址,最终通过路由和交换,到达另一个服务器的ip地址。

实现 overlay 网络需要有注册发现中心的键值数据库支持,可以用 consul、etcd、zookeeper 等。

区别:

  • consul:服务发现/全局的分布式 key-value 存储。自带 DNS 查询服务,可以跨数据中心。提供节点的健康检查,可以实现动态的 consul 节点增减,docker 官方的用例推荐。
  • etcd:服务发现/全局的分布式 key-value 存储。静态的服务发现,如果实现动态的新增etcd节点,需要依赖第三方组件。

5.4.1 consul

consul 用于微服务下的服务治理,主要特点有:服务发现、服务配置、健康检查、键值存储、安全服务通信、多数据中心等。

注意:overlay 所需内核版本为 3.18+

consul实现overlay架构

  1. 安装 consul
1
2
curl -O https://releases.hashicorp.com/consul/1.10.3/consul_1.10.3_linux_amd64.zip
unzip consul_1.10.3_linux_amd64.zip
  1. 准备 config.json 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"advertise_addr": "192.168.88.130", # 更改我们向群集中其他节点通告的地址
"bind_addr": "192.168.88.130", # 内部群集通信绑定的地址,这是群集中所有其他节点都应该可以访问的IP地址
"data_dir": "/opt/consul", # 数据存放目录
"server": true, # 是否是server agent节点
"node_name": "server1", # 节点名称
"enable_syslog": true,
"enable_debug": true,
"log_level": "info", # 日志级别
"bootstrap_expect": 3, # 提供数据中心中预期服务器的数量,即需要3台consul server agent
"start_join": ["192.168.88.130", "192.168.88.135", "192.168.88.100"], # 启动时指定节点地址的字符串数组,指定是其他的consul server agent的地址
"retry_join": ["192.168.88.130", "192.168.88.135", "192.168.88.100"], # 允许start_join时失败时,继续重新连接
"ui": true, # 启动ui界面
"client_addr": "0.0.0.0" # Consul将绑定客户端接口的地址,包括HTTP和DNS服务器
}
  1. 启动 consul 查看状态
1
2
3
4
5
./consul agent -config-dir ./config.json
# 查看集群状态
./consul members
# 查看leader
./consul operator raft list-peers
  1. 访问 consul ui

consul_ui

  1. 配置 docker/daemon.json
1
2
3
# 虽然是consul集群,但只能填写一个consul节点的信息
"cluster-store": "consul://192.168.88.130:8500",
"cluster-advertise": "192.168.88.130:2376"
  1. 创建 docker 网络,去到其它主机查看 docker 网络可以看到是已经同步的
1
docker network create --driver overlay consulnet

docker_consul_overlay

  1. 在不同的主机创建容器测试是否能够互联
1
docker run -d --name centos1 --network consulnet centos:7

在容器内部可以看到有两张网卡,eth0 是 overlay 创建的,即一个 vxlan;eth1 则是服务网关的网卡

overlay中容器网卡

5.4.2 etcd

etcd 是一个高可以的键值存储系统,主要用于分享配置和服务发现。

  1. 安装 etcd
1
2
3
4
5
6
7
8
9
10
11
ETCD_VER=v3.5.1
GITHUB_URL=https://github.com/etcd-io/etcd/releases/download
DOWNLOAD_URL=${GOOGLE_URL}

curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar -zvxf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz

/tmp/etcd-download-test/etcd --version
/tmp/etcd-download-test/etcdctl version
/tmp/etcd-download-test/etcdutl version
  1. 启动 etcd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HOST=192.168.88.130
CLUSTER_IPS=(192.168.88.130 192.168.88.135 192.168.88.136)
ETCD_DATADIR=/root/etcd/data
nohup ./etcd -name node1 \
-initial-advertise-peer-urls http://$HOST:2380 \
-listen-peer-urls http://$HOST:2380 \
-listen-client-urls http://$HOST:2379,http://127.0.0.1:2379 \
-advertise-client-urls http://$HOST:2379 \
-initial-cluster-token etcd-cluster \
-initial-cluster node1=http://${CLUSTER_IPS[0]}:2380,node2=http://${CLUSTER_IPS[1]}:2380,node3=http://${CLUSTER_IPS[2]}:2380 \
-initial-cluster-state new \
-data-dir $ETCD_DATADIR &
# listen-peer-urls:节点与节点之间数据交换, 因此需要监听在其他节点可以访问的IP地址上,默认端口2380
# listen-client-urls:用户客户机访问etcd数据, 一般监听在本地, 如果需要集中管理, 可以监听在管理服务器可以访问的IP地址上,默认端口2379
# initial-advertise-peer-urls:表示节点监听其他节点同步信号的地址,默认端口2380
# advertise-client-urls:在加入proxy节点后, 会使用该广播地址, 因此需要监听在一个proxy节点可以访问的IP地址上,默认端口2379
  1. 查看 etcd 集群信息和健康状态
1
2
./etcdctl member list
./etcdctl cluster-health
  1. 修改 docker/daemon.json
1
2
"cluster-store": "etcd://192.168.88.130:2379",
"cluster-advertise": "192.168.88.130:2376"
  1. 查看是否注册到 etcd
1
2
./etcdctl ls /
/docker
  1. 创建 docker 网络
1
docker network create --driver overlay etcdnet
  1. 创建容器测试是否互通
1
docker run -d -it --name centos1 --network etcdnet centos:7

5.5 Flannel与Calico

在 Kubernetes 中,使用的网络组件主要为 Flannel 和 Calico 两种,都可以实现不同主机之间容器的通信,使用前提是要有 etcd。

5.5.1 Flannel

Flannel 网络架构如下

flannel

  1. 添加网络配置到 etcd
1
2
3
# Network:flannel网段
# Type:网络类型
./etcdctl set /coreos.com/network/config '{ "Network": "10.0.0.0/24", "Backend": {"Type": "vxlan"} }'
  1. 安装 Flannel
1
curl -O https://github.com/flannel-io/flannel/releases/download/v0.15.0/flannel-v0.15.0-linux-amd64.tar.gz
  1. 启动 Flannel,会生成 /run/flannel/subnet.env,路由表也会加上新规则
1
2
# -etcd-endpoints:etcd地址
./flanneld -etcd-endpoints "http://192.168.88.30:2379"

flannel路由

  1. 生成 docker 参数信息
1
./mk-docker-opts.sh -d /root/etcd/docker -c
  1. 修改 /usr/lib/systemd/system/docker.service
1
2
3
4
5
vim /usr/lib/systemd/system/docker.service
# 添加
EnvironmentFile=/root/etcd/docker
# 在ExecStart后加上
ExecStart=/usr/bin/dockerd $DOCKER_OPTS
  1. 重启 docker 后测试容器是否能够互联

flannel测试

5.5.2 Calico

Calico 是目前企业在 k8s 集群上使用的最多的容器互通网络方案,比起 Flannel 能够实现更过复杂的需求。

Calico 是一种纯三层的解决方案,因此避免了与二层方案相关的数据包封装操作,中间没有任何 NAT 和 overlay,直接走 TCP/IP 协议栈,通过 iptables 实现复杂的网络规则。

calico架构

Calico 组件如下:

  • Felix:运行在每一台主机中,主要负责网络接口管理和监听、路由、ARP 管理、ACL 管理和同步、状态上报等。
  • etcd:分布式键值存储,保证网络数据的一致性。
  • BIDR(BGP Client):每一个主机都会有个 BIDR,用来实现不同的路由协议,Calico 监听主机上 Felix 注入的信息,然后通过 BGP 协议告诉其它节点,从而实现互联。
  • BGP Route Reflector:用于解决网络规模过大的组件。

Calico 工作模式:

  • IPIP:将 IP 数据包再次封装到一个 IP 包里,相当于一个基于网络层的网桥,普通的网桥是基于 Mac 地址的,而 IPIP 能将两端的路由做成一个 tunnel,将其连接。
  • BGP:即边界网关协议。
  1. 在所有主机上下载 Calico Controller
1
curl -o calicoctl -O -L  "https://github.com/projectcalico/calicoctl/releases/download/v3.21.0/calicoctl"
  1. 配置 calicoctl.cfg
1
2
3
4
5
apiVersion: projectcalico.org/v3
kind: CalicoAPIConfig
metadata:
spec:
etcdEndpoints: http://192.168.88.30:2379
  1. 初始化 Calico
1
2
# ip:宿主机ip
./calicoctl node run --ip=192.168.88.30 -c ./calicoctl.cfg
  1. 查看 Calico 状态
1
./calicoctl node status
  1. 由于新版本的 Calico 不再支持 docker 单独使用,但通过插件可以实现
1
2
3
4
git clone https://github.com/projectcalico/libnetwork-plugin.git
cd libnetwork-plugin
# 下载插件镜像
make image
  1. 运行 libnetwork 容器
1
docker run -d --privileged --name calico-docker-network-plugin --net host --restart always -v /run/docker/plugins:/run/docker/plugins -e ETCD_ENDPOINTS=http://192.168.88.30:2379 calico/libnetwork-plugin
  1. 创建 ippool
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看calico目前地址池
./calicoctl get ippools
# 创建地址池yaml文件
cat ip.yaml
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: ippool-test
spec:
cidr: 10.0.0.0/24
ipipMode: Nerver
natOutgoing: true
disabled: false
nodeSelector: all()
./calicoctl apply -f ip.yaml
  1. 创建 GlobalNetworkPolicy
1
2
3
4
5
6
7
8
9
10
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: gnp-test
spec:
selector: all
ingress:
- action: Allow
egress:
- action: Allow
  1. 创建 docker 网络
1
docker create network -d calico --ipam-driver calico-ipam --subnet 10.0.0.0/24 caliconet
  1. 关闭 ipv6 内核
1
echo 1 > /proc/sys/net/ipv6/conf/default/disable_ipv6
  1. 创建容器测试
1
docker run -d -it --name centos1 --network caliconet centos:7

六. Docker-Compose

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

6.1 Docker-Compose使用步骤

1
2
3
1.使用 Dockerfile 定义应用程序的环境
2.使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行
3.执行 docker-compose up 命令来启动并运行整个应用程序

6.2 安装Docker-Compose

1
2
1.下载
curl -L https://get.daocloud.io/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
1
2
2.赋权
chmod +x /usr/local/bin/docker-compose

6.3 编写Docker-Compose管理MySQL和Nginx容器

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
1.创建数据卷目录
mkdir /root/docker_mysql_nginx/docker_mysql/mysql_data
mkdir /root/docker_mysql_nginx/docker_nginx/nginx_conf
mkdir /root/docker_mysql_nginx/docker_nginx/nginx_html

2.编写
vim /root/docker_mysql_nginx/docker-compose.yml

version: '3.8' #指定本 yml 依从的 compose 哪个版本制定的
#可参考https://docs.docker.com/compose/compose-file/
services:
mysql: #指定服务的名称
restart: always #只要docker启动,这个容器就一起启动
image: daocloud.io/library/mysql:5.7.4 #指定镜像路径
container_name: mysql #指定容器名称
ports:
- 3306:3306 #指定端口号的映射
environment:
MYSQL_ROOT_PASSWORD: toortoor #指定mysql的root用户登录密码
TZ: Asia/Shanghai #指定时区
volumes:
- /root/docker_mysql_nginx/docker_mysql/mysql_data:/var/lib/mysql #映射数据卷

nginx:
restart: always
image: daocloud.io/library/nginx:1.18.0
container_name: nginx
ports:
- 80:80
environment:
TZ: Asia/Shanghai
volumes:
- /root/docker_mysql_nginx/docker_nginx/nginx_conf:/etc/nginx
- /root/docker_mysql_nginx/docker_nginx/nginx_html:/usr/share/nginx/html

6.4 使用Docker-Compose命令

在使用Docker-Compose命令时,系统会在当前目录下寻找yml文件

1
2
3
1.应用Docker-Compose启动容器
docker-compose up -d
-d:在后台运行容器
1
2
2.关闭并删除容器
docker-compose down
1
2
3.开启|关闭|重启已由docker-compose管理的容器
docker-compose start | stop | restart
1
2
4.查看由docker-compose管理的容器
docker-compose ps
1
2
3
5.查看docker-compose日志
docker-compose logs -f
-f:可以滚动查看

6.5 Docker-Compose配合Dockerfile使用

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
version: '3.8'
services:
nginx:
build:
context: ./ #指定在当前目录下寻找dockerfile
dockerfile: Dockerfile #指定dockerfile文件名
image: nginx:1.18.0 #使用上边制作好的镜像
container_name: nginx
ports:
- 8080:80
environment:
TZ: Asia/Shanghai

Dockerfile

1
2
from daocloud.io/library/nginx:1.18.0
copy test.html /usr/share/nginx/html/test.html

遇到的错误

1
2
3
4
5
6
7
8
9
10
11
1.
ERROR: yaml.scanner.ScannerError: while scanning for the next token
found character '\t' that cannot start any token
#是因为yml文件里使用了tab,yml文件格式不允许使用,全部换成空格

2.
WARNING: Image for service nginx was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
#是因为运行docker-compose时自定义镜像不存在,会帮助生成自定义镜像
#提供两种构建方法:
#docker-compose build
#docker-compose up --build

6.6 Docker-Compose部署wordpress

  1. 编写docker-compose.yml
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
version: '3.8'
services:
db:
container_name: mysql
image: daocloud.io/library/mysql:latest
volumes:
- db_data:/var/lib/mysql
restart: always
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: toortoor
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: toortoor

wordpress:
container_name: wordpress
image: wordpress:latest
ports:
- 80:80
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: toortoor
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
  1. 启动
1
docker-compose up -d

Ansible

一、Ansible简介

ansbile是一个IT自动化的配置管理工具,自动化主要体现在Ansible集成了丰富的模块,可以通过一个命令完成一系列的操作,进而减少运维重复性的工作和维护成本,提高工作效率。

ansible图标

1.1 为什么需要ansible

思考:假设我们要在10台服务器上安装并运行nginx,要如何操作?

  1. ssh远程登录到服务器
  2. 执行yum -y install nginx
  3. 执行systemctl start nginx
  4. 执行systemctl enable nginx
  5. 重复十次。。。

可以看到简单的工作要做十次是很浪费时间的,这时候我们就需要ansible了。

1.2 ansible有哪些功能

  1. 批量执行远程命令,可以同时对N台主机执行命令
  2. 批量配置软件服务,可以用自动化的方式配置和管理服务
  3. 实现软件开发功能,jumpserver底层使用ansible来实现自动化管理
  4. 编排高级的IT任务,playbook是ansbile的一门编程语言,可以用来描绘一套IT架构

ansbile架构

二、Ansible安装

2.1 在控制端上安装ansible

直接利用yum源安装即可,ansible配置文件一般不需要做变动

1
yum -y install ansible

2.2 ansible配置文件的优先级

  1. 如果当前目录不存在ansible.cfg,会采用默认的配置文件
1
2
3
4
ansible --version
...
config file = /etc/ansible/ansible.cfg
...
  1. 如果当前目录存在ansible.cfg,则会采用当前目录的配置文件
1
2
3
4
ansible --version
...
config file = /root/project1/ansible.cfg
...

2.3 ansible的Inventory

利用密钥来连接被控端

  1. 在项目目录下创建hosts文件
1
2
3
4
5
cd project1
vim hosts
[cqm]
192.168.88.133
192.168.88.134
  1. 创建密钥
1
2
3
ssh-keygen
ls /root/.ssh/
authorized_keys id_rsa id_rsa.pub known_hosts
  1. 将公钥传送至被控端主机
1
2
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.88.133
ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.88.134
  1. 测试是否不需要密码就可以连接
1
2
ssh root@192.168.88.133
ssh root@192.168.88.134

三、Ansible的ad-hoc和常用模块

3.1 ansible的ad-hoc

ad-hoc简而言之就是“临时命令”,执行完就结束,并不会保存

主要格式为:ansible + -i + 指定主机清单 + 指定主机组名称 + -m + 指定模块 + -a + 具体命令

使用ad-hoc后返回结果的颜色:

  1. 绿色:被控端主机没有发生变化
  2. 黄色:被控端主机发生了变化
  3. 红色:出现故障

3.2 ansible基本命令

  1. 直接执行命令
1
ansible -i hosts 主机组名 -m 模块 -a 命令
  1. 列出某个主机组的主机清单
1
ansible -i hosts 主机组名 --list-hosts
  1. 查看某个模块使用教程
1
2
ansible-doc 模块名称
/EX

3.2 shell和command模块

shell和command本质上都是用来执行linux的基础命令,如cat、ls等等,但command不支持用|这种管道符命令

例子,同样的命令在command上会报错

1
2
ansible -i hosts cqm -m shell -a 'ps -ef | grep nginx'
ansible -i hosts cqm -m command -a 'ps -ef | grep nginx'

shell和command

3.3 yum模块

1
2
3
4
5
6
7
ansible -i hosts cqm -m yum -a 'name=httpd state=latest enablerepo=epel'
name:指定安装软件名称
state:
1.latest为安装最新版本
2.absent为卸载
3.present为安装
enablerepo:指定在哪个yum源下安装

3.4 copy模块

1
2
3
4
5
6
7
ansible -i hosts cqm -m copy -a 'src=./httpd.conf dest=/etc/httpd/conf/httpd.conf owner=www group=www mode=0755'
src:表示要复制的文件路径
dest:要复制到被控端的哪个路径
owner:复制文件的属主
group:复制文件的属组
mode:文件的权限,0755为rwxr-xr-x(r:4,w:2,x:1),也可以写成(u+rwx,g+x,o-rwx)的形式
backup:如果被控端有同名文件,是否保留

3.5 file模块

1
2
3
#更改文件权限
ansible -i hosts cqm -m file -a 'path=/etc/nginx/nginx.conf owner=www group=www mode=0644'
path:代表要改变文件的路径(被控端)
1
2
3
4
5
#创建符号连接(软连接)
ansible -i hosts cqm -m file -a 'src=/root/test1 dest=/root/test2 mode=0755 state=link'
src:源文件路径(被控端)
dest:软连接路径(被控端)
state:link创建软连接
1
2
3
4
5
6
7
8
#创建文件和文件夹
ansible -i hosts cqm -m file -a 'path=/root/text state=touch/directory mode=0644 recurse=yes'
path:要创建在哪
state:
1.touch表创建文件
2.directory表创建文件夹
3.absent表删除
recurse: 递归授权

3.6 service模块

1
2
3
4
5
6
7
8
ansible -i hosts cqm -m service -a 'name=httpd state=started enabled=yes'
name:服务名称
state:
1.started启动
2.stopped关闭
3.restarted重启
4.reloaded重载
enabled:是否开机自启

3.7 group模块

1
2
3
4
5
6
7
ansible -i hosts cqm -m group -a 'name=cqm state=present gid=1000 system=yes/no'
name:创建组名称
state:
1.present创建组
2.absent删除组
gid:组id
system:是否设置为系统组

3.8 user模块

1
2
3
4
5
6
7
8
9
ansible -i hosts cqm -m user -a 'name=cqm uid=1000 group=cqm shell=/bin/bash state=present create_home=no'
name:创建用户名称
uid:用户id
group:添加到哪个组
shell:使用/bin/bash创建用户,或/sbin/nologin
state:
1.present创建用户
2.absent删除用户
create_home:是否创建家目录,默认为yes
1
2
3
4
ansible -i hosts cqm -m user -a 'name=cqm uid=1000 groups=cqm1,cqm2 append=yes state=present system:yes'
groups:添加到哪些组
append:添加到多个组时添加该项
system:是否设置为系统用户
1
2
ansible -i hosts cqm -m user -a 'name=cqm state=absent remove=yes'
remove:是否删除家目录

3.9 cron模块

1
2
3
4
5
6
7
8
9
10
11
ansible -i hosts cqm -m cron -a 'name="check dirs" minute=0 hour=5,2 job="ls -al > /dev/null"'
name:创建定时任务名称
minute:分钟
hour:小时
job:任务
state:删除定时任务用absent

#在被控端可查看
crontab -l
#Ansible: check dirs
0 5,2 * * * ls -al > /dev/null

3.10 mount模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ansible -i hosts cqm -m mount -a 'path=/root/data src=/dev/sr0 fstype=iso9660 opts="ro" state=present'
path:挂载到哪
src:被挂载的目录,可以是以下形式
1.UUID形式:src="UUID=......"
2.ip形式:src="192.168.88.128:/data",一般用于挂载nfs服务器的目录
fstype:挂载类型
1.iso9660:文件系统是一个标准CD-ROM文件系统
2.ext4:Linux系统下的日志文件系统
3.xfs:高性能的日志文件系统
4.none
state:挂载类型
1.present:开机挂载,仅将挂载配置写入/etc/fstab
2.mounted:挂载设备,并将配置写入/etc/fstab
3.unmounted:卸载设备,不会清除/etc/fstab写入的配置
4.absent:卸载设备,会清理/etc/fstab写入的配置
opts:权限等,默认填defaults
/etc/fstab:磁盘被手动挂载之后都必须把挂载信息写入/etc/fstab这个文件中,否则下次开机启动时仍然需要重新挂载。

3.11 firewalld模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ansible -i hosts cqm -m firewalld -a 'service=https permanent=yes enable=yes immediate=yes state=enabled'
service:放行服务
port:放行端口,如80/tcp
source:放行指定ip地址范围,如192.168.88.0/24
state:表防火墙策略状态
1.enabled策略生效
2.disabled禁用策略
3.present添加策略
4.absent删除策略
zone:指定防火墙信任级别
1.drop:丢弃所有进入的包,而不给出任何响应
2.block:拒绝所有外部发起的连接,允许内部发起的连接
3.public:允许指定的进入连接
4.internal:范围针对所有互联网用户
permanent:yes为永久生效
immediate:yes为立即生效

3.12 unarchive模块

unarchive模块能够实现解压再拷贝的功能

1
2
3
4
ansible -i hosts cqm -m unarchive -a 'src=./apache.tar.gz dest=/etc/httpd mode=0755 owner=www group=www'
src:压缩文件路径
dest:解压到哪
remote_src:设置为yes时表示压缩包已经在被控端主机上,而不是ansible控制端本地

3.13 get_url模块

1
2
3
4
...
url:下载链接
dest:保存文件地址
mode:设置权限

3.14 template模块

template与copy用法相同,但template可以实别变量,而copy不行。

3.15 yum_repository模块

yum_repository可以用来配置yum源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- name: Configure Nginx YUM Repo
yum_repository:
name: nginx
description: Nginx YUM Repo
file: nginx
baseurl: http://nginx.org/packages/centos/7/$basearch/
gpgcheck: no
enabled: yes
state: present
name:相当于.repo文件中括号的[仓库名]
description:相当于.repo文件中的name
file:相当于.repo文件的名称
baseurl:相当于.repo文件中baseurl
gpgcheck:相当于.repo文件中gpgcheck
enabled:相当于.repo文件中enabled

四、Ansible的Playbook

playbook是ansible的另一种使用方式,被称为“剧本”,与ad-hoc不同的是,playbook可以实现持久使用。

playbook是由一个或多个play组成的列表,play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色,从根本上来讲,所谓的task无非就是调用ansible的一个module,将多个play组织在一个playbook中,即可实现同时完成多项任务。

playbook的核心元素

  1. hosts:被控主机清单
  2. tasks:任务集
  3. vars:变量
  4. templates:模板
  5. handlers和notify:触发器
  6. tags:标签

4.1 利用playbook安装apache

playbook采用的是yml语法,举个栗子

1
2
cd /etc/project1
vim httpd.yml
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
- hosts: cqm
tasks:

#安装apache服务
- name: Install Httpd Service
yum:
name: httpd
state: latest

#创建www组
- name: Create www Group
group:
name: www
state: present

#创建www用户
- name: Create www User
user:
name: www
group: www
shell: /sbin/nologin
state: present

#修改apache配置文件
- name: Configure Httpd Conf
copy:
src: ./httpd.conf
dest: /etc/httpd/conf/httpd.conf
owner: www
group: www
mode: 0644

#启动服务并设为开机自启
- name: Start Httpd Service
service:
name: httpd
state: started
enabled: yes

#放行端口
- name: Configure Firewall
firewalld:
zone: public
ports: 8080/tcp
permanent: yes
immediate: yes
state: enabled
1
2
3
4
#检查语法
ansible-playbook --syntax -i hosts httpd.yml
#执行playbook
ansible-playbook -i hosts httpd.yml

4.2 利用playbook部署nfs

  1. 准备好nfs配置文件
1
2
3
cd /root/project1
cat exports
/root/nfs_data 192.168.88.0/24(rw,no_root_squash)
  1. 编写nfs.yml文件
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
61
62
#nfs主机
- hosts: 192.168.88.133
tasks:

- name: Create Share Directory
file:
path: /root/nfs_data
mode: 0744
owner: root
group: root
recurse: yes
state: directory

- name: Install NFS Service
yum:
name: nfs-utils
state: latest

- name: Configure NFS
copy:
src: ./exports
dest: /etc/exports
backup: yes

- name: Start NFS Service
service:
name: nfs
state: started
enabled: yes

- name: Stop Firewall Service
service:
name: firewalld
state: stopped

#挂载nfs目录的主机
- hosts: 192.168.88.134
tasks:

- name: Create Share Directory
file:
path: /root/nfs_data
state: directory

- name: Install NFS Service
yum:
name: nfs-utils
state: latest

- name: Start NFS Service
service:
name: nfs
state: started
enabled: yes

- name: Mount NFS Share Directory
mount:
path: /root/nfs_data
src: 192.168.88.133:/root/nfs_data
fstype: nfs
opts: defaults
state: mounted
  1. 执行playbook
1
ansible-playbook -i hosts nfs.yml

nfs挂载

五、Ansible的变量

5.1 在play中设置变量

1
2
3
4
5
6
7
8
9
10
11
12
- hosts: cqm
vars:
- web_package: httpd
- ftp_package: vsftpd
tasks:

- name: Install {{ web_package }} and {{ ftp_package }} Service
yum:
name:
- "{{ web_package }}"
- "{{ ftp_package }}"
state: latest

5.2 在vars_file中设置变量

1
2
3
cat vars.yml
web_package: httpd
ftp_package: vsftpd
1
2
3
4
5
6
7
8
9
10
11
- hosts: cqm
vars_files:
- ./vars.yml
tasks:

- name: Install {{ web_package }} and {{ ftp_package }} Service
yum:
name:
- "{{ web_package }}"
- "{{ ftp_package }}"
state: latest

5.3 通过inventory主机清单设置变量

  1. 创建两个变量目录
1
2
mkdir hosts_vars
mkdir groups_vars
  1. 在groups_vars目录中针对某个组创建变量
1
2
3
4
5
cat group_vars/cqm
web_package: httpd
ftp_package: vsftpd

#groups_vars目录中也可以新建all来设置变量,这样所有的主机组都可以调用
  1. 在hosts_vars目录中针对某个主机创建变量
1
2
3
cat hosts_vars/192.168.88.133
web_package: httpd
ftp_package: vsftpd

5.4 在执行playbook时通过 -e 参数设置变量

1
2
3
4
5
6
7
- hosts: cqm
tasks:
- name: Install {{ web_package }} Service
yum:
name:
- "{{ web_package }}"
state: latest
1
ansible-playbook -i hosts -e 'web_package=httpd' test.yml

5.5 ansible变量的优先级

优先级由上至下递减

  1. -e(外置传参)
  2. vars_files
  3. play中的vars
  4. hosts_vars
  5. groups_vars中的某个主机组
  6. groups_vars中的all

5.6 register注册变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- hosts: cqm
tasks:
- name: Install Apache Service
yum:
name: httpd
state: latest
- name: Start Apache Service
service:
name: httpd
state: started
- name: Check Apache Process
shell: ps -ef | grep httpd
#将结果储存到变量里
register: check_apache

#利用debug模块调用
- name: Output check_apache
debug:
#输出msg中的stdout_lines
msg: "{{ check_apache.stdout_lines }}"

5.7 facts变量

facts变量是ansible控制端采集被控端的变量,可以直接调用

为了方便可以将facts变量写到文本里

1
ansible -i hosts cqm -m setup > fasts.txt

六、Ansible语句

6.1 条件判断when

centos安装apache是httpd,而ubuntu安装apache则是httpd2,所以这时候就需要条件判断了

例一:不同系统安装apache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- hosts: cqm
tasks

- name: Centos Install Apache Service
yum:
name: httpd
state: latest
when:
- ( ansible_distribution == "CentOS" )

- name: Utunbu Install Apache2 Service
yum:
name: httpd2
state: latest
when:
- ( ansible_distribution == "Utunbu" )

例二:给主机名带有web的主机配置yum源

1
2
3
4
5
6
7
8
9
10
11
- hosts: cqm
tasks:
- name: Configure Cqm Yum Repo
yum_repository:
name: cqm
description: cqm yum repo
baseurl: http://www.cqmmmmm.com
gpgcheck: no
enabled: yes
when:
- ( ansible_fqdn is match ("web*") )

例三:判断nfs服务是否启动,没有则启动,否则重启

利用echo $?的返回值来查看是否启动

when判断一

when判断二

1
2
3
4
5
6
7
8
9
10
11
12
13
- hosts: cqm
tasks:
- name: Check NFS Service Status
shell: systemctl is-active nfs
ignore_errors: yes
register: check_nfs

- name: Restart NFS Service
service:
name: nfs
state: restarted
when:
- ( check_nfs.rc == 0 )

6.2 循环语句with_items

with_items可以实现循环,可以减少playbook中同样模块的使用次数

例一:利用with_items重启nginx和php服务

1
2
3
4
5
6
7
8
9
- hosts: cqm
tasks:
- name: Restart Nginx and PHP Service
service:
name: {{ item }}
state: restarted
with_items:
- nginx
- php-fpm

例二:利用变量循环安装多个服务

1
2
3
4
5
6
7
8
9
10
- hosts: cqm
tasks:
- name: Install Nginx and Mysql Service
yum:
name: {{ pack }}
state: latest
vars:
pack:
- nginx
- mysql-server

例三:利用循环创建多个用户

1
2
3
4
5
6
7
8
9
10
- hosts: cqm
tasks:
- name: Create Multiple Users
user:
name: {{ item.name }}
group: {{ item.group }}
state: present
with_items:
- { name: 'aaa', group: 'aaa' }
- { name: 'bbb', group: 'bbb' }

6.3 触发器handlers

handlers是ansible的触发器,配合notify使用。

handlers只有在playbook执行到最后才会执行,简单来说可以将handlers看作一个特殊的tasks,只有在notify指定的某个模块运行了才会触发handlers中的模块。

例一:修改nginx.conf的话就重启nginx服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- hosts: cqm
tasks:
- name: Configure Nginx File
template:
src: ./nginx.conf
dest: /etc/nginx/nginx.conf
mode: 0644
owner: www
group: www
backup: yes
notify: Restart Nginx Service

handlers:
- name: Restart Nginx Service
service:
name: nginx
state: restarted

6.4 tags标签

对tasks指定标签,可以在执行playbook的时候指定执行哪个tags的任务。

例一:利用tags来执行开启nginx和php服务

1
2
3
4
5
6
7
8
9
10
11
12
- hosts: cqm
tasks:
- name: Start Nginx and PHP Service
service:
name: {{ item }}
state: started
enabled: yes
with_items:
- nginx
- php-fpm
tags: start_services
......
1
ansible-playbook -i hosts cqm -t 'start_services' test.yml

如果要指定不执行哪个标签的任务,添加参数–skip-tags

1
ansible-playbook -i hosts cqm --skip-tags 'start_services' test.yml

6.5 包含include

如果每个playbook都会用到重启某个服务的任务,那么每个playbook都要写一次,利用include就只用写一次,让每个playbook调用即可。

例一:准备一个重启nginx的yml文件来给各个playbook调用

1
vim nginx_restart.yml
1
2
3
4
- name: Restart Nginx Service
service:
name: nginx
state: restarted
1
2
3
4
5
6
7
8
9
10
11
- hosts: cqm
tasks:
- name: Configure Nginx File
template:
src: ./nginx.conf
dest: /etc/nginx/nginx.conf
mode: 0644
owner: www
group: www
backup: yes
include: ./restart_nginx.yml

6.6 错误忽略ignore_errors

就是字面意思- -,尽管某个任务出错了,也会继续执行下面的任务。

例一:忽略某个任务

1
2
3
4
5
6
- hosts: cqm
tasks:
- name: ignore errors test
command: /bin/false
ignore_errors: yes
......

6.7 错误模块fail

fail模块是一个专门用来“执行失败”的模块,我们都知道在shell中只要添加exit就可以停止执行,而playbook只有在某个任务出错了才会停止执行,这时候就可以利用fail来停止playbook的执行。

例一:利用fail来实现exit的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- hosts: cqm
tasks:
- name: debug1
debug:
msg: "1"
- name: debug2
debug:
msg: "2"

#执行该模块后后面的debug都会执行
- name: fail
fail:
msg: " this is fail module test "

- name: debug3
debug:
msg: "3"
- name: debug4
debug:
msg: "4"

6.8 错误改变failed_when

failed_when的作用就是将条件成立的任务状态设置为失败。

例一:判断是否输出了error,是则设置为任务失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- hosts: cqm
tasks:
- name: debug1
debug:
msg: "i am debug1"

#因为输出里包含了error,所以条件成立将该任务设置为了fail,playbook中止
- name: Output Error
shell: echo 'this is error'
register: output_error
failed_when:
- ( 'error' in output_error.stdout )

- name: debug2
debug:
msg: "i am debug2"

6.9 错误处理changed_when

changed_when的作用就是将条件成立的任务状态设置为changed。

我们在调用handlers的时候,只有任务状态为changed才会调用,这时候就可以用changed_when改变任务状态为changed,就可以实现调用handlers了。

例一:改变任务状态为changed而调用触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- hosts: cqm
tasks:
- name: Configure Nginx File
template:
src: ./nginx.conf
dest: /etc/nginx/nginx.conf
mode: 0644
owner: www
group: www
backup: yes
notify: Restart Nginx Service
#尽管文件没有改变,任务状态为ok也会被改为changed而调用handlers
changed_when: yes

handlers:
- name: Restart Nginx Service
service:
name: nginx
state: restarted

changed_when也可以使任务永远不会是changed。

例二:使任务永远为ok

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- hosts: cqm
tasks:
- name: Configure Nginx File
template:
src: ./nginx.conf
dest: /etc/nginx/nginx.conf
mode: 0644
owner: www
group: www
backup: yes
notify: Restart Nginx Service
#尽管文件发生了改变,任务状态为changed也会被改为ok,永远不会调用handlers
changed_when: false

handlers:
- name: Restart Nginx Service
service:
name: nginx
state: restarted

七、Ansible的roles

roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中、并可以便捷地include它们的一种机制。

roles的目录结构:

role结构

  1. files:存放普通文件,比如copy调用的文件
  2. handlers:触发器
  3. meta:依赖关系
  4. tasks:任务
  5. templates:存放含有变量的文件
  6. vars:变量
  7. 在每个目录里的yml文件都必须命名为main.yml

一键生成roles目录

1
ansible-galaxy role init test

八、利用Ansible搭建Kodcloud

项目架构图

ansible部署kod架构图.png

8.1 准备项目环境

  1. 基本配置
1
2
3
mkdir -p /root/project/{host_vars,group_vars}
cd /root/project
cp /etc/ansible/ansible.cfg .
  1. 配置inventory
1
2
3
4
5
vim hosts
[web]
192.168.88.133
[db]
192.168.88.134
  1. 配置通用变量
1
2
3
4
5
6
vim group_vars/all
nfs_server_ip: 192.168.88.134
redis_server_ip: 192.168.88.134
ip_address_range: 192.168.88.0/24
web_user: www
web_group: www
  1. 创建各个role文件目录
1
mkdir -p {db_base,redis,mariadb,nfs_server,web_base,nginx,php,nfs_client,kodcloud}/{files,handlers,tasks,templates,vars}

8.2 配置roles文件

1
vim kod.yml
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
- hosts: db
roles:

- role: db_base
tags: db_base

- role: redis
tags: redis

- role: mariadb
tags: mariadb

- role: nfs_server
tags: nfs_server

- hosts: web
roles:

- role: web_base
tags: web_base

- role: nginx
tags: nginx

- role: php
tags: php

- role: nfs_client
tags: nfs_client

- role: kodcloud
tags: kodcloud

8.3 配置db端基本环境

  1. 编写tasks
1
vim db_base/tasks/main.yml
1
2
3
4
- name: Stop Firewall Service
service:
name: firewalld
state: stopped

8.4 配置db端redis服务

  1. 编写tasks
1
vim redis/tasks/main.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- name: Install Redis Service
yum:
name: redis
state: latest

- name: Configure Redis Service
template:
src: redis.conf.j2
dest: /etc/redis.conf
backup: yes
notify: Restart Redis Service

- name: Start Redis Service
service:
name: redis
state: started
enabled: yes
  1. 编写templates
1
2
3
4
5
6
vim redis/temlpates/redis.conf.j2
egrep -v '^#|^$' redis/templates/redis.conf.j2
bind {{ ansible_default_ipv4.address }}
protected-mode yes
port 6379
......
  1. 编写handlers
1
cat redis/handlers/main.yml
1
2
3
4
- name: Restart Redis Service
service:
name: redis
state: restarted

8.5 配置db端mariadb服务

  1. 编写tasks
1
vim mariadb/tasks/main.yml
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
- name: Install Mariadb Service
yum:
name: '{{ item }}'
state: latest
with_items:
- mariadb
- mariadb-server
- MySQL-python

- name: Configure Mariadb Service
copy:
src: my.cnf.j2
dest: /etc/my.cnf
backup: yes

- name: Start Mariadb Service
service:
name: mariadb
state: started

- name: Configure Mariadb Root User
mysql_user:
user: root
password: toortoor

- name: Create {{ website }} Databases
mysql_db:
login_user: root
login_password: toortoor
name: '{{ website }}'
state: present
collation: utf8_bin
encoding: utf8

- name: Create {{ website }} DB User
mysql_user:
login_user: root
login_password: toortoor
name: '{{ web_db_user }}'
password: '{{ web_db_pass }}'
host: '192.168.88.%'
priv: '*.*:ALL,GRANT'
state: present
  1. 编写files
1
vim mariadb/files/my.cnf.j2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# Settings user and group are ignored when systemd is used.
# If you need to run mysqld under a different user or group,
# customize your systemd unit file for mariadb according to the
# instructions in http://fedoraproject.org/wiki/Systemd

[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid

#
# include all files from the config directory
#
!includedir /etc/my.cnf.d
  1. 编写vars
1
vim mariadb/vars/main.yml
1
2
3
website: kodcloud
web_db_user: kodcloud
web_db_pass: toortoor

8.6 配置db端nfs服务

  1. 编写tasks
1
vim nfs_server/tasks/main.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- name: Create NFS Share Directory
file:
path: '{{ nfs_share_directory }}'
mode: 0757
recurse: yes
state: directory

- name: Install NFS Service
yum:
name: nfs-utils
state: latest

- name: Configure NFS
template:
src: exports.j2
dest: /etc/exports
backup: yes
notify: Restarted NFS Service

- name: Start NFS Service
service:
name: nfs
state: started
enabled: yes
  1. 编写handlers
1
vim nfs_server/handlers/main.yml
1
2
3
4
- name: Restarted NFS Service
service:
name: nfs
state: restarted
  1. 编写vars
1
vim nfs_server/vars/main.yml
1
nfs_share_directory: /root/nfs_share
  1. 编写temlpates
1
2
vim nfs_server/templates/exports.j2
{{ nfs_share_directory }} {{ ip_address_range }}(rw)

8.7 配置web端基本环境

  1. 编写tasks
1
vim web_base/tasks/main.yml
1
2
3
4
5
6
7
8
9
10
11
12
- name: Create {{ web_group }} Group
group:
name: '{{ web_group }}'
state: present

- name: Create {{ web_user }} User
user:
name: '{{ web_user }}'
group: '{{ web_group }}'
state: present
create_home: no
shell: /sbin/nologin

8.8 配置web端nginx服务

  1. 编写tasks
1
vim nginx/tasks/main.yml
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
- name: Configure Firewall
firewalld:
zone: public
port: 80/tcp
permanent: yes
immediate: yes
state: enabled

- name: Configure Nginx YUM Repo
yum_repository:
name: nginx
description: Nginx YUM Repo
file: nginx
baseurl: http://nginx.org/packages/centos/7/$basearch/
gpgcheck: no
enabled: yes
state: present

- name: Install Nginx Service
yum:
name: nginx
state: latest

- name: Configure Nginx
template:
src: '{{ item.src }}'
dest: '{{ item.dest }}'
backup: yes
notify: Restart Nginx Service
with_items:
- { src: 'nginx.conf.j2', dest: '/etc/nginx/nginx.conf' }
- { src: 'default.conf.j2', dest: '/etc/nginx/conf.d/default.conf' }

- name: Started Nginx Service
service:
name: nginx
state: started
enabled: yes
  1. 编写handlers
1
vim nginx/handlers/main.yml
1
2
3
4
- name: Restart Nginx Service
service:
name: nginx
state: restarted
  1. 编写templates
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
vim nginx/templates/nginx.conf.j2
user {{ web_user }};
worker_processes {{ ansible_processor_count * 1 }};

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;


events {
worker_connections {{ ansible_processor_count * 1024 }};
}


http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

#gzip on;

include /etc/nginx/conf.d/*.conf;
}
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
vim nginx/templates/default.conf.j2
server {
listen {{ nginx_port }};
server_name localhost;

#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;

location / {
root /usr/share/nginx/html;
index index.php index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /usr/share/nginx/html;
# fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/etc/nginx/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
  1. 编写vars
1
vim nginx/vars/main.yml
1
nginx_port: 80

8.9 配置web端php服务

  1. 编写tasks
1
vim php/tasks/main.yml
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
- name: Configure PHP YUM Repo
yum:
name: http://rpms.remirepo.net/enterprise/remi-release-7.rpm
state: present

- name: Install PHP Service
yum:
name: '{{ item }}'
state: present
with_items:
- '{{ php }}'
- "{{ php_version }}-cli"
- "{{ php_version }}-common"
- "{{ php_version }}-devel"
- "{{ php_version }}-embedded"
- "{{ php_version }}-gd"
- "{{ php_version }}-mcrypt"
- "{{ php_version }}-mbstring"
- "{{ php_version }}-pdo"
- "{{ php_version }}-xml"
- "{{ php_version }}-fpm"
- "{{ php_version }}-mysqlnd"
- "{{ php_version }}-opcache"
- "{{ php_version }}-pecl-memcached"
- "{{ php_version }}-pecl-redis"
- "{{ php_version }}-pecl-mongodb"

- name: Configure PHP Service
copy:
src: www.conf.j2
dest: "{{ php_route }}/php-fpm.d/www.conf"
backup: yes
notify: Restart Nginx and PHP Service

- name: Start PHP Service
service:
name: "{{ php_version }}-fpm"
state: started
enabled: yes
  1. 编写handlers
1
vim php/handlers/main.yml
1
2
3
4
5
6
7
- name: Restart Nginx and PHP Service
service:
name: '{{ item }}'
state: restarted
with_items:
- "{{ php_version }}-fpm"
- nginx
  1. 编写files
1
2
3
4
5
6
7
8
9
vim php/files/www.conf.j2
[www]
user = www
group = www
listen = /etc/nginx/php-fpm.sock
listen.owner = www
listen.group = www
listen.mode = 0660
......
  1. 编写vars
1
vim php/vars/main.yml
1
2
3
php: php74
php_version: php74-php
php_route: /etc/opt/remi/php74

8.10 配置web端nfs服务

  1. 编写tasks
1
vim nfs_client/tasks/main.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- name: Create NFS Share Directory
file:
path: '{{ nfs_share_directory }}'
state: directory

- name: Install NFS Service
yum:
name: nfs-utils
state: latest

- name: Start NFS Service
service:
name: nfs
state: started
enabled: yes

- name: Mount NFS Share Directory
mount:
path: '{{ nfs_share_directory }}'
src: "{{ nfs_server_ip }}:{{ nfs_share_directory }}"
fstype: nfs
opts: defaults
state: mounted
  1. 编写vars
1
vim nfs_client/tasks/main.yml
1
nfs_share_directory: /root/nfs_share

8.11 配置web端kodcloud

  1. 编写tasks
1
vim kodcloud/tasks/main.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- name: Create Kodcloud Directory
file:
path: /usr/share/nginx/html/kodcloud
owner: www
group: www
mode: 0777
state: directory

- name: Input Kodcloud File
unarchive:
src: '{{ kod_version }}'
dest: /usr/share/nginx/html/kodcloud
owner: www
group: www
mode: 0777

- name: Configure Nginx Virtual Host
copy:
src: kodcloud.conf.j2
dest: /etc/nginx/conf.d/kodcloud.conf
notify: Restart Nginx and PHP Service
  1. 编写handlers
1
vim kodcloud/handlers/main.yml
1
2
3
4
5
6
7
- name: Restart Nginx and PHP Service
service:
name: '{{ item }}'
state: restarted
with_items:
- nginx
- "{{ php_version }}-fpm"
  1. 编写vars
1
vim kodcloud/vars/main.yml
1
2
kod_version: kodbox.1.15.zip
php_version: php74-php
  1. 编写files
1
wget http://static.kodcloud.com/update/download/kodbox.1.15.zip kodcloud/files/kodbox.1.15.zip
1
2
3
4
5
6
7
8
9
10
vim kodcloud/files/kodcloud.conf.j2
server {
listen 80;
server_name localhost;

location / {
root /usr/share/nginx/html/kodcloud;
index index.php index.html index.htm;
}
}

8.12 执行playbook

1
ansible-playbook -i hosts kod.yml

8.13 测试

  1. 登录到web安装kodcloud

kod安装一

kod安装二

kod安装三

  1. 登录kodcloud

kod安装四

kod安装五

Cisco

在 Cisco 中,交换机设备主要分为以下几种模式:

  1. 用户模式,刚进入设备都是用户模式,只能用来看一些统计信息
1
Switch>
  1. 特权模式,可以查看并修改配置
1
2
Switch>enable
Switch#
  1. 全局配置模式,可以修改主机名等
1
2
Switch#config terminal
Switch(config)#
  1. 接口模式,可以对指定接口进行配置
1
2
Switch(config)#int f0/1
Switch(config-if)#

1. Vlan划分

Vlan 即虚拟局域网,能够有效防止广播风暴的发生。

Vlan 的划分是在交换机上进行,实际上就是给指定端口进行划分。

  1. 在划分 vlan 前,可以看到左边的主机与右边的主机都是能够通信的

划分vlan一

  1. 接下来将左右边的主机分别划分为 vlan10、vlan20,进入全局配置模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建vlan
Switch(config)#vlan 10
Switch(config-vlan)#exit
Switch(config)#vlan 20
Switch(config-vlan)#exit
# 划分vlan
Switch(config)#int range f0/1-3
Switch(config-if-range)#switchport mode access
Switch(config-if-range)#switchport access vlan 10
Switch(config-if-range)#exit
Switch(config)#int range f0/4-6
Switch(config-if-range)#switchport mode access
Switch(config-if-range)#switchport access vlan 20
Switch(config-if-range)#exit
# 查看配置
Switch#show running-config
...
  1. 再去进行 ping 测试连通性,会发现只能 ping 通同一 vlan 下的主机

划分vlan二

2. 分类地址的通信

IP 地址分为五类,如果不是同一网络号下的地址则不能实现通信,如下图

分类地址一

这时候可以在中间添加一台路由器,来作为各主机之间的默认网关(默认路由),即可实现通信。

  1. 路由配置
1
2
3
4
5
6
7
8
9
Router>en
Router#conf t
# 进入端口进行配置
Router(config)#int g0/0
Router(config-if)#ip add 192.168.88.1 255.255.255.0
Router(config-if)#no shutdown
Router(config)#int g0/1
Router(config-if)#ip add 172.16.0.1 255.255.0.0
Router(config-if)#no shutdown
  1. 主机配置默认网关

分类地址二

  1. 连通性测试,第一个 ICMP 包会丢失是因为路由器会发送 ARP 包去寻找目标主机的 Mac 地址,所以在规定时间内没有收到回送报文就会超时

分类地址三

3. 划分子网

子网的划分实际上就是在原有的基础上进行更小的划分,划分子网后,通过使用掩码,把子网隐藏起来,使得从外部看网络没有变化,这就是子网掩码。

如下图,左右边主机看似好像是在同一网络号下,但由于子网掩码是 255.255.255.192,所以之间是不能够通信的,只有 IP 地址与子网掩码进行位与的操作之后网络号相同的才能够进行通信,例如右边两台主机与子网掩码进行位与后网络号都是 192.168.88.64,即处于同一网络号下能够进行通信。

子网划分一

每个子网内可以使用的 IP 个数由子网号来判断,例如 255.255.255.192,192 转化为二进制为 11000000,有 6 个 0,即 IP 个数为 2 ** 6 = 64 个,常见子网掩码有以下几个(子网掩码:可用主机 IP 个数):

  • 255.255.255.128:126
  • 255.255.255.192:62
  • 255.255.255.224:30
  • 255.255.255.240:14
  • 255.255.255.248:6
  • 255.255.255.252:2

要解决上图网络通信问题,只需要在中间添加个路由器即可。

  1. 路由器配置
1
2
3
4
5
6
7
8
Router>en
Router#conf t
Router(config)#int g0/0
Router(config-if)#ip add 192.168.88.12 255.255.255.192
Router(config-if)#no shutdown
Router(config)#int g0/1
Router(config-if)#ip add 192.168.88.102 255.255.255.192
Router(config-if)#no shutdown
  1. 主机添加默认网关
  2. 测试连通性

子网划分二

4. 构造超网(路由聚合)

CIDR 地址格式为 xxx.xxx.xxx.xxx/xx,如 192.168.88.10/24,24 用来判断地址块信息,二进制后的 IP 前 24 位不变,后 8 位为 0,即可得出最小地址,后 8 位为 1 时,即可得出最大地址,即 192.168.88.0、192.168.88.255。

如下图,再没有配置静态路由时,上下网络的主机是不能够与右边的主机通信的,这时候添加静态路由,设置下一跳地址即可实现通信。

路由聚合一

  1. 左边路由配置
1
Router(config)#ip route 192.168.88.196 255.255.255.252 192.168.88.194
  1. 右边路由配置
1
2
Router(config)#ip route 192.168.88.0 255.255.255.128 192.168.88.193
Router(config)#ip route 192.168.88.128 255.255.255.192 192.168.88.193
  1. 测试连通性

路由聚合二

  1. 从右边路由配置可以看出,两条静态路由可以进行路由聚合,即 192.168.88 前三个字节是相同的,那么聚合后的地址块为 192.168.88.0/24,路由配置为
1
Router(config)#ip route 192.168.88.0 255.255.255.0 192.168.88.193

5. 特定路由和默认路由

特定路由即将数据报转发给特定的目标地址,而默认路由即除了特定的目标地址外都转发给默认的路由,流程如下图。

特定路由默认路由一

在以下网络中,目的地址只要是 192.168.4 网段,都会下一跳给 R1 路由器,再由 R2 进行路由转发。

特定路由默认路由二

  1. R1 路由配置
1
2
Router(config)#ip route 192.168.4.1 255.255.255.255 10.0.1.1
Router(config)#ip route 0.0.0.0 0.0.0.0 10.0.0.1
  1. R2 路由配置
1
Router(config)#ip route 0.0.0.0 0.0.0.0 10.0.1.2
  1. R3 路由配置
1
2
3
Router(config)#ip route 192.168.0.0 255.255.255.0 10.0.0.2
Router(config)#ip route 192.168.4.0 255.255.255.0 10.0.0.2
Router(config)#ip route 0.0.0.0 0.0.0.0 10.0.2.2
  1. R4 路由配置
1
Router(config)#ip route 0.0.0.0 0.0.0.0 10.0.2.1

6. 黑洞路由

如果静态路由配置了不存在的网段,那么就可能会导致路由环路的问题,这时候可以添加一个黑洞路由进行解决,如下,只要目的地址是 192.168.88.0/24 地址快的数据包都会被丢弃。

1
Router(config)#ip route 192.168.88.0 255.255.255.0 null0

7. 路由信息协议RIP

RIP 是一种分布式的基于距离向量的路由选择协议,属于内部网关协议,主要适用于小规模的网络环境。

在 RIP 协议中,每次数据的发送都会选择跳数最少的路由,即经过节点最少的路由线路。

如下网络中,在给所有主机和路由端口配好 IP 后,还是不能够通信的,给每个路由器设置宣告地址段,即配置 RIP,路由器就会发送 RIP 报文来算出每个网段之间的最短路径,并写入路由表中,最终路径为 PC1 -> R1 -> R3 -> PC2。

RIP一

  1. 在每个路由器设置 RIP
1
2
3
4
Router(config)#router rip
Router(config-router)#network 10.0.0.0
Router(config-router)#network 192.168.2.0
Router(config-router)#network 192.168.1.0
  1. 路由表中 R 代表的就是 RIP 协议写入的路由
1
Router#show ip route

RIP二

8. 开放最短路径优先OSFP

OSPF 也属于内部网关协议,用于在单一自治系统 AS 内决策路由,是基于链路状态的动态路由选择协议。

在 OSPF 中,当链路状态发生变化就会采用洪泛法去更新信息,每个路由器都与相邻的路由器成邻居关系,邻居再相互发送链路状态信息形成邻接关系,之后各自根据最短路径算法算出路由,放在OSPF路由表,OSPF路由与其他路由比较后优的加入全局路由表。整个过程使用了五种报文、三个阶段、四张表。

在以下网络中,通过 OSPF 配置路由后的路径为 PC1 -> R1 -> R2 -> R3 -> PC2,因为 R1 与 R3 之间采用的是串行接口,是低速链路。

OSPF一

  1. R1 路由器配置
1
2
3
4
5
6
# 100为指定进程,可在1-65535之间选择
Router(config)#route ospf 100
# 0.0.0.255为反子网掩码,area为地区号
Router(config-router)#network 10.0.0.0 0.0.0.255 area 0
Router(config-router)#network 192.168.1.0 0.0.0.255 area 0
Router(config-router)#network 10.0.1.0 0.0.0.255 area 0
  1. R2 路由器配置
1
2
3
4
Router(config)#route ospf 100
Router(config-router)#network 10.0.0.0 0.0.0.255 area 0
Router(config-router)#network 10.0.1.0 0.0.0.255 area 0
Router(config-router)#network 10.0.2.0 0.0.0.255 area 0
  1. R3 路由器配置
1
2
3
4
Router(config)#route ospf 100
Router(config-router)#network 10.0.1.0 0.0.0.255 area 0
Router(config-router)#network 10.0.2.0 0.0.0.255 area 0
Router(config-router)#network 192.168.2.0 0.0.0.255 area 0
  1. 通过测试可以看到路由线路的选择

OSPF二

  1. 如果线路断了会自动更新路由表,这时候再看线路的选择就会不一样,但依旧可以联通

OSPF三

9. 边界网关协议BGP

BGP 是一种外部网关协议,是基于自治系统 AS 之间的路由协议,BGP交换的网络可达性信息提供了足够的信息来检测路由回路并根据性能优先和策略约束对路由进行决策。

每个 AS 会设置一个 BGP 发言人,即一个路由器,用来与相邻 AS 的 BGP 发言人交换网络可达性的信息。

如下图网络,分别有三个 AS,要将不同 AS 之间进行联通,就可以通过 BGP 来实现。

BGP一

  1. AS 100 中路由配置
1
2
3
4
5
6
7
# 100为自治系统编号
Router(config)#route bgp 100
# 指定邻居,IP即与相邻路由器的连接端口IP,200/300为相邻AS的编号
Router(config-router)#neighbor 10.0.1.2 remote-as 200
Router(config-router)#neighbor 10.0.0.2 remote-as 300
# 将自身AS中的网络信息通报出去
Router(config-router)#network 192.168.10.0 mask 255.255.255.0
  1. AS 200 中路由配置
1
2
3
Router(config)#route bgp 200
Router(config-router)#neighbor 10.0.1.1 remote-as 100
Router(config-router)#network 192.168.20.0 mask 255.255.255.0
  1. AS 300 中路由配置
1
2
3
Router(config)#route bgp 300
Router(config-router)#neighbor 10.0.0.1 remote-as 100
Router(config-router)#network 192.168.30.0 mask 255.255.255.0
  1. 查看 BGP 路由信息,可以看到 B 代表的 BGP 路由信息
1
2
3
4
5
Router(config-router)#do show ip route
...
B 192.168.10.0/24 [20/0] via 10.0.1.1, 00:00:00
B 192.168.30.0/24 [20/0] via 10.0.1.1, 00:00:00
...

10. 多臂路由实现vlan之间的通信

vlan 之间如果网段不同,就无法进行通信,可以在中间添加一台路由器来做双方的网关,已多臂路由的方式实现不同 vlan 不同网段之间的通信。

多臂路由即一个端口对应一个 vlan,如下图。

多臂路由一

11. 单臂路由实现vlan之间的通信

单臂路由与多臂路由的原理是相同的,区别在于在多臂路由中,交换机到路由器的链路配置的是 access,在单臂路由中用一个主干 trunk 链路替代。

  1. 在连接交换机的路由器上配置
1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建逻辑子接口
Router(config)#int g0/0.1
# 可接受id为10的vlan数据包,并封装成802.1q桢进行转发
Router(config-subif)#encapsulation dot1Q 10
Router(config-subif)#ip add 192.168.1.254 255.255.255.0
Router(config-subif)#exit
Router(config)#int g0/0.2
Router(config-subif)#encapsulation dot1Q 20
Router(config-subif)#ip add 192.168.2.254 255.255.255.0
Router(config-subif)#exit
# 开启接口
Router(config)#int g0/0
Router(config-if)#no shutdown
  1. 在交换机上将与路由器相接的端口设置为 trunk 口
1
2
Switch(config)#int f0/7
Switch(config-if)#switchport mode trunk
  1. 连通性测试

单臂路由一

12. 三层交换机实现vlan之间的通信

三层交换机就是具有部分路由器功能的交换机。

  1. 三层交换机配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Switch(config)#vlan 10
Switch(config-vlan)#exit
# 进入vlan添加IP
Switch(config)#int vlan10
Switch(config-if)#ip add 192.168.1.254 255.255.255.0
Switch(config-if)#no shutdown
Switch(config-if)#exit
Switch(config)#vlan 20
Switch(config-vlan)#exit
Switch(config)#int vlan20
Switch(config-if)#ip add 192.168.2.254 255.255.255.0
Switch(config-if)#no shutdown
Switch(config-if)#exit
Switch(config)#int range f0/1-3
Switch(config-if-range)#switchport mode access
Switch(config-if-range)#switchport access vlan 10
Switch(config-if-range)#
Switch(config-if-range)#exit
Switch(config)#int range f0/4-6
Switch(config-if-range)#switchport mode access
Switch(config-if-range)#switchport access vlan 20
# 开启路由功能
Switch(config)#ip routing
  1. 测试连通性

三层交换机一