Bootstrap

Docker手册

安装Docker

安装最新版Docker

在 CentOS |安装码头发动机码头文件 (docker.com)

安装最新版docker-compose

Install Docker Compose | Docker Documentation

使用加速器

1、使用阿里云Docker镜像加速,提升pull的速度:
进入https://cr.console.aliyun.com的控制台,使用你的支付宝帐号登录,左侧的加速器帮助页面就会显示为你独立分配的加速地址。

image-20211114140428253

#修改Docker配置文件
[root@xuegod63 ~]# cat >> /etc/docker/daemon.json << EOF 
{
"registry-mirrors": ["https://e9yneuy4.mirror.aliyuncs.com"]
}
EOF

#daeman重新加载配置,重启docker
systemctl daemon-reload    
systemctl restart docker
docker info    #再次查看会多一个注册镜像源
……
Registry Mirrors:
https://3dv9rasy.mirror.aliyuncs.com
……

Docker部署应用

安装cAdvisor

google/cadvisor: Analyzes resource usage and performance characteristics of running containers. (github.com)

安装CentOS

docker run -itd -p 2222:22 --restart=always --privileged --name centos centos /usr/sbin/init

安装MySQL

docker run -d -e MYSQL_ROOT_PASSWORD=password -p 3306:3306 --privileged=true --name mysql mysql

安装Redis

docker run -itd --name redis -p 6379:6379 redis

Docker基本命令

①查看某容器挂载的目录

[root@api-public-02 data]# docker inspect -f “{{.Mounts}}” live-main-api

①查看Docker整体空间使用情况

[root@api-public-02 docker]# docker system df

②查看Docker详细空间使用情况

[root@api-public-02 docker]# docker system df -v

①解决方法-自动清理

可以通过 Docker 内置的 CLI 指令 docker system prune 来进行自动空间清理

②docker system prune自动清理说明
1)该指令默认会清除所有如下资源:
已停止的容器(container)
未被任何容器所使用的卷(volume)
未被任何容器所关联的网络(network)
所有悬空镜像(image)。
2)该指令默认只会清除悬空镜像,未被使用的镜像不会被删除。
3)添加 -a 或 --all 参数后,可以一并清除所有未使用的镜像和悬空镜像。
4)可以添加 -f 或 --force 参数用以忽略相关告警确认信息。
5)指令结尾处会显示总计清理释放的空间大小。

以下命令可以现实所有的未挂载数据卷:
docker volume ls -f dangling=true

组合使用以下命令可以删除所有未挂载卷:
docker volume rm $(docker volume ls -qf dangling=true)

Dockerfile多阶段构建

多个 FROM 指令时,最后生成的镜像,仍以最后一条 FROM 为准,之前的 FROM 会被抛弃。最大的使用场景是将编译环境和运行环境分离。COPY 指令的`–from=builder 参数,从前边的阶段中拷贝文件到当前阶段中,多个FROM语句时,builder代表第一个阶段。

FROM golang:1.16 as builder
MAINTAINER hushuai
WORKDIR /usr/local/services/vlink_data
COPY . ./
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64
RUN go mod tidy
RUN go build -o bin/monitorServer ./main.go

FROM centos:7
WORKDIR /opt/vlink_data/server/
COPY --from=builder /usr/local/services/vlink_data/bin/monitorServer ./
EXPOSE 31654
CMD ./monitorServer -c prod.yaml

Dockerfile部署

创建容器APP

1、定义一个容器Dockerfile
①Dockerfile类似于Ansible的一键部署剧本
[root@kuang-76 ~]# mkdir test_dockerfile && cd test_dockerfile
[root@kuang-76 test_dockerfile]# vim Dockerfile

# Use an official Python runtime as a parent image
FROM python:2.7-slim
# Set the working directory to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# Make port 80 available to the world outside this container
EXPOSE 80
# Define environment variable
ENV NAME World
# Run app.py when the container launches
CMD ["python", "app.py"]

2、创建APP内容
①创建APP本身
[root@kuang-76 test_dockerfile]# vim app.py

from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"
    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

②创建安装依赖的文件
[root@kuang-76 test_dockerfile]# vim requirements.txt
Flask
Redis

3、构建APP镜像
①构建APP镜像
[root@kuang-75 test_dockerfile]# docker build --tag=friendlyhello ./
②查看构建的镜像
[root@kuang-76 ~]# docker image ls
REPOSITORY TAG IMAGE ID
friendlyhello latest 326387cea398

4、运行构建的APP
①映射端口运行APP
[root@kuang-76 ~]# docker run -p 4000:80 friendlyhello #使用CTRL+C退出
[root@kuang-76 ~]# docker run -d -p 4000:80 friendlyhello #后台运行
②访问APP服务
[root@kuang-76 ~]# curl 127.0.0.1:4000 #即可访问服务器的4000端口

<h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>

③查看运行的容器
[root@kuang-76 ~]# docker container ls
CONTAINER ID IMAGE COMMAND CREATED
1fa4ab2cf395 friendlyhello “python app.py” 28 seconds ago
④关闭容器
[root@kuang-76 ~]# docker container stop 1fa4ab2cf395

docker-compose使用

1、YAML文件定义容器如何运行
①控制APP的该服务在容器内使用多少计算机资源
[root@kuang-76 ~]# vim docker-compose.yml

version: "3"
services:
  web:
    image: kuangzhilu/get-started:part2
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:

②配置说明
起5个副本;限制每个实例最多能用0.1个CPU(如果1.5则是1.5个CPU);50M内存;

2、运行负载均衡APP
①初始化
[root@kuang-76 ~]# docker swarm init #设置为swarm管理节点
②部署服务
[root@kuang-76 ~]# docker stack deploy -c docker-compose.yml getstartedlab #起个名字为getstartedlab
③查看服务运行
[root@kuang-76 ~]# docker service ls #获取服务ID
[root@kuang-76 ~]# docker stack services getstartedlab #和上条命令效果一样
ID NAME MODE REPLICAS IMAGE PORTS
y0dztwq34ztd getstartedlab_web replicated 5/5 kuangzhilu/get-started:part2 *:4000->80/tcp
④查看某服务的所有进程(每个进程即一个任务task)

[root@kuang-75 ~]# docker service ps getstartedlab_web
ID                  NAME                  IMAGE                          NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
qnkf6smmro6e        getstartedlab_web.1   kuangzhilu/get-started:part2   kuang-75            Running             Running about a minute ago                       
upowki1lgwha        getstartedlab_web.2   kuangzhilu/get-started:part2   kuang-75            Running             Running about a minute ago                       
hbgnuvxzpkd4        getstartedlab_web.3   kuangzhilu/get-started:part2   kuang-75            Running             Running about a minute ago                       
zqvr5vko32pb        getstartedlab_web.4   kuangzhilu/get-started:part2   kuang-75            Running             Running about a minute ago                       
hhe3c21kbl3c        getstartedlab_web.5   kuangzhilu/get-started:part2   kuang-75            Running             Running about a minute ago        

⑤查看所有进程也可以查看服务进程(只是没有单独过滤服务进程,包含了其他进程)
[root@kuang-76 ~]# docker container ls -q #只能查看所有进程的ID
⑥验证负载均衡
[root@kuang-76 ~]# curl -4 http://localhost:4000 #不停使用该命令访问服务可看到效果

3、扩大APP规模
①修改YAML配置文件
[root@kuang-76 ~]# vim docker-compose.yml #编辑该文件,修改replicas副本即可
②重新YAML文件部署服务
[root@kuang-76 ~]# docker stack deploy -c docker-compose.yml getstartedlab #重新运行一次该命令即可

4、卸下APP和Swarm
[root@kuang-76 ~]# docker stack rm getstartedlab
[root@kuang-76 ~]# docker swarm leave --force

镜像操作

导入镜像

方法一通过本地加载方式导入镜像

[root@node1 ~]# docker load -i docker.io-nginx.tar

[root@node1 ~]# docker load -i docker.io-tianyebj-pod-infrastructure.tar

方法二直接通过pull方式下载

[root@node1 ~]# docker pull nginx

[root@node1 ~]# docker pull tianyebj/pod-infrastructure #众所周知的原因,不能从rhel官网上下载基础设施镜像,需要先登录DockerHub账号再去下载

修改pod-infrastructure的标签和kubelet中指定的一致

[root@node1 ~]# docker tag docker.io/tianyebj/pod-infrastructure registry.access.redhat.com/rhel7/pod-infrastructure:latest #修改镜像tag名称,需要和配置文件中保持一致,然后把原tag给删除

[root@node1 ~]# vim /etc/kubernetes/kubelet #在此配置文件当中指定了镜像名

KUBELET_POD_INFRA_CONTAINER=“–pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest”

推送镜像

①推送前需要登录Docker官网账号
[root@kuang-76 ~]# docker login

②给镜像打上标签
[root@kuang-76 ~]# docker tag friendlyhello kuangzhilu/get-started:part2 #格式为命名空间/repository:tag,推送时会自动推送到该命名空间里(这里你的用户名即命名空间)

③发布本地镜像
[root@kuang-76 ~]# docker push kuangzhilu/get-started:part2 #如果用户kuangzhilu中未创建get-started仓库,则该命令会自动创建,推送完就可以在自己的用户空间看到了

④拓展直接拉取并启动远程仓库
[root@kuang-76 ~]# docker run -p 4000:80 kuangzhilu/get-started:part2 #本地没找到该镜像,则会自动到Docker官网去拉取

buildx插件

使用buildx构建多架构的镜像

https://cloud.tencent.com/developer/article/2021645

网络

永久启动网络转发功能

需要开启转发功能,否则容器不通外网
[root@xuegod63 ~]# vim /etc/sysctl.conf
net.ipv4.ip_forward = 1
[root@xuegod63 ~]# sysctl -p
net.ipv4.ip_forward = 1
[root@xuegod63 ~]# cat /proc/sys/net/ipv4/ip_forward
1

网桥操作

  • 建立一个bridge模式的新网桥
    docker network create --driver bridge --subnet=172.18.0.0/16 --gateway=172.18.0.1 new_bridge

  • 为容器添加某网卡并加入到某网桥中
    docker network connect new_bridge test2
    为test2 容器添加一块 new_bridge的 虚拟网卡,这样test2 上会 创建一个新的虚拟网卡,网段就是 新网桥设置的。如此就能互相ping通。

1、使用默认的桥接网络
①查看当前所有的网络

[root@kuang-76 ~]# docker network ls 
NETWORK ID          NAME                DRIVER              SCOPE
c11cb1977079        bridge              bridge              local
48c78038e899        host                host                local
cf427a1da23c        none                null                local

②启动两个alpine容器(即轻型linux)

[root@kuang-76 ~]# docker run -dit --name alpine1 alpine ash    #默认也是运行ash,而不是bash
[root@kuang-76 ~]# docker run -dit --name alpine2 alpine ash
[root@kuang-76 ~]# docker container ls    #确认容器都运行

③查看某个网络都有哪些容器连接进来
[root@kuang-76 ~]# docker network inspect bridge #没指定–network,默认连接到bridge
④连接并进入到一个容器
[root@kuang-76 ~]# docker attach alpine1 #使用exit退出时,会自动将容器关闭

# ip addr show
# ping -c 2 www.baidu.com    #可以ping通
# ping -c 2 172.17.0.3    #可以ping通

⑤移除这两个容器

[root@kuang-76 ~]# docker container stop alpine1 alpine2
[root@kuang-76 ~]# docker container rm alpine1 alpine2

2、使用用户定义网桥
①创建自定义网桥

[root@kuang-76 ~]# docker network create --driver bridge alpine-net
[root@kuang-76 ~]# docker network ls    #查看创建的网桥

②查看自定义网桥信息
[root@kuang-76 ~]# docker network inspect alpine-net #网桥相当于VM中的NAT模式,所以可以有多个
③创建相应容器

[root@kuang-76 ~]# docker run -dit --name alpine1 --network alpine-net alpine ash
[root@kuang-76 ~]# docker run -dit --name alpine2 --network alpine-net alpine ash
[root@kuang-76 ~]# docker run -dit --name alpine3 alpine ash
[root@kuang-76 ~]# docker run -dit --name alpine4 --network alpine-net alpine ash
[root@kuang-76 ~]# docker network connect bridge alpine4
[root@kuang-76 ~]# docker container ls    #确认所有容器运行

④检查网络
[root@kuang-76 ~]# docker network inspect bridge
[root@kuang-76 ~]# docker network inspect alpine-net #目前1、2和4同在alpine-net网内,可以互相ping通,但只有4可以和3互通,1和2不能和3互通
⑤测试网络连通性
[root@kuang-76 ~]# docker container attach alpine1
[root@kuang-76 ~]# ping -c 2 alpine2 #像host主机一样,容器名字可以被解析

⑦alpine4是连接了两个网络,他的网络情况如何
alpine4去ping1和2可以直接使用容器名或者IP地址,而ping alpine3则必须使用IP地址

3、管理用户定义的桥
①创建和删除自定义桥
[root@kuang-76 ~]# docker network create my-net #创建
[root@kuang-76 ~]# docker network rm my-net #删除
②容器连接到用户定义的桥

[root@kuang-76 ~]# docker create --name my-nginx \
> --network my-net \
> --publish 8080:80 \
> nginx:latest

③启动该容器
[root@node1 ~]# docker container start 4a6e400f2509

容器网络和宿主机网络

找 docker 和 宿主机上 veth 设备的关系

但是可以查看 container 里的 eth0 网卡的 iflink 找到对应关系。

# 宿主机上

$ ip link

......

9: veth0e9cd8d@if8:  mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default

  link/ether 6a:fb:59:e5:7e:da brd ff:ff:ff:ff:ff:ff link-netnsid 1

# 容器内

$ sudo docker exec -it e151 bash

root@e1517e9d9e1a:/# cat /sys/class/net/eth0/iflink

9

这样就可以确定 container e1517e9d9e1a 在物理机上对应的 veth pair 是 veth0e9cd8d 了。

这种方式需要登录到 docker 里执行命令,不是所有的容器都能这么做,不过 github 上有人专门做了个脚本来用实现这个功能,可以参考一下:https://github.com/micahculpepper/dockerveth

容器启动资源分配

资源限制说明

内存限制相关的参数

执行docker run命令时能使用的和内存限制相关的所有选项如下。

选项描述
-m,–memory内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M
–memory-swap内存+交换分区大小总限制。格式同上。必须必-m设置的大
–memory-reservation内存的软性限制。格式同上
–oom-kill-disable是否阻止 OOM killer 杀死容器,默认没设置
–oom-score-adj容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0
–memory-swappiness用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数
–kernel-memory核心内存限制。格式同上,最小为 4M

CPU限制相关的参数

docker run命令和 CPU 限制相关的所有选项如下:

选项描述
–cpuset-cpus=“”允许使用的 CPU 集,值可以为 0-3,0,1
-c,–cpu-shares=0CPU 共享权值(相对权重)
cpu-period=0限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000]
–cpu-quota=0限制 CPU CFS 配额,必须不小于1ms,即 >= 1000
–cpuset-mems=“”允许在上执行的内存节点(MEMs),只对 NUMA 系统有效

资源限制实例

1、容器启动基础配置
①给容器赋予host主机名
[root@kuang-75 ~]# docker run -it --name docker3 --hostname docker_kuang.cn centos bash
[root@docker_kuang /]# cat /etc/hostname
docker_kuang.cn

②给容器设置开机自启动
#注意:这里指的开机是Docker服务启动,只要Docker服务是运行的,该容器就运行
[root@kuang-73 ~]# docker run --restart=always -itd --name test666 centos bash
③测试开机自启动
[root@kuang-75 ~]# systemctl restart docker #重启Docker,其他容器都关闭了,就test666容器依然运行

④拓展Docker容器的重启策略
no,默认策略,在容器退出时不重启容器
on-failure,在容器非正常退出时(退出状态非0),才会重启
on-failure,在容器非正常退出时重启容器,最多重启3次(怕容器坏了,一直起不来,浪费资源)
always,在容器退出时总是重启容器
unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
⑤更新Docker容器的重启策略
[root@kuang-73 ~]# docker update --restart=unless-stopped test666 #还可以更新其他策略,并且可以更新运行中的容器的策略

2、对各容器的CPU分配限制
①默认都是1024,每个容器按照它的份额,获取可使用的CPU的时间片
[root@kuang-73 ~]# docker run -it --cpu-shares 512 centos bash
[root@04af4b570b7d /]# cat /sys/fs/cgroup/cpu/cpu.shares
512
[root@kuang-73 ~]# cat /sys/fs/cgroup/cpu/cpu.shares #Linux主机也有这一项
1024
②另外一种固定的分配限额模式
[root@kuang-73 ~]# docker run -it --cpu-period 1000000 --cpu-quota 200000 centos /bin/bash #每1000000微秒对容器重新分配,在这个周期内最多只能获取200000微秒的时间片,这种分配方式没有弹性
③限制使用的CPU内核
[root@kuang-73 ~]# docker run -it --name cpu1 --cpuset-cpus 0-2 centos #只给3个内核使用
[root@4f142f15c6f7 /]# cat /sys/fs/cgroup/cpuset/cpuset.cpus #默认情况是0-3
0-2

3、拓展实现两个进程争夺CPU资源现象
#实现原理:让两个进程绑定到同一个CPU上,然后对CPU做压测。然后使用stress让CPU做任意随机数的平方根
#方法:创建两个容器:docker10和docker20。让docker10和docker20只运行在cpu0和cpu1上,最终测试一下docker10和docker20使用cpu的百分比。
①分别运行两个Docker容器,并指定相同CPU内核
[root@kuang-73 ~]# docker run -itd --name docker10 --cpuset-cpus 0,1 --cpu-shares 512 centos /bin/bash
[root@kuang-73 ~]# docker run -itd --name docker20 --cpuset-cpus 0,1 --cpu-shares 1024 centos /bin/bash
②各自安装上压测工具stress,并进行压测
[root@caaa74d5b0d3 /]# yum -y install epel-release
[root@caaa74d5b0d3 /]# yum -y install stress
[root@b514e5da1ff8 /]# stress -c 2 -v -t 10m #运行压力测试,选项v显示详情
#拓展zabbix中介绍的一个压测方法
[root@caaa74d5b0d3 /]# cat /dev/urandom | md5sum #对随机数进行求md5,多运行几个
③host主机上查看CPU使用率
[root@kuang-75 ~]# top #按1显示所有CPU内核使用率

img

top内还能看到每个进程(即容器)占用的CPU内核使用率

img

4、当容器命令运行结束后,自动删除容器释放资源
#应用场景:在某些环境下,可能需要大量的新建docker实例,然后仅仅运行几分钟或几秒钟,就彻底删除,比如运行单元测试或测试弹性云计算。
#举例:阿里云,要模拟双11的压力,需要快速创建1万docker实例,每个docker容器实例中都运行ab命令,拼命访问tmall.com首页,运行1个小时,1个小时后自动删除
[root@kuang-73 ~]# docker run -it --rm --name mk centos sleep 5 #5秒后自动删除容器

5、Docker容器资源配置控制内存
[root@kuang-73 ~]# docker run -it -m 128M centos bash #给容器限额128M的内存
[root@9737df65bcb1 /]# cat /sys/fs/cgroup/memory/memory.limit_in_bytes
134217728
[root@9737df65bcb1 /]# cat /sys/fs/cgroup/memory/memory.usage_in_bytes
950272

6、Docker数据目录映射
①当容器坏了,数据还在物理机上,重新起一个容器就可以了
[root@kuang-73 ~]# docker run -it --name web1 -v /var/www/html/:/var/www/html centos bash #前面是host主机的目录,后面是容器的目录
[root@262fe04d4e5d /]# echo aaa > /var/www/html/index.html
[root@kuang-73 ~]# ls /var/www/html/ #在物理机上查看还在,是同步的
index.html

7、Docker容器资源配额控制IO
①启动一个限速容器
[root@kuang-73 ~]# docker run -it -v /var/www/html/:/var/www/html --device /dev/sda:/dev/sda --device-write-bps /dev/sda:1mb centos /bin/bash #因为容器一般没有sda这个设备的,所以要映射出一个这样的设备,然后做限速(1M每秒)
②测试容器IO速率
[root@a10be8c46a16 /]# time dd if=/dev/sda of=/var/www/html/test.out bs=1M count=10 oflag=direct,nonblock #direct:读写数据采用直接IO方式,不走缓存,直接从内存写硬盘。nonblock:读写数据采用非阻塞IO方式,优先写dd命令数据,避免其他使用IO进程的干扰
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 10.008 s, 1.0 MB/s
real 0m10.014s
user 0m0.002s
sys 0m0.017s

;