Docker 核心知识与实战

本文全面总结了 Docker 容器化技术的核心知识。从基础概念、底层原理、文件系统到容器网络通信,进一步扩展至 Dockerfile 编写、Docker Compose 与 Swarm 编排编排技术,并结合 Zabbix 与 MinIO 演示了实际应用部署。

1. 简介

docker

Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离

Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口

Docker 将应用程序与该程序的依赖,打包在一个文件(image)里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样.

容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例

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

Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版)

docker 用途

  1. 简化环境搭建
  2. 简化运维工作量
  3. 微服务利器

docker 安装

uname -r 内核版本大于 3.10

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
## 先进行卸载
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine \
docker-ce \
docker-*

systemctl disable docker
rm -rf /etc/systemd/system/docker.service.d

rm -rf /var/lib/docker

rm -rf /var/run/docker



curl https://get.docker.com > /tmp/install.sh
chmod +x /tmp/install.sh
/tmp/install.sh

## curl -sSL https://get.daocloud.io/docker | sh # 国内可以这样安装
sudo usermod -aG docker $USER # 避免每次都输入 sudo,将用户加入 docker 用户组
newgrp docker
service docker restart
systemctl enable docker # 开机自启
docker info # 获取docker信息
docker --help # docker 帮助文档
service docker status

快速确认

docker version

换源

cd /etc/docker 目录下找到在daemon.json文件(没有就新建),将下面内容写入(阿里云)

1
2
3
4
5
6
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://1hdirfy9.mirror.aliyuncs.com"],
"exec-opts":["native.cgroupdriver=systemd"]
}
EOF

重启daemon

systemctl daemon-reload

重启docker服务

systemctl restart docker

第一个镜像

1
2
3
docker pull [Registry]/[Respository]/[images]:[tag]  # 获取镜像操作
docker pull debian # 获取官方仓库下的 debian:latest
docker run debian echo "hello world"
  1. docker run : 启动容器
  2. debian : 使用的镜像名称 (本地如果没有镜像,就在docker hub 进行搜素,并下载最新版)
  3. echo “hello world” : 执行的命令
1
2
docker run -i -t debian /bin/bash   
docker run -i -t debian # 也可以不加 /bin/bash
  1. -i -t : 附有一个 tty 的交互会话 -i 支持stdin , -t 终端或伪终端
  2. /bin/bash : 获得一个 bash shell
  3. 退出 shell, 容器就会停止, Ctrl+ P + Q 退出而不停止

容器生命周期

docker容器生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
docker run --name weilai -h docker -it debian /bin/bash  # -h 指定hostname --name 指定docker name
docker inspect weilai # 获取 weilai 容器的更多信息
docker stats weilai # 查看容器 weilai 资源使用状态
docker run --name nginx -m 1024M --cpus=0.2 -d nginx # 启动限制资源容器
doker diff weilai # 查看 weilai 文件的更改
docker logs weilai # weilai 容器日志记录
docker ps # 正在运行的 docker 容器
docker ps -a # 列出所有容器
docker ps -n 2 # 显示最近创建的2个容器
docker ps -f status=exited # 查看停止的容器
docker start weilai # 启动已有容器 docker run 是启动一个新的实例
docker attach weilai # 切换到运行交互式容器,阻塞式 一般推荐使用 exec
docker exec -it weilai # 切换到运行交互式容器,阻塞式 一般推荐使用 exec
docker cp weilai:/tmp /home # 拷贝容器下的/tmp文件夹 到宿主机下的 /home 目录下
docker cp /home/test.sh weilai:/tmp/weuilai.sh # 宿主机下的 /home 目录的 tets.sh 到容器下的/tmp文件夹 并改名为weilai.sh
docker exec weilai ls -l # 进入容器 执行 ls -l 并回到宿主机,显示结果
docker start weilai # 查看容器进程
docker stop weilai # 停止容器
docker update --restart=always weilai # 已经启动的容器自动重启
docker kill weilai # 强制停止(不建议)
docker rm weilai # 删除容器
docker rmi -f debian # 删除 debian -f 强制删除

docker run :创建和启动一个新的容器实例,操作对象是镜像,选项较多,如果你要创建和启动一个容器,只能用run;
docker exec: 在已运行的容器中,执行命令,操作对象是容器,如果你要进入已运行的容器,并且执行命令,用exec;
docker attach: 同样操作的是已运行的容器,可以将本机标准输入(键盘输入)输到容器中,也可以将容器的输出显示在本机的屏幕上,如果你想查看容器运行过程中产生的标准输入输出,用attach;

docker 镜像

docker images : 列出本机所有镜像

1
2
3
docker images -qa # -a 显示所有镜像(含中间层) -q 只显示镜像id
docker images --digests # 显示镜像的摘要信息
docker images --no-trunc # 显示完整的镜像信息

docker search redis : 搜索 redis 镜像
docker pull redis:latest : 拉取 redis:latest 镜像 (TAG 默认为 latest

删除多个镜像:docker rmi -f 镜像名称1:[TAG] 镜像名称2:[TAG]
中间空格隔开

删除全部镜像:docker rmi -f $(docker images -qa)

同样的

强制删除 docker rm -f 容器ID或name

删除多个容器
docker rm -f 容器ID1 容器ID2

删除所有容器

docker rm -f $(docker ps -qa)

容器目录挂载

创建容器的时候,将宿主机的目录与容器内的目录进行映射,实现宿主机和容器目录的双向数据自动同步;

相比前面的 cp 更加简单方便

语法

docker run -it -v /宿主机目录:/容器目录 镜像名

多目录挂载
docker run -it -v /宿主机目录:/容器目录 -v /宿主机目录2:/容器目录2 镜像名

挂载目录制度
docker run -it -v /宿主机目录:/容器目录:ro 镜像名

例如安装redis

1
2
$ mkdir -p /opt/data/redis
$ docker run -p 6379:6379 --name myredis -v /opt/data/redis/redis.conf:/etc/redis/redis.conf -v /opt/data/redis:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes --requirepass "passwd"

安装 mysql

1
docker run -d -p 3306:3306 --name mysql -v /opt/mysql/data:/var/lib/mysql -v /etc/localtime:/etc/localtime -e MYSQL_ROOT_PASSWORD=password mysql:5.7.40

注意
同步多级目录,可能会出现权限不足的提示;
这是因为selinux把权限禁掉了,我们需要添加 –privileged=true 来解决挂载的目录没有权限的问题;

docker 网络模式

docker 默认使用的是 bridge桥接网络模式

1
2
3
4
5
## docker network ls
NETWORK ID NAME DRIVER SCOPE
d2a8ca970a9c bridge bridge local
e379fa1c8774 host host local
3dfff078ede1 none null local
  1. 自定义网络模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    ## docker network create --subnet=172.20.0.0/16 extnetwork
    a2c75e5e49ea2bf16380befd73ac19be54e271f4ad1e39549c47290d1b9fa7f3
    ## ifconfig
    br-a2c75e5e49ea: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
    inet 172.20.0.1 netmask 255.255.0.0 broadcast 172.20.255.255
    ether 02:42:43:82:72:6e txqueuelen 0 (Ethernet)
    RX packets 0 bytes 0 (0.0 B)
    RX errors 0 dropped 0 overruns 0 frame 0
    TX packets 0 bytes 0 (0.0 B)
    TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

    docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
    inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
    inet6 fe80::42:d5ff:fe34:51d4 prefixlen 64 scopeid 0x20<link>
    ether 02:42:d5:34:51:d4 txqueuelen 0 (Ethernet)
    RX packets 10771 bytes 601704 (587.6 KiB)
    RX errors 0 dropped 0 overruns 0 frame 0
    TX packets 10230 bytes 51101359 (48.7 MiB)
    TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
    ...
    ...
    ...
  2. 创建容器并指定ip --net extnetwork --ip 172.20.0.2

extnetwork 上文指定
172.20.0.1 是网关,所以从2 分配

1
2
3
4
5
6
7
8
9
10
## docker run -p 8066:8066 -it --net extnetwork --ip 172.20.0.2 debian
root@b4246dddf9f5:/#ip address
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
17: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:14:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.20.0.2/16 brd 172.20.255.255 scope global eth0
valid_lft forever preferred_lft forever

也可以用 docker inspect 容器id 查看信息

1
2
3
4
5
6
7
8
9
10
## docker inspect b4246dddf9f5
....
"NetworkID": "a2c75e5e49ea2bf16380befd73ac19be54e271f4ad1e39549c47290d1b9fa7f3",
"EndpointID": "efa3cd2ed24010ba37bcf7183fba6cce7bc89f9327375f1148db1a6888005d6f",
"Gateway": "172.20.0.1",
"IPAddress": "172.20.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",

....
  1. 删除网络
    docker network rm extnetwork

  2. 限制cpu 内存等

容器的监控原理其实就是定时读取 Linux 主机上相关的文件并展示给用户。

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
root@test:~# docker run --name nginx -m 200M --cpus=0.2 -d nginx
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
1c8999af3a3654cb9e053795198f0d1c377fc1080173213a6fdd2179123bcf00
root@test:~# ls -l /sys/fs/cgroup/memory/docker
total 0
drwxr-xr-x 2 root root 0 Oct 23 16:31 1c8999af3a3654cb9e053795198f0d1c377fc1080173213a6fdd2179123bcf00
-rw-r--r-- 1 root root 0 Oct 23 16:31 cgroup.clone_children
--w--w--w- 1 root root 0 Oct 23 16:31 cgroup.event_control
-rw-r--r-- 1 root root 0 Oct 23 16:31 cgroup.procs
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.failcnt
--w------- 1 root root 0 Oct 23 16:31 memory.force_empty
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 Oct 23 16:31 memory.numa_stat
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.oom_control
---------- 1 root root 0 Oct 23 16:31 memory.pressure_level
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 Oct 23 16:31 memory.stat
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.swappiness
-r--r--r-- 1 root root 0 Oct 23 16:31 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.use_hierarchy
-rw-r--r-- 1 root root 0 Oct 23 16:31 notify_on_release
-rw-r--r-- 1 root root 0 Oct 23 16:31 tasks
root@test:~# ls -l /sys/fs/cgroup/memory/docker/1c8999af3a3654cb9e053795198f0d1c377fc1080173213a6fdd2179123bcf00
total 0
-rw-r--r-- 1 root root 0 Oct 23 16:32 cgroup.clone_children
--w--w--w- 1 root root 0 Oct 23 16:31 cgroup.event_control
-rw-r--r-- 1 root root 0 Oct 23 16:31 cgroup.procs
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.failcnt
--w------- 1 root root 0 Oct 23 16:32 memory.force_empty
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 16:32 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 16:32 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 16:32 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 Oct 23 16:32 memory.numa_stat
-rw-r--r-- 1 root root 0 Oct 23 16:31 memory.oom_control
---------- 1 root root 0 Oct 23 16:32 memory.pressure_level
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 Oct 23 16:32 memory.stat
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.swappiness
-r--r--r-- 1 root root 0 Oct 23 16:32 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 16:32 memory.use_hierarchy
-rw-r--r-- 1 root root 0 Oct 23 16:32 notify_on_release
-rw-r--r-- 1 root root 0 Oct 23 16:32 tasks
root@test:~# cat /sys/fs/cgroup/memory/docker/1c8999af3a3654cb9e053795198f0d1c377fc1080173213a6fdd2179123bcf00/memory.limit_in_bytes
209715200 # 200M = 200 * 1024 * 1024 B = 209715200 B

memory.usage_in_bytes # 使用值

root@test:~# docker inspect nginx |grep Pid # 查看网络情况
"Pid": 7641,
"PidMode": "",
"PidsLimit": null,
root@test:~# cat /proc/7641/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
eth0: 1186 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0

设置代理

dockerd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

mkdir -p /etc/systemd/system/docker.service.d

cat > /etc/systemd/system/docker.service.d/proxy.conf <<EOF
[Service]
Environment="http_proxy=http://user:[email protected]:9870"

Environment="https_proxy=http://user:[email protected]:9870"

Environment="no_proxy=127.0.0.0/8,10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,ivolces.com,localhost"

EOF

systemctl daemon-reload
systemctl restart docker
systemctl show --property=Environment docker

containerd daemon.json 添加

1
2
3
4
5
6
7
8
9
10
11
{
"proxies":
{
"default":
{
"httpProxy": "http://proxy.example.com:8080",
"httpsProxy": "http://proxy.example.com:8080",
"noProxy": "localhost,127.0.0.1,.example.com"
}
}
}

build

1
2
3
4
5
docker build . \
--build-arg "HTTP_PROXY=http://proxy.example.com:8080/" \
--build-arg "HTTPS_PROXY=http://proxy.example.com:8080/" \
--build-arg "NO_PROXY=localhost,127.0.0.1,.example.com" \
-t your/image:tag

2. 基础知识windows10+docker+wsl+ubuntu

准备工作

安装wsl linux

  1. 开启Linux子系统,以管理员权限打开 PowerShell

    1
    2
    3
    4
    ## 启用虚拟机平台
    Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
    ## 开启Linux子系统
    Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
  2. 开启开发者模式,WIN + S 搜索 开发者设置,打开后选择 开发者模式

  3. WIN + S 搜索 store, 打开 Microsoft Store(微软应用商店),eg: 搜索 Ubuntu ,选择 Ubuntu 18.04 LST 进行安装
  4. 待安装完成后,从应用中打开 Ubuntu 18.04 LST
    优化
  5. 换源

  6. 打开自动切换到root用户

当前用户目录下 vi .bash_profile 添加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
##.bash_profile

## Get the aliases and functions
if [ -f ~/.bashrc ]; then



. ~/.bashrc

fi

sudo su - root

PATH=$PATH:$HOME/bin

export PATH

修改/etc/sudoers文件

1
2
3
4
5
6
7
8
9
10
su - root
chmod u+w /etc/sudoers

vi /etc/sudoers # 编辑文件,在root ALL=(ALL)ALL行下添加

xxx ALL=(ALL) NOPASSWD: ALL

## XXX为用户名。

chmod u-w /etc/sudoers # 回到文件的原权限!

安装docker

实际上是在Win10中安装Docker桌面服务,Linux子系统中安装客户端,连接Win10上的Docker服务,进行操作

在Ubuntu子系统中安装Docker

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
## 更新apt包管理列表
sudo apt-get update -y

## 安装依赖包
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common

## 加入Docker官方PGP公钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

## 确认指纹
sudo apt-key fingerprint 0EBFCD88

## 将stable(稳定版)Docker加入apt源中
##
## If you want to live on the edge, you can change "stable" below to "test" or
## "nightly". I highly recommend sticking with stable!
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"

## 更新apt包管理列表
sudo apt-get update -y

## 安装Docker CE最新版本
sudo apt-get install -y docker-ce

## 允许当前用户访问Docker CLI,不必使用root
sudo usermod -aG docker $USER

## 此时执行 docker version 会提示如下错误:
## Cannot connect to the Docker daemon at unix:///var/run/docker.sock.

## 配置Windows Docker服务地址 (Settings => General => Expose daemon on tcp://localhost:2375 without TLS)
echo "export DOCKER_HOST=localhost:2375" >> ~/.bashrc
## 使配置生效
. ~/.bashrc

powershell其他操作

使用管理员模式,打开PowerShell

1
2
3
4
5
启动停止 wsl 服务
## 停止子系统服务
net stop LxssManager
## 启动子系统服务
net start LxssManager

备份恢复,导出、导入子系统

1
2
3
4
## d:\Ubuntu-18.04.tar 导出文件路径
wsl --export Ubuntu-18.04 d:\Ubuntu-18.04.tar
## d:\wsl\u18.04 子系统导入后的安装路径
wsl --import Ubuntu-18.04 d:\wsl\u18.04 d:\Ubuntu-18.04.tar


3. docker原理

容器技术原理

容器,其实是一种特殊的进程而已。容器化后的用户应用,却依然还是一个宿主机上的普通进程,这就意味着这些因为虚拟化而带来的性能损耗都是不存在的

Docker 是利用 LinuxNamespaceCgroups 和联合文件系统三大机制来保证实现的, 所以它的原理是使用 Namespace 做主机名、网络、PID 等资源的隔离,使用 Cgroups 对进程或者进程组做资源(例如:CPU、内存等)的限制,联合文件系统用于镜像构建和容器运行环境.

一个“容器”,实际上是一个由 Linux NamespaceLinux Cgroupsrootfs 三种技术构建出来的进程的隔离环境。

一个正在运行的 Linux 容器,其实可以被“一分为二”地看待:

一组联合挂载在 /var/lib/docker/aufs/mnt 上的 rootfs,这一部分我们称为“容器镜像”(Container Image),是容器的静态视图;

一个由 Namespace+Cgroups 构成的隔离环境,这一部分我们称为“容器运行时”(Container Runtime),是容器的动态视图。

rootfs 里面打包的不只是应用,而是整个操作系统的文件和目录,也就意味着,应用和它运行所需要的所有依赖,都被封装在一起。(所有容器共享宿主机内核)

Kata ContainersgVisor 实现的方式与 Docker 不同

这两种容器实现的本质,都是给进程分配了一个独立的操作系统内核,从而避免了让容器共享宿主机的内核。这样,容器进程能够看到的攻击面,就从整个宿主机内核变成了一个极小的、独立的、以容器为单位的内核,从而有效解决了容器进程发生“逃逸”或者夺取整个宿主机的控制权的问题。

Kata Containers : 使用的是传统的虚拟化技术,通过虚拟硬件模拟出了一台“小虚拟机”,然后在这个小虚拟机里安装了一个裁剪后的 Linux 内核来实现强隔离。

gVisor 直接用 Go 语言“模拟”出了一个运行在用户态的操作系统内核,然后通过这个模拟的内核来代替容器进程向宿主机发起有限的、可控的系统调用。

Namespace

基于 Linux Namespace 的隔离机制相比于虚拟化技术也有很多不足之处,其中最主要的问题就是:隔离得不彻底。

NamespaceLinux 内核的一项功能,该功能对内核资源进行隔离,使得容器中的进程都可以在单独的命名空间中运行,并且只可以访问当前容器命名空间的资源。Namespace 可以隔离进程 ID、主机名、用户 ID、文件名、网络访问和进程间通信等相关资源。

Docker 主要用到以下五种命名空间。

pid namespace:用于隔离进程 ID。

net namespace:隔离网络接口,在虚拟的 net namespace 内用户可以拥有自己独立的 IP、路由、端口等。

mnt namespace:文件系统挂载点隔离。

ipc namespace:信号量,消息队列和共享内存的隔离。

uts namespace:主机名和域名的隔离

Cgroups

Cgroups 是一种 Linux 内核功能,可以限制和隔离进程的资源使用情况(CPU、内存、磁盘 I/O、网络等)。在容器的实现中,Cgroups 通常用来限制容器的 CPU 和内存等资源的使用。

cgroups 限制了固定几种资源的使用不会超限,但是它既不能隔离被共享的硬件比如 L3 cache,也不能有效地防止容器逃逸的问题

1
2
3
4
5
6
7
8
9


$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash


$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_period_us
100000 ## CPU period
$ cat /sys/fs/cgroup/cpu/docker/5d5c9f67d/cpu.cfs_quota_us
20000 ## 这就意味着这个 Docker 容器,只能使用到 20% 的 CPU 带宽。

cgroup 可以实现资源的限制,但不能保证资源的使用。

限制 cpu 使用

1
2
3
4
5
docker run -itd --name docker10 --cpuset-cpus 0,1 --cpu-shares 512 centos /bin/bash
##指定 docker10 只能在 cpu0 和 cpu1 上运行,而且 docker10 的使用 cpu 的份额 512

docker run -itd --name docker20 --cpuset-cpus 0,1 --cpu-shares 1024 centos /bin/bash
##指定 docker20 只能在 cpu0 和 cpu1 上运行,而且 docker20 的使用 cpu 的份额 1024,比 dcker10 多一倍

限制内存使用

1
2
3
4
docker run -it -m 128m centos

cat /sys/fs/cgroup/memory/memory.limit_in_bytes
134217728

限制硬盘速率

1
2
3
docker run -it -v /var/www/html/:/var/www/html --device /dev/sda:/dev/sda --device-write-bps /dev/sda:2mb centos /bin/bash

dd if=/dev/sda of=/var/www/html/test.out bs=2M count=50 oflag=direct,nonblock # direct:读写数据采用直接 IO 方式,不走缓存。直接从内存写硬盘上。nonblock:读写数据采用非阻塞 IO 方式,优先写 dd 命令的数据

联合文件系统

联合文件系统,又叫 UnionFS,是一种通过创建文件层进程操作的文件系统,因此,联合文件系统非常轻快。Docker 使用联合文件系统为容器提供构建层,使得容器可以实现写时复制以及镜像的分层构建和存储。常用的联合文件系统有 AUFSOverlayDevicemapper 等。

Docker 在镜像的设计中,引入了层(layer)的概念。也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量 rootfs
容器镜像是只读的,如果要修改必须通过 copy-on-write 机制把文件复制到只读层才行。

Docker 文件系统分层从下到上 : 只读层init层读写层

Init 层 : 是 Docker 项目单独生成的一个内部层,专门用来存放 /etc/hosts/etc/resolv.conf 等信息。这些文件本来属于只读镜像的一部分,但实际写入一些指定的值比如 hostname,所以就需要在可读写层对它们进行修改,可是,这些修改往往只对当前的容器有效,我们并不希望执行 docker commit 时,把这些信息连同可读写层一起提交掉. 所以,Docker 做法是,在修改了这些文件之后,以一个单独的层挂载了出来。而用户执行 docker commit 只会提交可读写层,所以是不包含init 层的内容

我现在有两个目录 A 和 B,它们分别有两个文件:

1
2
3
4
5
6
7
8
9

$ tree
.
├── A
│ ├── a
│ └── x
└── B
├── b
└── x

使用联合挂载的方式,将这两个目录挂载到一个公共的目录 C 上:

1
2
3
4
5
6
7
8
9
10

$ mkdir C
$ mount -t aufs -o dirs=./A:./B none ./C


$ tree ./C
./C
├── a
├── b
└── x

对于 AuFS 来说,它最关键的目录结构在 /var/lib/docker 路径下的 diff 目录:
/var/lib/docker/aufs/diff/<layer_id>

Volume

Volume 机制,允许你将宿主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作。

Docker 重要组件

containerd : containerd通过 containerd-shim 启动并管理 runC,可以说containerd真正管理了容器的生命周期。
containerd-shim
ctr
docker
docker-init
docker-proxy
dockerd
runc : 是一个用来运行容器的轻量级工具,是真正用来运行容器的。


4. docker文件系统

前言 Docker 卷与持久化数据存储

创建数据卷

1
2
3
[root@imwl-124 ~]# docker volume create myvolume
myvolume
[root@imwl-124 ~]# docker run -d --name=nginx-volume -v /usr/share/nginx/html 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
[root@imwl-124 ~]# docker volume ls
DRIVER VOLUME NAME
local c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8
local myvolume

[root@imwl-124 ~]# docker volume inspect myvolume
[
{
"CreatedAt": "2021-04-06T08:29:03Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/myvolume/_data",
"Name": "myvolume",
"Options": {},
"Scope": "local"
}
]

[root@imwl-124 ~]# docker volume inspect c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8
[
{
"CreatedAt": "2021-04-06T08:29:37Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8/_data",
"Name": "c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8",
"Options": null,
"Scope": "local"
}
]

使用数据卷

1
docker run -d --name=nginx --mount source=myvolume,target=/usr/share/nginx/html nginx

其中创建 + 使用 等同于

docker run -d --name=nginx-volume -v /usr/share/nginx/html nginx

旧容器删除,数据卷并不会删除

删除数据卷

1
docker volume rm myvolume

容器与容器之间数据共享

使用 volumes-from 参数可以在启动新的容器时来挂载已经存在的容器的卷,volumes-from 参数后面跟已经启动的容器名称

1
2
3
4
5
docker volume create log-vol

docker run --mount source=log-vol,target=/tmp/log --name=log-producer -it busybox

docker run -it --name consumer --volumes-from log-producer  busybox

主机与容器之间数据共享

-v HOST_PATH:CONTIANAER_PATH

1
docker run -v /data:/usr/local/data -it busybox

原理

1
2
3
4
5
6
7
8
9
10
[root@imwl-124 ~]# ls -l /var/lib/docker/volumes
total 32
drwxr-xr-x 3 root root 4096 Apr 6 08:29 c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8
-rw------- 1 root root 32768 Apr 6 08:29 metadata.db
drwxr-xr-x 3 root root 4096 Apr 6 08:29 myvolume

[root@imwl-124 ~]# ls -l /var/lib/docker/volumes/myvolume/_data/
total 8
-rw-r--r-- 1 root root 494 Dec 15 13:59 50x.html
-rw-r--r-- 1 root root 612 Dec 15 13:59 index.html

Docker 卷的实现原理是在主机的 /var/lib/docker/volumes 目录下,根据卷的名称创建相应的目录,然后在每个卷的目录下创建 _data 目录,在容器启动时如果使用 --mount 参数,Docker 会把主机上的目录直接映射到容器的指定目录下,实现数据持久化

分类

  1. AUFS : 现在很少使用
  2. Devicemapper : 常用在 Red HatCentOS 系统中
  3. OverlayFS : Docker 目前推荐选择

Devicemapper

DockerDevicemapper 模式有两种:

  1. loop-lvm : 主要用来开发和测试使用
  2. direct-lvm : 推荐在生产环境中使用
direct-lvm

当前生产环境 使用的 模式

loop-lvm 仅配置 “storage-driver”: “devicemapper”, 不配置 “storage-opts”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@test-196 ~]# cat /etc/docker/daemon.json
{
"bip": "172.27.0.1/16",
"fixed-cidr": "172.27.0.0/16",
"insecure-registries": ["127.0.0.1/8"],
"storage-driver": "devicemapper",
"exec-opts": ["native.cgroupdriver=systemd"],
"storage-opts": [
"dm.basesize=20G",
"dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true"
]
}

查看

1
2
3
4
5
6
7
8
9
[root@test-196 ~]# docker info |grep Driver
Storage Driver: devicemapper # 使用 devicemapper
Logging Driver: json-file
Cgroup Driver: systemd

[root@test-196 ~]# docker info |grep Pool
Pool Name: docker-thinpool # direct-lvm 模式已经配置成功
Pool Blocksize: 524.3kB
Thin Pool Minimum Free Space: 51GB

Devicemapper 原理

Devicemapper 是一种映射块设备的技术框架, 提供了一种将物理块设备映射到虚拟块设备的机制。

Devicemapper 将主要的工作部分分为 用户空间 和 内核空间

用户空间负责配置具体的设备映射策略与相关的内核空间控制逻辑,例如逻辑设备 dm-a 如何与物理设备 sda 相关联,怎么建立逻辑设备和物理设备的映射关系等。

内核空间则负责用户空间配置的关联关系实现,例如当 IO 请求到达虚拟设备 dm-a 时,内核空间负责接管 IO 请求,然后处理和过滤这些 IO 请求并转发到具体的物理设备 sda 上。

映射设备 通过 映射表 关联到具体的 物理目标设备 。事实上,映射设备不仅可以通过映射表关联到物理目标设备,也可以关联到虚拟目标设备,然后虚拟目标设备再通过映射表关联到物理目标设备。

devicemapper核心概念
瘦供给(Thin Provisioning) : 需要多少磁盘空间,存储驱动就帮我们分配多少磁盘空间

Docker 使用了瘦供给的 snapshot 技术, Docker 将镜像和容器的文件存储在瘦供给池(thinpool )中,并将这些内容挂载在 /var/lib/docker/devicemapper/ 目录下。

1
2
3
4
5
6
[root@test-249 devicemapper]# pwd
/var/lib/docker/devicemapper
[root@test-249 devicemapper]# ll
total 32
drwx------ 2 root root 24576 Apr 7 14:40 metadata # 包含 Devicemapper 本身配置的元数据信息, 以 json 的形式配置,这些元数据记录了镜像层和容器层之间的关联信息。
drwxr-xr-x 23 root root 4096 Apr 7 14:40 mnt # 是容器的联合挂载点目录,未生成容器时,该目录为空,而容器存在时,该目录下的内容跟容器中一致

Devicemapper存储模型

这个 Ubuntu 镜像一共有四层,每一层镜像都是下一层的快照,镜像的最底层是基础设备的快照。当容器运行时,容器是基于镜像的快照。Devicemapper 实现镜像分层的根本原理就是快照

快照是数据在某一个时间点的存储状态。快照的主要作用是对数据进行备份,当存储设备发生故障时,可以使用已经备份的快照将数据恢复到某一个时间点,而 Docker 中的数据分层存储也是基于快照实现的。

overlay2

要想使用 overlay2Docker版本必须高于17.06.02`。

如果你的操作系统是 RHELCentOSLinux 内核版本必须使用 3.10.0-514 或者更高版本,其他 Linux 发行版的内核版本必须高于 4.0(例如 Ubuntu 或 Debian),可以使用 uname -a 查看当前系统的内核版本。

overlay2 最好搭配 xfs 文件系统使用,并且使用 xfs 作为底层文件系统时,d_type 必须开启,可以使用以下命令验证 d_type 是否开启:

1
2
[imwl@imwl-u ~]$  xfs_info /var/lib/docker | grep ftype
naming =version 2 bsize=4096 ascii-ci=0 ftype=1

ftype=1 时,表示 d_type 已经开启。 sudo mkfs.xfs -f -n ftype=1 /path/to/disk

示例

挂载 /var/lib/docker 到 /dev/vdb1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda    253:0    0  500G  0 disk
└─vda1 253:1    0  500G  0 part /
vdb    253:16   0  500G  0 disk
└─vdb1 253:17   0    8G  0 part

$ sudo mkfs.xfs -f -n ftype=1 /dev/vdb1

$ sudo echo "/dev/vdb1 /var/lib/docker xfs defaults,pquota 0 0" >> /etc/fstab

$ sudo mount -a

$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda    253:0    0  500G  0 disk
└─vda1 253:1    0  500G  0 part /
vdb    253:16   0  500G  0 disk
└─vdb1 253:17   0    8G  0 part /var/lib/docker

$ xfs_info /var/lib/docker | grep ftype
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1

使用

修改 /etc/docker/daemon.json

1
2
3
4
5
6
7
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.size=20G",
"overlay2.override_kernel_check=true"
]
}

限制每个容器根目录大小为 20G

重启 docker

1
$ sudo systemctl start docker

docker_info 节选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Server:
Containers: 1
Running: 1
Paused: 0
Stopped: 0
Images: 1
Server Version: 20.10.6
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1

storage Driver 已经变为 overlay2,并且 d_type 也是 true

原理

overlay2AUFS 类似,它将所有目录称之为层(layer),overlay2 的目录是镜像和容器分层的基础,而把这些层统一展现到同一的目录下的过程称为联合挂载(union mount)。overlay2 把目录的下一层叫作 lowerdir,上一层叫作 upperdir,联合挂载后的结果叫作 merged

overlay2 将镜像层和容器层都放在单独的目录,并且有唯一 ID ,每一层仅存储发生变化的文件,最终使用联合挂载技术将容器层和镜像层的所有文件统一挂载到容器中,使得容器中看到完整的系统文件。

AUFS

配置

AUFS原理

1
2
3
{
"storage-driver": "aufs"
}

每一个目录在 AUFS 中都叫作分支,而在 Docker 中则称之为层(layer),但最终呈现给用户的则是一个普通单层的文件系统,我们把多层以单一层的方式呈现出来的过程叫作联合挂载。


5. 容器间的通信

docker run 创建 d dr ocker 容器时,可以用 –net 选项指定容器的网络模式, Docker 有以下4 种网络模式:
bridge 模式:使 –net =bridge 指定,默认设置, 容器启动后会通过 DHCP 获取一个地址 eg: 172.20.0.3
host 模式:使 –net=host 指定, 共享宿主机的网络
none 模式:使 –net=none 指定, 只有本地 lo 地址
container 模式:使用 –net=containerNAME orID ,其和已经存在的containerNAME 共享一个 Network Namespace

https://blog.csdn.net/u013355826/article/details/84987233
容器之间通信的主要方式

1.通过容器ip访问

容器重启后,ip会发生变化。通过容器ip访问不是一个好的方案。

2.通过宿主机的ip:port访问

通过宿主机的ip:port访问,只能依靠监听在暴露出的端口的进程来进行有限的通信。

3.通过link建立连接(官方不推荐使用)

运行容器时,指定参数link,使得源容器与被链接的容器可以进行相互通信,并且接受的容器可以获得源容器的一些数据,比如:环境变量。

源容器:mysql

docker run -itd –name test-mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7

##被链接容器 centos
docker run -itd –name test-centos –link test-mysql:mysql centos /bin/bash

##进入test-centos
docker exec -it test-centos /bin/bash
直接通过 link的名字或者link时候取的别名就能进入:

通过link建立连接的容器,被链接的容器能 ping 通源容器,反过来不行。

在被链接的容器上查看环境变量

被链接容器会继承源容器的环境变量信息。

与/etc/hosts中的主机条目不同,如果重新启动源容器,则不会自动更新存储在环境变量中的IP地址。我们建议使用主机条目 /etc/hosts来解析链接容器的IP地址。

除了环境变量之外,Docker还将源容器的主机条目添加到/etc/hosts文件中。

如果重新启动源容器,/etc/hosts链接容器上的文件将使用源容器的新IP地址自动更新,从而允许链接通信继续。

4.通过 User-defined networks(推荐)

docker network来创建一个桥接网络,在docker run的时候将容器指定到新创建的桥接网络中,这样同一桥接网络中的容器就可以通过互相访问。

创建网络

docker network create test-network
启动容器时,加入创建的网络

docker run -it –network test-network –network-alias mysql -e MYSQL_ROOT_PASSWORD=123 mysql:5.7
启动被链接的容器

docker run -it –network test-network –network-alias centos centos /bin/bash


6. 推送,备份

提交运行的容器成为镜像

生产环境一般使用 dockerfile. commit 的镜像不可控

1
2
3
4
5
6
7
8
docker run --name imwltest -h docker -dit -e TZ=Asia/Shanghai debian
## docker commit -a='作者' -m='备注' 运行时容器ID 新镜像名称
docker commit -a='imwl' -m='Test' imwltest imwl/test # 默认TAG: latest
docker commit -a='imwl' -m='Test' imwltest imwl/test:0.14

docker export imwltest > imwltest.tar # 导出容器

docker import imwltest.tar imwltest:test # 导入容器

1
2
3
4
## docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
imwl/test 0.14 69c1c6c58124 About a minute ago 114MB
imwl/test latest ad65438d599f 7 minutes ago 114MB

Docker Hub 账户 imwl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
docker login

docker push imwl/test:0.14
docker push imwl/test

docker tag flasky:latest imwl/flask:latest

docker push imwl/flask:latest




docker run --name flasky -d -p 8000:5000 imwl/flask:latest # -d 后台运行容器,并返回容器ID -p 端口映射(8000宿主机,5000容器端口)

## -P 随机端口映射
## -e TZ=Asia/Shanghai 指定时区

备份与还原

有些机密性,可以通过docker镜像备份和迁移实现

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
## docker save -o 备份镜像的名称  源镜像名称:tag版本
docker save -o imwltest.tar imwl/test:0.14

## 当前目录下 会有 imwltest.tar文件

## docker images
REPOSITORY TAG IMAGE ID CREATED
imwl/test 0.14 69c1c6c58124 27 minutes ago
imwl/test latest ad65438d599f 32 minutes ago

## docker rmi imwl/test:0.14
Untagged: imwl/test:0.14
Untagged: imwl/test@sha256:97b496724012eee3df3421edb2ab6edcd6115e42b8060c24ba06b51da466e0dd
Deleted: sha256:69c1c6c58124c56456a22c92a2e2125c9abc744f6acde379f5039779becbcc29

## docker images
REPOSITORY TAG IMAGE ID CREATED
imwl/test latest ad65438d599f 32 minutes ago

### 恢复
## docker load -i imwltest.tar
Loaded image: imwl/test:0.14

## docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
imwl/test 0.14 69c1c6c58124 32 minutes ago 114MB
imwl/test latest ad65438d599f 37 minutes ago 114MB
debian latest a8797652cfd9 2 weeks ago 114MB

私有仓库

Docker私有仓库内部用来存放镜像的仓库,具有更高的保密安全级别

搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
## docker pull registry # 拉取私有仓库镜像
## docker run -di --name=myRegistry --restart=always -p 5000:5000 registry # 启动私有仓库容器
365d3d60c6741b95bd4c7c1ea7ab0d68f2f3ef62264b2beff3120d77f5c82a88
## wget http://207.246.103.127:5000/v2/_catalog # 本机IP地址 207.246.103.127
--2020-02-20 15:27:49-- http://207.246.103.127:5000/v2/_catalog
Connecting to 207.246.103.127:5000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 20 [application/json]
Saving to: '_catalog'

_catalog 100%[=============================================================>] 20 --.-KB/s in 0s

2020-02-20 15:27:49 (2.54 MB/s) - '_catalog' saved [20/20]

## cat _catalog
{"repositories":[]}

### 因为仓库里还没有镜像,所以就是空的;

## cd /etc/docker
## "insecure-registries": ["207.246.103.127:5000"] 写入到 daemon.json文件 ## 信任私有仓库
## systemctl restart docker # 重启docker

测试

标记镜像为私有仓库的镜像
docker tag imwl/test 207.246.103.127:5000/debiantest

上传镜像到私有仓库
docker push 207.246.103.127:5000/debiantest


7. Docker安全

Docker 是基于 Linux 内核的 Namespace 技术实现资源隔离的,所有容器都共享主机的内核

尽管目前 Namespace 已经提供了非常多的资源隔离类型,但是仍然有部分关键内容没有完全隔离。其中包括一些系统的关键性目录(eg: /sys /proc)

安全问题解决

  1. User namespace : 主要用来做容器内用户和主机的用户隔离
  2. 保障镜像安全: 私有镜像进行安全检查,拉取镜像使用 https 和受信任的镜像仓库
  3. 及时升级宿主机内核
  4. 使用安全加固组件 : selinux , AppArmor(控制用户的访问权限), GRSecurity(只能访问控制,提供内存破坏防御,文件系统增强等多种防御形式)。
  5. 进行资源限制
  6. 使用安全容器 (每个容器都运行在一个单独的微型虚拟机中,拥有独立的操作系统和内核)(eg: Kata Container)
  7. 及时升级 docker 组件

8. DockerFile示例

简介

Dockerfile 是由一系列命令和参数构成的脚本,这些命令应用于操作系统基础镜像并最终创建的一个新镜像;

生产实践中一定优先使用 Dockerfile 的方式构建镜像。

基础镜像体积应该尽量小

尽量减少 Dockerfile 的行数,因为 Dockerfile 的每一条指令都会生成一个镜像层。

Dockerfile 书写原则

  1. 单一职责:由于容器的本质是进程,一个容器代表一个进程,因此不同功能的应用应该尽量拆分为不同的容器,每个容器只负责单一业务进程。

  2. 提供注释信息:Dockerfile 也是一种代码,我们应该保持良好的代码编写习惯,晦涩难懂的代码尽量添加注释,让协作者可以一目了然地知道每一行代码的作用,并且方便扩展和使用。

  3. 保持容器最小化:应该避免安装无用的软件包,可以加快容器构建速度,而且可以避免镜像体积过大。

  4. 合理选择基础镜 :容器的核心是应用,因此只要基础镜像能够满足应用的运行环境即可

  5. 使用 .dockerignore 文件 : 使用 .dockerignore 在构建时,忽略一些不需要参与构建的文件,从而提升构建效率。

  6. 尽量使用构建缓存 :Docker 构建过程中,每一条 Dockerfile 指令都会提交为一个镜像层,下一条指令都是基于上一条指令构建的。如果构建时发现要构建的镜像层的父镜像层已经存在,并且下一条命令使用了相同的指令,即可命中构建缓存。

  1. 正确设置时区: 从 Docker Hub 拉取的官方操作系统镜像大多数都是 UTC 时间 , 也可以运行时指定 -e TZ=Asia/Shanghai
1
2
3
4
5
6
## Debian
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo "Asia/Shanghai" >> /etc/timezone

## centos
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
  1. 使用国内软件源加快镜像构建速度: 最好的方式是换源
  1. 最小化镜像层数

常用命令

FROM image:tag : 使用的基础镜像构建
MAINTAINER user_info : 声明镜像维护者信息
LABEL value : 镜像描述元信息 (可以多条)
ENV key value : 设置环境变量 (可以多条)
RUN command : 构建镜像时需要运行的命令 (可以多条)
WORKDIR path_dir : 设置终端默认登录进来的工作目录
EXPOSE port : 当前容器对外暴露出的端口
ADD source_dir/file dest_dir/file : 宿主机内文件复制到容器,压缩文件会解压,也可以获取远程文件
COPY source_dir/file dest_dir/file : 宿主机内文件复制到容器,不过压缩文件不解压
VOLUME : 创建一个 可以从本机或其他容器挂载的挂载点,一般用来存放数据库和需要保存的数据
CMD : 指定容器启动时要运行的命令,多个CMD,最后一个生效 CMD 或CMD [““,”“,”“,…] CMD [““,”“,…]
ENTRYPOINT : 指定容器启动时要运行的命令,最后一个才有用
ONBUILD : 为子镜像服务

示例

简单实例:父镜像 Dockerfile:

1
2
3
4
5
6
FROM centos
ONBUILD RUN yum -y install vim
CMD /bin/bash

子镜像简单点:
FROM parent

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM centos
MAINTAINER weilai<[email protected]>

LABEL name="imwl CentOS Image" \
build-date="20180916"

ENV WORKPATH /home/
WORKDIR $WORKPATH

RUN yum -y install net-tools
RUN yum -y install vim

EXPOSE 80
CMD /bin/bash

多阶构建

多阶段构建可以通过一个 Dockerfile 很方便地构建出体积更小的镜像,只需要编写 Dockerfile 文件即可,无须借助外部脚本文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 编译代码
FROM golang:1.13
WORKDIR /go/src/github.com/wilhelmguo/multi-stage-demo/
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o http-server .

## 构建运行时镜像
FROM alpine:latest
WORKDIR /root/

## 使用 COPY 指令拷贝编译后的文件到 alpine 镜像中, --from=0 表示从第一阶段构建结果中拷贝文件到当前构建阶段
COPY --from=0 /go/src/github.com/wilhelmguo/multi-stage-demo/http-server .

CMD ["./http-server"]

与上面的效果一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM golang:1.13 AS builder  # 使用 AS 指令将这个阶段命名为 builder
WORKDIR /go/src/github.com/wilhelmguo/multi-stage-demo/
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o http-server .


FROM alpine:latest
WORKDIR /root/

## 可以使用 --from=builder,使得 Dockerfile 更加清晰可读
COPY --from=builder /go/src/github.com/wilhelmguo/multi-stage-demo/http-server .

## COPY --from=nginx:latest /etc/nginx/nginx.conf /etc/local/nginx.conf # 拷贝 nginx 官方镜像的配置文件到我们自己的镜像中时
CMD ["./http-server"]

在执行 docker build 命令时添加 target 参数,可以将构建阶段停止在指定阶段

1
docker build --target builder -t http-server:latest .

补充说明

cmdENTRYPOINT

CMDENTRYPOINT 的基本使用格式分为两种:

第一种为 CMD/ENTRYPOINT["command" , "param"] 。这种格式是使用 Linuxexec 实现的, 一般称为 exec 模式,这种书写格式为CMD/ENTRYPOINT 后面跟 json 数组,也是 Docker 推荐的使用格式。

另外一种格式为 CMD/ENTRYPOINT command param ,这种格式是基于 shell 实现的, 通常称为 shell 模式。当使用 shell 模式时,Docker 会以 /bin/sh -c command 的方式执行命令.

Dockerfile 中如果使用了 ENTRYPOINT 指令,启动 Docker 容器时需要使用 --entrypoint 参数才能覆盖 Dockerfile 中的 ENTRYPOINT 指令 ,而使用 CMD 设置的命令则可以被 docker run 后面的参数直接覆盖。

ENTRYPOINT 指令可以结合 CMD 指令使用,也可以单独使用,而 CMD 指令只能单独使用


9. docker-compose浅尝

简介

Docker ComposeDocker 官方的单机多容器管理系统,它本质是一个 Python 脚本,它通过解析用户编写的 yaml 文件,调用 Docker API 实现动态的创建和管理多个容器。

因为 目前项目上 使用的是 harbor 作为 k8s 镜像仓库,用的 docker-compose 所以了解一下.

安装

eg: 版本 1.28.6

1
2
3
4
5
6
sudo curl -L "https://github.com/docker/compose/releases/download/1.28.6/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

[root@imwl-175 ~]# docker-compose --version
docker-compose version 1.28.6, build 5db8d86f

测试

步骤

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
[root@imwl-175 ~]# mkdir docker-compose-test
[root@imwl-175 ~]# cd docker-compose-test/
[root@imwl-175 docker-compose-test]# touch docker-compose.yml
[root@imwl-175 docker-compose-test]# cat docker-compose.yml
version: '3'
services:
mysql:
image: mysql:5.7
volumes:
- mysql_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: mywordpress
MYSQL_USER: mywordpress
MYSQL_PASSWORD: mywordpress
wordpress:
depends_on:
- mysql # 依赖 mysql
image: wordpress:php7.4
ports:
- "8080:80"
restart: always
environment:
WORDPRESS_DB_HOST: mysql:3306
WORDPRESS_DB_USER: mywordpress
WORDPRESS_DB_PASSWORD: mywordpress
WORDPRESS_DB_NAME: mywordpress
volumes:
mysql_data: {}

Docker Compose 首先会为我们启动一个 MySQL 数据库,按照 MySQL 服务中声明的环境变量来设置 MySQL 数据库的用户名和密码。然后等待 MySQL 数据库启动后,再启动 WordPress 服务。WordPress 服务启动后,我们就可以通过 http://localhost:8080

1
2
docker-compose up -d # 后台运行
docker-compose stop # 停止服务

查看 信息

1
2
3
4
[root@imwl-175 docker-compose-test]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
90e131ea1694 wordpress:php7.4 "docker-entrypoint.s…" 6 seconds ago Up 5 seconds 0.0.0.0:8080->80/tcp docker-compose-test_wordpress_1
ad7f19844c6f mysql:5.7 "docker-entrypoint.s…" 7 seconds ago Up 6 seconds 3306/tcp, 33060/tcp docker-compose-test_mysql_1

访问

docker-compose信息


10. docker-swarm浅尝

1.12 版本以后自带

管理节点: 管理节点负责接受用户的请求,用户的请求中包含用户定义的容器运行状态描述,然后 Swarm 负责调度和管理容器,并且努力达到用户所期望的状态。(ratf 奇数节点)

工作节点: 工作节点运行执行器(Executor)负责执行具体的容器管理任务(Task),例如容器的启动、停止、删除等操作。

1
docker swarm init  (单网卡)

其他节点加入

1
docker swarm join --token SWMTKN-1-2q8i254edy5ibu3lzu78skyhw73qb3yad5hotnkiacc5ngj5je-2ndzf9f12nk370myw1ommxskm 66.42.99.175:2377

####
使用 docker-compose 的yaml 文件 ,加一个字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
version: '3'
services:
mysql:
image: mysql:5.7
volumes:
- mysql_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: mywordpress
MYSQL_USER: mywordpress
MYSQL_PASSWORD: mywordpress
wordpress:
depends_on:
- mysql # 依赖 mysql
image: wordpress:php7.4
deploy: # 添加字段
mode: replicated
replicas: 2
ports:
- "8080:80"
restart: always
environment:
WORDPRESS_DB_HOST: mysql:3306
WORDPRESS_DB_USER: mywordpress
WORDPRESS_DB_PASSWORD: mywordpress
WORDPRESS_DB_NAME: mywordpress
volumes:
mysql_data: {}

验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@imwl-175 docker-compose-test]# docker stack deploy -c docker-compose.yml wordpress
Ignoring unsupported options: restart

Creating network wordpress_default
Creating service wordpress_mysql
Creating service wordpress_wordpress

[root@imwl-175 docker-compose-test]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0vz3o5n0ww31 wordpress_mysql replicated 1/1 mysql:5.7
pxpnjyk0tase wordpress_wordpress replicated 2/2 wordpress:php7.4 *:8080->80/tcp

[root@imwl-175 docker-compose-test]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
zsgpzdksmq08e6v2u752smigf imwl-124 Ready Active 19.03.14
b4cya676sc3pt5fndub90lsmy * imwl-175 Ready Active Leader 1.13.1
unltyk3nwfon1u6evyledzmlm imwl-244 Ready Active 19.03.14

11. docker-buildx浅尝

安装

下载 Buildx 插件

1
2
3
mkdir -p ~/.docker/cli-plugins
curl -sSL https://github.com/docker/buildx/releases/download/v0.14.1/buildx-v0.14.1.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx

验证

1
docker buildx version

启用 BuildKit

1
export DOCKER_BUILDKIT=1

使用 Buildx 构建镜像

1
2
docker buildx create --use
docker buildx build -t test:test .

构建多平台镜像

1
2
3
4
5
6
7
8
9
10
11
## 安装 QEMU
docker run --privileged --rm tonistiigi/binfmt --install all

## 创建并使用 Buildx 构建器
docker buildx create --name multiarch-builder --driver docker-container --use
docker buildx inspect --bootstrap

docker buildx ls

## 构建并推送多架构镜像
docker buildx build --platform linux/amd64,linux/arm64/v8,linux/arm/v7,linux/386,linux/ppc64le,linux/s390x,linux/arm/v6,linux/riscv64 -t imwl/test:multiarch --push .

测试

Dockerfile

1
2
FROM python:3.9.19-alpine
RUN pip3 install kubernetes==29.0.0

构建

1
docker buildx build --platform linux/amd64,linux/arm64 -t imwl/py-kubernetes:29.0.0 --push .

查看

多平台镜像


12. dockerZabbix使用

服务端使用 docker-compose

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
63
64
65
66
67
68
69
70
71
72
73
version: '2'
services:
postgres-server:
container_name: postgres-server
image: postgres
ports:
- 5432:5432
volumes:
- ${PWD}/postgresql/data:/var/lib/postgresql/data:rw
environment:
- POSTGRES_USER=zabbix
- POSTGRES_PASSWORD=zabbix_pwd
- POSTGRES_DB=zabbix
- TZ=Asia/Shanghai
restart: unless-stopped

zabbix-snmptraps:
container_name: zabbix-snmptraps
image: zabbix/zabbix-snmptraps
ports:
- 162:1162/udp
volumes:
- ${PWD}/snmp/snmptraps:/var/lib/zabbix/snmptraps:rw
- ${PWD}/snmp/mibs:/usr/share/snmp/mibs:ro
environment:
- TZ=Asia/Shanghai
restart: unless-stopped

zabbix-server-pgsql:
container_name: zabbix-server-pgsql
image: zabbix/zabbix-server-pgsql
ports:
- 10051:10051
volumes_from:
- zabbix-snmptraps:rw
environment:
- DB_SERVER_HOST=postgres-server
- POSTGRES_USER=zabbix
- POSTGRES_PASSWORD=zabbix_pwd
- POSTGRES_DB=zabbix
- ZBX_ENABLE_SNMP_TRAPS=true
- TZ=Asia/Shanghai
restart: unless-stopped

zabbix-web-nginx-pgsql:
container_name: zabbix-web-nginx-pgsql
image: zabbix/zabbix-web-nginx-pgsql
ports:
- 443:8443
- 80:8080
volumes:
- ${PWD}/nginx/ssl:/etc/ssl/nginx:ro
environment:
- ZBX_SERVER_HOST=zabbix-server-pgsql
- DB_SERVER_HOST=postgres-server
- POSTGRES_USER=zabbix
- POSTGRES_PASSWORD=zabbix_pwd
- POSTGRES_DB=zabbix
- TZ=Asia/Shanghai
restart: unless-stopped

### 修改主机为 dns 名称 zabbix-agent
## zabbix-agent:
## container_name: zabbix-agent
## image: zabbix/zabbix-agent
## ports:
## - 10050:10050
## environment:
## - ZBX_HOSTNAME=Zabbix server
## - ZBX_SERVER_HOST=zabbix-server-pgsql
### - ZBX_ACTIVESERVERS=zabbix-server-pgsql:10051
## - TZ=Asia/Shanghai
## restart: always

使用 agent2

1
2
3
4
5
6
7
8
9
10

## agent2 启动
docker run --name zabbix-agent -t -e ZBX_HOSTNAME="172.20.19.20" \
-e ZBX_SERVER_HOST="172.20.19.55" \
-e ZBX_SERVER_PORT=10051 \
-p 10050:10050 \
--restart=unless-stopped \
--privileged \
--init \
-d zabbix/zabbix-agent2

13. minio搭建及基础使用

环境信息

4 节点,每个节点 4块盘,挂载目录 /mnt/minio{1..4}

1
2
3
4
5
6
192.168.2.144 minio1
192.168.2.145 minio2
192.168.2.146 minio3
192.168.2.147 minio4

192.168.2.143 registry.harbor.com # 使用 harbor 对接 minio

每台机器上均安装了 docker 和 docker-compose

安装 minio

因为要垮节点,使用 host 网络

docker-compose.yml 所有节点相同

192.168.2.144:8998 可以写成 minio1:8998, 我图方便没有去各个节点修改 /etc/hosts

所有节点均相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
minio:
image: quay.io/minio/minio:latest
network_mode: "host"
volumes:
- /mnt/minio1:/minio1
- /mnt/minio2:/minio2
- /mnt/minio3:/minio3
- /mnt/minio4:/minio4
environment:
MINIO_ROOT_USER: "minio"
MINIO_ROOT_PASSWORD: "minioadmin"
MINIO_OPTS: "--console-address :9001"
MINIO_PORT: "8998"
command: server --address ":8998" --console-address ":9001" http://192.168.2.144:8998/minio{1...4} http://192.168.2.145:8998/minio{1...4} http://192.168.2.146:8998/minio{1...4} http://192.168.2.147:8998/minio{1...4}

docker-compose -d 启动

使用 nginx 代理

192.168.2.143 机器上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
upstream minio{
server 192.168.2.144:8998;
server 192.168.2.145:8998;
server 192.168.2.146:8998;
server 192.168.2.147:8998;
}



server {
listen 9000;
server_name minio;
location / {
proxy_pass http://minio;
proxy_set_header Host $http_host;
client_max_body_size 1000m;
}


}

测试

192.168.2.143 上测试

下载 mc, 加入到 PATH 中,并添加 minio-server host

1
2
3
4
5
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
mv mc /usr/local/bin/

mc config host add minio-server http://192.168.2.143:8998 minio minioadmin

创建桶 harbor

1
mc mb minio-server/harbor

对接

1
2
3
4
5
6
7
8
9
10
11
12
data_volume: /data

## 新增部分
storage_service:
s3:
accesskey: minio
secretkey: minioadmin
region: us-west-1
regionendpoint: http://192.168.2.143:9000
bucket: harbor
secure: false
v4auth: true

启动后推送查看

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
mc tree minio-server/harbor
minio-server/harbor
└─ docker
└─ registry
└─ v2
├─ blobs
│ └─ sha256
│ ├─ 1b
│ │ └─ 1bbcb9df2c93e03db739f7e49ce73eda0325b8087ef8e88386d303d883c357ab
│ ├─ 53
│ │ └─ 537a6cfe3404285310129c72dfc3f352e7c5db1a5f296e514d739322bab5a998
│ ├─ 76
│ │ └─ 767bff2cc03ef46478039907c5bca487eb27d5f43a38571985e4ed4dc0403d5a
│ ├─ ad
│ │ └─ adc73cb74f2591613c7c88f7f6a313c3373bbfa3bda0983677bb233668b4033a
│ ├─ c6
│ │ └─ c6b156574604a095a5847d3b34cf36d484bb49862365e996b391d0ba0f345034
│ ├─ c9
│ │ └─ c94f3436f3bfcb467e9723bdb4957e2e86c00cc5f21e38a40d668c1a4c324696
│ ├─ ea
│ │ └─ ea5d7144c337402f813ea7c05c11dab58b7841f4c41fb5f5058abefbc2451ec5
│ ├─ f1
│ │ └─ f11c1adaa26e078479ccdd45312ea3b88476441b91be0ec898a7e07bfd05badc
│ └─ ff
│ └─ fffffc90d343cbcb01a5032edac86db5998c536cd0a366514121a45c6723765c
└─ repositories
└─ library
└─ nginx
├─ _layers
│ └─ sha256
│ ├─ 1bbcb9df2c93e03db739f7e49ce73eda0325b8087ef8e88386d303d883c357ab
│ ├─ 537a6cfe3404285310129c72dfc3f352e7c5db1a5f296e514d739322bab5a998
│ ├─ 767bff2cc03ef46478039907c5bca487eb27d5f43a38571985e4ed4dc0403d5a
│ ├─ adc73cb74f2591613c7c88f7f6a313c3373bbfa3bda0983677bb233668b4033a
│ ├─ c6b156574604a095a5847d3b34cf36d484bb49862365e996b391d0ba0f345034
│ ├─ ea5d7144c337402f813ea7c05c11dab58b7841f4c41fb5f5058abefbc2451ec5
│ ├─ f11c1adaa26e078479ccdd45312ea3b88476441b91be0ec898a7e07bfd05badc
│ └─ fffffc90d343cbcb01a5032edac86db5998c536cd0a366514121a45c6723765c
└─ _manifests
├─ revisions
│ └─ sha256
│ └─ c94f3436f3bfcb467e9723bdb4957e2e86c00cc5f21e38a40d668c1a4c324696
└─ tags
└─ latest
├─ current
└─ index
└─ sha256
└─ c94f3436f3bfcb467e9723bdb4957e2e86c00cc5f21e38a40d668c1a4c324696

下载整个 bucket

1
2
3
4
5
6
7
mc mirror minio-server/harbor ./local-harbor

root@ubuntu:~# ls
local-harbor
root@ubuntu:~# cd local-harbor/
root@ubuntu:~/local-harbor# ls
docker

上传到新的 bucket test

1
2
3
4
5
6
7
8
9
10
root@ubuntu:~/# mc mb minio-server/test
Bucket created successfully `minio-server/test`.
root@ubuntu:~/# mc ls minio-server
[2024-07-13 06:04:26 UTC] 0B harbor/
[2024-07-13 06:50:03 UTC] 0B test/

root@ubuntu:~# mc mirror ./local-harbor minio-server/test
...40d668c1a4c324696/link: 67.69 MiB / 67.69 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 158.75 MiB/s 0sroot@ubuntu:~# mc ls minio-server/test
[2024-07-13 06:51:56 UTC] 0B docker/
root@ubuntu:~#

其他 操作基本与 linux 下类似

网页

查看

minio网页

使用 velero minio 备份k8s

先创建认证文件 credentials-velero

1
2
3
[default]   
aws_access_key_id = minio
aws_secret_access_key = minio123

安装

1
2
3
4
5
6
7
8
9
./velero install \
--image velero/velero:v1.15.0 \
--provider aws \
--bucket velero \
--namespace velero \
--plugins velero/velero-plugin-for-aws:main \
--secret-file ./credentials-velero \
--use-volume-snapshots=false \
--backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://10.68.219.99:9000

简单备份

1
2
3
4
velero backup create backupk8s
velero backup get
velero backup describe backupk8s
velero backup logs backupk8s

velero-minio-备份k8s

连接公有云的oss

连接 oracle cloud 对象存储

oracleCloud对象存储填写信息

oracleCloud创建客户密钥

mc 连接

1
2
3
4
5
6
7
## us-phoenix-1 为所在区域,按需修改
## xxxx 为命名空间
mc alias set oracle https://xxxx.compat.objectstorage.us-phoenix-1.oraclecloud.com xxxxxx xxxxxxxxx
Added `oracle` successfully.

mc cp opt.tar.gz oracle/bucket-imwl/
...opt.tar.gz: 445.08 MiB / 445.08 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃

python 简单测试

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
import boto3
from botocore.client import Config

def init_s3(end_point, access_key, secret_key, region_name):
return boto3.client(
's3',
aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
use_ssl=True,
region_name=region_name,
endpoint_url=end_point,
config=Config(s3={"addressing_style": "path"})
)

## 示例调用
region_name = 'us-phoenix-1'
ns='xxxxxx'
s3_endpoint = f"https://{ns}.compat.objectstorage.{region_name}.oraclecloud.com"
s3_access_key = "xxxxxxx"
s3_secret_key = "xxxxxxxxxx"


s3 = init_s3(s3_endpoint, s3_access_key, s3_secret_key, region_name)

try:
# 列出所有存储桶
response = s3.list_buckets()
for bucket in response.get('Buckets', []):
print(f"Bucket Name: {bucket['Name']}")
except Exception as e:
print(f"Error: {e}")

运行

1
2
3
$ python client.py
Bucket Name: bucket-imwl
Bucket Name: bucket-public

连接 阿里云

遇到的问题

1
2
3
4
5
AccessDenied
The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint
问题原因:访问Bucket时使用的Endpoint不正确。

解决方案:确保使用正确的Endpoint访问Bucket。例如Bucket所在地域为oss-cn-hangzhou,则外网Endpoint为oss-cn-hangzhou.aliyuncs.com

一般使用

1
2
mc alias set aliyunoss https://oss-cn-hongkong.aliyuncs.com <AccessKey> <SecretKey> --api S3v4
mc ls aliyunoss

连接火山引擎

当前无法使用 mc

https://www.volcengine.com/docs/6349/1288493

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
问题描述
使用 PathStyle 的请求方式访问 TOS 时,报错 Forbidden path to access server 或 InvalidPathAccess。

问题原因
TOS 不支持路径样式(即 PathStyle)的请求方式,仅支持使用虚拟主机(即 VirtualHostStyle)的请求方式。

解决方案
Path 和 VirtualHost 的区别主要在于 HTTP URL 中存储桶名称的位置,如下所示:

## Path 访问方式
https://tos-s3-cn-beijing.volces.com/{bucketname}

## VirtualHost 访问方式
https://{bucketname}.tos-s3-cn-beijing.volces.com
使用各种支持 S3 协议的 SDK 和工具访问 TOS 时,请务必确保配置了 VirtualHostStyle 的请求方式。例如:

使用 AWS S3 Java SDK 时,需要在客户端初始化时确保设置了禁用 PathStyle 的配置参数:AmazonS3Builder.withPathStyleAccessEnabled(false)。
使用 AWS CLI 工具访问 TOS 时,需要在 ~/.aws/config 文件中添加 addressing_style = virtual 内容,确保请求方式为 VirtualHostStyle。

使用 s3cmd, 需要做如下修改

cn-shanghai 是区域

1
2
host_base = tos-s3-cn-shanghai.volces.com
host_bucket = %(bucket)s.tos-s3-cn-shanghai.volces.com