架构第二周作业-20230709
一、实现并总结容器跨主机的通信过程
【实验环境】
root@ubuntu20-server1-111:~# docker --version
Docker version 24.0.4, build 3713ee1
root@ubuntu20-server1-111:~# ip a show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:16:32:4b brd ff:ff:ff:ff:ff:ff
inet 192.168.119.111/24 brd 192.168.119.255 scope global eth0
root@ubuntu20-server2-112:~# docker --version
Docker version 24.0.4, build 3713ee1
root@ubuntu20-server2-112:~# ip a show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:fa:e8:c4 brd ff:ff:ff:ff:ff:ff
inet 192.168.119.112/24 brd 192.168.119.255 scope global eth0
【实验过程】
# 修改两台主机的默认dockerIP地址段,默认都是172.17.0.1
## 修改第一台主机默认 docker 地址段为 10.10.0.1/24
root@ubuntu20-server1-111:~# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=10.10.0.1/24
root@ubuntu20-server1-111:~# systemctl daemon-reload
root@ubuntu20-server1-111:~# systemctl restart docker
## 修改第二台的为 10.20.0.1/24
root@ubuntu20-server2-112:~# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=10.20.0.1/24
root@ubuntu20-server2-112:~# systemctl daemon-reload
root@ubuntu20-server2-112:~# systemctl restart docker
# 在宿主机添加静态路由,使其访问对应的容器可以让本机转发到对应的容器的宿主机
## 第一台主机
root@ubuntu20-server1-111:~# route add -net 10.20.0.0/24 gw 192.168.119.112
root@ubuntu20-server1-111:~# iptables -A FORWARD -s 192.168.119.0/24 -j ACCEPT
## 第二台主机
root@ubuntu20-server2-112:~# route add -net 10.10.0.0/24 gw 192.168.119.111
root@ubuntu20-server2-112:~# iptables -A FORWARD -s 192.168.119.0/24 -j ACCEPT
# 进入到容器查看IP地址
## 第一台主机
[root@27b67febb447 /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:0a:0a:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.10.0.2/24 brd 10.10.0.255 scope global eth0
valid_lft forever preferred_lft forever
## 第二台主机
[root@37928c559315 /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:0a:14:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.20.0.2/24 brd 10.20.0.255 scope global eth0
valid_lft forever preferred_lft forever
# 测试连通性
[root@27b67febb447 /]# ping -c 1 10.20.0.2
PING 10.20.0.2 (10.20.0.2) 56(84) bytes of data.
64 bytes from 10.20.0.2: icmp_seq=1 ttl=62 time=0.336 ms
--- 10.20.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.336/0.336/0.336/0.000 ms
[root@37928c559315 /]# ping -c1 10.10.0.2
PING 10.10.0.2 (10.10.0.2) 56(84) bytes of data.
64 bytes from 10.10.0.2: icmp_seq=1 ttl=62 time=0.469 ms
--- 10.10.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.469/0.469/0.469/0.000 ms
【跨主机通信原理】
root@ubuntu20-server1-111:~# tcpdump -nn -vvv -i veth86b6808 -vvv -nn ! port 22 and ! arp and ! port 53 -w veth86b6808.pacp
root@ubuntu20-server1-111:~# tcpdump -nn -vvv -i eth0 -vvv -nn ! port 22 and ! arp and ! port 53 -w eth0_server11.pacp
root@ubuntu20-server2-112:~# tcpdump -nn -vvv -i eth0 -vvv -nn ! port 22 and ! arp and ! port 53 -w eth0_server12.pacp
root@ubuntu20-server2-112:~# tcpdump -nn -vvv -i veth15e7d49 -vvv -nn ! port 22 and ! arp and ! port 53 -w veth15e7d49@if4.pacp
1、容器1 10.10.0.2
去 ping 容器2 10.20.0.2
,目的地址与本机不在同一个网段,将该数据包发送给自己的网关
2、网关收到该报文后转发给宿主机内核,宿主机收到后发现不是自己的,查看路由表,根据路由表从自己的eth0网卡转发给另一个容器的宿主机
3、容器2的宿主机收到后,发现目的地址是 10.20.0.2,通过宿主机2的 docker0 网卡发送给容器2
二、总结Dockerfile的常见指令
DockerFile 是一个可以被 Docker 程序解释的文本文件,它由指定的命令组成,在构建镜像的过程中,Docker 程序会读取 DockerFile 文件内容并生成一个临时容器,然后在临时容器中从上到下顺序执行 DockerFile 的指令,当执行完所有指令后再把临时容器提交为一个Docker镜像,这样就完成了一个镜像的构建。
# 指定当前镜像所引用的父镜像
FROM
# 设置镜像的属性标签
## 镜像的维护者信息,目前不推荐使用
MAINTAINER
## 推荐使用 LABEL,键值对形式
LABEL author="xxx@gmail.com"
LABEL version="1.0"
# 用于添加宿主机本地的文件、目录、压缩文件等资源到镜像中去,会自动解压tar.gz格式的压缩包,不自动解压zip包
ADD [--chown=<user>:<group>]<src>... <dest>
ADD --chown=root:root test /opt/test
# COPY 也可以复制相关文件到镜像中,用法同ADD,但COPY不可以解压缩任何文件
COPY [--chown=<user>:<group>]<src>... <dest>
# 设置容器的环境变量
ENV NANE="Jerry"
# 指定运行操作的用户
USER <user>[:<group>] or USER <UID>[:<GID>]
# 执行shell命令,但必须以非交互式的方式执行
RUN yum -y install net-tools && cd /etc/
# 定义volume
VOLUME ["/data/data1"]
# 定义当前工作目录,会切换到指定的目录中
WORKDIR /data/test1
# 声明要把容器的某些端口映射到宿主机,非主要
EXPOSE <port> [<port>/<protocol>...]
# 定义容器默认的启动命令或脚本
CMD ["nginx","-g","daemon off"]
# ENTRYPOINT 也可以定义容器在启动时默认执行的命令或脚本,当和CMD命令混用时,会将CMD命令当作参数传递给ENTRYPOINT后面的脚本,可以在脚本对参数做判断并对容器进行初始化操作
# 定义后续的命令以什么身份执行,默认root
USER
三、基于Dockerfile Nginx镜像并验证可以启动为容器
# 准备好相关文件
root@ubuntu20-server1-111:/data/dockerfile/nginx# ls
Dockerfile index.html nginx-1.18.0.tar.gz nginx.conf
# 编辑Dockerfile文件
root@ubuntu20-server1-111:/data/dockerfile/nginx# cat Dockerfile
FROM ubuntu:22.04
LABEL author="xxx@gmail.com"
RUN apt update && apt -y install iproute2 ntpdate tcpdump telnet traceroute nfs-kernel-server nfs-common lrzsz tree openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev gcc openssh-server iotop unzip zip make
RUN mkdir -p /apps/nginx && useradd -r -s /sbin/nologin nginx
ADD nginx-1.18.0.tar.gz /usr/local/src/
RUN cd /usr/local/src/nginx-1.18.0 && ./configure --prefix=/apps/nginx/ --user=nginx --group=nginx && make && make install && ln -s /apps/nginx/sbin/nginx /usr/sbin/nginx && chown -R nginx:nginx /apps/nginx/
RUN mkdir -p /apps/nginx/run/
ADD nginx.conf /apps/nginx/conf/
ADD index.html /apps/nginx/html/
CMD ["nginx","-g","daemon off;"]
# 构建镜像
docker build -t 192.168.119.113/myserver/nginx:20230718 .
# 启动镜像
root@ubuntu20-server1-111:/data/dockerfile/nginx# docker run -it -p 8080:80 192.168.119.113/myserver/nginx:20230718
# 验证结果如下
四、部署单机harbor并实现镜像的上传与下载
1、安装 harbor
# 安装前准备好docker环境
root@ubuntu20-server3-113:~# docker --version
Docker version 24.0.4, build 3713ee1
# 准备harbor安装包
root@ubuntu20-server3-113:~# ls
harbor-offline-installer-v2.8.2.tgz
# 安装harbor
root@ubuntu20-server3-113:~# tar xvf harbor-offline-installer-v2.8.2.tgz -C /usr/local/src/
root@ubuntu20-server3-113:~# cd /usr/local/src/harbor/
root@ubuntu20-server3-113:/usr/local/src/harbor# cp harbor.yml.tmpl harbor.yml
## 可根据自己需要修改文件,注释掉https,暂时不用
root@ubuntu20-server3-113:/usr/local/src/harbor# vim harbor.yml
hostname: 192.168.119.113
harbor_admin_password: 123456
data_volume: /data/harbor
## 开始安装,--with-trivy 是开启安全扫描
root@ubuntu20-server3-113:/usr/local/src/harbor# ./install.sh --with-trivy
···
···
[Step 5]: starting Harbor ...
[+] Running 11/11
✔ Network harbor_harbor Created 0.0s
✔ Container harbor-log Started 0.4s
✔ Container registryctl Started 0.7s
✔ Container registry Started 1.0s
✔ Container harbor-db Started 1.0s
✔ Container harbor-portal Started 1.1s
✔ Container redis Started 1.1s
✔ Container harbor-core Started 1.5s
✔ Container trivy-adapter Started 1.4s
✔ Container harbor-jobservice Started 1.9s
✔ Container nginx Started 1.9s
✔ ----Harbor has been installed and started successfully.----
2、使用浏览器进入harbor页面
3、创建一些仓库
4、镜像的上传
# 将alpine镜像重新打标签
root@ubuntu20-server1-111:~# docker tag alpine:latest 192.168.119.113/myserver/alpine:20230718
# docker默认登录镜像仓库使用https,我们这里使用http,需要修改配置文件,添加不安全的仓库,使其信任
## 添加该行
root@ubuntu20-server1-111:~# vim /etc/docker/daemon.json
{
"insecure-registries": ["192.168.119.113"]
}
## 重启
systemctl restart docker.service
# 登录到需要上传镜像的仓库
root@ubuntu20-server1-111:~# docker login 192.168.119.113
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
# 开始上传镜像
root@ubuntu20-server1-111:~# docker push 192.168.119.113/myserver/alpine:20230718
The push refers to repository [192.168.119.113/myserver/alpine]
8d3ac3489996: Pushed
20230718: digest: sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3 size: 528
# 结果如下
5、镜像下载
# 另一台主机下载该镜像仓库时,也需要配置"insecure-registries"
root@ubuntu20-server2-112:~# vim /etc/docker/daemon.json
{
"insecure-registries": ["192.168.119.113"]
}
root@ubuntu20-server2-112:~# systemctl restart docker.service
# 开始拉取镜像
root@ubuntu20-server2-112:~# docker pull 192.168.119.113/myserver/alpine:20230718
20230718: Pulling from myserver/alpine
59bf1c3509f3: Pull complete
Digest: sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3
Status: Downloaded newer image for 192.168.119.113/myserver/alpine:20230718
192.168.119.113/myserver/alpine:20230718
五、基于systemd实现容器的CPU及内存的使用限制
对于Linux主机,如果没有足够的内存来执行其它重要的任务,将会抛出OOM等异常,随后系统会开始杀死进程以释放内存,凡是运行在宿主机的内存都有可能被kill,包括docker和其它应用程序。默认容器是没有资源限制的,会尽可能的去使用系统的资源,如果因为某些原因容器大量的申请资源,使得系统资源不足而杀死掉了某些重要的进程,导致相关服务宕机,因此需要对容器资源进行限制。
【实验环境】
# 注意Cgroup的驱动是systemd
root@ubuntu20-server1-111:~# docker info | grep -i "cgroup driver"
Cgroup Driver: systemd
root@ubuntu20-server1-111:~# docker -v
Docker version 24.0.4, build 3713ee1
#如果不是,需要修改
root@ubuntu20-server1-111:~# vim /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
【实验过程】
1、拉取压力测试镜像
root@ubuntu20-server1-111:~# docker pull lorel/docker-stress-ng
# 查看相关帮助信息
docker run -it --rm lorel/docker-stress-ng --help
2、不限制容器使用的内存时
# 启动两个工作进程,每个工作进程最大128M
# --vm N 启动N个进程对内存进行压测 --vm-bytes 128M 每个进程使用多少内存进行压测
root@ubuntu20-server1-111:~# docker run -it --rm lorel/docker-stress-ng --vm 2 --vm-bytes 128M
2.1、查看结果
root@ubuntu20-server1-111:~# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
d521e828546c admiring_wing 285.81% 260.8MiB / 3.798GiB 6.71% 946B / 0B 0B / 0B 5
3、限制容器使用的内存最大为200M
# -m 200M 限制容器使用的最大内存为200M
root@ubuntu20-server1-111:~# docker run -it --rm -m 200M lorel/docker-stress-ng --vm 2 --vm-bytes 128M
3.1、查看结果
root@ubuntu20-server1-111:~# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
128db81b1dfb thirsty_raman 156.02% 199.9MiB / 200MiB 99.97% 806B / 0B 5.95GB / 0B 5
4、限制容器最多使用1核CPU,内存210M;压力测试2核CPU,内存256M
# --cpus 1 限制容器使用1核CPU,--cpu N 启动N个进程
root@ubuntu20-server1-111:~# docker run -it --rm -m 210M --cpus 1 lorel/docker-stress-ng --vm 2 --vm-bytes 128M --cpu 2
4.1 查看结果
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
68170178a74e distracted_northcutt 106.76% 209.9MiB / 210MiB 99.97% 806B / 0B 1.59GB / 0B 7
六、总结镜像的分层构建流程
1、获取官方原始镜像:官方原始镜像是构建其他镜像的基础,通常是一个最小的、基本的Linux发行版,例如Alpine Linux。
2、安装通用程序设置:在基础镜像上,安装通用的一些程序,以构建出一个通用的基础镜像版本。
3、定义应用程序和依赖项:在基础镜像上,定义应用程序基本环境,正确安装和配置应用程序及其依赖项。
4、定义业务镜像:每个业务镜像在自己业务的基础镜像上,运行特定的配置文件和业务代码。
5、测试镜像:镜像构建完成后,需要测试镜像是否满足生产需求。
6、分发镜像:如果镜像符合预期并通过测试,将镜像分发到仓库中供大家使用。
七、总结基于lxcfs对容器的内存及CPU的资源限制
在容器中是从/proc/cpuinfo
中获取到CPU的核数,但容器中的/proc
文件系统是物理机的,内存也是显示宿主机中的/proc/meminfo
信息,因此不够准确。**lxcfs **是通过文件挂载的方式将宿主机中关于系统的相关信息读取出来,通过docker的volume挂载给容器内部的proc系统,然后让docker内的应用读取proc中信息就是限定后的资源,其实还是读取宿主机真实的 proc。
# 安装 lxcfs,实现更加准确的限制
root@ubuntu20-server1-111:~# apt -y install lxcfs
# 注意挂载磁盘选项
root@ubuntu20-server1-111:~# mkfs.xfs /dev/sdb
root@ubuntu20-server1-111:~# vim /etc/fstab
/dev/sdb /var/lib/docker xfs defaults,pquota 0 0
root@ubuntu20-server1-111:~# mount -a
root@ubuntu20-server1-111:~# systemctl restart docker.service
# 限定内存为200M,CPU为1核
root@ubuntu20-server1-111:~# docker run -it -m 200M --cpus 1 \
-v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw \
-v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw \
-v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw \
-v /var/lib/lxcfs/proc/stat:/proc/stat:rw \
-v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \
-v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \
centos:7.9.2009 /bin/bash
--- 未使用 lxcfs 限制前 ---
[root@bb9761103925 /]# free -h
total used free shared buff/cache available
Mem: 3.8G 334M 2.7G 1.5M 819M 3.2G
Swap: 3.8G 0B 3.8G
[root@bb9761103925 /]# top
top - 13:26:08 up 8 min, 0 users, load average: 0.06, 0.08, 0.06
Tasks: 2 total, 1 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3982404 total, 2804284 free, 331008 used, 847112 buff/cache
KiB Swap: 3994620 total, 3994620 free, 0 used. 3400468 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 11836 3052 2648 S 0.0 0.1 0:00.01 bash
19 root 20 0 56184 3892 3364 R 0.0 0.1 0:00.00 top
--- 使用 lxcfs 限制后 ---
[root@3a25abf512b5 /]# free -h
total used free shared buff/cache available
Mem: 200M 2.6M 197M 0B 0B 197M
Swap: 3.8G 0B 3.8G
[root@3a25abf512b5 /]# top
top - 13:26:33 up 2 min, 0 users, load average: 0.08, 0.04, 0.05
Tasks: 2 total, 1 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 204800 total, 201896 free, 2904 used, 0 buff/cache
KiB Swap: 3994620 total, 3994620 free, 0 used. 201896 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 11836 3060 2656 S 0.0 1.5 0:00.01 bash
22 root 20 0 56192 3856 3312 R 0.0 1.9 0:00.00 top
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」